diff --git a/src/mem/gems_common/Allocator.hh b/src/mem/gems_common/Allocator.hh new file mode 100644 index 000000000..109696601 --- /dev/null +++ b/src/mem/gems_common/Allocator.hh @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1999 by Mark Hill and David Wood for the Wisconsin + * Multifacet Project. ALL RIGHTS RESERVED. + * + * ##HEADER## + * + * This software is furnished under a license and may be used and + * copied only in accordance with the terms of such license and the + * inclusion of the above copyright notice. This software or any + * other copies thereof or any derivative works may not be provided or + * otherwise made available to any other persons. Title to and + * ownership of the software is retained by Mark Hill and David Wood. + * Any use of this software must include the above copyright notice. + * + * THIS SOFTWARE IS PROVIDED "AS IS". THE LICENSOR MAKES NO + * WARRANTIES ABOUT ITS CORRECTNESS OR PERFORMANCE. + * */ + +/* + * $Id$ + */ + +#ifndef ALLOCATOR_H +#define ALLOCATOR_H + +#include "Vector.hh" + +template +class Allocator { +public: + // Constructors + Allocator() { m_counter = 0; } + + // Destructor + ~Allocator() { for(int i=0; i m_pool_vec; + int m_counter; +}; + +template +inline +TYPE* Allocator::allocate(const TYPE& obj) +{ + m_counter++; + DEBUG_EXPR(ALLOCATOR_COMP, LowPrio, m_counter); + TYPE* new_obj_ptr; + + // See if we need to allocate any new objects + if (m_pool_vec.size() == 0) { + // Allocate a new item + m_pool_vec.insertAtBottom(new TYPE); + } + + // Pop the pointer from the stack/pool + int size = m_pool_vec.size(); + new_obj_ptr = m_pool_vec[size-1]; + m_pool_vec.setSize(size-1); + + // Copy the object + *new_obj_ptr = obj; + return new_obj_ptr; +} + +template +inline +void Allocator::deallocate(TYPE* obj) +{ + m_pool_vec.insertAtBottom(obj); +} + +#endif //ALLOCATOR_H diff --git a/src/mem/gems_common/Map.hh b/src/mem/gems_common/Map.hh new file mode 100644 index 000000000..1ecd13d64 --- /dev/null +++ b/src/mem/gems_common/Map.hh @@ -0,0 +1,186 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +#ifndef MAP_H +#define MAP_H + +#include "Vector.hh" + +namespace __gnu_cxx { + template <> struct hash + { + size_t operator()(const string& s) const { return hash()(s.c_str()); } + }; +} + +typedef unsigned long long uint64; +//hack for uint64 hashes... +namespace __gnu_cxx { + template <> struct hash + { + size_t operator()(const uint64 & s) const { return (size_t) s; } + }; +} + +template +class Map +{ +public: + Map() { /* empty */ } + ~Map() { /* empty */ } + + void add(const KEY_TYPE& key, const VALUE_TYPE& value); + bool exist(const KEY_TYPE& key) const; + int size() const { return m_map.size(); } + void erase(const KEY_TYPE& key) { assert(exist(key)); m_map.erase(key); } + Vector keys() const; + Vector values() const; + void deleteKeys(); + void deleteValues(); + VALUE_TYPE& lookup(const KEY_TYPE& key) const; + void clear() { m_map.clear(); } + void print(ostream& out) const; + + // Synonyms + void remove(const KEY_TYPE& key) { erase(key); } + void deallocate(const KEY_TYPE& key) { erase(key); } + void allocate(const KEY_TYPE& key) { add(key, VALUE_TYPE()); } + void insert(const KEY_TYPE& key, const VALUE_TYPE& value) { add(key, value); } + + // Use default copy constructor and assignment operator +private: + // Data members + + // m_map is declared mutable because some methods from the STL "map" + // class that should be const are not. Thus we define this as + // mutable so we can still have conceptually const accessors. + mutable __gnu_cxx::hash_map m_map; +}; + +template +ostream& operator<<(ostream& out, const Map& map); + +// ********************* + +template +void Map::add(const KEY_TYPE& key, const VALUE_TYPE& value) +{ + // Update or add a new key/value pair + m_map[key] = value; +} + +template +bool Map::exist(const KEY_TYPE& key) const +{ + return (m_map.count(key) != 0); +} + +template +VALUE_TYPE& Map::lookup(const KEY_TYPE& key) const +{ + assert(exist(key)); + return m_map[key]; +} + +template +Vector Map::keys() const +{ + Vector keys; + typename hash_map::const_iterator iter; + for (iter = m_map.begin(); iter != m_map.end(); iter++) { + keys.insertAtBottom((*iter).first); + } + return keys; +} + +template +Vector Map::values() const +{ + Vector values; + typename hash_map::const_iterator iter; + pair p; + + for (iter = m_map.begin(); iter != m_map.end(); iter++) { + p = *iter; + values.insertAtBottom(p.second); + } + return values; +} + +template +void Map::deleteKeys() +{ + typename hash_map::const_iterator iter; + pair p; + + for (iter = m_map.begin(); iter != m_map.end(); iter++) { + p = *iter; + delete p.first; + } +} + +template +void Map::deleteValues() +{ + typename hash_map::const_iterator iter; + pair p; + + for (iter = m_map.begin(); iter != m_map.end(); iter++) { + p = *iter; + delete p.second; + } +} + +template +void Map::print(ostream& out) const +{ + typename hash_map::const_iterator iter; + pair p; + + out << "["; + for (iter = m_map.begin(); iter != m_map.end(); iter++) { + // unparse each basic block + p = *iter; + out << " " << p.first << "=" << p.second; + } + out << " ]"; +} + +template +ostream& operator<<(ostream& out, const Map& map) +{ + map.print(out); + return out; +} + +#endif //MAP_H diff --git a/src/mem/gems_common/PrioHeap.hh b/src/mem/gems_common/PrioHeap.hh new file mode 100644 index 000000000..d549f0944 --- /dev/null +++ b/src/mem/gems_common/PrioHeap.hh @@ -0,0 +1,249 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PRIOHEAP_H +#define PRIOHEAP_H + +#include "Vector.hh" + +typedef unsigned int HeapIndex; + +template +class PrioHeap { +public: + // Constructors + PrioHeap() { init(); } + + // Destructor + //~PrioHeap(); + + // Public Methods + void init() { m_current_size = 0; } + int size() const { return m_current_size; } + void insert(const TYPE& key); + const TYPE& peekMin() const; + const TYPE& peekElement(int index) const; + TYPE extractMin(); + void print(ostream& out) const; +private: + // Private Methods + bool verifyHeap() const; + bool verifyHeap(HeapIndex index) const; + void heapify(); + + // Private copy constructor and assignment operator + PrioHeap(const PrioHeap& obj); + PrioHeap& operator=(const PrioHeap& obj); + + // Data Members (m_ prefix) + Vector m_heap; + HeapIndex m_current_size; +}; + +// Output operator declaration +template +ostream& operator<<(ostream& out, const PrioHeap& obj); + +// ******************* Helper Functions ******************* +inline +HeapIndex get_parent(HeapIndex i) +{ + // return (i/2); + return (i>>1); +} + +inline +HeapIndex get_right(HeapIndex i) +{ + // return (2*i) + 1; + return (i<<1) | 1; +} + +inline +HeapIndex get_left(HeapIndex i) +{ + // return (2*i); + return (i<<1); +} + +template +void prio_heap_swap(TYPE& n1, TYPE& n2) +{ + TYPE temp = n1; + n1 = n2; + n2 = temp; +} + +// ******************* Definitions ******************* + +template +void PrioHeap::insert(const TYPE& key) +{ + int i; + // grow the vector size + m_current_size++; + m_heap.setSize(m_current_size+1); + + if(m_current_size == 1){ // HACK: need to initialize index 0 to avoid purify UMCs + m_heap[0] = key; + } + + i = m_current_size; + while ((i > 1) && (node_less_then_eq(key, m_heap[get_parent(i)]))) { + m_heap[i] = m_heap[get_parent(i)]; + i = get_parent(i); + } + m_heap[i] = key; + // assert(verifyHeap()); +} + +template +const TYPE& PrioHeap::peekMin() const +{ + assert(size() > 0); + return m_heap[1]; // 1, not 0, is the first element +} + +template +const TYPE& PrioHeap::peekElement(int index) const +{ + assert(size() > 0); + return m_heap[index]; +} + +template +TYPE PrioHeap::extractMin() +{ + // TYPE temp; + assert(size() > 0); + TYPE temp = m_heap[1]; // 1, not 0, is the first element + m_heap[1] = m_heap[m_current_size]; + m_current_size--; + heapify(); + return temp; +} + +template +bool PrioHeap::verifyHeap() const +{ + return verifyHeap(1); +} + +template +bool PrioHeap::verifyHeap(HeapIndex index) const +{ + // Recursively verify that each node is <= its parent + if(index > m_current_size) { + return true; + } else if (index == 1) { + return + verifyHeap(get_right(index)) && + verifyHeap(get_left(index)); + } else if (node_less_then_eq(m_heap[get_parent(index)], m_heap[index])) { + return + verifyHeap(get_right(index)) && + verifyHeap(get_left(index)); + } else { + // Heap property violation + return false; + } +} + +template +void PrioHeap::heapify() +{ + HeapIndex current_node = 1; + HeapIndex left, right, smallest; + // HeapIndex size = m_current_size; + + while(true) { + left = get_left(current_node); + right = get_right(current_node); + + // Find the smallest of the current node and children + if (left <= m_current_size && node_less_then_eq(m_heap[left], m_heap[current_node])) { + smallest = left; + } else { + smallest = current_node; + } + + if (right <= m_current_size && node_less_then_eq(m_heap[right], m_heap[smallest])) { + smallest = right; + } + + // Check to see if we are done + if (smallest == current_node) { + // We are done + break; + } else { + // Not done, heapify on the smallest child + prio_heap_swap(m_heap[current_node], m_heap[smallest]); + current_node = smallest; + } + } + // assert(verifyHeap()); +} + +template +void PrioHeap::print(ostream& out) const +{ + Vector copyHeap(m_heap); + + // sort copyHeap (inefficient, but will not be done often) + + for(HeapIndex i=0;i +ostream& operator<<(ostream& out, const PrioHeap& obj) +{ + obj.print(out); + out << flush; + return out; +} + +#endif //PRIOHEAP_H diff --git a/src/mem/gems_common/RefCnt.hh b/src/mem/gems_common/RefCnt.hh new file mode 100644 index 000000000..fc1ddbae9 --- /dev/null +++ b/src/mem/gems_common/RefCnt.hh @@ -0,0 +1,162 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef REFCNT_H +#define REFCNT_H + +template +class RefCnt { +public: + // Constructors + RefCnt(); + RefCnt(const TYPE& data); + + // Destructor + ~RefCnt(); + + // Public Methods + const TYPE* ref() const { return m_data_ptr; } + TYPE* ref() { return m_data_ptr; } + TYPE* mod_ref() const { return m_data_ptr; } + void freeRef(); + void print(ostream& out) const; + + // Public copy constructor and assignment operator + RefCnt(const RefCnt& obj); + RefCnt& operator=(const RefCnt& obj); + +private: + // Private Methods + + // Data Members (m_ prefix) + TYPE* m_data_ptr; + // int* m_count_ptr; // Not used yet +}; + +// Output operator declaration +template +inline +ostream& operator<<(ostream& out, const RefCnt& obj); + +// ******************* Definitions ******************* + +// Constructors +template +inline +RefCnt::RefCnt() +{ + m_data_ptr = NULL; +} + +template +inline +RefCnt::RefCnt(const TYPE& data) +{ + m_data_ptr = data.clone(); + m_data_ptr->setRefCnt(1); +} + +template +inline +RefCnt::~RefCnt() +{ + freeRef(); +} + +template +inline +void RefCnt::freeRef() +{ + if (m_data_ptr != NULL) { + m_data_ptr->decRefCnt(); + if (m_data_ptr->getRefCnt() == 0) { + m_data_ptr->destroy(); + } + m_data_ptr = NULL; + } +} + +template +inline +void RefCnt::print(ostream& out) const +{ + if (m_data_ptr == NULL) { + out << "[RefCnt: Null]"; + } else { + out << "[RefCnt: "; + m_data_ptr->print(out); + out << "]"; + } +} + +// Copy constructor +template +inline +RefCnt::RefCnt(const RefCnt& obj) +{ + // m_data_ptr = obj.m_data_ptr->clone(); + m_data_ptr = obj.m_data_ptr; + + // Increment the reference count + if (m_data_ptr != NULL) { + m_data_ptr->incRefCnt(); + } +} + +// Assignment operator +template +inline +RefCnt& RefCnt::operator=(const RefCnt& obj) +{ + if (this == &obj) { + // If this is the case, do nothing + // assert(false); + } else { + freeRef(); + m_data_ptr = obj.m_data_ptr; + if (m_data_ptr != NULL) { + m_data_ptr->incRefCnt(); + } + } + return *this; +} + + +// Output operator definition +template +inline +ostream& operator<<(ostream& out, const RefCnt& obj) +{ + obj.print(out); + out << flush; + return out; +} + + + +#endif //REFCNT_H diff --git a/src/mem/gems_common/RefCnt_tester.cc b/src/mem/gems_common/RefCnt_tester.cc new file mode 100644 index 000000000..574f8fe3c --- /dev/null +++ b/src/mem/gems_common/RefCnt_tester.cc @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Code used to test the RefCnt class + */ + +#include "RefCnt.hh" +#include "RefCountable.hh" + +class Foo : public RefCountable { +public: + int m_data; + Foo* clone() const; +private: +}; + +Foo* Foo::clone() const +{ + Foo* temp_ptr; + temp_ptr = new Foo; + *temp_ptr = *this; + cout << "Cloned!" << endl; + return temp_ptr; +} + +void bar(RefCnt f) +{ + cout << f.ref()->m_data << endl; +} + +Foo f2; + +int main() +{ + Foo f; + f.m_data = 2; + + { + RefCnt a(f); + + f.m_data = 3; + cout << a.ref()->m_data << endl; + cout << f.m_data << endl; + f2 = f; + } + + bar(f2); + + return 0; +} + + diff --git a/src/mem/gems_common/RefCountable.hh b/src/mem/gems_common/RefCountable.hh new file mode 100644 index 000000000..88aba07e6 --- /dev/null +++ b/src/mem/gems_common/RefCountable.hh @@ -0,0 +1,59 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Virtual base class for things that can be reference counted + */ + +#ifndef REFCOUNTABLE_H +#define REFCOUNTABLE_H + +#include "RefCnt.hh" + +class RefCountable { +public: + // Public Methods + + RefCountable() { m_refcnt = 0; } + + // These are used by the RefCnt class to hold the reference count + // for the object. These really should be private and accessed by a + // friend class, but I can't figure out how to make a template class + // a friend. + void incRefCnt() { m_refcnt++; } + void decRefCnt() { m_refcnt--; } + int getRefCnt() const { return m_refcnt; } + void setRefCnt(int cnt) { m_refcnt = cnt; } +private: + // Private Methods + + // Data Members (m_ prefix) + int m_refcnt; +}; + +#endif //REFCOUNTABLE_H diff --git a/src/mem/gems_common/Vector.hh b/src/mem/gems_common/Vector.hh new file mode 100644 index 000000000..744dc698c --- /dev/null +++ b/src/mem/gems_common/Vector.hh @@ -0,0 +1,334 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Description: The Vector class is a generic container which acts + * much like an array. The Vector class handles dynamic sizing and + * resizing as well as performing bounds checking on each access. An + * "insertAtBottom" operation is supported to allow adding elements to + * the Vector much like you would add new elements to a linked list or + * queue. + */ + +#ifndef VECTOR_H +#define VECTOR_H + +#include "std-includes.hh" + +template +class Vector +{ +public: + Vector(); + explicit Vector(int initial_size); // Construct with an initial max size + ~Vector(); + const TYPE& ref(int index) const; // Get an element of the vector + TYPE& ref(int index); // Get an element of the vector + void clear(); // remove all elements of the vector + void sortVector(); // sort all elements using < operator + int size() const { return m_size; } + void setSize(int new_size); // Increase size, reallocates memory as needed + void expand(int num) { setSize(m_size+num); } // Increase size by num + void increaseSize(int new_size, const TYPE& reset); // and adds num of slots at the bottom set to reset value + void insertAtTop(const TYPE& element); // Increase size by one and set last element + // FIXME - WARNING: insertAtTop is currently O(n) and needs to be fixed + void insertAtBottom(const TYPE& element); // Increase size by one and set last element + TYPE sum() const; // Uses the += operator to sum all the elements of the vector + void deletePointers(); // Walks the Vector calling delete on all + // elements and sets them to NULL, can only + // be used when the TYPE is a pointer type. + void removeFromTop(int num); // removes elements from top + void print(ostream& out) const; + + + // Array Reference operator overloading + const TYPE& operator[](int index) const { return ref(index); } + TYPE& operator[](int index) { return ref(index); } + + // Public copy constructor and assignment operator + Vector(const Vector& vec); + Vector& operator=(const Vector& vec); +private: + + void grow(int new_max_size); // Expands vector to new_max_size + + // Data members + TYPE* m_vec; // Array to hold the elements + int m_size; // Number of elements in use + int m_max_size; // Size of allocated array +}; + +template +ostream& operator<<(ostream& out, const Vector& vec); + +// ********************* + +template +Vector::Vector() +{ + m_size = 0; + m_max_size = 0; + m_vec = NULL; +} + +template +Vector::Vector(int initial_size) +{ + m_size = 0; + m_max_size = initial_size; + m_vec = NULL; + grow(initial_size); +} + +template +Vector::~Vector() +{ + delete [] m_vec; +} + +template +const TYPE& Vector::ref(int index) const +{ +#ifndef NO_VECTOR_BOUNDS_CHECKS + assert(m_size != 0); + assert(index < m_size); + assert(index >= 0); +#endif + return m_vec[index]; +} + +template +TYPE& Vector::ref(int index) +{ +#ifndef NO_VECTOR_BOUNDS_CHECKS + assert(m_size != 0); + assert(index < m_size); + assert(index >= 0); +#endif + return m_vec[index]; +} + + +template +void Vector::setSize(int new_size) +{ + // FIXME - this should also decrease or shrink the size of the array at some point. + if (new_size > m_max_size) { + grow(max((m_max_size+1)*2, new_size)); + } + m_size = new_size; +#ifndef NO_VECTOR_BOUNDS_CHECKS + assert(m_size <= m_max_size); + assert(m_size >= 0); +#endif +} + +template +inline +void Vector::increaseSize(int new_size, const TYPE& reset) +{ + assert(new_size >= m_size); + if (new_size >= m_max_size) { + grow(max((m_max_size+1)*2, new_size)); + } + int old_size = m_size; + m_size = new_size; + for (int j = old_size; j < m_size; j++) { + ref(j) = reset; + } + +#ifndef NO_VECTOR_BOUNDS_CHECKS + assert(m_size <= m_max_size); + assert(m_size >= 0); +#endif +} + +template +inline +void Vector::clear() +{ + m_size = 0; + m_max_size = 0; + delete [] m_vec; + m_vec = NULL; +} + +template +inline +void Vector::sortVector() +{ + sort(&m_vec[0], &m_vec[m_size]); +} + +template +inline +void Vector::insertAtTop(const TYPE& element) +{ + setSize(m_size+1); + for (int i = m_size-1; i >= 1; i--) { + ref(i) = ref(i-1); + } + ref(0) = element; +} + +template +inline +void Vector::removeFromTop(int num) +{ + if (num > m_size) { + num = m_size; + } + for (int i = 0; i < m_size - num; i++) { + m_vec[i] = m_vec[i+num]; + } + m_size = m_size - num; + +} + +template +void Vector::insertAtBottom(const TYPE& element) +{ + setSize(m_size+1); + ref(m_size-1) = element; +} + +template +TYPE Vector::sum() const +{ + assert(m_size > 0); + TYPE sum = ref(0); + for(int i=1; i +void Vector::deletePointers() +{ + assert(m_size >= 0); + for(int i=0; i +void Vector::print(ostream& out) const +{ + out << "[ "; + for(int i=0; i +Vector::Vector(const Vector& vec) +{ + // Setup the new memory + m_size = vec.m_size; + m_max_size = vec.m_max_size; + if (m_max_size != 0) { + m_vec = new TYPE[m_max_size]; + assert(m_vec != NULL); + } else { + m_vec = NULL; + } + + // Copy the elements of the array + for(int i=0; i +Vector& Vector::operator=(const Vector& vec) +{ + if (this == &vec) { + // assert(0); + } else { + // Free the old memory + delete [] m_vec; + + // Setup the new memory + m_size = vec.m_size; + m_max_size = vec.m_max_size; + + if (m_max_size != 0) { + m_vec = new TYPE[m_max_size]; + assert(m_vec != NULL); + } else { + m_vec = NULL; + } + + // Copy the elements of the array + for(int i=0; i +void Vector::grow(int new_max_size) +{ + TYPE* temp_vec; + m_max_size = new_max_size; + if (new_max_size != 0) { + temp_vec = new TYPE[new_max_size]; + assert(temp_vec != NULL); + } else { + temp_vec = NULL; + } + + // Copy the elements of the array + for(int i=0; i +ostream& operator<<(ostream& out, const Vector& vec) +{ + vec.print(out); + return out; +} + +#endif //VECTOR_H diff --git a/src/mem/gems_common/calc_host.sh b/src/mem/gems_common/calc_host.sh new file mode 100755 index 000000000..f7a6116c1 --- /dev/null +++ b/src/mem/gems_common/calc_host.sh @@ -0,0 +1,38 @@ +#!/bin/csh -f + +set OS=`uname -s` +set HOST_ARCH=`uname -m` + +switch ($OS) + case Linux: + set OS_PART=linux + breaksw + case SunOS: + set OS_PART=sol8-64 + breaksw + case OSF1: + set OS_PART=tru64-gcc + breaksw + default: + set OS_PART=`echo $OS | sed 's/ /-/g'` +endsw + +switch ($HOST_ARCH) + case i586: + set ARCH=x86 + breaksw + case i686: + set ARCH=x86 + breaksw + case x86_64: + set ARCH=amd64 + breaksw + case sun4u: + set ARCH=v9 + breaksw + default: + set ARCH=`echo $HOST_ARCH | sed 's/ /-/g'` +endsw + +echo $ARCH-$OS_PART + diff --git a/src/mem/gems_common/ioutil/attrlex.ll b/src/mem/gems_common/ioutil/attrlex.ll new file mode 100644 index 000000000..293350b23 --- /dev/null +++ b/src/mem/gems_common/ioutil/attrlex.ll @@ -0,0 +1,229 @@ +/* + Copyright (C) 1999-2005 by Mark D. Hill and David A. Wood for the + Wisconsin Multifacet Project. Contact: gems@cs.wisc.edu + http://www.cs.wisc.edu/gems/ + + -------------------------------------------------------------------- + + This file a component of the Multifacet GEMS (General + Execution-driven Multiprocessor Simulator) software toolset + originally developed at the University of Wisconsin-Madison. + + Ruby was originally developed primarily by Milo Martin and Daniel + Sorin with contributions from Ross Dickson, Carl Mauer, and Manoj + Plakal. + + SLICC was originally developed by Milo Martin with substantial + contributions from Daniel Sorin. + + Opal was originally developed by Carl Mauer based upon code by + Craig Zilles. + + Substantial further development of Multifacet GEMS at the + University of Wisconsin was performed by Alaa Alameldeen, Brad + Beckmann, Ross Dickson, Pacia Harper, Milo Martin, Michael Marty, + Carl Mauer, Kevin Moore, Manoj Plakal, Daniel Sorin, Min Xu, and + Luke Yen. + + -------------------------------------------------------------------- + + If your use of this software contributes to a published paper, we + request that you (1) cite our summary paper that appears on our + website (http://www.cs.wisc.edu/gems/) and (2) e-mail a citation + for your published paper to gems@cs.wisc.edu. + + If you redistribute derivatives of this software, we request that + you notify us and either (1) ask people to register with us at our + website (http://www.cs.wisc.edu/gems/) or (2) collect registration + information and periodically send it to us. + + -------------------------------------------------------------------- + + Multifacet GEMS is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + Multifacet GEMS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Multifacet GEMS; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA + + The GNU General Public License is contained in the file LICENSE. + +### END HEADER ### +*/ + + +%option noyywrap + +ALPHADIGIT [^\:\,\(\)\n\t\(\) \0\#] +HEXDIGIT [0-9a-fA-Fx] +NEWLINE [\n] +WHITESPACE [ \t] + +%{ + +#ifdef IS_RUBY +#include "Global.hh" +#endif + +using namespace std; +#include +#include +#include + +// Maurice +// extern "C" { +// #include "simics/api.h" +// }; + +#include "FakeSimicsDataTypes.hh" + +// CM: simics 1.6.5 API redefines fwrite, much to my chagrin +#undef fwrite +#undef printf +#include "attrparse.h" + +#define MAX_INCLUDE_DEPTH 10 + +/** global result of parsing file */ +extern attr_value_t g_attr_map; + +extern int atparse(void); + +static int linenum=1; /* the current line number */ +static int colnum=1; /* the current column number */ +static YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; +static int include_stack_ptr = 0; +static char g_relative_include_path[256]; + + +// forward declaration of aterror +void aterror(const char *msg); +%} + +%x SLASHCOMMENT INCLUDE + +%% + +%{ /* PATTERNS FOR STRING TOKENS */ +%} + +"//".*[\n] { linenum++; colnum=1; } /* C++ style comments */ + +\#include { colnum+=yyleng; BEGIN(INCLUDE); } +{WHITESPACE}* { colnum+=yyleng; } +[^ \t\n]+ { + // should really be FILEIO_MAX_FILENAME or MAX_NAME + char str[256]; + if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) + { + ERROR_OUT( "Includes nested too deeply" ); + exit( 1 ); + } + include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER; + + yyin = fopen( yytext, "r" ); + if ( ! yyin ) { + sprintf( str, "%s%s", g_relative_include_path, yytext ); + yyin = fopen( str, "r" ); + } + if ( ! yyin ) { + sprintf( str, "%s%s%s", g_relative_include_path, "config/", yytext ); + yyin = fopen( str, "r" ); + } + if ( ! yyin ) { + ERROR_OUT("unable to open included file: %s or %s\n", yytext, str); + aterror("file open error.\n"); + } + yy_switch_to_buffer(yy_create_buffer( yyin, YY_BUF_SIZE )); + BEGIN(INITIAL); + } +<> { + if ( --include_stack_ptr < 0 ) { + yyterminate(); + } else { + yy_delete_buffer( YY_CURRENT_BUFFER ); + fclose(yyin); + yy_switch_to_buffer(include_stack[include_stack_ptr] ); + } + } + +\( { colnum+=yyleng; return (LPAREN); } +\) { colnum+=yyleng; return (RPAREN); } +\: { colnum+=yyleng; return (':'); } +\, { colnum+=yyleng; return (','); } +{HEXDIGIT}+ { + colnum+=yyleng; + attr_value_t *val = (attr_value_t *) + malloc( sizeof(attr_value_t) ); + memset( val, 0, sizeof(attr_value_t) ); + atlval.attrval = val; + val->kind = Sim_Val_Integer; + val->u.integer = strtoull( yytext, NULL, 0 ); + return (INTEGER); } +{ALPHADIGIT}+ { + colnum+=yyleng; + attr_value_t *val = (attr_value_t *) + malloc( sizeof(attr_value_t) ); + memset( val, 0, sizeof(attr_value_t) ); + atlval.attrval = val; + val->kind = Sim_Val_String; + val->u.string = strdup(yytext); + return (STRING); } + +%{ /* OTHER PATTERNS */ +%} + +{WHITESPACE}+ {colnum += yyleng;} +{NEWLINE} {linenum++; colnum = 1;} + +%% + +extern "C" void parseInitialize( void ) +{ + // since no global variables are set in simics, we must do it manually + // this is also necessary now that the parser can be used more than once. + // (it is used to parse the defaults, and can be used after that) + linenum = 1; + colnum = 1; + include_stack_ptr = 0; +} + +extern "C" int parseAttrFile( FILE *inputFile, const char *relative_include_path, attr_value_t *myTable ) +{ + parseInitialize(); + strncpy( g_relative_include_path, relative_include_path, 255 ); + + int result; + yyin = inputFile; + YY_BUFFER_STATE scan_state = yy_create_buffer( yyin, YY_BUF_SIZE ); + yy_switch_to_buffer( scan_state ); + result = atparse(); + *myTable = g_attr_map; + yy_delete_buffer( scan_state ); + return (result); +} + +extern "C" int parseAttrString( const char *str, attr_value_t *myTable ) +{ + parseInitialize(); + + int result; + YY_BUFFER_STATE scan_state = yy_scan_string( str ); + result = atparse(); + *myTable = g_attr_map; + yy_delete_buffer( scan_state ); + return (result); +} + +extern void aterror(const char *msg) +{ + ERROR_OUT("%d:%d: ERROR while parsing config file%s\n", linenum, colnum, msg ); +} + diff --git a/src/mem/gems_common/ioutil/attrparse.yy b/src/mem/gems_common/ioutil/attrparse.yy new file mode 100644 index 000000000..8a0190e06 --- /dev/null +++ b/src/mem/gems_common/ioutil/attrparse.yy @@ -0,0 +1,232 @@ +/* + Copyright (C) 1999-2005 by Mark D. Hill and David A. Wood for the + Wisconsin Multifacet Project. Contact: gems@cs.wisc.edu + http://www.cs.wisc.edu/gems/ + + -------------------------------------------------------------------- + + This file a component of the Multifacet GEMS (General + Execution-driven Multiprocessor Simulator) software toolset + originally developed at the University of Wisconsin-Madison. + + Ruby was originally developed primarily by Milo Martin and Daniel + Sorin with contributions from Ross Dickson, Carl Mauer, and Manoj + Plakal. + + SLICC was originally developed by Milo Martin with substantial + contributions from Daniel Sorin. + + Opal was originally developed by Carl Mauer based upon code by + Craig Zilles. + + Substantial further development of Multifacet GEMS at the + University of Wisconsin was performed by Alaa Alameldeen, Brad + Beckmann, Ross Dickson, Pacia Harper, Milo Martin, Michael Marty, + Carl Mauer, Kevin Moore, Manoj Plakal, Daniel Sorin, Min Xu, and + Luke Yen. + + -------------------------------------------------------------------- + + If your use of this software contributes to a published paper, we + request that you (1) cite our summary paper that appears on our + website (http://www.cs.wisc.edu/gems/) and (2) e-mail a citation + for your published paper to gems@cs.wisc.edu. + + If you redistribute derivatives of this software, we request that + you notify us and either (1) ask people to register with us at our + website (http://www.cs.wisc.edu/gems/) or (2) collect registration + information and periodically send it to us. + + -------------------------------------------------------------------- + + Multifacet GEMS is free software; you can redistribute it and/or + modify it under the terms of version 2 of the GNU General Public + License as published by the Free Software Foundation. + + Multifacet GEMS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with the Multifacet GEMS; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307, USA + + The GNU General Public License is contained in the file LICENSE. + +### END HEADER ### +*/ + + + +%{ +/*------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------*/ + +#ifdef IS_RUBY +#include "Global.hh" +#endif + +using namespace std; +#include +#include +#include + +// Maurice +// extern "C" { +// #include "simics/api.h" +// }; + +#include "FakeSimicsDataTypes.hh" + +#include "confio.hh" + +// CM FIX: if I wasn't working on a paper: I'd re-write the grammer to +// be left (or right) recursive, which ever is more efficient +// This only affects extremely large cache sizes / BP sizes on read in. +#define YYMAXDEPTH 100000 + +extern char* attext; + +extern void aterror(const char *); +extern int atlex(); +attr_value_t g_attr_map; + +extern void fprintAttr( FILE *fp, attr_value_t attr ); + +%} + +/*------------------------------------------------------------------------*/ +/* Union declarations */ +/*------------------------------------------------------------------------*/ +// The types of the tokens and nonterminals +%union { + attr_value_t *attrval; +}; + +%token STRING INTEGER +%token MY_END LPAREN RPAREN + +%type confmapping confpair attributes attrlist +%% + +conffile : confmapping +{ + g_attr_map = *($1); + free( $1 ) +} + +confmapping : confmapping confpair +{ + attr_value_t *newattr = mallocAttribute(1); + newattr->kind = Sim_Val_List; + if ( $1 == NULL ) { + newattr->u.list.size = 1; + newattr->u.list.vector = $2; + } else { + // add the latest mapping to the return mapping + uint32 newsize = $1->u.list.size + 1; + attr_value_t *vector = mallocAttribute( newsize ); + newattr->u.list.size = newsize; + newattr->u.list.vector = vector; + for (uint32 i = 0; i < newsize - 1; i++) { + vector[i] = $1->u.list.vector[i]; + } + vector[newsize - 1] = *($2); + free( $1->u.list.vector ); + free( $1 ); + free( $2 ); + } + $$ = newattr; +} + | // nothing +{ + $$ = NULL; +} + +confpair : STRING ':' attributes +{ + attr_value_t *newattr = mallocAttribute(1); + newattr->kind = Sim_Val_List; + newattr->u.list.size = 2; + newattr->u.list.vector = mallocAttribute(2); + newattr->u.list.vector[0] = *($1); + newattr->u.list.vector[1] = *($3); + free( $1 ); + free( $3 ); + $$ = newattr; +} + +attributes : INTEGER +{ + $$ = $1; +} + | STRING +{ + $$ = $1; +} + | LPAREN attrlist RPAREN +{ + attr_value_t *newattr = mallocAttribute(1); + newattr->kind = Sim_Val_List; + if ( $2->kind != CONF_ATTR_SINGLE && + $2->kind != CONF_ATTR_PAIR ) { + newattr->u.list.size = 1; + newattr->u.list.vector = $2; + } else { + newattr->u.list.size = $2->u.list.size; + newattr->u.list.vector = mallocAttribute(newattr->u.list.size); + attr_value_t *curattr = $2; + uint32 i = 0; + while ( i < newattr->u.list.size ) { + if (curattr->kind == CONF_ATTR_SINGLE) { + newattr->u.list.vector[i] = curattr->u.list.vector[0]; + i++; + + curattr = NULL; + } else if (curattr->kind == CONF_ATTR_PAIR) { + newattr->u.list.vector[i] = curattr->u.list.vector[0]; + i++; + if ( i < newattr->u.list.size ) + curattr = &(curattr->u.list.vector[1]); + else + curattr = NULL; + + } else { + ERROR_OUT("error: unknown kind in pair: %d\n", curattr->kind); + ASSERT(0); + } + } + // FIX memory leak: around 600 KB + // freeAttribute( $2 ); // with gcc-3.4 this free call tries to free memory from the stack + } + $$ = newattr; +} + +attrlist : attributes +{ + attr_value_t *newattr = mallocAttribute(1); + newattr->kind = CONF_ATTR_SINGLE; + newattr->u.list.size = 1; + newattr->u.list.vector = $1; + $$ = newattr; +} + | attributes ',' attrlist +{ + // allocate the pair ( x , y ) attribute + attr_value_t *newattr = mallocAttribute(1); + int newsize = $3->u.list.size + 1; + attr_value_t *vector = mallocAttribute(2); + newattr->kind = CONF_ATTR_PAIR; + newattr->u.list.size = newsize; + newattr->u.list.vector = vector; + + // assign the LH attribute + vector[0] = *($1); + vector[1] = *($3); + free( $1 ); + free( $3 ); + $$ = newattr; +} diff --git a/src/mem/gems_common/ioutil/confio.cc b/src/mem/gems_common/ioutil/confio.cc new file mode 100644 index 000000000..68d44197a --- /dev/null +++ b/src/mem/gems_common/ioutil/confio.cc @@ -0,0 +1,456 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * saves configuration information for later runs + */ + +/*------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------*/ + +#ifdef IS_OPAL +#include "hfa.hh" +#endif + +#ifdef IS_RUBY +#include "Global.hh" +#define SIM_HALT ASSERT(0) +#endif + +#ifdef IS_TOURMALINE +#include "Tourmaline_Global.hh" +#endif + +using namespace std; +#include +#include +#include + +// Maurice +// extern "C" { +// #include "global.hh" +// #include "simics/api.hh" +// +// #ifdef SIMICS22X +// #include "sparc_api.hh" +// #endif +// #ifdef SIMICS30 +// #ifdef SPARC +// #include "sparc.hh" +// #else +// #include "x86.hh" +// #endif +// #endif +// }; + +#include "FakeSimicsDataTypes.hh" + +#include "confio.hh" + +/*------------------------------------------------------------------------*/ +/* Macro declarations */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Variable declarations */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Forward declarations */ +/*------------------------------------------------------------------------*/ + +// C++ Template: explicit instantiation +template class map; + +// These functions are defined in parser/attrlex.l +extern "C" int parseAttrFile( FILE *inputFile, const char *relative_include_path, attr_value_t *myTable ); +extern "C" int parseAttrString( const char *str, attr_value_t *myTable ); + +/*------------------------------------------------------------------------*/ +/* Constructor(s) / destructor */ +/*------------------------------------------------------------------------*/ + +//************************************************************************** +confio_t::confio_t( ) +{ + m_verbose = false; +} + +//************************************************************************** +confio_t::~confio_t( ) +{ + ConfTable::iterator iter; + + for ( iter = m_table.begin(); iter != m_table.end(); iter++ ) { + confnode_t *cfnode = (*iter).second; + free( cfnode ); + } +} + +/*------------------------------------------------------------------------*/ +/* Public methods */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Accessor(s) / mutator(s) */ +/*------------------------------------------------------------------------*/ + +//************************************************************************** +int confio_t::register_attribute( const char *name, + get_confio_t get_attr, void *get_attr_data, + set_confio_t set_attr, void *set_attr_data ) +{ + confnode_t *newnode; + if ( m_table.find(name) == m_table.end() ) { + if (m_verbose) + DEBUG_OUT(" registering checkpoint attribute: \"%s\"\n", name); + + // add a new entry to the table + newnode = (confnode_t *) malloc( sizeof( confnode_t ) ); + newnode->get_attr = get_attr; + newnode->set_attr = set_attr; + newnode->set_attr_data = set_attr_data; + newnode->get_attr_data = get_attr_data; + newnode->attr_is_set = false; + string key(name); + m_table[key] = newnode; + } else { + ERROR_OUT(" warning: confio: adding existing conf node: %s\n", name); + } + return 0; +} + +//************************************************************************** +void fprintAttr( FILE *fp, attr_value_t attr ) +{ + switch (attr.kind) { + case Sim_Val_Invalid: + fprintf(fp, "invalid"); + break; + + case Sim_Val_String: + fprintf(fp, "%s", attr.u.string); + break; + + case Sim_Val_Integer: + fprintf(fp, "0x%llx", attr.u.integer); + break; + + case Sim_Val_Floating: + fprintf(fp, "0x%llx", attr.u.integer); + break; + + case Sim_Val_List: + fprintf(fp, "("); + for (uint32 i = 0; i < attr.u.list.size; i++) { + fprintAttr(fp, attr.u.list.vector[i]); + if (i != attr.u.list.size -1) { + fprintf(fp, ", "); + } + } + fprintf(fp, ")"); + break; + + default: + ERROR_OUT("fprintAttr: unknown/unimplemented attribute %d\n", attr.kind); + } +} + +//************************************************************************** +void freeAttribute( attr_value_t *attr ) +{ + switch (attr->kind) { + case Sim_Val_Invalid: + break; + + case Sim_Val_String: + free( (char *) attr->u.string ); + break; + + case Sim_Val_Integer: + break; + + case Sim_Val_Floating: + break; + + case Sim_Val_List: + for (uint32 i = 0; i < attr->u.list.size; i++) { + freeAttribute( &(attr->u.list.vector[i]) ); + } + free( attr->u.list.vector ); + break; + + default: + ERROR_OUT("freeAttr: unknown/unimplemented attribute %d\n", attr->kind); + } +} + +/** + * Allocates, and initializes a attribute value. + * @param number The number of values to allocate. + * @return A pointer to the newly allocated structure. + */ +//************************************************************************** +attr_value_t *mallocAttribute( uint32 number ) +{ + attr_value_t *newattr = (attr_value_t *) malloc( number * + sizeof(attr_value_t) ); + if ( newattr == NULL ) { + ERROR_OUT( "confio: mallocAttribute: out of memory\n" ); + exit(1); + } + memset( newattr, 0, number*sizeof(attr_value_t) ); + return (newattr); +} + + +//************************************************************************** +void fprintMap( FILE *fp, attr_value_t attr ) +{ + attr_value_t name; + attr_value_t value; + + if (attr.kind != Sim_Val_List) + return; + + for (int i = 0; i < attr.u.list.size; i++) { + + if (attr.u.list.vector[i].kind != Sim_Val_List || + attr.u.list.vector[i].u.list.size != 2) + return; + + name = attr.u.list.vector[i].u.list.vector[0]; + value = attr.u.list.vector[i].u.list.vector[1]; + fprintf( fp, " %s: ", name.u.string); + fprintAttr( fp, value ); + fprintf( fp, "\n"); + } +} + +/** + * write a configuration file: e.g. save state + */ +//************************************************************************** +int confio_t::writeConfiguration( const char *outputFilename ) +{ + FILE *fp; + ConfTable::iterator iter; + confnode_t *cfnode; + attr_value_t attr; + + memset( &attr, 0, sizeof(attr_value_t) ); + if ( outputFilename == NULL ) { + fp = stdout; + } else { + fp = fopen( outputFilename, "w" ); + if ( fp == NULL ) { + ERROR_OUT("error: writeConfiguration: unable to open file %s\n", + outputFilename ); + return (-1); + } + } + + for (iter = m_table.begin(); iter != m_table.end(); iter++) { + fprintf( fp, " %s: ", (*iter).first.c_str() ); + cfnode = (*iter).second; + attr = (*(cfnode->get_attr))( cfnode->get_attr_data, NULL ); + fprintAttr( fp, attr ); + fprintf(fp, "\n"); + } + + if ( outputFilename != NULL ) { + // wrote to a file: now close it! + fclose( fp ); + } + return 0; +} + +/** + * read state from an existing configuration file + */ +//************************************************************************** +int confio_t::readConfiguration( const char *inputFilename, + const char *relativeIncludePath ) +{ + // parse the input stream + FILE *fp; + char relativeFilename[256]; + + fp = fopen( inputFilename, "r" ); + if ( fp == NULL ) { + sprintf( relativeFilename, "%s%s", relativeIncludePath, inputFilename ); + fp = fopen( relativeFilename, "r" ); + } + if ( fp == NULL ) { + sprintf( relativeFilename, "%s%s%s", relativeIncludePath, "config/", + inputFilename ); + fp = fopen( relativeFilename, "r" ); + } + if ( fp == NULL ) { + ERROR_OUT("error: readConfiguration: unable to open file %s or %s\n", + inputFilename, relativeFilename); + return (-1); + } + + attr_value_t *myattr = mallocAttribute(1); +#ifdef MODINIT_VERBOSE + DEBUG_OUT("confio_t() parsing conf file\n"); +#endif + int rc = parseAttrFile( fp, relativeIncludePath, myattr ); +#ifdef MODINIT_VERBOSE + DEBUG_OUT("confio_t() parse completed\n"); +#endif + if ( rc == 0 ) { + applyConfiguration( myattr ); + freeAttribute( myattr ); + free(myattr); + } + + fclose( fp ); +#ifdef MODINIT_VERBOSE + DEBUG_OUT("confio_t() completed\n"); +#endif + return (rc); +} + +/** + * read state from a configuration string + */ +//************************************************************************** +int confio_t::readConfigurationString( const char *inputBuffer ) +{ + if ( inputBuffer == NULL ) { + ERROR_OUT( "error: readConfiguration: NULL inputBuffer\n" ); + return (-1); + } + + attr_value_t *myattr = mallocAttribute(1); +#ifdef MODINIT_VERBOSE + DEBUG_OUT("confio_t() parsing conf string\n"); +#endif + + int rc = parseAttrString( inputBuffer, myattr ); +#ifdef MODINIT_VERBOSE + DEBUG_OUT("confio_t() parse completed\n"); +#endif + if ( rc == 0 ) { + applyConfiguration( myattr ); + freeAttribute( myattr ); + free(myattr); + } + return (rc); +} + +//************************************************************************** +void confio_t::checkInitialization( void ) +{ + ConfTable::iterator iter; + confnode_t *cfnode; + + for (iter = m_table.begin(); iter != m_table.end(); iter++) { + cfnode = (*iter).second; + if ( !cfnode->attr_is_set ) { + DEBUG_OUT(" warning: %s is not set in configuration file.\n", (*iter).first.c_str() ); + } + } +} + +/*------------------------------------------------------------------------*/ +/* Private methods */ +/*------------------------------------------------------------------------*/ + +//************************************************************************** +int confio_t::applyConfiguration( attr_value_t *attr ) +{ + confnode_t *cfnode; + attr_value_t name; + attr_value_t value; + set_error_t seterr; + +#ifdef MODINIT_VERBOSE + DEBUG_OUT("confio_t() data in memory\n"); + fprintMap( stdout, *attr ); +#endif + + // apply the configuration the the m_table + if (attr->kind != Sim_Val_List || + attr->u.list.size <= 0) { + ERROR_OUT("readconfiguration: internal error #1\n"); + return -1; + } + + for (int i = 0; i < attr->u.list.size; i++) { + + if (attr->u.list.vector[i].kind != Sim_Val_List || + attr->u.list.vector[i].u.list.size != 2) { + ERROR_OUT("readconfiguration: illegal configuration kind:%d size:%lld\n", + attr->u.list.vector[i].kind, + attr->u.list.vector[i].u.list.size); + continue; + } + + name = attr->u.list.vector[i].u.list.vector[0]; + value = attr->u.list.vector[i].u.list.vector[1]; + string newstr((char *) name.u.string); + if ( m_table.find(newstr) != m_table.end()) { + + // set the value found in the configuration + cfnode = m_table[newstr]; + seterr = (*cfnode->set_attr)( cfnode->set_attr_data, NULL, + &(value) ); + if ( seterr == Sim_Set_Ok ) { + cfnode->attr_is_set = true; + if (m_verbose) + DEBUG_OUT("configuration set for: %s\n", name.u.string); + } else { + ERROR_OUT("error: \"%s\" unable to set value: %d\n", + name.u.string, (int) seterr); + } + } else { + ERROR_OUT("error: \"%s\" not found. unable to set value.\n", + name.u.string); + } + } + return 0; +} + +/*------------------------------------------------------------------------*/ +/* Static methods */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Global functions */ +/*------------------------------------------------------------------------*/ + + +/** [Memo]. + * [Internal Documentation] + */ +//************************************************************************** + diff --git a/src/mem/gems_common/ioutil/confio.hh b/src/mem/gems_common/ioutil/confio.hh new file mode 100644 index 000000000..143c4da8b --- /dev/null +++ b/src/mem/gems_common/ioutil/confio.hh @@ -0,0 +1,192 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _CONFIO_H_ +#define _CONFIO_H_ + +/*------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------*/ + +#include "FakeSimicsDataTypes.hh" + +/*------------------------------------------------------------------------*/ +/* Macro declarations */ +/*------------------------------------------------------------------------*/ + +/// constant for attribute parsing: a (x) single value +const attr_kind_t CONF_ATTR_SINGLE = (attr_kind_t) (Sim_Val_Object + 1); +/// constant for attribute parsing: a (x,y) pair of values +const attr_kind_t CONF_ATTR_PAIR = (attr_kind_t) (Sim_Val_Object + 2); + +/*------------------------------------------------------------------------*/ +/* Class declaration(s) */ +/*------------------------------------------------------------------------*/ + +/* + * Functions for modifying the micro-architectural configuation of + * a class. + */ + +/// function for getting the configuration value +typedef attr_value_t (*get_confio_t)( void *ptr, void *obj ); +/// function for setting the configuration value +typedef set_error_t (*set_confio_t)( void *ptr, void *obj, + attr_value_t *value ); + +/// a struture containing the functional callbacks for each conf node +typedef struct confnode { + get_confio_t get_attr; + set_confio_t set_attr; + void *set_attr_data; + void *get_attr_data; + bool attr_is_set; +} confnode_t; + +/// a mapping from a string to a configuration structure +typedef map ConfTable; + +/** +* Configuration state saving: allows the user to save the micro-architectural +* state in a text file for later runs. This file is also used to set +* globals during simulation. +* +* @author cmauer +* @version $Id$ +*/ +class confio_t { + +public: + + + /** + * @name Constructor(s) / destructor + */ + //@{ + + /** + * Constructor: creates object + */ + confio_t(); + + /** + * Destructor: frees object. + */ + ~confio_t(); + //@} + + /** + * @name Methods + */ + //@{ + //@} + + /** + * @name Accessor(s) / mutator(s) + */ + //@{ + /** + * register a configuration variable with the configuration manager. + * @param get_attr A function to get the attribute value + * @param get_attr_data Void pointer, available to get_attr + * @param set_attr A function to set the attribute value + * @param set_attr_data Void pointer, available to set_attr + * @return [Description of return value] + */ + int register_attribute( const char *name, + get_confio_t get_attr, void *get_attr_data, + set_confio_t set_attr, void *set_attr_data ); + + /** + * Set verbosity of the configuration + * @param verbose True causes more info to be printed out, False doesn't + */ + void setVerbosity( bool verbose ) { + m_verbose = verbose; + } + + /** + * write a configuration file: e.g. save state + */ + int writeConfiguration( const char *outputFilename ); + + /** + * read state from an existing configuration file + * @param inputFilename The file to read + * @param relativeIncludePath The path to search on 'include' statements + */ + int readConfiguration( const char *inputFilename, + const char *relativeIncludePath ); + + /** + * read state from a string + */ + int readConfigurationString( const char *inputBuffer ); + + /** + * check that each registered configuration is set (reports a warning if + * they are not.) + */ + void checkInitialization( void ); + //@} + +private: + /** + * Apply an attribute list to the configuration table + */ + int applyConfiguration( attr_value_t *attr ); + + /// configuration table: contains a map from a string -> conf node + ConfTable m_table; + + /// if false, nothing is printed under normal operation + bool m_verbose; +}; + +/*------------------------------------------------------------------------*/ +/* Global variables */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Global functions */ +/*------------------------------------------------------------------------*/ + +/** + * Allocates an array of attributes. + */ +attr_value_t *mallocAttribute( uint32 number ); + +/** + * Walks an attribute tree, freeing all memory under attr. Does not free + * attr itself. + */ +void freeAttribute( attr_value_t *attr ); + +#endif /* _CONFIO_H_ */ + + diff --git a/src/mem/gems_common/ioutil/embedtext.py b/src/mem/gems_common/ioutil/embedtext.py new file mode 100644 index 000000000..64e1c97f3 --- /dev/null +++ b/src/mem/gems_common/ioutil/embedtext.py @@ -0,0 +1,54 @@ + +import sys + +#--------------------------------------------------------------------------- + +class embedText: + """ + embedText converts a text file into a file that can be embedded in C + using an #include statement, that defines a \"const char *\" pointing + to the same text. + + This is useful to embed scripts and configuration files in object files. + """ + def __init__(self, filename): + self.filename = filename + self.escape = [ "\'", "\"", "\\", "\?" ] + + def write(self, outputfile, varname): + # reads the text file in, line by line, converting it to a C string + fin = open( self.filename, 'r' ) + fout= open( outputfile, 'w' ) + fout.write("static const char *%s =\n" % varname); + l = " " + while l != "": + l = fin.readline() + + # add escape sequences for the characters in escape + fout.write("\"") + for char in l: + if char == "\n": + break + if char in self.escape: + fout.write( "\\" ) + fout.write( char ) + else: + fout.write( char ) + fout.write("\\n\"\n"); + fout.write(";\n"); + fin.close() + fout.close() + +#--------------------------------------------------------------------------- + +if __name__ == "__main__": + if len(sys.argv) != 4: + print len(sys.argv) + print "usage:", sys.argv[0], " input-file output-file varname" + sys.exit(1) + inputfile = sys.argv[1] + outputfile = sys.argv[2] + varname = sys.argv[3] + print "generating embedded text file: %s from %s\n" % (outputfile, inputfile) + inc = embedText( inputfile ) + inc.write( outputfile, varname ) diff --git a/src/mem/gems_common/ioutil/initvar.cc b/src/mem/gems_common/ioutil/initvar.cc new file mode 100644 index 000000000..9cccdf64b --- /dev/null +++ b/src/mem/gems_common/ioutil/initvar.cc @@ -0,0 +1,626 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + This file has been modified by Kevin Moore and Dan Nussbaum of the + Scalable Systems Research Group at Sun Microsystems Laboratories + (http://research.sun.com/scalable/) to support the Adaptive + Transactional Memory Test Platform (ATMTP). + + Please send email to atmtp-interest@sun.com with feedback, questions, or + to request future announcements about ATMTP. + + ---------------------------------------------------------------------- + + File modification date: 2008-02-23 + + ---------------------------------------------------------------------- +*/ + +/* + * FileName: initvar.C + * Synopsis: implementation of global variable initialization in simics + * Author: cmauer + * Version: $Id$ + */ + +/*------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------*/ + +using namespace std; +#include +#include +#include + +// Maurice +// extern "C" { +// #include "global.hh" +// #include "simics/api.hh" +// #ifdef SIMICS22X +// #include "configuration_api.hh" +// #endif +// #ifdef SIMICS30 +// #include "configuration.hh" +// #endif +// }; + +#include "FakeSimicsDataTypes.hh" + +#ifdef IS_OPAL +#include "hfatypes.hh" +#include "debugio.hh" +#endif + +#ifdef IS_RUBY +#include "Global.hh" +#endif + +#ifdef IS_TOURMALINE +#include "Tourmaline_Global.hh" +#endif + +#include "confio.hh" +#include "initvar.hh" + +/*------------------------------------------------------------------------*/ +/* Macro declarations */ +/*------------------------------------------------------------------------*/ + +#define CONFIG_VAR_FILENAME "config.include" + +/*------------------------------------------------------------------------*/ +/* Variable declarations */ +/*------------------------------------------------------------------------*/ + +// define global "constants" using centralized file +#define PARAM( NAME ) \ + int32 NAME; +#define PARAM_UINT( NAME ) \ + uint32 NAME; +#define PARAM_ULONG( NAME ) \ + uint64 NAME; +#define PARAM_BOOL( NAME ) \ + bool NAME; +#define PARAM_DOUBLE( NAME ) \ + double NAME; +#define PARAM_STRING( NAME ) \ + char *NAME; +#define PARAM_ARRAY( PTYPE, NAME, ARRAY_SIZE ) \ + PTYPE NAME[ARRAY_SIZE]; +#include CONFIG_VAR_FILENAME +#undef PARAM +#undef PARAM_UINT +#undef PARAM_ULONG +#undef PARAM_BOOL +#undef PARAM_DOUBLE +#undef PARAM_STRING +#undef PARAM_ARRAY + +/** global initvar object */ +initvar_t *initvar_t::m_inst = NULL; + +/*------------------------------------------------------------------------*/ +/* Forward declarations */ +/*------------------------------------------------------------------------*/ + +static attr_value_t initvar_get_attr( void *ptr, void *obj ); +static set_error_t initvar_set_attr( void *ptr, void *obj, + attr_value_t *value ); + +/*------------------------------------------------------------------------*/ +/* Constructor(s) / destructor */ +/*------------------------------------------------------------------------*/ + +//************************************************************************** +initvar_t::initvar_t( const char *name, const char *relativeIncludePath, + const char *initializingString, + void (*allocate_fn)(void), + void (*my_generate_fn)(void), + get_attr_t my_get_attr, set_attr_t my_set_attr ) +{ + m_is_init = false; + m_name = (char *) malloc( sizeof(char)*(strlen( name ) + 2) ); + m_rel_include_path = (char *) malloc( sizeof(char)*(strlen( relativeIncludePath ) + 2) ); + m_config_filename = NULL; + strcpy( m_name, name ); + strcpy( m_rel_include_path, relativeIncludePath ); + m_allocate_f = allocate_fn; + m_generate_values_f = my_generate_fn; + m_my_get_attr = my_get_attr; + m_my_set_attr = my_set_attr; + + initvar_t::m_inst = this; + init_config_reader( initializingString ); +} + +//************************************************************************** +initvar_t::~initvar_t( ) +{ +#define PARAM( NAME ) +#define PARAM_UINT( NAME ) +#define PARAM_ULONG( NAME ) +#define PARAM_BOOL( NAME ) +#define PARAM_DOUBLE( NAME ) +#define PARAM_STRING( NAME ) \ + if (NAME != NULL) { \ + free( NAME ); \ + NAME = NULL; \ + } +#define PARAM_ARRAY( PTYPE, NAME, ARRAY_SIZE ) +#include CONFIG_VAR_FILENAME +#undef PARAM +#undef PARAM_UINT +#undef PARAM_ULONG +#undef PARAM_BOOL +#undef PARAM_DOUBLE +#undef PARAM_STRING +#undef PARAM_ARRAY + if (m_name) { + free( m_name ); + } + if (m_rel_include_path) { + free( m_rel_include_path ); + } + if (m_config_reader) { + delete m_config_reader; + } + if (m_config_filename) { + delete m_config_filename; + } +} + +//************************************************************************** +void initvar_t::init_config_reader( const char *initString ) +{ + int rc; + const char *name; + + m_config_reader = new confio_t(); + m_config_reader->setVerbosity( false ); + + // Initialize the config reader object to identify each parameter +#define PARAM_UINT PARAM +#define PARAM_ULONG PARAM +#define PARAM_BOOL PARAM +#define PARAM_DOUBLE PARAM +#define PARAM( NAME ) \ + name = #NAME; \ + rc = m_config_reader->register_attribute( name, \ + initvar_get_attr, (void *) name, \ + initvar_set_attr, (void *) name ); +#define PARAM_STRING( NAME ) \ + NAME = NULL; \ + name = #NAME; \ + rc = m_config_reader->register_attribute( name, \ + initvar_get_attr, (void *) name, \ + initvar_set_attr, (void *) name ); +#define PARAM_ARRAY( PTYPE, NAME, ARRAY_SIZE ) \ + name = #NAME; \ + rc = m_config_reader->register_attribute( name, \ + initvar_get_attr, (void *) name, \ + initvar_set_attr, (void *) name ); + +#include CONFIG_VAR_FILENAME +#undef PARAM +#undef PARAM_UINT +#undef PARAM_ULONG +#undef PARAM_BOOL +#undef PARAM_DOUBLE +#undef PARAM_STRING +#undef PARAM_ARRAY + + // read the default configuration from the embedded text file + rc = m_config_reader->readConfigurationString( initString ); + (*m_generate_values_f)(); +} + +/*------------------------------------------------------------------------*/ +/* Public methods */ +/*------------------------------------------------------------------------*/ + +//************************************************************************** +void initvar_t::allocate( void ) +{ + if ( confirm_init() ) { + DEBUG_OUT("error: %s initvar::allocate() called twice\n", m_name); + return; + } + + (*m_generate_values_f)(); + (*m_allocate_f)(); + m_is_init = true; +} + +//************************************************************************** +void initvar_t::checkInitialization( void ) +{ + m_config_reader->checkInitialization(); +} + +//************************************************************************** +attr_value_t initvar_t::dispatch_get( void *id, void *obj, + attr_value_t *idx ) +{ + const char *command = (const char *) id; + if ( !confirm_init() ) { + DEBUG_OUT("error: %s is uninitialized. unable to get \'%s\'\n", m_name, command); + DEBUG_OUT(" : you must initialize %s with a configuration file first.\n", m_name); + DEBUG_OUT(" : use the command \'%s0.init\'\n", m_name); + + attr_value_t ret; + ret.kind = Sim_Val_Invalid; + ret.u.integer = 0; + return ret; + } + + return ((*m_my_get_attr)(id, obj, idx)); +} + + +//************************************************************************** +set_error_t initvar_t::dispatch_set( void *id, void *obj, + attr_value_t *val, attr_value_t *idx ) +{ + const char *command = (const char *) id; + + // DEBUG_OUT("set attribute: %s\n", command); + if (!strcmp(command, "init")) { + if (val->kind == Sim_Val_String) { + if (!strcmp( val->u.string, "" )) { + // update generated values, then allocate + allocate(); + } else { + read_config( val->u.string ); + allocate(); + } + return Sim_Set_Ok; + } else { + return Sim_Set_Need_String; + } + } else if (!strcmp(command, "readparam")) { + if (val->kind == Sim_Val_String) { + read_config( val->u.string ); + return Sim_Set_Ok; + } else { + return Sim_Set_Need_String; + } + } else if (!strcmp(command, "saveparam")) { + if (val->kind == Sim_Val_String) { + FILE *fp = fopen( val->u.string, "w" ); + if (fp == NULL) { + ERROR_OUT("error: unable to open file: %s\n", val->u.string); + return Sim_Set_Illegal_Value; + } + list_param( fp ); + if (fp != NULL) { + fclose( fp ); + } + return Sim_Set_Ok; + } else { + ERROR_OUT("error: saveparam given wrong type.\n"); + return Sim_Set_Illegal_Value; + } + } else if (!strcmp(command, "param")) { + if (val->kind == Sim_Val_Integer) { + list_param( stdout ); + return Sim_Set_Ok; + } else if ( val->kind == Sim_Val_List && + val->u.list.size == 2 && + val->u.list.vector[0].kind == Sim_Val_String ) { + return (set_param( val->u.list.vector[0].u.string, + &val->u.list.vector[1] )); + } else { + DEBUG_OUT("error: set parameter given wrong type.\n"); + return Sim_Set_Illegal_Value; + } + } + + if ( !confirm_init() ) { + DEBUG_OUT("error: %s is uninitialized. unable to set \'%s\'\n", m_name, id); + DEBUG_OUT(" : you must initialize %s with a configuration file first.\n", m_name); + DEBUG_OUT(" : use the command \'%s0.init\'\n", m_name); + return Sim_Set_Illegal_Value; + } + + return (*m_my_set_attr)( id, obj, val, idx ); +} + +/*------------------------------------------------------------------------*/ +/* Accessor(s) / mutator(s) */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Private methods */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Static methods */ +/*------------------------------------------------------------------------*/ + +//************************************************************************** +static attr_value_t initvar_get_attr( void *ptr, void *obj ) +{ + const char *name = (const char *) ptr; + attr_value_t ret; + memset( &ret, 0, sizeof(attr_value_t) ); + +#define PARAM_UINT PARAM +#define PARAM_ULONG PARAM +#define PARAM_BOOL PARAM +#define PARAM( NAME ) \ + if (!strcmp(name, #NAME)) { \ + ret.kind = Sim_Val_Integer; \ + ret.u.integer = NAME; \ + return (ret); \ + } +#define PARAM_DOUBLE( NAME ) \ + if (!strcmp(name, #NAME)) { \ + ret.kind = Sim_Val_Floating; \ + ret.u.floating = NAME; \ + return (ret); \ + } +#define PARAM_STRING( NAME ) \ + if (!strcmp(name, #NAME)) { \ + ret.kind = Sim_Val_String; \ + ret.u.string = NAME; \ + return (ret); \ + } +#define PARAM_ARRAY( PTYPE, NAME, ARRAY_SIZE ) \ + if (!strcmp(name, #NAME)) { \ + ret.kind = Sim_Val_List; \ + ret.u.list.size = ARRAY_SIZE; \ + ret.u.list.vector = mallocAttribute( ARRAY_SIZE ); \ + for (int i = 0; i < ARRAY_SIZE; i++) { \ + ret.u.list.vector[i].u.integer = NAME[i]; \ + } \ + return (ret); \ + } + +#include CONFIG_VAR_FILENAME +#undef PARAM +#undef PARAM_UINT +#undef PARAM_ULONG +#undef PARAM_BOOL +#undef PARAM_DOUBLE +#undef PARAM_STRING +#undef PARAM_ARRAY + + DEBUG_OUT("error: %s not found.\n", name); + ret.kind = Sim_Val_Invalid; + return (ret); +} + +//*************************************************************************** +static set_error_t initvar_set_attr( void *ptr, void *obj, + attr_value_t *value ) +{ + const char *name = (const char *) ptr; + +#define PARAM_UINT PARAM +#define PARAM_ULONG PARAM +#define PARAM( NAME ) \ + if (!strcmp(name, #NAME)) { \ + if ( value->kind != Sim_Val_Integer ) { \ + ERROR_OUT("error: %s is not an integer\n", name );\ + return Sim_Set_Need_Integer; \ + } \ + NAME = value->u.integer; \ + return Sim_Set_Ok; \ + } +#define PARAM_BOOL( NAME ) \ + if (!strcmp(name, #NAME)) { \ + if ( value->kind != Sim_Val_String ) { \ + ERROR_OUT("error: %s is not an bool string\n", name );\ + return Sim_Set_Need_String; \ + } \ + if (!strcmp(value->u.string, "true")) { \ + NAME = true; \ + } else if (!strcmp(value->u.string, "false")) { \ + NAME = false; \ + } else { \ + ERROR_OUT("error: value %s for %s is not an bool string (set to false)\n", value->u.string, name );\ + NAME = false; \ + } \ + return Sim_Set_Ok; \ + } +#define PARAM_DOUBLE( NAME ) \ + if (!strcmp(name, #NAME)) { \ + if ( value->kind != Sim_Val_String ) { \ + ERROR_OUT("error: %s is not a float\n", name );\ + return Sim_Set_Need_Floating; \ + } \ + NAME = atof( value->u.string ); \ + return Sim_Set_Ok; \ + } +#define PARAM_STRING( NAME ) \ + if (!strcmp(name, #NAME)) { \ + if ( value->kind != Sim_Val_String ) { \ + ERROR_OUT("error: %s is not an string\n", name ); \ + return Sim_Set_Need_String; \ + } \ + if (NAME != NULL) { \ + free( NAME ); \ + } \ + NAME = strdup( value->u.string ); \ + return Sim_Set_Ok; \ + } +#define PARAM_ARRAY( PTYPE, NAME, ARRAY_SIZE ) \ + if (!strcmp(name, #NAME)) { \ + if ( value->kind != Sim_Val_List ) { \ + ERROR_OUT("error: %s is not an list\n", name ); \ + return Sim_Set_Need_List; \ + } \ + if ( value->u.list.size != ARRAY_SIZE ) { \ + ERROR_OUT("error: %s has %lld elements (should be %d)\n", name, value->u.list.size, ARRAY_SIZE); \ + return Sim_Set_Illegal_Value; \ + } \ + for (int i = 0; i < ARRAY_SIZE; i++) { \ + NAME[i] = value->u.list.vector[i].u.integer; \ + } \ + return Sim_Set_Ok; \ + } + +#include CONFIG_VAR_FILENAME +#undef PARAM +#undef PARAM_UINT +#undef PARAM_ULONG +#undef PARAM_BOOL +#undef PARAM_DOUBLE +#undef PARAM_STRING +#undef PARAM_ARRAY + + ERROR_OUT("error: %s not a parameter\n", name); + return Sim_Set_Illegal_Value; +} + +//*************************************************************************** +void initvar_t::read_config( const char *parameterFile ) +{ + DEBUG_OUT("read configuration: %s\n", parameterFile ); + m_config_filename = strdup( parameterFile ); + int rc = m_config_reader->readConfiguration( parameterFile, + m_rel_include_path ); + if ( rc < 0 ) { + ERROR_OUT("fatal error in read configuration: unable to continue.\n"); + exit(1); + } + // update generated values + (*m_generate_values_f)(); +} + +/** sets one of the parameters */ +//************************************************************************** +set_error_t initvar_t::set_param( const char *name, attr_value_t *value ) +{ + + // [dann 2007-04-04] ATMTP VV + // + // HACK ALERT: allow setting REMOVE_SINGLE_CYCLE_DCACHE_FAST_PATH, + // PROFILE_EXCEPTIONS, PROFILE_XACT, ATMTP_DEBUG_LEVEL and + // ATMTP_ENABLED after initialization. This works is because ruby's + // m_generate_values_f() does nothing -- more particularly, nothing + // that depends on any of these parameters is pre-calculated + // anywhere. + // + if (strcmp(name, "REMOVE_SINGLE_CYCLE_DCACHE_FAST_PATH") != 0 && + strcmp(name, "PROFILE_EXCEPTIONS") != 0 && + strcmp(name, "PROFILE_XACT") != 0 && + strcmp(name, "ATMTP_DEBUG_LEVEL") != 0 && + strcmp(name, "ATMTP_ENABLED") != 0) { + // + // [dann 2007-04-04] ATMTP ^^ + if ( confirm_init() ) { + DEBUG_OUT("error: %s is already initialized.\n", m_name); + DEBUG_OUT(" : setting parameters after initialization is unsupported\n"); + return (Sim_Set_Illegal_Value); + } + // [dann 2007-04-04] ATMTP VV + // + } + // + // [dann 2007-04-04] ATMTP ^^ + + set_error_t result = initvar_set_attr( (void *) name, NULL, value ); + (*m_generate_values_f)(); + return (result); +} + +/** print out a list of valid parameters */ +//************************************************************************** +void initvar_t::list_param( FILE *fp ) +{ + if (!fp) + fp = stdout; + +#define PARAM( NAME ) \ + fprintf( fp, "%-44.44s: %26d\n", #NAME, NAME ); +#define PARAM_UINT( NAME ) \ + fprintf( fp, "%-44.44s: %26u\n", #NAME, NAME ); +#define PARAM_ULONG( NAME ) \ + fprintf( fp, "%-44.44s: %26llu\n", #NAME, NAME ); +#define PARAM_BOOL( NAME ) \ + if (NAME == true) { \ + fprintf( fp, "%-44.44s: %26.26s\n", #NAME, "true" ); \ + } else { \ + fprintf( fp, "%-44.44s: %26.26s\n", #NAME, "false" );\ + } +#define PARAM_DOUBLE( NAME ) \ + fprintf( fp, "%-44.44s: %26f\n", #NAME, NAME ); +#define PARAM_STRING( NAME ) \ + if ( NAME != NULL ) { \ + fprintf( fp, "%-44.44s: %26.26s\n", #NAME, NAME ); \ + } +#define PARAM_ARRAY( PTYPE, NAME, ARRAY_SIZE ) \ + fprintf( fp, "%-44.44s: (", #NAME ); \ + for (int i = 0; i < ARRAY_SIZE; i++) { \ + if ( i != 0 ) { \ + fprintf( fp, ", " ); \ + } \ + fprintf( fp, "%d", NAME[i] ); \ + } \ + fprintf( fp, ")\n" ); + +#include CONFIG_VAR_FILENAME +#undef PARAM +#undef PARAM_UINT +#undef PARAM_ULONG +#undef PARAM_BOOL +#undef PARAM_DOUBLE +#undef PARAM_STRING +#undef PARAM_ARRAY +} + +//************************************************************************** +const char *initvar_t::get_config_name( void ) +{ + if (m_config_filename == NULL) { + return "default"; + } + return m_config_filename; +} + +/*------------------------------------------------------------------------*/ +/* Global functions */ +/*------------------------------------------------------------------------*/ + +//************************************************************************** +attr_value_t initvar_dispatch_get( void *id, void *obj, + attr_value_t *idx ) +{ + initvar_t *init_obj = initvar_t::m_inst; + return (init_obj->dispatch_get( id, obj, idx )); +} + +//************************************************************************** +set_error_t initvar_dispatch_set( void *id, void *obj, + attr_value_t *val, attr_value_t *idx ) +{ + initvar_t *init_obj = initvar_t::m_inst; + return (init_obj->dispatch_set( id, obj, val, idx )); +} diff --git a/src/mem/gems_common/ioutil/initvar.hh b/src/mem/gems_common/ioutil/initvar.hh new file mode 100644 index 000000000..8dea8dfc1 --- /dev/null +++ b/src/mem/gems_common/ioutil/initvar.hh @@ -0,0 +1,181 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _INCLUDE_H_ +#define _INCLUDE_H_ + +/*------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Macro declarations */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Class declaration(s) */ +/*------------------------------------------------------------------------*/ + +/** +* This class deals with initializing the global variables in the object, +* setting the varibles (from the command line), printing the configuration, +* and saving it to a file. +* +* Before including this file, you must define the variable CONFIG_VAR_FILENAME +* to define which variables are to be used. +* +* @see confio_t +* @author cmauer +* @version $Id$ +*/ +class initvar_t { +public: + /** + * @name Constructor(s) / destructor + */ + //@{ + + /** + * Constructor: creates object + * @param name The name of this object + * @param relativeIncludePath The relative path to config files + * @param initializingString A string (with value pairs) for initialization + * @param allocate_f A ptr to the allocate function + * @param generate_values A ptr to the generate values function + * @param my_get_attr A ptr to the get attribute function + * @param my_set_attr A ptr to the set attribute function + */ + initvar_t( const char *name, const char *relativeIncludePath, + const char *initializingString, + void (*allocate_fn)(void), + void (*my_generate_fn)(void), + get_attr_t my_get_attr, set_attr_t my_set_attr ); + + /** + * Destructor: frees object. + */ + ~initvar_t(); + //@} + + /** + * @name Methods + */ + //@{ + /// calls the allocation routine explicitly (used by the tester) + void allocate( void ); + + /// checks to see if all vars have been initialized + void checkInitialization( void ); + + /// list all parameters: to a file (or stdout if file is NULL) + void list_param( FILE *fp ); + + /// returns the name of the last config file to be read ("default" is none) + const char *get_config_name( void ); + + /// calls through to the get_attr function, if object is initialized + attr_value_t dispatch_get( void *id, void *obj, + attr_value_t *idx ); + + /** adds initialization attributes, calls through to the set_attr function, + * if object is initialized. + */ + set_error_t dispatch_set( void *id, void *obj, + attr_value_t *val, attr_value_t *idx ); + //@} + /// (single) instance of the init var object + static initvar_t *m_inst; + +protected: + ///returns true if the variables are initialized + bool confirm_init( void ) { + return m_is_init; + } + + ///read a configuration file + void read_config( const char *parameterFile ); + + /// set a parameter to be a particular value + set_error_t set_param( const char *name, attr_value_t *value ); + + /// initializes the configuration reader + void init_config_reader( const char *initString ); + + /// bool value (true if initialized) + bool m_is_init; + + /// configuration reader + confio_t *m_config_reader; + + /// a pointer to a string (corresponding to this objects name) + char *m_name; + + /// a pointer to a string (representing the last config file read) + char *m_config_filename; + + /// the relative include path to the configuration files + char *m_rel_include_path; + + /// a pointer to the allocation function + void (*m_allocate_f)(void); + + /// a pointer to the generate values function + void (*m_generate_values_f)(void); + + /// a pointer to the session get function + get_attr_t m_my_get_attr; + /// a pointer to the session set function + set_attr_t m_my_set_attr; +}; + + +/*------------------------------------------------------------------------*/ +/* Global variables */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Global functions */ +/*------------------------------------------------------------------------*/ + +#ifdef __cplusplus +extern "C" { +#endif + +///provides a dispatch mechanism that catches a few commands to get variables +attr_value_t initvar_dispatch_get( void *id, void *obj, + attr_value_t *idx ); + +///provides a dispatch mechanism that catches a few commands to set variables +set_error_t initvar_dispatch_set( void *id, void *obj, + attr_value_t *val, attr_value_t *idx ); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* _INCLUDE_H_ */ diff --git a/src/mem/gems_common/ioutil/vardecl.hh b/src/mem/gems_common/ioutil/vardecl.hh new file mode 100644 index 000000000..21bc62d02 --- /dev/null +++ b/src/mem/gems_common/ioutil/vardecl.hh @@ -0,0 +1,75 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _VARDECL_H_ +#define _VARDECL_H_ + +/*------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Macro declarations */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Class declaration(s) */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Global variables */ +/*------------------------------------------------------------------------*/ + +#define PARAM( NAME ) \ + extern int32 NAME; +#define PARAM_UINT( NAME ) \ + extern uint32 NAME; +#define PARAM_ULONG( NAME ) \ + extern uint64 NAME; +#define PARAM_BOOL( NAME ) \ + extern bool NAME; +#define PARAM_DOUBLE( NAME ) \ + extern double NAME; +#define PARAM_STRING( NAME ) \ + extern char *NAME; +#define PARAM_ARRAY( PTYPE, NAME, ARRAY_SIZE ) \ + extern PTYPE NAME[ARRAY_SIZE]; +#include CONFIG_VAR_FILENAME +#undef PARAM +#undef PARAM_UINT +#undef PARAM_ULONG +#undef PARAM_BOOL +#undef PARAM_DOUBLE +#undef PARAM_STRING +#undef PARAM_ARRAY + +/*------------------------------------------------------------------------*/ +/* Global functions */ +/*------------------------------------------------------------------------*/ + +#endif /* _VARDECL_H_ */ diff --git a/src/mem/gems_common/std-includes.hh b/src/mem/gems_common/std-includes.hh new file mode 100644 index 000000000..619214f1d --- /dev/null +++ b/src/mem/gems_common/std-includes.hh @@ -0,0 +1,51 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef INCLUDES_H +#define INCLUDES_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace __gnu_cxx; + +#endif //INCLUDES_H diff --git a/src/mem/gems_common/util.cc b/src/mem/gems_common/util.cc new file mode 100644 index 000000000..c5b8f22b5 --- /dev/null +++ b/src/mem/gems_common/util.cc @@ -0,0 +1,109 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +#include "assert.h" +#include "util.hh" + +// Split a string into a head and tail strings on the specified +// character. Return the head and the string passed in is modified by +// removing the head, leaving just the tail. + +string string_split(string& str, char split_character) +{ + string head = ""; + string tail = ""; + + uint counter = 0; + while(counter < str.size()) { + if (str[counter] == split_character) { + counter++; + break; + } else { + head += str[counter]; + } + counter++; + } + + while(counter < str.size()) { + tail += str[counter]; + counter++; + } + str = tail; + return head; +} + +string bool_to_string(bool value) +{ + if (value) { + return "true"; + } else { + return "false"; + } +} + +string int_to_string(int n, bool zero_fill, int width) +{ + ostringstream sstr; + if(zero_fill) { + sstr << setw(width) << setfill('0') << n; + } else { + sstr << n; + } + string str = sstr.str(); + return str; +} + +float string_to_float(string& str) +{ + stringstream sstr(str); + float ret; + sstr >> ret; + return ret; +} + +// Log functions +int log_int(long long n) +{ + assert(n > 0); + int counter = 0; + while (n >= 2) { + counter++; + n = n>>(long long)(1); + } + return counter; +} + +bool is_power_of_2(long long n) +{ + return (n == ((long long)(1) << log_int(n))); +} + diff --git a/src/mem/gems_common/util.hh b/src/mem/gems_common/util.hh new file mode 100644 index 000000000..d9e9fec3e --- /dev/null +++ b/src/mem/gems_common/util.hh @@ -0,0 +1,68 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +#ifndef UTIL_H +#define UTIL_H + +#include "std-includes.hh" + +string string_split(string& str, char split_character); +string bool_to_string(bool value); +string int_to_string(int n, bool zero_fill = false, int width = 0); +float string_to_float(string& str); +int log_int(long long n); +bool is_power_of_2(long long n); + +// Min and Max functions (since they are extern inline, they are as +// fast as macros) + +extern inline +int max(int n1, int n2) +{ + if (n1 > n2) { + return n1; + } else { + return n2; + } +} + +extern inline +int min(int n1, int n2) +{ + if (n1 < n2) { + return n1; + } else { + return n2; + } +} + +#endif //UTIL_H diff --git a/src/mem/protocol/LogTM.sm b/src/mem/protocol/LogTM.sm new file mode 100644 index 000000000..02c6656ac --- /dev/null +++ b/src/mem/protocol/LogTM.sm @@ -0,0 +1,83 @@ +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + This file has been modified by Kevin Moore and Dan Nussbaum of the + Scalable Systems Research Group at Sun Microsystems Laboratories + (http://research.sun.com/scalable/) to support the Adaptive + Transactional Memory Test Platform (ATMTP). + + Please send email to atmtp-interest@sun.com with feedback, questions, or + to request future announcements about ATMTP. + + ---------------------------------------------------------------------- + + File modification date: 2008-02-23 + + ---------------------------------------------------------------------- +*/ + +external_type(PartialAddressFilter, desc="Bloom filter for tracking transaction locks."){ + bool isRead(Address); + bool isWrite(Address); + + void addEntry(Address, bool); + void clear(); +} + +external_type(TransactionInterfaceManager) { + bool shouldNackLoad(Address, uint64, MachineID); + bool shouldNackStore(Address, uint64, MachineID); + bool checkReadWriteSignatures(Address); + bool checkWriteSignatures(Address); + + void notifySendNack(Address, uint64, MachineID); + void notifyReceiveNack(int, Address, uint64, uint64, MachineID); + void notifyReceiveNackFinal(int, Address); + + uint64 getTimestamp(int); + uint64 getOldestTimestamp(); + + bool existGlobalLoadConflict(int, Address); + bool existGlobalStoreConflict(int, Address); + + void profileTransactionMiss(int, bool); + + void xactReplacement(Address); + + /* DEPRECATED */ + bool existLoadConflict(Address); + bool existStoreConflict(Address); + bool isInReadFilterSummary(Address); + bool isInWriteFilterSummary(Address); + bool isTokenOwner(int); + void setAbortFlag(int, Address); + void setEnemyProcessor(int, MachineID); + bool isRemoteOlder(uint64); + +} diff --git a/src/mem/protocol/MESI_CMP_directory-L1cache.sm b/src/mem/protocol/MESI_CMP_directory-L1cache.sm new file mode 100644 index 000000000..8f2096666 --- /dev/null +++ b/src/mem/protocol/MESI_CMP_directory-L1cache.sm @@ -0,0 +1,867 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MSI_MOSI_CMP_directory-L1cache.sm 1.10 05/01/19 15:55:40-06:00 beckmann@s0-28.cs.wisc.edu $ + * + */ + + +machine(L1Cache, "MSI Directory L1 Cache CMP") { + + // NODE L1 CACHE + // From this node's L1 cache TO the network + // a local L1 -> this L2 bank, currently ordered with directory forwarded requests + MessageBuffer requestFromL1Cache, network="To", virtual_network="0", ordered="false"; + // a local L1 -> this L2 bank + MessageBuffer responseFromL1Cache, network="To", virtual_network="3", ordered="false"; + MessageBuffer unblockFromL1Cache, network="To", virtual_network="4", ordered="false"; + + + // To this node's L1 cache FROM the network + // a L2 bank -> this L1 + MessageBuffer requestToL1Cache, network="From", virtual_network="1", ordered="false"; + // a L2 bank -> this L1 + MessageBuffer responseToL1Cache, network="From", virtual_network="3", ordered="false"; + + // STATES + enumeration(State, desc="Cache states", default="L1Cache_State_I") { + // Base states + NP, desc="Not present in either cache"; + I, desc="a L1 cache entry Idle"; + S, desc="a L1 cache entry Shared"; + E, desc="a L1 cache entry Exclusive"; + M, desc="a L1 cache entry Modified", format="!b"; + + // Transient States + IS, desc="L1 idle, issued GETS, have not seen response yet"; + IM, desc="L1 idle, issued GETX, have not seen response yet"; + SM, desc="L1 idle, issued GETX, have not seen response yet"; + IS_I, desc="L1 idle, issued GETS, saw Inv before data because directory doesn't block on GETS hit"; + + M_I, desc="L1 replacing, waiting for ACK"; + E_I, desc="L1 replacing, waiting for ACK"; + + } + + // EVENTS + enumeration(Event, desc="Cache events") { + // L1 events + Load, desc="Load request from the home processor"; + Ifetch, desc="I-fetch request from the home processor"; + Store, desc="Store request from the home processor"; + + Inv, desc="Invalidate request from L2 bank"; + + // internal generated request + L1_Replacement, desc="L1 Replacement", format="!r"; + + // other requests + Fwd_GETX, desc="GETX from other processor"; + Fwd_GETS, desc="GETS from other processor"; + Fwd_GET_INSTR, desc="GET_INSTR from other processor"; + + Data, desc="Data for processor"; + Data_Exclusive, desc="Data for processor"; + DataS_fromL1, desc="data for GETS request, need to unblock directory"; + Data_all_Acks, desc="Data for processor, all acks"; + + Ack, desc="Ack for processor"; + Ack_all, desc="Last ack for processor"; + + WB_Ack, desc="Ack for replacement"; + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry" ) { + State CacheState, desc="cache state"; + DataBlock DataBlk, desc="data for the block"; + bool Dirty, default="false", desc="data is dirty"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="Buffer for the data block"; + bool Dirty, default="false", desc="data is dirty"; + bool isPrefetch, desc="Set if this was caused by a prefetch"; + int pendingAcks, default="0", desc="number of pending acks"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + TBETable L1_TBEs, template_hack=""; + + CacheMemory L1IcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1I"', abstract_chip_ptr="true"; + CacheMemory L1DcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1D"', abstract_chip_ptr="true"; + + MessageBuffer mandatoryQueue, ordered="false", rank="100", abstract_chip_ptr="true"; + + Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i"; + + int cache_state_to_int(State state); + + // inclusive cache returns L1 entries only + Entry getL1CacheEntry(Address addr), return_by_ref="yes" { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory[addr]; + } else { + return L1IcacheMemory[addr]; + } + } + + void changeL1Permission(Address addr, AccessPermission permission) { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory.changePermission(addr, permission); + } else if(L1IcacheMemory.isTagPresent(addr)) { + return L1IcacheMemory.changePermission(addr, permission); + } else { + error("cannot change permission, L1 block not present"); + } + } + + bool isL1CacheTagPresent(Address addr) { + return (L1DcacheMemory.isTagPresent(addr) || L1IcacheMemory.isTagPresent(addr)); + } + + State getState(Address addr) { + if((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == true){ + DEBUG_EXPR(id); + DEBUG_EXPR(addr); + } + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + + if(L1_TBEs.isPresent(addr)) { + return L1_TBEs[addr].TBEState; + } else if (isL1CacheTagPresent(addr)) { + return getL1CacheEntry(addr).CacheState; + } + return State:NP; + } + + + void setState(Address addr, State state) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + + // MUST CHANGE + if(L1_TBEs.isPresent(addr)) { + L1_TBEs[addr].TBEState := state; + } + + if (isL1CacheTagPresent(addr)) { + getL1CacheEntry(addr).CacheState := state; + + // Set permission + if (state == State:I) { + changeL1Permission(addr, AccessPermission:Invalid); + } else if (state == State:S || state == State:E) { + changeL1Permission(addr, AccessPermission:Read_Only); + } else if (state == State:M) { + changeL1Permission(addr, AccessPermission:Read_Write); + } else { + changeL1Permission(addr, AccessPermission:Busy); + } + } + } + + Event mandatory_request_type_to_event(CacheRequestType type) { + if (type == CacheRequestType:LD) { + return Event:Load; + } else if (type == CacheRequestType:IFETCH) { + return Event:Ifetch; + } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { + return Event:Store; + } else { + error("Invalid CacheRequestType"); + } + } + + + out_port(requestIntraChipL1Network_out, RequestMsg, requestFromL1Cache); + out_port(responseIntraChipL1Network_out, ResponseMsg, responseFromL1Cache); + out_port(unblockNetwork_out, ResponseMsg, unblockFromL1Cache); + + // Response IntraChip L1 Network - response msg to this L1 cache + in_port(responseIntraChipL1Network_in, ResponseMsg, responseToL1Cache) { + if (responseIntraChipL1Network_in.isReady()) { + peek(responseIntraChipL1Network_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if(in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE) { + trigger(Event:Data_Exclusive, in_msg.Address); + } else if(in_msg.Type == CoherenceResponseType:DATA) { + if ( (getState(in_msg.Address) == State:IS || getState(in_msg.Address) == State:IS_I) && + machineIDToMachineType(in_msg.Sender) == MachineType:L1Cache ) { + + trigger(Event:DataS_fromL1, in_msg.Address); + + } else if ( (L1_TBEs[in_msg.Address].pendingAcks - in_msg.AckCount) == 0 ) { + trigger(Event:Data_all_Acks, in_msg.Address); + } else { + trigger(Event:Data, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:ACK) { + if ( (L1_TBEs[in_msg.Address].pendingAcks - in_msg.AckCount) == 0 ) { + trigger(Event:Ack_all, in_msg.Address); + } else { + trigger(Event:Ack, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:WB_ACK) { + trigger(Event:WB_Ack, in_msg.Address); + } else { + error("Invalid L1 response type"); + } + } + } + } + + // Request InterChip network - request from this L1 cache to the shared L2 + in_port(requestIntraChipL1Network_in, RequestMsg, requestToL1Cache) { + if(requestIntraChipL1Network_in.isReady()) { + peek(requestIntraChipL1Network_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceRequestType:INV) { + trigger(Event:Inv, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX || in_msg.Type == CoherenceRequestType:UPGRADE) { + // upgrade transforms to GETX due to race + trigger(Event:Fwd_GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:Fwd_GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GET_INSTR) { + trigger(Event:Fwd_GET_INSTR, in_msg.Address); + } else { + error("Invalid forwarded request type"); + } + } + } + } + + // Mandatory Queue betweens Node's CPU and it's L1 caches + in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { + if (mandatoryQueue_in.isReady()) { + peek(mandatoryQueue_in, CacheMsg) { + + // Check for data access to blocks in I-cache and ifetchs to blocks in D-cache + + if (in_msg.Type == CacheRequestType:IFETCH) { + // ** INSTRUCTION ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, put the request on the queue to the shared L2 + trigger(Event:L1_Replacement, in_msg.Address); + } + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 asks the L2 for it. + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1IcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 so let's see if the L2 has it + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // No room in the L1, so we need to make room in the L1 + trigger(Event:L1_Replacement, L1IcacheMemory.cacheProbe(in_msg.Address)); + } + } + } else { + // *** DATA ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, put the request on the queue to the shared L2 + trigger(Event:L1_Replacement, in_msg.Address); + } + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 ask the L2 for it + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1DcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 let's see if the L2 has it + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // No room in the L1, so we need to make room in the L1 + trigger(Event:L1_Replacement, L1DcacheMemory.cacheProbe(in_msg.Address)); + } + } + } + } + } + } + + // ACTIONS + action(a_issueGETS, "a", desc="Issue GETS") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + } + } + } + + action(ai_issueGETINSTR, "ai", desc="Issue GETINSTR") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GET_INSTR; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + } + } + } + + + action(b_issueGETX, "b", desc="Issue GETX") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.Requestor := machineID; + DEBUG_EXPR(machineID); + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + } + } + } + + action(c_issueUPGRADE, "c", desc="Issue GETX") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:UPGRADE; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + } + } + } + + action(d_sendDataToRequestor, "d", desc="send data to requestor") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(d2_sendDataToL2, "d2", desc="send data to the L2 cache because of M downgrade") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(dt_sendDataToRequestor_fromTBE, "dt", desc="send data to requestor") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := L1_TBEs[address].DataBlk; + out_msg.Dirty := L1_TBEs[address].Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(d2t_sendDataToL2_fromTBE, "d2t", desc="send data to the L2 cache") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := L1_TBEs[address].DataBlk; + out_msg.Dirty := L1_TBEs[address].Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(e_sendAckToRequestor, "e", desc="send invalidate ack to requestor (could be L2 or L1)") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + + action(f_sendDataToL2, "f", desc="send data to the L2 cache") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } + } + + action(ft_sendDataToL2_fromTBE, "ft", desc="send data to the L2 cache") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := L1_TBEs[address].DataBlk; + out_msg.Dirty := L1_TBEs[address].Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } + } + + action(fi_sendInvAck, "fi", desc="send data to the L2 cache") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + out_msg.AckCount := 1; + } + } + } + + + action(g_issuePUTX, "g", desc="send data to the L2 cache") { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTX; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Requestor:= machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + if (getL1CacheEntry(address).Dirty) { + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } else { + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(j_sendUnblock, "j", desc="send unblock to the L2 cache") { + enqueue(unblockNetwork_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:UNBLOCK; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + + action(jj_sendExclusiveUnblock, "\j", desc="send unblock to the L2 cache") { + enqueue(unblockNetwork_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:EXCLUSIVE_UNBLOCK; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + + + + action(h_load_hit, "h", desc="If not prefetch, notify sequencer the load completed.") { + DEBUG_EXPR(getL1CacheEntry(address).DataBlk); + sequencer.readCallback(address, getL1CacheEntry(address).DataBlk); + } + + action(hh_store_hit, "\h", desc="If not prefetch, notify sequencer that store completed.") { + DEBUG_EXPR(getL1CacheEntry(address).DataBlk); + sequencer.writeCallback(address, getL1CacheEntry(address).DataBlk); + getL1CacheEntry(address).Dirty := true; + } + + action(i_allocateTBE, "i", desc="Allocate TBE (isPrefetch=0, number of invalidates=0)") { + check_allocate(L1_TBEs); + L1_TBEs.allocate(address); + L1_TBEs[address].isPrefetch := false; + L1_TBEs[address].Dirty := getL1CacheEntry(address).Dirty; + L1_TBEs[address].DataBlk := getL1CacheEntry(address).DataBlk; + } + + action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") { + mandatoryQueue_in.dequeue(); + } + + action(l_popRequestQueue, "l", desc="Pop incoming request queue and profile the delay within this virtual network") { + profileMsgDelay(2, requestIntraChipL1Network_in.dequeue_getDelayCycles()); + } + + action(o_popIncomingResponseQueue, "o", desc="Pop Incoming Response queue and profile the delay within this virtual network") { + profileMsgDelay(3, responseIntraChipL1Network_in.dequeue_getDelayCycles()); + } + + action(s_deallocateTBE, "s", desc="Deallocate TBE") { + L1_TBEs.deallocate(address); + } + + action(u_writeDataToL1Cache, "u", desc="Write data to cache") { + peek(responseIntraChipL1Network_in, ResponseMsg) { + getL1CacheEntry(address).DataBlk := in_msg.DataBlk; + getL1CacheEntry(address).Dirty := in_msg.Dirty; + } + } + + action(q_updateAckCount, "q", desc="Update ack count") { + peek(responseIntraChipL1Network_in, ResponseMsg) { + L1_TBEs[address].pendingAcks := L1_TBEs[address].pendingAcks - in_msg.AckCount; + APPEND_TRANSITION_COMMENT(in_msg.AckCount); + APPEND_TRANSITION_COMMENT(" p: "); + APPEND_TRANSITION_COMMENT(L1_TBEs[address].pendingAcks); + } + } + + action(z_stall, "z", desc="Stall") { + } + + action(ff_deallocateL1CacheBlock, "\f", desc="Deallocate L1 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + if (L1DcacheMemory.isTagPresent(address)) { + L1DcacheMemory.deallocate(address); + } else { + L1IcacheMemory.deallocate(address); + } + } + + action(oo_allocateL1DCacheBlock, "\o", desc="Set L1 D-cache tag equal to tag of block B.") { + if (L1DcacheMemory.isTagPresent(address) == false) { + L1DcacheMemory.allocate(address); + } + } + + action(pp_allocateL1ICacheBlock, "\p", desc="Set L1 I-cache tag equal to tag of block B.") { + if (L1IcacheMemory.isTagPresent(address) == false) { + L1IcacheMemory.allocate(address); + } + } + + action(zz_recycleRequestQueue, "zz", desc="recycle L1 request queue") { + requestIntraChipL1Network_in.recycle(); + } + + action(z_recycleMandatoryQueue, "\z", desc="recycle L1 request queue") { + mandatoryQueue_in.recycle(); + } + + + //***************************************************** + // TRANSITIONS + //***************************************************** + + // Transitions for Load/Store/Replacement/WriteBack from transient states + transition({IS, IM, IS_I, M_I, E_I, SM}, {Load, Ifetch, Store, L1_Replacement}) { + z_recycleMandatoryQueue; + } + + // Transitions from Idle + transition({NP,I}, L1_Replacement) { + ff_deallocateL1CacheBlock; + } + + transition({NP,I}, Load, IS) { + oo_allocateL1DCacheBlock; + i_allocateTBE; + a_issueGETS; + k_popMandatoryQueue; + } + + transition({NP,I}, Ifetch, IS) { + pp_allocateL1ICacheBlock; + i_allocateTBE; + ai_issueGETINSTR; + k_popMandatoryQueue; + } + + transition({NP,I}, Store, IM) { + oo_allocateL1DCacheBlock; + i_allocateTBE; + b_issueGETX; + k_popMandatoryQueue; + } + + transition({NP, I}, Inv) { + fi_sendInvAck; + l_popRequestQueue; + } + + // Transitions from Shared + transition(S, {Load,Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(S, Store, SM) { + i_allocateTBE; + c_issueUPGRADE; + k_popMandatoryQueue; + } + + transition(S, L1_Replacement, I) { + ff_deallocateL1CacheBlock; + } + + transition(S, Inv, I) { + fi_sendInvAck; + l_popRequestQueue; + } + + // Transitions from Exclusive + + transition(E, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(E, Store, M) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(E, L1_Replacement, M_I) { + // silent E replacement?? + i_allocateTBE; + g_issuePUTX; // send data, but hold in case forwarded request + ff_deallocateL1CacheBlock; + } + + transition(E, Inv, I) { + // don't send data + fi_sendInvAck; + l_popRequestQueue; + } + + transition(E, Fwd_GETX, I) { + d_sendDataToRequestor; + l_popRequestQueue; + } + + transition(E, {Fwd_GETS, Fwd_GET_INSTR}, S) { + d_sendDataToRequestor; + d2_sendDataToL2; + l_popRequestQueue; + } + + // Transitions from Modified + transition(M, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(M, Store) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(M, L1_Replacement, M_I) { + i_allocateTBE; + g_issuePUTX; // send data, but hold in case forwarded request + ff_deallocateL1CacheBlock; + } + + transition(M_I, WB_Ack, I) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(M, Inv, I) { + f_sendDataToL2; + l_popRequestQueue; + } + + transition(M_I, Inv, I) { + ft_sendDataToL2_fromTBE; + s_deallocateTBE; + l_popRequestQueue; + } + + transition(M, Fwd_GETX, I) { + d_sendDataToRequestor; + l_popRequestQueue; + } + + transition(M, {Fwd_GETS, Fwd_GET_INSTR}, S) { + d_sendDataToRequestor; + d2_sendDataToL2; + l_popRequestQueue; + } + + transition(M_I, Fwd_GETX, I) { + dt_sendDataToRequestor_fromTBE; + s_deallocateTBE; + l_popRequestQueue; + } + + transition(M_I, {Fwd_GETS, Fwd_GET_INSTR}, I) { + dt_sendDataToRequestor_fromTBE; + d2t_sendDataToL2_fromTBE; + s_deallocateTBE; + l_popRequestQueue; + } + + // Transitions from IS + transition({IS, IS_I}, Inv, IS_I) { + fi_sendInvAck; + l_popRequestQueue; + } + + transition(IS, Data_all_Acks, S) { + u_writeDataToL1Cache; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IS_I, Data_all_Acks, I) { + u_writeDataToL1Cache; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + + transition(IS, DataS_fromL1, S) { + u_writeDataToL1Cache; + j_sendUnblock; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IS_I, DataS_fromL1, I) { + u_writeDataToL1Cache; + j_sendUnblock; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // directory is blocked when sending exclusive data + transition(IS_I, Data_Exclusive, E) { + u_writeDataToL1Cache; + h_load_hit; + jj_sendExclusiveUnblock; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IS, Data_Exclusive, E) { + u_writeDataToL1Cache; + h_load_hit; + jj_sendExclusiveUnblock; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // Transitions from IM + transition({IM, SM}, Inv, IM) { + fi_sendInvAck; + l_popRequestQueue; + } + + transition(IM, Data, SM) { + u_writeDataToL1Cache; + q_updateAckCount; + o_popIncomingResponseQueue; + } + + transition(IM, Data_all_Acks, M) { + u_writeDataToL1Cache; + hh_store_hit; + jj_sendExclusiveUnblock; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // transitions from SM + transition({SM, IM}, Ack) { + q_updateAckCount; + o_popIncomingResponseQueue; + } + + transition(SM, Ack_all, M) { + jj_sendExclusiveUnblock; + hh_store_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } +} + + + diff --git a/src/mem/protocol/MESI_CMP_directory-L2cache.sm b/src/mem/protocol/MESI_CMP_directory-L2cache.sm new file mode 100644 index 000000000..43c37e832 --- /dev/null +++ b/src/mem/protocol/MESI_CMP_directory-L2cache.sm @@ -0,0 +1,1036 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MSI_MOSI_CMP_directory-L2cache.sm 1.12 05/01/19 15:55:40-06:00 beckmann@s0-28.cs.wisc.edu $ + * + */ + +machine(L2Cache, "MOSI Directory L2 Cache CMP") { + + // L2 BANK QUEUES + // From local bank of L2 cache TO the network + MessageBuffer DirRequestFromL2Cache, network="To", virtual_network="2", ordered="false"; // this L2 bank -> Memory + MessageBuffer L1RequestFromL2Cache, network="To", virtual_network="1", ordered="false"; // this L2 bank -> a local L1 + MessageBuffer responseFromL2Cache, network="To", virtual_network="3", ordered="false"; // this L2 bank -> a local L1 || Memory + + // FROM the network to this local bank of L2 cache + MessageBuffer L1RequestToL2Cache, network="From", virtual_network="0", ordered="false"; // a local L1 -> this L2 bank + MessageBuffer responseToL2Cache, network="From", virtual_network="3", ordered="false"; // a local L1 || Memory -> this L2 bank + MessageBuffer unblockToL2Cache, network="From", virtual_network="4", ordered="false"; // a local L1 || Memory -> this L2 bank + + // STATES + enumeration(State, desc="L2 Cache states", default="L2Cache_State_NP") { + // Base states + NP, desc="Not present in either cache"; + SS, desc="L2 cache entry Shared, also present in one or more L1s"; + M, desc="L2 cache entry Modified, not present in any L1s", format="!b"; + MT, desc="L2 cache entry Modified in a local L1, assume L2 copy stale", format="!b"; + + // L2 replacement + M_I, desc="L2 cache replacing, have all acks, sent dirty data to memory, waiting for ACK from memory"; + MT_I, desc="L2 cache replacing, getting data from exclusive"; + MCT_I, desc="L2 cache replacing, clean in L2, getting data or ack from exclusive"; + I_I, desc="L2 replacing clean data, need to inv sharers and then drop data"; + S_I, desc="L2 replacing dirty data, collecting acks from L1s"; + + // Transient States for fetching data from memory + ISS, desc="L2 idle, got single L1_GETS, issued memory fetch, have not seen response yet"; + IS, desc="L2 idle, got L1_GET_INSTR or multiple L1_GETS, issued memory fetch, have not seen response yet"; + IM, desc="L2 idle, got L1_GETX, issued memory fetch, have not seen response(s) yet"; + + // Blocking states + SS_MB, desc="Blocked for L1_GETX from SS"; + MT_MB, desc="Blocked for L1_GETX from MT"; + M_MB, desc="Blocked for L1_GETX from M"; + + MT_IIB, desc="Blocked for L1_GETS from MT, waiting for unblock and data"; + MT_IB, desc="Blocked for L1_GETS from MT, got unblock, waiting for data"; + MT_SB, desc="Blocked for L1_GETS from MT, got data, waiting for unblock"; + + } + + // EVENTS + enumeration(Event, desc="L2 Cache events") { + // L2 events + + // events initiated by the local L1s + L1_GET_INSTR, desc="a L1I GET INSTR request for a block maped to us"; + L1_GETS, desc="a L1D GETS request for a block maped to us"; + L1_GETX, desc="a L1D GETX request for a block maped to us"; + L1_UPGRADE, desc="a L1D GETX request for a block maped to us"; + + L1_PUTX, desc="L1 replacing data"; + L1_PUTX_old, desc="L1 replacing data, but no longer sharer"; + + Fwd_L1_GETX, desc="L1 did not have data, so we supply"; + Fwd_L1_GETS, desc="L1 did not have data, so we supply"; + Fwd_L1_GET_INSTR, desc="L1 did not have data, so we supply"; + + // events initiated by this L2 + L2_Replacement, desc="L2 Replacement", format="!r"; + L2_Replacement_clean, desc="L2 Replacement, but data is clean", format="!r"; + + // events from memory controller + Mem_Data, desc="data from memory", format="!r"; + Mem_Ack, desc="ack from memory", format="!r"; + + // M->S data writeback + WB_Data, desc="data from L1"; + WB_Data_clean, desc="clean data from L1"; + Ack, desc="writeback ack"; + Ack_all, desc="writeback ack"; + + Unblock, desc="Unblock from L1 requestor"; + Unblock_Cancel, desc="Unblock from L1 requestor (FOR XACT MEMORY)"; + Exclusive_Unblock, desc="Unblock from L1 requestor"; + + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + NetDest Sharers, desc="tracks the L1 shares on-chip"; + MachineID Exclusive, desc="Exclusive holder of block"; + DataBlock DataBlk, desc="data for the block"; + bool Dirty, default="false", desc="data is dirty"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="Buffer for the data block"; + bool Dirty, default="false", desc="Data is Dirty"; + + NetDest L1_GetS_IDs, desc="Set of the internal processors that want the block in shared state"; + MachineID L1_GetX_ID, desc="ID of the L1 cache to forward the block to once we get a response"; + bool isPrefetch, desc="Set if this was caused by a prefetch"; + + int pendingAcks, desc="number of pending acks for invalidates during writeback"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + void setMRU(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + TBETable L2_TBEs, template_hack=""; + + CacheMemory L2cacheMemory, template_hack="", constructor_hack='L2_CACHE_NUM_SETS_BITS,L2_CACHE_ASSOC,MachineType_L2Cache,int_to_string(i)'; + + // inclusive cache, returns L2 entries only + Entry getL2CacheEntry(Address addr), return_by_ref="yes" { + return L2cacheMemory[addr]; + } + + void changeL2Permission(Address addr, AccessPermission permission) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory.changePermission(addr, permission); + } + } + + string getCoherenceRequestTypeStr(CoherenceRequestType type) { + return CoherenceRequestType_to_string(type); + } + + bool isL2CacheTagPresent(Address addr) { + return (L2cacheMemory.isTagPresent(addr)); + } + + bool isOneSharerLeft(Address addr, MachineID requestor) { + assert(L2cacheMemory[addr].Sharers.isElement(requestor)); + return (L2cacheMemory[addr].Sharers.count() == 1); + } + + bool isSharer(Address addr, MachineID requestor) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory[addr].Sharers.isElement(requestor); + } else { + return false; + } + } + + void addSharer(Address addr, MachineID requestor) { + DEBUG_EXPR(machineID); + DEBUG_EXPR(requestor); + DEBUG_EXPR(addr); + assert(map_L1CacheMachId_to_L2Cache(addr, requestor) == machineID); + L2cacheMemory[addr].Sharers.add(requestor); + } + + State getState(Address addr) { + if(L2_TBEs.isPresent(addr)) { + return L2_TBEs[addr].TBEState; + } else if (isL2CacheTagPresent(addr)) { + return getL2CacheEntry(addr).CacheState; + } + return State:NP; + } + + string getStateStr(Address addr) { + return L2Cache_State_to_string(getState(addr)); + } + + // when is this called + void setState(Address addr, State state) { + + // MUST CHANGE + if (L2_TBEs.isPresent(addr)) { + L2_TBEs[addr].TBEState := state; + } + + if (isL2CacheTagPresent(addr)) { + getL2CacheEntry(addr).CacheState := state; + + // Set permission + if (state == State:SS ) { + changeL2Permission(addr, AccessPermission:Read_Only); + } else if (state == State:M) { + changeL2Permission(addr, AccessPermission:Read_Write); + } else if (state == State:MT) { + changeL2Permission(addr, AccessPermission:Stale); + } else { + changeL2Permission(addr, AccessPermission:Busy); + } + } + } + + Event L1Cache_request_type_to_event(CoherenceRequestType type, Address addr, MachineID requestor) { + if(type == CoherenceRequestType:GETS) { + return Event:L1_GETS; + } else if(type == CoherenceRequestType:GET_INSTR) { + return Event:L1_GET_INSTR; + } else if (type == CoherenceRequestType:GETX) { + return Event:L1_GETX; + } else if (type == CoherenceRequestType:UPGRADE) { + if ( isL2CacheTagPresent(addr) && getL2CacheEntry(addr).Sharers.isElement(requestor) ) { + return Event:L1_UPGRADE; + } else { + return Event:L1_GETX; + } + } else if (type == CoherenceRequestType:PUTX) { + if (isSharer(addr, requestor)) { + return Event:L1_PUTX; + } else { + return Event:L1_PUTX_old; + } + } else { + DEBUG_EXPR(addr); + DEBUG_EXPR(type); + error("Invalid L1 forwarded request type"); + } + } + + // ** OUT_PORTS ** + + out_port(L1RequestIntraChipL2Network_out, RequestMsg, L1RequestFromL2Cache); + out_port(DirRequestIntraChipL2Network_out, RequestMsg, DirRequestFromL2Cache); + out_port(responseIntraChipL2Network_out, ResponseMsg, responseFromL2Cache); + + + // Response IntraChip L2 Network - response msg to this particular L2 bank + in_port(responseIntraChipL2Network_in, ResponseMsg, responseToL2Cache) { + if (responseIntraChipL2Network_in.isReady()) { + peek(responseIntraChipL2Network_in, ResponseMsg) { + // test wether it's from a local L1 or an off chip source + assert(in_msg.Destination.isElement(machineID)); + if(machineIDToMachineType(in_msg.Sender) == MachineType:L1Cache) { + if(in_msg.Type == CoherenceResponseType:DATA) { + if (in_msg.Dirty) { + trigger(Event:WB_Data, in_msg.Address); + } else { + trigger(Event:WB_Data_clean, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:ACK) { + if ((L2_TBEs[in_msg.Address].pendingAcks - in_msg.AckCount) == 0) { + trigger(Event:Ack_all, in_msg.Address); + } else { + trigger(Event:Ack, in_msg.Address); + } + } else { + error("unknown message type"); + } + + } else { // external message + if(in_msg.Type == CoherenceResponseType:MEMORY_DATA) { + trigger(Event:Mem_Data, in_msg.Address); // L2 now has data and all off-chip acks + } else if(in_msg.Type == CoherenceResponseType:MEMORY_ACK) { + trigger(Event:Mem_Ack, in_msg.Address); // L2 now has data and all off-chip acks + } else { + error("unknown message type"); + } + } + } + } // if not ready, do nothing + } + + // L1 Request + in_port(L1RequestIntraChipL2Network_in, RequestMsg, L1RequestToL2Cache) { + if(L1RequestIntraChipL2Network_in.isReady()) { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(id); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.Requestor); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(in_msg.Destination); + assert(machineIDToMachineType(in_msg.Requestor) == MachineType:L1Cache); + assert(in_msg.Destination.isElement(machineID)); + if (L2cacheMemory.isTagPresent(in_msg.Address)) { + // The L2 contains the block, so proceeded with handling the request + trigger(L1Cache_request_type_to_event(in_msg.Type, in_msg.Address, in_msg.Requestor), in_msg.Address); + } else { + if (L2cacheMemory.cacheAvail(in_msg.Address)) { + // L2 does't have the line, but we have space for it in the L2 + trigger(L1Cache_request_type_to_event(in_msg.Type, in_msg.Address, in_msg.Requestor), in_msg.Address); + } else { + // No room in the L2, so we need to make room before handling the request + if (L2cacheMemory[ L2cacheMemory.cacheProbe(in_msg.Address) ].Dirty ) { + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } else { + trigger(Event:L2_Replacement_clean, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } + } + } + } + } + + in_port(L1unblockNetwork_in, ResponseMsg, unblockToL2Cache) { + if(L1unblockNetwork_in.isReady()) { + peek(L1unblockNetwork_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceResponseType:EXCLUSIVE_UNBLOCK) { + trigger(Event:Exclusive_Unblock, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:UNBLOCK) { + trigger(Event:Unblock, in_msg.Address); + } else { + error("unknown unblock message"); + } + } + } + } + + // ACTIONS + + action(a_issueFetchToMemory, "a", desc="fetch data from memory") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(DirRequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + + action(b_forwardRequestToExclusive, "b", desc="Forward request to the exclusive L1") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(L2cacheMemory[address].Exclusive); + out_msg.MessageSize := MessageSizeType:Request_Control; + } + } + } + + action(c_exclusiveReplacement, "c", desc="Send data to memory") { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(ct_exclusiveReplacementFromTBE, "ct", desc="Send data to memory") { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.DataBlk := L2_TBEs[address].DataBlk; + out_msg.Dirty := L2_TBEs[address].Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + + action(d_sendDataToRequestor, "d", desc="Send data from cache to reqeustor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count(); + if (getL2CacheEntry(address).Sharers.isElement(in_msg.Requestor)) { + out_msg.AckCount := out_msg.AckCount + 1; + } + } + } + } + + action(dd_sendExclusiveDataToRequestor, "dd", desc="Send data from cache to reqeustor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count(); + if (getL2CacheEntry(address).Sharers.isElement(in_msg.Requestor)) { + out_msg.AckCount := out_msg.AckCount + 1; + } + } + } + } + + action(ds_sendSharedDataToRequestor, "ds", desc="Send data from cache to reqeustor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + out_msg.AckCount := 0; + } + } + } + + action(e_sendDataToGetSRequestors, "e", desc="Send data from cache to all GetS IDs") { + assert(L2_TBEs[address].L1_GetS_IDs.count() > 0); + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination := L2_TBEs[address].L1_GetS_IDs; // internal nodes + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(ex_sendExclusiveDataToGetSRequestors, "ex", desc="Send data from cache to all GetS IDs") { + assert(L2_TBEs[address].L1_GetS_IDs.count() == 1); + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.Destination := L2_TBEs[address].L1_GetS_IDs; // internal nodes + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + + action(ee_sendDataToGetXRequestor, "ee", desc="Send data from cache to GetX ID") { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(L2_TBEs[address].L1_GetX_ID); + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + DEBUG_EXPR(out_msg.Address); + DEBUG_EXPR(out_msg.Destination); + DEBUG_EXPR(out_msg.DataBlk); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + + action(f_sendInvToSharers, "f", desc="invalidate sharers for L2 replacement") { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := machineID; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.MessageSize := MessageSizeType:Request_Control; + } + } + + action(fw_sendFwdInvToSharers, "fw", desc="invalidate sharers for request") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.MessageSize := MessageSizeType:Request_Control; + } + } + } + + + action(fwm_sendFwdInvToSharersMinusRequestor, "fwm", desc="invalidate sharers for request, requestor is sharer") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Request_Control; + } + } + } + + // OTHER ACTIONS + action(i_allocateTBE, "i", desc="Allocate TBE for internal/external request(isPrefetch=0, number of invalidates=0)") { + check_allocate(L2_TBEs); + L2_TBEs.allocate(address); + L2_TBEs[address].L1_GetS_IDs.clear(); + L2_TBEs[address].DataBlk := getL2CacheEntry(address).DataBlk; + L2_TBEs[address].Dirty := getL2CacheEntry(address).Dirty; + L2_TBEs[address].pendingAcks := getL2CacheEntry(address).Sharers.count(); + } + + action(s_deallocateTBE, "s", desc="Deallocate external TBE") { + L2_TBEs.deallocate(address); + } + + action(jj_popL1RequestQueue, "\j", desc="Pop incoming L1 request queue") { + profileMsgDelay(0, L1RequestIntraChipL2Network_in.dequeue_getDelayCycles()); + } + + action(k_popUnblockQueue, "k", desc="Pop incoming unblock queue") { + profileMsgDelay(0, L1unblockNetwork_in.dequeue_getDelayCycles()); + } + + + action(o_popIncomingResponseQueue, "o", desc="Pop Incoming Response queue") { + profileMsgDelay(3, responseIntraChipL2Network_in.dequeue_getDelayCycles()); + } + + + action(m_writeDataToCache, "m", desc="Write data from response queue to cache") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + getL2CacheEntry(address).DataBlk := in_msg.DataBlk; + getL2CacheEntry(address).Dirty := in_msg.Dirty; + } + } + + action(mr_writeDataToCacheFromRequest, "mr", desc="Write data from response queue to cache") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + getL2CacheEntry(address).DataBlk := in_msg.DataBlk; + getL2CacheEntry(address).Dirty := in_msg.Dirty; + } + } + + action(q_updateAck, "q", desc="update pending ack count") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + L2_TBEs[address].pendingAcks := L2_TBEs[address].pendingAcks - in_msg.AckCount; + APPEND_TRANSITION_COMMENT(in_msg.AckCount); + APPEND_TRANSITION_COMMENT(" p: "); + APPEND_TRANSITION_COMMENT(L2_TBEs[address].pendingAcks); + } + } + + action(qq_writeDataToTBE, "\qq", desc="Write data from response queue to TBE") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + L2_TBEs[address].DataBlk := in_msg.DataBlk; + L2_TBEs[address].Dirty := in_msg.Dirty; + } + } + + + action(z_stall, "z", desc="Stall") { + } + + + action(ss_recordGetSL1ID, "\s", desc="Record L1 GetS for load response") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2_TBEs[address].L1_GetS_IDs.add(in_msg.Requestor); + } + } + + action(xx_recordGetXL1ID, "\x", desc="Record L1 GetX for store response") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2_TBEs[address].L1_GetX_ID := in_msg.Requestor; + } + } + + action(set_setMRU, "\set", desc="set the MRU entry") { + L2cacheMemory.setMRU(address); + } + + action(qq_allocateL2CacheBlock, "\q", desc="Set L2 cache tag equal to tag of block B.") { + if (L2cacheMemory.isTagPresent(address) == false) { + L2cacheMemory.allocate(address); + } + } + + action(rr_deallocateL2CacheBlock, "\r", desc="Deallocate L2 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + L2cacheMemory.deallocate(address); + } + + action(t_sendWBAck, "t", desc="Send writeback ACK") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:WB_ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + + action(ts_sendInvAckToUpgrader, "ts", desc="Send ACK to upgrader") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // upgrader doesn't get ack from itself, hence the + 1 + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count() + 1; + } + } + } + + action(uu_profileMiss, "\u", desc="Profile the demand miss") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + //profile_L2Cache_miss(convertToGenericType(in_msg.Type), in_msg.AccessMode, MessageSizeTypeToInt(in_msg.MessageSize), in_msg.Prefetch, L1CacheMachIDToProcessorNum(in_msg.Requestor)); + } + } + + action(ww_profileMissNoDir, "\w", desc="Profile this transition at the L2 because Dir won't see the request") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + // profile_request(in_msg.L1CacheStateStr, getStateStr(address), "NA", getCoherenceRequestTypeStr(in_msg.Type)); + } + } + + + + action(nn_addSharer, "\n", desc="Add L1 sharer to list") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + addSharer(address, in_msg.Requestor); + APPEND_TRANSITION_COMMENT( getL2CacheEntry(address).Sharers ); + } + } + + action(nnu_addSharerFromUnblock, "\nu", desc="Add L1 sharer to list") { + peek(L1unblockNetwork_in, ResponseMsg) { + addSharer(address, in_msg.Sender); + } + } + + + action(kk_removeRequestSharer, "\k", desc="Remove L1 Request sharer from list") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2cacheMemory[address].Sharers.remove(in_msg.Requestor); + } + } + + action(ll_clearSharers, "\l", desc="Remove all L1 sharers from list") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2cacheMemory[address].Sharers.clear(); + } + } + + action(mm_markExclusive, "\m", desc="set the exclusive owner") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2cacheMemory[address].Sharers.clear(); + L2cacheMemory[address].Exclusive := in_msg.Requestor; + addSharer(address, in_msg.Requestor); + } + } + + action(mmu_markExclusiveFromUnblock, "\mu", desc="set the exclusive owner") { + peek(L1unblockNetwork_in, ResponseMsg) { + L2cacheMemory[address].Sharers.clear(); + L2cacheMemory[address].Exclusive := in_msg.Sender; + addSharer(address, in_msg.Sender); + } + } + + action(zz_recycleL1RequestQueue, "zz", desc="recycle L1 request queue") { + L1RequestIntraChipL2Network_in.recycle(); + } + + //***************************************************** + // TRANSITIONS + //***************************************************** + + + //=============================================== + // BASE STATE - I + + // Transitions from I (Idle) + transition({NP, IS, ISS, IM, SS, M, M_I, MT_I, MCT_I, I_I, S_I, SS_MB, M_MB, MT_IIB, MT_IB, MT_SB}, L1_PUTX) { + jj_popL1RequestQueue; + } + + transition({NP, SS, M, MT, M_I, MT_I, MCT_I, I_I, S_I, IS, ISS, IM, SS_MB, MT_MB, M_MB, MT_IIB, MT_IB, MT_SB}, L1_PUTX_old) { + jj_popL1RequestQueue; + } + + transition({IM, IS, ISS, SS_MB, M_MB, MT_MB, MT_IIB, MT_IB, MT_SB}, {L2_Replacement, L2_Replacement_clean}) { + zz_recycleL1RequestQueue; + } + + transition({SS_MB, M_MB, MT_MB, MT_IIB, MT_IB, MT_SB}, {L1_GETS, L1_GET_INSTR, L1_GETX, L1_UPGRADE}) { + zz_recycleL1RequestQueue; + } + + + transition(NP, L1_GETS, ISS) { + qq_allocateL2CacheBlock; + ll_clearSharers; + nn_addSharer; + i_allocateTBE; + ss_recordGetSL1ID; + a_issueFetchToMemory; + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition(NP, L1_GET_INSTR, IS) { + qq_allocateL2CacheBlock; + ll_clearSharers; + nn_addSharer; + i_allocateTBE; + ss_recordGetSL1ID; + a_issueFetchToMemory; + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition(NP, L1_GETX, IM) { + qq_allocateL2CacheBlock; + ll_clearSharers; + // nn_addSharer; + i_allocateTBE; + xx_recordGetXL1ID; + a_issueFetchToMemory; + uu_profileMiss; + jj_popL1RequestQueue; + } + + + // transitions from IS/IM + + transition(ISS, Mem_Data, MT_MB) { + m_writeDataToCache; + ex_sendExclusiveDataToGetSRequestors; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IS, Mem_Data, SS) { + m_writeDataToCache; + e_sendDataToGetSRequestors; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IM, Mem_Data, MT_MB) { + m_writeDataToCache; + ee_sendDataToGetXRequestor; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition({IS, ISS}, {L1_GETS, L1_GET_INSTR}, IS) { + nn_addSharer; + ss_recordGetSL1ID; + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition({IS, ISS}, L1_GETX) { + zz_recycleL1RequestQueue; + } + + transition(IM, {L1_GETX, L1_GETS, L1_GET_INSTR}) { + zz_recycleL1RequestQueue; + } + + // transitions from SS + transition(SS, {L1_GETS, L1_GET_INSTR}) { + ds_sendSharedDataToRequestor; + nn_addSharer; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + + transition(SS, L1_GETX, SS_MB) { + d_sendDataToRequestor; + // fw_sendFwdInvToSharers; + fwm_sendFwdInvToSharersMinusRequestor; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(SS, L1_UPGRADE, SS_MB) { + fwm_sendFwdInvToSharersMinusRequestor; + ts_sendInvAckToUpgrader; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(SS, L2_Replacement_clean, I_I) { + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(SS, L2_Replacement, S_I) { + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(M, L1_GETX, MT_MB) { + d_sendDataToRequestor; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(M, L1_GET_INSTR, SS) { + d_sendDataToRequestor; + nn_addSharer; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(M, L1_GETS, MT_MB) { + dd_sendExclusiveDataToRequestor; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(M, L2_Replacement, M_I) { + i_allocateTBE; + c_exclusiveReplacement; + rr_deallocateL2CacheBlock; + } + + transition(M, L2_Replacement_clean, M_I) { + rr_deallocateL2CacheBlock; + } + + + // transitions from MT + + transition(MT, L1_GETX, MT_MB) { + b_forwardRequestToExclusive; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + + transition(MT, {L1_GETS, L1_GET_INSTR}, MT_IIB) { + b_forwardRequestToExclusive; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(MT, L2_Replacement, MT_I) { + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(MT, L2_Replacement_clean, MCT_I) { + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(MT, L1_PUTX, M) { + ll_clearSharers; + mr_writeDataToCacheFromRequest; + t_sendWBAck; + jj_popL1RequestQueue; + } + + + // transitions from blocking states + transition(SS_MB, Unblock_Cancel, SS) { + k_popUnblockQueue; + } + + transition(MT_MB, Unblock_Cancel, MT) { + k_popUnblockQueue; + } + + transition(MT_IB, Unblock_Cancel, MT) { + k_popUnblockQueue; + } + + transition(SS_MB, Exclusive_Unblock, MT) { + // update actual directory + mmu_markExclusiveFromUnblock; + k_popUnblockQueue; + } + + transition({M_MB, MT_MB}, Exclusive_Unblock, MT) { + // update actual directory + mmu_markExclusiveFromUnblock; + k_popUnblockQueue; + } + + transition(MT_IIB, Unblock, MT_IB) { + nnu_addSharerFromUnblock; + k_popUnblockQueue; + } + + transition(MT_IIB, {WB_Data, WB_Data_clean}, MT_SB) { + m_writeDataToCache; + o_popIncomingResponseQueue; + } + + transition(MT_IB, {WB_Data, WB_Data_clean}, SS) { + m_writeDataToCache; + o_popIncomingResponseQueue; + } + + transition(MT_SB, Unblock, SS) { + nnu_addSharerFromUnblock; + k_popUnblockQueue; + } + + // writeback states + transition({I_I, S_I, MT_I, MCT_I, M_I}, {L1_GETX, L1_UPGRADE, L1_GETS, L1_GET_INSTR}) { + zz_recycleL1RequestQueue; + } + + transition(I_I, Ack) { + q_updateAck; + o_popIncomingResponseQueue; + } + + transition(I_I, Ack_all, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition({MT_I, MCT_I}, WB_Data, M_I) { + qq_writeDataToTBE; + ct_exclusiveReplacementFromTBE; + o_popIncomingResponseQueue; + } + + transition(MCT_I, WB_Data_clean, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // L1 never changed Dirty data + transition(MT_I, Ack_all, M_I) { + ct_exclusiveReplacementFromTBE; + o_popIncomingResponseQueue; + } + + // clean data that L1 exclusive never wrote + transition(MCT_I, Ack_all, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // drop this because L1 will send data again + // the reason we don't accept is that the request virtual network may be completely backed up + // transition(MT_I, L1_PUTX) { + // jj_popL1RequestQueue; + //} + + // possible race between unblock and immediate replacement + transition(MT_MB, L1_PUTX) { + zz_recycleL1RequestQueue; + } + + transition(MT_I, WB_Data_clean, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(S_I, Ack) { + q_updateAck; + o_popIncomingResponseQueue; + } + + transition(S_I, Ack_all, M_I) { + ct_exclusiveReplacementFromTBE; + o_popIncomingResponseQueue; + } + + transition(M_I, Mem_Ack, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } +} + diff --git a/src/mem/protocol/MESI_CMP_directory-mem.sm b/src/mem/protocol/MESI_CMP_directory-mem.sm new file mode 100644 index 000000000..1fcd234fe --- /dev/null +++ b/src/mem/protocol/MESI_CMP_directory-mem.sm @@ -0,0 +1,166 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOESI_CMP_token-dir.sm 1.6 05/01/19 15:48:35-06:00 mikem@royal16.cs.wisc.edu $ + */ + + +machine(Directory, "Token protocol") { + + MessageBuffer requestToDir, network="From", virtual_network="2", ordered="false"; + MessageBuffer responseToDir, network="From", virtual_network="3", ordered="false"; + MessageBuffer responseFromDir, network="To", virtual_network="3", ordered="false"; + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_I") { + // Base states + I, desc="Owner"; + } + + // Events + enumeration(Event, desc="Directory events") { + Fetch, desc="A GETX arrives"; + Data, desc="A GETS arrives"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + DataBlock DataBlk, desc="data for the block"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + + State getState(Address addr) { + return State:I; + } + + void setState(Address addr, State state) { + } + + // ** OUT_PORTS ** + out_port(responseNetwork_out, ResponseMsg, responseFromDir); + + // ** IN_PORTS ** + + in_port(requestNetwork_in, RequestMsg, requestToDir) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:Fetch, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:Fetch, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + in_port(responseNetwork_in, ResponseMsg, responseToDir) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceResponseType:MEMORY_DATA) { + trigger(Event:Data, in_msg.Address); + } else { + DEBUG_EXPR(in_msg.Type); + error("Invalid message"); + } + } + } + } + + // Actions + action(a_sendAck, "a", desc="Send ack to L2") { + peek(responseNetwork_in, ResponseMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Sender); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + + action(d_sendData, "d", desc="Send data to requestor") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(j_popIncomingRequestQueue, "j", desc="Pop incoming request queue") { + requestNetwork_in.dequeue(); + } + + action(k_popIncomingResponseQueue, "k", desc="Pop incoming request queue") { + responseNetwork_in.dequeue(); + } + + action(m_writeDataToMemory, "m", desc="Write dirty writeback to memory") { + peek(responseNetwork_in, ResponseMsg) { + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + // TRANSITIONS + + transition(I, Fetch) { + d_sendData; + j_popIncomingRequestQueue; + } + + transition(I, Data) { + m_writeDataToMemory; + a_sendAck; + k_popIncomingResponseQueue; + } +} diff --git a/src/mem/protocol/MESI_CMP_directory-msg.sm b/src/mem/protocol/MESI_CMP_directory-msg.sm new file mode 100644 index 000000000..c2d02b59d --- /dev/null +++ b/src/mem/protocol/MESI_CMP_directory-msg.sm @@ -0,0 +1,112 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MSI_MOSI_CMP_directory-msg.sm 1.5 05/01/19 15:48:37-06:00 mikem@royal16.cs.wisc.edu $ + * + */ + +// CoherenceRequestType +enumeration(CoherenceRequestType, desc="...") { + GETX, desc="Get eXclusive"; + UPGRADE, desc="UPGRADE to exclusive"; + GETS, desc="Get Shared"; + GET_INSTR, desc="Get Instruction"; + INV, desc="INValidate"; + PUTX, desc="replacement message"; +} + +// CoherenceResponseType +enumeration(CoherenceResponseType, desc="...") { + MEMORY_ACK, desc="Ack from memory controller"; + DATA, desc="Data"; + DATA_EXCLUSIVE, desc="Data"; + MEMORY_DATA, desc="Data"; + ACK, desc="Generic invalidate ack"; + WB_ACK, desc="writeback ack"; + UNBLOCK, desc="unblock"; + EXCLUSIVE_UNBLOCK, desc="exclusive unblock"; +} + +// RequestMsg +structure(RequestMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)"; + AccessModeType AccessMode, desc="user/supervisor access type"; + MachineID Requestor , desc="What component request"; + NetDest Destination, desc="What components receive the request, includes MachineType and num"; + MessageSizeType MessageSize, desc="size category of the message"; + DataBlock DataBlk, desc="Data for the cache line (if PUTX)"; + bool Dirty, default="false", desc="Dirty bit"; + PrefetchBit Prefetch, desc="Is this a prefetch request"; +} + +// ResponseMsg +structure(ResponseMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)"; + MachineID Sender, desc="What component sent the data"; + NetDest Destination, desc="Node to whom the data is sent"; + DataBlock DataBlk, desc="Data for the cache line"; + bool Dirty, default="false", desc="Dirty bit"; + int AckCount, default="0", desc="number of acks in this message"; + MessageSizeType MessageSize, desc="size category of the message"; +} + +/* +GenericRequestType convertToGenericType(CoherenceRequestType type) { + if(type == CoherenceRequestType:PUTX) { + return GenericRequestType:PUTX; + } else if(type == CoherenceRequestType:GETS) { + return GenericRequestType:GETS; + } else if(type == CoherenceRequestType:GET_INSTR) { + return GenericRequestType:GET_INSTR; + } else if(type == CoherenceRequestType:GETX) { + return GenericRequestType:GETX; + } else if(type == CoherenceRequestType:UPGRADE) { + return GenericRequestType:UPGRADE; + } else if(type == CoherenceRequestType:PUTS) { + return GenericRequestType:PUTS; + } else if(type == CoherenceRequestType:INV) { + return GenericRequestType:INV; + } else if(type == CoherenceRequestType:INV_S) { + return GenericRequestType:INV_S; + } else if(type == CoherenceRequestType:L1_DG) { + return GenericRequestType:DOWNGRADE; + } else if(type == CoherenceRequestType:WB_ACK) { + return GenericRequestType:WB_ACK; + } else if(type == CoherenceRequestType:EXE_ACK) { + return GenericRequestType:EXE_ACK; + } else { + DEBUG_EXPR(type); + error("invalid CoherenceRequestType"); + } +} +*/ + diff --git a/src/mem/protocol/MESI_CMP_directory.slicc b/src/mem/protocol/MESI_CMP_directory.slicc new file mode 100644 index 000000000..34303f97e --- /dev/null +++ b/src/mem/protocol/MESI_CMP_directory.slicc @@ -0,0 +1,5 @@ +MESI_CMP_directory-msg.sm +MESI_CMP_directory-L2cache.sm +MESI_CMP_directory-L1cache.sm +MESI_CMP_directory-mem.sm +standard_CMP-protocol.sm diff --git a/src/mem/protocol/MESI_CMP_filter_directory-L1cache.sm b/src/mem/protocol/MESI_CMP_filter_directory-L1cache.sm new file mode 100644 index 000000000..468cf3c1c --- /dev/null +++ b/src/mem/protocol/MESI_CMP_filter_directory-L1cache.sm @@ -0,0 +1,1800 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + This file has been modified by Kevin Moore and Dan Nussbaum of the + Scalable Systems Research Group at Sun Microsystems Laboratories + (http://research.sun.com/scalable/) to support the Adaptive + Transactional Memory Test Platform (ATMTP). + + Please send email to atmtp-interest@sun.com with feedback, questions, or + to request future announcements about ATMTP. + + ---------------------------------------------------------------------- + + File modification date: 2008-02-23 + + ---------------------------------------------------------------------- +*/ + +/* + * $Id$ + * + */ + + +machine(L1Cache, "MESI Directory L1 Cache CMP") { + + // NODE L1 CACHE + // From this node's L1 cache TO the network + // a local L1 -> this L2 bank, currently ordered with directory forwarded requests + MessageBuffer requestFromL1Cache, network="To", virtual_network="0", ordered="false"; + // a local L1 -> this L2 bank + MessageBuffer responseFromL1Cache, network="To", virtual_network="3", ordered="false"; + MessageBuffer unblockFromL1Cache, network="To", virtual_network="4", ordered="false"; + + + // To this node's L1 cache FROM the network + // a L2 bank -> this L1 + MessageBuffer requestToL1Cache, network="From", virtual_network="1", ordered="false"; + // a L2 bank -> this L1 + MessageBuffer responseToL1Cache, network="From", virtual_network="3", ordered="false"; + + // STATES + enumeration(State, desc="Cache states", default="L1Cache_State_I") { + // Base states + NP, desc="Not present in either cache"; + I, desc="a L1 cache entry Idle"; + S, desc="a L1 cache entry Shared"; + E, desc="a L1 cache entry Exclusive"; + M, desc="a L1 cache entry Modified", format="!b"; + + // Transient States + IS, desc="L1 idle, issued GETS, have not seen response yet"; + IM, desc="L1 idle, issued GETX, have not seen response yet"; + SM, desc="L1 idle, issued GETX, have not seen response yet"; + IS_I, desc="L1 idle, issued GETS, saw Inv before data because directory doesn't block on GETS hit"; + IS_S, desc="L1 idle, issued GETS, L2 sent us data but responses from filters have not arrived"; + IS_E, desc="L1 idle, issued GETS, L2 sent us exclusive data, but responses from filters have not arrived"; + IM_M, desc="L1 idle, issued GETX, L2 sent us data, but responses from filters have not arrived"; + + M_I, desc="L1 replacing, waiting for ACK"; + E_I, desc="L1 replacing, waiting for ACK"; + + } + + // EVENTS + enumeration(Event, desc="Cache events") { + // L1 events + Load, desc="Load request from the home processor"; + Ifetch, desc="I-fetch request from the home processor"; + Store, desc="Store request from the home processor"; + + Replace, desc="lower level cache replaced this line, also need to invalidate to maintain inclusion"; + Inv, desc="Invalidate request from L2 bank"; + Inv_X, desc="Invalidate request from L2 bank, trans CONFLICT"; + + // internal generated request + L1_Replacement, desc="L1 Replacement", format="!r"; + L1_Replacement_XACT, desc="L1 Replacement of trans. data", format="!r"; + + // other requests + Fwd_GETX, desc="GETX from other processor"; + Fwd_GETS, desc="GETS from other processor"; + Fwd_GET_INSTR, desc="GET_INSTR from other processor"; + + //Data, desc="Data for processor"; + L2_Data, desc="Data for processor, from L2"; + L2_Data_all_Acks, desc="Data for processor, from L2, all acks"; + L2_Exclusive_Data, desc="Exlusive Data for processor, from L2"; + L2_Exclusive_Data_all_Acks, desc="Exlusive Data for processor, from L2, all acks"; + DataS_fromL1, desc="data for GETS request, need to unblock directory"; + Data_all_Acks, desc="Data for processor, all acks"; + + Ack, desc="Ack for processor"; + Ack_all, desc="Last ack for processor"; + + WB_Ack, desc="Ack for replacement"; + + // Transactional responses/requests + Nack, desc="Nack for processor"; + Nack_all, desc="Last Nack for processor"; + Check_Write_Filter, desc="Check the write filter"; + Check_Read_Write_Filter, desc="Check the read and write filters"; + + //Fwd_GETS_T, desc="A GetS from another processor, part of a trans, but not a conflict"; + Fwd_GETS_X, desc="A GetS from another processor, trans CONFLICT"; + Fwd_GETX_X, desc="A GetS from another processor, trans CONFLICT"; + Fwd_GET_INSTR_X, desc="A GetInstr from another processor, trans CONFLICT"; + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry" ) { + State CacheState, desc="cache state"; + DataBlock DataBlk, desc="data for the block"; + bool Dirty, default="false", desc="data is dirty"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Line address for this TBE"; + Address PhysicalAddress, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="Buffer for the data block"; + bool Dirty, default="false", desc="data is dirty"; + bool isPrefetch, desc="Set if this was caused by a prefetch"; + int pendingAcks, default="0", desc="number of pending acks"; + int ThreadID, default="0", desc="SMT thread issuing the request"; + + bool RemoveLastOwnerFromDir, default="false", desc="The forwarded data was being replaced"; + MachineID LastOwnerID, desc="What component forwarded (last owned) the data"; // For debugging + + // for Transactional Memory + uint64 Timestamp, default="0", desc="Timestamp of request"; + bool nack, default="false", desc="has this request been nacked?"; + NetDest Nackers, desc="The nodes which sent a NACK to us"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + TBETable L1_TBEs, template_hack=""; + + CacheMemory L1IcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1I"', abstract_chip_ptr="true"; + CacheMemory L1DcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1D"', abstract_chip_ptr="true"; + + + MessageBuffer mandatoryQueue, ordered="false", rank="100", abstract_chip_ptr="true"; + + Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i"; + TransactionInterfaceManager xact_mgr, abstract_chip_ptr="true", constructor_hack="i"; + + // triggerQueue used to indicate when all acks/nacks have been received + MessageBuffer triggerQueue, ordered="false"; + + int cache_state_to_int(State state); + + // inclusive cache returns L1 entries only + Entry getL1CacheEntry(Address addr), return_by_ref="yes" { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory[addr]; + } else { + return L1IcacheMemory[addr]; + } + } + + void changeL1Permission(Address addr, AccessPermission permission) { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory.changePermission(addr, permission); + } else if(L1IcacheMemory.isTagPresent(addr)) { + return L1IcacheMemory.changePermission(addr, permission); + } else { + error("cannot change permission, L1 block not present"); + } + } + + bool isL1CacheTagPresent(Address addr) { + return (L1DcacheMemory.isTagPresent(addr) || L1IcacheMemory.isTagPresent(addr)); + } + + State getState(Address addr) { + if((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == true){ + DEBUG_EXPR(id); + DEBUG_EXPR(addr); + } + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + + if(L1_TBEs.isPresent(addr)) { + return L1_TBEs[addr].TBEState; + } else if (isL1CacheTagPresent(addr)) { + return getL1CacheEntry(addr).CacheState; + } + return State:NP; + } + + + // For detecting read/write conflicts on requests from remote processors + bool shouldNackLoad(Address addr, uint64 remote_timestamp, MachineID remote_id){ + return xact_mgr.shouldNackLoad(addr, remote_timestamp, remote_id); + } + + bool shouldNackStore(Address addr, uint64 remote_timestamp, MachineID remote_id){ + return xact_mgr.shouldNackStore(addr, remote_timestamp, remote_id); + } + + // For querying read/write signatures on current processor + bool checkReadWriteSignatures(Address addr){ + return xact_mgr.checkReadWriteSignatures(addr); + } + + bool checkWriteSignatures(Address addr){ + return xact_mgr.checkWriteSignatures(addr); + } + + void setState(Address addr, State state) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + + // MUST CHANGE + if(L1_TBEs.isPresent(addr)) { + L1_TBEs[addr].TBEState := state; + } + + if (isL1CacheTagPresent(addr)) { + getL1CacheEntry(addr).CacheState := state; + + // Set permission + if (state == State:I) { + changeL1Permission(addr, AccessPermission:Invalid); + } else if (state == State:S || state == State:E) { + changeL1Permission(addr, AccessPermission:Read_Only); + } else if (state == State:M) { + changeL1Permission(addr, AccessPermission:Read_Write); + } else { + changeL1Permission(addr, AccessPermission:Busy); + } + } + } + + Event mandatory_request_type_to_event(CacheRequestType type) { + if (type == CacheRequestType:LD) { + return Event:Load; + } else if (type == CacheRequestType:LD_XACT) { + return Event:Load; + } else if (type == CacheRequestType:IFETCH) { + return Event:Ifetch; + } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { + return Event:Store; + } else if((type == CacheRequestType:ST_XACT) || (type == CacheRequestType:LDX_XACT) ) { + return Event:Store; + } else { + error("Invalid CacheRequestType"); + } + } + + + void printRequest(CacheMsg in_msg){ + DEBUG_EXPR("Regquest msg: "); + DEBUG_EXPR(machineID); + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.PhysicalAddress); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(in_msg.ProgramCounter); + DEBUG_EXPR(in_msg.AccessMode); + DEBUG_EXPR(in_msg.Size); + DEBUG_EXPR(in_msg.Prefetch); + DEBUG_EXPR(in_msg.Version); + DEBUG_EXPR(in_msg.LogicalAddress); + DEBUG_EXPR(in_msg.ThreadID); + DEBUG_EXPR(in_msg.Timestamp); + DEBUG_EXPR(in_msg.ExposedAction); + } + + out_port(requestIntraChipL1Network_out, RequestMsg, requestFromL1Cache); + out_port(responseIntraChipL1Network_out, ResponseMsg, responseFromL1Cache); + out_port(unblockNetwork_out, ResponseMsg, unblockFromL1Cache); + out_port(triggerQueue_out, TriggerMsg, triggerQueue); + + // Trigger Queue + in_port(triggerQueue_in, TriggerMsg, triggerQueue) { + if (triggerQueue_in.isReady()) { + peek(triggerQueue_in, TriggerMsg) { + if (in_msg.Type == TriggerType:ALL_ACKS) { + if (L1_TBEs[in_msg.Address].nack == true){ + trigger(Event:Nack_all, in_msg.Address); + } else { + trigger(Event:Ack_all, in_msg.Address); + } + } else { + error("Unexpected message"); + } + } + } + } + + // Response IntraChip L1 Network - response msg to this L1 cache + in_port(responseIntraChipL1Network_in, ResponseMsg, responseToL1Cache) { + if (responseIntraChipL1Network_in.isReady()) { + peek(responseIntraChipL1Network_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if(in_msg.Type == CoherenceResponseType:L2_DATA_EXCLUSIVE) { + if( in_msg.AckCount == 0 ){ + trigger(Event:L2_Exclusive_Data_all_Acks, in_msg.Address); + } + else{ + trigger(Event:L2_Exclusive_Data, in_msg.Address); + } + } else if(in_msg.Type == CoherenceResponseType:L2_DATA) { + if( in_msg.AckCount == 0 ){ + trigger(Event:L2_Data_all_Acks, in_msg.Address); + } + else{ + trigger(Event:L2_Data, in_msg.Address); + } + } else if(in_msg.Type == CoherenceResponseType:DATA) { + if ( (getState(in_msg.Address) == State:IS || getState(in_msg.Address) == State:IS_I) && + machineIDToMachineType(in_msg.Sender) == MachineType:L1Cache ) { + + trigger(Event:DataS_fromL1, in_msg.Address); + } else if ( (L1_TBEs[in_msg.Address].pendingAcks - in_msg.AckCount) == 0 ) { + trigger(Event:Data_all_Acks, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:ACK) { + trigger(Event:Ack, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:NACK) { + trigger(Event:Nack, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WB_ACK) { + trigger(Event:WB_Ack, in_msg.Address); + } else { + error("Invalid L1 response type"); + } + } + } + } + + // Request InterChip network - request from this L1 cache to the shared L2 + in_port(requestIntraChipL1Network_in, RequestMsg, requestToL1Cache) { + if(requestIntraChipL1Network_in.isReady()) { + peek(requestIntraChipL1Network_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceRequestType:INV) { + // check whether we have a inter-proc conflict + if(shouldNackStore(in_msg.PhysicalAddress, in_msg.Timestamp, in_msg.Requestor) == false){ + trigger(Event:Inv, in_msg.Address); + } + else{ + // there's a conflict + trigger(Event:Inv_X, in_msg.Address); + } + } else if(in_msg.Type == CoherenceRequestType:INV_ESCAPE) { + // we cannot NACK this + trigger(Event:Inv, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX || in_msg.Type == CoherenceRequestType:UPGRADE) { + // check whether we have a conflict + if(shouldNackStore(in_msg.PhysicalAddress, in_msg.Timestamp, in_msg.Requestor) == true){ + trigger(Event:Fwd_GETX_X, in_msg.Address); + } + else{ + // else no conflict + // upgrade transforms to GETX due to race + trigger(Event:Fwd_GETX, in_msg.Address); + } + } else if(in_msg.Type == CoherenceRequestType:GETX_ESCAPE) { + // no need for filter checks + trigger(Event:Fwd_GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETS) { + // check whether we have a conflict + if(shouldNackLoad(in_msg.PhysicalAddress, in_msg.Timestamp, in_msg.Requestor) == true){ + trigger(Event:Fwd_GETS_X, in_msg.Address); + } + else{ + // else no conflict + trigger(Event:Fwd_GETS, in_msg.Address); + } + } else if(in_msg.Type == CoherenceRequestType:GETS_ESCAPE) { + // no need for filter checks + trigger(Event:Fwd_GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GET_INSTR) { + if(shouldNackLoad(in_msg.PhysicalAddress, in_msg.Timestamp, in_msg.Requestor) == true){ + trigger(Event:Fwd_GET_INSTR_X, in_msg.Address); + } + else{ + // else no conflict + trigger(Event:Fwd_GET_INSTR, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:GET_INSTR_ESCAPE) { + // no need for filter checks + trigger(Event:Fwd_GET_INSTR, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:REPLACE) { + trigger(Event:Replace, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:CHECK_WRITE_FILTER) { + trigger(Event:Check_Write_Filter, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:CHECK_READ_WRITE_FILTER) { + trigger(Event:Check_Read_Write_Filter, in_msg.Address); + } else { + error("Invalid forwarded request type"); + } + } + } + } + + // Mandatory Queue betweens Node's CPU and it's L1 caches + in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { + if (mandatoryQueue_in.isReady()) { + peek(mandatoryQueue_in, CacheMsg) { + + // Check for data access to blocks in I-cache and ifetchs to blocks in D-cache + + if (in_msg.Type == CacheRequestType:IFETCH) { + // ** INSTRUCTION ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // check whether block is transactional + if (checkReadWriteSignatures(in_msg.Address) == true){ + // The block is in the wrong L1, put the request on the queue to the shared L2 + trigger(Event:L1_Replacement_XACT, in_msg.Address); + } + else{ + // The block is in the wrong L1, put the request on the queue to the shared L2 + trigger(Event:L1_Replacement, in_msg.Address); + } + } + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 asks the L2 for it. + printRequest(in_msg); + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1IcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 so let's see if the L2 has it + printRequest(in_msg); + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // check whether block is transactional + if(checkReadWriteSignatures( L1IcacheMemory.cacheProbe(in_msg.Address) ) == true){ + // No room in the L1, so we need to make room in the L1 + trigger(Event:L1_Replacement_XACT, L1IcacheMemory.cacheProbe(in_msg.Address)); + } + else{ + // No room in the L1, so we need to make room in the L1 + trigger(Event:L1_Replacement, L1IcacheMemory.cacheProbe(in_msg.Address)); + } + } + } + } else { + // *** DATA ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // check whether block is transactional + if(checkReadWriteSignatures(in_msg.Address) == true){ + // The block is in the wrong L1, put the request on the queue to the shared L2 + trigger(Event:L1_Replacement_XACT, in_msg.Address); + } + else{ + // The block is in the wrong L1, put the request on the queue to the shared L2 + trigger(Event:L1_Replacement, in_msg.Address); + } + } + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 ask the L2 for it + printRequest(in_msg); + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1DcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 let's see if the L2 has it + printRequest(in_msg); + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // check whether block is transactional + if(checkReadWriteSignatures( L1DcacheMemory.cacheProbe(in_msg.Address) ) == true){ + // No room in the L1, so we need to make room in the L1 + trigger(Event:L1_Replacement_XACT, L1DcacheMemory.cacheProbe(in_msg.Address)); + } + else{ + // No room in the L1, so we need to make room in the L1 + trigger(Event:L1_Replacement, L1DcacheMemory.cacheProbe(in_msg.Address)); + } + } + } + } + } + } + } + + // ACTIONS + action(a_issueGETS, "a", desc="Issue GETS") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + if(in_msg.ExposedAction){ + out_msg.Type := CoherenceRequestType:GETS_ESCAPE; + } + else{ + out_msg.Type := CoherenceRequestType:GETS; + } + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + // either return transactional timestamp or current time + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(out_msg.Timestamp); + } + } + } + + action(ai_issueGETINSTR, "ai", desc="Issue GETINSTR") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + if(in_msg.ExposedAction){ + out_msg.Type := CoherenceRequestType:GET_INSTR_ESCAPE; + } + else{ + out_msg.Type := CoherenceRequestType:GET_INSTR; + } + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + // either return transactional timestamp or current time + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(out_msg.Timestamp); + } + } + } + + + action(b_issueGETX, "b", desc="Issue GETX") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + if(in_msg.ExposedAction){ + out_msg.Type := CoherenceRequestType:GETX_ESCAPE; + } + else{ + out_msg.Type := CoherenceRequestType:GETX; + } + out_msg.Requestor := machineID; + DEBUG_EXPR(machineID); + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + // either return transactional timestamp or current time + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(out_msg.Timestamp); + } + } + } + + action(c_issueUPGRADE, "c", desc="Issue GETX") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceRequestType:UPGRADE; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + // either return transactional timestamp or current time + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(out_msg.Timestamp); + } + } + } + + /****************************BEGIN Transactional Actions*************************/ + // send a NACK to requestor - the equivalent of a NACKed data response + // Note we don't have to track the ackCount here because we only send data NACKs when + // we are exclusive with the data. Otherwise the L2 will source the data (and set the ackCount + // appropriately) + action(e_sendNackToRequestor, "en", desc="send nack to requestor (could be L2 or L1)") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceResponseType:NACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // send oldest timestamp (or current time if no thread in transaction) + out_msg.Timestamp := xact_mgr.getOldestTimestamp(); + APPEND_TRANSITION_COMMENT(out_msg.Timestamp); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.Timestamp); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + // ackCount is by default 0 + } + // also inform driver about sending NACK + xact_mgr.notifySendNack(in_msg.PhysicalAddress, in_msg.Timestamp, in_msg.Requestor); + } + } + + // send a NACK when L2 wants us to invalidate ourselves + action(fi_sendInvNack, "fin", desc="send data to the L2 cache") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceResponseType:NACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // send oldest timestamp (or current time if no thread in transaction) + out_msg.Timestamp := xact_mgr.getOldestTimestamp(); + out_msg.AckCount := 1; + APPEND_TRANSITION_COMMENT(out_msg.Timestamp); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.Timestamp); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + } + // also inform driver about sending NACK + xact_mgr.notifySendNack(in_msg.PhysicalAddress, in_msg.Timestamp, in_msg.Requestor); + } + } + + // for when we want to check our Write filters + action(a_checkWriteFilter, "awf", desc="Check our write filter for conflicts") { + peek(requestIntraChipL1Network_in, RequestMsg) { + // For correct conflict detection, should call shouldNackLoad() NOT + // checkWriteSignatures() + if(shouldNackLoad(in_msg.PhysicalAddress, in_msg.Timestamp, in_msg.Requestor) == true){ + // conflict - send a NACK + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceResponseType:NACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // send oldest timestamp (or current time if no thread in transaction) + out_msg.Timestamp := xact_mgr.getOldestTimestamp(); + out_msg.AckCount := 1; + APPEND_TRANSITION_COMMENT(out_msg.Timestamp); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.Timestamp); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + } + // also inform driver about sending NACK + xact_mgr.notifySendNack(in_msg.PhysicalAddress, in_msg.Timestamp, in_msg.Requestor); + } + else{ + // no conflict - send ACK + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + out_msg.AckCount := 1; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + } + } + } + } + + // for when we want to check our Read + Write filters + action(a_checkReadWriteFilter, "arwf", desc="Check our write filter for conflicts") { + peek(requestIntraChipL1Network_in, RequestMsg) { + // For correct conflict detection, we should call shouldNackStore() NOT + // checkReadWriteSignatures() + if(shouldNackStore(in_msg.PhysicalAddress, in_msg.Timestamp,in_msg.Requestor ) == true){ + // conflict - send a NACK + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceResponseType:NACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // send oldest timestamp (or current time if no thread in transaction) + out_msg.Timestamp := xact_mgr.getOldestTimestamp(); + out_msg.AckCount := 1; + APPEND_TRANSITION_COMMENT(out_msg.Timestamp); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.Timestamp); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + } + // also inform driver about sending NACK + xact_mgr.notifySendNack(in_msg.PhysicalAddress, in_msg.Timestamp, in_msg.Requestor); + } + else{ + // no conflict - send ACK + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + out_msg.AckCount := 1; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + } + } + } + } + + action(r_notifyReceiveNack, "nrn", desc="Notify the driver when a nack is received"){ + peek(responseIntraChipL1Network_in, ResponseMsg) { + xact_mgr.notifyReceiveNack(L1_TBEs[address].ThreadID, in_msg.PhysicalAddress, L1_TBEs[address].Timestamp, in_msg.Timestamp, in_msg.Sender); + } + } + + // Used to driver to take abort or retry action + action(r_notifyReceiveNackFinal, "nrnf", desc="Notify the driver when the final nack is received"){ + xact_mgr.notifyReceiveNackFinal(L1_TBEs[address].ThreadID, L1_TBEs[address].PhysicalAddress); + } + + // this version uses physical address stored in TBE + + action(x_tbeSetPrefetch, "xp", desc="Set the prefetch bit in the TBE."){ + peek(mandatoryQueue_in, CacheMsg) { + if(in_msg.Prefetch == PrefetchBit:No){ + L1_TBEs[address].isPrefetch := false; + } + else{ + assert(in_msg.Prefetch == PrefetchBit:Yes); + L1_TBEs[address].isPrefetch := true; + } + } + } + + action(x_tbeSetPhysicalAddress, "ia", desc="Sets the physical address field of the TBE"){ + peek(mandatoryQueue_in, CacheMsg) { + L1_TBEs[address].PhysicalAddress := in_msg.PhysicalAddress; + L1_TBEs[address].ThreadID := in_msg.ThreadID; + L1_TBEs[address].Timestamp := in_msg.Timestamp; + } + } + + // Send unblock cancel to L2 (for nacked requests that blocked directory) + action(jj_sendUnblockCancel, "\jc", desc="send unblock to the L2 cache") { + enqueue(unblockNetwork_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:UNBLOCK_CANCEL; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Control; + // also pass along list of NACKers + out_msg.Nackers := L1_TBEs[address].Nackers; + } + } + + //same as ACK case, but sets the NACK flag for TBE entry + action(q_updateNackCount, "qn", desc="Update ack count") { + peek(responseIntraChipL1Network_in, ResponseMsg) { + // mark this request as having been NACKed + L1_TBEs[address].nack := true; + APPEND_TRANSITION_COMMENT(" before pendingAcks: "); + APPEND_TRANSITION_COMMENT(L1_TBEs[address].pendingAcks); + L1_TBEs[address].pendingAcks := L1_TBEs[address].pendingAcks - in_msg.AckCount; + L1_TBEs[address].Nackers.add(in_msg.Sender); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.AckCount); + + APPEND_TRANSITION_COMMENT(" after pendingAcks: "); + APPEND_TRANSITION_COMMENT(L1_TBEs[address].pendingAcks); + APPEND_TRANSITION_COMMENT(" sender: "); + APPEND_TRANSITION_COMMENT(in_msg.Sender); + if (L1_TBEs[address].pendingAcks == 0) { + enqueue(triggerQueue_out, TriggerMsg) { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := TriggerType:ALL_ACKS; + APPEND_TRANSITION_COMMENT(" Triggering All_Acks"); + } + } + } + } + + action(q_profileOverflow, "po", desc="profile the overflowed block"){ + profileOverflow(address, machineID); + } + + action(qq_xactReplacement, "\q", desc="replaced a transactional block"){ + xact_mgr.xactReplacement(address); + } + + action(p_profileRequest, "pcc", desc="Profile request msg") { + peek(mandatoryQueue_in, CacheMsg) { + APPEND_TRANSITION_COMMENT(" request: Timestamp: "); + APPEND_TRANSITION_COMMENT(in_msg.Timestamp); + APPEND_TRANSITION_COMMENT(" PA: "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + APPEND_TRANSITION_COMMENT(" Type: "); + APPEND_TRANSITION_COMMENT(in_msg.Type); + APPEND_TRANSITION_COMMENT(" VPC: "); + APPEND_TRANSITION_COMMENT(in_msg.ProgramCounter); + APPEND_TRANSITION_COMMENT(" Mode: "); + APPEND_TRANSITION_COMMENT(in_msg.AccessMode); + APPEND_TRANSITION_COMMENT(" PF: "); + APPEND_TRANSITION_COMMENT(in_msg.Prefetch); + APPEND_TRANSITION_COMMENT(" VA: "); + APPEND_TRANSITION_COMMENT(in_msg.LogicalAddress); + APPEND_TRANSITION_COMMENT(" Thread: "); + APPEND_TRANSITION_COMMENT(in_msg.ThreadID); + APPEND_TRANSITION_COMMENT(" Exposed: "); + APPEND_TRANSITION_COMMENT(in_msg.ExposedAction); + } + } + + /********************************END Transactional Actions************************/ + + action(d_sendDataToRequestor, "d", desc="send data to requestor") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(d2_sendDataToL2, "d2", desc="send data to the L2 cache because of M downgrade") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(dt_sendDataToRequestor_fromTBE, "dt", desc="send data to requestor") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := L1_TBEs[address].DataBlk; + out_msg.Dirty := L1_TBEs[address].Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Data; + out_msg.RemoveLastOwnerFromDir := true; + out_msg.LastOwnerID := machineID; + } + } + } + + action(d2t_sendDataToL2_fromTBE, "d2t", desc="send data to the L2 cache") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := L1_TBEs[address].DataBlk; + out_msg.Dirty := L1_TBEs[address].Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(f_sendDataToL2, "f", desc="send data to the L2 cache") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } + } + + action(ft_sendDataToL2_fromTBE, "ft", desc="send data to the L2 cache") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := L1_TBEs[address].DataBlk; + out_msg.Dirty := L1_TBEs[address].Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } + } + + action(fi_sendInvAck, "fi", desc="send data to the L2 cache") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + out_msg.AckCount := 1; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + } + } + } + + + action(g_issuePUTX, "g", desc="send data to the L2 cache") { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTX; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Requestor:= machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + if (getL1CacheEntry(address).Dirty) { + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } else { + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(g_issuePUTS, "gs", desc="send clean data to the L2 cache") { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTS; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Requestor:= machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + if (getL1CacheEntry(address).Dirty) { + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } else { + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + // used to determine whether to set sticky-M or sticky-S state in directory (M or SS in L2) + action(g_issuePUTXorPUTS, "gxs", desc="send data to the L2 cache") { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + if(checkWriteSignatures(address) == true){ + // we should set sticky-M + out_msg.Type := CoherenceRequestType:PUTX; + } + else{ + // we should set sticky-S + out_msg.Type := CoherenceRequestType:PUTS; + } + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Requestor:= machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + if (getL1CacheEntry(address).Dirty) { + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } else { + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(j_sendUnblock, "j", desc="send unblock to the L2 cache") { + enqueue(unblockNetwork_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:UNBLOCK; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Control; + // inform L2 whether request was transactional + //out_msg.Transactional := L1_TBEs[address].Trans; + out_msg.Transactional := checkReadWriteSignatures(address); + + out_msg.RemoveLastOwnerFromDir := L1_TBEs[address].RemoveLastOwnerFromDir; + out_msg.LastOwnerID := L1_TBEs[address].LastOwnerID; + } + } + + action(jj_sendExclusiveUnblock, "\j", desc="send unblock to the L2 cache") { + enqueue(unblockNetwork_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:EXCLUSIVE_UNBLOCK; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Control; + // inform L2 whether request was transactional + // out_msg.Transactional := L1_TBEs[address].Trans; + out_msg.Transactional := checkReadWriteSignatures(address); + + out_msg.RemoveLastOwnerFromDir := L1_TBEs[address].RemoveLastOwnerFromDir; + out_msg.LastOwnerID := L1_TBEs[address].LastOwnerID; + } + } + + + + action(h_load_hit, "h", desc="If not prefetch, notify sequencer the load completed.") { + DEBUG_EXPR(getL1CacheEntry(address).DataBlk); + sequencer.readCallback(address, getL1CacheEntry(address).DataBlk); + } + + action(hh_store_hit, "\h", desc="If not prefetch, notify sequencer that store completed.") { + DEBUG_EXPR(getL1CacheEntry(address).DataBlk); + sequencer.writeCallback(address, getL1CacheEntry(address).DataBlk); + getL1CacheEntry(address).Dirty := true; + } + + action(h_load_conflict, "hc", desc="Notify sequencer of conflict on load") { + sequencer.readConflictCallback(address); + } + + action(hh_store_conflict, "\hc", desc="If not prefetch, notify sequencer that store completed.") { + sequencer.writeConflictCallback(address); + } + + action(i_allocateTBE, "i", desc="Allocate TBE (isPrefetch=0, number of invalidates=0)") { + check_allocate(L1_TBEs); + L1_TBEs.allocate(address); + L1_TBEs[address].isPrefetch := false; + L1_TBEs[address].Dirty := getL1CacheEntry(address).Dirty; + L1_TBEs[address].DataBlk := getL1CacheEntry(address).DataBlk; + } + + action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") { + mandatoryQueue_in.dequeue(); + } + + action(j_popTriggerQueue, "jp", desc="Pop trigger queue.") { + triggerQueue_in.dequeue(); + } + + action(l_popRequestQueue, "l", desc="Pop incoming request queue and profile the delay within this virtual network") { + profileMsgDelay(2, requestIntraChipL1Network_in.dequeue_getDelayCycles()); + } + + action(o_popIncomingResponseQueue, "o", desc="Pop Incoming Response queue and profile the delay within this virtual network") { + profileMsgDelay(3, responseIntraChipL1Network_in.dequeue_getDelayCycles()); + } + + action(s_deallocateTBE, "s", desc="Deallocate TBE") { + L1_TBEs.deallocate(address); + } + + action(u_writeDataToL1Cache, "u", desc="Write data to cache") { + peek(responseIntraChipL1Network_in, ResponseMsg) { + getL1CacheEntry(address).DataBlk := in_msg.DataBlk; + getL1CacheEntry(address).Dirty := in_msg.Dirty; + if (machineIDToMachineType(in_msg.Sender) == MachineType:L1Cache) { + L1_TBEs[address].RemoveLastOwnerFromDir := in_msg.RemoveLastOwnerFromDir; + L1_TBEs[address].LastOwnerID := in_msg.LastOwnerID; + } + } + } + + action(q_updateAckCount, "q", desc="Update ack count") { + peek(responseIntraChipL1Network_in, ResponseMsg) { + APPEND_TRANSITION_COMMENT(" before pendingAcks: "); + APPEND_TRANSITION_COMMENT(L1_TBEs[address].pendingAcks); + L1_TBEs[address].pendingAcks := L1_TBEs[address].pendingAcks - in_msg.AckCount; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.AckCount); + APPEND_TRANSITION_COMMENT(" after pendingAcks: "); + APPEND_TRANSITION_COMMENT(L1_TBEs[address].pendingAcks); + APPEND_TRANSITION_COMMENT(" sender: "); + APPEND_TRANSITION_COMMENT(in_msg.Sender); + if (L1_TBEs[address].pendingAcks == 0) { + enqueue(triggerQueue_out, TriggerMsg) { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := TriggerType:ALL_ACKS; + APPEND_TRANSITION_COMMENT(" Triggering All_Acks"); + } + } + } + } + + action(z_stall, "z", desc="Stall") { + } + + action(ff_deallocateL1CacheBlock, "\f", desc="Deallocate L1 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + if (L1DcacheMemory.isTagPresent(address)) { + L1DcacheMemory.deallocate(address); + } else { + L1IcacheMemory.deallocate(address); + } + } + + action(oo_allocateL1DCacheBlock, "\o", desc="Set L1 D-cache tag equal to tag of block B.") { + if (L1DcacheMemory.isTagPresent(address) == false) { + L1DcacheMemory.allocate(address); + // reset trans bit + } + } + + action(pp_allocateL1ICacheBlock, "\p", desc="Set L1 I-cache tag equal to tag of block B.") { + if (L1IcacheMemory.isTagPresent(address) == false) { + L1IcacheMemory.allocate(address); + // reset trans bit + } + } + + action(zz_recycleRequestQueue, "zz", desc="recycle L1 request queue") { + requestIntraChipL1Network_in.recycle(); + } + + action(z_recycleMandatoryQueue, "\z", desc="recycle L1 request queue") { + mandatoryQueue_in.recycle(); + } + + action(uu_profileMiss, "\u", desc="Profile the demand miss") { + peek(mandatoryQueue_in, CacheMsg) { + profile_L1Cache_miss(in_msg, id); + } + } + + action(uuu_profileTransactionLoadMiss, "\uu", desc="Profile Miss") { + xact_mgr.profileTransactionMiss(L1_TBEs[address].ThreadID, true); + } + + action(uuu_profileTransactionStoreMiss, "\uuu", desc="Profile Miss") { + xact_mgr.profileTransactionMiss(L1_TBEs[address].ThreadID, false); + } + + + //***************************************************** + // TRANSITIONS + //***************************************************** + + // For filter responses + transition({NP, I, S, E, M, IS, IM, SM, IS_I, IS_S, IS_E, IM_M, M_I, E_I}, Check_Write_Filter){ + a_checkWriteFilter; + l_popRequestQueue; + } + + transition({NP, I, S, E, M, IS, IM, SM, IS_I, IS_S, IS_E, IM_M, M_I, E_I}, Check_Read_Write_Filter){ + a_checkReadWriteFilter; + l_popRequestQueue; + } + + // Transitions for Load/Store/Replacement/WriteBack from transient states + transition({IS, IM, IS_I, IS_S, IS_E, IM_M, M_I, E_I}, {Load, Ifetch, Store, L1_Replacement, L1_Replacement_XACT}) { + z_recycleMandatoryQueue; + } + + // Transitions from Idle + transition({NP,I}, {L1_Replacement, L1_Replacement_XACT}) { + ff_deallocateL1CacheBlock; + } + + transition({NP,I}, Load, IS) { + p_profileRequest; + oo_allocateL1DCacheBlock; + i_allocateTBE; + x_tbeSetPrefetch; + x_tbeSetPhysicalAddress; + a_issueGETS; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition({NP,I}, Ifetch, IS) { + p_profileRequest; + pp_allocateL1ICacheBlock; + i_allocateTBE; + x_tbeSetPhysicalAddress; + ai_issueGETINSTR; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition({NP,I}, Store, IM) { + p_profileRequest; + oo_allocateL1DCacheBlock; + i_allocateTBE; + x_tbeSetPrefetch; + x_tbeSetPhysicalAddress; + b_issueGETX; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition({NP, I}, Inv) { + fi_sendInvAck; + l_popRequestQueue; + } + + // Transactional invalidates to blocks in NP or I are + // transactional blocks that have been silently replaced + // FALSE POSITIVE - can't tell whether block was never in our read/write set or was replaced + transition({NP, I}, Inv_X) { + fi_sendInvNack; + l_popRequestQueue; + } + + // for L2 replacements. This happens due to our silent replacements. + transition({NP, I}, Replace) { + fi_sendInvAck; + l_popRequestQueue; + } + + // Transitions from Shared + transition(S, {Load,Ifetch}) { + p_profileRequest; + h_load_hit; + k_popMandatoryQueue; + } + + transition(S, Store, IM) { + p_profileRequest; + i_allocateTBE; + x_tbeSetPrefetch; + x_tbeSetPhysicalAddress; + b_issueGETX; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(S, L1_Replacement, I) { + ff_deallocateL1CacheBlock; + } + + transition(S, L1_Replacement_XACT, I) { + q_profileOverflow; + qq_xactReplacement; + ff_deallocateL1CacheBlock; + } + + transition(S, Inv, I) { + fi_sendInvAck; + l_popRequestQueue; + } + + transition(S, Inv_X) { + fi_sendInvNack; + l_popRequestQueue; + } + + // for L2 replacements. + transition(S, Replace, I){ + fi_sendInvAck; + l_popRequestQueue; + } + + // Transitions from Exclusive + + transition(E, {Load, Ifetch}) { + p_profileRequest; + h_load_hit; + k_popMandatoryQueue; + } + + transition(E, Store, M) { + p_profileRequest; + hh_store_hit; + k_popMandatoryQueue; + } + + transition(E, L1_Replacement, M_I) { + // The data is clean + i_allocateTBE; + g_issuePUTX; // send data, but hold in case forwarded request + ff_deallocateL1CacheBlock; + } + + // we can't go to M_I here because we need to maintain transactional read isolation on this line, and M_I allows GETS and GETXs to + // be serviced. For correctness we need to make sure we are marked as a transactional reader (if we never read transactionally written data back exclusively) or transactional writer + transition(E, L1_Replacement_XACT, E_I) { + q_profileOverflow; + qq_xactReplacement; + // The data is clean + i_allocateTBE; + g_issuePUTXorPUTS; // send data and hold, but do not release on forwarded requests + ff_deallocateL1CacheBlock; + } + + transition(E, Inv, I) { + // don't send data + fi_sendInvAck; + l_popRequestQueue; + } + + transition(E, Inv_X){ + fi_sendInvNack; + l_popRequestQueue; + } + + // for L2 replacements + transition(E, Replace, I) { + // don't send data + fi_sendInvAck; + l_popRequestQueue; + } + + transition(E, Fwd_GETX, I) { + d_sendDataToRequestor; + l_popRequestQueue; + } + + transition(E, {Fwd_GETS, Fwd_GET_INSTR}, S) { + d_sendDataToRequestor; + d2_sendDataToL2; + l_popRequestQueue; + } + + // If we see Fwd_GETS_X this is a FALSE POSITIVE, since we never + // modified this block + transition(E, {Fwd_GETS_X, Fwd_GETX_X, Fwd_GET_INSTR_X}){ + // send NACK instead of data + e_sendNackToRequestor; + l_popRequestQueue; + } + + // Transitions from Modified + transition(M, {Load, Ifetch}) { + p_profileRequest; + h_load_hit; + k_popMandatoryQueue; + } + + transition(M, Store) { + p_profileRequest; + hh_store_hit; + k_popMandatoryQueue; + } + + transition(M, L1_Replacement, M_I) { + i_allocateTBE; + g_issuePUTX; // send data, but hold in case forwarded request + ff_deallocateL1CacheBlock; + } + + // in order to prevent releasing isolation of transactional data (either written to just read) we need to + // mark ourselves as a transactional reader (e.g. SS state in L2) or transactional writer (e.g. M state in L2). We need to transition to the same E_I + // state as for transactional replacements from E state, and ignore all requests. + transition(M, L1_Replacement_XACT, E_I) { + q_profileOverflow; + qq_xactReplacement; + i_allocateTBE; + g_issuePUTXorPUTS; // send data, but do not release on forwarded requests + ff_deallocateL1CacheBlock; + } + + transition({M_I, E_I}, WB_Ack, I) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(M, Inv, I) { + f_sendDataToL2; + l_popRequestQueue; + } + + // for L2 replacement + transition(M, Replace, I) { + f_sendDataToL2; + l_popRequestQueue; + } + + transition(M, Inv_X){ + fi_sendInvNack; + l_popRequestQueue; + } + + transition(E_I, Inv) { + // ack requestor's GETX, but wait for WB_Ack from L2 + fi_sendInvAck; + l_popRequestQueue; + } + + // maintain isolation on M or E replacements + // took out M_I, since L2 transitions to M upon PUTX, and we should no longer receives invalidates + transition(E_I, Inv_X) { + fi_sendInvNack; + l_popRequestQueue; + } + + // allow L2 to get data while we replace + transition({M_I, E_I}, Replace, I) { + ft_sendDataToL2_fromTBE; + s_deallocateTBE; + l_popRequestQueue; + } + + transition(M, Fwd_GETX, I) { + d_sendDataToRequestor; + l_popRequestQueue; + } + + transition(M, {Fwd_GETS, Fwd_GET_INSTR}, S) { + d_sendDataToRequestor; + d2_sendDataToL2; + l_popRequestQueue; + } + + transition(M, {Fwd_GETX_X, Fwd_GETS_X, Fwd_GET_INSTR_X}) { + // send NACK instead of data + e_sendNackToRequestor; + l_popRequestQueue; + } + + // for simplicity we ignore all other requests while we wait for L2 to receive the clean data. Otherwise we will incorrectly transfer + // ownership and not mark ourselves as a transactional sharer in the L2 directory + transition(E_I, {Fwd_GETX, Fwd_GETS, Fwd_GET_INSTR, Fwd_GETS_X, Fwd_GETX_X, Fwd_GET_INSTR_X}) { + // send NACK instead of data + e_sendNackToRequestor; + l_popRequestQueue; + } + + transition(M_I, Fwd_GETX, I) { + dt_sendDataToRequestor_fromTBE; + s_deallocateTBE; + l_popRequestQueue; + } + + transition(M_I, {Fwd_GETS, Fwd_GET_INSTR}, I) { + dt_sendDataToRequestor_fromTBE; + d2t_sendDataToL2_fromTBE; + s_deallocateTBE; + l_popRequestQueue; + } + + // don't release isolation on forwarded conflicting requests + transition(M_I, {Fwd_GETS_X, Fwd_GETX_X, Fwd_GET_INSTR_X}) { + // send NACK instead of data + e_sendNackToRequestor; + l_popRequestQueue; + } + + // Transitions from IS + transition({IS, IS_I}, Inv, IS_I) { + fi_sendInvAck; + l_popRequestQueue; + } + + // Only possible when L2 sends us data in SS state. No conflict is possible, so no need to unblock L2 + transition(IS, L2_Data_all_Acks, S) { + u_writeDataToL1Cache; + // unblock L2 because it blocks on GETS + j_sendUnblock; + uuu_profileTransactionLoadMiss; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // Made the L2 block on GETS requests, so we are guaranteed to have no races with GETX + // We only get into this transition if the writer had to retry his GETX request that invalidated us, and L2 went back to SS + transition(IS_I, L2_Data_all_Acks, S) { + u_writeDataToL1Cache; + // unblock L2 because it blocks on GETS + j_sendUnblock; + uuu_profileTransactionLoadMiss; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // for L2 replacements + transition({IS, IS_I}, Replace, IS_I) { + fi_sendInvAck; + l_popRequestQueue; + } + + // These transitions are for when L2 sends us data, because it has exclusive copy, but L1 filter responses have not arrived + transition({IS, IS_I}, Ack){ + q_updateAckCount; + o_popIncomingResponseQueue; + } + + transition({IS, IS_I}, Nack) { + r_notifyReceiveNack; + q_updateNackCount; + o_popIncomingResponseQueue; + } + + // IS_I also allowed because L2 Inv beat our GETS request, and now L2 is in NP state, ready to service our GETS. + transition({IS, IS_I}, L2_Data, IS_S) { + u_writeDataToL1Cache; + // This message carries the inverse of the ack count + q_updateAckCount; + o_popIncomingResponseQueue; + } + + transition(IS_S, Ack){ + q_updateAckCount; + o_popIncomingResponseQueue; + } + + transition(IS_S, Nack) { + r_notifyReceiveNack; + q_updateNackCount; + o_popIncomingResponseQueue; + } + + transition(IS_S, Ack_all, S){ + // tell L2 we succeeded + j_sendUnblock; + uuu_profileTransactionLoadMiss; + h_load_hit; + s_deallocateTBE; + j_popTriggerQueue; + } + + // retry request from I + transition(IS_S, Nack_all, I){ + ff_deallocateL1CacheBlock; + // This is also the final NACK + r_notifyReceiveNackFinal; + // tell L2 we failed + jj_sendUnblockCancel; + h_load_conflict; + s_deallocateTBE; + j_popTriggerQueue; + } + + // L2 is trying to give us exclusive data + // we can go to E because L2 is guaranteed to have only copy (ie no races from other L1s possible) + transition({IS, IS_I}, L2_Exclusive_Data, IS_E) { + u_writeDataToL1Cache; + // This message carries the inverse of the ack count + q_updateAckCount; + o_popIncomingResponseQueue; + } + + transition({IS, IS_I}, L2_Exclusive_Data_all_Acks, E){ + u_writeDataToL1Cache; + // tell L2 we succeeded + jj_sendExclusiveUnblock; + uuu_profileTransactionLoadMiss; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IS_E, Ack){ + q_updateAckCount; + o_popIncomingResponseQueue; + } + + transition(IS_E, Nack) { + r_notifyReceiveNack; + q_updateNackCount; + o_popIncomingResponseQueue; + } + + transition(IS_E, Ack_all, E){ + // tell L2 we succeeded + jj_sendExclusiveUnblock; + uuu_profileTransactionLoadMiss; + h_load_hit; + s_deallocateTBE; + j_popTriggerQueue; + } + + // retry request from I + transition(IS_E, Nack_all, I){ + ff_deallocateL1CacheBlock; + // This is also the final NACK + r_notifyReceiveNackFinal; + // need to tell L2 we failed + jj_sendUnblockCancel; + h_load_conflict; + s_deallocateTBE; + j_popTriggerQueue; + } + + // Normal case - when L2 doesn't have exclusive line, but L1 has line. + // We got NACKed . Try again in state I + // IMPORTANT: filters are NOT checked when L2 is in SS, because nobody has modified the line. + // For this transition we only receive NACKs from the exclusive writer + transition({IS, IS_I}, Nack_all, I) { + // This is also the final NACK + r_notifyReceiveNackFinal; + // L2 is blocked when L1 is exclusive + jj_sendUnblockCancel; + h_load_conflict; + s_deallocateTBE; + j_popTriggerQueue; + } + + transition({IS, IS_I}, Inv_X) { + fi_sendInvNack; + l_popRequestQueue; + } + + transition(IS, DataS_fromL1, S) { + u_writeDataToL1Cache; + j_sendUnblock; + uuu_profileTransactionLoadMiss; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // This occurs when there is a race between our GETS and another L1's GETX, and the GETX wins + // The L2 is now blocked because our request was forwarded to exclusive L1 (ie MT_IIB) + transition(IS_I, DataS_fromL1, S) { + u_writeDataToL1Cache; + j_sendUnblock; + uuu_profileTransactionLoadMiss; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // Transitions from IM + transition({IM, SM}, Inv, IM) { + fi_sendInvAck; + l_popRequestQueue; + } + + transition({IM, SM}, Inv_X) { + fi_sendInvNack; + l_popRequestQueue; + } + + // for L2 replacements + transition({IM, SM}, Replace, IM) { + fi_sendInvAck; + l_popRequestQueue; + } + + // only possible when L1 exclusive sends us the line + transition(IM, Data_all_Acks, M) { + u_writeDataToL1Cache; + jj_sendExclusiveUnblock; + uuu_profileTransactionStoreMiss; + hh_store_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // L2 is trying to give us data + // Don't go to SM because we do not want a S copy on failure. This might cause conflicts for older writers that + // nacked us. + transition(IM, L2_Data, IM_M) { + u_writeDataToL1Cache; + // This message carries the inverse of the ack count + q_updateAckCount; + o_popIncomingResponseQueue; + } + + transition(IM, L2_Data_all_Acks, M){ + u_writeDataToL1Cache; + // tell L2 we succeeded + jj_sendExclusiveUnblock; + uuu_profileTransactionStoreMiss; + hh_store_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IM_M, Ack){ + q_updateAckCount; + o_popIncomingResponseQueue; + } + + transition(IM_M, Nack) { + r_notifyReceiveNack; + q_updateNackCount; + o_popIncomingResponseQueue; + } + + transition(IM_M, Ack_all, M){ + // tell L2 we succeeded + jj_sendExclusiveUnblock; + uuu_profileTransactionStoreMiss; + hh_store_hit; + s_deallocateTBE; + j_popTriggerQueue; + } + + // retry request from I + transition(IM_M, Nack_all, I){ + ff_deallocateL1CacheBlock; + // This is also the final NACK + r_notifyReceiveNackFinal; + // need to tell L2 we failed + jj_sendUnblockCancel; + hh_store_conflict; + s_deallocateTBE; + j_popTriggerQueue; + } + + // transitions from SM + transition({SM, IM}, Ack) { + q_updateAckCount; + o_popIncomingResponseQueue; + } + + // instead of Data we receive Nacks + transition({SM, IM}, Nack) { + r_notifyReceiveNack; + // mark this request as being NACKed + q_updateNackCount; + o_popIncomingResponseQueue; + } + + transition(SM, Ack_all, M) { + jj_sendExclusiveUnblock; + uuu_profileTransactionStoreMiss; + hh_store_hit; + s_deallocateTBE; + j_popTriggerQueue; + } + + // retry in state S + transition(SM, Nack_all, S){ + // This is the final nack + r_notifyReceiveNackFinal; + // unblock the L2 + jj_sendUnblockCancel; + hh_store_conflict; + s_deallocateTBE; + j_popTriggerQueue; + } + + // retry in state I + transition(IM, Nack_all, I){ + // This is the final NACK + r_notifyReceiveNackFinal; + // unblock the L2 + jj_sendUnblockCancel; + hh_store_conflict; + s_deallocateTBE; + j_popTriggerQueue; + } + +} + + + diff --git a/src/mem/protocol/MESI_CMP_filter_directory-L2cache.sm b/src/mem/protocol/MESI_CMP_filter_directory-L2cache.sm new file mode 100644 index 000000000..9085ae33f --- /dev/null +++ b/src/mem/protocol/MESI_CMP_filter_directory-L2cache.sm @@ -0,0 +1,2123 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MSI_MOSI_CMP_directory-L2cache.sm 1.12 05/01/19 15:55:40-06:00 beckmann@s0-28.cs.wisc.edu $ + * + */ + +machine(L2Cache, "MESI Directory L2 Cache CMP") { + + // L2 BANK QUEUES + // From local bank of L2 cache TO the network + MessageBuffer DirRequestFromL2Cache, network="To", virtual_network="2", ordered="false"; // this L2 bank -> Memory + MessageBuffer L1RequestFromL2Cache, network="To", virtual_network="1", ordered="false"; // this L2 bank -> a local L1 + MessageBuffer responseFromL2Cache, network="To", virtual_network="3", ordered="false"; // this L2 bank -> a local L1 || Memory + + // FROM the network to this local bank of L2 cache + MessageBuffer L1RequestToL2Cache, network="From", virtual_network="0", ordered="false"; // a local L1 -> this L2 bank + MessageBuffer responseToL2Cache, network="From", virtual_network="3", ordered="false"; // a local L1 || Memory -> this L2 bank + MessageBuffer unblockToL2Cache, network="From", virtual_network="4", ordered="false"; // a local L1 || Memory -> this L2 bank + + // STATES + enumeration(State, desc="L2 Cache states", default="L2Cache_State_NP") { + // Base states + NP, desc="Not present in either cache"; + SS, desc="L2 cache entry Shared, also present in one or more L1s"; + M, desc="L2 cache entry Modified, not present in any L1s", format="!b"; + MT, desc="L2 cache entry Modified in a local L1, assume L2 copy stale", format="!b"; + + // L2 replacement + M_I, desc="L2 cache replacing, have all acks, sent dirty data to memory, waiting for ACK from memory"; + MT_I, desc="L2 cache replacing, getting data from exclusive"; + MCT_I, desc="L2 cache replacing, clean in L2, getting data or ack from exclusive"; + I_I, desc="L2 replacing clean data, need to inv sharers and then drop data"; + S_I, desc="L2 replacing dirty data, collecting acks from L1s"; + + // Transient States for fetching data from memory + ISS, desc="L2 idle, got single L1_GETS, issued memory fetch, have not seen response yet"; + IS, desc="L2 idle, got L1_GET_INSTR or multiple L1_GETS, issued memory fetch, have not seen response yet"; + IM, desc="L2 idle, got L1_GETX, issued memory fetch, have not seen response(s) yet"; + + // Blocking states + SS_MB, desc="Blocked for L1_GETX from SS"; + SS_SSB, desc="Blocked for L1_GETS from SS"; + MT_MB, desc="Blocked for L1_GETX from MT"; + M_MB, desc="Blocked for L1_GETX from M"; + ISS_MB, desc="Blocked for L1_GETS or L1_GETX from NP, received Mem Data"; + IS_SSB, desc="Blocked for L1_GET_INSTR from NP, received Mem Data"; + M_SSB, desc="Blocked for L1_GET_INSTR from M"; + + MT_IIB, desc="Blocked for L1_GETS from MT, waiting for unblock and data"; + MT_IB, desc="Blocked for L1_GETS from MT, got unblock, waiting for data"; + MT_SB, desc="Blocked for L1_GETS from MT, got data, waiting for unblock"; + + // for resolving PUTX/PUTS races + PB_MT, desc="Going to MT, got data and unblock, waiting for PUT"; + PB_SS, desc="Going to SS, got unblock, waiting for PUT"; + PB_MT_IB, desc="Blocked from MT, got unblock, waiting for data and PUT"; + + } + + // EVENTS + enumeration(Event, desc="L2 Cache events") { + // L2 events + + // events initiated by the local L1s + L1_GET_INSTR, desc="a L1I GET INSTR request for a block maped to us"; + L1_GET_INSTR_ESCAPE, desc="a L1I GET INSTR in an escape action request for a block mapped to us"; + L1_GETS, desc="a L1D GETS request for a block maped to us"; + L1_GETS_ESCAPE, desc="a L1D GETS in an escape action request for a block mapped to us"; + L1_GETX, desc="a L1D GETX request for a block maped to us"; + L1_GETX_ESCAPE, desc="a L1D GETX in an escape action request for a block mapped to us"; + L1_UPGRADE, desc="a L1D GETX request for a block maped to us"; + + L1_PUTX, desc="L1 replacing data"; + L1_PUTX_old, desc="L1 replacing data, but no longer sharer"; + L1_PUTS, desc="L1 replacing clean data"; + L1_PUTS_old, desc="L1 replacing clean data, but no longer sharer"; + L1_PUT_PENDING, desc="L1 PUT msg pending (recycled)"; + + Fwd_L1_GETX, desc="L1 did not have data, so we supply"; + Fwd_L1_GETS, desc="L1 did not have data, so we supply"; + Fwd_L1_GET_INSTR, desc="L1 did not have data, so we supply"; + + // events initiated by this L2 + L2_Replacement, desc="L2 Replacement", format="!r"; + L2_Replacement_XACT, desc="L2 Replacement of trans. data", format="!r"; + L2_Replacement_clean, desc="L2 Replacement, but data is clean", format="!r"; + L2_Replacement_clean_XACT, desc="L2 Replacement of trans. data, but data is clean", format="!r"; + + // events from memory controller + Mem_Data, desc="data from memory", format="!r"; + Mem_Ack, desc="ack from memory", format="!r"; + + // M->S data writeback + WB_Data, desc="data from L1"; + WB_Data_clean, desc="clean data from L1"; + Ack, desc="writeback ack"; + Ack_all, desc="writeback ack"; + // For transactional memory + Nack, desc="filter indicates conflict"; + Nack_all, desc="all filters have responded, at least one conflict"; + + Unblock, desc="Unblock from L1 requestor"; + Unblock_Cancel, desc="Unblock from L1 requestor (FOR XACT MEMORY)"; + Exclusive_Unblock, desc="Unblock from L1 requestor"; + + Unblock_WaitPUTold, desc="Unblock from L1 requestor, last requestor was replacing so wait for PUT msg"; + Exclusive_Unblock_WaitPUTold, desc="Unblock from L1 requestor, last requestor was replacing so wait for PUT msg"; + + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + NetDest Sharers, desc="tracks the L1 shares on-chip"; + MachineID Exclusive, desc="Exclusive holder of block"; + DataBlock DataBlk, desc="data for the block"; + bool Dirty, default="false", desc="data is dirty"; + + bool Trans, desc="dummy bit for debugging"; + bool Read, desc="LogTM R bit"; + bool Write, desc="LogTM W bit"; + bool L2Miss, desc="Was this block sourced from memory"; + int L1PutsPending, default="0", desc="how many PUT_ are pending for this entry (being recyled)"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Line address for this TBE"; + Address PhysicalAddress, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="Buffer for the data block"; + bool Dirty, default="false", desc="Data is Dirty"; + + NetDest L1_GetS_IDs, desc="Set of the internal processors that want the block in shared state"; + MachineID L1_GetX_ID, desc="ID of the L1 cache to forward the block to once we get a response"; + bool isPrefetch, desc="Set if this was caused by a prefetch"; + + int pendingAcks, desc="number of pending acks for invalidates during writeback"; + bool nack, default="false", desc="has this request been NACKed?"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + void setMRU(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + TBETable L2_TBEs, template_hack=""; + + CacheMemory L2cacheMemory, template_hack="", constructor_hack='L2_CACHE_NUM_SETS_BITS,L2_CACHE_ASSOC,MachineType_L2Cache,int_to_string(i)'; + + // inclusive cache, returns L2 entries only + Entry getL2CacheEntry(Address addr), return_by_ref="yes" { + return L2cacheMemory[addr]; + } + + void changeL2Permission(Address addr, AccessPermission permission) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory.changePermission(addr, permission); + } + } + + string getCoherenceRequestTypeStr(CoherenceRequestType type) { + return CoherenceRequestType_to_string(type); + } + + bool isL2CacheTagPresent(Address addr) { + return (L2cacheMemory.isTagPresent(addr)); + } + + bool isOneSharerLeft(Address addr, MachineID requestor) { + assert(L2cacheMemory[addr].Sharers.isElement(requestor)); + return (L2cacheMemory[addr].Sharers.count() == 1); + } + + bool isSharer(Address addr, MachineID requestor) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory[addr].Sharers.isElement(requestor); + } else { + return false; + } + } + + void addSharer(Address addr, MachineID requestor) { + DEBUG_EXPR(machineID); + DEBUG_EXPR(requestor); + DEBUG_EXPR(addr); + assert(map_L1CacheMachId_to_L2Cache(addr, requestor) == machineID); + L2cacheMemory[addr].Sharers.add(requestor); + } + + State getState(Address addr) { + if(L2_TBEs.isPresent(addr)) { + return L2_TBEs[addr].TBEState; + } else if (isL2CacheTagPresent(addr)) { + return getL2CacheEntry(addr).CacheState; + } + return State:NP; + } + + string getStateStr(Address addr) { + return L2Cache_State_to_string(getState(addr)); + } + + // when is this called + void setState(Address addr, State state) { + + // MUST CHANGE + if (L2_TBEs.isPresent(addr)) { + L2_TBEs[addr].TBEState := state; + } + + if (isL2CacheTagPresent(addr)) { + getL2CacheEntry(addr).CacheState := state; + + // Set permission + if (state == State:SS ) { + changeL2Permission(addr, AccessPermission:Read_Only); + } else if (state == State:M) { + changeL2Permission(addr, AccessPermission:Read_Write); + } else if (state == State:MT) { + changeL2Permission(addr, AccessPermission:Stale); + } else { + changeL2Permission(addr, AccessPermission:Busy); + } + } + } + + Event L1Cache_request_type_to_event(CoherenceRequestType type, Address addr, MachineID requestor) { + if (L2cacheMemory.isTagPresent(addr)){ /* Present */ + if(getL2CacheEntry(addr).L1PutsPending > 0 && /* At least one PUT pending */ + (getL2CacheEntry(addr).CacheState == State:SS || getL2CacheEntry(addr).CacheState == State:MT || getL2CacheEntry(addr).CacheState == State:M )) { /* Base state */ + + /* Only allow PUTX/PUTS to go on */ + if (type != CoherenceRequestType:PUTX && + type != CoherenceRequestType:PUTS) { + return Event:L1_PUT_PENDING; // Don't serve any req until the wb is serviced + } + } + } + if(type == CoherenceRequestType:GETS) { + return Event:L1_GETS; + } else if(type == CoherenceRequestType:GETS_ESCAPE) { + return Event:L1_GETS_ESCAPE; + } else if(type == CoherenceRequestType:GET_INSTR) { + return Event:L1_GET_INSTR; + } else if(type == CoherenceRequestType:GET_INSTR_ESCAPE) { + return Event:L1_GET_INSTR_ESCAPE; + } else if (type == CoherenceRequestType:GETX) { + return Event:L1_GETX; + } else if(type == CoherenceRequestType:GETX_ESCAPE) { + return Event:L1_GETX_ESCAPE; + } else if (type == CoherenceRequestType:UPGRADE) { + if ( isL2CacheTagPresent(addr) && getL2CacheEntry(addr).Sharers.isElement(requestor) ) { + return Event:L1_UPGRADE; + } else { + return Event:L1_GETX; + } + } else if (type == CoherenceRequestType:PUTX) { + if ( isL2CacheTagPresent(addr) && getL2CacheEntry(addr).L1PutsPending > 0) { + getL2CacheEntry(addr).L1PutsPending := getL2CacheEntry(addr).L1PutsPending - 1; + DEBUG_EXPR("PUTX PutSPending "); + DEBUG_EXPR(getL2CacheEntry(addr).L1PutsPending); + } + if (isSharer(addr, requestor)) { + return Event:L1_PUTX; + } else { + return Event:L1_PUTX_old; + } + } else if (type == CoherenceRequestType:PUTS) { + if ( isL2CacheTagPresent(addr) && getL2CacheEntry(addr).L1PutsPending > 0) { + getL2CacheEntry(addr).L1PutsPending := getL2CacheEntry(addr).L1PutsPending - 1; + DEBUG_EXPR("PUTS PutSPending "); + DEBUG_EXPR(getL2CacheEntry(addr).L1PutsPending); + } + if (isSharer(addr, requestor)) { + return Event:L1_PUTS; + } else { + return Event:L1_PUTS_old; + } + } else { + DEBUG_EXPR(addr); + DEBUG_EXPR(type); + error("Invalid L1 forwarded request type"); + } + } + + // ** OUT_PORTS ** + + out_port(L1RequestIntraChipL2Network_out, RequestMsg, L1RequestFromL2Cache); + out_port(DirRequestIntraChipL2Network_out, RequestMsg, DirRequestFromL2Cache); + out_port(responseIntraChipL2Network_out, ResponseMsg, responseFromL2Cache); + + + // Response IntraChip L2 Network - response msg to this particular L2 bank + in_port(responseIntraChipL2Network_in, ResponseMsg, responseToL2Cache) { + if (responseIntraChipL2Network_in.isReady()) { + peek(responseIntraChipL2Network_in, ResponseMsg) { + // test wether it's from a local L1 or an off chip source + assert(in_msg.Destination.isElement(machineID)); + if(machineIDToMachineType(in_msg.Sender) == MachineType:L1Cache) { + if(in_msg.Type == CoherenceResponseType:DATA) { + if (in_msg.Dirty) { + trigger(Event:WB_Data, in_msg.Address); + } else { + trigger(Event:WB_Data_clean, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:ACK) { + if ((L2_TBEs[in_msg.Address].pendingAcks - in_msg.AckCount) == 0) { + // check whether any previous responses have been NACKs + if(L2_TBEs[in_msg.Address].nack == false) { + trigger(Event:Ack_all, in_msg.Address); + } + else { + // at least one nack received + trigger(Event:Nack_all, in_msg.Address); + } + } else { + trigger(Event:Ack, in_msg.Address); + } + // for NACKs + } else if (in_msg.Type == CoherenceResponseType:NACK) { + if ((L2_TBEs[in_msg.Address].pendingAcks - in_msg.AckCount) == 0) { + trigger(Event:Nack_all, in_msg.Address); + } else { + trigger(Event:Nack, in_msg.Address); + } + } else { + error("unknown message type"); + } + + } else { // external message + if(in_msg.Type == CoherenceResponseType:MEMORY_DATA) { + trigger(Event:Mem_Data, in_msg.Address); // L2 now has data and all off-chip acks + } else if(in_msg.Type == CoherenceResponseType:MEMORY_ACK) { + trigger(Event:Mem_Ack, in_msg.Address); // L2 now has data and all off-chip acks + } else { + error("unknown message type"); + } + } + } + } // if not ready, do nothing + } + + // L1 Request + in_port(L1RequestIntraChipL2Network_in, RequestMsg, L1RequestToL2Cache) { + if(L1RequestIntraChipL2Network_in.isReady()) { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + /* + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(id); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.Requestor); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(in_msg.Destination); + */ + assert(machineIDToMachineType(in_msg.Requestor) == MachineType:L1Cache); + assert(in_msg.Destination.isElement(machineID)); + if (L2cacheMemory.isTagPresent(in_msg.Address)) { + // The L2 contains the block, so proceeded with handling the request + trigger(L1Cache_request_type_to_event(in_msg.Type, in_msg.Address, in_msg.Requestor), in_msg.Address); + } else { + if (L2cacheMemory.cacheAvail(in_msg.Address)) { + // L2 does't have the line, but we have space for it in the L2 + trigger(L1Cache_request_type_to_event(in_msg.Type, in_msg.Address, in_msg.Requestor), in_msg.Address); + } else { + // No room in the L2, so we need to make room before handling the request + if (L2cacheMemory[ L2cacheMemory.cacheProbe(in_msg.Address) ].Dirty ) { + // check whether block is transactional + if(L2cacheMemory[ L2cacheMemory.cacheProbe(in_msg.Address) ].Trans == true){ + trigger(Event:L2_Replacement_XACT, L2cacheMemory.cacheProbe(in_msg.Address)); + } + else{ + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } else { + // check whether block is transactional + if(L2cacheMemory[ L2cacheMemory.cacheProbe(in_msg.Address) ].Trans == true){ + trigger(Event:L2_Replacement_clean_XACT, L2cacheMemory.cacheProbe(in_msg.Address)); + } + else{ + trigger(Event:L2_Replacement_clean, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } + } + } + } + } + } + + in_port(L1unblockNetwork_in, ResponseMsg, unblockToL2Cache) { + if(L1unblockNetwork_in.isReady()) { + peek(L1unblockNetwork_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceResponseType:EXCLUSIVE_UNBLOCK) { + if (in_msg.RemoveLastOwnerFromDir == true) { + if (isSharer(in_msg.Address,in_msg.LastOwnerID)) { + trigger(Event:Exclusive_Unblock_WaitPUTold, in_msg.Address); + } + else { // PUT arrived, requestor already removed from dir + trigger(Event:Exclusive_Unblock, in_msg.Address); + } + } + else { + trigger(Event:Exclusive_Unblock, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:UNBLOCK) { + if (in_msg.RemoveLastOwnerFromDir == true) { + if (isSharer(in_msg.Address,in_msg.LastOwnerID)) { + trigger(Event:Unblock_WaitPUTold, in_msg.Address); + } + else { // PUT arrived, requestor already removed from dir + trigger(Event:Unblock, in_msg.Address); + } + } + else { + trigger(Event:Unblock, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:UNBLOCK_CANCEL) { + trigger(Event:Unblock_Cancel, in_msg.Address); + } else { + error("unknown unblock message"); + } + } + } + } + + // ACTIONS + + action(a_issueFetchToMemory, "a", desc="fetch data from memory") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(DirRequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + + action(b_forwardRequestToExclusive, "b", desc="Forward request to the exclusive L1") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_TAG_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := in_msg.Type; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(L2cacheMemory[address].Exclusive); + out_msg.MessageSize := MessageSizeType:Request_Control; + // also pass along timestamp + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + } + } + } + + action(c_exclusiveReplacement, "c", desc="Send data to memory") { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(ct_exclusiveReplacementFromTBE, "ct", desc="Send data to memory") { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.DataBlk := L2_TBEs[address].DataBlk; + out_msg.Dirty := L2_TBEs[address].Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + + //************Transactional memory actions ************** + //broadcast a write filter lookup request to all L1s except for the requestor + action(a_checkL1WriteFiltersExceptRequestor, "wr", desc="Broadcast a Write Filter lookup request"){ + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_TAG_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceRequestType:CHECK_WRITE_FILTER; + // make L1s forward responses to requestor + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := getLocalL1IDs(machineID); + // don't broadcast to requestor + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // also pass along timestamp of requestor + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + } + } + } + + //broadcast a read + write filter lookup request to all L1s except for the requestor + action(a_checkL1ReadWriteFiltersExceptRequestor, "rwr", desc="Broadcast a Read + Write Filter lookup request"){ + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_TAG_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceRequestType:CHECK_READ_WRITE_FILTER; + // make L1 forward responses to requestor + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := getLocalL1IDs(machineID); + // don't broadcast to requestor + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // also pass along timestamp of requestor + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + } + } + } + + // These are to send out filter checks to those NACKers in our sharers or exclusive ptr list + action(a_checkNackerL1WriteFiltersExceptRequestor, "wrn", desc="Broadcast a Write Filter lookup request"){ + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + // check if the L2 miss bit is set - if it is, send check filter requests to those in our Sharers list only + if(getL2CacheEntry(address).L2Miss == true){ + // check whether we are the only sharer on the list. If so, no need to broadcast. + if(isSharer(address, in_msg.Requestor) == true && isOneSharerLeft(address, in_msg.Requestor) == true){ + // no filter check needed + APPEND_TRANSITION_COMMENT("L2 Miss: No need to check L1 write filter "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + } + else{ + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_TAG_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceRequestType:CHECK_WRITE_FILTER; + // make L1s forward responses to requestor + out_msg.Requestor := in_msg.Requestor; + assert(getL2CacheEntry(address).Sharers.count() > 0); + out_msg.Destination := getL2CacheEntry(address).Sharers; + // don't broadcast to requestor + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // also pass along timestamp of requestor + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + APPEND_TRANSITION_COMMENT(" requestor: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" dest: "); + APPEND_TRANSITION_COMMENT(out_msg.Destination); + } + } + } + else{ + // This is a read request, so check whether we have a writer + if(getL2CacheEntry(address).Sharers.count() == 0 && getL2CacheEntry(address).Exclusive != in_msg.Requestor){ + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_TAG_LATENCY") { + // we have a writer, and it is not us + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceRequestType:CHECK_WRITE_FILTER; + // make L1s forward responses to requestor + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(getL2CacheEntry(address).Exclusive); + out_msg.MessageSize := MessageSizeType:Response_Control; + // also pass along timestamp of requestor + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + APPEND_TRANSITION_COMMENT(" requestor: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" dest: "); + APPEND_TRANSITION_COMMENT(out_msg.Destination); + } + } + else{ + APPEND_TRANSITION_COMMENT("L1 replacement: No need to check L1 write filter"); + APPEND_TRANSITION_COMMENT(" requestor: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" exclusive: "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Exclusive); + // we should not have any sharers + assert( getL2CacheEntry(address).Sharers.count() == 0 ); + } + } + } + } + + action(a_checkNackerL1ReadWriteFiltersExceptRequestor, "wrrn", desc="Broadcast a Read + Write Filter lookup request"){ + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + // check if the L2 miss bit is set - if it is, send check filter requests to those in our Sharers list only + if(getL2CacheEntry(address).L2Miss == true){ + // check whether we are the only sharer on the list. If so, no need to broadcast. + if(isSharer(address, in_msg.Requestor) == true && isOneSharerLeft(address, in_msg.Requestor) == true){ + // no filter check needed + APPEND_TRANSITION_COMMENT("L2 Miss: No need to check L1 read/write filter"); + APPEND_TRANSITION_COMMENT(" requestor: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + } + else{ + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_TAG_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceRequestType:CHECK_READ_WRITE_FILTER; + // make L1s forward responses to requestor + out_msg.Requestor := in_msg.Requestor; + assert(getL2CacheEntry(address).Sharers.count() > 0); + out_msg.Destination := getL2CacheEntry(address).Sharers; + // don't broadcast to requestor + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // also pass along timestamp of requestor + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + APPEND_TRANSITION_COMMENT(" requestor: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" dest: "); + APPEND_TRANSITION_COMMENT(out_msg.Destination); + } + } + } + else{ + // This is a write request, so check whether we have readers not including us or a writer that is not us + if(getL2CacheEntry(address).Sharers.count() == 0 && getL2CacheEntry(address).Exclusive != in_msg.Requestor){ + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_TAG_LATENCY") { + // we have a writer, and it is not us + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceRequestType:CHECK_READ_WRITE_FILTER; + // make L1s forward responses to requestor + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(getL2CacheEntry(address).Exclusive); + out_msg.MessageSize := MessageSizeType:Response_Control; + // also pass along timestamp of requestor + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + APPEND_TRANSITION_COMMENT(" requestor: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" dest: "); + APPEND_TRANSITION_COMMENT(out_msg.Destination); + } + } + else if(getL2CacheEntry(address).Sharers.count() > 0){ + // this should never happen - since we allow silent S replacements but we always track exclusive L1 + assert(false); + if(isSharer(address, in_msg.Requestor) == true && isOneSharerLeft(address, in_msg.Requestor) == true){ + // no filter check needed + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" L1 replacement: No need to check L1 read/write filter - we are only reader"); + } + else{ + // reader(s) exist that is not us + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_TAG_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceRequestType:CHECK_READ_WRITE_FILTER; + // make L1s forward responses to requestor + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := getL2CacheEntry(address).Sharers; + // don't check our own filter + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // also pass along timestamp of requestor + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + APPEND_TRANSITION_COMMENT(" requestor: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" dest: "); + APPEND_TRANSITION_COMMENT(out_msg.Destination); + } + } + } + else{ + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Sharers); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Exclusive); + APPEND_TRANSITION_COMMENT(" L1 replacement: No need to check L1 read/write filter"); + } + } + } + } + + // send data but force L1 requestor to wait for filter responses + action(f_sendDataToGetSRequestor, "f", desc="Send data from cache to reqeustor") { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := L2_TBEs[address].PhysicalAddress; + out_msg.Type := CoherenceResponseType:L2_DATA; + out_msg.Sender := machineID; + out_msg.Destination := L2_TBEs[address].L1_GetS_IDs; // internal nodes + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + + // wait for the filter responses from other L1s + out_msg.AckCount := 0 - (numberOfL1CachePerChip() - 1); + } + } + + // send exclusive data + action(f_sendExclusiveDataToGetSRequestor, "fx", desc="Send data from cache to reqeustor") { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := L2_TBEs[address].PhysicalAddress; + out_msg.Type := CoherenceResponseType:L2_DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.Destination := L2_TBEs[address].L1_GetS_IDs; // internal nodes + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + + // wait for the filter responses from other L1s + out_msg.AckCount := 0 - (numberOfL1CachePerChip() - 1); + } + } + + action(f_sendDataToGetXRequestor, "fxx", desc="Send data from cache to reqeustor") { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := L2_TBEs[address].PhysicalAddress; + out_msg.Type := CoherenceResponseType:L2_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(L2_TBEs[address].L1_GetX_ID); // internal nodes + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + + // wait for the filter responses from other L1s + out_msg.AckCount := 0 - (numberOfL1CachePerChip() - 1); + } + } + + action(f_sendDataToRequestor, "fd", desc="Send data from cache to reqeustor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceResponseType:L2_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + + // different ack counts for different situations + if(in_msg.Type == CoherenceRequestType:GET_INSTR_ESCAPE || in_msg.Type == CoherenceRequestType:GETX_ESCAPE){ + // no acks needed + out_msg.AckCount := 0; + } + else{ + + // ORIGINAL + if( false ) { + out_msg.AckCount := 0 - (numberOfL1CachePerChip() - 1); + } + + else{ + // NEW*** + // differentiate btw read and write requests + if(in_msg.Type == CoherenceRequestType:GET_INSTR){ + if(getL2CacheEntry(address).L2Miss == true){ + // check whether we are the only sharer on the list. If so, no need to broadcast. + if(isSharer(address, in_msg.Requestor) == true && isOneSharerLeft(address, in_msg.Requestor) == true){ + // no filter check needed + APPEND_TRANSITION_COMMENT("We are only sharer"); + out_msg.AckCount := 0; + } + else{ + // wait for ACKs from the other NACKers + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count(); + if(isSharer(address, in_msg.Requestor)){ + // don't include us + out_msg.AckCount := out_msg.AckCount + 1; + } + APPEND_TRANSITION_COMMENT("Nackers exist"); + } + } + else{ + // This is a read request, so check whether we have a writer + if(getL2CacheEntry(address).Sharers.count() == 0 && getL2CacheEntry(address).Exclusive != in_msg.Requestor){ + // we have a writer and it is not us + out_msg.AckCount := 0 - 1; + + APPEND_TRANSITION_COMMENT(" Writer exists "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Exclusive); + } + else{ + // we should have no sharers! + assert(getL2CacheEntry(address).Sharers.count() == 0); + assert(getL2CacheEntry(address).Exclusive == in_msg.Requestor); + + APPEND_TRANSITION_COMMENT(" Sharers or we are writer exist, ok to read "); + APPEND_TRANSITION_COMMENT(" sharers: "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Sharers); + APPEND_TRANSITION_COMMENT(" exclusive: "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Exclusive); + out_msg.AckCount := 0; + } + } + } + else if(in_msg.Type == CoherenceRequestType:GETX){ + if(getL2CacheEntry(address).L2Miss == true){ + // check whether we are the only sharer on the list. If so, no need to broadcast. + if(isSharer(address, in_msg.Requestor) == true && isOneSharerLeft(address, in_msg.Requestor) == true){ + // no filter check needed + APPEND_TRANSITION_COMMENT(" L2Miss and we are only sharer "); + out_msg.AckCount := 0; + } + else{ + // nackers exist + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count(); + if(isSharer(address, in_msg.Requestor)){ + // don't include us + out_msg.AckCount := out_msg.AckCount + 1; + } + APPEND_TRANSITION_COMMENT("Nackers exist"); + } + } + else{ + // This is a write request, so check whether we have readers not including us or a writer that is not us + if(getL2CacheEntry(address).Sharers.count() == 0 && getL2CacheEntry(address).Exclusive != in_msg.Requestor){ + // we have a writer and it is not us + out_msg.AckCount := 0 - 1; + + APPEND_TRANSITION_COMMENT(" Writer exists "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Exclusive); + + } + else if(getL2CacheEntry(address).Sharers.count() > 0){ + // this shouldn't be possible - we always track exclusive owner, but allow silent S replacements + assert(false); + + if(isSharer(address, in_msg.Requestor) == true && isOneSharerLeft(address, in_msg.Requestor) == true){ + // no filter check needed + APPEND_TRANSITION_COMMENT(" L1 replacement: No need to check L1 read/write filter - we are only reader"); + out_msg.AckCount := 0; + } + else{ + // reader(s) exist that is not us + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count(); + if(isSharer(address, in_msg.Requestor)){ + // don't include us + out_msg.AckCount := out_msg.AckCount + 1; + } + APPEND_TRANSITION_COMMENT(" Readers exist "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Sharers); + } + } + else{ + // we should always have no sharers! + assert(getL2CacheEntry(address).Sharers.count() == 0); + assert(getL2CacheEntry(address).Exclusive == in_msg.Requestor); + + out_msg.AckCount := 0; + + APPEND_TRANSITION_COMMENT(" sharers: "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Sharers); + APPEND_TRANSITION_COMMENT(" exclusive: "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Exclusive); + APPEND_TRANSITION_COMMENT(" L1 replacement: No need to check L1 read/write filter"); + } + } + } // for GETX + else{ + // unknown request type + APPEND_TRANSITION_COMMENT(in_msg.Type); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + assert(false); + } + } + } // for original vs new code + APPEND_TRANSITION_COMMENT(" requestor: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" AckCount: "); + APPEND_TRANSITION_COMMENT(out_msg.AckCount); + } + } + } + + action(f_sendExclusiveDataToRequestor, "fdx", desc="Send data from cache to reqeustor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceResponseType:L2_DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + + // different ack counts depending on situation + // IMPORTANT: assuming data sent exclusively for GETS request + if(in_msg.Type == CoherenceRequestType:GETS_ESCAPE){ + // no acks needed + out_msg.AckCount := 0; + } + else{ + + // ORIGINAL : + if( false ){ + // request filter checks from all L1s + out_msg.AckCount := 0 - (numberOfL1CachePerChip() - 1); + } + else{ + // NEW*** + if(getL2CacheEntry(address).L2Miss == true){ + // check whether we are the only sharer on the list. If so, no need to broadcast. + if(isSharer(address, in_msg.Requestor) == true && isOneSharerLeft(address, in_msg.Requestor) == true){ + // no filter check needed + APPEND_TRANSITION_COMMENT("We are only sharer"); + out_msg.AckCount := 0; + } + else{ + // wait for ACKs from the other NACKers + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count(); + if(isSharer(address, in_msg.Requestor)){ + // don't include us + out_msg.AckCount := out_msg.AckCount + 1; + } + APPEND_TRANSITION_COMMENT("Nackers exist"); + } + } + else{ + // This is a read request, so check whether we have a writer + if(getL2CacheEntry(address).Sharers.count() == 0 && getL2CacheEntry(address).Exclusive != in_msg.Requestor){ + // we have a writer and it is not us + out_msg.AckCount := 0 - 1; + + APPEND_TRANSITION_COMMENT(" Writer exists "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Exclusive); + } + else{ + // we should always have no sharers! + APPEND_TRANSITION_COMMENT(address); + APPEND_TRANSITION_COMMENT(" requestor: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" sharers: "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Sharers); + + DEBUG_EXPR(address); + DEBUG_EXPR(" requestor: "); + DEBUG_EXPR(in_msg.Requestor); + DEBUG_EXPR(" sharers: "); + DEBUG_EXPR(getL2CacheEntry(address).Sharers); + + assert(getL2CacheEntry(address).Sharers.count() == 0); + assert(getL2CacheEntry(address).Exclusive == in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" Sharers exist or we are writer, ok to read "); + out_msg.AckCount := 0; + } + } + } // for orginal vs new code + } + + APPEND_TRANSITION_COMMENT(" requestor: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" AckCount: "); + APPEND_TRANSITION_COMMENT(out_msg.AckCount); + } + } + } + + // send an accumulated ACK to requestor when we don't care about checking filters (for escape actions) + action(f_sendAccumulatedAckToRequestor, "faa", desc="Send ACKs to requestor") { + // special case: don't send ACK if uniprocessor, since we don't need it (just send data) + if((numberOfL1CachePerChip() - 1) > 0){ + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // count all L1s except requestor + out_msg.AckCount := numberOfL1CachePerChip() - 1; + APPEND_TRANSITION_COMMENT(" Total L1s: "); + APPEND_TRANSITION_COMMENT(numberOfL1CachePerChip()); + APPEND_TRANSITION_COMMENT(" Total ACKS: "); + APPEND_TRANSITION_COMMENT(out_msg.AckCount); + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + } + } + } + } + + // special INV used when we receive an escape action request. Sharers cannot NACK this invalidate. + action(fwm_sendFwdInvEscapeToSharersMinusRequestor, "fwme", desc="invalidate sharers for request, requestor is sharer") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="1") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceRequestType:INV_ESCAPE; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Request_Control; + //also pass along timestamp + out_msg.Timestamp := in_msg.Timestamp; + } + } + } + + action(f_profileRequestor, "prq", desc="Profiles the requestor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + APPEND_TRANSITION_COMMENT(" requestor: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + } + } + + // marks the L2 block as transactional if request was transactional + action(f_markBlockTransIfTrans, "\mbt", desc="Mark an L2 block as transactional") { + peek(L1unblockNetwork_in, ResponseMsg) { + if(in_msg.Transactional == true){ + L2cacheMemory[address].Trans := true; + } + } + } + + action(q_profileOverflow, "po", desc="profile the overflowed block"){ + profileOverflow(address, machineID); + } + + action(p_profileRequest, "pcc", desc="Profile request msg") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + APPEND_TRANSITION_COMMENT(" request: Timestamp: "); + APPEND_TRANSITION_COMMENT(in_msg.Timestamp); + APPEND_TRANSITION_COMMENT(" Requestor: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + APPEND_TRANSITION_COMMENT(" Dest: "); + APPEND_TRANSITION_COMMENT(in_msg.Destination); + APPEND_TRANSITION_COMMENT(" PA: "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + APPEND_TRANSITION_COMMENT(" Type: "); + APPEND_TRANSITION_COMMENT(in_msg.Type); + APPEND_TRANSITION_COMMENT(" Mode: "); + APPEND_TRANSITION_COMMENT(in_msg.AccessMode); + APPEND_TRANSITION_COMMENT(" PF: "); + APPEND_TRANSITION_COMMENT(in_msg.Prefetch); + } + } + + //********************************END*************************** + + action(d_sendDataToRequestor, "d", desc="Send data from cache to reqeustor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceResponseType:L2_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count(); + if (getL2CacheEntry(address).Sharers.isElement(in_msg.Requestor)) { + out_msg.AckCount := out_msg.AckCount + 1; + } + APPEND_TRANSITION_COMMENT(" AckCount: "); + APPEND_TRANSITION_COMMENT(out_msg.AckCount); + } + } + } + + // use DATA instead of L2_DATA because L1 doesn't need to wait for acks from L1 filters in this case + action(ds_sendSharedDataToRequestor, "ds", desc="Send data from cache to reqeustor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:L2_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + // no ACKS needed because no possible conflicts + out_msg.AckCount := 0; + } + } + } + + action(f_sendInvToSharers, "fsi", desc="invalidate sharers for L2 replacement") { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_TAG_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:REPLACE; + out_msg.Requestor := machineID; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.MessageSize := MessageSizeType:Request_Control; + } + } + + action(fwm_sendFwdInvToSharersMinusRequestor, "fwm", desc="invalidate sharers for request, requestor is sharer") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_TAG_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Request_Control; + //also pass along timestamp + out_msg.Timestamp := in_msg.Timestamp; + APPEND_TRANSITION_COMMENT(" Sharers: "); + APPEND_TRANSITION_COMMENT(L2cacheMemory[address].Sharers); + } + } + } + + // OTHER ACTIONS + action(i_allocateTBE, "i", desc="Allocate TBE for internal/external request(isPrefetch=0, number of invalidates=0)") { + check_allocate(L2_TBEs); + L2_TBEs.allocate(address); + L2_TBEs[address].L1_GetS_IDs.clear(); + L2_TBEs[address].DataBlk := getL2CacheEntry(address).DataBlk; + L2_TBEs[address].Dirty := getL2CacheEntry(address).Dirty; + L2_TBEs[address].pendingAcks := getL2CacheEntry(address).Sharers.count(); + } + + action(i_setTBEPhysicalAddress, "ia", desc="Sets the physical address field of the TBE"){ + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2_TBEs[address].PhysicalAddress := in_msg.PhysicalAddress; + } + } + + action(s_deallocateTBE, "s", desc="Deallocate external TBE") { + L2_TBEs.deallocate(address); + } + + action(jj_popL1RequestQueue, "\j", desc="Pop incoming L1 request queue") { + profileMsgDelay(0, L1RequestIntraChipL2Network_in.dequeue_getDelayCycles()); + } + + action(k_popUnblockQueue, "k", desc="Pop incoming unblock queue") { + profileMsgDelay(0, L1unblockNetwork_in.dequeue_getDelayCycles()); + } + + + action(o_popIncomingResponseQueue, "o", desc="Pop Incoming Response queue") { + profileMsgDelay(3, responseIntraChipL2Network_in.dequeue_getDelayCycles()); + } + + + action(m_writeDataToCache, "m", desc="Write data from response queue to cache") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + getL2CacheEntry(address).DataBlk := in_msg.DataBlk; + getL2CacheEntry(address).Dirty := in_msg.Dirty; + // reset the L2 miss bit + getL2CacheEntry(address).L2Miss := false; + } + } + + // Sets the L2Miss bit in the L2 entry - indicates data was sourced from memory + action(m_markL2MissBit, "mi", desc="Set the entry's L2 Miss bit") { + getL2CacheEntry(address).L2Miss := true; + } + + action(m_copyNackersIntoSharers, "mn", desc="Copy the NACKers list into our sharers list") { + peek(L1unblockNetwork_in, ResponseMsg) { + assert(in_msg.Nackers.count() > 0); + getL2CacheEntry(address).Sharers.clear(); + // only need to copy into sharers list if we are in special state of "multicast" filter checks + if(getL2CacheEntry(address).L2Miss == true){ + getL2CacheEntry(address).Sharers := in_msg.Nackers; + APPEND_TRANSITION_COMMENT(" Unblocker: "); + APPEND_TRANSITION_COMMENT(in_msg.Sender); + APPEND_TRANSITION_COMMENT(" Nackers: "); + APPEND_TRANSITION_COMMENT(getL2CacheEntry(address).Sharers); + } + } + } + + action(mr_writeDataToCacheFromRequest, "mr", desc="Write data from response queue to cache") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + getL2CacheEntry(address).DataBlk := in_msg.DataBlk; + getL2CacheEntry(address).Dirty := in_msg.Dirty; + // reset the L2 miss bit + getL2CacheEntry(address).L2Miss := false; + } + } + + action(q_updateAck, "q", desc="update pending ack count") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + L2_TBEs[address].pendingAcks := L2_TBEs[address].pendingAcks - in_msg.AckCount; + APPEND_TRANSITION_COMMENT(in_msg.AckCount); + APPEND_TRANSITION_COMMENT(" p: "); + APPEND_TRANSITION_COMMENT(L2_TBEs[address].pendingAcks); + } + } + + // For transactional memory. If received NACK instead of ACK + action(q_updateNack, "qn", desc="update pending ack count") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + // set flag indicating we have seen NACK + L2_TBEs[address].nack := true; + L2_TBEs[address].pendingAcks := L2_TBEs[address].pendingAcks - in_msg.AckCount; + APPEND_TRANSITION_COMMENT(in_msg.AckCount); + APPEND_TRANSITION_COMMENT(" p: "); + APPEND_TRANSITION_COMMENT(L2_TBEs[address].pendingAcks); + } + } + + action(qq_writeDataToTBE, "\qq", desc="Write data from response queue to TBE") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + L2_TBEs[address].DataBlk := in_msg.DataBlk; + L2_TBEs[address].Dirty := in_msg.Dirty; + } + } + + + action(z_stall, "z", desc="Stall") { + } + + + action(ss_recordGetSL1ID, "\s", desc="Record L1 GetS for load response") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2_TBEs[address].L1_GetS_IDs.add(in_msg.Requestor); + } + } + + action(xx_recordGetXL1ID, "\x", desc="Record L1 GetX for store response") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2_TBEs[address].L1_GetX_ID := in_msg.Requestor; + } + } + + action(set_setMRU, "\set", desc="set the MRU entry") { + L2cacheMemory.setMRU(address); + } + + action(qq_allocateL2CacheBlock, "\q", desc="Set L2 cache tag equal to tag of block B.") { + if (L2cacheMemory.isTagPresent(address) == false) { + L2cacheMemory.allocate(address); + } + } + + action(rr_deallocateL2CacheBlock, "\r", desc="Deallocate L2 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + L2cacheMemory.deallocate(address); + } + + action(t_sendWBAck, "t", desc="Send writeback ACK") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_TAG_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:WB_ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + + action(ts_sendInvAckToUpgrader, "ts", desc="Send ACK to upgrader") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_TAG_LATENCY") { + out_msg.Address := address; + out_msg.PhysicalAddress := in_msg.PhysicalAddress; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // upgrader doesn't get ack from itself, hence the + 1 + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count() + 1; + APPEND_TRANSITION_COMMENT(" "); + APPEND_TRANSITION_COMMENT(in_msg.PhysicalAddress); + } + } + } + + // same as above, but send NACK instead of ACK + action(ts_sendInvNackToUpgrader, "tsn", desc="Send NACK to upgrader") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_TAG_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:NACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // upgrader doesn't get ack from itself, hence the + 1 + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count() + 1; + } + } + } + + action(uu_profileMiss, "\u", desc="Profile the demand miss") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + profile_L2Cache_miss(convertToGenericType(in_msg.Type), in_msg.AccessMode, MessageSizeTypeToInt(in_msg.MessageSize), in_msg.Prefetch, L1CacheMachIDToProcessorNum(in_msg.Requestor)); + } + } + + action(ww_profileMissNoDir, "\w", desc="Profile this transition at the L2 because Dir won't see the request") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + // profile_request(in_msg.L1CacheStateStr, getStateStr(address), "NA", getCoherenceRequestTypeStr(in_msg.Type)); + } + } + + + + action(nn_addSharer, "\n", desc="Add L1 sharer to list") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + addSharer(address, in_msg.Requestor); + APPEND_TRANSITION_COMMENT( getL2CacheEntry(address).Sharers ); + } + } + + action(nnu_addSharerFromUnblock, "\nu", desc="Add L1 sharer to list") { + peek(L1unblockNetwork_in, ResponseMsg) { + addSharer(address, in_msg.Sender); + if (in_msg.RemoveLastOwnerFromDir == true) { + // We do this to solve some races with PUTX + APPEND_TRANSITION_COMMENT("Last owner removed, it was "); + APPEND_TRANSITION_COMMENT(in_msg.LastOwnerID); + L2cacheMemory[address].Sharers.remove(in_msg.LastOwnerID); + assert(in_msg.LastOwnerID == L2cacheMemory[address].Exclusive); + } + } + } + + + action(kk_removeRequestSharer, "\k", desc="Remove L1 Request sharer from list") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2cacheMemory[address].Sharers.remove(in_msg.Requestor); + } + } + + action(ll_clearSharers, "\l", desc="Remove all L1 sharers from list") { + L2cacheMemory[address].Sharers.clear(); + } + + action(mmu_markExclusiveFromUnblock, "\mu", desc="set the exclusive owner") { + peek(L1unblockNetwork_in, ResponseMsg) { + if (in_msg.RemoveLastOwnerFromDir == true) { + // We do this to solve some races with PUTX + APPEND_TRANSITION_COMMENT(" Last owner removed, it was "); + APPEND_TRANSITION_COMMENT(in_msg.LastOwnerID); + assert(in_msg.LastOwnerID == L2cacheMemory[address].Exclusive); + } + L2cacheMemory[address].Sharers.clear(); + L2cacheMemory[address].Exclusive := in_msg.Sender; + addSharer(address, in_msg.Sender); + } + } + + action(zz_recycleL1RequestQueue, "zz", desc="recycle L1 request queue") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:PUTX || in_msg.Type == CoherenceRequestType:PUTS) { + if (L2cacheMemory.isTagPresent(in_msg.Address)) { + getL2CacheEntry(in_msg.Address).L1PutsPending := getL2CacheEntry(in_msg.Address).L1PutsPending + 1; + DEBUG_EXPR("RECYCLE PutSPending "); + DEBUG_EXPR(getL2CacheEntry(in_msg.Address).L1PutsPending); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(in_msg.Requestor); + } + } + } + L1RequestIntraChipL2Network_in.recycle(); + } + + //***************************************************** + // TRANSITIONS + //***************************************************** + + /* Recycle while waiting for PUT */ + transition({PB_MT, PB_MT_IB, PB_SS}, {L1_GETS, L1_GET_INSTR, L1_GETX, L1_UPGRADE, L1_GETS_ESCAPE, L1_GETX_ESCAPE, L1_GET_INSTR_ESCAPE, L2_Replacement, L2_Replacement_clean, L2_Replacement_XACT, L2_Replacement_clean_XACT}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + transition({IM, IS, ISS, SS_MB, M_MB, ISS_MB, IS_SSB, MT_MB, MT_IIB, MT_IB, MT_SB, M_SSB, SS_SSB}, + {L2_Replacement, L2_Replacement_clean, L2_Replacement_XACT, L2_Replacement_clean_XACT}) { + zz_recycleL1RequestQueue; + } + + transition({SS_MB, M_MB, ISS_MB, IS_SSB, MT_MB, MT_IIB, MT_IB, MT_SB, M_SSB, SS_SSB}, + {L1_GETS, L1_GET_INSTR, L1_GETX, L1_UPGRADE, L1_GETS_ESCAPE, L1_GETX_ESCAPE, L1_GET_INSTR_ESCAPE}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + transition({NP, SS, M, M_I, MT_I, MCT_I, I_I, S_I, ISS, IS, IM, /*SS_MB,*/ SS_SSB, /* MT_MB, M_MB, ISS_MB,*/ IS_SSB, M_SSB, /*MT_IIB, */MT_IB/*, MT_SB*/}, {L1_PUTX,L1_PUTS}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + // occurs when L2 replacement raced with L1 replacement, and L2 finished its replacement first + transition({NP, M_I, MCT_I, I_I, S_I, IS, ISS, IM, SS, M, MT, IS_SSB, MT_IB, M_SSB, SS_SSB}, {L1_PUTX_old, L1_PUTS_old}){ + f_profileRequestor; + jj_popL1RequestQueue; + } + + // PUT from current (last) exclusive owner, that was replacing the line when it received Fwd req + transition(MT_I, {L1_PUTX_old, L1_PUTS_old}) { + f_profileRequestor; + jj_popL1RequestQueue; + } + + transition({SS, M, MT}, {L1_PUT_PENDING}) { // L1_PUT_ msg pending for the block, don't accept new requests until PUT is processed */ + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + //=============================================== + // BASE STATE - I + + // Transitions from I (Idle) + + // When L2 doesn't have block, need to send broadcasst to all L1s to check appropriate filter(s) + transition(NP, L1_GETS, ISS) { + p_profileRequest; + f_profileRequestor; + qq_allocateL2CacheBlock; + ll_clearSharers; + // will mark as exclusive when we get unblocked with success + //nn_addSharer; + i_allocateTBE; + i_setTBEPhysicalAddress; + ss_recordGetSL1ID; + a_issueFetchToMemory; + // for correctness we need to query both read + write filters + a_checkL1ReadWriteFiltersExceptRequestor; + uu_profileMiss; + jj_popL1RequestQueue; + } + + // no need to check filters, send accumulated ACK to requestor + transition(NP, L1_GETS_ESCAPE, ISS) { + p_profileRequest; + f_profileRequestor; + qq_allocateL2CacheBlock; + ll_clearSharers; + // will mark as exclusive when we get unblocked with success + //nn_addSharer; + i_allocateTBE; + i_setTBEPhysicalAddress; + ss_recordGetSL1ID; + a_issueFetchToMemory; + // send accumulated ACK + f_sendAccumulatedAckToRequestor; + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition(NP, L1_GET_INSTR, IS) { + p_profileRequest; + f_profileRequestor; + qq_allocateL2CacheBlock; + ll_clearSharers; + nn_addSharer; + i_allocateTBE; + i_setTBEPhysicalAddress; + ss_recordGetSL1ID; + a_issueFetchToMemory; + // for correctness query the read + write filters + a_checkL1ReadWriteFiltersExceptRequestor; + uu_profileMiss; + jj_popL1RequestQueue; + } + + // no need to query filters, send accumluated ACK to requestor + transition(NP, L1_GET_INSTR_ESCAPE, IS) { + p_profileRequest; + f_profileRequestor; + qq_allocateL2CacheBlock; + ll_clearSharers; + nn_addSharer; + i_allocateTBE; + i_setTBEPhysicalAddress; + ss_recordGetSL1ID; + a_issueFetchToMemory; + // send accumulated ACK + f_sendAccumulatedAckToRequestor; + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition(NP, L1_GETX, IM) { + p_profileRequest; + f_profileRequestor; + qq_allocateL2CacheBlock; + ll_clearSharers; + // nn_addSharer; + i_allocateTBE; + i_setTBEPhysicalAddress; + xx_recordGetXL1ID; + a_issueFetchToMemory; + // also query the L1 write and read filters + a_checkL1ReadWriteFiltersExceptRequestor; + uu_profileMiss; + jj_popL1RequestQueue; + } + + // don't check filters + transition(NP, L1_GETX_ESCAPE, IM) { + p_profileRequest; + f_profileRequestor; + qq_allocateL2CacheBlock; + ll_clearSharers; + // nn_addSharer; + i_allocateTBE; + i_setTBEPhysicalAddress; + xx_recordGetXL1ID; + a_issueFetchToMemory; + // send accumulated ACK to requestor + f_sendAccumulatedAckToRequestor; + uu_profileMiss; + jj_popL1RequestQueue; + } + + + // transitions from IS/IM + + // force L1s to respond success or failure + transition(ISS, Mem_Data, ISS_MB){ + m_writeDataToCache; + m_markL2MissBit; + // send exclusive data but force L1 to wait for filter responses + f_sendExclusiveDataToGetSRequestor; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IS, Mem_Data, IS_SSB){ + m_writeDataToCache; + m_markL2MissBit; + // send data but force L1 to wait for filter responses + f_sendDataToGetSRequestor; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IM, Mem_Data, ISS_MB){ + m_writeDataToCache; + m_markL2MissBit; + // send data but force L1 to wait for filter responses + f_sendDataToGetXRequestor; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // disallow grouping of requestors. There is a correctness problem if we check the wrong + // filters as indicated by the original requestor. + transition({IS, ISS}, {L1_GETX, L1_GETS, L1_GET_INSTR, L1_GETX_ESCAPE, L1_GETS_ESCAPE, L1_GET_INSTR_ESCAPE}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + transition(IM, {L1_GETX, L1_GETS, L1_GET_INSTR, L1_GETX_ESCAPE, L1_GETS_ESCAPE, L1_GET_INSTR_ESCAPE}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + // transitions from SS + transition(SS, {L1_GETS, L1_GET_INSTR, L1_GETS_ESCAPE, L1_GET_INSTR_ESCAPE}, SS_SSB) { + p_profileRequest; + f_profileRequestor; + ds_sendSharedDataToRequestor; + nn_addSharer; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + // For isolation the L1 filters might return NACKs to the requestor + transition(SS, L1_GETX, SS_MB) { + p_profileRequest; + f_profileRequestor; + d_sendDataToRequestor; + fwm_sendFwdInvToSharersMinusRequestor; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + // send special INV to sharers - they have to invalidate + transition(SS, L1_GETX_ESCAPE, SS_MB) { + p_profileRequest; + f_profileRequestor; + d_sendDataToRequestor; + fwm_sendFwdInvEscapeToSharersMinusRequestor; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + // For isolation the L1 filters might return NACKs to the requestor + transition(SS, L1_UPGRADE, SS_MB) { + f_profileRequestor; + fwm_sendFwdInvToSharersMinusRequestor; + ts_sendInvAckToUpgrader; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(SS, L2_Replacement_clean, I_I) { + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(SS, L2_Replacement_clean_XACT, I_I) { + q_profileOverflow; + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(SS, L2_Replacement, S_I) { + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(SS, L2_Replacement_XACT, S_I) { + q_profileOverflow; + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + // Transitions from M + + // send data, but force L1 to wait for filter responses + transition(M, L1_GETS, M_MB) { + p_profileRequest; + f_profileRequestor; + f_sendExclusiveDataToRequestor; + // selective filter checks, but need to check both read+write in case nackers put NP block into M state + a_checkNackerL1ReadWriteFiltersExceptRequestor; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + // don't care about filters + transition(M, L1_GETS_ESCAPE, M_MB) { + p_profileRequest; + f_profileRequestor; + f_sendExclusiveDataToRequestor; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(M, L1_GET_INSTR, M_SSB) { + p_profileRequest; + f_profileRequestor; + f_sendDataToRequestor; + // NEW - selective filter checks, but need to check both read+write in case nackers put NP block into M state + a_checkNackerL1ReadWriteFiltersExceptRequestor; + // This should always be _after_ f_sendDataToRequestor and a_checkNackerL1WriteFiltersExceptRequestor, since they + // explicitly look at the sharers list! + nn_addSharer; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + // don't care about filters + transition(M, L1_GET_INSTR_ESCAPE, M_SSB) { + p_profileRequest; + f_profileRequestor; + f_sendDataToRequestor; + nn_addSharer; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(M, L1_GETX, M_MB) { + p_profileRequest; + f_profileRequestor; + f_sendDataToRequestor; + // selective filter checks + a_checkNackerL1ReadWriteFiltersExceptRequestor; + // issue filter checks + //a_checkL1ReadWriteFiltersExceptRequestor; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + // don't care about filters + transition(M, L1_GETX_ESCAPE, M_MB) { + p_profileRequest; + f_profileRequestor; + f_sendDataToRequestor; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(M, L2_Replacement, M_I) { + i_allocateTBE; + c_exclusiveReplacement; + rr_deallocateL2CacheBlock; + } + + transition(M, L2_Replacement_clean, M_I) { + rr_deallocateL2CacheBlock; + } + + transition(M, L2_Replacement_XACT, M_I) { + q_profileOverflow; + i_allocateTBE; + c_exclusiveReplacement; + rr_deallocateL2CacheBlock; + } + + transition(M, L2_Replacement_clean_XACT, M_I) { + q_profileOverflow; + rr_deallocateL2CacheBlock; + } + + + // transitions from MT + transition(MT, {L1_GETX, L1_GETX_ESCAPE}, MT_MB) { + p_profileRequest; + f_profileRequestor; + b_forwardRequestToExclusive; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + + transition(MT, {L1_GETS, L1_GET_INSTR, L1_GETS_ESCAPE, L1_GET_INSTR_ESCAPE}, MT_IIB) { + p_profileRequest; + f_profileRequestor; + b_forwardRequestToExclusive; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(MT, L2_Replacement, MT_I) { + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(MT, L2_Replacement_clean, MCT_I) { + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(MT, L2_Replacement_XACT, MT_I) { + q_profileOverflow; + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(MT, L2_Replacement_clean_XACT, MCT_I) { + q_profileOverflow; + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(MT, L1_PUTX, M) { + f_profileRequestor; + // this doesn't affect exlusive ptr + ll_clearSharers; + mr_writeDataToCacheFromRequest; + t_sendWBAck; + jj_popL1RequestQueue; + } + + // This is for the case of transactional read line in E state being replaced from L1. We need to maintain isolation on this + // in the event of a future transactional store from another proc, so we maintain this transactional sharer on the list + transition(MT, L1_PUTS, SS) { + f_profileRequestor; + ll_clearSharers; + // maintain transactional read isolation + nn_addSharer; + mr_writeDataToCacheFromRequest; + t_sendWBAck; + jj_popL1RequestQueue; + } + + // transitions from blocking states + transition(SS_MB, Unblock_Cancel, SS) { + k_popUnblockQueue; + } + + transition(M_SSB, Unblock_Cancel, M) { + ll_clearSharers; + // copy NACKers list from unblock message to our sharers list + m_copyNackersIntoSharers; + k_popUnblockQueue; + } + + transition(MT_MB, Unblock_Cancel, MT) { + k_popUnblockQueue; + } + + transition(MT_IB, Unblock_Cancel, MT) { + k_popUnblockQueue; + } + + transition(MT_IIB, Unblock_Cancel, MT){ + k_popUnblockQueue; + } + + // L2 just got the data from memory, but we have Nackers. We can let nacked block reside in M, but GETS request needs to check read+write + // signatures to avoid atomicity violations. + transition({ISS_MB, IS_SSB}, Unblock_Cancel, M){ + //rr_deallocateL2CacheBlock; + // copy NACKers list from unblock message to our sharers list + m_copyNackersIntoSharers; + k_popUnblockQueue; + } + + transition(M_MB, Unblock_Cancel, M) { + // copy NACKers list from unblock message to our sharers list + m_copyNackersIntoSharers; + k_popUnblockQueue; + } + + transition(SS_MB, Exclusive_Unblock, MT) { + // update actual directory + mmu_markExclusiveFromUnblock; + // mark block as trans if needed + f_markBlockTransIfTrans; + k_popUnblockQueue; + } + + // PUT from next exclusive surpassed its own ExclusiveUnblock + // Perceived as PUTX_old because the directory is outdated + transition(SS_MB, {L1_PUTX_old, L1_PUTS_old}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + // PUT from current (old) exclusive, can't do anything with it in this state + // Don't know whether exclusive was replacing or not, so wait to see what Unblock says + transition(SS_MB, {L1_PUTX, L1_PUTS}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + // Next exclusive informs that last owner was replacing the line when it received Fwd req + // Thus, expect a PUTX_old from previous owner + transition(SS_MB, Exclusive_Unblock_WaitPUTold, PB_MT) { + // update actual directory + mmu_markExclusiveFromUnblock; + // mark block as trans if needed + f_markBlockTransIfTrans; + k_popUnblockQueue; + } + + transition(PB_MT, {L1_PUTX_old, L1_PUTS_old}, MT) { // OK, PUT_old received, go to MT + f_profileRequestor; + jj_popL1RequestQueue; + } + + // PUT from current (next) exclusive, so recycle + // Expecting PUT_old, won't take in new PUT until previous PUT arrives + transition(PB_MT, {L1_PUTX, L1_PUTS}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + // L2 blocks on GETS requests in SS state + transition(SS_SSB, Unblock, SS) { + // mark block as trans if needed + f_markBlockTransIfTrans; + k_popUnblockQueue; + } + + transition({M_SSB, IS_SSB}, Unblock, SS) { + // we already added the sharer when we received original request + // mark block as trans if needed + f_markBlockTransIfTrans; + k_popUnblockQueue; + } + + transition({M_MB, MT_MB, ISS_MB}, Exclusive_Unblock, MT) { + // update actual directory + mmu_markExclusiveFromUnblock; + // mark block as trans if needed + f_markBlockTransIfTrans; + k_popUnblockQueue; + } + + transition({M_MB, MT_MB, ISS_MB}, Exclusive_Unblock_WaitPUTold, PB_MT) { + // update actual directory + mmu_markExclusiveFromUnblock; + // mark block as trans if needed + f_markBlockTransIfTrans; + k_popUnblockQueue; + } + + // PUT from (not yet) next exclusive surpassed its own ExclusiveUnblock + // thus became PUTX_old (since directory is not up-to-date) + transition({M_MB, MT_MB, ISS_MB}, {L1_PUTX_old, L1_PUTS_old}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + // PUT from current (previous) owner: recycle until unblock arrives + // We don't know whether replacing cache is waiting for WB_Ack or it was replacing when fwd arrived + transition({M_MB, MT_MB, ISS_MB}, {L1_PUTX, L1_PUTS}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + // L1 requestor received data from exclusive L1, but writeback data from exclusive L1 hasn't arrived yet + transition(MT_IIB, Unblock, MT_IB) { + nnu_addSharerFromUnblock; + // mark block as trans if needed + f_markBlockTransIfTrans; + k_popUnblockQueue; + } + + // PUT from current (previous) owner: recycle + // We don't know whether replacing cache is waiting for WB_Ack or it was replacing when fwd arrived + transition(MT_IIB, {L1_PUTX, L1_PUTS}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + transition(MT_IB, {WB_Data, WB_Data_clean}, SS) { + m_writeDataToCache; + o_popIncomingResponseQueue; + } + + // PUT from (not yet) next exclusive, but unblock hasn't arrived yet, so it became PUT_old: recycle + transition(MT_IIB, {L1_PUTX_old, L1_PUTS_old}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + transition(MT_IIB, Unblock_WaitPUTold, PB_MT_IB) { // Now arrives Unblock, wait for PUT and WB_Data + nnu_addSharerFromUnblock; + // mark block as trans if needed + f_markBlockTransIfTrans; + k_popUnblockQueue; + } + + // L1 requestor has not received data from exclusive L1, but we received writeback data from exclusive L1 + transition(MT_IIB, {WB_Data, WB_Data_clean}, MT_SB) { + m_writeDataToCache; + o_popIncomingResponseQueue; + } + + // PUT_old from previous owner, that was replacing when it received Fwd req + transition(PB_MT_IB, {L1_PUTX_old, L1_PUTS_old}, MT_IB) { // Go to MT_IB, and wait for WB_Data + f_profileRequestor; + jj_popL1RequestQueue; + } + + transition(PB_MT_IB, {L1_PUTX, L1_PUTS}) { // Waiting for PUT_old, don't take new PUT in + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + // WB_data from previous owner, we already received unblock, just wait for PUT_old to go to SS + transition(PB_MT_IB, {WB_Data, WB_Data_clean}, PB_SS) { // Received Unblock, now arrives WB_Data, wait for PUT + m_writeDataToCache; + o_popIncomingResponseQueue; + } + + transition(PB_SS, {L1_PUTX_old, L1_PUTS_old}, SS) { // Received Unblock and WB_Data, now arrives PUT, go to SS + f_profileRequestor; + jj_popL1RequestQueue; + } + + // PUT from new exclusive owner, while waiting for PUT from previous exclusive owner: recycle + transition(PB_SS, {L1_PUTX, L1_PUTS}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + transition(MT_SB, Unblock, SS) { + nnu_addSharerFromUnblock; + // mark block as trans if needed + f_markBlockTransIfTrans; + k_popUnblockQueue; + } + + transition(MT_SB, Unblock_WaitPUTold, PB_SS) { // Received WB_Data, now arriving Unblock, wait for PUT + nnu_addSharerFromUnblock; + // mark block as trans if needed + f_markBlockTransIfTrans; + k_popUnblockQueue; + } + + // PUT from (not yet) new exclusive owner, before we receive Unblock from it (became PUT_old because directory is not up-to-date) + transition(MT_SB, {L1_PUTX_old, L1_PUTS_old}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + // PUT from current (last) exclusive owner, that was replacing the line when it received Fwd req + transition(MT_SB, {L1_PUTX, L1_PUTS}) { + kk_removeRequestSharer; // When Unblock arrives, it'll trigger Unblock, not Unblock_WaitPUTold + f_profileRequestor; + jj_popL1RequestQueue; + } + + // writeback states + transition({I_I, S_I, MT_I, MCT_I, M_I}, {L1_GETX, L1_UPGRADE, L1_GETS, L1_GET_INSTR, L1_GETX_ESCAPE, L1_GETS_ESCAPE, L1_GET_INSTR_ESCAPE}) { + f_profileRequestor; + zz_recycleL1RequestQueue; + } + + transition(I_I, Ack) { + q_updateAck; + o_popIncomingResponseQueue; + } + + transition(I_I, Ack_all, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition({MT_I, MCT_I}, WB_Data, M_I) { + qq_writeDataToTBE; + ct_exclusiveReplacementFromTBE; + o_popIncomingResponseQueue; + } + + transition(MCT_I, WB_Data_clean, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // L1 never changed Dirty data + transition(MT_I, Ack_all, M_I) { + ct_exclusiveReplacementFromTBE; + o_popIncomingResponseQueue; + } + + // clean data that L1 exclusive never wrote + transition(MCT_I, Ack_all, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(MT_I, WB_Data_clean, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(S_I, Ack) { + q_updateAck; + o_popIncomingResponseQueue; + } + + transition(S_I, Ack_all, M_I) { + ct_exclusiveReplacementFromTBE; + o_popIncomingResponseQueue; + } + + transition(M_I, Mem_Ack, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } +} + diff --git a/src/mem/protocol/MESI_CMP_filter_directory-mem.sm b/src/mem/protocol/MESI_CMP_filter_directory-mem.sm new file mode 100644 index 000000000..1fcd234fe --- /dev/null +++ b/src/mem/protocol/MESI_CMP_filter_directory-mem.sm @@ -0,0 +1,166 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOESI_CMP_token-dir.sm 1.6 05/01/19 15:48:35-06:00 mikem@royal16.cs.wisc.edu $ + */ + + +machine(Directory, "Token protocol") { + + MessageBuffer requestToDir, network="From", virtual_network="2", ordered="false"; + MessageBuffer responseToDir, network="From", virtual_network="3", ordered="false"; + MessageBuffer responseFromDir, network="To", virtual_network="3", ordered="false"; + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_I") { + // Base states + I, desc="Owner"; + } + + // Events + enumeration(Event, desc="Directory events") { + Fetch, desc="A GETX arrives"; + Data, desc="A GETS arrives"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + DataBlock DataBlk, desc="data for the block"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + + State getState(Address addr) { + return State:I; + } + + void setState(Address addr, State state) { + } + + // ** OUT_PORTS ** + out_port(responseNetwork_out, ResponseMsg, responseFromDir); + + // ** IN_PORTS ** + + in_port(requestNetwork_in, RequestMsg, requestToDir) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:Fetch, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:Fetch, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + in_port(responseNetwork_in, ResponseMsg, responseToDir) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceResponseType:MEMORY_DATA) { + trigger(Event:Data, in_msg.Address); + } else { + DEBUG_EXPR(in_msg.Type); + error("Invalid message"); + } + } + } + } + + // Actions + action(a_sendAck, "a", desc="Send ack to L2") { + peek(responseNetwork_in, ResponseMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Sender); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + + action(d_sendData, "d", desc="Send data to requestor") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(j_popIncomingRequestQueue, "j", desc="Pop incoming request queue") { + requestNetwork_in.dequeue(); + } + + action(k_popIncomingResponseQueue, "k", desc="Pop incoming request queue") { + responseNetwork_in.dequeue(); + } + + action(m_writeDataToMemory, "m", desc="Write dirty writeback to memory") { + peek(responseNetwork_in, ResponseMsg) { + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + // TRANSITIONS + + transition(I, Fetch) { + d_sendData; + j_popIncomingRequestQueue; + } + + transition(I, Data) { + m_writeDataToMemory; + a_sendAck; + k_popIncomingResponseQueue; + } +} diff --git a/src/mem/protocol/MESI_CMP_filter_directory-msg.sm b/src/mem/protocol/MESI_CMP_filter_directory-msg.sm new file mode 100644 index 000000000..a888e2450 --- /dev/null +++ b/src/mem/protocol/MESI_CMP_filter_directory-msg.sm @@ -0,0 +1,153 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MSI_MOSI_CMP_directory-msg.sm 1.5 05/01/19 15:48:37-06:00 mikem@royal16.cs.wisc.edu $ + * + */ + +// CoherenceRequestType +enumeration(CoherenceRequestType, desc="...") { + GETX, desc="Get eXclusive"; + GETX_ESCAPE, desc="Get eXclusive, while in escape action"; + UPGRADE, desc="UPGRADE to exclusive"; + GETS, desc="Get Shared"; + GETS_ESCAPE, desc="Get Shared, while in escape action"; + GET_INSTR, desc="Get Instruction"; + GET_INSTR_ESCAPE, desc="Get Instruction, while in escape action"; + INV, desc="INValidate, could be NACKed"; + INV_ESCAPE, desc="INValidate, cannot be NACKed"; + PUTX, desc="replacement message, for writeback to lower caches"; + PUTS, desc="clean replacement message, for writeback to lower caches"; + REPLACE, desc="replacement message, from lowest cache"; + CHECK_WRITE_FILTER, desc="check write filter message"; + CHECK_READ_WRITE_FILTER, desc="check both read and write filters message"; +} + +// CoherenceResponseType +enumeration(CoherenceResponseType, desc="...") { + MEMORY_ACK, desc="Ack from memory controller"; + DATA, desc="Data"; + DATA_EXCLUSIVE, desc="Data"; + L2_DATA, desc="data from L2, in shared mode"; + L2_DATA_EXCLUSIVE, desc="data from L2, in exclusive mode"; + MEMORY_DATA, desc="Data"; + ACK, desc="Generic invalidate ack"; + NACK, desc="NACK used to maintain transactional isolation"; + WB_ACK, desc="writeback ack"; + UNBLOCK, desc="unblock"; + EXCLUSIVE_UNBLOCK, desc="exclusive unblock"; + UNBLOCK_CANCEL, desc="unblock when trans. request fails"; +} + +// RequestMsg +structure(RequestMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Line address for this request"; + Address PhysicalAddress, desc="Physical address for this request"; + CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)"; + AccessModeType AccessMode, desc="user/supervisor access type"; + MachineID Requestor , desc="What component request"; + NetDest Destination, desc="What components receive the request, includes MachineType and num"; + MessageSizeType MessageSize, desc="size category of the message"; + DataBlock DataBlk, desc="Data for the cache line (if PUTX)"; + bool Dirty, default="false", desc="Dirty bit"; + PrefetchBit Prefetch, desc="Is this a prefetch request"; + uint64 Timestamp, desc="TLR-like Timestamp"; +} + +// ResponseMsg +structure(ResponseMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Line address for this request"; + Address PhysicalAddress, desc="Physical address for this request"; + CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)"; + MachineID Sender, desc="What component sent the data"; + NetDest Destination, desc="Node to whom the data is sent"; + DataBlock DataBlk, desc="Data for the cache line"; + bool Dirty, default="false", desc="Dirty bit"; + int AckCount, default="0", desc="number of acks in this message"; + MessageSizeType MessageSize, desc="size category of the message"; + uint64 Timestamp, desc="TLR-like Timestamp"; + NetDest Nackers, desc="The nodes which sent NACKs to requestor"; + bool Transactional, desc="Whether this address was transactional"; + bool RemoveLastOwnerFromDir, desc="To solve some races with PUTX/GETS"; + MachineID LastOwnerID, desc="What component sent the data"; +} + +// TriggerType +enumeration(TriggerType, desc="...") { + ALL_ACKS, desc="When all acks/nacks have been received"; +} + +// TriggerMsg +structure(TriggerMsg, desc="...", interface="Message") { + Address Address, desc="Line address for this request"; + Address PhysicalAddress, desc="Physical address for this request"; + TriggerType Type, desc="Type of trigger"; +} + +/* + GETX, desc="Get eXclusive"; + UPGRADE, desc="UPGRADE to exclusive"; + GETS, desc="Get Shared"; + GET_INSTR, desc="Get Instruction"; + INV, desc="INValidate"; + PUTX, desc="replacement message, for writeback to lower caches"; + REPLACE, desc="replacement message, from lowest cache"; + CHECK_WRITE_FILTER, desc="check write filter message"; + CHECK_READ_WRITE_FILTER, desc="check both read and write filters message"; +*/ + +GenericRequestType convertToGenericType(CoherenceRequestType type) { + if(type == CoherenceRequestType:PUTX) { + return GenericRequestType:PUTX; + } else if(type == CoherenceRequestType:GETS) { + return GenericRequestType:GETS; + } else if(type == CoherenceRequestType:GETS_ESCAPE) { + return GenericRequestType:GETS; + } else if(type == CoherenceRequestType:GET_INSTR) { + return GenericRequestType:GET_INSTR; + } else if(type == CoherenceRequestType:GET_INSTR_ESCAPE) { + return GenericRequestType:GET_INSTR; + } else if(type == CoherenceRequestType:GETX) { + return GenericRequestType:GETX; + } else if(type == CoherenceRequestType:GETX_ESCAPE) { + return GenericRequestType:GETX; + } else if(type == CoherenceRequestType:UPGRADE) { + return GenericRequestType:UPGRADE; + } else if(type == CoherenceRequestType:INV) { + return GenericRequestType:INV; + } else if( type == CoherenceRequestType:REPLACE) { + return GenericRequestType:REPLACEMENT; + } else { + DEBUG_EXPR(type); + error("invalid CoherenceRequestType"); + } +} + + diff --git a/src/mem/protocol/MESI_CMP_filter_directory.slicc b/src/mem/protocol/MESI_CMP_filter_directory.slicc new file mode 100644 index 000000000..715da5795 --- /dev/null +++ b/src/mem/protocol/MESI_CMP_filter_directory.slicc @@ -0,0 +1,7 @@ +../protocols/LogTM.sm +../protocols/MESI_CMP_filter_directory-msg.sm +../protocols/MESI_CMP_filter_directory-L2cache.sm +../protocols/MESI_CMP_filter_directory-L1cache.sm +../protocols/MESI_CMP_filter_directory-mem.sm +../protocols/standard_CMP-protocol.sm + diff --git a/src/mem/protocol/MESI_CMP_filter_directory_m-mem.sm b/src/mem/protocol/MESI_CMP_filter_directory_m-mem.sm new file mode 100644 index 000000000..2f8818489 --- /dev/null +++ b/src/mem/protocol/MESI_CMP_filter_directory_m-mem.sm @@ -0,0 +1,250 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOESI_CMP_token-dir.sm 1.6 05/01/19 15:48:35-06:00 mikem@royal16.cs.wisc.edu $ + */ + +// This file is copied from Yasuko Watanabe's prefetch / memory protocol +// Copied here by aep 12/14/07 + + +machine(Directory, "MESI_CMP_filter_directory protocol") { + + MessageBuffer requestToDir, network="From", virtual_network="2", ordered="false"; + MessageBuffer responseToDir, network="From", virtual_network="3", ordered="false"; + MessageBuffer responseFromDir, network="To", virtual_network="3", ordered="false"; + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_I") { + // Base states + I, desc="Owner"; + } + + // Events + enumeration(Event, desc="Directory events") { + Fetch, desc="A memory fetch arrives"; + Data, desc="writeback data arrives"; + Memory_Data, desc="Fetched data from memory arrives"; + Memory_Ack, desc="Writeback Ack from memory arrives"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + DataBlock DataBlk, desc="data for the block"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + // to simulate detailed DRAM + external_type(MemoryControl, inport="yes", outport="yes") { + + } + + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + MemoryControl memBuffer, constructor_hack="i"; + + State getState(Address addr) { + return State:I; + } + + void setState(Address addr, State state) { + } + + bool isGETRequest(CoherenceRequestType type) { + return (type == CoherenceRequestType:GETS) || + (type == CoherenceRequestType:GET_INSTR) || + (type == CoherenceRequestType:GETX); + } + + + // ** OUT_PORTS ** + out_port(responseNetwork_out, ResponseMsg, responseFromDir); + out_port(memQueue_out, MemoryMsg, memBuffer); + + // ** IN_PORTS ** + + in_port(requestNetwork_in, RequestMsg, requestToDir) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (isGETRequest(in_msg.Type)) { + trigger(Event:Fetch, in_msg.Address); + } else { + DEBUG_EXPR(in_msg); + error("Invalid message"); + } + } + } + } + + in_port(responseNetwork_in, ResponseMsg, responseToDir) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceResponseType:MEMORY_DATA) { + trigger(Event:Data, in_msg.Address); + } else { + DEBUG_EXPR(in_msg.Type); + error("Invalid message"); + } + } + } + } + + // off-chip memory request/response is done + in_port(memQueue_in, MemoryMsg, memBuffer) { + if (memQueue_in.isReady()) { + peek(memQueue_in, MemoryMsg) { + if (in_msg.Type == MemoryRequestType:MEMORY_READ) { + trigger(Event:Memory_Data, in_msg.Address); + } else if (in_msg.Type == MemoryRequestType:MEMORY_WB) { + trigger(Event:Memory_Ack, in_msg.Address); + } else { + DEBUG_EXPR(in_msg.Type); + error("Invalid message"); + } + } + } + } + + + + // Actions + action(a_sendAck, "a", desc="Send ack to L2") { + peek(memQueue_in, MemoryMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.OriginalRequestorMachId); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + + action(d_sendData, "d", desc="Send data to requestor") { + peek(memQueue_in, MemoryMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.OriginalRequestorMachId); + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(j_popIncomingRequestQueue, "j", desc="Pop incoming request queue") { + requestNetwork_in.dequeue(); + } + + action(k_popIncomingResponseQueue, "k", desc="Pop incoming request queue") { + responseNetwork_in.dequeue(); + } + + action(l_popMemQueue, "q", desc="Pop off-chip request queue") { + memQueue_in.dequeue(); + } + + action(qf_queueMemoryFetchRequest, "qf", desc="Queue off-chip fetch request") { + peek(requestNetwork_in, RequestMsg) { + enqueue(memQueue_out, MemoryMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := MemoryRequestType:MEMORY_READ; + out_msg.Sender := machineID; + out_msg.OriginalRequestorMachId := in_msg.Requestor; + out_msg.MessageSize := in_msg.MessageSize; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + + DEBUG_EXPR(out_msg); + } + } + } + + action(qw_queueMemoryWBRequest, "qw", desc="Queue off-chip writeback request") { + peek(responseNetwork_in, ResponseMsg) { + enqueue(memQueue_out, MemoryMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := MemoryRequestType:MEMORY_WB; + out_msg.Sender := machineID; + out_msg.OriginalRequestorMachId := in_msg.Sender; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.MessageSize := in_msg.MessageSize; + //out_msg.Prefetch := in_msg.Prefetch; + + DEBUG_EXPR(out_msg); + } + } + } + + action(m_writeDataToMemory, "m", desc="Write dirty writeback to memory") { + peek(responseNetwork_in, ResponseMsg) { + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + // TRANSITIONS + + transition(I, Fetch) { + //d_sendData; + qf_queueMemoryFetchRequest; + j_popIncomingRequestQueue; + } + + transition(I, Data) { + m_writeDataToMemory; + //a_sendAck; + qw_queueMemoryWBRequest; + k_popIncomingResponseQueue; + } + + transition(I, Memory_Data) { + d_sendData; + l_popMemQueue; + } + + transition(I, Memory_Ack) { + a_sendAck; + l_popMemQueue; + } +} diff --git a/src/mem/protocol/MESI_CMP_filter_directory_m.slicc b/src/mem/protocol/MESI_CMP_filter_directory_m.slicc new file mode 100644 index 000000000..43c9d4019 --- /dev/null +++ b/src/mem/protocol/MESI_CMP_filter_directory_m.slicc @@ -0,0 +1,7 @@ +../protocols/LogTM.sm +../protocols/MESI_CMP_filter_directory-msg.sm +../protocols/MESI_CMP_filter_directory-L2cache.sm +../protocols/MESI_CMP_filter_directory-L1cache.sm +../protocols/MESI_CMP_filter_directory_m-mem.sm +../protocols/standard_CMP-protocol.sm + diff --git a/src/mem/protocol/MESI_SCMP_bankdirectory-L1cache.sm b/src/mem/protocol/MESI_SCMP_bankdirectory-L1cache.sm new file mode 100644 index 000000000..6e707a431 --- /dev/null +++ b/src/mem/protocol/MESI_SCMP_bankdirectory-L1cache.sm @@ -0,0 +1,894 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MSI_MOSI_CMP_directory-L1cache.sm 1.10 05/01/19 15:55:40-06:00 beckmann@s0-28.cs.wisc.edu $ + * + */ + + +machine(L1Cache, "MSI Directory L1 Cache CMP") { + + // NODE L1 CACHE + // From this node's L1 cache TO the network + // a local L1 -> this L2 bank, currently ordered with directory forwarded requests + MessageBuffer requestFromL1Cache, network="To", virtual_network="0", ordered="false"; + // a local L1 -> this L2 bank + MessageBuffer responseFromL1Cache, network="To", virtual_network="3", ordered="false"; + MessageBuffer unblockFromL1Cache, network="To", virtual_network="4", ordered="false"; + + + // To this node's L1 cache FROM the network + // a L2 bank -> this L1 + MessageBuffer requestToL1Cache, network="From", virtual_network="1", ordered="false"; + // a L2 bank -> this L1 + MessageBuffer responseToL1Cache, network="From", virtual_network="3", ordered="false"; + + // STATES + enumeration(State, desc="Cache states", default="L1Cache_State_I") { + // Base states + NP, desc="Not present in either cache"; + I, desc="a L1 cache entry Idle"; + S, desc="a L1 cache entry Shared"; + E, desc="a L1 cache entry Exclusive"; + M, desc="a L1 cache entry Modified", format="!b"; + + // Transient States + IS, desc="L1 idle, issued GETS, have not seen response yet"; + IM, desc="L1 idle, issued GETX, have not seen response yet"; + SM, desc="L1 idle, issued GETX, have not seen response yet"; + IS_I, desc="L1 idle, issued GETS, saw Inv before data because directory doesn't block on GETS hit"; + + M_I, desc="L1 replacing, waiting for ACK"; + E_I, desc="L1 replacing, waiting for ACK"; + + } + + // EVENTS + enumeration(Event, desc="Cache events") { + // L1 events + Load, desc="Load request from the home processor"; + Ifetch, desc="I-fetch request from the home processor"; + Store, desc="Store request from the home processor"; + + Inv, desc="Invalidate request from L2 bank"; + + // internal generated request + L1_Replacement, desc="L1 Replacement", format="!r"; + + // other requests + Fwd_GETX, desc="GETX from other processor"; + Fwd_GETS, desc="GETS from other processor"; + Fwd_GET_INSTR, desc="GET_INSTR from other processor"; + + Data, desc="Data for processor"; + Data_Exclusive, desc="Data for processor"; + DataS_fromL1, desc="data for GETS request, need to unblock directory"; + Data_all_Acks, desc="Data for processor, all acks"; + + Ack, desc="Ack for processor"; + Ack_all, desc="Last ack for processor"; + + WB_Ack, desc="Ack for replacement"; + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry" ) { + State CacheState, desc="cache state"; + DataBlock DataBlk, desc="data for the block"; + bool Dirty, default="false", desc="data is dirty"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="Buffer for the data block"; + bool Dirty, default="false", desc="data is dirty"; + bool isPrefetch, desc="Set if this was caused by a prefetch"; + int pendingAcks, default="0", desc="number of pending acks"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + TBETable L1_TBEs, template_hack=""; + + CacheMemory L1IcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1I"', abstract_chip_ptr="true"; + CacheMemory L1DcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1D"', abstract_chip_ptr="true"; + + MessageBuffer mandatoryQueue, ordered="false", rank="100", abstract_chip_ptr="true"; + + Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i"; + + int cache_state_to_int(State state); + + // inclusive cache returns L1 entries only + Entry getL1CacheEntry(Address addr), return_by_ref="yes" { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory[addr]; + } else { + return L1IcacheMemory[addr]; + } + } + + void changeL1Permission(Address addr, AccessPermission permission) { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory.changePermission(addr, permission); + } else if(L1IcacheMemory.isTagPresent(addr)) { + return L1IcacheMemory.changePermission(addr, permission); + } else { + error("cannot change permission, L1 block not present"); + } + } + + bool isL1CacheTagPresent(Address addr) { + return (L1DcacheMemory.isTagPresent(addr) || L1IcacheMemory.isTagPresent(addr)); + } + + State getState(Address addr) { + if((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == true){ + DEBUG_EXPR(id); + DEBUG_EXPR(addr); + } + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + + if(L1_TBEs.isPresent(addr)) { + return L1_TBEs[addr].TBEState; + } else if (isL1CacheTagPresent(addr)) { + return getL1CacheEntry(addr).CacheState; + } + return State:NP; + } + + + void setState(Address addr, State state) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + + // MUST CHANGE + if(L1_TBEs.isPresent(addr)) { + L1_TBEs[addr].TBEState := state; + } + + if (isL1CacheTagPresent(addr)) { + getL1CacheEntry(addr).CacheState := state; + + // Set permission + if (state == State:I) { + changeL1Permission(addr, AccessPermission:Invalid); + } else if (state == State:S || state == State:E) { + changeL1Permission(addr, AccessPermission:Read_Only); + } else if (state == State:M) { + changeL1Permission(addr, AccessPermission:Read_Write); + } else { + changeL1Permission(addr, AccessPermission:Busy); + } + } + } + + Event mandatory_request_type_to_event(CacheRequestType type) { + if (type == CacheRequestType:LD) { + return Event:Load; + } else if (type == CacheRequestType:IFETCH) { + return Event:Ifetch; + } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { + return Event:Store; + } else { + error("Invalid CacheRequestType"); + } + } + + GenericMachineType getNondirectHitMachType(Address addr, MachineID sender) { + if (machineIDToMachineType(sender) == MachineType:L1Cache) { + return GenericMachineType:L1Cache_wCC; // NOTE direct L1 hits should not call this + } else if (machineIDToMachineType(sender) == MachineType:L2Cache) { + return GenericMachineType:L2Cache; + } else { + return ConvertMachToGenericMach(machineIDToMachineType(sender)); + } + } + + + + out_port(requestIntraChipL1Network_out, RequestMsg, requestFromL1Cache); + out_port(responseIntraChipL1Network_out, ResponseMsg, responseFromL1Cache); + out_port(unblockNetwork_out, ResponseMsg, unblockFromL1Cache); + + // Response IntraChip L1 Network - response msg to this L1 cache + in_port(responseIntraChipL1Network_in, ResponseMsg, responseToL1Cache) { + if (responseIntraChipL1Network_in.isReady()) { + peek(responseIntraChipL1Network_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if(in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE) { + trigger(Event:Data_Exclusive, in_msg.Address); + } else if(in_msg.Type == CoherenceResponseType:DATA) { + if ( (getState(in_msg.Address) == State:IS || getState(in_msg.Address) == State:IS_I) && + machineIDToMachineType(in_msg.Sender) == MachineType:L1Cache ) { + + trigger(Event:DataS_fromL1, in_msg.Address); + + } else if ( (L1_TBEs[in_msg.Address].pendingAcks - in_msg.AckCount) == 0 ) { + trigger(Event:Data_all_Acks, in_msg.Address); + } else { + trigger(Event:Data, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:ACK) { + if ( (L1_TBEs[in_msg.Address].pendingAcks - in_msg.AckCount) == 0 ) { + trigger(Event:Ack_all, in_msg.Address); + } else { + trigger(Event:Ack, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:WB_ACK) { + trigger(Event:WB_Ack, in_msg.Address); + } else { + error("Invalid L1 response type"); + } + } + } + } + + // Request InterChip network - request from this L1 cache to the shared L2 + in_port(requestIntraChipL1Network_in, RequestMsg, requestToL1Cache) { + if(requestIntraChipL1Network_in.isReady()) { + peek(requestIntraChipL1Network_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceRequestType:INV) { + trigger(Event:Inv, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX || in_msg.Type == CoherenceRequestType:UPGRADE) { + // upgrade transforms to GETX due to race + trigger(Event:Fwd_GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:Fwd_GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GET_INSTR) { + trigger(Event:Fwd_GET_INSTR, in_msg.Address); + } else { + error("Invalid forwarded request type"); + } + } + } + } + + // Mandatory Queue betweens Node's CPU and it's L1 caches + in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { + if (mandatoryQueue_in.isReady()) { + peek(mandatoryQueue_in, CacheMsg) { + + // Check for data access to blocks in I-cache and ifetchs to blocks in D-cache + + if (in_msg.Type == CacheRequestType:IFETCH) { + // ** INSTRUCTION ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, put the request on the queue to the shared L2 + trigger(Event:L1_Replacement, in_msg.Address); + } + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 asks the L2 for it. + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1IcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 so let's see if the L2 has it + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // No room in the L1, so we need to make room in the L1 + trigger(Event:L1_Replacement, L1IcacheMemory.cacheProbe(in_msg.Address)); + } + } + } else { + // *** DATA ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, put the request on the queue to the shared L2 + trigger(Event:L1_Replacement, in_msg.Address); + } + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 ask the L2 for it + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1DcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 let's see if the L2 has it + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // No room in the L1, so we need to make room in the L1 + trigger(Event:L1_Replacement, L1DcacheMemory.cacheProbe(in_msg.Address)); + } + } + } + } + } + } + + // ACTIONS + action(a_issueGETS, "a", desc="Issue GETS") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + } + } + } + + action(ai_issueGETINSTR, "ai", desc="Issue GETINSTR") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GET_INSTR; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + } + } + } + + + action(b_issueGETX, "b", desc="Issue GETX") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.Requestor := machineID; + DEBUG_EXPR(machineID); + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + } + } + } + + action(c_issueUPGRADE, "c", desc="Issue GETX") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:UPGRADE; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + } + } + } + + action(d_sendDataToRequestor, "d", desc="send data to requestor") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(d2_sendDataToL2, "d2", desc="send data to the L2 cache because of M downgrade") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(dt_sendDataToRequestor_fromTBE, "dt", desc="send data to requestor") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := L1_TBEs[address].DataBlk; + out_msg.Dirty := L1_TBEs[address].Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(d2t_sendDataToL2_fromTBE, "d2t", desc="send data to the L2 cache") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := L1_TBEs[address].DataBlk; + out_msg.Dirty := L1_TBEs[address].Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(e_sendAckToRequestor, "e", desc="send invalidate ack to requestor (could be L2 or L1)") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + + action(f_sendDataToL2, "f", desc="send data to the L2 cache") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } + } + + action(ft_sendDataToL2_fromTBE, "ft", desc="send data to the L2 cache") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.DataBlk := L1_TBEs[address].DataBlk; + out_msg.Dirty := L1_TBEs[address].Dirty; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } + } + + action(fi_sendInvAck, "fi", desc="send data to the L2 cache") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + out_msg.AckCount := 1; + } + } + } + + + action(g_issuePUTX, "g", desc="send data to the L2 cache") { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTX; + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.Dirty := getL1CacheEntry(address).Dirty; + out_msg.Requestor:= machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + if (getL1CacheEntry(address).Dirty) { + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } else { + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(j_sendUnblock, "j", desc="send unblock to the L2 cache") { + enqueue(unblockNetwork_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:UNBLOCK; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + + action(jj_sendExclusiveUnblock, "\j", desc="send unblock to the L2 cache") { + enqueue(unblockNetwork_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:EXCLUSIVE_UNBLOCK; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + + + + action(h_load_hit, "h", desc="If not prefetch, notify sequencer the load completed.") { + DEBUG_EXPR(getL1CacheEntry(address).DataBlk); + sequencer.readCallback(address, getL1CacheEntry(address).DataBlk); + } + + action(x_external_load_hit, "x", desc="Notify sequencer the load completed.") { + peek(responseIntraChipL1Network_in, ResponseMsg) { + sequencer.readCallback(address, getL1CacheEntry(address).DataBlk, getNondirectHitMachType(in_msg.Address, in_msg.Sender), PrefetchBit:No); + } + } + + + action(hh_store_hit, "\h", desc="If not prefetch, notify sequencer that store completed.") { + sequencer.writeCallback(address, getL1CacheEntry(address).DataBlk); + getL1CacheEntry(address).Dirty := true; + } + + action(xx_external_store_hit, "\x", desc="Notify sequencer that store completed.") { + peek(responseIntraChipL1Network_in, ResponseMsg) { + sequencer.writeCallback(address, getL1CacheEntry(address).DataBlk, getNondirectHitMachType(in_msg.Address, in_msg.Sender), PrefetchBit:No); + } + getL1CacheEntry(address).Dirty := true; + } + + + action(i_allocateTBE, "i", desc="Allocate TBE (isPrefetch=0, number of invalidates=0)") { + check_allocate(L1_TBEs); + L1_TBEs.allocate(address); + L1_TBEs[address].isPrefetch := false; + L1_TBEs[address].Dirty := getL1CacheEntry(address).Dirty; + L1_TBEs[address].DataBlk := getL1CacheEntry(address).DataBlk; + } + + action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") { + mandatoryQueue_in.dequeue(); + } + + action(l_popRequestQueue, "l", desc="Pop incoming request queue and profile the delay within this virtual network") { + profileMsgDelay(2, requestIntraChipL1Network_in.dequeue_getDelayCycles()); + } + + action(o_popIncomingResponseQueue, "o", desc="Pop Incoming Response queue and profile the delay within this virtual network") { + profileMsgDelay(3, responseIntraChipL1Network_in.dequeue_getDelayCycles()); + } + + action(s_deallocateTBE, "s", desc="Deallocate TBE") { + L1_TBEs.deallocate(address); + } + + action(u_writeDataToL1Cache, "u", desc="Write data to cache") { + peek(responseIntraChipL1Network_in, ResponseMsg) { + getL1CacheEntry(address).DataBlk := in_msg.DataBlk; + getL1CacheEntry(address).Dirty := in_msg.Dirty; + } + } + + action(q_updateAckCount, "q", desc="Update ack count") { + peek(responseIntraChipL1Network_in, ResponseMsg) { + L1_TBEs[address].pendingAcks := L1_TBEs[address].pendingAcks - in_msg.AckCount; + APPEND_TRANSITION_COMMENT(in_msg.AckCount); + APPEND_TRANSITION_COMMENT(" p: "); + APPEND_TRANSITION_COMMENT(L1_TBEs[address].pendingAcks); + } + } + + action(z_stall, "z", desc="Stall") { + } + + action(ff_deallocateL1CacheBlock, "\f", desc="Deallocate L1 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + if (L1DcacheMemory.isTagPresent(address)) { + L1DcacheMemory.deallocate(address); + } else { + L1IcacheMemory.deallocate(address); + } + } + + action(oo_allocateL1DCacheBlock, "\o", desc="Set L1 D-cache tag equal to tag of block B.") { + if (L1DcacheMemory.isTagPresent(address) == false) { + L1DcacheMemory.allocate(address); + } + } + + action(pp_allocateL1ICacheBlock, "\p", desc="Set L1 I-cache tag equal to tag of block B.") { + if (L1IcacheMemory.isTagPresent(address) == false) { + L1IcacheMemory.allocate(address); + } + } + + action(zz_recycleRequestQueue, "zz", desc="recycle L1 request queue") { + requestIntraChipL1Network_in.recycle(); + } + + action(z_recycleMandatoryQueue, "\z", desc="recycle L1 request queue") { + mandatoryQueue_in.recycle(); + } + + + //***************************************************** + // TRANSITIONS + //***************************************************** + + // Transitions for Load/Store/Replacement/WriteBack from transient states + transition({IS, IM, IS_I, M_I, E_I, SM}, {Load, Ifetch, Store, L1_Replacement}) { + z_recycleMandatoryQueue; + } + + // Transitions from Idle + transition({NP,I}, L1_Replacement) { + ff_deallocateL1CacheBlock; + } + + transition({NP,I}, Load, IS) { + oo_allocateL1DCacheBlock; + i_allocateTBE; + a_issueGETS; + k_popMandatoryQueue; + } + + transition({NP,I}, Ifetch, IS) { + pp_allocateL1ICacheBlock; + i_allocateTBE; + ai_issueGETINSTR; + k_popMandatoryQueue; + } + + transition({NP,I}, Store, IM) { + oo_allocateL1DCacheBlock; + i_allocateTBE; + b_issueGETX; + k_popMandatoryQueue; + } + + transition({NP, I}, Inv) { + fi_sendInvAck; + l_popRequestQueue; + } + + // Transitions from Shared + transition(S, {Load,Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(S, Store, SM) { + i_allocateTBE; + c_issueUPGRADE; + k_popMandatoryQueue; + } + + transition(S, L1_Replacement, I) { + ff_deallocateL1CacheBlock; + } + + transition(S, Inv, I) { + fi_sendInvAck; + l_popRequestQueue; + } + + // Transitions from Exclusive + + transition(E, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(E, Store, M) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(E, L1_Replacement, M_I) { + // silent E replacement?? + i_allocateTBE; + g_issuePUTX; // send data, but hold in case forwarded request + ff_deallocateL1CacheBlock; + } + + transition(E, Inv, I) { + // don't send data + fi_sendInvAck; + l_popRequestQueue; + } + + transition(E, Fwd_GETX, I) { + d_sendDataToRequestor; + l_popRequestQueue; + } + + transition(E, {Fwd_GETS, Fwd_GET_INSTR}, S) { + d_sendDataToRequestor; + d2_sendDataToL2; + l_popRequestQueue; + } + + // Transitions from Modified + transition(M, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(M, Store) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(M, L1_Replacement, M_I) { + i_allocateTBE; + g_issuePUTX; // send data, but hold in case forwarded request + ff_deallocateL1CacheBlock; + } + + transition(M_I, WB_Ack, I) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(M, Inv, I) { + f_sendDataToL2; + l_popRequestQueue; + } + + transition(M_I, Inv, I) { + ft_sendDataToL2_fromTBE; + s_deallocateTBE; + l_popRequestQueue; + } + + transition(M, Fwd_GETX, I) { + d_sendDataToRequestor; + l_popRequestQueue; + } + + transition(M, {Fwd_GETS, Fwd_GET_INSTR}, S) { + d_sendDataToRequestor; + d2_sendDataToL2; + l_popRequestQueue; + } + + transition(M_I, Fwd_GETX, I) { + dt_sendDataToRequestor_fromTBE; + s_deallocateTBE; + l_popRequestQueue; + } + + transition(M_I, {Fwd_GETS, Fwd_GET_INSTR}, I) { + dt_sendDataToRequestor_fromTBE; + d2t_sendDataToL2_fromTBE; + s_deallocateTBE; + l_popRequestQueue; + } + + // Transitions from IS + transition({IS, IS_I}, Inv, IS_I) { + fi_sendInvAck; + l_popRequestQueue; + } + + transition(IS, Data_all_Acks, S) { + u_writeDataToL1Cache; + x_external_load_hit; + s_deallocateTBE; + j_sendUnblock; + o_popIncomingResponseQueue; + } + + transition(IS_I, Data_all_Acks, I) { + u_writeDataToL1Cache; + x_external_load_hit; + s_deallocateTBE; + j_sendUnblock; + o_popIncomingResponseQueue; + } + + + transition(IS, DataS_fromL1, S) { + u_writeDataToL1Cache; + j_sendUnblock; + x_external_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IS_I, DataS_fromL1, I) { + u_writeDataToL1Cache; + j_sendUnblock; + x_external_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // directory is blocked when sending exclusive data + transition(IS_I, Data_Exclusive, E) { + u_writeDataToL1Cache; + x_external_load_hit; + jj_sendExclusiveUnblock; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IS, Data_Exclusive, E) { + u_writeDataToL1Cache; + x_external_load_hit; + jj_sendExclusiveUnblock; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // Transitions from IM + transition({IM, SM}, Inv, IM) { + fi_sendInvAck; + l_popRequestQueue; + } + + transition(IM, Data, SM) { + u_writeDataToL1Cache; + q_updateAckCount; + o_popIncomingResponseQueue; + } + + transition(IM, Data_all_Acks, M) { + u_writeDataToL1Cache; + xx_external_store_hit; + jj_sendExclusiveUnblock; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // transitions from SM + transition({SM, IM}, Ack) { + q_updateAckCount; + o_popIncomingResponseQueue; + } + + transition(SM, Ack_all, M) { + jj_sendExclusiveUnblock; + xx_external_store_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } +} + + + diff --git a/src/mem/protocol/MESI_SCMP_bankdirectory-L2cache.sm b/src/mem/protocol/MESI_SCMP_bankdirectory-L2cache.sm new file mode 100644 index 000000000..0bd9c2b14 --- /dev/null +++ b/src/mem/protocol/MESI_SCMP_bankdirectory-L2cache.sm @@ -0,0 +1,1052 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MSI_MOSI_CMP_directory-L2cache.sm 1.12 05/01/19 15:55:40-06:00 beckmann@s0-28.cs.wisc.edu $ + * + */ + +machine(L2Cache, "MOSI Directory L2 Cache CMP") { + + // L2 BANK QUEUES + // From local bank of L2 cache TO the network + MessageBuffer DirRequestFromL2Cache, network="To", virtual_network="2", ordered="false"; // this L2 bank -> Memory + MessageBuffer L1RequestFromL2Cache, network="To", virtual_network="1", ordered="false"; // this L2 bank -> a local L1 + MessageBuffer responseFromL2Cache, network="To", virtual_network="3", ordered="false"; // this L2 bank -> a local L1 || Memory + + // FROM the network to this local bank of L2 cache + MessageBuffer L1RequestToL2Cache, network="From", virtual_network="0", ordered="false"; // a local L1 -> this L2 bank + MessageBuffer responseToL2Cache, network="From", virtual_network="3", ordered="false"; // a local L1 || Memory -> this L2 bank + MessageBuffer unblockToL2Cache, network="From", virtual_network="4", ordered="false"; // a local L1 || Memory -> this L2 bank + + // STATES + enumeration(State, desc="L2 Cache states", default="L2Cache_State_NP") { + // Base states + NP, desc="Not present in either cache"; + SS, desc="L2 cache entry Shared, also present in one or more L1s"; + M, desc="L2 cache entry Modified, not present in any L1s", format="!b"; + MT, desc="L2 cache entry Modified in a local L1, assume L2 copy stale", format="!b"; + + // L2 replacement + M_I, desc="L2 cache replacing, have all acks, sent dirty data to memory, waiting for ACK from memory"; + MT_I, desc="L2 cache replacing, getting data from exclusive"; + MCT_I, desc="L2 cache replacing, clean in L2, getting data or ack from exclusive"; + I_I, desc="L2 replacing clean data, need to inv sharers and then drop data"; + S_I, desc="L2 replacing dirty data, collecting acks from L1s"; + + // Transient States for fetching data from memory + ISS, desc="L2 idle, got single L1_GETS, issued memory fetch, have not seen response yet"; + IS, desc="L2 idle, got L1_GET_INSTR or multiple L1_GETS, issued memory fetch, have not seen response yet"; + IM, desc="L2 idle, got L1_GETX, issued memory fetch, have not seen response(s) yet"; + + // Blocking states + SS_MB, desc="Blocked for L1_GETX from SS"; + MT_MB, desc="Blocked for L1_GETX from MT"; + M_MB, desc="Blocked for L1_GETX from M"; + + MT_IIB, desc="Blocked for L1_GETS from MT, waiting for unblock and data"; + MT_IB, desc="Blocked for L1_GETS from MT, got unblock, waiting for data"; + MT_SB, desc="Blocked for L1_GETS from MT, got data, waiting for unblock"; + + } + + // EVENTS + enumeration(Event, desc="L2 Cache events") { + // L2 events + + // events initiated by the local L1s + L1_GET_INSTR, desc="a L1I GET INSTR request for a block maped to us"; + L1_GETS, desc="a L1D GETS request for a block maped to us"; + L1_GETX, desc="a L1D GETX request for a block maped to us"; + L1_UPGRADE, desc="a L1D GETX request for a block maped to us"; + + L1_PUTX, desc="L1 replacing data"; + L1_PUTX_old, desc="L1 replacing data, but no longer sharer"; + + Fwd_L1_GETX, desc="L1 did not have data, so we supply"; + Fwd_L1_GETS, desc="L1 did not have data, so we supply"; + Fwd_L1_GET_INSTR, desc="L1 did not have data, so we supply"; + + // events initiated by this L2 + L2_Replacement, desc="L2 Replacement", format="!r"; + L2_Replacement_clean, desc="L2 Replacement, but data is clean", format="!r"; + + // events from memory controller + Mem_Data, desc="data from memory", format="!r"; + Mem_Ack, desc="ack from memory", format="!r"; + + // M->S data writeback + WB_Data, desc="data from L1"; + WB_Data_clean, desc="clean data from L1"; + Ack, desc="writeback ack"; + Ack_all, desc="writeback ack"; + + Unblock, desc="Unblock from L1 requestor"; + Unblock_Cancel, desc="Unblock from L1 requestor (FOR XACT MEMORY)"; + Exclusive_Unblock, desc="Unblock from L1 requestor"; + + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + NetDest Sharers, desc="tracks the L1 shares on-chip"; + MachineID Exclusive, desc="Exclusive holder of block"; + DataBlock DataBlk, desc="data for the block"; + bool Dirty, default="false", desc="data is dirty"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="Buffer for the data block"; + bool Dirty, default="false", desc="Data is Dirty"; + + NetDest L1_GetS_IDs, desc="Set of the internal processors that want the block in shared state"; + MachineID L1_GetX_ID, desc="ID of the L1 cache to forward the block to once we get a response"; + bool isPrefetch, desc="Set if this was caused by a prefetch"; + + int pendingAcks, desc="number of pending acks for invalidates during writeback"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + void setMRU(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + TBETable L2_TBEs, template_hack=""; + + CacheMemory L2cacheMemory, template_hack="", constructor_hack='L2_CACHE_NUM_SETS_BITS,L2_CACHE_ASSOC,MachineType_L2Cache,int_to_string(i)'; + + // inclusive cache, returns L2 entries only + Entry getL2CacheEntry(Address addr), return_by_ref="yes" { + return L2cacheMemory[addr]; + } + + void changeL2Permission(Address addr, AccessPermission permission) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory.changePermission(addr, permission); + } + } + + string getCoherenceRequestTypeStr(CoherenceRequestType type) { + return CoherenceRequestType_to_string(type); + } + + bool isL2CacheTagPresent(Address addr) { + return (L2cacheMemory.isTagPresent(addr)); + } + + bool isOneSharerLeft(Address addr, MachineID requestor) { + assert(L2cacheMemory[addr].Sharers.isElement(requestor)); + return (L2cacheMemory[addr].Sharers.count() == 1); + } + + bool isSharer(Address addr, MachineID requestor) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory[addr].Sharers.isElement(requestor); + } else { + return false; + } + } + + void addSharer(Address addr, MachineID requestor) { + DEBUG_EXPR(machineID); + DEBUG_EXPR(requestor); + DEBUG_EXPR(addr); + assert(map_L1CacheMachId_to_L2Cache(addr, requestor) == machineID); + L2cacheMemory[addr].Sharers.add(requestor); + } + + State getState(Address addr) { + if(L2_TBEs.isPresent(addr)) { + return L2_TBEs[addr].TBEState; + } else if (isL2CacheTagPresent(addr)) { + return getL2CacheEntry(addr).CacheState; + } + return State:NP; + } + + string getStateStr(Address addr) { + return L2Cache_State_to_string(getState(addr)); + } + + // when is this called + void setState(Address addr, State state) { + + // MUST CHANGE + if (L2_TBEs.isPresent(addr)) { + L2_TBEs[addr].TBEState := state; + } + + if (isL2CacheTagPresent(addr)) { + getL2CacheEntry(addr).CacheState := state; + + // Set permission + if (state == State:SS ) { + changeL2Permission(addr, AccessPermission:Read_Only); + } else if (state == State:M) { + changeL2Permission(addr, AccessPermission:Read_Write); + } else if (state == State:MT) { + changeL2Permission(addr, AccessPermission:Stale); + } else { + changeL2Permission(addr, AccessPermission:Busy); + } + } + } + + Event L1Cache_request_type_to_event(CoherenceRequestType type, Address addr, MachineID requestor) { + if(type == CoherenceRequestType:GETS) { + return Event:L1_GETS; + } else if(type == CoherenceRequestType:GET_INSTR) { + return Event:L1_GET_INSTR; + } else if (type == CoherenceRequestType:GETX) { + return Event:L1_GETX; + } else if (type == CoherenceRequestType:UPGRADE) { + if ( isL2CacheTagPresent(addr) && getL2CacheEntry(addr).Sharers.isElement(requestor) ) { + return Event:L1_UPGRADE; + } else { + return Event:L1_GETX; + } + } else if (type == CoherenceRequestType:PUTX) { + if (isSharer(addr, requestor)) { + return Event:L1_PUTX; + } else { + return Event:L1_PUTX_old; + } + } else { + DEBUG_EXPR(addr); + DEBUG_EXPR(type); + error("Invalid L1 forwarded request type"); + } + } + + // ** OUT_PORTS ** + + out_port(L1RequestIntraChipL2Network_out, RequestMsg, L1RequestFromL2Cache); + out_port(DirRequestIntraChipL2Network_out, RequestMsg, DirRequestFromL2Cache); + out_port(responseIntraChipL2Network_out, ResponseMsg, responseFromL2Cache); + + + // Response IntraChip L2 Network - response msg to this particular L2 bank + in_port(responseIntraChipL2Network_in, ResponseMsg, responseToL2Cache) { + if (responseIntraChipL2Network_in.isReady()) { + peek(responseIntraChipL2Network_in, ResponseMsg) { + // test wether it's from a local L1 or an off chip source + assert(in_msg.Destination.isElement(machineID)); + if(machineIDToMachineType(in_msg.Sender) == MachineType:L1Cache) { + if(in_msg.Type == CoherenceResponseType:DATA) { + if (in_msg.Dirty) { + trigger(Event:WB_Data, in_msg.Address); + } else { + trigger(Event:WB_Data_clean, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:ACK) { + if ((L2_TBEs[in_msg.Address].pendingAcks - in_msg.AckCount) == 0) { + trigger(Event:Ack_all, in_msg.Address); + } else { + trigger(Event:Ack, in_msg.Address); + } + } else { + error("unknown message type"); + } + + } else { // external message + if(in_msg.Type == CoherenceResponseType:MEMORY_DATA) { + trigger(Event:Mem_Data, in_msg.Address); // L2 now has data and all off-chip acks + } else if(in_msg.Type == CoherenceResponseType:MEMORY_ACK) { + trigger(Event:Mem_Ack, in_msg.Address); // L2 now has data and all off-chip acks + } else { + error("unknown message type"); + } + } + } + } // if not ready, do nothing + } + + // L1 Request + in_port(L1RequestIntraChipL2Network_in, RequestMsg, L1RequestToL2Cache) { + if(L1RequestIntraChipL2Network_in.isReady()) { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(id); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.Requestor); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(in_msg.Destination); + assert(machineIDToMachineType(in_msg.Requestor) == MachineType:L1Cache); + assert(in_msg.Destination.isElement(machineID)); + if (L2cacheMemory.isTagPresent(in_msg.Address)) { + // The L2 contains the block, so proceeded with handling the request + trigger(L1Cache_request_type_to_event(in_msg.Type, in_msg.Address, in_msg.Requestor), in_msg.Address); + } else { + if (L2cacheMemory.cacheAvail(in_msg.Address)) { + // L2 does't have the line, but we have space for it in the L2 + trigger(L1Cache_request_type_to_event(in_msg.Type, in_msg.Address, in_msg.Requestor), in_msg.Address); + } else { + // No room in the L2, so we need to make room before handling the request + if (L2cacheMemory[ L2cacheMemory.cacheProbe(in_msg.Address) ].Dirty ) { + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } else { + trigger(Event:L2_Replacement_clean, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } + } + } + } + } + + in_port(L1unblockNetwork_in, ResponseMsg, unblockToL2Cache) { + if(L1unblockNetwork_in.isReady()) { + peek(L1unblockNetwork_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceResponseType:EXCLUSIVE_UNBLOCK) { + trigger(Event:Exclusive_Unblock, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:UNBLOCK) { + trigger(Event:Unblock, in_msg.Address); + } else { + error("unknown unblock message"); + } + } + } + } + + // ACTIONS + + action(a_issueFetchToMemory, "a", desc="fetch data from memory") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(DirRequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + + action(b_forwardRequestToExclusive, "b", desc="Forward request to the exclusive L1") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(L2cacheMemory[address].Exclusive); + out_msg.MessageSize := MessageSizeType:Request_Control; + } + } + } + + action(c_exclusiveReplacement, "c", desc="Send data to memory") { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(ct_exclusiveReplacementFromTBE, "ct", desc="Send data to memory") { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.DataBlk := L2_TBEs[address].DataBlk; + out_msg.Dirty := L2_TBEs[address].Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + + action(d_sendDataToRequestor, "d", desc="Send data from cache to reqeustor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count(); + if (getL2CacheEntry(address).Sharers.isElement(in_msg.Requestor)) { + out_msg.AckCount := out_msg.AckCount + 1; + } + } + } + } + + action(dd_sendExclusiveDataToRequestor, "dd", desc="Send data from cache to reqeustor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count(); + if (getL2CacheEntry(address).Sharers.isElement(in_msg.Requestor)) { + out_msg.AckCount := out_msg.AckCount + 1; + } + } + } + } + + action(ds_sendSharedDataToRequestor, "ds", desc="Send data from cache to reqeustor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + out_msg.AckCount := 0; + } + } + } + + action(e_sendDataToGetSRequestors, "e", desc="Send data from cache to all GetS IDs") { + assert(L2_TBEs[address].L1_GetS_IDs.count() > 0); + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + //out_msg.Sender := machineID; + out_msg.Sender := map_Address_to_Directory(address); + out_msg.Destination := L2_TBEs[address].L1_GetS_IDs; // internal nodes + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(ex_sendExclusiveDataToGetSRequestors, "ex", desc="Send data from cache to all GetS IDs") { + assert(L2_TBEs[address].L1_GetS_IDs.count() == 1); + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + // out_msg.Sender := machineID; + out_msg.Sender := map_Address_to_Directory(address); + out_msg.Destination := L2_TBEs[address].L1_GetS_IDs; // internal nodes + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + + action(ee_sendDataToGetXRequestor, "ee", desc="Send data from cache to GetX ID") { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + // out_msg.Sender := machineID; + out_msg.Sender := map_Address_to_Directory(address); + out_msg.Destination.add(L2_TBEs[address].L1_GetX_ID); + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + DEBUG_EXPR(out_msg.Address); + DEBUG_EXPR(out_msg.Destination); + DEBUG_EXPR(out_msg.DataBlk); + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + + action(f_sendInvToSharers, "f", desc="invalidate sharers for L2 replacement") { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := machineID; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.MessageSize := MessageSizeType:Request_Control; + } + } + + action(fw_sendFwdInvToSharers, "fw", desc="invalidate sharers for request") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.MessageSize := MessageSizeType:Request_Control; + } + } + } + + + action(fwm_sendFwdInvToSharersMinusRequestor, "fwm", desc="invalidate sharers for request, requestor is sharer") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Request_Control; + } + } + } + + // OTHER ACTIONS + action(i_allocateTBE, "i", desc="Allocate TBE for internal/external request(isPrefetch=0, number of invalidates=0)") { + check_allocate(L2_TBEs); + L2_TBEs.allocate(address); + L2_TBEs[address].L1_GetS_IDs.clear(); + L2_TBEs[address].DataBlk := getL2CacheEntry(address).DataBlk; + L2_TBEs[address].Dirty := getL2CacheEntry(address).Dirty; + L2_TBEs[address].pendingAcks := getL2CacheEntry(address).Sharers.count(); + } + + action(s_deallocateTBE, "s", desc="Deallocate external TBE") { + L2_TBEs.deallocate(address); + } + + action(jj_popL1RequestQueue, "\j", desc="Pop incoming L1 request queue") { + profileMsgDelay(0, L1RequestIntraChipL2Network_in.dequeue_getDelayCycles()); + } + + action(k_popUnblockQueue, "k", desc="Pop incoming unblock queue") { + profileMsgDelay(0, L1unblockNetwork_in.dequeue_getDelayCycles()); + } + + + action(o_popIncomingResponseQueue, "o", desc="Pop Incoming Response queue") { + profileMsgDelay(3, responseIntraChipL2Network_in.dequeue_getDelayCycles()); + } + + + action(m_writeDataToCache, "m", desc="Write data from response queue to cache") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + getL2CacheEntry(address).DataBlk := in_msg.DataBlk; + getL2CacheEntry(address).Dirty := in_msg.Dirty; + } + } + + action(mr_writeDataToCacheFromRequest, "mr", desc="Write data from response queue to cache") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + getL2CacheEntry(address).DataBlk := in_msg.DataBlk; + getL2CacheEntry(address).Dirty := in_msg.Dirty; + } + } + + action(q_updateAck, "q", desc="update pending ack count") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + L2_TBEs[address].pendingAcks := L2_TBEs[address].pendingAcks - in_msg.AckCount; + APPEND_TRANSITION_COMMENT(in_msg.AckCount); + APPEND_TRANSITION_COMMENT(" p: "); + APPEND_TRANSITION_COMMENT(L2_TBEs[address].pendingAcks); + } + } + + action(qq_writeDataToTBE, "\qq", desc="Write data from response queue to TBE") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + L2_TBEs[address].DataBlk := in_msg.DataBlk; + L2_TBEs[address].Dirty := in_msg.Dirty; + } + } + + + action(z_stall, "z", desc="Stall") { + } + + + action(ss_recordGetSL1ID, "\s", desc="Record L1 GetS for load response") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2_TBEs[address].L1_GetS_IDs.add(in_msg.Requestor); + } + } + + action(xx_recordGetXL1ID, "\x", desc="Record L1 GetX for store response") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2_TBEs[address].L1_GetX_ID := in_msg.Requestor; + } + } + + action(set_setMRU, "\set", desc="set the MRU entry") { + L2cacheMemory.setMRU(address); + } + + action(qq_allocateL2CacheBlock, "\q", desc="Set L2 cache tag equal to tag of block B.") { + if (L2cacheMemory.isTagPresent(address) == false) { + L2cacheMemory.allocate(address); + } + } + + action(rr_deallocateL2CacheBlock, "\r", desc="Deallocate L2 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + L2cacheMemory.deallocate(address); + } + + action(t_sendWBAck, "t", desc="Send writeback ACK") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:WB_ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + + action(ts_sendInvAckToUpgrader, "ts", desc="Send ACK to upgrader") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Response_Control; + // upgrader doesn't get ack from itself, hence the + 1 + out_msg.AckCount := 0 - getL2CacheEntry(address).Sharers.count() + 1; + } + } + } + + action(uu_profileMiss, "\u", desc="Profile the demand miss") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + //profile_L2Cache_miss(convertToGenericType(in_msg.Type), in_msg.AccessMode, MessageSizeTypeToInt(in_msg.MessageSize), in_msg.Prefetch, L1CacheMachIDToProcessorNum(in_msg.Requestor)); + } + } + + action(ww_profileMissNoDir, "\w", desc="Profile this transition at the L2 because Dir won't see the request") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + // profile_request(in_msg.L1CacheStateStr, getStateStr(address), "NA", getCoherenceRequestTypeStr(in_msg.Type)); + } + } + + + + action(nn_addSharer, "\n", desc="Add L1 sharer to list") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + addSharer(address, in_msg.Requestor); + APPEND_TRANSITION_COMMENT( getL2CacheEntry(address).Sharers ); + } + } + + action(nnu_addSharerFromUnblock, "\nu", desc="Add L1 sharer to list") { + peek(L1unblockNetwork_in, ResponseMsg) { + addSharer(address, in_msg.Sender); + } + } + + + action(kk_removeRequestSharer, "\k", desc="Remove L1 Request sharer from list") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2cacheMemory[address].Sharers.remove(in_msg.Requestor); + } + } + + action(ll_clearSharers, "\l", desc="Remove all L1 sharers from list") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2cacheMemory[address].Sharers.clear(); + } + } + + action(mm_markExclusive, "\m", desc="set the exclusive owner") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2cacheMemory[address].Sharers.clear(); + L2cacheMemory[address].Exclusive := in_msg.Requestor; + addSharer(address, in_msg.Requestor); + } + } + + action(mmu_markExclusiveFromUnblock, "\mu", desc="set the exclusive owner") { + peek(L1unblockNetwork_in, ResponseMsg) { + L2cacheMemory[address].Sharers.clear(); + L2cacheMemory[address].Exclusive := in_msg.Sender; + addSharer(address, in_msg.Sender); + } + } + + action(zz_recycleL1RequestQueue, "zz", desc="recycle L1 request queue") { + L1RequestIntraChipL2Network_in.recycle(); + } + + //***************************************************** + // TRANSITIONS + //***************************************************** + + + //=============================================== + // BASE STATE - I + + // Transitions from I (Idle) + transition({NP, IS, ISS, IM, SS, M, M_I, MT_I, MCT_I, I_I, S_I, SS_MB, M_MB, MT_IIB, MT_IB, MT_SB}, L1_PUTX) { + jj_popL1RequestQueue; + } + + transition({NP, SS, M, MT, M_I, MT_I, MCT_I, I_I, S_I, IS, ISS, IM, SS_MB, MT_MB, M_MB, MT_IIB, MT_IB, MT_SB}, L1_PUTX_old) { + jj_popL1RequestQueue; + } + + transition({IM, IS, ISS, SS_MB, M_MB, MT_MB, MT_IIB, MT_IB, MT_SB}, {L2_Replacement, L2_Replacement_clean}) { + zz_recycleL1RequestQueue; + } + + transition({SS_MB, M_MB, MT_MB, MT_IIB, MT_IB, MT_SB}, {L1_GETS, L1_GET_INSTR, L1_GETX, L1_UPGRADE}) { + zz_recycleL1RequestQueue; + } + + + transition(NP, L1_GETS, ISS) { + qq_allocateL2CacheBlock; + ll_clearSharers; + nn_addSharer; + i_allocateTBE; + ss_recordGetSL1ID; + a_issueFetchToMemory; + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition(NP, L1_GET_INSTR, IS) { + qq_allocateL2CacheBlock; + ll_clearSharers; + nn_addSharer; + i_allocateTBE; + ss_recordGetSL1ID; + a_issueFetchToMemory; + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition(NP, L1_GETX, IM) { + qq_allocateL2CacheBlock; + ll_clearSharers; + // nn_addSharer; + i_allocateTBE; + xx_recordGetXL1ID; + a_issueFetchToMemory; + uu_profileMiss; + jj_popL1RequestQueue; + } + + + // transitions from IS/IM + + transition(ISS, Mem_Data, MT_MB) { + m_writeDataToCache; + ex_sendExclusiveDataToGetSRequestors; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // transition(IS, Mem_Data, SS) { + transition(IS, Mem_Data, SS_MB) { + m_writeDataToCache; + e_sendDataToGetSRequestors; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IM, Mem_Data, MT_MB) { + m_writeDataToCache; + ee_sendDataToGetXRequestor; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + +/* + transition({IS, ISS}, {L1_GETS, L1_GET_INSTR}, IS) { + nn_addSharer; + ss_recordGetSL1ID; + uu_profileMiss; + jj_popL1RequestQueue; + } +*/ + + transition({IS, ISS}, {L1_GETS, L1_GET_INSTR}) { + zz_recycleL1RequestQueue; + } + + transition({IS, ISS}, L1_GETX) { + zz_recycleL1RequestQueue; + } + + transition(IM, {L1_GETX, L1_GETS, L1_GET_INSTR}) { + zz_recycleL1RequestQueue; + } + + // transitions from SS + // transition(SS, {L1_GETS, L1_GET_INSTR}) { + transition(SS, {L1_GETS, L1_GET_INSTR}, SS_MB) { + ds_sendSharedDataToRequestor; + nn_addSharer; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + + transition(SS, L1_GETX, SS_MB) { + d_sendDataToRequestor; + // fw_sendFwdInvToSharers; + fwm_sendFwdInvToSharersMinusRequestor; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(SS, L1_UPGRADE, SS_MB) { + fwm_sendFwdInvToSharersMinusRequestor; + ts_sendInvAckToUpgrader; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(SS, L2_Replacement_clean, I_I) { + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(SS, L2_Replacement, S_I) { + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(M, L1_GETX, MT_MB) { + d_sendDataToRequestor; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + // transition(M, L1_GET_INSTR, SS) { + transition(M, L1_GET_INSTR, SS_MB) { + d_sendDataToRequestor; + nn_addSharer; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(M, L1_GETS, MT_MB) { + dd_sendExclusiveDataToRequestor; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(M, L2_Replacement, M_I) { + i_allocateTBE; + c_exclusiveReplacement; + rr_deallocateL2CacheBlock; + } + + transition(M, L2_Replacement_clean, M_I) { + rr_deallocateL2CacheBlock; + } + + + // transitions from MT + + transition(MT, L1_GETX, MT_MB) { + b_forwardRequestToExclusive; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + + transition(MT, {L1_GETS, L1_GET_INSTR}, MT_IIB) { + b_forwardRequestToExclusive; + uu_profileMiss; + set_setMRU; + jj_popL1RequestQueue; + } + + transition(MT, L2_Replacement, MT_I) { + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(MT, L2_Replacement_clean, MCT_I) { + i_allocateTBE; + f_sendInvToSharers; + rr_deallocateL2CacheBlock; + } + + transition(MT, L1_PUTX, M) { + ll_clearSharers; + mr_writeDataToCacheFromRequest; + t_sendWBAck; + jj_popL1RequestQueue; + } + + + // transitions from blocking states + transition(SS_MB, Unblock_Cancel, SS) { + k_popUnblockQueue; + } + + transition(MT_MB, Unblock_Cancel, MT) { + k_popUnblockQueue; + } + + transition(MT_IB, Unblock_Cancel, MT) { + k_popUnblockQueue; + } + + transition(SS_MB, Exclusive_Unblock, MT) { + // update actual directory + mmu_markExclusiveFromUnblock; + k_popUnblockQueue; + } + + transition(SS_MB, Unblock, SS) { + k_popUnblockQueue; + } + + transition({M_MB, MT_MB}, Exclusive_Unblock, MT) { + // update actual directory + mmu_markExclusiveFromUnblock; + k_popUnblockQueue; + } + + transition(MT_IIB, Unblock, MT_IB) { + nnu_addSharerFromUnblock; + k_popUnblockQueue; + } + + transition(MT_IIB, {WB_Data, WB_Data_clean}, MT_SB) { + m_writeDataToCache; + o_popIncomingResponseQueue; + } + + transition(MT_IB, {WB_Data, WB_Data_clean}, SS) { + m_writeDataToCache; + o_popIncomingResponseQueue; + } + + transition(MT_SB, Unblock, SS) { + nnu_addSharerFromUnblock; + k_popUnblockQueue; + } + + // writeback states + transition({I_I, S_I, MT_I, MCT_I, M_I}, {L1_GETX, L1_UPGRADE, L1_GETS, L1_GET_INSTR}) { + zz_recycleL1RequestQueue; + } + + transition(I_I, Ack) { + q_updateAck; + o_popIncomingResponseQueue; + } + + transition(I_I, Ack_all, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition({MT_I, MCT_I}, WB_Data, M_I) { + qq_writeDataToTBE; + ct_exclusiveReplacementFromTBE; + o_popIncomingResponseQueue; + } + + transition(MCT_I, WB_Data_clean, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // L1 never changed Dirty data + transition(MT_I, Ack_all, M_I) { + ct_exclusiveReplacementFromTBE; + o_popIncomingResponseQueue; + } + + // clean data that L1 exclusive never wrote + transition(MCT_I, Ack_all, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // drop this because L1 will send data again + // the reason we don't accept is that the request virtual network may be completely backed up + // transition(MT_I, L1_PUTX) { + // jj_popL1RequestQueue; + //} + + // possible race between unblock and immediate replacement + transition(MT_MB, L1_PUTX) { + zz_recycleL1RequestQueue; + } + + transition(MT_I, WB_Data_clean, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(S_I, Ack) { + q_updateAck; + o_popIncomingResponseQueue; + } + + transition(S_I, Ack_all, M_I) { + ct_exclusiveReplacementFromTBE; + o_popIncomingResponseQueue; + } + + transition(M_I, Mem_Ack, NP) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } +} + diff --git a/src/mem/protocol/MESI_SCMP_bankdirectory-mem.sm b/src/mem/protocol/MESI_SCMP_bankdirectory-mem.sm new file mode 100644 index 000000000..1fcd234fe --- /dev/null +++ b/src/mem/protocol/MESI_SCMP_bankdirectory-mem.sm @@ -0,0 +1,166 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOESI_CMP_token-dir.sm 1.6 05/01/19 15:48:35-06:00 mikem@royal16.cs.wisc.edu $ + */ + + +machine(Directory, "Token protocol") { + + MessageBuffer requestToDir, network="From", virtual_network="2", ordered="false"; + MessageBuffer responseToDir, network="From", virtual_network="3", ordered="false"; + MessageBuffer responseFromDir, network="To", virtual_network="3", ordered="false"; + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_I") { + // Base states + I, desc="Owner"; + } + + // Events + enumeration(Event, desc="Directory events") { + Fetch, desc="A GETX arrives"; + Data, desc="A GETS arrives"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + DataBlock DataBlk, desc="data for the block"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + + State getState(Address addr) { + return State:I; + } + + void setState(Address addr, State state) { + } + + // ** OUT_PORTS ** + out_port(responseNetwork_out, ResponseMsg, responseFromDir); + + // ** IN_PORTS ** + + in_port(requestNetwork_in, RequestMsg, requestToDir) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:Fetch, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:Fetch, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + in_port(responseNetwork_in, ResponseMsg, responseToDir) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceResponseType:MEMORY_DATA) { + trigger(Event:Data, in_msg.Address); + } else { + DEBUG_EXPR(in_msg.Type); + error("Invalid message"); + } + } + } + } + + // Actions + action(a_sendAck, "a", desc="Send ack to L2") { + peek(responseNetwork_in, ResponseMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Sender); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + + action(d_sendData, "d", desc="Send data to requestor") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(j_popIncomingRequestQueue, "j", desc="Pop incoming request queue") { + requestNetwork_in.dequeue(); + } + + action(k_popIncomingResponseQueue, "k", desc="Pop incoming request queue") { + responseNetwork_in.dequeue(); + } + + action(m_writeDataToMemory, "m", desc="Write dirty writeback to memory") { + peek(responseNetwork_in, ResponseMsg) { + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + // TRANSITIONS + + transition(I, Fetch) { + d_sendData; + j_popIncomingRequestQueue; + } + + transition(I, Data) { + m_writeDataToMemory; + a_sendAck; + k_popIncomingResponseQueue; + } +} diff --git a/src/mem/protocol/MESI_SCMP_bankdirectory-msg.sm b/src/mem/protocol/MESI_SCMP_bankdirectory-msg.sm new file mode 100644 index 000000000..c2d02b59d --- /dev/null +++ b/src/mem/protocol/MESI_SCMP_bankdirectory-msg.sm @@ -0,0 +1,112 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MSI_MOSI_CMP_directory-msg.sm 1.5 05/01/19 15:48:37-06:00 mikem@royal16.cs.wisc.edu $ + * + */ + +// CoherenceRequestType +enumeration(CoherenceRequestType, desc="...") { + GETX, desc="Get eXclusive"; + UPGRADE, desc="UPGRADE to exclusive"; + GETS, desc="Get Shared"; + GET_INSTR, desc="Get Instruction"; + INV, desc="INValidate"; + PUTX, desc="replacement message"; +} + +// CoherenceResponseType +enumeration(CoherenceResponseType, desc="...") { + MEMORY_ACK, desc="Ack from memory controller"; + DATA, desc="Data"; + DATA_EXCLUSIVE, desc="Data"; + MEMORY_DATA, desc="Data"; + ACK, desc="Generic invalidate ack"; + WB_ACK, desc="writeback ack"; + UNBLOCK, desc="unblock"; + EXCLUSIVE_UNBLOCK, desc="exclusive unblock"; +} + +// RequestMsg +structure(RequestMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)"; + AccessModeType AccessMode, desc="user/supervisor access type"; + MachineID Requestor , desc="What component request"; + NetDest Destination, desc="What components receive the request, includes MachineType and num"; + MessageSizeType MessageSize, desc="size category of the message"; + DataBlock DataBlk, desc="Data for the cache line (if PUTX)"; + bool Dirty, default="false", desc="Dirty bit"; + PrefetchBit Prefetch, desc="Is this a prefetch request"; +} + +// ResponseMsg +structure(ResponseMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)"; + MachineID Sender, desc="What component sent the data"; + NetDest Destination, desc="Node to whom the data is sent"; + DataBlock DataBlk, desc="Data for the cache line"; + bool Dirty, default="false", desc="Dirty bit"; + int AckCount, default="0", desc="number of acks in this message"; + MessageSizeType MessageSize, desc="size category of the message"; +} + +/* +GenericRequestType convertToGenericType(CoherenceRequestType type) { + if(type == CoherenceRequestType:PUTX) { + return GenericRequestType:PUTX; + } else if(type == CoherenceRequestType:GETS) { + return GenericRequestType:GETS; + } else if(type == CoherenceRequestType:GET_INSTR) { + return GenericRequestType:GET_INSTR; + } else if(type == CoherenceRequestType:GETX) { + return GenericRequestType:GETX; + } else if(type == CoherenceRequestType:UPGRADE) { + return GenericRequestType:UPGRADE; + } else if(type == CoherenceRequestType:PUTS) { + return GenericRequestType:PUTS; + } else if(type == CoherenceRequestType:INV) { + return GenericRequestType:INV; + } else if(type == CoherenceRequestType:INV_S) { + return GenericRequestType:INV_S; + } else if(type == CoherenceRequestType:L1_DG) { + return GenericRequestType:DOWNGRADE; + } else if(type == CoherenceRequestType:WB_ACK) { + return GenericRequestType:WB_ACK; + } else if(type == CoherenceRequestType:EXE_ACK) { + return GenericRequestType:EXE_ACK; + } else { + DEBUG_EXPR(type); + error("invalid CoherenceRequestType"); + } +} +*/ + diff --git a/src/mem/protocol/MESI_SCMP_bankdirectory.slicc b/src/mem/protocol/MESI_SCMP_bankdirectory.slicc new file mode 100644 index 000000000..2d07999d7 --- /dev/null +++ b/src/mem/protocol/MESI_SCMP_bankdirectory.slicc @@ -0,0 +1,5 @@ +MESI_SCMP_bankdirectory-msg.sm +MESI_SCMP_bankdirectory-L2cache.sm +MESI_SCMP_bankdirectory-L1cache.sm +MESI_SCMP_bankdirectory-mem.sm +standard_CMP-protocol.sm diff --git a/src/mem/protocol/MESI_SCMP_bankdirectory_m-mem.sm b/src/mem/protocol/MESI_SCMP_bankdirectory_m-mem.sm new file mode 100644 index 000000000..37ecb2ffa --- /dev/null +++ b/src/mem/protocol/MESI_SCMP_bankdirectory_m-mem.sm @@ -0,0 +1,250 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOESI_CMP_token-dir.sm 1.6 05/01/19 15:48:35-06:00 mikem@royal16.cs.wisc.edu $ + */ + +// This file is copied from Yasuko Watanabe's prefetch / memory protocol +// Copied here by aep 12/14/07 + + +machine(Directory, "MESI_SCMP_bankdirectory protocol") { + + MessageBuffer requestToDir, network="From", virtual_network="2", ordered="false"; + MessageBuffer responseToDir, network="From", virtual_network="3", ordered="false"; + MessageBuffer responseFromDir, network="To", virtual_network="3", ordered="false"; + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_I") { + // Base states + I, desc="Owner"; + } + + // Events + enumeration(Event, desc="Directory events") { + Fetch, desc="A memory fetch arrives"; + Data, desc="writeback data arrives"; + Memory_Data, desc="Fetched data from memory arrives"; + Memory_Ack, desc="Writeback Ack from memory arrives"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + DataBlock DataBlk, desc="data for the block"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + // to simulate detailed DRAM + external_type(MemoryControl, inport="yes", outport="yes") { + + } + + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + MemoryControl memBuffer, constructor_hack="i"; + + State getState(Address addr) { + return State:I; + } + + void setState(Address addr, State state) { + } + + bool isGETRequest(CoherenceRequestType type) { + return (type == CoherenceRequestType:GETS) || + (type == CoherenceRequestType:GET_INSTR) || + (type == CoherenceRequestType:GETX); + } + + + // ** OUT_PORTS ** + out_port(responseNetwork_out, ResponseMsg, responseFromDir); + out_port(memQueue_out, MemoryMsg, memBuffer); + + // ** IN_PORTS ** + + in_port(requestNetwork_in, RequestMsg, requestToDir) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (isGETRequest(in_msg.Type)) { + trigger(Event:Fetch, in_msg.Address); + } else { + DEBUG_EXPR(in_msg); + error("Invalid message"); + } + } + } + } + + in_port(responseNetwork_in, ResponseMsg, responseToDir) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceResponseType:MEMORY_DATA) { + trigger(Event:Data, in_msg.Address); + } else { + DEBUG_EXPR(in_msg.Type); + error("Invalid message"); + } + } + } + } + + // off-chip memory request/response is done + in_port(memQueue_in, MemoryMsg, memBuffer) { + if (memQueue_in.isReady()) { + peek(memQueue_in, MemoryMsg) { + if (in_msg.Type == MemoryRequestType:MEMORY_READ) { + trigger(Event:Memory_Data, in_msg.Address); + } else if (in_msg.Type == MemoryRequestType:MEMORY_WB) { + trigger(Event:Memory_Ack, in_msg.Address); + } else { + DEBUG_EXPR(in_msg.Type); + error("Invalid message"); + } + } + } + } + + + + // Actions + action(a_sendAck, "a", desc="Send ack to L2") { + peek(memQueue_in, MemoryMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.OriginalRequestorMachId); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + + action(d_sendData, "d", desc="Send data to requestor") { + peek(memQueue_in, MemoryMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:MEMORY_DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.OriginalRequestorMachId); + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(j_popIncomingRequestQueue, "j", desc="Pop incoming request queue") { + requestNetwork_in.dequeue(); + } + + action(k_popIncomingResponseQueue, "k", desc="Pop incoming request queue") { + responseNetwork_in.dequeue(); + } + + action(l_popMemQueue, "q", desc="Pop off-chip request queue") { + memQueue_in.dequeue(); + } + + action(qf_queueMemoryFetchRequest, "qf", desc="Queue off-chip fetch request") { + peek(requestNetwork_in, RequestMsg) { + enqueue(memQueue_out, MemoryMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := MemoryRequestType:MEMORY_READ; + out_msg.Sender := machineID; + out_msg.OriginalRequestorMachId := in_msg.Requestor; + out_msg.MessageSize := in_msg.MessageSize; + out_msg.Prefetch := in_msg.Prefetch; + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + + DEBUG_EXPR(out_msg); + } + } + } + + action(qw_queueMemoryWBRequest, "qw", desc="Queue off-chip writeback request") { + peek(responseNetwork_in, ResponseMsg) { + enqueue(memQueue_out, MemoryMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := MemoryRequestType:MEMORY_WB; + out_msg.Sender := machineID; + out_msg.OriginalRequestorMachId := in_msg.Sender; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.MessageSize := in_msg.MessageSize; + //out_msg.Prefetch := in_msg.Prefetch; + + DEBUG_EXPR(out_msg); + } + } + } + + action(m_writeDataToMemory, "m", desc="Write dirty writeback to memory") { + peek(responseNetwork_in, ResponseMsg) { + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + // TRANSITIONS + + transition(I, Fetch) { + //d_sendData; + qf_queueMemoryFetchRequest; + j_popIncomingRequestQueue; + } + + transition(I, Data) { + m_writeDataToMemory; + //a_sendAck; + qw_queueMemoryWBRequest; + k_popIncomingResponseQueue; + } + + transition(I, Memory_Data) { + d_sendData; + l_popMemQueue; + } + + transition(I, Memory_Ack) { + a_sendAck; + l_popMemQueue; + } +} diff --git a/src/mem/protocol/MESI_SCMP_bankdirectory_m.slicc b/src/mem/protocol/MESI_SCMP_bankdirectory_m.slicc new file mode 100644 index 000000000..4752cea5a --- /dev/null +++ b/src/mem/protocol/MESI_SCMP_bankdirectory_m.slicc @@ -0,0 +1,5 @@ +MESI_SCMP_bankdirectory-msg.sm +MESI_SCMP_bankdirectory-L2cache.sm +MESI_SCMP_bankdirectory-L1cache.sm +MESI_SCMP_bankdirectory_m-mem.sm +standard_CMP-protocol.sm diff --git a/src/mem/protocol/MI_example-cache.sm b/src/mem/protocol/MI_example-cache.sm new file mode 100644 index 000000000..6c1cb02b6 --- /dev/null +++ b/src/mem/protocol/MI_example-cache.sm @@ -0,0 +1,369 @@ + +machine(L1Cache, "MI Example") { + + // NETWORK BUFFERS + MessageBuffer requestFromCache, network="To", virtual_network="0", ordered="true"; + MessageBuffer responseFromCache, network="To", virtual_network="1", ordered="true"; + + MessageBuffer forwardToCache, network="From", virtual_network="2", ordered="true"; + MessageBuffer responseToCache, network="From", virtual_network="1", ordered="true"; + + // STATES + enumeration(State, desc="Cache states") { + I, desc="Not Present/Invalid"; + II, desc="Not Present/Invalid, issued PUT"; + M, desc="Modified"; + MI, desc="Modified, issued PUT"; + + IS, desc="Issued request for IFETCH/GETX"; + IM, desc="Issued request for STORE/ATOMIC"; + } + + // EVENTS + enumeration(Event, desc="Cache events") { + // From processor + + Load, desc="Load request from processor"; + Ifetch, desc="Ifetch request from processor"; + Store, desc="Store request from processor"; + + Data, desc="Data from network"; + Fwd_GETX, desc="Forward from network"; + + Replacement, desc="Replace a block"; + Writeback_Ack, desc="Ack from the directory for a writeback"; + Writeback_Nack, desc="Nack from the directory for a writeback"; + } + + // STRUCTURE DEFINITIONS + + MessageBuffer mandatoryQueue, ordered="false", abstract_chip_ptr="true"; + Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i"; + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + DataBlock DataBlk, desc="data for the block"; + } + + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + } + + // TBE fields + structure(TBE, desc="...") { + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="data for the block, required for concurrent writebacks"; + bool Trans, desc="Is this block part of a the current transaction?"; + bool Logged, desc="Has this block been logged in the current transaction?"; + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + + // STRUCTURES + + CacheMemory cacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS, L1_CACHE_ASSOC, MachineType_L1Cache, int_to_string(i)+"_L1"', abstract_chip_ptr="true"; + + TBETable TBEs, template_hack=""; + + + + // FUNCTIONS + Event mandatory_request_type_to_event(CacheRequestType type) { + if (type == CacheRequestType:LD) { + return Event:Load; + } else if (type == CacheRequestType:IFETCH) { + return Event:Ifetch; + } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { + return Event:Store; + } else { + error("Invalid CacheRequestType"); + } + } + + + State getState(Address addr) { + + if (TBEs.isPresent(addr)) { + return TBEs[addr].TBEState; + } + else if (cacheMemory.isTagPresent(addr)) { + return cacheMemory[addr].CacheState; + } + else { + return State:I; + } + } + + void setState(Address addr, State state) { + + if (TBEs.isPresent(addr)) { + TBEs[addr].TBEState := state; + } + + if (cacheMemory.isTagPresent(addr)) { + cacheMemory[addr].CacheState := state; + } + } + + + // NETWORK PORTS + + out_port(requestNetwork_out, RequestMsg, requestFromCache); + out_port(responseNetwork_out, ResponseMsg, responseFromCache); + + in_port(forwardRequestNetwork_in, RequestMsg, forwardToCache) { + if (forwardRequestNetwork_in.isReady()) { + peek(forwardRequestNetwork_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:Fwd_GETX, in_msg.Address); + } + else if (in_msg.Type == CoherenceRequestType:WB_ACK) { + trigger(Event:Writeback_Ack, in_msg.Address); + } + else if (in_msg.Type == CoherenceRequestType:WB_NACK) { + trigger(Event:Writeback_Nack, in_msg.Address); + } + else { + error("Unexpected message"); + } + } + } + } + + in_port(responseNetwork_in, ResponseMsg, responseToCache) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + if (in_msg.Type == CoherenceResponseType:DATA) { + trigger(Event:Data, in_msg.Address); + } + else { + error("Unexpected message"); + } + } + } + } + + // Mandatory Queue + in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { + if (mandatoryQueue_in.isReady()) { + peek(mandatoryQueue_in, CacheMsg) { + + + if (cacheMemory.isTagPresent(in_msg.Address) == false && + cacheMemory.cacheAvail(in_msg.Address) == false ) { + // make room for the block + trigger(Event:Replacement, cacheMemory.cacheProbe(in_msg.Address)); + } + else { + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } + } + } + } + + // ACTIONS + + action(a_issueRequest, "a", desc="Issue a request") { + enqueue(requestNetwork_out, RequestMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(b_issuePUT, "b", desc="Issue a PUT request") { + enqueue(requestNetwork_out, RequestMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTX; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.DataBlk := cacheMemory[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } + + + action(e_sendData, "e", desc="Send data from cache to requestor") { + peek(forwardRequestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := cacheMemory[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(ee_sendDataFromTBE, "\e", desc="Send data from TBE to requestor") { + peek(forwardRequestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := TBEs[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + + action(i_allocateL1CacheBlock, "i", desc="Allocate a cache block") { + if (cacheMemory.isTagPresent(address) == false) { + cacheMemory.allocate(address); + } + } + + action(h_deallocateL1CacheBlock, "h", desc="deallocate a cache block") { + if (cacheMemory.isTagPresent(address) == true) { + cacheMemory.deallocate(address); + } + } + + action(m_popMandatoryQueue, "m", desc="Pop the mandatory request queue") { + mandatoryQueue_in.dequeue(); + } + + action(n_popResponseQueue, "n", desc="Pop the response queue") { + responseNetwork_in.dequeue(); + } + + action(o_popForwardedRequestQueue, "o", desc="Pop the forwarded request queue") { + forwardRequestNetwork_in.dequeue(); + } + + action(r_load_hit, "r", desc="Notify sequencer the load completed.") { + DEBUG_EXPR(cacheMemory[address].DataBlk); + sequencer.readCallback(address, cacheMemory[address].DataBlk); + } + + action(s_store_hit, "s", desc="Notify sequencer that store completed.") { + DEBUG_EXPR(cacheMemory[address].DataBlk); + sequencer.writeCallback(address, cacheMemory[address].DataBlk); + } + + + action(u_writeDataToCache, "u", desc="Write data to the cache") { + peek(responseNetwork_in, ResponseMsg) { + cacheMemory[address].DataBlk := in_msg.DataBlk; + } + } + + + action(v_allocateTBE, "v", desc="Allocate TBE") { + TBEs.allocate(address); + } + + + action(w_deallocateTBE, "w", desc="Deallocate TBE") { + TBEs.deallocate(address); + } + + action(x_copyDataFromCacheToTBE, "x", desc="Copy data from cache to TBE") { + TBEs[address].DataBlk := cacheMemory[address].DataBlk; + } + + action(z_stall, "z", desc="stall") { + // do nothing + } + + // TRANSITIONS + + transition({IS, IM, MI, II}, {Load, Ifetch, Store, Replacement}) { + z_stall; + } + + transition({IS, IM}, Fwd_GETX) { + z_stall; + } + + transition(M, Store) { + s_store_hit; + m_popMandatoryQueue; + } + + transition(M, {Load, Ifetch}) { + r_load_hit; + m_popMandatoryQueue; + } + + + transition(I, Store, IM) { + v_allocateTBE; + i_allocateL1CacheBlock; + a_issueRequest; + m_popMandatoryQueue; + } + + transition(I, {Load, Ifetch}, IS) { + v_allocateTBE; + i_allocateL1CacheBlock; + a_issueRequest; + m_popMandatoryQueue; + } + + transition(IS, Data, M) { + u_writeDataToCache; + r_load_hit; + w_deallocateTBE; + n_popResponseQueue; + } + + transition(IM, Data, M) { + u_writeDataToCache; + s_store_hit; + w_deallocateTBE; + n_popResponseQueue; + } + + transition(M, Fwd_GETX, I) { + e_sendData; + o_popForwardedRequestQueue; + } + + transition(I, Replacement) { + h_deallocateL1CacheBlock; + } + + transition(M, Replacement, MI) { + v_allocateTBE; + b_issuePUT; + x_copyDataFromCacheToTBE; + h_deallocateL1CacheBlock; + } + + transition(MI, Writeback_Ack, I) { + w_deallocateTBE; + o_popForwardedRequestQueue; + } + + transition(MI, Fwd_GETX, II) { + ee_sendDataFromTBE; + o_popForwardedRequestQueue; + } + + transition(II, Writeback_Nack, I) { + w_deallocateTBE; + o_popForwardedRequestQueue; + } +} + diff --git a/src/mem/protocol/MI_example-dir.sm b/src/mem/protocol/MI_example-dir.sm new file mode 100644 index 000000000..311f8488b --- /dev/null +++ b/src/mem/protocol/MI_example-dir.sm @@ -0,0 +1,257 @@ + +machine(Directory, "Directory protocol") { + + MessageBuffer forwardFromDir, network="To", virtual_network="2", ordered="true"; + MessageBuffer responseFromDir, network="To", virtual_network="1", ordered="false"; + + MessageBuffer requestToDir, network="From", virtual_network="0", ordered="true"; + MessageBuffer unblockToDir, network="From", virtual_network="3", ordered="true"; + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_I") { + // Base states + I, desc="Invalid"; + M, desc="Modified"; + + MI, desc="Blocked on a writeback"; + } + + // Events + enumeration(Event, desc="Directory events") { + GETX, desc="A GETX arrives"; + GETS, desc="A GETS arrives"; + PUTX, desc="A PUTX arrives"; + PUTX_NotOwner, desc="A PUTX arrives"; + PUTO, desc="A PUTO arrives"; + Unblock, desc="An unblock message arrives"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + State DirectoryState, desc="Directory state"; + DataBlock DataBlk, desc="data for the block"; + NetDest Sharers, desc="Sharers for this block"; + NetDest Owner, desc="Owner of this block"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + + State getState(Address addr) { + return directory[addr].DirectoryState; + } + + void setState(Address addr, State state) { + if (directory.isPresent(addr)) { + + if (state == State:I) { + assert(directory[addr].Owner.count() == 0); + assert(directory[addr].Sharers.count() == 0); + } + + if (state == State:M) { + assert(directory[addr].Owner.count() == 1); + assert(directory[addr].Sharers.count() == 0); + } + + directory[addr].DirectoryState := state; + } + } + + // ** OUT_PORTS ** + out_port(forwardNetwork_out, RequestMsg, forwardFromDir); + out_port(responseNetwork_out, ResponseMsg, responseFromDir); + out_port(requestQueue_out, ResponseMsg, requestToDir); // For recycling requests + + // ** IN_PORTS ** + + + in_port(requestQueue_in, RequestMsg, requestToDir) { + if (requestQueue_in.isReady()) { + peek(requestQueue_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTX) { + if (directory[in_msg.Address].Owner.isElement(in_msg.Requestor)) { + trigger(Event:PUTX, in_msg.Address); + } else { + trigger(Event:PUTX_NotOwner, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:PUTO) { + trigger(Event:PUTO, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + in_port(unblockNetwork_in, ResponseMsg, unblockToDir) { + if (unblockNetwork_in.isReady()) { + peek(unblockNetwork_in, ResponseMsg) { + if (in_msg.Type == CoherenceResponseType:UNBLOCK) { + trigger(Event:Unblock, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + + + // Actions + + action(a_sendWriteBackAck, "a", desc="Send writeback ack to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:WB_ACK; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(b_sendWriteBackNack, "b", desc="Send writeback nack to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:WB_NACK; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(c_clearOwner, "c", desc="Clear the owner field") { + directory[address].Owner.clear(); + } + + action(d_sendData, "d", desc="Send data to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + + if (in_msg.Type == CoherenceRequestType:GETS && directory[address].Sharers.count() == 0) { + // out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE_CLEAN; + out_msg.Type := CoherenceResponseType:DATA; + } else { + out_msg.Type := CoherenceResponseType:DATA; + } + + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.Dirty := false; // By definition, the block is now clean + out_msg.Acks := directory[address].Sharers.count(); + if (directory[address].Sharers.isElement(in_msg.Requestor)) { + out_msg.Acks := out_msg.Acks - 1; + } + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(e_ownerIsRequestor, "e", desc="The owner is now the requestor") { + peek(requestQueue_in, RequestMsg) { + directory[address].Owner.clear(); + directory[address].Owner.add(in_msg.Requestor); + } + } + + action(f_forwardRequest, "f", desc="Forward request to owner") { + peek(requestQueue_in, RequestMsg) { + APPEND_TRANSITION_COMMENT("Own: "); + APPEND_TRANSITION_COMMENT(directory[in_msg.Address].Owner); + APPEND_TRANSITION_COMMENT("Req: "); + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := directory[in_msg.Address].Owner; + out_msg.Acks := directory[address].Sharers.count(); + if (directory[address].Sharers.isElement(in_msg.Requestor)) { + out_msg.Acks := out_msg.Acks - 1; + } + out_msg.MessageSize := MessageSizeType:Forwarded_Control; + } + } + } + + + action(i_popIncomingRequestQueue, "i", desc="Pop incoming request queue") { + requestQueue_in.dequeue(); + } + + action(j_popIncomingUnblockQueue, "j", desc="Pop incoming unblock queue") { + unblockNetwork_in.dequeue(); + } + + action(l_writeDataToMemory, "l", desc="Write PUTX/PUTO data to memory") { + // peek(unblockNetwork_in, ResponseMsg) { + peek(requestQueue_in, RequestMsg) { + // assert(in_msg.Dirty); + // assert(in_msg.MessageSize == MessageSizeType:Writeback_Data); + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + + + // TRANSITIONS + + transition(I, GETX, M) { + d_sendData; + e_ownerIsRequestor; + i_popIncomingRequestQueue; + } + + + + transition(M, GETX, M) { + f_forwardRequest; + e_ownerIsRequestor; + i_popIncomingRequestQueue; + } + + // transition(M, PUTX, MI) { + transition(M, PUTX, I) { + c_clearOwner; + l_writeDataToMemory; + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + transition(M, PUTX_NotOwner, M) { + b_sendWriteBackNack; + i_popIncomingRequestQueue; + } + + transition(I, PUTX_NotOwner, I) { + b_sendWriteBackNack; + i_popIncomingRequestQueue; + } + + + transition(MI, Unblock, M) { + j_popIncomingUnblockQueue; + } + +} diff --git a/src/mem/protocol/MI_example-msg.sm b/src/mem/protocol/MI_example-msg.sm new file mode 100644 index 000000000..f577d60df --- /dev/null +++ b/src/mem/protocol/MI_example-msg.sm @@ -0,0 +1,92 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOESI_SMP_directory-msg.sm 1.8 05/01/19 15:48:36-06:00 mikem@royal16.cs.wisc.edu $ + * + */ + +// CoherenceRequestType +enumeration(CoherenceRequestType, desc="...") { + GETX, desc="Get eXclusive"; + GETS, desc="Get Shared"; + PUTX, desc="Put eXclusive"; + PUTO, desc="Put Owned"; + WB_ACK, desc="Writeback ack"; + WB_NACK, desc="Writeback neg. ack"; + INV, desc="Invalidation"; + FWD, desc="Generic FWD"; +} + +// CoherenceResponseType +enumeration(CoherenceResponseType, desc="...") { + ACK, desc="ACKnowledgment, responder doesn't have a copy"; + DATA, desc="Data"; + DATA_EXCLUSIVE_CLEAN, desc="Data, no other processor has a copy, data is clean"; + DATA_EXCLUSIVE_DIRTY, desc="Data, no other processor has a copy, data is dirty"; + UNBLOCK, desc="Unblock"; + UNBLOCK_EXCLUSIVE, desc="Unblock, we're in E/M"; + WRITEBACK_CLEAN, desc="Clean writeback (no data)"; + WRITEBACK_DIRTY, desc="Dirty writeback (contains data)"; + WRITEBACK, desc="Generic writeback (contains data)"; +} + +// TriggerType +enumeration(TriggerType, desc="...") { + ALL_ACKS, desc="See corresponding event"; +} + +// TriggerMsg +structure(TriggerMsg, desc="...", interface="Message") { + Address Address, desc="Physical address for this request"; + TriggerType Type, desc="Type of trigger"; +} + +// RequestMsg (and also forwarded requests) +structure(RequestMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)"; + MachineID Requestor, desc="Node who initiated the request"; + NetDest Destination, desc="Multicast destination mask"; + int Acks, desc="How many acks to expect"; + DataBlock DataBlk, desc="data for the cache line"; + MessageSizeType MessageSize, desc="size category of the message"; +} + +// ResponseMsg (and also unblock requests) +structure(ResponseMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)"; + MachineID Sender, desc="Node who sent the data"; + NetDest Destination, desc="Node to whom the data is sent"; + DataBlock DataBlk, desc="data for the cache line"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + int Acks, desc="How many acks to expect"; + MessageSizeType MessageSize, desc="size category of the message"; +} diff --git a/src/mem/protocol/MI_example.slicc b/src/mem/protocol/MI_example.slicc new file mode 100644 index 000000000..cb1f80135 --- /dev/null +++ b/src/mem/protocol/MI_example.slicc @@ -0,0 +1,4 @@ +MI_example-msg.sm +MI_example-cache.sm +MI_example-dir.sm +standard_1level_SMP-protocol.sm diff --git a/src/mem/protocol/MOESI_CMP_directory-L1cache.sm b/src/mem/protocol/MOESI_CMP_directory-L1cache.sm new file mode 100644 index 000000000..a65ade10f --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_directory-L1cache.sm @@ -0,0 +1,1153 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +machine(L1Cache, "Directory protocol") { + + // NODE L1 CACHE + // From this node's L1 cache TO the network + // a local L1 -> this L2 bank, currently ordered with directory forwarded requests + MessageBuffer requestFromL1Cache, network="To", virtual_network="0", ordered="false"; + MessageBuffer foo, network="To", virtual_network="1", ordered="false"; + // a local L1 -> this L2 bank + MessageBuffer responseFromL1Cache, network="To", virtual_network="2", ordered="false"; +// MessageBuffer writebackFromL1Cache, network="To", virtual_network="3", ordered="false"; + + + // To this node's L1 cache FROM the network + // a L2 bank -> this L1 + MessageBuffer requestToL1Cache, network="From", virtual_network="0", ordered="false"; + MessageBuffer goo, network="From", virtual_network="1", ordered="false"; + // a L2 bank -> this L1 + MessageBuffer responseToL1Cache, network="From", virtual_network="2", ordered="false"; + + + + // STATES + enumeration(State, desc="Cache states", default="L1Cache_State_I") { + // Base states + I, desc="Idle"; + S, desc="Shared"; + O, desc="Owned"; + M, desc="Modified (dirty)"; + M_W, desc="Modified (dirty)"; + MM, desc="Modified (dirty and locally modified)"; + MM_W, desc="Modified (dirty and locally modified)"; + + // Transient States + IM, "IM", desc="Issued GetX"; + SM, "SM", desc="Issued GetX, we still have an old copy of the line"; + OM, "SM", desc="Issued GetX, received data"; + IS, "IS", desc="Issued GetS"; + SI, "OI", desc="Issued PutS, waiting for ack"; + OI, "OI", desc="Issued PutO, waiting for ack"; + MI, "MI", desc="Issued PutX, waiting for ack"; + II, "II", desc="Issued PutX/O, saw Fwd_GETS or Fwd_GETX, waiting for ack"; + } + + // EVENTS + enumeration(Event, desc="Cache events") { + Load, desc="Load request from the processor"; + Ifetch, desc="I-fetch request from the processor"; + Store, desc="Store request from the processor"; + L1_Replacement, desc="Replacement"; + + // Requests + Own_GETX, desc="We observe our own GetX forwarded back to us"; + Fwd_GETX, desc="A GetX from another processor"; + Fwd_GETS, desc="A GetS from another processor"; + Inv, desc="Invalidations from the directory"; + + // Responses + Ack, desc="Received an ack message"; + Data, desc="Received a data message, responder has a shared copy"; + Exclusive_Data, desc="Received a data message"; + + Writeback_Ack, desc="Writeback O.K. from directory"; + Writeback_Ack_Data, desc="Writeback O.K. from directory"; + Writeback_Nack, desc="Writeback not O.K. from directory"; + + // Triggers + All_acks, desc="Received all required data and message acks"; + + // Timeouts + Use_Timeout, desc="lockout period ended"; + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + DataBlock DataBlk, desc="data for the block"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="data for the block, required for concurrent writebacks"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + int NumPendingMsgs, default="0", desc="Number of acks/data messages that this processor is waiting for"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + + MessageBuffer mandatoryQueue, ordered="false", abstract_chip_ptr="true"; + Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i"; + + TBETable TBEs, template_hack=""; + CacheMemory L1IcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1I"', abstract_chip_ptr="true"; + CacheMemory L1DcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1D"', abstract_chip_ptr="true"; + TimerTable useTimerTable; + + Entry getCacheEntry(Address addr), return_by_ref="yes" { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory[addr]; + } else { + return L1IcacheMemory[addr]; + } + } + + void changePermission(Address addr, AccessPermission permission) { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory.changePermission(addr, permission); + } else { + return L1IcacheMemory.changePermission(addr, permission); + } + } + + bool isCacheTagPresent(Address addr) { + return (L1DcacheMemory.isTagPresent(addr) || L1IcacheMemory.isTagPresent(addr)); + } + + State getState(Address addr) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + + if(TBEs.isPresent(addr)) { + return TBEs[addr].TBEState; + } else if (isCacheTagPresent(addr)) { + return getCacheEntry(addr).CacheState; + } + return State:I; + } + + void setState(Address addr, State state) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + + if (TBEs.isPresent(addr)) { + TBEs[addr].TBEState := state; + } + + if (isCacheTagPresent(addr)) { + if ( ((getCacheEntry(addr).CacheState != State:M) && (state == State:M)) || + ((getCacheEntry(addr).CacheState != State:MM) && (state == State:MM)) || + ((getCacheEntry(addr).CacheState != State:S) && (state == State:S)) || + ((getCacheEntry(addr).CacheState != State:O) && (state == State:O)) ) { + + getCacheEntry(addr).CacheState := state; + sequencer.checkCoherence(addr); + } + else { + getCacheEntry(addr).CacheState := state; + } + + // Set permission + if (state == State:MM || state == State:MM_W) { + changePermission(addr, AccessPermission:Read_Write); + } else if ((state == State:S) || + (state == State:O) || + (state == State:M) || + (state == State:M_W) || + (state == State:SM) || + (state == State:OM)) { + changePermission(addr, AccessPermission:Read_Only); + } else { + changePermission(addr, AccessPermission:Invalid); + } + } + } + + bool isBlockExclusive(Address addr) { + + if (isCacheTagPresent(addr)) { + if ( (getCacheEntry(addr).CacheState == State:M) || (getCacheEntry(addr).CacheState == State:MM) + || (getCacheEntry(addr).CacheState == State:MI) || (getCacheEntry(addr).CacheState == State:MM_W) + ) { + return true; + } + } + + return false; + } + + bool isBlockShared(Address addr) { + if (isCacheTagPresent(addr)) { + if ( (getCacheEntry(addr).CacheState == State:S) || (getCacheEntry(addr).CacheState == State:O) + || (getCacheEntry(addr).CacheState == State:SM) + || (getCacheEntry(addr).CacheState == State:OI) + || (getCacheEntry(addr).CacheState == State:SI) + || (getCacheEntry(addr).CacheState == State:OM) + ) { + return true; + } + } + + return false; + } + + + Event mandatory_request_type_to_event(CacheRequestType type) { + if (type == CacheRequestType:LD) { + return Event:Load; + } else if (type == CacheRequestType:IFETCH) { + return Event:Ifetch; + } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { + return Event:Store; + } else { + error("Invalid CacheRequestType"); + } + } + + MessageBuffer triggerQueue, ordered="true"; + + // ** OUT_PORTS ** + + out_port(requestNetwork_out, RequestMsg, requestFromL1Cache); + out_port(responseNetwork_out, ResponseMsg, responseFromL1Cache); + out_port(triggerQueue_out, TriggerMsg, triggerQueue); + out_port(foo_out, ResponseMsg, foo); + + // ** IN_PORTS ** + + // Use Timer + in_port(useTimerTable_in, Address, useTimerTable) { + if (useTimerTable_in.isReady()) { + trigger(Event:Use_Timeout, useTimerTable.readyAddress()); + } + } + + + in_port(goo_in, RequestMsg, goo) { + if (goo_in.isReady()) { + peek(goo_in, RequestMsg) { + assert(false); + } + } + } + + // Trigger Queue + in_port(triggerQueue_in, TriggerMsg, triggerQueue) { + if (triggerQueue_in.isReady()) { + peek(triggerQueue_in, TriggerMsg) { + if (in_msg.Type == TriggerType:ALL_ACKS) { + trigger(Event:All_acks, in_msg.Address); + } else { + error("Unexpected message"); + } + } + } + } + + // Nothing from the request network + + // Request Network + in_port(requestNetwork_in, RequestMsg, requestToL1Cache) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + DEBUG_EXPR("MRM_DEBUG: L1 received"); + DEBUG_EXPR(in_msg.Type); + if (in_msg.Type == CoherenceRequestType:GETX) { + if (in_msg.Requestor == machineID && in_msg.RequestorMachine == MachineType:L1Cache) { + trigger(Event:Own_GETX, in_msg.Address); + } else { + trigger(Event:Fwd_GETX, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:Fwd_GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:WB_ACK) { + trigger(Event:Writeback_Ack, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:WB_ACK_DATA) { + trigger(Event:Writeback_Ack_Data, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:WB_NACK) { + trigger(Event:Writeback_Nack, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:INV) { + trigger(Event:Inv, in_msg.Address); + } else { + error("Unexpected message"); + } + } + } + } + + // Response Network + in_port(responseToL1Cache_in, ResponseMsg, responseToL1Cache) { + if (responseToL1Cache_in.isReady()) { + peek(responseToL1Cache_in, ResponseMsg) { + if (in_msg.Type == CoherenceResponseType:ACK) { + trigger(Event:Ack, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA) { + trigger(Event:Data, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE) { + trigger(Event:Exclusive_Data, in_msg.Address); + } else { + error("Unexpected message"); + } + } + } + } + + // Nothing from the unblock network + // Mandatory Queue betweens Node's CPU and it's L1 caches + in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { + if (mandatoryQueue_in.isReady()) { + peek(mandatoryQueue_in, CacheMsg) { + + // Check for data access to blocks in I-cache and ifetchs to blocks in D-cache + + if (in_msg.Type == CacheRequestType:IFETCH) { + // ** INSTRUCTION ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, put the request on the queue to the shared L2 + trigger(Event:L1_Replacement, in_msg.Address); + } + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 asks the L2 for it. + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1IcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 so let's see if the L2 has it + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // No room in the L1, so we need to make room in the L1 + trigger(Event:L1_Replacement, L1IcacheMemory.cacheProbe(in_msg.Address)); + } + } + } else { + // *** DATA ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, put the request on the queue to the shared L2 + trigger(Event:L1_Replacement, in_msg.Address); + } + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 ask the L2 for it + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1DcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 let's see if the L2 has it + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // No room in the L1, so we need to make room in the L1 + trigger(Event:L1_Replacement, L1DcacheMemory.cacheProbe(in_msg.Address)); + } + } + } + } + } + } + + + // ACTIONS + + action(a_issueGETS, "a", desc="Issue GETS") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestNetwork_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Request_Control; + out_msg.AccessMode := in_msg.AccessMode; + out_msg.Prefetch := in_msg.Prefetch; + } + } + } + + action(b_issueGETX, "b", desc="Issue GETX") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestNetwork_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Request_Control; + out_msg.AccessMode := in_msg.AccessMode; + out_msg.Prefetch := in_msg.Prefetch; + } + } + } + + action(d_issuePUTX, "d", desc="Issue PUTX") { + // enqueue(writebackNetwork_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + enqueue(requestNetwork_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTX; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + + action(dd_issuePUTO, "\d", desc="Issue PUTO") { + // enqueue(writebackNetwork_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + enqueue(requestNetwork_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTO; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + + action(dd_issuePUTS, "\ds", desc="Issue PUTS") { + // enqueue(writebackNetwork_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + enqueue(requestNetwork_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTS; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + + action(e_sendData, "e", desc="Send data from cache to requestor") { + peek(requestNetwork_in, RequestMsg) { + if (in_msg.RequestorMachine == MachineType:L2Cache) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(in_msg.Address, machineID)); + out_msg.DataBlk := getCacheEntry(address).DataBlk; + // out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.Dirty := false; + out_msg.Acks := in_msg.Acks; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + DEBUG_EXPR("Sending data to L2"); + DEBUG_EXPR(in_msg.Address); + } + else { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getCacheEntry(address).DataBlk; + // out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.Dirty := false; + out_msg.Acks := in_msg.Acks; + out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; + } + DEBUG_EXPR("Sending data to L1"); + } + } + } + + action(e_sendDataToL2, "ee", desc="Send data from cache to requestor") { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.Acks := 0; // irrelevant + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + + action(ee_sendDataExclusive, "\e", desc="Send data from cache to requestor, don't keep a shared copy") { + peek(requestNetwork_in, RequestMsg) { + if (in_msg.RequestorMachine == MachineType:L2Cache) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(in_msg.Address, machineID)); + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.Acks := in_msg.Acks; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + DEBUG_EXPR("Sending exclusive data to L2"); + } + else { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.Acks := in_msg.Acks; + out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; + } + DEBUG_EXPR("Sending exclusive data to L1"); + } + } + } + + action(f_sendAck, "f", desc="Send ack from cache to requestor") { + peek(requestNetwork_in, RequestMsg) { + if (in_msg.RequestorMachine == MachineType:L1Cache) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.Acks := 0 - 1; // -1 + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + else { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(in_msg.Address, machineID)); + out_msg.Acks := 0 - 1; // -1 + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + } + + action(g_sendUnblock, "g", desc="Send unblock to memory") { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:UNBLOCK; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Unblock_Control; + } + } + + action(gg_sendUnblockExclusive, "\g", desc="Send unblock exclusive to memory") { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:UNBLOCK_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.MessageSize := MessageSizeType:Unblock_Control; + } + } + + action(h_load_hit, "h", desc="Notify sequencer the load completed.") { + DEBUG_EXPR(getCacheEntry(address).DataBlk); + sequencer.readCallback(address, getCacheEntry(address).DataBlk); + } + + action(hh_store_hit, "\h", desc="Notify sequencer that store completed.") { + DEBUG_EXPR(getCacheEntry(address).DataBlk); + sequencer.writeCallback(address, getCacheEntry(address).DataBlk); + getCacheEntry(address).Dirty := true; + } + + action(i_allocateTBE, "i", desc="Allocate TBE") { + check_allocate(TBEs); + TBEs.allocate(address); + TBEs[address].DataBlk := getCacheEntry(address).DataBlk; // Data only used for writebacks + TBEs[address].Dirty := getCacheEntry(address).Dirty; + } + + action(j_popTriggerQueue, "j", desc="Pop trigger queue.") { + triggerQueue_in.dequeue(); + } + + action(jj_unsetUseTimer, "\jj", desc="Unset use timer.") { + useTimerTable.unset(address); + } + + action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") { + mandatoryQueue_in.dequeue(); + } + + action(l_popForwardQueue, "l", desc="Pop forwareded request queue.") { + requestNetwork_in.dequeue(); + } + + action(m_decrementNumberOfMessages, "m", desc="Decrement the number of messages for which we're waiting") { + peek(responseToL1Cache_in, ResponseMsg) { + DEBUG_EXPR("MRM_DEBUG: L1 decrementNumberOfMessages"); + DEBUG_EXPR(id); + DEBUG_EXPR(in_msg.Acks); + TBEs[address].NumPendingMsgs := TBEs[address].NumPendingMsgs - in_msg.Acks; + } + } + + action(mm_decrementNumberOfMessages, "\m", desc="Decrement the number of messages for which we're waiting") { + peek(requestNetwork_in, RequestMsg) { + TBEs[address].NumPendingMsgs := TBEs[address].NumPendingMsgs - in_msg.Acks; + } + } + + action(n_popResponseQueue, "n", desc="Pop response queue") { + responseToL1Cache_in.dequeue(); + } + + action(o_checkForCompletion, "o", desc="Check if we have received all the messages required for completion") { + if (TBEs[address].NumPendingMsgs == 0) { + enqueue(triggerQueue_out, TriggerMsg) { + out_msg.Address := address; + out_msg.Type := TriggerType:ALL_ACKS; + } + } + } + + action(o_scheduleUseTimeout, "oo", desc="Schedule a use timeout.") { + useTimerTable.set(address, 50); + } + + + action(q_sendDataFromTBEToCache, "q", desc="Send data from TBE to cache") { + peek(requestNetwork_in, RequestMsg) { + if (in_msg.RequestorMachine == MachineType:L1Cache) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := TBEs[address].DataBlk; + // out_msg.Dirty := TBEs[address].Dirty; + out_msg.Dirty := false; + out_msg.Acks := in_msg.Acks; + out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; + } + } + else { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address,machineID)); + out_msg.DataBlk := TBEs[address].DataBlk; + // out_msg.Dirty := TBEs[address].Dirty; + out_msg.Dirty := false; + out_msg.Acks := in_msg.Acks; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + } + + action(q_sendExclusiveDataFromTBEToCache, "qq", desc="Send data from TBE to cache") { + peek(requestNetwork_in, RequestMsg) { + if (in_msg.RequestorMachine == MachineType:L1Cache) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := TBEs[address].DataBlk; + out_msg.Dirty := TBEs[address].Dirty; + out_msg.Acks := in_msg.Acks; + out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; + } + } + else { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address,machineID)); + out_msg.DataBlk := TBEs[address].DataBlk; + out_msg.Dirty := TBEs[address].Dirty; + out_msg.Acks := in_msg.Acks; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + } + + + // L2 will usually request data for a writeback + action(qq_sendWBDataFromTBEToL2, "\q", desc="Send data from TBE to L2") { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.Dirty := TBEs[address].Dirty; + if (TBEs[address].Dirty) { + out_msg.Type := CoherenceResponseType:WRITEBACK_DIRTY_DATA; + } else { + out_msg.Type := CoherenceResponseType:WRITEBACK_CLEAN_DATA; + } + out_msg.DataBlk := TBEs[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } + } + + action(s_deallocateTBE, "s", desc="Deallocate TBE") { + TBEs.deallocate(address); + } + + action(u_writeDataToCache, "u", desc="Write data to cache") { + peek(responseToL1Cache_in, ResponseMsg) { + getCacheEntry(address).DataBlk := in_msg.DataBlk; + getCacheEntry(address).Dirty := in_msg.Dirty; + + if (in_msg.Type == CoherenceResponseType:DATA) { + //assert(in_msg.Dirty == false); + } + } + + } + + action(v_writeDataToCacheVerify, "v", desc="Write data to cache, assert it was same as before") { + peek(responseToL1Cache_in, ResponseMsg) { + assert(getCacheEntry(address).DataBlk == in_msg.DataBlk); + getCacheEntry(address).DataBlk := in_msg.DataBlk; + getCacheEntry(address).Dirty := in_msg.Dirty; + } + } + + action(kk_deallocateL1CacheBlock, "\k", desc="Deallocate cache block. Sets the cache to invalid, allowing a replacement in parallel with a fetch.") { + if (L1DcacheMemory.isTagPresent(address)) { + L1DcacheMemory.deallocate(address); + } else { + L1IcacheMemory.deallocate(address); + } + } + + action(ii_allocateL1DCacheBlock, "\i", desc="Set L1 D-cache tag equal to tag of block B.") { + if (L1DcacheMemory.isTagPresent(address) == false) { + L1DcacheMemory.allocate(address); + } + } + + action(jj_allocateL1ICacheBlock, "\j", desc="Set L1 I-cache tag equal to tag of block B.") { + if (L1IcacheMemory.isTagPresent(address) == false) { + L1IcacheMemory.allocate(address); + } + } + + + + action(uu_profileMiss, "\u", desc="Profile the demand miss") { + peek(mandatoryQueue_in, CacheMsg) { + profile_miss(in_msg, id); + } + } + + action(z_recycleRequestQueue, "z", desc="Send the head of the mandatory queue to the back of the queue.") { + requestNetwork_in.recycle(); + } + + action(zz_recycleMandatoryQueue, "\z", desc="Send the head of the mandatory queue to the back of the queue.") { + mandatoryQueue_in.recycle(); + } + + //***************************************************** + // TRANSITIONS + //***************************************************** + + // Transitions for Load/Store/L2_Replacement from transient states + transition({IM, SM, OM, IS, OI, SI, MI, II}, {Store, L1_Replacement}) { + zz_recycleMandatoryQueue; + } + + transition({M_W, MM_W}, L1_Replacement) { + zz_recycleMandatoryQueue; + } + + transition({M_W, MM_W}, {Fwd_GETS, Fwd_GETX, Own_GETX, Inv}) { + z_recycleRequestQueue; + } + + transition({IM, IS, OI, MI, SI, II}, {Load, Ifetch}) { + zz_recycleMandatoryQueue; + } + + // Transitions from Idle + transition(I, Load, IS) { + ii_allocateL1DCacheBlock; + i_allocateTBE; + a_issueGETS; + // uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I, Ifetch, IS) { + jj_allocateL1ICacheBlock; + i_allocateTBE; + a_issueGETS; + // uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I, Store, IM) { + ii_allocateL1DCacheBlock; + i_allocateTBE; + b_issueGETX; + // uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I, L1_Replacement) { + kk_deallocateL1CacheBlock; + } + + transition(I, Inv) { + f_sendAck; + l_popForwardQueue; + } + + // Transitions from Shared + transition({S, SM}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(S, Store, SM) { + i_allocateTBE; + b_issueGETX; + // uu_profileMiss; + k_popMandatoryQueue; + } + + transition(S, L1_Replacement, SI) { + i_allocateTBE; + dd_issuePUTS; + kk_deallocateL1CacheBlock; + } + + transition(S, Inv, I) { + f_sendAck; + l_popForwardQueue; + } + + transition(S, Fwd_GETS) { + e_sendData; + l_popForwardQueue; + } + + // Transitions from Owned + transition({O, OM}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(O, Store, OM) { + i_allocateTBE; + b_issueGETX; + // uu_profileMiss; + k_popMandatoryQueue; + } + + transition(O, L1_Replacement, OI) { + i_allocateTBE; + dd_issuePUTO; + kk_deallocateL1CacheBlock; + } + + transition(O, Fwd_GETX, I) { + ee_sendDataExclusive; + l_popForwardQueue; + } + + transition(O, Fwd_GETS) { + e_sendData; + l_popForwardQueue; + } + + // Transitions from MM + transition({MM, MM_W}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition({MM, MM_W}, Store) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(MM, L1_Replacement, MI) { + i_allocateTBE; + d_issuePUTX; + kk_deallocateL1CacheBlock; + } + + transition(MM, Fwd_GETX, I) { + ee_sendDataExclusive; + l_popForwardQueue; + } + + transition(MM, Fwd_GETS, I) { + ee_sendDataExclusive; + l_popForwardQueue; + } + + // Transitions from M + transition({M, M_W}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(M, Store, MM) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(M_W, Store, MM_W) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(M, L1_Replacement, MI) { + i_allocateTBE; + d_issuePUTX; + kk_deallocateL1CacheBlock; + } + + transition(M, Fwd_GETX, I) { + // e_sendData; + ee_sendDataExclusive; + l_popForwardQueue; + } + + transition(M, Fwd_GETS, O) { + e_sendData; + l_popForwardQueue; + } + + // Transitions from IM + + transition(IM, Inv) { + f_sendAck; + l_popForwardQueue; + } + + transition(IM, Ack) { + m_decrementNumberOfMessages; + o_checkForCompletion; + n_popResponseQueue; + } + + transition(IM, {Exclusive_Data, Data}, OM) { + u_writeDataToCache; + m_decrementNumberOfMessages; + o_checkForCompletion; + n_popResponseQueue; + } + + // Transitions from SM + transition(SM, Inv, IM) { + f_sendAck; + l_popForwardQueue; + } + + transition(SM, Ack) { + m_decrementNumberOfMessages; + o_checkForCompletion; + n_popResponseQueue; + } + + transition(SM, {Data, Exclusive_Data}, OM) { + // v_writeDataToCacheVerify; + m_decrementNumberOfMessages; + o_checkForCompletion; + n_popResponseQueue; + } + + transition(SM, Fwd_GETS) { + e_sendData; + l_popForwardQueue; + } + + // Transitions from OM + transition(OM, Own_GETX) { + mm_decrementNumberOfMessages; + o_checkForCompletion; + l_popForwardQueue; + } + + + // transition(OM, Fwd_GETX, OMF) { + transition(OM, Fwd_GETX, IM) { + ee_sendDataExclusive; + l_popForwardQueue; + } + + transition(OM, Fwd_GETS, OM) { + e_sendData; + l_popForwardQueue; + } + + //transition({OM, OMF}, Ack) { + transition(OM, Ack) { + m_decrementNumberOfMessages; + o_checkForCompletion; + n_popResponseQueue; + } + + transition(OM, All_acks, MM_W) { + hh_store_hit; + gg_sendUnblockExclusive; + s_deallocateTBE; + o_scheduleUseTimeout; + j_popTriggerQueue; + } + + transition(MM_W, Use_Timeout, MM) { + jj_unsetUseTimer; + } + + // Transitions from IS + + transition(IS, Inv) { + f_sendAck; + l_popForwardQueue; + } + + transition(IS, Data, S) { + u_writeDataToCache; + m_decrementNumberOfMessages; + h_load_hit; + g_sendUnblock; + s_deallocateTBE; + n_popResponseQueue; + } + + transition(IS, Exclusive_Data, M_W) { + u_writeDataToCache; + m_decrementNumberOfMessages; + h_load_hit; + gg_sendUnblockExclusive; + o_scheduleUseTimeout; + s_deallocateTBE; + n_popResponseQueue; + } + + transition(M_W, Use_Timeout, M) { + jj_unsetUseTimer; + } + + // Transitions from OI/MI + + transition(MI, Fwd_GETS, OI) { + q_sendDataFromTBEToCache; + l_popForwardQueue; + } + + transition(MI, Fwd_GETX, II) { + q_sendExclusiveDataFromTBEToCache; + l_popForwardQueue; + } + + transition({SI, OI}, Fwd_GETS) { + q_sendDataFromTBEToCache; + l_popForwardQueue; + } + + transition(OI, Fwd_GETX, II) { + q_sendExclusiveDataFromTBEToCache; + l_popForwardQueue; + } + + transition({SI, OI, MI}, Writeback_Ack_Data, I) { + qq_sendWBDataFromTBEToL2; // always send data + s_deallocateTBE; + l_popForwardQueue; + } + + transition({SI, OI, MI}, Writeback_Ack, I) { + g_sendUnblock; + s_deallocateTBE; + l_popForwardQueue; + } + + transition({MI, OI}, Writeback_Nack, OI) { + // FIXME: This might cause deadlock by re-using the writeback + // channel, we should handle this case differently. + dd_issuePUTO; + l_popForwardQueue; + } + + // Transitions from II + transition(II, {Writeback_Ack, Writeback_Ack_Data}, I) { + g_sendUnblock; + s_deallocateTBE; + l_popForwardQueue; + } + + // transition({II, SI}, Writeback_Nack, I) { + transition(II, Writeback_Nack, I) { + s_deallocateTBE; + l_popForwardQueue; + } + + transition(SI, Writeback_Nack) { + dd_issuePUTS; + l_popForwardQueue; + } + + transition(II, Inv) { + f_sendAck; + l_popForwardQueue; + } + + transition(SI, Inv, II) { + f_sendAck; + l_popForwardQueue; + } +} + diff --git a/src/mem/protocol/MOESI_CMP_directory-L2cache.sm b/src/mem/protocol/MOESI_CMP_directory-L2cache.sm new file mode 100644 index 000000000..fa01f925c --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_directory-L2cache.sm @@ -0,0 +1,2569 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +machine(L2Cache, "Token protocol") { + + // L2 BANK QUEUES + // From local bank of L2 cache TO the network + MessageBuffer L1RequestFromL2Cache, network="To", virtual_network="0", ordered="false"; // this L2 bank -> a local L1 + MessageBuffer GlobalRequestFromL2Cache, network="To", virtual_network="1", ordered="false"; // this L2 bank -> mod-directory + MessageBuffer responseFromL2Cache, network="To", virtual_network="2", ordered="false"; // this L2 bank -> a local L1 || mod-directory + + // FROM the network to this local bank of L2 cache + MessageBuffer L1RequestToL2Cache, network="From", virtual_network="0", ordered="false"; // a local L1 -> this L2 bank, Lets try this??? + MessageBuffer GlobalRequestToL2Cache, network="From", virtual_network="1", ordered="false"; // mod-directory -> this L2 bank + MessageBuffer responseToL2Cache, network="From", virtual_network="2", ordered="false"; // a local L1 || mod-directory -> this L2 bank +// MessageBuffer L1WritebackToL2Cache, network="From", virtual_network="3", ordered="false"; + + // STATES + enumeration(State, desc="L2 Cache states", default="L2Cache_State_I") { + + // Stable states + NP, desc="Not Present"; + I, desc="Invalid"; + ILS, desc="Idle/NP, but local sharers exist"; + ILX, desc="Idle/NP, but local exclusive exists"; + ILO, desc="Idle/NP, but local owner exists"; + ILOX, desc="Idle/NP, but local owner exists and chip is exclusive"; + ILOS, desc="Idle/NP, but local owner exists and local sharers as well"; + ILOSX, desc="Idle/NP, but local owner exists, local sharers exist, chip is exclusive "; + S, desc="Shared, no local sharers"; + O, desc="Owned, no local sharers"; + OLS, desc="Owned with local sharers"; + OLSX, desc="Owned with local sharers, chip is exclusive"; + SLS, desc="Shared with local sharers"; + M, desc="Modified"; + + // Transient States + + IFGX, desc="Blocked, forwarded global GETX to local owner/exclusive. No other on-chip invs needed"; + IFGS, desc="Blocked, forwarded global GETS to local owner"; + ISFGS, desc="Blocked, forwarded global GETS to local owner, local sharers exist"; + // UNUSED + IFGXX, desc="Blocked, forwarded global GETX to local owner but may need acks from other sharers"; + OFGX, desc="Blocked, forwarded global GETX to owner and got data but may need acks"; + + OLSF, desc="Blocked, got Fwd_GETX with local sharers, waiting for local inv acks"; + + // writebacks + ILOW, desc="local WB request, was ILO"; + ILOXW, desc="local WB request, was ILOX"; + ILOSW, desc="local WB request, was ILOS"; + ILOSXW, desc="local WB request, was ILOSX"; + SLSW, desc="local WB request, was SLS"; + OLSW, desc="local WB request, was OLS"; + ILSW, desc="local WB request, was ILS"; + IW, desc="local WB request from only sharer, was ILS"; + OW, desc="local WB request from only sharer, was OLS"; + SW, desc="local WB request from only sharer, was SLS"; + OXW, desc="local WB request from only sharer, was OLSX"; + OLSXW, desc="local WB request from sharer, was OLSX"; + ILXW, desc="local WB request, was ILX"; + + IFLS, desc="Blocked, forwarded local GETS to _some_ local sharer"; + IFLO, desc="Blocked, forwarded local GETS to local owner"; + IFLOX, desc="Blocked, forwarded local GETS to local owner but chip is exclusive"; + IFLOXX, desc="Blocked, forwarded local GETX to local owner/exclusive, chip is exclusive"; + IFLOSX, desc="Blocked, forwarded local GETS to local owner w/ other sharers, chip is exclusive"; + IFLXO, desc="Blocked, forwarded local GETX to local owner with other sharers, chip is exclusive"; + + IGS, desc="Semi-blocked, issued local GETS to directory"; + IGM, desc="Blocked, issued local GETX to directory. Need global acks and data"; + IGMLS, desc="Blocked, issued local GETX to directory but may need to INV local sharers"; + IGMO, desc="Blocked, have data for local GETX but need all acks"; + IGMIO, desc="Blocked, issued local GETX, local owner with possible local sharer, may need to INV"; + OGMIO, desc="Blocked, issued local GETX, was owner, may need to INV"; + IGMIOF, desc="Blocked, issued local GETX, local owner, waiting for global acks, got Fwd_GETX"; + IGMIOFS, desc="Blocked, issued local GETX, local owner, waiting for global acks, got Fwd_GETS"; + OGMIOF, desc="Blocked, issued local GETX, was owner, waiting for global acks, got Fwd_GETX"; + + II, desc="Blocked, handling invalidations"; + MM, desc="Blocked, was M satisfying local GETX"; + SS, desc="Blocked, was S satisfying local GETS"; + OO, desc="Blocked, was O satisfying local GETS"; + OLSS, desc="Blocked, satisfying local GETS"; + OLSXS, desc="Blocked, satisfying local GETS"; + SLSS, desc="Blocked, satisfying local GETS"; + + OI, desc="Blocked, doing writeback, was O"; + MI, desc="Blocked, doing writeback, was M"; + MII, desc="Blocked, doing writeback, was M, got Fwd_GETX"; + OLSI, desc="Blocked, doing writeback, was OLS"; + ILSI, desc="Blocked, doing writeback, was OLS got Fwd_GETX"; + } + + // EVENTS + enumeration(Event, desc="Cache events") { + + // Requests + L1_GETS, desc="local L1 GETS request"; + L1_GETX, desc="local L1 GETX request"; + L1_PUTO, desc="local owner wants to writeback"; + L1_PUTX, desc="local exclusive wants to writeback"; + L1_PUTS_only, desc="only local sharer wants to writeback"; + L1_PUTS, desc="local sharer wants to writeback"; + Fwd_GETX, desc="A GetX from another processor"; + Fwd_GETS, desc="A GetS from another processor"; + Own_GETX, desc="A GetX from this node"; + Inv, desc="Invalidations from the directory"; + + // Responses + IntAck, desc="Received an ack message"; + ExtAck, desc="Received an ack message"; + All_Acks, desc="Received all ack messages"; + Data, desc="Received a data message, responder has a shared copy"; + Data_Exclusive, desc="Received a data message"; + L1_WBCLEANDATA, desc="Writeback from L1, with data"; + L1_WBDIRTYDATA, desc="Writeback from L1, with data"; + + Writeback_Ack, desc="Writeback O.K. from directory"; + Writeback_Nack, desc="Writeback not O.K. from directory"; + + Unblock, desc="Local L1 is telling L2 dir to unblock"; + Exclusive_Unblock, desc="Local L1 is telling L2 dir to unblock"; + + + // events initiated by this L2 + L2_Replacement, desc="L2 Replacement", format="!r"; + + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + NetDest Sharers, desc="Set of the internal processors that want the block in shared state"; + MachineID Owner, desc="ID of the L1 cache to forward the block to once we get a response"; + bool OwnerValid, default="false", desc="true if Owner means something"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + DataBlock DataBlk, desc="data for the block"; + } + + + structure(DirEntry, desc="...") { + NetDest Sharers, desc="Set of the internal processors that want the block in shared state"; + MachineID Owner, desc="ID of the L1 cache to forward the block to once we get a response"; + bool OwnerValid, default="false", desc="true if Owner means something"; + State DirState, desc="directory state"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + Address PC, desc="Program counter of request"; + DataBlock DataBlk, desc="Buffer for the data block"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + + int NumExtPendingAcks, default="0", desc="Number of global acks/data messages waiting for"; + int NumIntPendingAcks, default="0", desc="Number of global acks/data messages waiting for"; + int Fwd_GETX_ExtAcks, default="0", desc="Number of acks that requestor will need"; + int Local_GETX_IntAcks, default="0", desc="Number of acks that requestor will need"; + + NetDest L1_GetS_IDs, desc="Set of the internal processors that want the block in shared state"; + MachineID L1_GetX_ID, desc="ID of the L1 cache to forward the block to once we get a response"; + NetDest Fwd_GetS_IDs, desc="Set of the internal processors that want the block in shared state"; + MachineID Fwd_GetX_ID, desc="ID of the L1 cache to forward the block to once we get a response"; + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + void setMRU(Address); + } + + external_type(PerfectCacheMemory) { + void allocate(Address); + void deallocate(Address); + DirEntry lookup(Address); + bool isTagPresent(Address); + } + + + TBETable L2_TBEs, template_hack=""; + CacheMemory L2cacheMemory, template_hack="", constructor_hack='L2_CACHE_NUM_SETS_BITS,L2_CACHE_ASSOC,MachineType_L2Cache,int_to_string(i)+"_L2"'; + PerfectCacheMemory localDirectory, template_hack=""; + + + Entry getL2CacheEntry(Address addr), return_by_ref="yes" { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory[addr]; + } + } + + void changePermission(Address addr, AccessPermission permission) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory.changePermission(addr, permission); + } + } + + bool isCacheTagPresent(Address addr) { + return (L2cacheMemory.isTagPresent(addr) ); + } + + bool isDirTagPresent(Address addr) { + return (localDirectory.isTagPresent(addr) ); + } + + bool isOnlySharer(Address addr, MachineID shar_id) { + if (isCacheTagPresent(addr)) { + assert (localDirectory.isTagPresent(addr) == false); + if (L2cacheMemory[addr].Sharers.count() > 1) { + return false; + } + else if (L2cacheMemory[addr].Sharers.count() == 1) { + if (L2cacheMemory[addr].Sharers.isElement(shar_id)) { + return true; + } + else { + return false; // something happened which should cause this PUTS to be nacked + } + return true; + } + else { + return false; + } + } + else if (localDirectory.isTagPresent(addr)){ + if (localDirectory[addr].Sharers.count() > 1) { + return false; + } + else if (localDirectory[addr].Sharers.count() == 1) { + if (localDirectory[addr].Sharers.isElement(shar_id)) { + return true; + } + else { + return false; // something happened which should cause this PUTS to be nacked + } + } + else { + return false; + } + } + else { + // shouldn't happen unless L1 issues PUTS before unblock received + return false; + } + } + + void copyCacheStateToDir(Address addr) { + assert(localDirectory.isTagPresent(addr) == false); + localDirectory.allocate(addr); + localDirectory[addr].DirState := L2cacheMemory[addr].CacheState; + localDirectory[addr].Sharers := L2cacheMemory[addr].Sharers; + localDirectory[addr].Owner := L2cacheMemory[addr].Owner; + localDirectory[addr].OwnerValid := L2cacheMemory[addr].OwnerValid; + + } + + void copyDirToCache(Address addr) { + L2cacheMemory[addr].Sharers := localDirectory[addr].Sharers; + L2cacheMemory[addr].Owner := localDirectory[addr].Owner; + L2cacheMemory[addr].OwnerValid := localDirectory[addr].OwnerValid; + } + + + void recordLocalSharerInDir(Address addr, MachineID shar_id) { + if (isCacheTagPresent(addr)) { + assert (localDirectory.isTagPresent(addr) == false); + L2cacheMemory[addr].Sharers.add(shar_id); + } + else { + if (localDirectory.isTagPresent(addr) == false) { + localDirectory.allocate(addr); + localDirectory[addr].Sharers.clear(); + localDirectory[addr].OwnerValid := false; + } + localDirectory[addr].Sharers.add(shar_id); + } + } + + void recordNewLocalExclusiveInDir(Address addr, MachineID exc_id) { + + if (isCacheTagPresent(addr)) { + assert (localDirectory.isTagPresent(addr) == false); + L2cacheMemory[addr].Sharers.clear(); + L2cacheMemory[addr].OwnerValid := true; + L2cacheMemory[addr].Owner := exc_id; + } + else { + if (localDirectory.isTagPresent(addr) == false) { + localDirectory.allocate(addr); + } + localDirectory[addr].Sharers.clear(); + localDirectory[addr].OwnerValid := true; + localDirectory[addr].Owner := exc_id; + } + } + + + void removeAllLocalSharersFromDir(Address addr) { + if (isCacheTagPresent(addr)) { + assert (localDirectory.isTagPresent(addr) == false); + L2cacheMemory[addr].Sharers.clear(); + L2cacheMemory[addr].OwnerValid := false; + } + else { + localDirectory[addr].Sharers.clear(); + localDirectory[addr].OwnerValid := false; + } + } + + void removeSharerFromDir(Address addr, MachineID sender) { + if (isCacheTagPresent(addr)) { + assert (localDirectory.isTagPresent(addr) == false); + L2cacheMemory[addr].Sharers.remove(sender); + } + else { + localDirectory[addr].Sharers.remove(sender); + } + } + + void removeOwnerFromDir(Address addr, MachineID sender) { + if (isCacheTagPresent(addr)) { + assert (localDirectory.isTagPresent(addr) == false); + L2cacheMemory[addr].OwnerValid := false; + } + else { + localDirectory[addr].OwnerValid := false; + } + } + + bool isLocalSharer(Address addr, MachineID shar_id) { + if (isCacheTagPresent(addr)) { + assert (localDirectory.isTagPresent(addr) == false); + return L2cacheMemory[addr].Sharers.isElement(shar_id); + } + else { + return localDirectory[addr].Sharers.isElement(shar_id); + } + + } + + NetDest getLocalSharers(Address addr) { + if (isCacheTagPresent(addr)) { + assert (localDirectory.isTagPresent(addr) == false); + return L2cacheMemory[addr].Sharers; + } + else { + return localDirectory[addr].Sharers; + } + + } + + MachineID getLocalOwner(Address addr) { + if (isCacheTagPresent(addr)) { + assert (localDirectory.isTagPresent(addr) == false); + return L2cacheMemory[addr].Owner; + } + else { + return localDirectory[addr].Owner; + } + + } + + + int countLocalSharers(Address addr) { + if (isCacheTagPresent(addr)) { + assert (localDirectory.isTagPresent(addr) == false); + return L2cacheMemory[addr].Sharers.count(); + } + else { + return localDirectory[addr].Sharers.count(); + } + } + + bool isLocalOwnerValid(Address addr) { + if (isCacheTagPresent(addr)) { + assert (localDirectory.isTagPresent(addr) == false); + return L2cacheMemory[addr].OwnerValid; + } + else { + return localDirectory[addr].OwnerValid; + } + } + + int countLocalSharersExceptRequestor(Address addr, MachineID requestor) { + if (isCacheTagPresent(addr)) { + assert (localDirectory.isTagPresent(addr) == false); + if (L2cacheMemory[addr].Sharers.isElement(requestor)) { + return ( L2cacheMemory[addr].Sharers.count() - 1 ); + } + else { + return L2cacheMemory[addr].Sharers.count(); + } + } + else { + if (localDirectory[addr].Sharers.isElement(requestor)) { + return ( localDirectory[addr].Sharers.count() - 1 ); + } + else { + return localDirectory[addr].Sharers.count(); + } + } + } + + + + State getState(Address addr) { + + if (L2_TBEs.isPresent(addr)) { + return L2_TBEs[addr].TBEState; + } else if (isCacheTagPresent(addr)) { + return getL2CacheEntry(addr).CacheState; + } else if (isDirTagPresent(addr)) { + return localDirectory[addr].DirState; + } else { + return State:NP; + } + } + + string getStateStr(Address addr) { + return L2Cache_State_to_string(getState(addr)); + } + + string getCoherenceRequestTypeStr(CoherenceRequestType type) { + return CoherenceRequestType_to_string(type); + } + + + void setState(Address addr, State state) { + assert((localDirectory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + + if (L2_TBEs.isPresent(addr)) { + L2_TBEs[addr].TBEState := state; + } + + if ( + (state == State:M) || + (state == State:O) || + (state == State:S) || + (state == State:OLS) || + (state == State:SLS) || + (state == State:OLSX) || + (state == State:SLS) + ) { + assert(isCacheTagPresent(addr)); + } + else if ( + (state == State:ILS) || + (state == State:ILX) || + (state == State:ILO) || + (state == State:ILOX) || + (state == State:ILOS) || + (state == State:ILOSX) + ) { + // assert(isCacheTagPresent(addr) == false); + } + + + + if (isCacheTagPresent(addr)) { + if ( ((getL2CacheEntry(addr).CacheState != State:M) && (state == State:M)) || + ((getL2CacheEntry(addr).CacheState != State:S) && (state == State:S)) || + ((getL2CacheEntry(addr).CacheState != State:O) && (state == State:O)) ) { + getL2CacheEntry(addr).CacheState := state; + // disable Coherence Checker for now + // sequencer.checkCoherence(addr); + } + else { + getL2CacheEntry(addr).CacheState := state; + } + + // Set permission + changePermission(addr, AccessPermission:Read_Only); + } + else if (localDirectory.isTagPresent(addr)) { + localDirectory[addr].DirState := state; + } + + } + + + bool isBlockExclusive(Address addr) { + if (isCacheTagPresent(addr)) { + // the list of exclusive states below is likely incomplete + if ( (getL2CacheEntry(addr).CacheState == State:M) || + (getL2CacheEntry(addr).CacheState == State:MI) ) { + return true; + } + } + + return false; + } + + bool isBlockShared(Address addr) { + if (isCacheTagPresent(addr)) { + // the list of shared states below is likely incomplete + if ( (getL2CacheEntry(addr).CacheState == State:S) || + (getL2CacheEntry(addr).CacheState == State:O) || + (getL2CacheEntry(addr).CacheState == State:OI) || + (getL2CacheEntry(addr).CacheState == State:OXW) ) { + return true; + } + } + return false; + } + + MessageBuffer triggerQueue, ordered="true"; + + out_port(globalRequestNetwork_out, RequestMsg, GlobalRequestFromL2Cache); + out_port(localRequestNetwork_out, RequestMsg, L1RequestFromL2Cache); + out_port(responseNetwork_out, ResponseMsg, responseFromL2Cache); + + out_port(triggerQueue_out, TriggerMsg, triggerQueue); + + + + // ** IN_PORTS ** + + // Trigger Queue + in_port(triggerQueue_in, TriggerMsg, triggerQueue) { + if (triggerQueue_in.isReady()) { + peek(triggerQueue_in, TriggerMsg) { + if (in_msg.Type == TriggerType:ALL_ACKS) { + trigger(Event:All_Acks, in_msg.Address); + } else { + error("Unexpected message"); + } + } + } + } + + + // Request Network + in_port(requestNetwork_in, RequestMsg, GlobalRequestToL2Cache) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETX) { + if (in_msg.Requestor == machineID) { + trigger(Event:Own_GETX, in_msg.Address); + } else { + trigger(Event:Fwd_GETX, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:Fwd_GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:INV) { + trigger(Event:Inv, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:WB_ACK) { + trigger(Event:Writeback_Ack, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:WB_NACK) { + trigger(Event:Writeback_Nack, in_msg.Address); + } else { + error("Unexpected message"); + } + } + } + } + + in_port(L1requestNetwork_in, RequestMsg, L1RequestToL2Cache) { + if (L1requestNetwork_in.isReady()) { + peek(L1requestNetwork_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:L1_GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:L1_GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTO) { + trigger(Event:L1_PUTO, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTX) { + trigger(Event:L1_PUTX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTS) { + if (isOnlySharer(in_msg.Address, in_msg.Requestor)) { + trigger(Event:L1_PUTS_only, in_msg.Address); + } + else { + trigger(Event:L1_PUTS, in_msg.Address); + } + } else { + error("Unexpected message"); + } + } + } + } + + + // Response Network + in_port(responseNetwork_in, ResponseMsg, responseToL2Cache) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceResponseType:ACK) { + if (in_msg.SenderMachine == MachineType:L2Cache) { + trigger(Event:ExtAck, in_msg.Address); + } + else { + trigger(Event:IntAck, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:DATA) { + trigger(Event:Data, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE) { + trigger(Event:Data_Exclusive, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:UNBLOCK) { + trigger(Event:Unblock, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:UNBLOCK_EXCLUSIVE) { + trigger(Event:Exclusive_Unblock, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WRITEBACK_DIRTY_DATA) { + if (L2cacheMemory.isTagPresent(in_msg.Address) == false && + L2cacheMemory.cacheAvail(in_msg.Address) == false) { + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } + else { + trigger(Event:L1_WBDIRTYDATA, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:WRITEBACK_CLEAN_DATA) { + if (L2cacheMemory.isTagPresent(in_msg.Address) == false && + L2cacheMemory.cacheAvail(in_msg.Address) == false) { + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } + else { + trigger(Event:L1_WBCLEANDATA, in_msg.Address); + } + } else { + error("Unexpected message"); + } + } + } + } + + + // ACTIONS + + action(a_issueGETS, "a", desc="issue local request globally") { + peek(L1requestNetwork_in, RequestMsg) { + enqueue(globalRequestNetwork_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.RequestorMachine := MachineType:L2Cache; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Request_Control; + } + } + } + + action(a_issueGETX, "\a", desc="issue local request globally") { + peek(L1requestNetwork_in, RequestMsg) { + enqueue(globalRequestNetwork_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.RequestorMachine := MachineType:L2Cache; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Request_Control; + } + } + } + + action(b_issuePUTX, "b", desc="Issue PUTX") { + enqueue(globalRequestNetwork_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTX; + out_msg.RequestorMachine := MachineType:L2Cache; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + + action(b_issuePUTO, "\b", desc="Issue PUTO") { + enqueue(globalRequestNetwork_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTO; + out_msg.Requestor := machineID; + out_msg.RequestorMachine := MachineType:L2Cache; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + + /* PUTO, but local sharers exist */ + action(b_issuePUTO_ls, "\bb", desc="Issue PUTO") { + enqueue(globalRequestNetwork_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTO_SHARERS; + out_msg.Requestor := machineID; + out_msg.RequestorMachine := MachineType:L2Cache; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + + action(c_sendDataFromTBEToL1GETS, "c", desc="Send data from TBE to L1 requestors in TBE") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.addNetDest(L2_TBEs[address].L1_GetS_IDs); + out_msg.DataBlk := L2_TBEs[address].DataBlk; + // out_msg.Dirty := L2_TBEs[address].Dirty; + // shared data should be clean + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + DEBUG_EXPR(address); + DEBUG_EXPR(L2_TBEs[address].DataBlk); + } + + action(c_sendDataFromTBEToL1GETX, "\c", desc="Send data from TBE to L1 requestors in TBE") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(L2_TBEs[address].L1_GetX_ID); + out_msg.DataBlk := L2_TBEs[address].DataBlk; + out_msg.Dirty := L2_TBEs[address].Dirty; + out_msg.Acks := L2_TBEs[address].Local_GETX_IntAcks; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + DEBUG_EXPR(address); + DEBUG_EXPR(L2_TBEs[address].DataBlk); + } + + action(c_sendExclusiveDataFromTBEToL1GETS, "\cc", desc="Send data from TBE to L1 requestors in TBE") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.addNetDest(L2_TBEs[address].L1_GetS_IDs); + out_msg.DataBlk := L2_TBEs[address].DataBlk; + out_msg.Dirty := L2_TBEs[address].Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(c_sendDataFromTBEToFwdGETX, "cc", desc="Send data from TBE to external GETX") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(L2_TBEs[address].Fwd_GetX_ID); + out_msg.DataBlk := L2_TBEs[address].DataBlk; + out_msg.Dirty := L2_TBEs[address].Dirty; + out_msg.Acks := L2_TBEs[address].Fwd_GETX_ExtAcks; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + + action(c_sendDataFromTBEToFwdGETS, "ccc", desc="Send data from TBE to external GETX") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.addNetDest(L2_TBEs[address].Fwd_GetS_IDs); + out_msg.DataBlk := L2_TBEs[address].DataBlk; + // out_msg.Dirty := L2_TBEs[address].Dirty; + // shared data should be clean + out_msg.Dirty := false; + out_msg.Acks := L2_TBEs[address].Fwd_GETX_ExtAcks; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + DEBUG_EXPR(address); + DEBUG_EXPR(L2_TBEs[address].DataBlk); + } + + action(c_sendExclusiveDataFromTBEToFwdGETS, "\ccc", desc="Send data from TBE to external GETX") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.addNetDest(L2_TBEs[address].Fwd_GetS_IDs); + out_msg.DataBlk := L2_TBEs[address].DataBlk; + out_msg.Dirty := L2_TBEs[address].Dirty; + out_msg.Acks := L2_TBEs[address].Fwd_GETX_ExtAcks; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + DEBUG_EXPR(address); + DEBUG_EXPR(L2_TBEs[address].DataBlk); + } + + action(d_sendDataToL1GETS, "d", desc="Send data directly to L1 requestor") { + peek(L1requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + // out_msg.Dirty := getL2CacheEntry(address).Dirty; + // shared data should be clean + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:ResponseL2hit_Data; + } + } + DEBUG_EXPR(address); + DEBUG_EXPR(getL2CacheEntry(address).DataBlk); + } + + action(d_sendDataToL1GETX, "\d", desc="Send data and a token from TBE to L1 requestor") { + peek(L1requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:ResponseL2hit_Data; + out_msg.Acks := L2_TBEs[address].Local_GETX_IntAcks; + } + } + DEBUG_EXPR(address); + DEBUG_EXPR(getL2CacheEntry(address).DataBlk); + } + + action(dd_sendDataToFwdGETX, "dd", desc="send data") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + out_msg.Acks := in_msg.Acks; + } + } + DEBUG_EXPR(address); + DEBUG_EXPR(getL2CacheEntry(address).DataBlk); + } + + + action(dd_sendDataToFwdGETS, "\dd", desc="send data") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + // out_msg.Dirty := getL2CacheEntry(address).Dirty; + // shared data should be clean + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + DEBUG_EXPR(address); + DEBUG_EXPR(getL2CacheEntry(address).DataBlk); + } + + action(dd_sendExclusiveDataToFwdGETS, "\d\d", desc="send data") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(e_sendAck, "e", desc="Send ack with the tokens we've collected thus far.") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + + out_msg.Destination.add( L2_TBEs[address].Fwd_GetX_ID); + out_msg.Acks := 0 - 1; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + + action(e_sendAckToL1Requestor, "\e", desc="Send ack with the tokens we've collected thus far.") { + peek(L1requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.Acks := 0 - 1; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + + action(e_sendAckToL1RequestorFromTBE, "eee", desc="Send ack with the tokens we've collected thus far.") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(L2_TBEs[address].L1_GetX_ID); + out_msg.Acks := 0 - 1; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + + action(ee_sendLocalInv, "\ee", desc="Send local invalidates") { + L2_TBEs[address].NumIntPendingAcks := countLocalSharers(address); + DEBUG_EXPR(address); + DEBUG_EXPR(getLocalSharers(address)); + DEBUG_EXPR(id); + DEBUG_EXPR(L2_TBEs[address].NumIntPendingAcks); + if (isLocalOwnerValid(address)) { + L2_TBEs[address].NumIntPendingAcks := L2_TBEs[address].NumIntPendingAcks + 1; + DEBUG_EXPR(getLocalOwner(address)); + } + + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := machineID; + out_msg.RequestorMachine := MachineType:L2Cache; + out_msg.Destination.addNetDest(getLocalSharers(address)); + if (isLocalOwnerValid(address)) + { + out_msg.Destination.add(getLocalOwner(address)); + } + out_msg.MessageSize := MessageSizeType:Invalidate_Control; + } + } + + action(ee_sendLocalInvSharersOnly, "\eee", desc="Send local invalidates to sharers if they exist") { + + // assert(countLocalSharers(address) > 0); + L2_TBEs[address].NumIntPendingAcks := countLocalSharers(address); + + if (countLocalSharers(address) > 0) { + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := machineID; + out_msg.RequestorMachine := MachineType:L2Cache; + out_msg.Destination.addNetDest(getLocalSharers(address)); + out_msg.MessageSize := MessageSizeType:Invalidate_Control; + } + } + } + + action(ee_addLocalIntAck, "e\ee", desc="add a local ack to wait for") { + L2_TBEs[address].NumIntPendingAcks := L2_TBEs[address].NumIntPendingAcks + 1; + } + + action(ee_issueLocalInvExceptL1Requestor, "\eeee", desc="Send local invalidates to sharers if they exist") { + peek(L1requestNetwork_in, RequestMsg) { + +// assert(countLocalSharers(address) > 0); + if (countLocalSharers(address) == 0) { + L2_TBEs[address].NumIntPendingAcks := 0; + } + else { + + if (isLocalSharer(address, in_msg.Requestor)) { + L2_TBEs[address].NumIntPendingAcks := countLocalSharers(address) - 1; + } + else { + L2_TBEs[address].NumIntPendingAcks := countLocalSharers(address); + } + + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := in_msg.Requestor; + out_msg.RequestorMachine := MachineType:L1Cache; + out_msg.Destination.addNetDest(getLocalSharers(address)); + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Invalidate_Control; + } + } + } + } + + action(ee_issueLocalInvExceptL1RequestorInTBE, "\eeeeee", desc="Send local invalidates to sharers if they exist") { + if (countLocalSharers(address) == 0) { + L2_TBEs[address].NumIntPendingAcks := 0; + } + else { + if (isLocalSharer(address, L2_TBEs[address].L1_GetX_ID)) { + L2_TBEs[address].NumIntPendingAcks := countLocalSharers(address) - 1; + } + else { + L2_TBEs[address].NumIntPendingAcks := countLocalSharers(address); + } + } + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := L2_TBEs[address].L1_GetX_ID; + out_msg.RequestorMachine := MachineType:L1Cache; + out_msg.Destination.addNetDest(getLocalSharers(address)); + out_msg.Destination.remove(L2_TBEs[address].L1_GetX_ID); + out_msg.MessageSize := MessageSizeType:Invalidate_Control; + } + } + + + action(f_sendUnblock, "f", desc="Send unblock to global directory") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:UNBLOCK; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.MessageSize := MessageSizeType:Unblock_Control; + } + } + + + action(f_sendExclusiveUnblock, "\f", desc="Send unblock to global directory") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:UNBLOCK_EXCLUSIVE; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.MessageSize := MessageSizeType:Unblock_Control; + } + } + + + action(g_recordLocalSharer, "g", desc="Record new local sharer from unblock message") { + peek(responseNetwork_in, ResponseMsg) { + recordLocalSharerInDir(in_msg.Address, in_msg.Sender); + } + } + + action(g_recordLocalExclusive, "\g", desc="Record new local exclusive sharer from unblock message") { + peek(responseNetwork_in, ResponseMsg) { + recordNewLocalExclusiveInDir(address, in_msg.Sender); + } + } + + action(gg_clearLocalSharers, "gg", desc="Clear local sharers") { + removeAllLocalSharersFromDir(address); + } + + action(gg_clearSharerFromL1Response, "\gg", desc="Clear sharer from L1 response queue") { + peek(responseNetwork_in, ResponseMsg) { + removeSharerFromDir(in_msg.Address, in_msg.Sender); + } + } + + action(gg_clearOwnerFromL1Response, "g\g", desc="Clear sharer from L1 response queue") { + peek(responseNetwork_in, ResponseMsg) { + removeOwnerFromDir(in_msg.Address, in_msg.Sender); + } + } + + action(h_countLocalSharersExceptRequestor, "h", desc="counts number of acks needed for L1 GETX") { + peek(L1requestNetwork_in, RequestMsg) { + L2_TBEs[address].Local_GETX_IntAcks := countLocalSharersExceptRequestor(address, in_msg.Requestor); + } + } + + action(h_clearIntAcks, "\h", desc="clear IntAcks") { + L2_TBEs[address].Local_GETX_IntAcks := 0; + } + + action(hh_countLocalSharersExceptL1GETXRequestorInTBE, "hh", desc="counts number of acks needed for L1 GETX") { + L2_TBEs[address].Local_GETX_IntAcks := countLocalSharersExceptRequestor(address, L2_TBEs[address].L1_GetX_ID); + } + + action(i_copyDataToTBE, "\i", desc="Copy data from response queue to TBE") { + peek(responseNetwork_in, ResponseMsg) { + L2_TBEs[address].DataBlk := in_msg.DataBlk; + L2_TBEs[address].Dirty := in_msg.Dirty; + } + } + + action(i_allocateTBE, "i", desc="Allocate TBE for internal/external request(isPrefetch=0, number of invalidates=0)") { + check_allocate(L2_TBEs); + L2_TBEs.allocate(address); + if(isCacheTagPresent(address)) { + L2_TBEs[address].DataBlk := getL2CacheEntry(address).DataBlk; + L2_TBEs[address].Dirty := getL2CacheEntry(address).Dirty; + } + L2_TBEs[address].NumIntPendingAcks := 0; // default value + L2_TBEs[address].NumExtPendingAcks := 0; // default value + L2_TBEs[address].Fwd_GetS_IDs.clear(); + L2_TBEs[address].L1_GetS_IDs.clear(); + } + + + + action(j_forwardGlobalRequestToLocalOwner, "j", desc="Forward external request to local owner") { + peek(requestNetwork_in, RequestMsg) { + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := in_msg.Address; + out_msg.Type := in_msg.Type; + out_msg.Requestor := machineID; + out_msg.RequestorMachine := MachineType:L2Cache; + out_msg.Destination.add(getLocalOwner(in_msg.Address)); + out_msg.Type := in_msg.Type; + out_msg.MessageSize := MessageSizeType:Forwarded_Control; + out_msg.Acks := 0 - 1; + } + } + } + + + action(k_forwardLocalGETSToLocalSharer, "k", desc="Forward local request to local sharer/owner") { + peek(L1requestNetwork_in, RequestMsg) { + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := in_msg.Address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.Requestor := in_msg.Requestor; + out_msg.RequestorMachine := MachineType:L1Cache; + // should randomize this so one node doesn't get abused more than others + out_msg.Destination.add(localDirectory[in_msg.Address].Sharers.smallestElement(MachineType:L1Cache)); + out_msg.MessageSize := MessageSizeType:Forwarded_Control; + } + } + } + + action(k_forwardLocalGETXToLocalOwner, "\k", desc="Forward local request to local owner") { + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.Requestor := L2_TBEs[address].L1_GetX_ID; + out_msg.RequestorMachine := MachineType:L1Cache; + out_msg.Destination.add(localDirectory[address].Owner); + out_msg.MessageSize := MessageSizeType:Forwarded_Control; + out_msg.Acks := 1 + L2_TBEs[address].Local_GETX_IntAcks; + } + } + + // same as previous except that it assumes to TBE is present to get number of acks + action(kk_forwardLocalGETXToLocalExclusive, "kk", desc="Forward local request to local owner") { + peek(L1requestNetwork_in, RequestMsg) { + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := in_msg.Address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.Requestor := in_msg.Requestor; + out_msg.RequestorMachine := MachineType:L1Cache; + out_msg.Destination.add(getLocalOwner(in_msg.Address)); + out_msg.MessageSize := MessageSizeType:Forwarded_Control; + out_msg.Acks := 1; + } + } + } + + action(kk_forwardLocalGETSToLocalOwner, "\kk", desc="Forward local request to local owner") { + peek(L1requestNetwork_in, RequestMsg) { + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := in_msg.Address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.Requestor := in_msg.Requestor; + out_msg.RequestorMachine := MachineType:L1Cache; + out_msg.Destination.add(getLocalOwner(in_msg.Address)); + out_msg.MessageSize := MessageSizeType:Forwarded_Control; + } + } + } + + + action(l_writebackAckNeedData, "l", desc="Send writeback ack to L1 requesting data") { + peek(L1requestNetwork_in, RequestMsg) { + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := in_msg.Address; + // out_msg.Type := CoherenceResponseType:WRITEBACK_SEND_DATA; + out_msg.Type := CoherenceRequestType:WB_ACK_DATA; + out_msg.Requestor := machineID; + out_msg.RequestorMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(l_writebackAckDropData, "\l", desc="Send writeback ack to L1 indicating to drop data") { + peek(L1requestNetwork_in, RequestMsg) { + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := in_msg.Address; + // out_msg.Type := CoherenceResponseType:WRITEBACK_ACK; + out_msg.Type := CoherenceRequestType:WB_ACK; + out_msg.Requestor := machineID; + out_msg.RequestorMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(ll_writebackNack, "\ll", desc="Send writeback nack to L1") { + peek(L1requestNetwork_in, RequestMsg) { + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := in_msg.Address; + out_msg.Type := CoherenceRequestType:WB_NACK; + out_msg.Requestor := machineID; + out_msg.RequestorMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(m_popRequestQueue, "m", desc="Pop request queue.") { + requestNetwork_in.dequeue(); + } + + action(m_decrementNumberOfMessagesInt, "\m", desc="Decrement the number of messages for which we're waiting") { + peek(responseNetwork_in, ResponseMsg) { + L2_TBEs[address].NumIntPendingAcks := L2_TBEs[address].NumIntPendingAcks + in_msg.Acks; + } + } + + action(m_decrementNumberOfMessagesExt, "\mmm", desc="Decrement the number of messages for which we're waiting") { + peek(responseNetwork_in, ResponseMsg) { + L2_TBEs[address].NumExtPendingAcks := L2_TBEs[address].NumExtPendingAcks - in_msg.Acks; + } + } + + action(mm_decrementNumberOfMessagesExt, "\mm", desc="Decrement the number of messages for which we're waiting") { + peek(requestNetwork_in, RequestMsg) { + L2_TBEs[address].NumExtPendingAcks := L2_TBEs[address].NumExtPendingAcks - in_msg.Acks; + } + } + + action(n_popResponseQueue, "n", desc="Pop response queue") { + responseNetwork_in.dequeue(); + } + + action(n_popTriggerQueue, "\n", desc="Pop trigger queue.") { + triggerQueue_in.dequeue(); + } + + action(o_popL1RequestQueue, "o", desc="Pop L1 request queue.") { + L1requestNetwork_in.dequeue(); + } + + + action(o_checkForIntCompletion, "\o", desc="Check if we have received all the messages required for completion") { + if (L2_TBEs[address].NumIntPendingAcks == 0) { + enqueue(triggerQueue_out, TriggerMsg) { + out_msg.Address := address; + out_msg.Type := TriggerType:ALL_ACKS; + } + } + } + + action(o_checkForExtCompletion, "\oo", desc="Check if we have received all the messages required for completion") { + if (L2_TBEs[address].NumExtPendingAcks == 0) { + enqueue(triggerQueue_out, TriggerMsg) { + out_msg.Address := address; + out_msg.Type := TriggerType:ALL_ACKS; + } + } + } + + + action( qq_sendDataFromTBEToMemory, "qq", desc="Send data from TBE to directory") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.Dirty := L2_TBEs[address].Dirty; + if (L2_TBEs[address].Dirty) { + out_msg.Type := CoherenceResponseType:WRITEBACK_DIRTY_DATA; + out_msg.DataBlk := L2_TBEs[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } else { + out_msg.Type := CoherenceResponseType:WRITEBACK_CLEAN_ACK; + // NOTE: in a real system this would not send data. We send + // data here only so we can check it at the memory + out_msg.DataBlk := L2_TBEs[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action( r_setMRU, "\rrr", desc="manually set the MRU bit for cache line" ) { + if(isCacheTagPresent(address)) { + L2cacheMemory.setMRU(address); + } + } + + action( s_recordGetXL1ID, "ss", desc="record local GETX requestor") { + peek(L1requestNetwork_in, RequestMsg) { + L2_TBEs[address].L1_GetX_ID := in_msg.Requestor; + } + } + + action(s_deallocateTBE, "s", desc="Deallocate external TBE") { + L2_TBEs.deallocate(address); + } + + action( s_recordGetSL1ID, "\ss", desc="record local GETS requestor") { + peek(L1requestNetwork_in, RequestMsg) { + L2_TBEs[address].L1_GetS_IDs.add(in_msg.Requestor); + } + } + + action(t_recordFwdXID, "t", desc="record global GETX requestor") { + peek(requestNetwork_in, RequestMsg) { + L2_TBEs[address].Fwd_GetX_ID := in_msg.Requestor; + L2_TBEs[address].Fwd_GETX_ExtAcks := in_msg.Acks; + } + } + + action(t_recordFwdSID, "\t", desc="record global GETS requestor") { + peek(requestNetwork_in, RequestMsg) { + L2_TBEs[address].Fwd_GetS_IDs.clear(); + L2_TBEs[address].Fwd_GetS_IDs.add(in_msg.Requestor); + } + } + + + action(u_writeDataToCache, "u", desc="Write data to cache") { + peek(responseNetwork_in, ResponseMsg) { + getL2CacheEntry(address).DataBlk := in_msg.DataBlk; + if ((getL2CacheEntry(address).Dirty == false) && in_msg.Dirty) { + getL2CacheEntry(address).Dirty := in_msg.Dirty; + } + } + } + + action(vv_allocateL2CacheBlock, "\v", desc="Set L2 cache tag equal to tag of block B.") { + L2cacheMemory.allocate(address); + } + + action(rr_deallocateL2CacheBlock, "\r", desc="Deallocate L2 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + L2cacheMemory.deallocate(address); + } + + + action(w_assertIncomingDataAndCacheDataMatch, "w", desc="Assert that the incoming data and the data in the cache match") { + peek(responseNetwork_in, ResponseMsg) { + assert(getL2CacheEntry(address).DataBlk == in_msg.DataBlk); + } + } + + action(uu_profileMiss, "\u", desc="Profile the demand miss") { + peek(L1requestNetwork_in, RequestMsg) { + // AccessModeType not implemented + profile_L2Cache_miss(convertToGenericType(in_msg.Type), in_msg.AccessMode, MessageSizeTypeToInt(in_msg.MessageSize), in_msg.Prefetch, machineIDToNodeID(in_msg.Requestor)); + } + } + + + + action(y_copyCacheStateToDir, "y", desc="Copy cache state to directory state") { + + assert(isCacheTagPresent(address)); + copyCacheStateToDir(address); + + } + + action(y_copyDirToCacheAndRemove, "/y", desc="Copy dir state to cache and remove") { + copyDirToCache(address); + localDirectory.deallocate(address); + } + + + action(z_stall, "z", desc="Stall") { + } + + + action(zz_recycleL1RequestQueue, "zz", desc="Send the head of the mandatory queue to the back of the queue.") { + peek(L1requestNetwork_in, RequestMsg) { + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + } + L1requestNetwork_in.recycle(); + } + + action(zz_recycleRequestQueue, "\zz", desc="Send the head of the mandatory queue to the back of the queue.") { + peek(requestNetwork_in, RequestMsg) { + APPEND_TRANSITION_COMMENT(in_msg.Requestor); + } + requestNetwork_in.recycle(); + } + + + action(zz_recycleResponseQueue, "\z\z", desc="Send the head of the mandatory queue to the back of the queue.") { + peek(responseNetwork_in, ResponseMsg) { + APPEND_TRANSITION_COMMENT(in_msg.Sender); + } + responseNetwork_in.recycle(); + } + + + + //***************************************************** + // TRANSITIONS + //***************************************************** + + transition({II, IFGX, IFGS, ISFGS, IFGXX, IFLXO, OFGX, ILOW, ILOXW, ILOSW, ILOSXW, SLSW, OLSW, ILSW, IW, OW, SW, OXW, OLSXW, ILXW, IFLS, IFLO, IFLOX, IFLOXX, IFLOSX, OLSXS, IGS, IGM, IGMLS, IGMO, IGMIO, OGMIO, IGMIOF, OGMIOF, MM, SS, OO, OI, MI, MII, OLSI, ILSI, SLSS, OLSS, OLSF, IGMIOFS}, {L1_PUTO, L1_PUTS, L1_PUTS_only, L1_PUTX}) { + zz_recycleL1RequestQueue; + } + + transition({II, IFGX, IFGS, ISFGS, IFGXX, IFLXO, OFGX, ILOW, ILOXW, ILOSW, ILOSXW, SLSW, OLSW, ILSW, IW, OW, SW, OXW, OLSXW, ILXW, IFLS, IFLO, IFLOX, IFLOXX, IFLOSX, OLSXS, IGS, IGM, IGMLS, IGMO, IGMIO, OGMIO, IGMIOF, OGMIOF, MM, SS, OO, OI, MI, MII, OLSI, ILSI, SLSS, OLSS, OLSF, IGMIOFS}, {L1_GETX, L1_GETS}) { + zz_recycleL1RequestQueue; + } + + transition({IFGX, IFGS, ISFGS, IFGXX, IFLXO, OFGX, ILOW, ILOXW, ILOSW, ILOSXW, SLSW, OLSW, ILSW, IW, ILXW, OW, SW, OXW, OLSXW, IFLS, IFLO, IFLOX, IFLOXX, IFLOSX,OLSXS, IGS, IGM, IGMLS, IGMO, MM, SS, OO, OI, MI, MII, OLSI, ILSI, SLSS, OLSS, OLSF, IGMIOFS}, L2_Replacement) { + zz_recycleResponseQueue; + } + + transition({IFGX, IFGS, ISFGS, IFGXX, IFLXO, OFGX, ILOW, ILOXW, ILOSW, ILOSXW, SLSW, OLSW, ILSW, IW, OW, SW, OXW, OLSXW, ILXW, IFLS, IFLO, IFLOX, IFLOXX, IFLOSX,OLSXS, MM, SS, OO, SLSS, OLSS, OLSF, IGMIOFS}, {Fwd_GETX, Fwd_GETS, Inv}) { + zz_recycleRequestQueue; + } + + // must happened because we forwarded GETX to local exclusive trying to do wb + transition({I, M, O, ILS, ILOX, OLS, OLSX, SLS, S}, L1_PUTX) { + ll_writebackNack; + o_popL1RequestQueue; + } + + transition({M}, {L1_PUTS, L1_PUTO} ) { + ll_writebackNack; + o_popL1RequestQueue; + } + + transition({ILS, OLSX}, L1_PUTO){ + ll_writebackNack; + o_popL1RequestQueue; + } + +// happened if we forwarded GETS to exclusive who tried to do writeback +// ?? should we just Nack these instead? Could be a bugs here + transition(ILO, L1_PUTX, ILOW) { + l_writebackAckNeedData; + o_popL1RequestQueue; + } + + // this can happen if we forwarded a L1_GETX to exclusiver after it issued a PUTX + transition(ILOS, L1_PUTX, ILOSW) { + l_writebackAckNeedData; + o_popL1RequestQueue; + } + + transition(ILOSX, L1_PUTX, ILOSXW) { + l_writebackAckNeedData; + o_popL1RequestQueue; + } + + // must happened because we got Inv when L1 attempted PUTS + transition(I, L1_PUTS) { + ll_writebackNack; + o_popL1RequestQueue; + } + + transition(I, L1_PUTO) { + ll_writebackNack; + o_popL1RequestQueue; + } + + // FORWARDED REQUESTS + + transition({ILO, ILX, ILOX}, Fwd_GETS, IFGS) { + i_allocateTBE; + t_recordFwdSID; + j_forwardGlobalRequestToLocalOwner; + m_popRequestQueue; + } + + transition({ILOS, ILOSX}, Fwd_GETS, ISFGS) { + i_allocateTBE; + t_recordFwdSID; + j_forwardGlobalRequestToLocalOwner; + m_popRequestQueue; + } + + transition(IFGS, Data, ILO) { + i_copyDataToTBE; + c_sendDataFromTBEToFwdGETS; + s_deallocateTBE; + n_popResponseQueue; + } + + transition(ISFGS, Data, ILOS) { + i_copyDataToTBE; + c_sendDataFromTBEToFwdGETS; + s_deallocateTBE; + n_popResponseQueue; + } + + transition(IFGS, Data_Exclusive, I) { + i_copyDataToTBE; + c_sendExclusiveDataFromTBEToFwdGETS; + gg_clearLocalSharers; + s_deallocateTBE; + n_popResponseQueue; + } + + + transition({ILX, ILO, ILOX}, Fwd_GETX, IFGX) { + i_allocateTBE; + t_recordFwdXID; + j_forwardGlobalRequestToLocalOwner; + m_popRequestQueue; + } + + transition(IFGX, {Data_Exclusive, Data}, I) { + i_copyDataToTBE; + c_sendDataFromTBEToFwdGETX; + gg_clearLocalSharers; + s_deallocateTBE; + n_popResponseQueue; + } + + transition({ILOSX, ILOS}, Fwd_GETX, IFGXX) { + i_allocateTBE; + t_recordFwdXID; + j_forwardGlobalRequestToLocalOwner; + ee_sendLocalInvSharersOnly; + ee_addLocalIntAck; + m_popRequestQueue; + } + + + transition(IFGXX, IntAck) { + m_decrementNumberOfMessagesInt; + o_checkForIntCompletion; + n_popResponseQueue; + } + + transition(IFGXX, Data_Exclusive) { + i_copyDataToTBE; + m_decrementNumberOfMessagesInt; + o_checkForIntCompletion; + n_popResponseQueue; + } + + transition(IFGXX, All_Acks, I) { + c_sendDataFromTBEToFwdGETX; + gg_clearLocalSharers; + s_deallocateTBE; + n_popTriggerQueue; + } + + + // transition({O, OX}, Fwd_GETX, I) { + transition(O, Fwd_GETX, I) { + dd_sendDataToFwdGETX; + y_copyCacheStateToDir; + rr_deallocateL2CacheBlock; + m_popRequestQueue; + } + + transition({O, OLS}, Fwd_GETS) { + dd_sendDataToFwdGETS; + m_popRequestQueue; + } + + // transition({OLSX, OX}, Fwd_GETS, O) { + transition(OLSX, Fwd_GETS, OLS) { + dd_sendDataToFwdGETS; + m_popRequestQueue; + } + + + transition(M, Fwd_GETX, I) { + dd_sendDataToFwdGETX; + rr_deallocateL2CacheBlock; + m_popRequestQueue; + } + + // MAKE THIS THE SAME POLICY FOR NOW + + // transition(M, Fwd_GETS, O) { + // dd_sendDataToFwdGETS; + // m_popRequestQueue; + // } + + transition(M, Fwd_GETS, I) { + dd_sendExclusiveDataToFwdGETS; + rr_deallocateL2CacheBlock; + m_popRequestQueue; + } + + + transition({OLS, OLSX}, Fwd_GETX, OLSF) { + i_allocateTBE; + t_recordFwdXID; + ee_sendLocalInv; + m_popRequestQueue; + } + + transition(OLSF, IntAck) { + m_decrementNumberOfMessagesInt; + o_checkForIntCompletion; + n_popResponseQueue; + } + + transition(OLSF, All_Acks, I) { + c_sendDataFromTBEToFwdGETX; + gg_clearLocalSharers; + s_deallocateTBE; + rr_deallocateL2CacheBlock; + n_popTriggerQueue; + } + + + + // INVALIDATIONS FROM GLOBAL DIRECTORY + + transition({IGM, IGS}, Inv) { + t_recordFwdXID; + e_sendAck; + m_popRequestQueue; + } + + transition({I,NP}, Inv) { + i_allocateTBE; + t_recordFwdXID; + e_sendAck; + s_deallocateTBE; + m_popRequestQueue; + } + + // NEED INV for S state + + transition({ILS, ILO, ILX}, Inv, II) { + i_allocateTBE; + t_recordFwdXID; + ee_sendLocalInv; + gg_clearLocalSharers; + m_popRequestQueue; + } + + transition(SLS, Inv, II) { + i_allocateTBE; + t_recordFwdXID; + ee_sendLocalInv; + rr_deallocateL2CacheBlock; + m_popRequestQueue; + } + + transition(II, IntAck) { + m_decrementNumberOfMessagesInt; + o_checkForIntCompletion; + n_popResponseQueue; + } + + transition(II, All_Acks, I) { + e_sendAck; + s_deallocateTBE; + n_popTriggerQueue; + } + + transition(S, Inv, I) { + i_allocateTBE; + t_recordFwdXID; + e_sendAck; + s_deallocateTBE; + rr_deallocateL2CacheBlock; + m_popRequestQueue; + } + + + // LOCAL REQUESTS SATISFIED LOCALLY + + transition(OLSX, L1_GETX, IFLOX) { + i_allocateTBE; + s_recordGetXL1ID; + // count number of INVs needed that doesn't include requestor + h_countLocalSharersExceptRequestor; + // issue INVs to everyone except requestor + ee_issueLocalInvExceptL1Requestor; + d_sendDataToL1GETX + y_copyCacheStateToDir; + r_setMRU; + rr_deallocateL2CacheBlock; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(IFLOX, Exclusive_Unblock, ILX) { + g_recordLocalExclusive; + s_deallocateTBE; + n_popResponseQueue; + } + + transition(OLSX, L1_GETS, OLSXS) { + d_sendDataToL1GETS; + r_setMRU; + o_popL1RequestQueue; + } + + transition(OLSXS, Unblock, OLSX) { + g_recordLocalSharer; + n_popResponseQueue; + } + + // after this, can't get Fwd_GETX + transition(IGMO, Own_GETX) { + mm_decrementNumberOfMessagesExt; + o_checkForExtCompletion; + m_popRequestQueue; + + } + + + transition(ILX, L1_GETS, IFLOXX) { + kk_forwardLocalGETSToLocalOwner; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(ILOSX, L1_GETS, IFLOSX) { + kk_forwardLocalGETSToLocalOwner; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition({ILOS, ILO}, L1_GETS, IFLO) { + kk_forwardLocalGETSToLocalOwner; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(ILS, L1_GETS, IFLS) { + k_forwardLocalGETSToLocalSharer; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition({ILX, ILOX}, L1_GETX, IFLOXX) { + kk_forwardLocalGETXToLocalExclusive; + e_sendAckToL1Requestor; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(ILOX, L1_GETS, IFLOX) { + kk_forwardLocalGETSToLocalOwner; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(IFLOX, Unblock, ILOSX) { + g_recordLocalSharer; + n_popResponseQueue; + } + + transition(IFLS, Unblock, ILS) { + g_recordLocalSharer; + n_popResponseQueue; + } + + transition(IFLOXX, Unblock, ILOSX) { + g_recordLocalSharer; + n_popResponseQueue; + } + + transition(IFLOSX, Unblock, ILOSX) { + g_recordLocalSharer; + n_popResponseQueue; + } + + transition({IFLOSX, IFLOXX}, Exclusive_Unblock, ILX) { + g_recordLocalExclusive; + n_popResponseQueue; + } + + transition(IFLO, Unblock, ILOS) { + g_recordLocalSharer; + n_popResponseQueue; + } + + + transition(ILOSX, L1_GETX, IFLXO) { + i_allocateTBE; + s_recordGetXL1ID; + h_countLocalSharersExceptRequestor; + ee_issueLocalInvExceptL1Requestor; + k_forwardLocalGETXToLocalOwner; + e_sendAckToL1RequestorFromTBE; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(IFLXO, Exclusive_Unblock, ILX) { + g_recordLocalExclusive; + s_deallocateTBE; + n_popResponseQueue; + } + + + + // LOCAL REQUESTS THAT MUST ISSUE + + transition(NP, {L1_PUTS, L1_PUTX, L1_PUTO}) { + ll_writebackNack; + o_popL1RequestQueue; + } + + transition({NP, I}, L1_GETS, IGS) { + i_allocateTBE; + s_recordGetSL1ID; + a_issueGETS; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition({NP, I}, L1_GETX, IGM) { + i_allocateTBE; + s_recordGetXL1ID; + a_issueGETX; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(S, L1_GETX, IGM) { + i_allocateTBE; + s_recordGetXL1ID; + a_issueGETX; + y_copyCacheStateToDir; + r_setMRU; + rr_deallocateL2CacheBlock; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(ILS, L1_GETX, IGMLS) { + i_allocateTBE; + s_recordGetXL1ID; + a_issueGETX; + // count number of INVs (just sharers?) needed that doesn't include requestor + h_countLocalSharersExceptRequestor; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(IGMLS, Inv) { + t_recordFwdXID; + ee_sendLocalInv; + m_popRequestQueue; + } + + transition(IGMLS, IntAck) { + m_decrementNumberOfMessagesInt; + o_checkForIntCompletion; + n_popResponseQueue; + } + + transition(IGMLS, All_Acks, IGM) { + gg_clearLocalSharers; + h_clearIntAcks; + e_sendAck; + n_popTriggerQueue; + } + + // transition(IGMLS, ExtAck, IGMO) { + transition(IGMLS, ExtAck) { + m_decrementNumberOfMessagesExt; + o_checkForExtCompletion; + n_popResponseQueue; + } + + transition(IGMLS, {Data, Data_Exclusive}, IGMO) { + ee_issueLocalInvExceptL1RequestorInTBE; + i_copyDataToTBE; + m_decrementNumberOfMessagesExt; + o_checkForExtCompletion; + n_popResponseQueue; + } + + + transition(ILOS, L1_GETX, IGMIO) { + i_allocateTBE; + s_recordGetXL1ID; + a_issueGETX; + uu_profileMiss; + o_popL1RequestQueue; + } + + // new exclusive happened while sharer attempted writeback + transition(ILX, {L1_PUTS, L1_PUTS_only, L1_PUTO}) { + ll_writebackNack; + o_popL1RequestQueue; + } + + transition(S, L1_PUTS) { + ll_writebackNack; + o_popL1RequestQueue; + } + + transition(OLS, L1_GETX, OGMIO) { + i_allocateTBE; + s_recordGetXL1ID; + a_issueGETX; + h_countLocalSharersExceptRequestor; + // COPY DATA FROM CACHE TO TBE (happens during i_allocateTBE) + y_copyCacheStateToDir; + rr_deallocateL2CacheBlock; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(OGMIO, Fwd_GETS) { + t_recordFwdSID; + c_sendDataFromTBEToFwdGETS; + m_popRequestQueue; + } + + transition(ILO, L1_GETX, IGMIO) { + i_allocateTBE; + s_recordGetXL1ID; + a_issueGETX; + // the following, of course, returns 0 sharers but do anyways for consistency + h_countLocalSharersExceptRequestor; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition({ILO, ILOX}, L1_PUTS) { + ll_writebackNack; + o_popL1RequestQueue; + } + + transition(IGMIO, Fwd_GETX, IGMIOF) { + t_recordFwdXID; + j_forwardGlobalRequestToLocalOwner; + ee_sendLocalInvSharersOnly; + ee_addLocalIntAck; + m_popRequestQueue; + } + + transition(IGMIO, Fwd_GETS, IGMIOFS) { + t_recordFwdSID; + j_forwardGlobalRequestToLocalOwner; + m_popRequestQueue; + } + + transition(IGMIOFS, Data, IGMIO) { + i_copyDataToTBE; + c_sendDataFromTBEToFwdGETS; + n_popResponseQueue; + } + + transition(OGMIO, Fwd_GETX, OGMIOF) { + t_recordFwdXID; + ee_sendLocalInvSharersOnly; + m_popRequestQueue; + } + + transition(OGMIOF, IntAck) { + m_decrementNumberOfMessagesInt; + o_checkForIntCompletion; + n_popResponseQueue; + } + + transition(OGMIOF, All_Acks, IGM) { + gg_clearLocalSharers; + hh_countLocalSharersExceptL1GETXRequestorInTBE; + c_sendDataFromTBEToFwdGETX; + n_popTriggerQueue; + } + + transition(IGMIOF, IntAck) { + m_decrementNumberOfMessagesInt; + o_checkForIntCompletion; + n_popResponseQueue; + } + + transition(IGMIOF, Data_Exclusive) { + i_copyDataToTBE; + m_decrementNumberOfMessagesInt; + o_checkForIntCompletion; + n_popResponseQueue; + } + + transition(IGMIOF, All_Acks, IGM) { + gg_clearLocalSharers; + c_sendDataFromTBEToFwdGETX; + n_popTriggerQueue; + } + + transition(IGMIO, All_Acks, IGMO) { + hh_countLocalSharersExceptL1GETXRequestorInTBE; + ee_issueLocalInvExceptL1RequestorInTBE; + k_forwardLocalGETXToLocalOwner; + e_sendAckToL1RequestorFromTBE; + n_popTriggerQueue; + } + + transition(OGMIO, All_Acks, IGMO) { + ee_issueLocalInvExceptL1RequestorInTBE; + c_sendDataFromTBEToL1GETX; + n_popTriggerQueue; + } + + transition({IGMIO, OGMIO}, Own_GETX) { + mm_decrementNumberOfMessagesExt; + o_checkForExtCompletion; + m_popRequestQueue; + + } + + transition(IGM, {Data, Data_Exclusive}, IGMO) { + i_copyDataToTBE; + m_decrementNumberOfMessagesExt; + o_checkForExtCompletion; + n_popResponseQueue; + } + + transition({IGM, IGMIO, OGMIO}, ExtAck) { + m_decrementNumberOfMessagesExt; + o_checkForExtCompletion; + n_popResponseQueue; + } + + transition(IGMO, ExtAck) { + m_decrementNumberOfMessagesExt; + o_checkForExtCompletion; + n_popResponseQueue; + } + + transition(IGS, Data) { + i_copyDataToTBE; + m_decrementNumberOfMessagesExt; + c_sendDataFromTBEToL1GETS; + n_popResponseQueue; + } + + transition(IGS, Data_Exclusive) { + i_copyDataToTBE; + m_decrementNumberOfMessagesExt; + c_sendExclusiveDataFromTBEToL1GETS; + n_popResponseQueue; + } + + transition(IGS, Unblock, ILS) { + g_recordLocalSharer; + f_sendUnblock; + s_deallocateTBE; + n_popResponseQueue; + } + + transition(IGS, Exclusive_Unblock, ILX) { + g_recordLocalExclusive; + f_sendExclusiveUnblock; + s_deallocateTBE; + n_popResponseQueue; + } + + transition(IGMO, All_Acks) { + c_sendDataFromTBEToL1GETX; + n_popTriggerQueue; + } + + transition(IGMO, Exclusive_Unblock, ILX) { + g_recordLocalExclusive; + f_sendExclusiveUnblock; + s_deallocateTBE; + n_popResponseQueue; + } + + + transition(SLS, L1_GETX, IGMLS) { + i_allocateTBE; + s_recordGetXL1ID; + a_issueGETX; + // count number of INVs needed that doesn't include requestor + h_countLocalSharersExceptRequestor; + // issue INVs to everyone except requestor + y_copyCacheStateToDir; + rr_deallocateL2CacheBlock; + uu_profileMiss; + o_popL1RequestQueue; + + } + + transition(SLS, L1_GETS, SLSS ) { + d_sendDataToL1GETS; + r_setMRU; + o_popL1RequestQueue; + } + + transition(SLSS, Unblock, SLS) { + g_recordLocalSharer; + n_popResponseQueue; + } + + + transition(O, L1_GETX, IGMO) { + i_allocateTBE; + s_recordGetXL1ID; + a_issueGETX; + y_copyCacheStateToDir; + rr_deallocateL2CacheBlock; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(OLS, L1_GETS, OLSS) { + d_sendDataToL1GETS; + r_setMRU; + o_popL1RequestQueue; + } + + transition(OLSS, Unblock, OLS) { + g_recordLocalSharer; + n_popResponseQueue; + } + + transition(IGMO, Fwd_GETX, IGM) { + t_recordFwdXID; + c_sendDataFromTBEToFwdGETX; + m_popRequestQueue; + + } + + transition(IGMO, Fwd_GETS) { + t_recordFwdSID; + c_sendDataFromTBEToFwdGETS; + m_popRequestQueue; + } + + + // LOCAL REQUESTS SATISFIED DIRECTLY BY L2 + + transition(M, L1_GETX, MM) { + i_allocateTBE; + // should count 0 of course + h_countLocalSharersExceptRequestor; + d_sendDataToL1GETX + y_copyCacheStateToDir; + rr_deallocateL2CacheBlock; + s_deallocateTBE; + o_popL1RequestQueue; + } + + transition(MM, Exclusive_Unblock, ILX) { + g_recordLocalExclusive; + n_popResponseQueue; + } + + transition(M, L1_GETS, OO) { + i_allocateTBE; + // should count 0 of course + h_countLocalSharersExceptRequestor; + d_sendDataToL1GETX; + r_setMRU; + s_deallocateTBE; + o_popL1RequestQueue; + } + + transition(S, L1_GETS, SS) { + d_sendDataToL1GETS; + r_setMRU; + o_popL1RequestQueue; + } + + transition(SS, Unblock, SLS) { + g_recordLocalSharer; + n_popResponseQueue; + } + + transition(O, L1_GETS, OO) { + d_sendDataToL1GETS; + r_setMRU; + o_popL1RequestQueue; + } + + transition(OO, Unblock, OLS) { + g_recordLocalSharer; + n_popResponseQueue; + } + + transition(OO, Exclusive_Unblock, ILX) { + g_recordLocalExclusive + y_copyCacheStateToDir; + rr_deallocateL2CacheBlock; + n_popResponseQueue; + } + + + // L1 WRITEBACKS + transition(ILO, L1_PUTO, ILOW) { + l_writebackAckNeedData; + o_popL1RequestQueue; + } + + transition(ILOX, L1_PUTO, ILOXW) { + l_writebackAckNeedData; + o_popL1RequestQueue; + } + + + transition(ILOS, L1_PUTO, ILOSW) { + l_writebackAckNeedData; + o_popL1RequestQueue; + } + + transition(ILOSX, L1_PUTO, ILOSXW) { + l_writebackAckNeedData; + o_popL1RequestQueue; + } + + + // hmmm...keep data or drop. Just drop for now + transition(ILOS, L1_PUTS_only, ILOW) { + l_writebackAckDropData; + o_popL1RequestQueue; + } + + transition(ILSW, Unblock, ILS) { + gg_clearSharerFromL1Response; + n_popResponseQueue; + } + + transition(ILOW, Unblock, ILO) { + gg_clearSharerFromL1Response; + n_popResponseQueue; + } + + transition(ILOSX, L1_PUTS_only, ILOXW) { + l_writebackAckDropData; + o_popL1RequestQueue; + } + + transition(ILOXW, Unblock, ILOX) { + gg_clearSharerFromL1Response; + n_popResponseQueue; + } + + // hmmm...keep data or drop. Just drop for now + transition(ILOS, L1_PUTS, ILOSW) { + l_writebackAckDropData; + o_popL1RequestQueue; + } + + transition(ILOSX, L1_PUTS, ILOSXW) { + l_writebackAckDropData; + o_popL1RequestQueue; + } + + transition(ILOSW, Unblock, ILOS) { + gg_clearSharerFromL1Response; + n_popResponseQueue; + } + + transition(ILOSXW, Unblock, ILOSX) { + gg_clearSharerFromL1Response; + n_popResponseQueue; + } + + transition(SLS, L1_PUTS, SLSW) { + l_writebackAckDropData; + o_popL1RequestQueue; + } + + transition(SLS, L1_PUTS_only, SW) { + l_writebackAckDropData; + o_popL1RequestQueue; + } + + transition(SW, {Unblock}, S) { + gg_clearSharerFromL1Response; + n_popResponseQueue; + } + + transition(OLS, L1_PUTS, OLSW) { + l_writebackAckDropData; + o_popL1RequestQueue; + } + + transition(ILS, L1_PUTS, ILSW) { + l_writebackAckNeedData; + o_popL1RequestQueue; + } + + transition(ILS, L1_PUTS_only, IW) { + l_writebackAckNeedData; + o_popL1RequestQueue; + } + + transition(OLS, L1_PUTS_only, OW) { + l_writebackAckDropData; + o_popL1RequestQueue; + } + + transition(OLSX, L1_PUTS_only, OXW) { + l_writebackAckDropData; + o_popL1RequestQueue; + } + + transition(OLSX, L1_PUTS, OLSXW) { + l_writebackAckDropData; + o_popL1RequestQueue; + } + + transition(OLSXW, {Unblock}, OLSX) { + gg_clearSharerFromL1Response; + n_popResponseQueue; + } + + transition(OW, {Unblock}, O) { + gg_clearSharerFromL1Response; + n_popResponseQueue; + } + + transition(OXW, {Unblock}, M) { + gg_clearSharerFromL1Response; + n_popResponseQueue; + } + + transition(ILX, L1_PUTX, ILXW ) { + l_writebackAckNeedData; + o_popL1RequestQueue; + } + + transition(ILXW, L1_WBDIRTYDATA, M) { + gg_clearLocalSharers; + vv_allocateL2CacheBlock; + y_copyDirToCacheAndRemove; + u_writeDataToCache; + n_popResponseQueue; + } + + // clean writeback + transition(ILXW, L1_WBCLEANDATA, M) { + gg_clearLocalSharers; + vv_allocateL2CacheBlock; + y_copyDirToCacheAndRemove; + u_writeDataToCache; + n_popResponseQueue; + } + + transition(ILXW, Unblock, ILX) { + // writeback canceled because L1 invalidated + n_popResponseQueue; + } + + transition(ILSW, L1_WBCLEANDATA, SLS) { + vv_allocateL2CacheBlock; + y_copyDirToCacheAndRemove; + u_writeDataToCache; + gg_clearSharerFromL1Response; + n_popResponseQueue; + } + + transition(IW, L1_WBCLEANDATA, S) { + vv_allocateL2CacheBlock; + y_copyDirToCacheAndRemove; + u_writeDataToCache; + gg_clearSharerFromL1Response; + n_popResponseQueue; + + } + + // Owner can have dirty data + transition(ILOW, {L1_WBCLEANDATA, L1_WBDIRTYDATA}, O) { + vv_allocateL2CacheBlock; + y_copyDirToCacheAndRemove; + gg_clearOwnerFromL1Response; + u_writeDataToCache; + n_popResponseQueue; + } + + transition(ILOXW, L1_WBDIRTYDATA, M) { + vv_allocateL2CacheBlock; + y_copyDirToCacheAndRemove; + gg_clearOwnerFromL1Response; + u_writeDataToCache; + n_popResponseQueue; + } + + transition(ILOXW, L1_WBCLEANDATA, M) { + vv_allocateL2CacheBlock; + y_copyDirToCacheAndRemove; + gg_clearOwnerFromL1Response; + u_writeDataToCache; + n_popResponseQueue; + } + + transition(ILOSW, {L1_WBCLEANDATA, L1_WBDIRTYDATA}, OLS) { + vv_allocateL2CacheBlock; + y_copyDirToCacheAndRemove; + gg_clearOwnerFromL1Response; + u_writeDataToCache; + n_popResponseQueue; + } + + transition(ILOSXW, {L1_WBCLEANDATA, L1_WBDIRTYDATA}, OLSX) { + vv_allocateL2CacheBlock; + y_copyDirToCacheAndRemove; + gg_clearOwnerFromL1Response; + u_writeDataToCache; + n_popResponseQueue; + } + + + transition(SLSW, {Unblock}, SLS) { + gg_clearSharerFromL1Response; + n_popResponseQueue; + } + + transition(OLSW, {Unblock}, OLS) { + gg_clearSharerFromL1Response; + n_popResponseQueue; + } + + + // L2 WRITEBACKS + transition({I, S}, L2_Replacement, I) { + rr_deallocateL2CacheBlock; + } + + transition(ILS, L2_Replacement) { + y_copyCacheStateToDir; + rr_deallocateL2CacheBlock; + } + + transition(ILX, L2_Replacement ) { + y_copyCacheStateToDir; + rr_deallocateL2CacheBlock; + } + + transition({ILO, ILOS}, L2_Replacement ) { + y_copyCacheStateToDir; + rr_deallocateL2CacheBlock; + } + + transition(SLS, L2_Replacement, ILS) { + y_copyCacheStateToDir; + rr_deallocateL2CacheBlock; + } + + transition({OLS, OLSX}, L2_Replacement, OLSI) { + y_copyCacheStateToDir; + b_issuePUTO_ls; + i_allocateTBE; + rr_deallocateL2CacheBlock; + } + + + transition(O, L2_Replacement, OI) { + b_issuePUTO; + i_allocateTBE; + rr_deallocateL2CacheBlock; + } + + transition(M, L2_Replacement, MI) { + b_issuePUTX; + i_allocateTBE; + rr_deallocateL2CacheBlock; + } + + transition(OLSI, Fwd_GETX, ILSI) { + t_recordFwdXID; + ee_sendLocalInv; + m_popRequestQueue; + } + + transition(ILSI, IntAck) { + m_decrementNumberOfMessagesInt; + o_checkForIntCompletion; + n_popResponseQueue; + } + + transition(ILSI, All_Acks, MII) { + gg_clearLocalSharers; + c_sendDataFromTBEToFwdGETX; + n_popTriggerQueue; + } + + transition(OLSI, Fwd_GETS) { + t_recordFwdSID; + c_sendDataFromTBEToFwdGETS; + m_popRequestQueue; + } + + transition({MI, OI}, Fwd_GETS, OI) { + t_recordFwdSID; + c_sendDataFromTBEToFwdGETS; + m_popRequestQueue; + } + + transition({MI, OI}, Fwd_GETX, MII) { + t_recordFwdXID; + c_sendDataFromTBEToFwdGETX; + m_popRequestQueue; + } + + transition({MI, OI}, Writeback_Ack, I) { + qq_sendDataFromTBEToMemory; + s_deallocateTBE; + m_popRequestQueue; + } + + transition(MII, Writeback_Nack, I) { + s_deallocateTBE; + m_popRequestQueue; + } + + transition(OI, Writeback_Nack) { + b_issuePUTO; + m_popRequestQueue; + } + + transition(OLSI, Writeback_Ack, ILS) { + qq_sendDataFromTBEToMemory; + s_deallocateTBE; + m_popRequestQueue; + } + + transition(MII, Writeback_Ack, I) { + f_sendUnblock; + s_deallocateTBE; + m_popRequestQueue; + } + + transition(ILSI, Writeback_Ack, ILS) { + f_sendUnblock; + s_deallocateTBE; + m_popRequestQueue; + } +} + diff --git a/src/mem/protocol/MOESI_CMP_directory-dir.sm b/src/mem/protocol/MOESI_CMP_directory-dir.sm new file mode 100644 index 000000000..a016836c2 --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_directory-dir.sm @@ -0,0 +1,573 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +machine(Directory, "Directory protocol") { + + // ** IN QUEUES ** + MessageBuffer foo1, network="From", virtual_network="0", ordered="false"; // a mod-L2 bank -> this Dir + MessageBuffer requestToDir, network="From", virtual_network="1", ordered="false"; // a mod-L2 bank -> this Dir + MessageBuffer responseToDir, network="From", virtual_network="2", ordered="false"; // a mod-L2 bank -> this Dir + + MessageBuffer goo1, network="To", virtual_network="0", ordered="false"; + MessageBuffer forwardFromDir, network="To", virtual_network="1", ordered="false"; + MessageBuffer responseFromDir, network="To", virtual_network="2", ordered="false"; // Dir -> mod-L2 bank + + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_I") { + // Base states + I, desc="Invalid"; + S, desc="Shared"; + O, desc="Owner"; + M, desc="Modified"; + + IS, desc="Blocked, was in idle"; + SS, desc="Blocked, was in shared"; + OO, desc="Blocked, was in owned"; + MO, desc="Blocked, going to owner or maybe modified"; + MM, desc="Blocked, going to modified"; + + MI, desc="Blocked on a writeback"; + MIS, desc="Blocked on a writeback, but don't remove from sharers when received"; + OS, desc="Blocked on a writeback"; + OSS, desc="Blocked on a writeback, but don't remove from sharers when received"; + } + + // Events + enumeration(Event, desc="Directory events") { + GETX, desc="A GETX arrives"; + GETS, desc="A GETS arrives"; + PUTX, desc="A PUTX arrives"; + PUTO, desc="A PUTO arrives"; + PUTO_SHARERS, desc="A PUTO arrives, but don't remove from sharers list"; + Unblock, desc="An unblock message arrives"; + Last_Unblock, desc="An unblock message arrives, we're not waiting for any additional unblocks"; + Exclusive_Unblock, desc="The processor become the exclusive owner (E or M) of the line"; + Clean_Writeback, desc="The final message as part of a PutX/PutS, no data"; + Dirty_Writeback, desc="The final message as part of a PutX/PutS, contains data"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + State DirectoryState, desc="Directory state"; + DataBlock DataBlk, desc="data for the block"; + NetDest Sharers, desc="Sharers for this block"; + NetDest Owner, desc="Owner of this block"; + int WaitingUnblocks, desc="Number of acks we're waiting for"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + + State getState(Address addr) { + return directory[addr].DirectoryState; + } + + void setState(Address addr, State state) { + if (directory.isPresent(addr)) { + + if (state == State:I) { + assert(directory[addr].Owner.count() == 0); + assert(directory[addr].Sharers.count() == 0); + } + + if (state == State:S) { + assert(directory[addr].Owner.count() == 0); + } + + if (state == State:O) { + assert(directory[addr].Owner.count() == 1); + assert(directory[addr].Sharers.isSuperset(directory[addr].Owner) == false); + } + + if (state == State:M) { + assert(directory[addr].Owner.count() == 1); + assert(directory[addr].Sharers.count() == 0); + } + + if ((state != State:SS) && (state != State:OO)) { + assert(directory[addr].WaitingUnblocks == 0); + } + + if ( (directory[addr].DirectoryState != State:I) && (state == State:I) ) { + directory[addr].DirectoryState := state; + // disable coherence checker + // sequencer.checkCoherence(addr); + } + else { + directory[addr].DirectoryState := state; + } + } + } + + // if no sharers, then directory can be considered both a sharer and exclusive w.r.t. coherence checking + bool isBlockShared(Address addr) { + if (directory.isPresent(addr)) { + if (directory[addr].DirectoryState == State:I) { + return true; + } + } + return false; + } + + bool isBlockExclusive(Address addr) { + if (directory.isPresent(addr)) { + if (directory[addr].DirectoryState == State:I) { + return true; + } + } + return false; + } + + + // ** OUT_PORTS ** + out_port(forwardNetwork_out, RequestMsg, forwardFromDir); + out_port(responseNetwork_out, ResponseMsg, responseFromDir); +// out_port(requestQueue_out, ResponseMsg, requestFromDir); // For recycling requests + out_port(goo1_out, ResponseMsg, goo1); + + // ** IN_PORTS ** + + in_port(foo1_in, ResponseMsg, foo1) { + + } + + // in_port(unblockNetwork_in, ResponseMsg, unblockToDir) { + // if (unblockNetwork_in.isReady()) { + in_port(unblockNetwork_in, ResponseMsg, responseToDir) { + if (unblockNetwork_in.isReady()) { + peek(unblockNetwork_in, ResponseMsg) { + if (in_msg.Type == CoherenceResponseType:UNBLOCK) { + if (directory[in_msg.Address].WaitingUnblocks == 1) { + trigger(Event:Last_Unblock, in_msg.Address); + } else { + trigger(Event:Unblock, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:UNBLOCK_EXCLUSIVE) { + trigger(Event:Exclusive_Unblock, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WRITEBACK_DIRTY_DATA) { + trigger(Event:Dirty_Writeback, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WRITEBACK_CLEAN_ACK) { + trigger(Event:Clean_Writeback, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + in_port(requestQueue_in, RequestMsg, requestToDir) { + if (requestQueue_in.isReady()) { + peek(requestQueue_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTX) { + trigger(Event:PUTX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTO) { + trigger(Event:PUTO, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTO_SHARERS) { + trigger(Event:PUTO_SHARERS, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + // Actions + + action(a_sendWriteBackAck, "a", desc="Send writeback ack to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:WB_ACK; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(b_sendWriteBackNack, "b", desc="Send writeback nack to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:WB_NACK; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(c_clearOwner, "c", desc="Clear the owner field") { + directory[address].Owner.clear(); + } + + action(c_moveOwnerToSharer, "cc", desc="Move owner to sharers") { + directory[address].Sharers.addNetDest(directory[address].Owner); + directory[address].Owner.clear(); + } + + action(cc_clearSharers, "\c", desc="Clear the sharers field") { + directory[address].Sharers.clear(); + } + + action(d_sendData, "d", desc="Send data to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + // enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + + if (in_msg.Type == CoherenceRequestType:GETS && directory[address].Sharers.count() == 0) { + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + } else { + out_msg.Type := CoherenceResponseType:DATA; + } + + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.Dirty := false; // By definition, the block is now clean + out_msg.Acks := directory[address].Sharers.count(); + if (directory[address].Sharers.isElement(in_msg.Requestor)) { + out_msg.Acks := out_msg.Acks - 1; + } + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(e_ownerIsUnblocker, "e", desc="The owner is now the unblocker") { + peek(unblockNetwork_in, ResponseMsg) { + directory[address].Owner.clear(); + directory[address].Owner.add(in_msg.Sender); + } + } + + action(f_forwardRequest, "f", desc="Forward request to owner") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.addNetDest(directory[in_msg.Address].Owner); + out_msg.Acks := directory[address].Sharers.count(); + if (directory[address].Sharers.isElement(in_msg.Requestor)) { + out_msg.Acks := out_msg.Acks - 1; + } + out_msg.MessageSize := MessageSizeType:Forwarded_Control; + } + } + } + + action(g_sendInvalidations, "g", desc="Send invalidations to sharers, not including the requester") { + peek(requestQueue_in, RequestMsg) { + if ((directory[in_msg.Address].Sharers.count() > 1) || + ((directory[in_msg.Address].Sharers.count() > 0) && (directory[in_msg.Address].Sharers.isElement(in_msg.Requestor) == false))) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := in_msg.Requestor; + // out_msg.Destination := directory[in_msg.Address].Sharers; + out_msg.Destination.addNetDest(directory[in_msg.Address].Sharers); + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Invalidate_Control; + } + } + } + } + + action(i_popIncomingRequestQueue, "i", desc="Pop incoming request queue") { + requestQueue_in.dequeue(); + } + + action(j_popIncomingUnblockQueue, "j", desc="Pop incoming unblock queue") { + unblockNetwork_in.dequeue(); + } + + action(l_writeDataToMemory, "l", desc="Write PUTX/PUTO data to memory") { + peek(unblockNetwork_in, ResponseMsg) { + assert(in_msg.Dirty); + assert(in_msg.MessageSize == MessageSizeType:Writeback_Data); + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + action(ll_checkDataInMemory, "\l", desc="Check PUTX/PUTO data is same as in the memory") { + peek(unblockNetwork_in, ResponseMsg) { + assert(in_msg.Dirty == false); + assert(in_msg.MessageSize == MessageSizeType:Writeback_Control); + + // NOTE: The following check would not be valid in a real + // implementation. We include the data in the "dataless" + // message so we can assert the clean data matches the datablock + // in memory + assert(directory[in_msg.Address].DataBlk == in_msg.DataBlk); + } + } + + action(m_addUnlockerToSharers, "m", desc="Add the unlocker to the sharer list") { + peek(unblockNetwork_in, ResponseMsg) { + directory[address].Sharers.add(in_msg.Sender); + } + } + + action(n_incrementOutstanding, "n", desc="Increment outstanding requests") { + directory[address].WaitingUnblocks := directory[address].WaitingUnblocks + 1; + } + + action(o_decrementOutstanding, "o", desc="Decrement outstanding requests") { + directory[address].WaitingUnblocks := directory[address].WaitingUnblocks - 1; + assert(directory[address].WaitingUnblocks >= 0); + } + + // action(z_stall, "z", desc="Cannot be handled right now.") { + // Special name recognized as do nothing case + // } + + action(zz_recycleRequest, "\z", desc="Recycle the request queue") { + requestQueue_in.recycle(); + } + + // TRANSITIONS + + transition(I, GETX, MM) { + d_sendData; + i_popIncomingRequestQueue; + } + + transition(S, GETX, MM) { + d_sendData; + g_sendInvalidations; + i_popIncomingRequestQueue; + } + + transition(I, GETS, IS) { + d_sendData; + i_popIncomingRequestQueue; + } + + transition({S, SS}, GETS, SS) { + d_sendData; + n_incrementOutstanding; + i_popIncomingRequestQueue; + } + + transition({I, S}, PUTO) { + b_sendWriteBackNack; + i_popIncomingRequestQueue; + } + + transition({I, S, O}, PUTX) { + b_sendWriteBackNack; + i_popIncomingRequestQueue; + } + + transition(O, GETX, MM) { + f_forwardRequest; + g_sendInvalidations; + i_popIncomingRequestQueue; + } + + transition({O, OO}, GETS, OO) { + f_forwardRequest; + n_incrementOutstanding; + i_popIncomingRequestQueue; + } + + transition(M, GETX, MM) { + f_forwardRequest; + i_popIncomingRequestQueue; + } + + transition(M, GETS, MO) { + f_forwardRequest; + i_popIncomingRequestQueue; + } + + transition(M, PUTX, MI) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + // happens if M->O transition happens on-chip + transition(M, PUTO, MI) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + transition(M, PUTO_SHARERS, MIS) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + transition(O, PUTO, OS) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + transition(O, PUTO_SHARERS, OSS) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + + transition({MM, MO, MI, MIS, OS, OSS}, {GETS, GETX, PUTO, PUTO_SHARERS, PUTX}) { + zz_recycleRequest; + } + + transition({MM, MO}, Exclusive_Unblock, M) { + cc_clearSharers; + e_ownerIsUnblocker; + j_popIncomingUnblockQueue; + } + + transition(MO, Unblock, O) { + m_addUnlockerToSharers; + j_popIncomingUnblockQueue; + } + + transition({IS, SS, OO}, {GETX, PUTO, PUTO_SHARERS, PUTX}) { + zz_recycleRequest; + } + + transition(IS, GETS) { + zz_recycleRequest; + } + + transition(IS, Unblock, S) { + m_addUnlockerToSharers; + j_popIncomingUnblockQueue; + } + + transition(IS, Exclusive_Unblock, M) { + cc_clearSharers; + e_ownerIsUnblocker; + j_popIncomingUnblockQueue; + } + + transition(SS, Unblock) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(SS, Last_Unblock, S) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(OO, Unblock) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(OO, Last_Unblock, O) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(MI, Dirty_Writeback, I) { + c_clearOwner; + cc_clearSharers; + l_writeDataToMemory; + j_popIncomingUnblockQueue; + } + + transition(MIS, Dirty_Writeback, S) { + c_moveOwnerToSharer; + l_writeDataToMemory; + j_popIncomingUnblockQueue; + } + + transition(MIS, Clean_Writeback, S) { + c_moveOwnerToSharer; + j_popIncomingUnblockQueue; + } + + transition(OS, Dirty_Writeback, S) { + c_clearOwner; + l_writeDataToMemory; + j_popIncomingUnblockQueue; + } + + transition(OSS, Dirty_Writeback, S) { + c_moveOwnerToSharer; + l_writeDataToMemory; + j_popIncomingUnblockQueue; + } + + transition(OSS, Clean_Writeback, S) { + c_moveOwnerToSharer; + j_popIncomingUnblockQueue; + } + + transition(MI, Clean_Writeback, I) { + c_clearOwner; + cc_clearSharers; + ll_checkDataInMemory; + j_popIncomingUnblockQueue; + } + + transition(OS, Clean_Writeback, S) { + c_clearOwner; + ll_checkDataInMemory; + j_popIncomingUnblockQueue; + } + + transition({MI, MIS}, Unblock, M) { + j_popIncomingUnblockQueue; + } + + transition({OS, OSS}, Unblock, O) { + j_popIncomingUnblockQueue; + } +} diff --git a/src/mem/protocol/MOESI_CMP_directory-msg.sm b/src/mem/protocol/MOESI_CMP_directory-msg.sm new file mode 100644 index 000000000..08b4abec3 --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_directory-msg.sm @@ -0,0 +1,126 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +// CoherenceRequestType +enumeration(CoherenceRequestType, desc="...") { + GETX, desc="Get eXclusive"; + GETS, desc="Get Shared"; + PUTX, desc="Put eXclusive"; + PUTO, desc="Put Owned"; + PUTO_SHARERS, desc="Put Owned, but sharers exist so don't remove from sharers list"; + PUTS, desc="Put Shared"; + WB_ACK, desc="Writeback ack"; + WB_ACK_DATA, desc="Writeback ack"; + WB_NACK, desc="Writeback neg. ack"; + INV, desc="Invalidation"; +} + +// CoherenceResponseType +enumeration(CoherenceResponseType, desc="...") { + ACK, desc="ACKnowledgment, responder doesn't have a copy"; + DATA, desc="Data"; + DATA_EXCLUSIVE, desc="Data, no processor has a copy"; + UNBLOCK, desc="Unblock"; + UNBLOCK_EXCLUSIVE, desc="Unblock, we're in E/M"; + WRITEBACK_CLEAN_DATA, desc="Clean writeback (contains data)"; + WRITEBACK_CLEAN_ACK, desc="Clean writeback (contains no data)"; + WRITEBACK_DIRTY_DATA, desc="Dirty writeback (contains data)"; +} + +// TriggerType +enumeration(TriggerType, desc="...") { + ALL_ACKS, desc="See corresponding event"; +} + +// TriggerMsg +structure(TriggerMsg, desc="...", interface="Message") { + Address Address, desc="Physical address for this request"; + TriggerType Type, desc="Type of trigger"; +} + +// RequestMsg (and also forwarded requests) +structure(RequestMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)"; + MachineID Requestor, desc="Node who initiated the request"; + MachineType RequestorMachine, desc="type of component"; + NetDest Destination, desc="Multicast destination mask"; + int Acks, desc="How many acks to expect"; + MessageSizeType MessageSize, desc="size category of the message"; + AccessModeType AccessMode, desc="user/supervisor access type"; + PrefetchBit Prefetch, desc="Is this a prefetch request"; +} + +// ResponseMsg (and also unblock requests) +structure(ResponseMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)"; + MachineID Sender, desc="Node who sent the data"; + MachineType SenderMachine, desc="type of component sending msg"; + NetDest Destination, desc="Node to whom the data is sent"; + DataBlock DataBlk, desc="data for the cache line"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + int Acks, desc="How many acks to expect"; + MessageSizeType MessageSize, desc="size category of the message"; +} + +GenericRequestType convertToGenericType(CoherenceRequestType type) { + if(type == CoherenceRequestType:PUTX) { + return GenericRequestType:PUTX; + } else if(type == CoherenceRequestType:GETS) { + return GenericRequestType:GETS; + } else if(type == CoherenceRequestType:GETX) { + return GenericRequestType:GETX; + } else if(type == CoherenceRequestType:PUTS) { + return GenericRequestType:PUTS; + } else if(type == CoherenceRequestType:PUTX) { + return GenericRequestType:PUTS; + } else if(type == CoherenceRequestType:PUTO) { + return GenericRequestType:PUTO; + } else if(type == CoherenceRequestType:PUTO_SHARERS) { + return GenericRequestType:PUTO; + } else if(type == CoherenceRequestType:INV) { + return GenericRequestType:INV; + } else if(type == CoherenceRequestType:WB_ACK) { + return GenericRequestType:WB_ACK; + } else if(type == CoherenceRequestType:WB_ACK_DATA) { + return GenericRequestType:WB_ACK; + } else if(type == CoherenceRequestType:WB_NACK) { + return GenericRequestType:NACK; + } else { + DEBUG_EXPR(type); + error("invalid CoherenceRequestType"); + } +} + diff --git a/src/mem/protocol/MOESI_CMP_directory-perfectDir.sm b/src/mem/protocol/MOESI_CMP_directory-perfectDir.sm new file mode 100644 index 000000000..7717434f8 --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_directory-perfectDir.sm @@ -0,0 +1,573 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOESI_CMP_directory-dir.sm 1.11 05/01/19 15:48:35-06:00 mikem@royal16.cs.wisc.edu $ + */ + +machine(Directory, "Directory protocol") { + + // ** IN QUEUES ** + MessageBuffer foo1, network="From", virtual_network="0", ordered="false"; // a mod-L2 bank -> this Dir + MessageBuffer requestToDir, network="From", virtual_network="1", ordered="false"; // a mod-L2 bank -> this Dir + MessageBuffer responseToDir, network="From", virtual_network="2", ordered="false"; // a mod-L2 bank -> this Dir + + MessageBuffer goo1, network="To", virtual_network="0", ordered="false"; + MessageBuffer forwardFromDir, network="To", virtual_network="1", ordered="false"; + MessageBuffer responseFromDir, network="To", virtual_network="2", ordered="false"; // Dir -> mod-L2 bank + + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_I") { + // Base states + I, desc="Invalid"; + S, desc="Shared"; + O, desc="Owner"; + M, desc="Modified"; + + IS, desc="Blocked, was in idle"; + SS, desc="Blocked, was in shared"; + OO, desc="Blocked, was in owned"; + MO, desc="Blocked, going to owner or maybe modified"; + MM, desc="Blocked, going to modified"; + + MI, desc="Blocked on a writeback"; + MIS, desc="Blocked on a writeback, but don't remove from sharers when received"; + OS, desc="Blocked on a writeback"; + OSS, desc="Blocked on a writeback, but don't remove from sharers when received"; + } + + // Events + enumeration(Event, desc="Directory events") { + GETX, desc="A GETX arrives"; + GETS, desc="A GETS arrives"; + PUTX, desc="A PUTX arrives"; + PUTO, desc="A PUTO arrives"; + PUTO_SHARERS, desc="A PUTO arrives, but don't remove from sharers list"; + Unblock, desc="An unblock message arrives"; + Last_Unblock, desc="An unblock message arrives, we're not waiting for any additional unblocks"; + Exclusive_Unblock, desc="The processor become the exclusive owner (E or M) of the line"; + Clean_Writeback, desc="The final message as part of a PutX/PutS, no data"; + Dirty_Writeback, desc="The final message as part of a PutX/PutS, contains data"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + State DirectoryState, desc="Directory state"; + DataBlock DataBlk, desc="data for the block"; + NetDest Sharers, desc="Sharers for this block"; + NetDest Owner, desc="Owner of this block"; + int WaitingUnblocks, desc="Number of acks we're waiting for"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + + State getState(Address addr) { + return directory[addr].DirectoryState; + } + + void setState(Address addr, State state) { + if (directory.isPresent(addr)) { + + if (state == State:I) { + assert(directory[addr].Owner.count() == 0); + assert(directory[addr].Sharers.count() == 0); + } + + if (state == State:S) { + assert(directory[addr].Owner.count() == 0); + } + + if (state == State:O) { + assert(directory[addr].Owner.count() == 1); + assert(directory[addr].Sharers.isSuperset(directory[addr].Owner) == false); + } + + if (state == State:M) { + assert(directory[addr].Owner.count() == 1); + assert(directory[addr].Sharers.count() == 0); + } + + if ((state != State:SS) && (state != State:OO)) { + assert(directory[addr].WaitingUnblocks == 0); + } + + if ( (directory[addr].DirectoryState != State:I) && (state == State:I) ) { + directory[addr].DirectoryState := state; + // disable coherence checker + // sequencer.checkCoherence(addr); + } + else { + directory[addr].DirectoryState := state; + } + } + } + + // if no sharers, then directory can be considered both a sharer and exclusive w.r.t. coherence checking + bool isBlockShared(Address addr) { + if (directory.isPresent(addr)) { + if (directory[addr].DirectoryState == State:I) { + return true; + } + } + return false; + } + + bool isBlockExclusive(Address addr) { + if (directory.isPresent(addr)) { + if (directory[addr].DirectoryState == State:I) { + return true; + } + } + return false; + } + + + // ** OUT_PORTS ** + out_port(forwardNetwork_out, RequestMsg, forwardFromDir); + out_port(responseNetwork_out, ResponseMsg, responseFromDir); +// out_port(requestQueue_out, ResponseMsg, requestFromDir); // For recycling requests + out_port(goo1_out, ResponseMsg, goo1); + + // ** IN_PORTS ** + + in_port(foo1_in, ResponseMsg, foo1) { + + } + + // in_port(unblockNetwork_in, ResponseMsg, unblockToDir) { + // if (unblockNetwork_in.isReady()) { + in_port(unblockNetwork_in, ResponseMsg, responseToDir) { + if (unblockNetwork_in.isReady()) { + peek(unblockNetwork_in, ResponseMsg) { + if (in_msg.Type == CoherenceResponseType:UNBLOCK) { + if (directory[in_msg.Address].WaitingUnblocks == 1) { + trigger(Event:Last_Unblock, in_msg.Address); + } else { + trigger(Event:Unblock, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:UNBLOCK_EXCLUSIVE) { + trigger(Event:Exclusive_Unblock, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WRITEBACK_DIRTY_DATA) { + trigger(Event:Dirty_Writeback, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WRITEBACK_CLEAN_ACK) { + trigger(Event:Clean_Writeback, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + in_port(requestQueue_in, RequestMsg, requestToDir) { + if (requestQueue_in.isReady()) { + peek(requestQueue_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTX) { + trigger(Event:PUTX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTO) { + trigger(Event:PUTO, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTO_SHARERS) { + trigger(Event:PUTO_SHARERS, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + // Actions + + action(a_sendWriteBackAck, "a", desc="Send writeback ack to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_CACHE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:WB_ACK; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(b_sendWriteBackNack, "b", desc="Send writeback nack to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_CACHE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:WB_NACK; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(c_clearOwner, "c", desc="Clear the owner field") { + directory[address].Owner.clear(); + } + + action(c_moveOwnerToSharer, "cc", desc="Move owner to sharers") { + directory[address].Sharers.addNetDest(directory[address].Owner); + directory[address].Owner.clear(); + } + + action(cc_clearSharers, "\c", desc="Clear the sharers field") { + directory[address].Sharers.clear(); + } + + action(d_sendData, "d", desc="Send data to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + // enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + + if (in_msg.Type == CoherenceRequestType:GETS && directory[address].Sharers.count() == 0) { + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + } else { + out_msg.Type := CoherenceResponseType:DATA; + } + + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.Dirty := false; // By definition, the block is now clean + out_msg.Acks := directory[address].Sharers.count(); + if (directory[address].Sharers.isElement(in_msg.Requestor)) { + out_msg.Acks := out_msg.Acks - 1; + } + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(e_ownerIsUnblocker, "e", desc="The owner is now the unblocker") { + peek(unblockNetwork_in, ResponseMsg) { + directory[address].Owner.clear(); + directory[address].Owner.add(in_msg.Sender); + } + } + + action(f_forwardRequest, "f", desc="Forward request to owner") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_CACHE_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.addNetDest(directory[in_msg.Address].Owner); + out_msg.Acks := directory[address].Sharers.count(); + if (directory[address].Sharers.isElement(in_msg.Requestor)) { + out_msg.Acks := out_msg.Acks - 1; + } + out_msg.MessageSize := MessageSizeType:Forwarded_Control; + } + } + } + + action(g_sendInvalidations, "g", desc="Send invalidations to sharers, not including the requester") { + peek(requestQueue_in, RequestMsg) { + if ((directory[in_msg.Address].Sharers.count() > 1) || + ((directory[in_msg.Address].Sharers.count() > 0) && (directory[in_msg.Address].Sharers.isElement(in_msg.Requestor) == false))) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_CACHE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := in_msg.Requestor; + // out_msg.Destination := directory[in_msg.Address].Sharers; + out_msg.Destination.addNetDest(directory[in_msg.Address].Sharers); + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Invalidate_Control; + } + } + } + } + + action(i_popIncomingRequestQueue, "i", desc="Pop incoming request queue") { + requestQueue_in.dequeue(); + } + + action(j_popIncomingUnblockQueue, "j", desc="Pop incoming unblock queue") { + unblockNetwork_in.dequeue(); + } + + action(l_writeDataToMemory, "l", desc="Write PUTX/PUTO data to memory") { + peek(unblockNetwork_in, ResponseMsg) { + assert(in_msg.Dirty); + assert(in_msg.MessageSize == MessageSizeType:Writeback_Data); + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + action(ll_checkDataInMemory, "\l", desc="Check PUTX/PUTO data is same as in the memory") { + peek(unblockNetwork_in, ResponseMsg) { + assert(in_msg.Dirty == false); + assert(in_msg.MessageSize == MessageSizeType:Writeback_Control); + + // NOTE: The following check would not be valid in a real + // implementation. We include the data in the "dataless" + // message so we can assert the clean data matches the datablock + // in memory + assert(directory[in_msg.Address].DataBlk == in_msg.DataBlk); + } + } + + action(m_addUnlockerToSharers, "m", desc="Add the unlocker to the sharer list") { + peek(unblockNetwork_in, ResponseMsg) { + directory[address].Sharers.add(in_msg.Sender); + } + } + + action(n_incrementOutstanding, "n", desc="Increment outstanding requests") { + directory[address].WaitingUnblocks := directory[address].WaitingUnblocks + 1; + } + + action(o_decrementOutstanding, "o", desc="Decrement outstanding requests") { + directory[address].WaitingUnblocks := directory[address].WaitingUnblocks - 1; + assert(directory[address].WaitingUnblocks >= 0); + } + + // action(z_stall, "z", desc="Cannot be handled right now.") { + // Special name recognized as do nothing case + // } + + action(zz_recycleRequest, "\z", desc="Recycle the request queue") { + requestQueue_in.recycle(); + } + + // TRANSITIONS + + transition(I, GETX, MM) { + d_sendData; + i_popIncomingRequestQueue; + } + + transition(S, GETX, MM) { + d_sendData; + g_sendInvalidations; + i_popIncomingRequestQueue; + } + + transition(I, GETS, IS) { + d_sendData; + i_popIncomingRequestQueue; + } + + transition({S, SS}, GETS, SS) { + d_sendData; + n_incrementOutstanding; + i_popIncomingRequestQueue; + } + + transition({I, S}, PUTO) { + b_sendWriteBackNack; + i_popIncomingRequestQueue; + } + + transition({I, S, O}, PUTX) { + b_sendWriteBackNack; + i_popIncomingRequestQueue; + } + + transition(O, GETX, MM) { + f_forwardRequest; + g_sendInvalidations; + i_popIncomingRequestQueue; + } + + transition({O, OO}, GETS, OO) { + f_forwardRequest; + n_incrementOutstanding; + i_popIncomingRequestQueue; + } + + transition(M, GETX, MM) { + f_forwardRequest; + i_popIncomingRequestQueue; + } + + transition(M, GETS, MO) { + f_forwardRequest; + i_popIncomingRequestQueue; + } + + transition(M, PUTX, MI) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + // happens if M->O transition happens on-chip + transition(M, PUTO, MI) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + transition(M, PUTO_SHARERS, MIS) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + transition(O, PUTO, OS) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + transition(O, PUTO_SHARERS, OSS) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + + transition({MM, MO, MI, MIS, OS, OSS}, {GETS, GETX, PUTO, PUTO_SHARERS, PUTX}) { + zz_recycleRequest; + } + + transition({MM, MO}, Exclusive_Unblock, M) { + cc_clearSharers; + e_ownerIsUnblocker; + j_popIncomingUnblockQueue; + } + + transition(MO, Unblock, O) { + m_addUnlockerToSharers; + j_popIncomingUnblockQueue; + } + + transition({IS, SS, OO}, {GETX, PUTO, PUTO_SHARERS, PUTX}) { + zz_recycleRequest; + } + + transition(IS, GETS) { + zz_recycleRequest; + } + + transition(IS, Unblock, S) { + m_addUnlockerToSharers; + j_popIncomingUnblockQueue; + } + + transition(IS, Exclusive_Unblock, M) { + cc_clearSharers; + e_ownerIsUnblocker; + j_popIncomingUnblockQueue; + } + + transition(SS, Unblock) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(SS, Last_Unblock, S) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(OO, Unblock) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(OO, Last_Unblock, O) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(MI, Dirty_Writeback, I) { + c_clearOwner; + cc_clearSharers; + l_writeDataToMemory; + j_popIncomingUnblockQueue; + } + + transition(MIS, Dirty_Writeback, S) { + c_moveOwnerToSharer; + l_writeDataToMemory; + j_popIncomingUnblockQueue; + } + + transition(MIS, Clean_Writeback, S) { + c_moveOwnerToSharer; + j_popIncomingUnblockQueue; + } + + transition(OS, Dirty_Writeback, S) { + c_clearOwner; + l_writeDataToMemory; + j_popIncomingUnblockQueue; + } + + transition(OSS, Dirty_Writeback, S) { + c_moveOwnerToSharer; + l_writeDataToMemory; + j_popIncomingUnblockQueue; + } + + transition(OSS, Clean_Writeback, S) { + c_moveOwnerToSharer; + j_popIncomingUnblockQueue; + } + + transition(MI, Clean_Writeback, I) { + c_clearOwner; + cc_clearSharers; + ll_checkDataInMemory; + j_popIncomingUnblockQueue; + } + + transition(OS, Clean_Writeback, S) { + c_clearOwner; + ll_checkDataInMemory; + j_popIncomingUnblockQueue; + } + + transition({MI, MIS}, Unblock, M) { + j_popIncomingUnblockQueue; + } + + transition({OS, OSS}, Unblock, O) { + j_popIncomingUnblockQueue; + } +} diff --git a/src/mem/protocol/MOESI_CMP_directory.slicc b/src/mem/protocol/MOESI_CMP_directory.slicc new file mode 100644 index 000000000..c552d7157 --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_directory.slicc @@ -0,0 +1,5 @@ +MOESI_CMP_directory-msg.sm +MOESI_CMP_directory-L2cache.sm +MOESI_CMP_directory-L1cache.sm +MOESI_CMP_directory-dir.sm +standard_CMP-protocol.sm diff --git a/src/mem/protocol/MOESI_CMP_directory_m-dir.sm b/src/mem/protocol/MOESI_CMP_directory_m-dir.sm new file mode 100644 index 000000000..3a4d875c1 --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_directory_m-dir.sm @@ -0,0 +1,652 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +machine(Directory, "Directory protocol") { + + // ** IN QUEUES ** + MessageBuffer foo1, network="From", virtual_network="0", ordered="false"; // a mod-L2 bank -> this Dir + MessageBuffer requestToDir, network="From", virtual_network="1", ordered="false"; // a mod-L2 bank -> this Dir + MessageBuffer responseToDir, network="From", virtual_network="2", ordered="false"; // a mod-L2 bank -> this Dir + + MessageBuffer goo1, network="To", virtual_network="0", ordered="false"; + MessageBuffer forwardFromDir, network="To", virtual_network="1", ordered="false"; + MessageBuffer responseFromDir, network="To", virtual_network="2", ordered="false"; // Dir -> mod-L2 bank + + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_I") { + // Base states + I, desc="Invalid"; + S, desc="Shared"; + O, desc="Owner"; + M, desc="Modified"; + + IS, desc="Blocked, was in idle"; + SS, desc="Blocked, was in shared"; + OO, desc="Blocked, was in owned"; + MO, desc="Blocked, going to owner or maybe modified"; + MM, desc="Blocked, going to modified"; + + MI, desc="Blocked on a writeback"; + MIS, desc="Blocked on a writeback, but don't remove from sharers when received"; + OS, desc="Blocked on a writeback"; + OSS, desc="Blocked on a writeback, but don't remove from sharers when received"; + } + + // Events + enumeration(Event, desc="Directory events") { + GETX, desc="A GETX arrives"; + GETS, desc="A GETS arrives"; + PUTX, desc="A PUTX arrives"; + PUTO, desc="A PUTO arrives"; + PUTO_SHARERS, desc="A PUTO arrives, but don't remove from sharers list"; + Unblock, desc="An unblock message arrives"; + Last_Unblock, desc="An unblock message arrives, we're not waiting for any additional unblocks"; + Exclusive_Unblock, desc="The processor become the exclusive owner (E or M) of the line"; + Clean_Writeback, desc="The final message as part of a PutX/PutS, no data"; + Dirty_Writeback, desc="The final message as part of a PutX/PutS, contains data"; + Memory_Data, desc="Fetched data from memory arrives"; + Memory_Ack, desc="Writeback Ack from memory arrives"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + State DirectoryState, desc="Directory state"; + DataBlock DataBlk, desc="data for the block"; + NetDest Sharers, desc="Sharers for this block"; + NetDest Owner, desc="Owner of this block"; + int WaitingUnblocks, desc="Number of acks we're waiting for"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + // to simulate detailed DRAM + external_type(MemoryControl, inport="yes", outport="yes") { + + } + + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + MemoryControl memBuffer, constructor_hack="i"; + + State getState(Address addr) { + return directory[addr].DirectoryState; + } + + void setState(Address addr, State state) { + if (directory.isPresent(addr)) { + + if (state == State:I) { + assert(directory[addr].Owner.count() == 0); + assert(directory[addr].Sharers.count() == 0); + } + + if (state == State:S) { + assert(directory[addr].Owner.count() == 0); + } + + if (state == State:O) { + assert(directory[addr].Owner.count() == 1); + assert(directory[addr].Sharers.isSuperset(directory[addr].Owner) == false); + } + + if (state == State:M) { + assert(directory[addr].Owner.count() == 1); + assert(directory[addr].Sharers.count() == 0); + } + + if ((state != State:SS) && (state != State:OO)) { + assert(directory[addr].WaitingUnblocks == 0); + } + + if ( (directory[addr].DirectoryState != State:I) && (state == State:I) ) { + directory[addr].DirectoryState := state; + // disable coherence checker + // sequencer.checkCoherence(addr); + } + else { + directory[addr].DirectoryState := state; + } + } + } + + // if no sharers, then directory can be considered both a sharer and exclusive w.r.t. coherence checking + bool isBlockShared(Address addr) { + if (directory.isPresent(addr)) { + if (directory[addr].DirectoryState == State:I) { + return true; + } + } + return false; + } + + bool isBlockExclusive(Address addr) { + if (directory.isPresent(addr)) { + if (directory[addr].DirectoryState == State:I) { + return true; + } + } + return false; + } + + + // ** OUT_PORTS ** + out_port(forwardNetwork_out, RequestMsg, forwardFromDir); + out_port(responseNetwork_out, ResponseMsg, responseFromDir); +// out_port(requestQueue_out, ResponseMsg, requestFromDir); // For recycling requests + out_port(goo1_out, ResponseMsg, goo1); + out_port(memQueue_out, MemoryMsg, memBuffer); + + // ** IN_PORTS ** + + in_port(foo1_in, ResponseMsg, foo1) { + + } + + // in_port(unblockNetwork_in, ResponseMsg, unblockToDir) { + // if (unblockNetwork_in.isReady()) { + in_port(unblockNetwork_in, ResponseMsg, responseToDir) { + if (unblockNetwork_in.isReady()) { + peek(unblockNetwork_in, ResponseMsg) { + if (in_msg.Type == CoherenceResponseType:UNBLOCK) { + if (directory[in_msg.Address].WaitingUnblocks == 1) { + trigger(Event:Last_Unblock, in_msg.Address); + } else { + trigger(Event:Unblock, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:UNBLOCK_EXCLUSIVE) { + trigger(Event:Exclusive_Unblock, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WRITEBACK_DIRTY_DATA) { + trigger(Event:Dirty_Writeback, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WRITEBACK_CLEAN_ACK) { + trigger(Event:Clean_Writeback, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + in_port(requestQueue_in, RequestMsg, requestToDir) { + if (requestQueue_in.isReady()) { + peek(requestQueue_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTX) { + trigger(Event:PUTX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTO) { + trigger(Event:PUTO, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTO_SHARERS) { + trigger(Event:PUTO_SHARERS, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + // off-chip memory request/response is done + in_port(memQueue_in, MemoryMsg, memBuffer) { + if (memQueue_in.isReady()) { + peek(memQueue_in, MemoryMsg) { + if (in_msg.Type == MemoryRequestType:MEMORY_READ) { + trigger(Event:Memory_Data, in_msg.Address); + } else if (in_msg.Type == MemoryRequestType:MEMORY_WB) { + trigger(Event:Memory_Ack, in_msg.Address); + } else { + DEBUG_EXPR(in_msg.Type); + error("Invalid message"); + } + } + } + } + + // Actions + + action(a_sendWriteBackAck, "a", desc="Send writeback ack to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:WB_ACK; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(b_sendWriteBackNack, "b", desc="Send writeback nack to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:WB_NACK; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(c_clearOwner, "c", desc="Clear the owner field") { + directory[address].Owner.clear(); + } + + action(c_moveOwnerToSharer, "cc", desc="Move owner to sharers") { + directory[address].Sharers.addNetDest(directory[address].Owner); + directory[address].Owner.clear(); + } + + action(cc_clearSharers, "\c", desc="Clear the sharers field") { + directory[address].Sharers.clear(); + } + + action(d_sendDataMsg, "d", desc="Send data to requestor") { + peek(memQueue_in, MemoryMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="1") { + out_msg.Address := address; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(in_msg.OriginalRequestorMachId); + //out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := false; // By definition, the block is now clean + out_msg.Acks := in_msg.Acks; + if (in_msg.ReadX) { + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE; + } else { + out_msg.Type := CoherenceResponseType:DATA; + } + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(e_ownerIsUnblocker, "e", desc="The owner is now the unblocker") { + peek(unblockNetwork_in, ResponseMsg) { + directory[address].Owner.clear(); + directory[address].Owner.add(in_msg.Sender); + } + } + + action(f_forwardRequest, "f", desc="Forward request to owner") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.addNetDest(directory[in_msg.Address].Owner); + out_msg.Acks := directory[address].Sharers.count(); + if (directory[address].Sharers.isElement(in_msg.Requestor)) { + out_msg.Acks := out_msg.Acks - 1; + } + out_msg.MessageSize := MessageSizeType:Forwarded_Control; + } + } + } + + action(g_sendInvalidations, "g", desc="Send invalidations to sharers, not including the requester") { + peek(requestQueue_in, RequestMsg) { + if ((directory[in_msg.Address].Sharers.count() > 1) || + ((directory[in_msg.Address].Sharers.count() > 0) && (directory[in_msg.Address].Sharers.isElement(in_msg.Requestor) == false))) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := in_msg.Requestor; + // out_msg.Destination := directory[in_msg.Address].Sharers; + out_msg.Destination.addNetDest(directory[in_msg.Address].Sharers); + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Invalidate_Control; + } + } + } + } + + action(i_popIncomingRequestQueue, "i", desc="Pop incoming request queue") { + requestQueue_in.dequeue(); + } + + action(j_popIncomingUnblockQueue, "j", desc="Pop incoming unblock queue") { + unblockNetwork_in.dequeue(); + } + + action(l_writeDataToMemory, "l", desc="Write PUTX/PUTO data to memory") { + peek(unblockNetwork_in, ResponseMsg) { + assert(in_msg.Dirty); + assert(in_msg.MessageSize == MessageSizeType:Writeback_Data); + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + action(ll_checkDataInMemory, "\l", desc="Check PUTX/PUTO data is same as in the memory") { + peek(unblockNetwork_in, ResponseMsg) { + assert(in_msg.Dirty == false); + assert(in_msg.MessageSize == MessageSizeType:Writeback_Control); + + // NOTE: The following check would not be valid in a real + // implementation. We include the data in the "dataless" + // message so we can assert the clean data matches the datablock + // in memory + assert(directory[in_msg.Address].DataBlk == in_msg.DataBlk); + } + } + + action(m_addUnlockerToSharers, "m", desc="Add the unlocker to the sharer list") { + peek(unblockNetwork_in, ResponseMsg) { + directory[address].Sharers.add(in_msg.Sender); + } + } + + action(n_incrementOutstanding, "n", desc="Increment outstanding requests") { + directory[address].WaitingUnblocks := directory[address].WaitingUnblocks + 1; + } + + action(o_decrementOutstanding, "o", desc="Decrement outstanding requests") { + directory[address].WaitingUnblocks := directory[address].WaitingUnblocks - 1; + assert(directory[address].WaitingUnblocks >= 0); + } + + action(q_popMemQueue, "q", desc="Pop off-chip request queue") { + memQueue_in.dequeue(); + } + + action(qf_queueMemoryFetchRequest, "qf", desc="Queue off-chip fetch request") { + peek(requestQueue_in, RequestMsg) { + enqueue(memQueue_out, MemoryMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := MemoryRequestType:MEMORY_READ; + out_msg.Sender := machineID; + out_msg.OriginalRequestorMachId := in_msg.Requestor; + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.MessageSize := in_msg.MessageSize; + //out_msg.Prefetch := false; + // These are not used by memory but are passed back here with the read data: + out_msg.ReadX := (in_msg.Type == CoherenceRequestType:GETS && directory[address].Sharers.count() == 0); + out_msg.Acks := directory[address].Sharers.count(); + if (directory[address].Sharers.isElement(in_msg.Requestor)) { + out_msg.Acks := out_msg.Acks - 1; + } + DEBUG_EXPR(out_msg); + } + } + } + + action(qw_queueMemoryWBRequest, "qw", desc="Queue off-chip writeback request") { + peek(unblockNetwork_in, ResponseMsg) { + enqueue(memQueue_out, MemoryMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := MemoryRequestType:MEMORY_WB; + out_msg.Sender := machineID; + //out_msg.OriginalRequestorMachId := in_msg.Requestor; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.MessageSize := in_msg.MessageSize; + //out_msg.Prefetch := false; + // Not used: + out_msg.ReadX := false; + out_msg.Acks := 0; + DEBUG_EXPR(out_msg); + } + } + } + + + // action(z_stall, "z", desc="Cannot be handled right now.") { + // Special name recognized as do nothing case + // } + + action(zz_recycleRequest, "\z", desc="Recycle the request queue") { + requestQueue_in.recycle(); + } + + // TRANSITIONS + + transition(I, GETX, MM) { + qf_queueMemoryFetchRequest; + i_popIncomingRequestQueue; + } + + transition(S, GETX, MM) { + qf_queueMemoryFetchRequest; + g_sendInvalidations; + i_popIncomingRequestQueue; + } + + transition(I, GETS, IS) { + qf_queueMemoryFetchRequest; + i_popIncomingRequestQueue; + } + + transition({S, SS}, GETS, SS) { + qf_queueMemoryFetchRequest; + n_incrementOutstanding; + i_popIncomingRequestQueue; + } + + transition({I, S}, PUTO) { + b_sendWriteBackNack; + i_popIncomingRequestQueue; + } + + transition({I, S, O}, PUTX) { + b_sendWriteBackNack; + i_popIncomingRequestQueue; + } + + transition(O, GETX, MM) { + f_forwardRequest; + g_sendInvalidations; + i_popIncomingRequestQueue; + } + + transition({O, OO}, GETS, OO) { + f_forwardRequest; + n_incrementOutstanding; + i_popIncomingRequestQueue; + } + + transition(M, GETX, MM) { + f_forwardRequest; + i_popIncomingRequestQueue; + } + + transition(M, GETS, MO) { + f_forwardRequest; + i_popIncomingRequestQueue; + } + + transition(M, PUTX, MI) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + // happens if M->O transition happens on-chip + transition(M, PUTO, MI) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + transition(M, PUTO_SHARERS, MIS) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + transition(O, PUTO, OS) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + transition(O, PUTO_SHARERS, OSS) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + + transition({MM, MO, MI, MIS, OS, OSS}, {GETS, GETX, PUTO, PUTO_SHARERS, PUTX}) { + zz_recycleRequest; + } + + transition({MM, MO}, Exclusive_Unblock, M) { + cc_clearSharers; + e_ownerIsUnblocker; + j_popIncomingUnblockQueue; + } + + transition(MO, Unblock, O) { + m_addUnlockerToSharers; + j_popIncomingUnblockQueue; + } + + transition({IS, SS, OO}, {GETX, PUTO, PUTO_SHARERS, PUTX}) { + zz_recycleRequest; + } + + transition(IS, GETS) { + zz_recycleRequest; + } + + transition(IS, Unblock, S) { + m_addUnlockerToSharers; + j_popIncomingUnblockQueue; + } + + transition(IS, Exclusive_Unblock, M) { + cc_clearSharers; + e_ownerIsUnblocker; + j_popIncomingUnblockQueue; + } + + transition(SS, Unblock) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(SS, Last_Unblock, S) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(OO, Unblock) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(OO, Last_Unblock, O) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(MI, Dirty_Writeback, I) { + c_clearOwner; + cc_clearSharers; + l_writeDataToMemory; + qw_queueMemoryWBRequest; + j_popIncomingUnblockQueue; + } + + transition(MIS, Dirty_Writeback, S) { + c_moveOwnerToSharer; + l_writeDataToMemory; + qw_queueMemoryWBRequest; + j_popIncomingUnblockQueue; + } + + transition(MIS, Clean_Writeback, S) { + c_moveOwnerToSharer; + j_popIncomingUnblockQueue; + } + + transition(OS, Dirty_Writeback, S) { + c_clearOwner; + l_writeDataToMemory; + qw_queueMemoryWBRequest; + j_popIncomingUnblockQueue; + } + + transition(OSS, Dirty_Writeback, S) { + c_moveOwnerToSharer; + l_writeDataToMemory; + qw_queueMemoryWBRequest; + j_popIncomingUnblockQueue; + } + + transition(OSS, Clean_Writeback, S) { + c_moveOwnerToSharer; + j_popIncomingUnblockQueue; + } + + transition(MI, Clean_Writeback, I) { + c_clearOwner; + cc_clearSharers; + ll_checkDataInMemory; + j_popIncomingUnblockQueue; + } + + transition(OS, Clean_Writeback, S) { + c_clearOwner; + ll_checkDataInMemory; + j_popIncomingUnblockQueue; + } + + transition({MI, MIS}, Unblock, M) { + j_popIncomingUnblockQueue; + } + + transition({OS, OSS}, Unblock, O) { + j_popIncomingUnblockQueue; + } + + transition({I, S, O, M, IS, SS, OO, MO, MM, MI, MIS, OS, OSS}, Memory_Data) { + d_sendDataMsg; + q_popMemQueue; + } + + transition({I, S, O, M, IS, SS, OO, MO, MM, MI, MIS, OS, OSS}, Memory_Ack) { + //a_sendAck; + q_popMemQueue; + } + +} diff --git a/src/mem/protocol/MOESI_CMP_directory_m.slicc b/src/mem/protocol/MOESI_CMP_directory_m.slicc new file mode 100644 index 000000000..3abe8603a --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_directory_m.slicc @@ -0,0 +1,5 @@ +MOESI_CMP_directory-msg.sm +MOESI_CMP_directory-L2cache.sm +MOESI_CMP_directory-L1cache.sm +MOESI_CMP_directory_m-dir.sm +standard_CMP-protocol.sm diff --git a/src/mem/protocol/MOESI_CMP_token-L1cache.sm b/src/mem/protocol/MOESI_CMP_token-L1cache.sm new file mode 100644 index 000000000..ab58c5c00 --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_token-L1cache.sm @@ -0,0 +1,2041 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOESI_CMP_token-L1cache.sm 1.22 05/01/19 15:55:39-06:00 beckmann@s0-28.cs.wisc.edu $ + * + */ + +machine(L1Cache, "Token protocol") { + + // From this node's L1 cache TO the network + // a local L1 -> this L2 bank, currently ordered with directory forwarded requests + MessageBuffer requestFromL1Cache, network="To", virtual_network="0", ordered="false"; + // a local L1 -> this L2 bank + MessageBuffer responseFromL1Cache, network="To", virtual_network="2", ordered="false"; + MessageBuffer persistentFromL1Cache, network="To", virtual_network="3", ordered="true"; + + // To this node's L1 cache FROM the network + // a L2 bank -> this L1 + MessageBuffer requestToL1Cache, network="From", virtual_network="0", ordered="false"; + // a L2 bank -> this L1 + MessageBuffer responseToL1Cache, network="From", virtual_network="2", ordered="false"; + MessageBuffer persistentToL1Cache, network="From", virtual_network="3", ordered="true"; + + // STATES + enumeration(State, desc="Cache states", default="L1Cache_State_I") { + // Base states + NP, "NP", desc="Not Present"; + I, "I", desc="Idle"; + S, "S", desc="Shared"; + O, "O", desc="Owned"; + M, "M", desc="Modified (dirty)"; + MM, "MM", desc="Modified (dirty and locally modified)"; + M_W, "M^W", desc="Modified (dirty), waiting"; + MM_W, "MM^W", desc="Modified (dirty and locally modified), waiting"; + + // Transient States + IM, "IM", desc="Issued GetX"; + SM, "SM", desc="Issued GetX, we still have an old copy of the line"; + OM, "OM", desc="Issued GetX, received data"; + IS, "IS", desc="Issued GetS"; + + // Locked states + I_L, "I^L", desc="Invalid, Locked"; + S_L, "S^L", desc="Shared, Locked"; + IM_L, "IM^L", desc="Invalid, Locked, trying to go to Modified"; + SM_L, "SM^L", desc="Shared, Locked, trying to go to Modified"; + IS_L, "IS^L", desc="Invalid, Locked, trying to go to Shared"; + } + + // EVENTS + enumeration(Event, desc="Cache events") { + Load, desc="Load request from the processor"; + Ifetch, desc="I-fetch request from the processor"; + Store, desc="Store request from the processor"; + L1_Replacement, desc="L1 Replacement"; + + // Responses + Data_Shared, desc="Received a data message, we are now a sharer"; + Data_Owner, desc="Received a data message, we are now the owner"; + Data_All_Tokens, desc="Received a data message, we are now the owner, we now have all the tokens"; + Ack, desc="Received an ack message"; + Ack_All_Tokens, desc="Received an ack message, we now have all the tokens"; + + // Requests + Transient_GETX, desc="A GetX from another processor"; + Transient_Local_GETX, desc="A GetX from another processor"; + Transient_GETS, desc="A GetS from another processor"; + Transient_Local_GETS, desc="A GetS from another processor"; + Transient_GETS_Last_Token, desc="A GetS from another processor"; + Transient_Local_GETS_Last_Token, desc="A GetS from another processor"; + + // Lock/Unlock for distributed + Persistent_GETX, desc="Another processor has priority to read/write"; + Persistent_GETS, desc="Another processor has priority to read"; + Own_Lock_or_Unlock, desc="This processor now has priority"; + + // Triggers + Request_Timeout, desc="Timeout"; + Use_TimeoutStarverX, desc="Timeout"; + Use_TimeoutStarverS, desc="Timeout"; + Use_TimeoutNoStarvers, desc="Timeout"; + + } + + // TYPES + + int getRetryThreshold(); + int getFixedTimeoutLatency(); + bool getDynamicTimeoutEnabled(); + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + int Tokens, desc="The number of tokens we're holding for the line"; + DataBlock DataBlk, desc="data for the block"; + } + + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + int IssueCount, default="0", desc="The number of times we've issued a request for this line."; + Address PC, desc="Program counter of request"; + + bool WentPersistent, default="false", desc="Request went persistent"; + bool ExternalResponse, default="false", desc="Response came from an external controller"; + + AccessType AccessType, desc="Type of request (used for profiling)"; + Time IssueTime, desc="Time the request was issued"; + AccessModeType AccessMode, desc="user/supervisor access type"; + PrefetchBit Prefetch, desc="Is this a prefetch request"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + + TBETable L1_TBEs, template_hack=""; + CacheMemory L1IcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1I"', abstract_chip_ptr="true"; + CacheMemory L1DcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1D"', abstract_chip_ptr="true"; + + MessageBuffer mandatoryQueue, ordered="false", abstract_chip_ptr="true"; + Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i"; + + bool starving, default="false"; + + PersistentTable persistentTable, constructor_hack="i"; + TimerTable useTimerTable; + TimerTable reissueTimerTable; + + int outstandingRequests, default="0"; + int outstandingPersistentRequests, default="0"; + + int averageLatencyHysteresis, default="(8)"; // Constant that provides hysteresis for calculated the estimated average + int averageLatencyCounter, default="(500 << (*m_L1Cache_averageLatencyHysteresis_vec[i]))"; + + int averageLatencyEstimate() { + DEBUG_EXPR( (averageLatencyCounter >> averageLatencyHysteresis) ); + profile_average_latency_estimate( (averageLatencyCounter >> averageLatencyHysteresis) ); + return averageLatencyCounter >> averageLatencyHysteresis; + } + + void updateAverageLatencyEstimate(int latency) { + DEBUG_EXPR( latency ); + assert(latency >= 0); + + // By subtracting the current average and then adding the most + // recent sample, we calculate an estimate of the recent average. + // If we simply used a running sum and divided by the total number + // of entries, the estimate of the average would adapt very slowly + // after the execution has run for a long time. + // averageLatencyCounter := averageLatencyCounter - averageLatencyEstimate() + latency; + + averageLatencyCounter := averageLatencyCounter - averageLatencyEstimate() + latency; + } + + + Entry getCacheEntry(Address addr), return_by_ref="yes" { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory[addr]; + } else { + return L1IcacheMemory[addr]; + } + } + + int getTokens(Address addr) { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory[addr].Tokens; + } else if (L1IcacheMemory.isTagPresent(addr)) { + return L1IcacheMemory[addr].Tokens; + } else { + return 0; + } + } + + void changePermission(Address addr, AccessPermission permission) { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory.changePermission(addr, permission); + } else { + return L1IcacheMemory.changePermission(addr, permission); + } + } + + bool isCacheTagPresent(Address addr) { + return (L1DcacheMemory.isTagPresent(addr) || L1IcacheMemory.isTagPresent(addr)); + } + + State getState(Address addr) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + + if (L1_TBEs.isPresent(addr)) { + return L1_TBEs[addr].TBEState; + } else if (isCacheTagPresent(addr)) { + return getCacheEntry(addr).CacheState; + } else { + if ((persistentTable.isLocked(addr) == true) && (persistentTable.findSmallest(addr) != machineID)) { + // Not in cache, in persistent table, but this processor isn't highest priority + return State:I_L; + } else { + return State:NP; + } + } + } + + void setState(Address addr, State state) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + + if (L1_TBEs.isPresent(addr)) { + assert(state != State:I); + assert(state != State:S); + assert(state != State:O); + assert(state != State:MM); + assert(state != State:M); + L1_TBEs[addr].TBEState := state; + } + + if (isCacheTagPresent(addr)) { + // Make sure the token count is in range + assert(getCacheEntry(addr).Tokens >= 0); + assert(getCacheEntry(addr).Tokens <= max_tokens()); + + if ((state == State:I_L) || + (state == State:IM_L) || + (state == State:IS_L)) { + // Make sure we have no tokens in the "Invalid, locked" states + if (isCacheTagPresent(addr)) { + assert(getCacheEntry(addr).Tokens == 0); + } + + // Make sure the line is locked + // assert(persistentTable.isLocked(addr)); + + // But we shouldn't have highest priority for it + // assert(persistentTable.findSmallest(addr) != id); + + } else if ((state == State:S_L) || + (state == State:SM_L)) { + assert(getCacheEntry(addr).Tokens >= 1); + + // Make sure the line is locked... + // assert(persistentTable.isLocked(addr)); + + // ...But we shouldn't have highest priority for it... + // assert(persistentTable.findSmallest(addr) != id); + + // ...And it must be a GETS request + // assert(persistentTable.typeOfSmallest(addr) == AccessType:Read); + + } else { + + // If there is an entry in the persistent table of this block, + // this processor needs to have an entry in the table for this + // block, and that entry better be the smallest (highest + // priority). Otherwise, the state should have been one of + // locked states + + //if (persistentTable.isLocked(addr)) { + // assert(persistentTable.findSmallest(addr) == id); + //} + } + + // in M and E you have all the tokens + if (state == State:MM || state == State:M || state == State:MM_W || state == State:M_W) { + assert(getCacheEntry(addr).Tokens == max_tokens()); + } + + // in NP you have no tokens + if (state == State:NP) { + assert(getCacheEntry(addr).Tokens == 0); + } + + // You have at least one token in S-like states + if (state == State:S || state == State:SM) { + assert(getCacheEntry(addr).Tokens > 0); + } + + // You have at least half the token in O-like states + if (state == State:O && state == State:OM) { + assert(getCacheEntry(addr).Tokens >= 1); // Must have at least one token + assert(getCacheEntry(addr).Tokens >= (max_tokens() / 2)); // Only mostly true; this might not always hold + } + + getCacheEntry(addr).CacheState := state; + + // Set permission + if (state == State:MM || + state == State:MM_W) { + changePermission(addr, AccessPermission:Read_Write); + } else if ((state == State:S) || + (state == State:O) || + (state == State:M) || + (state == State:M_W) || + (state == State:SM) || + (state == State:S_L) || + (state == State:SM_L) || + (state == State:OM)) { + changePermission(addr, AccessPermission:Read_Only); + } else { + changePermission(addr, AccessPermission:Invalid); + } + } + } + + Event mandatory_request_type_to_event(CacheRequestType type) { + if (type == CacheRequestType:LD) { + return Event:Load; + } else if (type == CacheRequestType:IFETCH) { + return Event:Ifetch; + } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { + return Event:Store; + } else { + error("Invalid CacheRequestType"); + } + } + + AccessType cache_request_type_to_access_type(CacheRequestType type) { + if ((type == CacheRequestType:LD) || (type == CacheRequestType:IFETCH)) { + return AccessType:Read; + } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { + return AccessType:Write; + } else { + error("Invalid CacheRequestType"); + } + } + + GenericMachineType getNondirectHitMachType(Address addr, MachineID sender) { + if (machineIDToMachineType(sender) == MachineType:L1Cache) { + return GenericMachineType:L1Cache_wCC; // NOTE direct L1 hits should not call this + } else if (machineIDToMachineType(sender) == MachineType:L2Cache) { + if ( sender == (map_L1CacheMachId_to_L2Cache(addr,machineID))) { + return GenericMachineType:L2Cache; + } else { + return GenericMachineType:L2Cache_wCC; + } + } else { + return ConvertMachToGenericMach(machineIDToMachineType(sender)); + } + } + + bool okToIssueStarving(Address addr) { + return persistentTable.okToIssueStarving(addr); + } + + void markPersistentEntries(Address addr) { + persistentTable.markEntries(addr); + } + + MessageBuffer triggerQueue, ordered="false", random="false"; + + // ** OUT_PORTS ** + out_port(persistentNetwork_out, PersistentMsg, persistentFromL1Cache); + out_port(requestNetwork_out, RequestMsg, requestFromL1Cache); + out_port(responseNetwork_out, ResponseMsg, responseFromL1Cache); + out_port(requestRecycle_out, RequestMsg, requestToL1Cache); + + // ** IN_PORTS ** + + // Use Timer + in_port(useTimerTable_in, Address, useTimerTable) { + if (useTimerTable_in.isReady()) { + if (persistentTable.isLocked(useTimerTable.readyAddress()) && (persistentTable.findSmallest(useTimerTable.readyAddress()) != machineID)) { + if (persistentTable.typeOfSmallest(useTimerTable.readyAddress()) == AccessType:Write) { + trigger(Event:Use_TimeoutStarverX, useTimerTable.readyAddress()); + } + else { + trigger(Event:Use_TimeoutStarverS, useTimerTable.readyAddress()); + } + } + else { + trigger(Event:Use_TimeoutNoStarvers, useTimerTable.readyAddress()); + } + } + } + + // Reissue Timer + in_port(reissueTimerTable_in, Address, reissueTimerTable) { + if (reissueTimerTable_in.isReady()) { + trigger(Event:Request_Timeout, reissueTimerTable.readyAddress()); + } + } + + + + // Persistent Network + in_port(persistentNetwork_in, PersistentMsg, persistentToL1Cache) { + if (persistentNetwork_in.isReady()) { + peek(persistentNetwork_in, PersistentMsg) { + assert(in_msg.Destination.isElement(machineID)); + + // Apply the lockdown or unlockdown message to the table + if (in_msg.Type == PersistentRequestType:GETX_PERSISTENT) { + persistentTable.persistentRequestLock(in_msg.Address, in_msg.Requestor, AccessType:Write); + } else if (in_msg.Type == PersistentRequestType:GETS_PERSISTENT) { + persistentTable.persistentRequestLock(in_msg.Address, in_msg.Requestor, AccessType:Read); + } else if (in_msg.Type == PersistentRequestType:DEACTIVATE_PERSISTENT) { + persistentTable.persistentRequestUnlock(in_msg.Address, in_msg.Requestor); + } else { + error("Unexpected message"); + } + + // React to the message based on the current state of the table + if (persistentTable.isLocked(in_msg.Address)) { + if (persistentTable.findSmallest(in_msg.Address) == machineID) { + // Our Own Lock - this processor is highest priority + trigger(Event:Own_Lock_or_Unlock, in_msg.Address); + } else { + if (persistentTable.typeOfSmallest(in_msg.Address) == AccessType:Read) { + trigger(Event:Persistent_GETS, in_msg.Address); + } else { + trigger(Event:Persistent_GETX, in_msg.Address); + } + } + } else { + // Unlock case - no entries in the table + trigger(Event:Own_Lock_or_Unlock, in_msg.Address); + } + } + } + } + + + // Request Network + in_port(requestNetwork_in, RequestMsg, requestToL1Cache) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceRequestType:GETX) { + if (in_msg.isLocal) { + trigger(Event:Transient_Local_GETX, in_msg.Address); + } + else { + trigger(Event:Transient_GETX, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:GETS) { + if ( (L1DcacheMemory.isTagPresent(in_msg.Address) || L1IcacheMemory.isTagPresent(in_msg.Address)) && getCacheEntry(in_msg.Address).Tokens == 1) { + if (in_msg.isLocal) { + trigger(Event:Transient_Local_GETS_Last_Token, in_msg.Address); + } + else { + trigger(Event:Transient_GETS_Last_Token, in_msg.Address); + } + } + else { + if (in_msg.isLocal) { + trigger(Event:Transient_Local_GETS, in_msg.Address); + } + else { + trigger(Event:Transient_GETS, in_msg.Address); + } + } + } else { + error("Unexpected message"); + } + } + } + } + + // Response Network + in_port(responseNetwork_in, ResponseMsg, responseToL1Cache) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + + // Mark TBE flag if response received off-chip. Use this to update average latency estimate + if ( in_msg.SenderMachine == MachineType:L2Cache ) { + + if (in_msg.Sender == map_L1CacheMachId_to_L2Cache(in_msg.Address, machineID)) { + // came from an off-chip L2 cache + if (L1_TBEs.isPresent(in_msg.Address)) { + // L1_TBEs[in_msg.Address].ExternalResponse := true; + // profile_offchipL2_response(in_msg.Address); + } + } + else { + // profile_onchipL2_response(in_msg.Address ); + } + } else if ( in_msg.SenderMachine == MachineType:Directory ) { + if (L1_TBEs.isPresent(in_msg.Address)) { + L1_TBEs[in_msg.Address].ExternalResponse := true; + // profile_memory_response( in_msg.Address); + } + } else if ( in_msg.SenderMachine == MachineType:L1Cache) { + if (isLocalProcessor(machineID, in_msg.Sender) == false) { + if (L1_TBEs.isPresent(in_msg.Address)) { + // L1_TBEs[in_msg.Address].ExternalResponse := true; + // profile_offchipL1_response(in_msg.Address ); + } + } + else { + // profile_onchipL1_response(in_msg.Address ); + } + } else { + error("unexpected SenderMachine"); + } + + + if (getTokens(in_msg.Address) + in_msg.Tokens != max_tokens()) { + if (in_msg.Type == CoherenceResponseType:ACK) { + trigger(Event:Ack, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_OWNER) { + trigger(Event:Data_Owner, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_SHARED) { + trigger(Event:Data_Shared, in_msg.Address); + } else { + error("Unexpected message"); + } + } else { + if (in_msg.Type == CoherenceResponseType:ACK) { + trigger(Event:Ack_All_Tokens, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_OWNER || in_msg.Type == CoherenceResponseType:DATA_SHARED) { + trigger(Event:Data_All_Tokens, in_msg.Address); + } else { + error("Unexpected message"); + } + } + } + } + } + + // Mandatory Queue + in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { + if (mandatoryQueue_in.isReady()) { + peek(mandatoryQueue_in, CacheMsg) { + // Check for data access to blocks in I-cache and ifetchs to blocks in D-cache + + if (in_msg.Type == CacheRequestType:IFETCH) { + // ** INSTRUCTION ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, try to write it to the L2 + trigger(Event:L1_Replacement, in_msg.Address); + } + + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 fetches the line. We know it can't be in the L2 due to exclusion + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1IcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // No room in the L1, so we need to make room + trigger(Event:L1_Replacement, L1IcacheMemory.cacheProbe(in_msg.Address)); + } + } + } else { + // *** DATA ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, try to write it to the L2 + trigger(Event:L1_Replacement, in_msg.Address); + } + + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 fetches the line. We know it can't be in the L2 due to exclusion + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1DcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // No room in the L1, so we need to make room + trigger(Event:L1_Replacement, L1DcacheMemory.cacheProbe(in_msg.Address)); + } + } + } + } + } + } + + // ACTIONS + + action(a_issueReadRequest, "a", desc="Issue GETS") { + if (L1_TBEs[address].IssueCount == 0) { + // Update outstanding requests + profile_outstanding_request(outstandingRequests); + outstandingRequests := outstandingRequests + 1; + } + + if (L1_TBEs[address].IssueCount >= getRetryThreshold() ) { + // Issue a persistent request if possible + if (okToIssueStarving(address) && (starving == false)) { + enqueue(persistentNetwork_out, PersistentMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := PersistentRequestType:GETS_PERSISTENT; + out_msg.Requestor := machineID; + out_msg.Destination.broadcast(MachineType:L1Cache); + out_msg.Destination.addNetDest(getAllPertinentL2Banks(address)); + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Persistent_Control; + out_msg.Prefetch := L1_TBEs[address].Prefetch; + out_msg.AccessMode := L1_TBEs[address].AccessMode; + } + markPersistentEntries(address); + starving := true; + + if (L1_TBEs[address].IssueCount == 0) { + profile_persistent_prediction(address, L1_TBEs[address].AccessType); + } + + // Update outstanding requests + profile_outstanding_persistent_request(outstandingPersistentRequests); + outstandingPersistentRequests := outstandingPersistentRequests + 1; + + // Increment IssueCount + L1_TBEs[address].IssueCount := L1_TBEs[address].IssueCount + 1; + + L1_TBEs[address].WentPersistent := true; + + // Do not schedule a wakeup, a persistent requests will always complete + } + else { + + // We'd like to issue a persistent request, but are not allowed + // to issue a P.R. right now. This, we do not increment the + // IssueCount. + + // Set a wakeup timer + reissueTimerTable.set(address, 10); + + } + } else { + // Make a normal request + enqueue(requestNetwork_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address,machineID)); + out_msg.RetryNum := L1_TBEs[address].IssueCount; + if (L1_TBEs[address].IssueCount == 0) { + out_msg.MessageSize := MessageSizeType:Request_Control; + } else { + out_msg.MessageSize := MessageSizeType:Reissue_Control; + } + out_msg.Prefetch := L1_TBEs[address].Prefetch; + out_msg.AccessMode := L1_TBEs[address].AccessMode; + } + + // send to other local L1s, with local bit set + enqueue(requestNetwork_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.Requestor := machineID; + out_msg.Destination := getOtherLocalL1IDs(machineID); + out_msg.RetryNum := L1_TBEs[address].IssueCount; + out_msg.isLocal := true; + if (L1_TBEs[address].IssueCount == 0) { + out_msg.MessageSize := MessageSizeType:Request_Control; + } else { + out_msg.MessageSize := MessageSizeType:Reissue_Control; + } + out_msg.Prefetch := L1_TBEs[address].Prefetch; + out_msg.AccessMode := L1_TBEs[address].AccessMode; + } + + // Increment IssueCount + L1_TBEs[address].IssueCount := L1_TBEs[address].IssueCount + 1; + + // Set a wakeup timer + + if (getDynamicTimeoutEnabled()) { + reissueTimerTable.set(address, 1.25 * averageLatencyEstimate()); + } else { + reissueTimerTable.set(address, getFixedTimeoutLatency()); + } + + } + } + + action(b_issueWriteRequest, "b", desc="Issue GETX") { + + if (L1_TBEs[address].IssueCount == 0) { + // Update outstanding requests + profile_outstanding_request(outstandingRequests); + outstandingRequests := outstandingRequests + 1; + } + + if (L1_TBEs[address].IssueCount >= getRetryThreshold() ) { + // Issue a persistent request if possible + if ( okToIssueStarving(address) && (starving == false)) { + enqueue(persistentNetwork_out, PersistentMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := PersistentRequestType:GETX_PERSISTENT; + out_msg.Requestor := machineID; + out_msg.RequestorMachine := MachineType:L1Cache; + out_msg.Destination.broadcast(MachineType:L1Cache); + out_msg.Destination.addNetDest(getAllPertinentL2Banks(address)); + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Persistent_Control; + out_msg.Prefetch := L1_TBEs[address].Prefetch; + out_msg.AccessMode := L1_TBEs[address].AccessMode; + } + markPersistentEntries(address); + starving := true; + + // Update outstanding requests + profile_outstanding_persistent_request(outstandingPersistentRequests); + outstandingPersistentRequests := outstandingPersistentRequests + 1; + + if (L1_TBEs[address].IssueCount == 0) { + profile_persistent_prediction(address, L1_TBEs[address].AccessType); + } + + // Increment IssueCount + L1_TBEs[address].IssueCount := L1_TBEs[address].IssueCount + 1; + + L1_TBEs[address].WentPersistent := true; + + // Do not schedule a wakeup, a persistent requests will always complete + } + else { + + // We'd like to issue a persistent request, but are not allowed + // to issue a P.R. right now. This, we do not increment the + // IssueCount. + + // Set a wakeup timer + reissueTimerTable.set(address, 10); + } + + + } else { + // Make a normal request + enqueue(requestNetwork_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.Requestor := machineID; + out_msg.RequestorMachine := MachineType:L1Cache; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address,machineID)); + out_msg.RetryNum := L1_TBEs[address].IssueCount; + + if (L1_TBEs[address].IssueCount == 0) { + out_msg.MessageSize := MessageSizeType:Request_Control; + } else { + out_msg.MessageSize := MessageSizeType:Reissue_Control; + } + out_msg.Prefetch := L1_TBEs[address].Prefetch; + out_msg.AccessMode := L1_TBEs[address].AccessMode; + } + + // send to other local L1s too + enqueue(requestNetwork_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.Requestor := machineID; + out_msg.isLocal := true; + out_msg.Destination := getOtherLocalL1IDs(machineID); + out_msg.RetryNum := L1_TBEs[address].IssueCount; + if (L1_TBEs[address].IssueCount == 0) { + out_msg.MessageSize := MessageSizeType:Request_Control; + } else { + out_msg.MessageSize := MessageSizeType:Reissue_Control; + } + out_msg.Prefetch := L1_TBEs[address].Prefetch; + out_msg.AccessMode := L1_TBEs[address].AccessMode; + } + + // Increment IssueCount + L1_TBEs[address].IssueCount := L1_TBEs[address].IssueCount + 1; + + DEBUG_EXPR("incremented issue count"); + DEBUG_EXPR(L1_TBEs[address].IssueCount); + + // Set a wakeup timer + if (getDynamicTimeoutEnabled()) { + reissueTimerTable.set(address, 1.25 * averageLatencyEstimate()); + } else { + reissueTimerTable.set(address, getFixedTimeoutLatency()); + } + } + } + + action(bb_bounceResponse, "\b", desc="Bounce tokens and data to memory") { + peek(responseNetwork_in, ResponseMsg) { + // FIXME, should use a 3rd vnet + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.Tokens := in_msg.Tokens; + out_msg.MessageSize := in_msg.MessageSize; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := in_msg.Dirty; + } + } + } + + action(c_ownedReplacement, "c", desc="Issue writeback") { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address,machineID)); + out_msg.Tokens := getCacheEntry(address).Tokens; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.Type := CoherenceResponseType:WB_OWNED; + + // always send the data? + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } + getCacheEntry(address).Tokens := 0; + } + + action(cc_sharedReplacement, "\c", desc="Issue dirty writeback") { + + // don't send writeback if replacing block with no tokens + if (getCacheEntry(address).Tokens != 0) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address,machineID)); + out_msg.Tokens := getCacheEntry(address).Tokens; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + // assert(getCacheEntry(address).Dirty == false); + out_msg.Dirty := false; + + // always send the data? + if (getCacheEntry(address).Tokens > 1) { + out_msg.MessageSize := MessageSizeType:Writeback_Data; + out_msg.Type := CoherenceResponseType:WB_SHARED_DATA; + } else { + out_msg.MessageSize := MessageSizeType:Writeback_Control; + out_msg.Type := CoherenceResponseType:WB_TOKENS; + } + } + getCacheEntry(address).Tokens := 0; + } + } + + + action(d_sendDataWithToken, "d", desc="Send data and a token from cache to requestor") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_SHARED; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.Tokens := 1; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + // out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.Dirty := false; + if (in_msg.isLocal) { + out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; + } else { + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + getCacheEntry(address).Tokens := getCacheEntry(address).Tokens - 1; + assert(getCacheEntry(address).Tokens >= 1); + } + + action(d_sendDataWithNTokenIfAvail, "\dd", desc="Send data and a token from cache to requestor") { + peek(requestNetwork_in, RequestMsg) { + if (getCacheEntry(address).Tokens > N_tokens()) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_SHARED; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.Tokens := N_tokens(); + out_msg.DataBlk := getCacheEntry(address).DataBlk; + // out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.Dirty := false; + if (in_msg.isLocal) { + out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; + } else { + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + getCacheEntry(address).Tokens := getCacheEntry(address).Tokens - N_tokens(); + } + else if (getCacheEntry(address).Tokens > 1) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_SHARED; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.Tokens := 1; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + // out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.Dirty := false; + if (in_msg.isLocal) { + out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; + } else { + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + getCacheEntry(address).Tokens := getCacheEntry(address).Tokens - 1; + } + } +// assert(getCacheEntry(address).Tokens >= 1); + } + + action(dd_sendDataWithAllTokens, "\d", desc="Send data and all tokens from cache to requestor") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(in_msg.Requestor); + assert(getCacheEntry(address).Tokens >= 1); + out_msg.Tokens := getCacheEntry(address).Tokens; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + if (in_msg.isLocal) { + out_msg.MessageSize := MessageSizeType:ResponseLocal_Data; + } else { + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + getCacheEntry(address).Tokens := 0; + } + + action(e_sendAckWithCollectedTokens, "e", desc="Send ack with the tokens we've collected thus far.") { + // assert(persistentTable.findSmallest(address) != id); // Make sure we never bounce tokens to ourself + if (getCacheEntry(address).Tokens > 0) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + assert(getCacheEntry(address).Tokens >= 1); + out_msg.Tokens := getCacheEntry(address).Tokens; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + getCacheEntry(address).Tokens := 0; + } + + action(ee_sendDataWithAllTokens, "\e", desc="Send data and all tokens from cache to starver") { + //assert(persistentTable.findSmallest(address) != id); // Make sure we never bounce tokens to ourself + assert(getCacheEntry(address).Tokens > 0); + enqueue(responseNetwork_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + assert(getCacheEntry(address).Tokens >= 1); + out_msg.Tokens := getCacheEntry(address).Tokens; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + getCacheEntry(address).Tokens := 0; + } + + action(f_sendAckWithAllButNorOneTokens, "f", desc="Send ack with all our tokens but one to starver.") { + //assert(persistentTable.findSmallest(address) != id); // Make sure we never bounce tokens to ourself + assert(getCacheEntry(address).Tokens > 0); + if (getCacheEntry(address).Tokens > 1) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + assert(getCacheEntry(address).Tokens >= 1); + if (getCacheEntry(address).Tokens > N_tokens()) { + out_msg.Tokens := getCacheEntry(address).Tokens - N_tokens(); + } else { + out_msg.Tokens := getCacheEntry(address).Tokens - 1; + } + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + if (getCacheEntry(address).Tokens > N_tokens()) { + getCacheEntry(address).Tokens := N_tokens(); + } else { + getCacheEntry(address).Tokens := 1; + } + } + + action(ff_sendDataWithAllButNorOneTokens, "\f", desc="Send data and out tokens but one to starver") { + //assert(persistentTable.findSmallest(address) != id); // Make sure we never bounce tokens to ourself + assert(getCacheEntry(address).Tokens > 0); + if (getCacheEntry(address).Tokens > 1) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + assert(getCacheEntry(address).Tokens >= 1); + if (getCacheEntry(address).Tokens > N_tokens()) { + out_msg.Tokens := getCacheEntry(address).Tokens - N_tokens(); + } else { + out_msg.Tokens := getCacheEntry(address).Tokens - 1; + } + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + if (getCacheEntry(address).Tokens > N_tokens()) { + getCacheEntry(address).Tokens := N_tokens(); + } else { + getCacheEntry(address).Tokens := 1; + } + } + } + + action(g_bounceResponseToStarver, "g", desc="Redirect response to starving processor") { + // assert(persistentTable.isLocked(address)); + + peek(responseNetwork_in, ResponseMsg) { + // assert(persistentTable.findSmallest(address) != id); // Make sure we never bounce tokens to ourself + // FIXME, should use a 3rd vnet in some cases + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.Tokens := in_msg.Tokens; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := in_msg.Dirty; + out_msg.MessageSize := in_msg.MessageSize; + } + } + } + + + action(h_load_hit, "h", desc="Notify sequencer the load completed.") { + DEBUG_EXPR(address); + DEBUG_EXPR(getCacheEntry(address).DataBlk); + sequencer.readCallback(address, getCacheEntry(address).DataBlk, GenericMachineType:L1Cache, PrefetchBit:No); + } + + action(x_external_load_hit, "x", desc="Notify sequencer the load completed.") { + DEBUG_EXPR(address); + DEBUG_EXPR(getCacheEntry(address).DataBlk); + peek(responseNetwork_in, ResponseMsg) { + + sequencer.readCallback(address, getCacheEntry(address).DataBlk, getNondirectHitMachType(in_msg.Address, in_msg.Sender), PrefetchBit:No); + } + } + + action(hh_store_hit, "\h", desc="Notify sequencer that store completed.") { + DEBUG_EXPR(address); + DEBUG_EXPR(getCacheEntry(address).DataBlk); + sequencer.writeCallback(address, getCacheEntry(address).DataBlk, GenericMachineType:L1Cache, PrefetchBit:No); + getCacheEntry(address).Dirty := true; + DEBUG_EXPR(getCacheEntry(address).DataBlk); + } + + action(xx_external_store_hit, "\x", desc="Notify sequencer that store completed.") { + DEBUG_EXPR(address); + DEBUG_EXPR(getCacheEntry(address).DataBlk); + peek(responseNetwork_in, ResponseMsg) { + sequencer.writeCallback(address, getCacheEntry(address).DataBlk, getNondirectHitMachType(in_msg.Address, in_msg.Sender), PrefetchBit:No); + } + getCacheEntry(address).Dirty := true; + DEBUG_EXPR(getCacheEntry(address).DataBlk); + } + + action(i_allocateTBE, "i", desc="Allocate TBE") { + check_allocate(L1_TBEs); + L1_TBEs.allocate(address); + L1_TBEs[address].IssueCount := 0; + peek(mandatoryQueue_in, CacheMsg) { + L1_TBEs[address].PC := in_msg.ProgramCounter; + L1_TBEs[address].AccessType := cache_request_type_to_access_type(in_msg.Type); + L1_TBEs[address].Prefetch := in_msg.Prefetch; + L1_TBEs[address].AccessMode := in_msg.AccessMode; + } + L1_TBEs[address].IssueTime := get_time(); + } + + + action(j_unsetReissueTimer, "j", desc="Unset reissue timer.") { + if (reissueTimerTable.isSet(address)) { + reissueTimerTable.unset(address); + } + } + + action(jj_unsetUseTimer, "\j", desc="Unset use timer.") { + useTimerTable.unset(address); + } + + + + action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") { + mandatoryQueue_in.dequeue(); + } + + action(l_popPersistentQueue, "l", desc="Pop persistent queue.") { + persistentNetwork_in.dequeue(); + } + + action(m_popRequestQueue, "m", desc="Pop request queue.") { + requestNetwork_in.dequeue(); + } + + action(n_popResponseQueue, "n", desc="Pop response queue") { + responseNetwork_in.dequeue(); + } + + action(o_scheduleUseTimeout, "o", desc="Schedule a use timeout.") { + useTimerTable.set(address, 50); + } + + action(p_informL2AboutTokenLoss, "p", desc="Inform L2 about loss of all tokens") { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:INV; + out_msg.Tokens := 0; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.DestMachine := MachineType:L2Cache; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address,machineID)); + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + + + action(q_updateTokensFromResponse, "q", desc="Update the token count based on the incoming response message") { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Tokens != 0); + DEBUG_EXPR("MRM_DEBUG L1 received tokens"); + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.Tokens); + getCacheEntry(address).Tokens := getCacheEntry(address).Tokens + in_msg.Tokens; + DEBUG_EXPR(getCacheEntry(address).Tokens); + + if (getCacheEntry(address).Dirty == false && in_msg.Dirty) { + getCacheEntry(address).Dirty := true; + } + } + } + + action(s_deallocateTBE, "s", desc="Deallocate TBE") { + + if (L1_TBEs[address].WentPersistent) { + // assert(starving == true); + outstandingRequests := outstandingRequests - 1; + enqueue(persistentNetwork_out, PersistentMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := PersistentRequestType:DEACTIVATE_PERSISTENT; + out_msg.Requestor := machineID; + out_msg.RequestorMachine := MachineType:L1Cache; + out_msg.Destination.broadcast(MachineType:L1Cache); + out_msg.Destination.addNetDest(getAllPertinentL2Banks(address)); + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Persistent_Control; + } + starving := false; + } + + // Update average latency + if (L1_TBEs[address].IssueCount <= 1) { + if (L1_TBEs[address].ExternalResponse == true) { + updateAverageLatencyEstimate(time_to_int(get_time()) - time_to_int(L1_TBEs[address].IssueTime)); + } + } + + // Profile + //if (L1_TBEs[address].WentPersistent) { + // profile_token_retry(address, L1_TBEs[address].AccessType, 2); + //} + //else { + // profile_token_retry(address, L1_TBEs[address].AccessType, 1); + //} + + profile_token_retry(address, L1_TBEs[address].AccessType, L1_TBEs[address].IssueCount); + L1_TBEs.deallocate(address); + } + + action(t_sendAckWithCollectedTokens, "t", desc="Send ack with the tokens we've collected thus far.") { + if (getCacheEntry(address).Tokens > 0) { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(in_msg.Requestor); + assert(getCacheEntry(address).Tokens >= 1); + out_msg.Tokens := getCacheEntry(address).Tokens; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + getCacheEntry(address).Tokens := 0; + } + + action(u_writeDataToCache, "u", desc="Write data to cache") { + peek(responseNetwork_in, ResponseMsg) { + getCacheEntry(address).DataBlk := in_msg.DataBlk; + if (getCacheEntry(address).Dirty == false && in_msg.Dirty) { + getCacheEntry(address).Dirty := in_msg.Dirty; + } + + } + } + + action(gg_deallocateL1CacheBlock, "\g", desc="Deallocate cache block. Sets the cache to invalid, allowing a replacement in parallel with a fetch.") { + if (L1DcacheMemory.isTagPresent(address)) { + L1DcacheMemory.deallocate(address); + } else { + L1IcacheMemory.deallocate(address); + } + } + + action(ii_allocateL1DCacheBlock, "\i", desc="Set L1 D-cache tag equal to tag of block B.") { + if (L1DcacheMemory.isTagPresent(address) == false) { + L1DcacheMemory.allocate(address); + } + } + + action(pp_allocateL1ICacheBlock, "\p", desc="Set L1 I-cache tag equal to tag of block B.") { + if (L1IcacheMemory.isTagPresent(address) == false) { + L1IcacheMemory.allocate(address); + } + } + + action(uu_profileMiss, "\u", desc="Profile the demand miss") { + peek(mandatoryQueue_in, CacheMsg) { + // profile_miss(in_msg, id); + } + } + + action(w_assertIncomingDataAndCacheDataMatch, "w", desc="Assert that the incoming data and the data in the cache match") { + peek(responseNetwork_in, ResponseMsg) { + assert(getCacheEntry(address).DataBlk == in_msg.DataBlk); + } + } + + + action(z_stall, "z", desc="Stall") { + + } + + action(zz_recycleMandatoryQueue, "\z", desc="Send the head of the mandatory queue to the back of the queue.") { + mandatoryQueue_in.recycle(); + } + + //***************************************************** + // TRANSITIONS + //***************************************************** + + // Transitions for Load/Store/L2_Replacement from transient states + transition({IM, SM, OM, IS, IM_L, IS_L, I_L, S_L, SM_L, M_W, MM_W}, L1_Replacement) { + zz_recycleMandatoryQueue; + } + + transition({IM, SM, OM, IS, IM_L, IS_L, SM_L}, Store) { + zz_recycleMandatoryQueue; + } + + transition({IM, IS, IM_L, IS_L}, {Load, Ifetch}) { + zz_recycleMandatoryQueue; + } + + + // Lockdowns + transition({NP, I, S, O, M, MM, M_W, MM_W, IM, SM, OM, IS}, Own_Lock_or_Unlock) { + l_popPersistentQueue; + } + + // Transitions from NP + transition(NP, Load, IS) { + ii_allocateL1DCacheBlock; + i_allocateTBE; + a_issueReadRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(NP, Ifetch, IS) { + pp_allocateL1ICacheBlock; + i_allocateTBE; + a_issueReadRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(NP, Store, IM) { + ii_allocateL1DCacheBlock; + i_allocateTBE; + b_issueWriteRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(NP, {Ack, Data_Shared, Data_Owner, Data_All_Tokens}) { + bb_bounceResponse; + n_popResponseQueue; + } + + transition(NP, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_Local_GETS}) { + m_popRequestQueue; + } + + transition(NP, {Persistent_GETX, Persistent_GETS}, I_L) { + l_popPersistentQueue; + } + + // Transitions from Idle + transition(I, Load, IS) { + i_allocateTBE; + a_issueReadRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I, Ifetch, IS) { + i_allocateTBE; + a_issueReadRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I, Store, IM) { + i_allocateTBE; + b_issueWriteRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I, L1_Replacement) { + cc_sharedReplacement; + gg_deallocateL1CacheBlock; + } + + transition(I, {Transient_GETX, Transient_Local_GETX}) { + t_sendAckWithCollectedTokens; + m_popRequestQueue; + } + + transition(I, {Transient_GETS, Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token, Transient_Local_GETS}) { + m_popRequestQueue; + } + + transition(I, {Persistent_GETX, Persistent_GETS}, I_L) { + e_sendAckWithCollectedTokens; + l_popPersistentQueue; + } + + transition(I_L, {Persistent_GETX, Persistent_GETS}) { + l_popPersistentQueue; + } + + transition(I, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(I, Data_Shared, S) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(I, Data_Owner, O) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(I, Data_All_Tokens, M) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + // Transitions from Shared + transition({S, SM, S_L, SM_L}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(S, Store, SM) { + i_allocateTBE; + b_issueWriteRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(S, L1_Replacement, I) { + cc_sharedReplacement; // Only needed in some cases + gg_deallocateL1CacheBlock; + } + + transition(S, {Transient_GETX, Transient_Local_GETX}, I) { + t_sendAckWithCollectedTokens; + p_informL2AboutTokenLoss; + m_popRequestQueue; + } + + // only owner responds to non-local requests + transition(S, Transient_GETS) { + m_popRequestQueue; + } + + transition(S, Transient_Local_GETS) { + d_sendDataWithToken; + m_popRequestQueue; + } + + transition(S, {Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token}) { + m_popRequestQueue; + } + + transition({S, S_L}, Persistent_GETX, I_L) { + e_sendAckWithCollectedTokens; + p_informL2AboutTokenLoss; + l_popPersistentQueue; + } + + transition(S, Persistent_GETS, S_L) { + f_sendAckWithAllButNorOneTokens; + l_popPersistentQueue; + } + + transition(S_L, Persistent_GETS) { + l_popPersistentQueue; + } + + transition(S, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(S, Data_Shared) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(S, Data_Owner, O) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(S, Data_All_Tokens, M) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + // Transitions from Owned + transition({O, OM}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(O, Store, OM) { + i_allocateTBE; + b_issueWriteRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(O, L1_Replacement, I) { + c_ownedReplacement; + gg_deallocateL1CacheBlock; + } + + transition(O, {Transient_GETX, Transient_Local_GETX}, I) { + dd_sendDataWithAllTokens; + p_informL2AboutTokenLoss; + m_popRequestQueue; + } + + transition(O, Persistent_GETX, I_L) { + ee_sendDataWithAllTokens; + p_informL2AboutTokenLoss; + l_popPersistentQueue; + } + + transition(O, Persistent_GETS, S_L) { + ff_sendDataWithAllButNorOneTokens; + l_popPersistentQueue; + } + + transition(O, Transient_GETS) { + d_sendDataWithToken; + m_popRequestQueue; + } + + transition(O, Transient_Local_GETS) { + d_sendDataWithToken; + m_popRequestQueue; + } + + // ran out of tokens, wait for it to go persistent + transition(O, {Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token}) { + m_popRequestQueue; + } + + transition(O, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(O, Ack_All_Tokens, M) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(O, Data_Shared) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(O, Data_All_Tokens, M) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + // Transitions from Modified + transition({MM, MM_W}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition({MM, MM_W}, Store) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(MM, L1_Replacement, I) { + c_ownedReplacement; + gg_deallocateL1CacheBlock; + } + + transition(MM, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_Local_GETS}, I) { + dd_sendDataWithAllTokens; + p_informL2AboutTokenLoss; + m_popRequestQueue; + } + + transition({MM_W}, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_Local_GETS}) { // Ignore the request + m_popRequestQueue; + } + + // Implement the migratory sharing optimization, even for persistent requests + transition(MM, {Persistent_GETX, Persistent_GETS}, I_L) { + ee_sendDataWithAllTokens; + p_informL2AboutTokenLoss; + l_popPersistentQueue; + } + + // ignore persistent requests in lockout period + transition(MM_W, {Persistent_GETX, Persistent_GETS}) { + l_popPersistentQueue; + } + + + transition(MM_W, Use_TimeoutNoStarvers, MM) { + s_deallocateTBE; + jj_unsetUseTimer; + } + + // Transitions from Dirty Exclusive + transition({M, M_W}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(M, Store, MM) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(M_W, Store, MM_W) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(M, L1_Replacement, I) { + c_ownedReplacement; + gg_deallocateL1CacheBlock; + } + + transition(M, {Transient_GETX, Transient_Local_GETX}, I) { + dd_sendDataWithAllTokens; + p_informL2AboutTokenLoss; + m_popRequestQueue; + } + + transition(M, Transient_Local_GETS, O) { + d_sendDataWithToken; + m_popRequestQueue; + } + + transition(M, Transient_GETS, O) { + d_sendDataWithNTokenIfAvail; + m_popRequestQueue; + } + + transition(M_W, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_Local_GETS}) { // Ignore the request + m_popRequestQueue; + } + + transition(M, Persistent_GETX, I_L) { + ee_sendDataWithAllTokens; + p_informL2AboutTokenLoss; + l_popPersistentQueue; + } + + transition(M, Persistent_GETS, S_L) { + ff_sendDataWithAllButNorOneTokens; + l_popPersistentQueue; + } + + // ignore persistent requests in lockout period + transition(M_W, {Persistent_GETX, Persistent_GETS}) { + l_popPersistentQueue; + } + + transition(M_W, Use_TimeoutStarverS, S_L) { + s_deallocateTBE; + ff_sendDataWithAllButNorOneTokens; + jj_unsetUseTimer; + } + + // someone unlocked during timeout + transition(M_W, Use_TimeoutNoStarvers, M) { + s_deallocateTBE; + jj_unsetUseTimer; + } + + transition(M_W, Use_TimeoutStarverX, I_L) { + s_deallocateTBE; + ee_sendDataWithAllTokens; + p_informL2AboutTokenLoss; + jj_unsetUseTimer; + } + + + + // migratory + transition(MM_W, {Use_TimeoutStarverX, Use_TimeoutStarverS}, I_L) { + s_deallocateTBE; + ee_sendDataWithAllTokens; + p_informL2AboutTokenLoss; + jj_unsetUseTimer; + + } + + + // Transient_GETX and Transient_GETS in transient states + transition(OM, {Transient_GETX, Transient_Local_GETX, Transient_GETS, Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token, Transient_Local_GETS}) { + m_popRequestQueue; // Even if we have the data, we can pretend we don't have it yet. + } + + transition(IS, {Transient_GETX, Transient_Local_GETX}) { + t_sendAckWithCollectedTokens; + m_popRequestQueue; + } + + transition(IS, {Transient_GETS, Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token, Transient_Local_GETS}) { + m_popRequestQueue; + } + + transition(IS, {Persistent_GETX, Persistent_GETS}, IS_L) { + e_sendAckWithCollectedTokens; + l_popPersistentQueue; + } + + transition(IS_L, {Persistent_GETX, Persistent_GETS}) { + l_popPersistentQueue; + } + + transition(IM, {Persistent_GETX, Persistent_GETS}, IM_L) { + e_sendAckWithCollectedTokens; + l_popPersistentQueue; + } + + transition(IM_L, {Persistent_GETX, Persistent_GETS}) { + l_popPersistentQueue; + } + + transition({SM, SM_L}, Persistent_GETX, IM_L) { + e_sendAckWithCollectedTokens; + l_popPersistentQueue; + } + + transition(SM, Persistent_GETS, SM_L) { + f_sendAckWithAllButNorOneTokens; + l_popPersistentQueue; + } + + transition(SM_L, Persistent_GETS) { + l_popPersistentQueue; + } + + transition(OM, Persistent_GETX, IM_L) { + ee_sendDataWithAllTokens; + l_popPersistentQueue; + } + + transition(OM, Persistent_GETS, SM_L) { + ff_sendDataWithAllButNorOneTokens; + l_popPersistentQueue; + } + + // Transitions from IM/SM + + transition({IM, SM}, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(IM, Data_Shared, SM) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(IM, Data_Owner, OM) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(IM, Data_All_Tokens, MM_W) { + u_writeDataToCache; + q_updateTokensFromResponse; + xx_external_store_hit; + o_scheduleUseTimeout; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(SM, Data_Shared) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(SM, Data_Owner, OM) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(SM, Data_All_Tokens, MM_W) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + xx_external_store_hit; + o_scheduleUseTimeout; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition({IM, SM}, {Transient_GETX, Transient_Local_GETX}, IM) { // We don't have the data yet, but we might have collected some tokens. We give them up here to avoid livelock + t_sendAckWithCollectedTokens; + m_popRequestQueue; + } + + transition({IM, SM}, {Transient_GETS, Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token, Transient_Local_GETS}) { + m_popRequestQueue; + } + + transition({IM, SM}, Request_Timeout) { + j_unsetReissueTimer; + b_issueWriteRequest; + } + + // Transitions from OM + + transition(OM, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(OM, Ack_All_Tokens, MM_W) { + q_updateTokensFromResponse; + xx_external_store_hit; + o_scheduleUseTimeout; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(OM, Data_Shared) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(OM, Data_All_Tokens, MM_W) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + xx_external_store_hit; + o_scheduleUseTimeout; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(OM, Request_Timeout) { + j_unsetReissueTimer; + b_issueWriteRequest; + } + + // Transitions from IS + + transition(IS, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(IS, Data_Shared, S) { + u_writeDataToCache; + q_updateTokensFromResponse; + x_external_load_hit; + s_deallocateTBE; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(IS, Data_Owner, O) { + u_writeDataToCache; + q_updateTokensFromResponse; + x_external_load_hit; + s_deallocateTBE; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(IS, Data_All_Tokens, M_W) { + u_writeDataToCache; + q_updateTokensFromResponse; + x_external_load_hit; + o_scheduleUseTimeout; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(IS, Request_Timeout) { + j_unsetReissueTimer; + a_issueReadRequest; + } + + // Transitions from I_L + + transition(I_L, Load, IS_L) { + ii_allocateL1DCacheBlock; + i_allocateTBE; + a_issueReadRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I_L, Ifetch, IS_L) { + pp_allocateL1ICacheBlock; + i_allocateTBE; + a_issueReadRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I_L, Store, IM_L) { + ii_allocateL1DCacheBlock; + i_allocateTBE; + b_issueWriteRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + + // Transitions from S_L + + transition(S_L, Store, SM_L) { + i_allocateTBE; + b_issueWriteRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + // Other transitions from *_L states + + transition({I_L, IM_L, IS_L, S_L, SM_L}, {Transient_GETS, Transient_GETS_Last_Token, Transient_Local_GETS_Last_Token, Transient_Local_GETS, Transient_GETX, Transient_Local_GETX}) { + m_popRequestQueue; + } + + transition({I_L, IM_L, IS_L, S_L, SM_L}, Ack) { + g_bounceResponseToStarver; + n_popResponseQueue; + } + + transition({I_L, IM_L, S_L, SM_L}, {Data_Shared, Data_Owner}) { + g_bounceResponseToStarver; + n_popResponseQueue; + } + + transition({I_L, S_L}, Data_All_Tokens) { + g_bounceResponseToStarver; + n_popResponseQueue; + } + + transition(IS_L, Request_Timeout) { + j_unsetReissueTimer; + a_issueReadRequest; + } + + transition({IM_L, SM_L}, Request_Timeout) { + j_unsetReissueTimer; + b_issueWriteRequest; + } + + // Opportunisticly Complete the memory operation in the following + // cases. Note: these transitions could just use + // g_bounceResponseToStarver, but if we have the data and tokens, we + // might as well complete the memory request while we have the + // chance (and then immediately forward on the data) + + transition(IM_L, Data_All_Tokens, MM_W) { + u_writeDataToCache; + q_updateTokensFromResponse; + xx_external_store_hit; + j_unsetReissueTimer; + o_scheduleUseTimeout; + n_popResponseQueue; + } + + transition(SM_L, Data_All_Tokens, S_L) { + u_writeDataToCache; + q_updateTokensFromResponse; + xx_external_store_hit; + ff_sendDataWithAllButNorOneTokens; + s_deallocateTBE; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(IS_L, Data_Shared, I_L) { + u_writeDataToCache; + q_updateTokensFromResponse; + x_external_load_hit; + s_deallocateTBE; + e_sendAckWithCollectedTokens; + p_informL2AboutTokenLoss; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(IS_L, Data_Owner, I_L) { + u_writeDataToCache; + q_updateTokensFromResponse; + x_external_load_hit; + ee_sendDataWithAllTokens; + s_deallocateTBE; + p_informL2AboutTokenLoss; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(IS_L, Data_All_Tokens, M_W) { + u_writeDataToCache; + q_updateTokensFromResponse; + x_external_load_hit; + j_unsetReissueTimer; + o_scheduleUseTimeout; + n_popResponseQueue; + } + + + // Own_Lock_or_Unlock + + transition(I_L, Own_Lock_or_Unlock, I) { + l_popPersistentQueue; + } + + transition(S_L, Own_Lock_or_Unlock, S) { + l_popPersistentQueue; + } + + transition(IM_L, Own_Lock_or_Unlock, IM) { + l_popPersistentQueue; + } + + transition(IS_L, Own_Lock_or_Unlock, IS) { + l_popPersistentQueue; + } + + transition(SM_L, Own_Lock_or_Unlock, SM) { + l_popPersistentQueue; + } +} + diff --git a/src/mem/protocol/MOESI_CMP_token-L2cache.sm b/src/mem/protocol/MOESI_CMP_token-L2cache.sm new file mode 100644 index 000000000..21fbf0b95 --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_token-L2cache.sm @@ -0,0 +1,1424 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +machine(L2Cache, "Token protocol") { + + // L2 BANK QUEUES + // From local bank of L2 cache TO the network + MessageBuffer L1RequestFromL2Cache, network="To", virtual_network="0", ordered="false"; // this L2 bank -> a local L1 + MessageBuffer GlobalRequestFromL2Cache, network="To", virtual_network="1", ordered="false"; // this L2 bank -> mod-directory + MessageBuffer responseFromL2Cache, network="To", virtual_network="2", ordered="false"; // this L2 bank -> a local L1 || mod-directory + + + // FROM the network to this local bank of L2 cache + MessageBuffer L1RequestToL2Cache, network="From", virtual_network="0", ordered="false"; // a local L1 -> this L2 bank + MessageBuffer GlobalRequestToL2Cache, network="From", virtual_network="1", ordered="false"; // mod-directory -> this L2 bank + MessageBuffer responseToL2Cache, network="From", virtual_network="2", ordered="false"; // a local L1 || mod-directory -> this L2 bank + MessageBuffer persistentToL2Cache, network="From", virtual_network="3", ordered="true"; + + // STATES + enumeration(State, desc="L2 Cache states", default="L2Cache_State_I") { + // Base states + NP, desc="Not Present"; + I, desc="Idle"; + S, desc="Shared, not present in any local L1s"; + O, desc="Owned, not present in any L1s"; + M, desc="Modified, not present in any L1s"; + + // Locked states + I_L, "I^L", desc="Invalid, Locked"; + S_L, "S^L", desc="Shared, Locked"; + } + + // EVENTS + enumeration(Event, desc="Cache events") { + + // Requests + L1_GETS, desc="local L1 GETS request"; + L1_GETS_Last_Token, desc="local L1 GETS request"; + L1_GETX, desc="local L1 GETX request"; + L1_INV, desc="L1 no longer has tokens"; + Transient_GETX, desc="A GetX from another processor"; + Transient_GETS, desc="A GetS from another processor"; + Transient_GETS_Last_Token, desc="A GetS from another processor"; + + // events initiated by this L2 + L2_Replacement, desc="L2 Replacement", format="!r"; + + // events of external L2 responses + + // Responses + Writeback_Tokens, desc="Received a writeback from L1 with only tokens (no data)"; + Writeback_Shared_Data, desc="Received a writeback from L1 that includes clean data"; + Writeback_All_Tokens, desc="Received a writeback from L1"; + Writeback_Owned, desc="Received a writeback from L1"; + + + Data_Shared, desc="Received a data message, we are now a sharer"; + Data_Owner, desc="Received a data message, we are now the owner"; + Data_All_Tokens, desc="Received a data message, we are now the owner, we now have all the tokens"; + Ack, desc="Received an ack message"; + Ack_All_Tokens, desc="Received an ack message, we now have all the tokens"; + + // Lock/Unlock + Persistent_GETX, desc="Another processor has priority to read/write"; + Persistent_GETS, desc="Another processor has priority to read"; + Own_Lock_or_Unlock, desc="This processor now has priority"; + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + int Tokens, desc="The number of tokens we're holding for the line"; + DataBlock DataBlk, desc="data for the block"; + } + + + + structure(DirEntry, desc="...") { + Set Sharers, desc="Set of the internal processors that want the block in shared state"; + bool exclusive, default="false", desc="if local exclusive is likely"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + void setMRU(Address); + } + + external_type(PerfectCacheMemory) { + void allocate(Address); + void deallocate(Address); + DirEntry lookup(Address); + bool isTagPresent(Address); + } + + + CacheMemory L2cacheMemory, template_hack="", constructor_hack='L2_CACHE_NUM_SETS_BITS,L2_CACHE_ASSOC,MachineType_L2Cache,int_to_string(i)+"_L2"'; + + PersistentTable persistentTable, constructor_hack="i"; + PerfectCacheMemory localDirectory, template_hack=""; + + + bool getFilteringEnabled(); + + Entry getL2CacheEntry(Address addr), return_by_ref="yes" { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory[addr]; + } + } + + int getTokens(Address addr) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory[addr].Tokens; + } else { + return 0; + } + } + + void changePermission(Address addr, AccessPermission permission) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory.changePermission(addr, permission); + } + } + + bool isCacheTagPresent(Address addr) { + return (L2cacheMemory.isTagPresent(addr) ); + } + + State getState(Address addr) { + if (isCacheTagPresent(addr)) { + return getL2CacheEntry(addr).CacheState; + } else if (persistentTable.isLocked(addr) == true) { + return State:I_L; + } else { + return State:NP; + } + } + + string getStateStr(Address addr) { + return L2Cache_State_to_string(getState(addr)); + } + + void setState(Address addr, State state) { + + + if (isCacheTagPresent(addr)) { + // Make sure the token count is in range + assert(getL2CacheEntry(addr).Tokens >= 0); + assert(getL2CacheEntry(addr).Tokens <= max_tokens()); + + // Make sure we have no tokens in L + if ((state == State:I_L) ) { + if (isCacheTagPresent(addr)) { + assert(getL2CacheEntry(addr).Tokens == 0); + } + } + + // in M and E you have all the tokens + if (state == State:M ) { + assert(getL2CacheEntry(addr).Tokens == max_tokens()); + } + + // in NP you have no tokens + if (state == State:NP) { + assert(getL2CacheEntry(addr).Tokens == 0); + } + + // You have at least one token in S-like states + if (state == State:S ) { + assert(getL2CacheEntry(addr).Tokens > 0); + } + + // You have at least half the token in O-like states + if (state == State:O ) { + assert(getL2CacheEntry(addr).Tokens >= 1); // Must have at least one token + // assert(getL2CacheEntry(addr).Tokens >= (max_tokens() / 2)); // Only mostly true; this might not always hold + } + + getL2CacheEntry(addr).CacheState := state; + + // Set permission + if (state == State:I) { + changePermission(addr, AccessPermission:Invalid); + } else if (state == State:S || state == State:O ) { + changePermission(addr, AccessPermission:Read_Only); + } else if (state == State:M ) { + changePermission(addr, AccessPermission:Read_Write); + } else { + changePermission(addr, AccessPermission:Invalid); + } + } + } + + void removeSharer(Address addr, NodeID id) { + + if (localDirectory.isTagPresent(addr)) { + localDirectory[addr].Sharers.remove(id); + if (localDirectory[addr].Sharers.count() == 0) { + localDirectory.deallocate(addr); + } + } + } + + bool sharersExist(Address addr) { + if (localDirectory.isTagPresent(addr)) { + if (localDirectory[addr].Sharers.count() > 0) { + return true; + } + else { + return false; + } + } + else { + return false; + } + } + + bool exclusiveExists(Address addr) { + if (localDirectory.isTagPresent(addr)) { + if (localDirectory[addr].exclusive == true) { + return true; + } + else { + return false; + } + } + else { + return false; + } + } + + // assumes that caller will check to make sure tag is present + Set getSharers(Address addr) { + return localDirectory[addr].Sharers; + } + + void setNewWriter(Address addr, NodeID id) { + if (localDirectory.isTagPresent(addr) == false) { + localDirectory.allocate(addr); + } + localDirectory[addr].Sharers.clear(); + localDirectory[addr].Sharers.add(id); + localDirectory[addr].exclusive := true; + } + + void addNewSharer(Address addr, NodeID id) { + if (localDirectory.isTagPresent(addr) == false) { + localDirectory.allocate(addr); + } + localDirectory[addr].Sharers.add(id); + // localDirectory[addr].exclusive := false; + } + + void clearExclusiveBitIfExists(Address addr) { + if (localDirectory.isTagPresent(addr) == true) { + localDirectory[addr].exclusive := false; + } + } + + // ** OUT_PORTS ** + out_port(globalRequestNetwork_out, RequestMsg, GlobalRequestFromL2Cache); + out_port(localRequestNetwork_out, RequestMsg, L1RequestFromL2Cache); + out_port(responseNetwork_out, ResponseMsg, responseFromL2Cache); + + + + // ** IN_PORTS ** + + // Persistent Network + in_port(persistentNetwork_in, PersistentMsg, persistentToL2Cache) { + if (persistentNetwork_in.isReady()) { + peek(persistentNetwork_in, PersistentMsg) { + assert(in_msg.Destination.isElement(machineID)); + + if (in_msg.Type == PersistentRequestType:GETX_PERSISTENT) { + persistentTable.persistentRequestLock(in_msg.Address, in_msg.Requestor, AccessType:Write); + } else if (in_msg.Type == PersistentRequestType:GETS_PERSISTENT) { + persistentTable.persistentRequestLock(in_msg.Address, in_msg.Requestor, AccessType:Read); + } else if (in_msg.Type == PersistentRequestType:DEACTIVATE_PERSISTENT) { + persistentTable.persistentRequestUnlock(in_msg.Address, in_msg.Requestor); + } else { + error("Unexpected message"); + } + + // React to the message based on the current state of the table + if (persistentTable.isLocked(in_msg.Address)) { + + if (persistentTable.typeOfSmallest(in_msg.Address) == AccessType:Read) { + trigger(Event:Persistent_GETS, in_msg.Address); + } else { + trigger(Event:Persistent_GETX, in_msg.Address); + } + } + else { + trigger(Event:Own_Lock_or_Unlock, in_msg.Address); + } + } + } + } + + + // Request Network + in_port(requestNetwork_in, RequestMsg, GlobalRequestToL2Cache) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + + if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:Transient_GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETS) { + if (L2cacheMemory.isTagPresent(in_msg.Address) && getL2CacheEntry(in_msg.Address).Tokens == 1) { + trigger(Event:Transient_GETS_Last_Token, in_msg.Address); + } + else { + trigger(Event:Transient_GETS, in_msg.Address); + } + } else { + error("Unexpected message"); + } + } + } + } + + in_port(L1requestNetwork_in, RequestMsg, L1RequestToL2Cache) { + if (L1requestNetwork_in.isReady()) { + peek(L1requestNetwork_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:L1_GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETS) { + if (L2cacheMemory.isTagPresent(in_msg.Address) && getL2CacheEntry(in_msg.Address).Tokens == 1) { + trigger(Event:L1_GETS_Last_Token, in_msg.Address); + } + else { + trigger(Event:L1_GETS, in_msg.Address); + } + } else { + error("Unexpected message"); + } + } + } + } + + + // Response Network + in_port(responseNetwork_in, ResponseMsg, responseToL2Cache) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (getTokens(in_msg.Address) + in_msg.Tokens != max_tokens()) { + if (in_msg.Type == CoherenceResponseType:ACK) { + trigger(Event:Ack, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_OWNER) { + trigger(Event:Data_Owner, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_SHARED) { + trigger(Event:Data_Shared, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WB_TOKENS || in_msg.Type == CoherenceResponseType:WB_OWNED || in_msg.Type == CoherenceResponseType:WB_SHARED_DATA) { + + if (L2cacheMemory.cacheAvail(in_msg.Address) || L2cacheMemory.isTagPresent(in_msg.Address)) { + + // either room is available or the block is already present + + if (in_msg.Type == CoherenceResponseType:WB_TOKENS) { + assert(in_msg.Dirty == false); + trigger(Event:Writeback_Tokens, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WB_SHARED_DATA) { + assert(in_msg.Dirty == false); + trigger(Event:Writeback_Shared_Data, in_msg.Address); + } + else if (in_msg.Type == CoherenceResponseType:WB_OWNED) { + //assert(in_msg.Dirty == false); + trigger(Event:Writeback_Owned, in_msg.Address); + } + } + else { + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } else if (in_msg.Type == CoherenceResponseType:INV) { + trigger(Event:L1_INV, in_msg.Address); + } else { + error("Unexpected message"); + } + } else { + if (in_msg.Type == CoherenceResponseType:ACK) { + trigger(Event:Ack_All_Tokens, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_OWNER || in_msg.Type == CoherenceResponseType:DATA_SHARED) { + trigger(Event:Data_All_Tokens, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WB_TOKENS || in_msg.Type == CoherenceResponseType:WB_OWNED || in_msg.Type == CoherenceResponseType:WB_SHARED_DATA) { + if (L2cacheMemory.cacheAvail(in_msg.Address) || L2cacheMemory.isTagPresent(in_msg.Address)) { + + // either room is available or the block is already present + + if (in_msg.Type == CoherenceResponseType:WB_TOKENS) { + assert(in_msg.Dirty == false); + assert( (getState(in_msg.Address) != State:NP) && (getState(in_msg.Address) != State:I) ); + trigger(Event:Writeback_All_Tokens, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WB_SHARED_DATA) { + assert(in_msg.Dirty == false); + trigger(Event:Writeback_All_Tokens, in_msg.Address); + } + else if (in_msg.Type == CoherenceResponseType:WB_OWNED) { + trigger(Event:Writeback_All_Tokens, in_msg.Address); + } + } + else { + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } else if (in_msg.Type == CoherenceResponseType:INV) { + trigger(Event:L1_INV, in_msg.Address); + } else { + DEBUG_EXPR(in_msg.Type); + error("Unexpected message"); + } + } + } + } + } + + + // ACTIONS + + action(a_broadcastLocalRequest, "a", desc="broadcast local request globally") { + + peek(L1requestNetwork_in, RequestMsg) { + + // if this is a retry or no local sharers, broadcast normally + + // if (in_msg.RetryNum > 0 || (in_msg.Type == CoherenceRequestType:GETX && exclusiveExists(in_msg.Address) == false) || (in_msg.Type == CoherenceRequestType:GETS && sharersExist(in_msg.Address) == false)) { + enqueue(globalRequestNetwork_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := in_msg.Address; + out_msg.Type := in_msg.Type; + out_msg.Requestor := in_msg.Requestor; + out_msg.RequestorMachine := in_msg.RequestorMachine; + //out_msg.Destination.broadcast(MachineType:L2Cache); + out_msg.RetryNum := in_msg.RetryNum; + out_msg.Destination.addNetDest(getAllPertinentL2Banks(address)); + out_msg.Destination.remove(map_L1CacheMachId_to_L2Cache(address, in_msg.Requestor)); + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Request_Control; + out_msg.AccessMode := in_msg.AccessMode; + out_msg.Prefetch := in_msg.Prefetch; + } //enqueue + // } // if + + //profile_filter_action(0); + } // peek + } //action + + + action(bb_bounceResponse, "\b", desc="Bounce tokens and data to memory") { + peek(responseNetwork_in, ResponseMsg) { + // FIXME, should use a 3rd vnet + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.Tokens := in_msg.Tokens; + out_msg.MessageSize := in_msg.MessageSize; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := in_msg.Dirty; + } + } + } + + action(c_cleanReplacement, "c", desc="Issue clean writeback") { + if (getL2CacheEntry(address).Tokens > 0) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.Tokens := getL2CacheEntry(address).Tokens; + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + getL2CacheEntry(address).Tokens := 0; + } + } + + action(cc_dirtyReplacement, "\c", desc="Issue dirty writeback") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.Tokens := getL2CacheEntry(address).Tokens; + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + + if (getL2CacheEntry(address).Dirty) { + out_msg.MessageSize := MessageSizeType:Writeback_Data; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + } else { + out_msg.MessageSize := MessageSizeType:Writeback_Control; + out_msg.Type := CoherenceResponseType:ACK_OWNER; + } + } + getL2CacheEntry(address).Tokens := 0; + } + + action(d_sendDataWithTokens, "d", desc="Send data and a token from cache to requestor") { + peek(requestNetwork_in, RequestMsg) { + if (getL2CacheEntry(address).Tokens > N_tokens()) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_SHARED; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.Tokens := N_tokens(); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + getL2CacheEntry(address).Tokens := getL2CacheEntry(address).Tokens - N_tokens(); + } + else { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_SHARED; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.Tokens := 1; + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + getL2CacheEntry(address).Tokens := getL2CacheEntry(address).Tokens - 1; + } + } + } + + action(dd_sendDataWithAllTokens, "\d", desc="Send data and all tokens from cache to requestor") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + assert(getL2CacheEntry(address).Tokens >= 1); + out_msg.Tokens := getL2CacheEntry(address).Tokens; + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + getL2CacheEntry(address).Tokens := 0; + } + + action(e_sendAckWithCollectedTokens, "e", desc="Send ack with the tokens we've collected thus far.") { + if (getL2CacheEntry(address).Tokens > 0) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + assert(getL2CacheEntry(address).Tokens >= 1); + out_msg.Tokens := getL2CacheEntry(address).Tokens; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + getL2CacheEntry(address).Tokens := 0; + } + + action(ee_sendDataWithAllTokens, "\e", desc="Send data and all tokens from cache to starver") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + assert(getL2CacheEntry(address).Tokens >= 1); + out_msg.Tokens := getL2CacheEntry(address).Tokens; + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + getL2CacheEntry(address).Tokens := 0; + } + + action(f_sendAckWithAllButOneTokens, "f", desc="Send ack with all our tokens but one to starver.") { + //assert(persistentTable.findSmallest(address) != id); // Make sure we never bounce tokens to ourself + assert(getL2CacheEntry(address).Tokens > 0); + if (getL2CacheEntry(address).Tokens > 1) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + assert(getL2CacheEntry(address).Tokens >= 1); + out_msg.Tokens := getL2CacheEntry(address).Tokens - 1; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + getL2CacheEntry(address).Tokens := 1; + } + + action(ff_sendDataWithAllButOneTokens, "\f", desc="Send data and out tokens but one to starver") { + //assert(persistentTable.findSmallest(address) != id); // Make sure we never bounce tokens to ourself + assert(getL2CacheEntry(address).Tokens > 0); + if (getL2CacheEntry(address).Tokens > 1) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + assert(getL2CacheEntry(address).Tokens >= 1); + out_msg.Tokens := getL2CacheEntry(address).Tokens - 1; + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + getL2CacheEntry(address).Tokens := 1; + } + } + + + + action(gg_bounceResponseToStarver, "\g", desc="Redirect response to starving processor") { + // assert(persistentTable.isLocked(address)); + peek(responseNetwork_in, ResponseMsg) { + // FIXME, should use a 3rd vnet in some cases + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.Tokens := in_msg.Tokens; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := in_msg.Dirty; + out_msg.MessageSize := in_msg.MessageSize; + } + } + } + + action(gg_bounceWBSharedToStarver, "\gg", desc="Redirect response to starving processor") { + //assert(persistentTable.isLocked(address)); + peek(responseNetwork_in, ResponseMsg) { + // FIXME, should use a 3rd vnet in some cases + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + if (in_msg.Type == CoherenceResponseType:WB_SHARED_DATA) { + out_msg.Type := CoherenceResponseType:DATA_SHARED; + } else { + out_msg.Type := CoherenceResponseType:ACK; + } + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.Tokens := in_msg.Tokens; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := in_msg.Dirty; + out_msg.MessageSize := in_msg.MessageSize; + } + } + } + + action(gg_bounceWBOwnedToStarver, "\ggg", desc="Redirect response to starving processor") { + // assert(persistentTable.isLocked(address)); + peek(responseNetwork_in, ResponseMsg) { + // FIXME, should use a 3rd vnet in some cases + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.Tokens := in_msg.Tokens; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := in_msg.Dirty; + out_msg.MessageSize := in_msg.MessageSize; + } + } + } + + + action(h_updateFilterFromL1HintOrWB, "h", desc="update filter from received writeback") { + peek(responseNetwork_in, ResponseMsg) { + removeSharer(in_msg.Address, machineIDToNodeID(in_msg.Sender)); + } + } + + action(j_forwardTransientRequestToLocalSharers, "j", desc="Forward external transient request to local sharers") { + peek(requestNetwork_in, RequestMsg) { + if (getFilteringEnabled() == true && in_msg.RetryNum == 0 && sharersExist(in_msg.Address) == false) { + profile_filter_action(1); + DEBUG_EXPR("filtered message"); + DEBUG_EXPR(in_msg.RetryNum); + } + else { + enqueue( localRequestNetwork_out, RequestMsg, latency="L2_RESPONSE_LATENCY" ) { + out_msg.Address := in_msg.Address; + out_msg.Requestor := in_msg.Requestor; + out_msg.RequestorMachine := in_msg.RequestorMachine; + out_msg.Destination := getLocalL1IDs(machineID); + out_msg.Type := in_msg.Type; + out_msg.isLocal := false; + out_msg.MessageSize := MessageSizeType:Request_Control; + out_msg.AccessMode := in_msg.AccessMode; + out_msg.Prefetch := in_msg.Prefetch; + } + profile_filter_action(0); + } + } + } + + + action(k_dataFromL2CacheToL1Requestor, "k", desc="Send data and a token from cache to L1 requestor") { + peek(L1requestNetwork_in, RequestMsg) { + assert(getL2CacheEntry(address).Tokens > 0); + //enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_to_L1_RESPONSE_LATENCY") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_SHARED; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:ResponseL2hit_Data; + out_msg.Tokens := 1; + } + getL2CacheEntry(address).Tokens := getL2CacheEntry(address).Tokens - 1; + } + } + + action(k_dataOwnerFromL2CacheToL1Requestor, "\k", desc="Send data and a token from cache to L1 requestor") { + peek(L1requestNetwork_in, RequestMsg) { + assert(getL2CacheEntry(address).Tokens > 0); + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:ResponseL2hit_Data; + out_msg.Tokens := 1; + } + getL2CacheEntry(address).Tokens := getL2CacheEntry(address).Tokens - 1; + } + } + + action(k_dataAndAllTokensFromL2CacheToL1Requestor, "\kk", desc="Send data and a token from cache to L1 requestor") { + peek(L1requestNetwork_in, RequestMsg) { +// assert(getL2CacheEntry(address).Tokens == max_tokens()); + //enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_to_L1_RESPONSE_LATENCY") { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.Dirty := getL2CacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:ResponseL2hit_Data; + //out_msg.Tokens := max_tokens(); + out_msg.Tokens := getL2CacheEntry(address).Tokens; + } + getL2CacheEntry(address).Tokens := 0; + } + } + + action(l_popPersistentQueue, "l", desc="Pop persistent queue.") { + persistentNetwork_in.dequeue(); + } + + action(m_popRequestQueue, "m", desc="Pop request queue.") { + requestNetwork_in.dequeue(); + } + + action(n_popResponseQueue, "n", desc="Pop response queue") { + responseNetwork_in.dequeue(); + } + + action(o_popL1RequestQueue, "o", desc="Pop L1 request queue.") { + L1requestNetwork_in.dequeue(); + } + + + action(q_updateTokensFromResponse, "q", desc="Update the token count based on the incoming response message") { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Tokens != 0); + getL2CacheEntry(address).Tokens := getL2CacheEntry(address).Tokens + in_msg.Tokens; + + // this should ideally be in u_writeDataToCache, but Writeback_All_Tokens + // may not trigger this action. + if ( (in_msg.Type == CoherenceResponseType:DATA_OWNER || in_msg.Type == CoherenceResponseType:WB_OWNED) && in_msg.Dirty) { + getL2CacheEntry(address).Dirty := true; + } + } + } + + action(r_markNewSharer, "r", desc="Mark the new local sharer from local request message") { + + peek(L1requestNetwork_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETX) { + setNewWriter(in_msg.Address, machineIDToNodeID(in_msg.Requestor)); + } else if (in_msg.Type == CoherenceRequestType:GETS) { + addNewSharer(in_msg.Address, machineIDToNodeID(in_msg.Requestor)); + } + } + } + + action(r_clearExclusive, "\rrr", desc="clear exclusive bit") { + clearExclusiveBitIfExists(address); + } + + action( r_setMRU, "\rr", desc="manually set the MRU bit for cache line" ) { + if(isCacheTagPresent(address)) { + L2cacheMemory.setMRU(address); + } + } + + action(t_sendAckWithCollectedTokens, "t", desc="Send ack with the tokens we've collected thus far.") { + if (getL2CacheEntry(address).Tokens > 0) { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + assert(getL2CacheEntry(address).Tokens >= 1); + out_msg.Tokens := getL2CacheEntry(address).Tokens; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + getL2CacheEntry(address).Tokens := 0; + } + + action(tt_sendLocalAckWithCollectedTokens, "tt", desc="Send ack with the tokens we've collected thus far.") { + if (getL2CacheEntry(address).Tokens > 0) { + peek(L1requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L2Cache; + out_msg.Destination.add(in_msg.Requestor); + assert(getL2CacheEntry(address).Tokens >= 1); + out_msg.Tokens := getL2CacheEntry(address).Tokens; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + getL2CacheEntry(address).Tokens := 0; + } + + action(u_writeDataToCache, "u", desc="Write data to cache") { + peek(responseNetwork_in, ResponseMsg) { + getL2CacheEntry(address).DataBlk := in_msg.DataBlk; + if ((getL2CacheEntry(address).Dirty == false) && in_msg.Dirty) { + getL2CacheEntry(address).Dirty := in_msg.Dirty; + } + } + } + + action(vv_allocateL2CacheBlock, "\v", desc="Set L2 cache tag equal to tag of block B.") { + L2cacheMemory.allocate(address); + } + + action(rr_deallocateL2CacheBlock, "\r", desc="Deallocate L2 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + L2cacheMemory.deallocate(address); + } + + action(uu_profileMiss, "\u", desc="Profile the demand miss") { + peek(L1requestNetwork_in, RequestMsg) { + // AccessModeType not implemented + profile_L2Cache_miss(convertToGenericType(in_msg.Type), in_msg.AccessMode, MessageSizeTypeToInt(in_msg.MessageSize), in_msg.Prefetch, machineIDToNodeID(in_msg.Requestor)); + } + } + + + action(w_assertIncomingDataAndCacheDataMatch, "w", desc="Assert that the incoming data and the data in the cache match") { + peek(responseNetwork_in, ResponseMsg) { + assert(getL2CacheEntry(address).DataBlk == in_msg.DataBlk); + } + } + + action(z_stall, "z", desc="Stall") { + } + + + + + //***************************************************** + // TRANSITIONS + //***************************************************** + + transition({NP, I, S, O, M, I_L, S_L}, L1_INV) { + + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition({NP, I, S, O, M}, Own_Lock_or_Unlock) { + l_popPersistentQueue; + } + + + // Transitions from NP + + transition(NP, {Transient_GETX, Transient_GETS}) { + // forward message to local sharers + r_clearExclusive; + j_forwardTransientRequestToLocalSharers; + m_popRequestQueue; + } + + + transition(NP, {L1_GETS, L1_GETX}) { + a_broadcastLocalRequest; + r_markNewSharer; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(NP, {Ack, Data_Shared, Data_Owner, Data_All_Tokens}) { + bb_bounceResponse; + n_popResponseQueue; + } + + transition(NP, Writeback_Shared_Data, S) { + vv_allocateL2CacheBlock; + u_writeDataToCache; + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition(NP, Writeback_Tokens, I) { + vv_allocateL2CacheBlock; + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition(NP, Writeback_All_Tokens, M) { + vv_allocateL2CacheBlock; + u_writeDataToCache; + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition(NP, Writeback_Owned, O) { + vv_allocateL2CacheBlock; + u_writeDataToCache; + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + + transition(NP, {Persistent_GETX, Persistent_GETS}, I_L) { + l_popPersistentQueue; + } + + // Transitions from Idle + + transition(I, {L1_GETS, L1_GETS_Last_Token}) { + a_broadcastLocalRequest; + tt_sendLocalAckWithCollectedTokens; // send any tokens we have collected + r_markNewSharer; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(I, L1_GETX) { + a_broadcastLocalRequest; + tt_sendLocalAckWithCollectedTokens; // send any tokens we have collected + r_markNewSharer; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(I, L2_Replacement) { + c_cleanReplacement; // Only needed in some cases + rr_deallocateL2CacheBlock; + } + + transition(I, {Transient_GETX, Transient_GETS, Transient_GETS_Last_Token}) { + r_clearExclusive; + t_sendAckWithCollectedTokens; + j_forwardTransientRequestToLocalSharers; + m_popRequestQueue; + } + + transition(I, {Persistent_GETX, Persistent_GETS}, I_L) { + e_sendAckWithCollectedTokens; + l_popPersistentQueue; + } + + + transition(I, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(I, Data_Shared, S) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(I, Writeback_Shared_Data, S) { + u_writeDataToCache; + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition(I, Writeback_Tokens) { + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition(I, Data_Owner, O) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(I, Writeback_Owned, O) { + u_writeDataToCache; + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition(I, Data_All_Tokens, M) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + + transition(I, Writeback_All_Tokens, M) { + u_writeDataToCache; + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + // Transitions from Shared + + transition(S, L2_Replacement, I) { + c_cleanReplacement; + rr_deallocateL2CacheBlock; + } + + transition(S, Transient_GETX, I) { + r_clearExclusive; + t_sendAckWithCollectedTokens; + j_forwardTransientRequestToLocalSharers; + m_popRequestQueue; + } + + transition(S, {Transient_GETS, Transient_GETS_Last_Token}) { + j_forwardTransientRequestToLocalSharers; + r_clearExclusive; + m_popRequestQueue; + } + + transition(S, Persistent_GETX, I_L) { + e_sendAckWithCollectedTokens; + l_popPersistentQueue; + } + + + transition(S, Persistent_GETS, S_L) { + f_sendAckWithAllButOneTokens; + l_popPersistentQueue; + } + + + transition(S, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(S, Data_Shared) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(S, Writeback_Tokens) { + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition(S, Writeback_Shared_Data) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + + transition(S, Data_Owner, O) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(S, Writeback_Owned, O) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition(S, Data_All_Tokens, M) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(S, Writeback_All_Tokens, M) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition(S, L1_GETX, I) { + a_broadcastLocalRequest; + tt_sendLocalAckWithCollectedTokens; + r_markNewSharer; + r_setMRU; + uu_profileMiss; + o_popL1RequestQueue; + } + + + transition(S, L1_GETS) { + k_dataFromL2CacheToL1Requestor; + r_markNewSharer; + r_setMRU; + o_popL1RequestQueue; + } + + transition(S, L1_GETS_Last_Token, I) { + + k_dataFromL2CacheToL1Requestor; + r_markNewSharer; + r_setMRU; + o_popL1RequestQueue; + } + + // Transitions from Owned + + transition(O, L2_Replacement, I) { + cc_dirtyReplacement; + rr_deallocateL2CacheBlock; + } + + transition(O, Transient_GETX, I) { + r_clearExclusive; + dd_sendDataWithAllTokens; + j_forwardTransientRequestToLocalSharers; + m_popRequestQueue; + } + + transition(O, Persistent_GETX, I_L) { + ee_sendDataWithAllTokens; + l_popPersistentQueue; + } + + transition(O, Persistent_GETS, S_L) { + ff_sendDataWithAllButOneTokens; + l_popPersistentQueue; + } + + transition(O, Transient_GETS) { + // send multiple tokens + r_clearExclusive; + d_sendDataWithTokens; + m_popRequestQueue; + } + + transition(O, Transient_GETS_Last_Token) { + // WAIT FOR IT TO GO PERSISTENT + r_clearExclusive; + m_popRequestQueue; + } + + transition(O, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(O, Ack_All_Tokens, M) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(O, Data_Shared) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + + transition(O, {Writeback_Tokens, Writeback_Shared_Data}) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition(O, Data_All_Tokens, M) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(O, Writeback_All_Tokens, M) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition(O, L1_GETS) { + k_dataFromL2CacheToL1Requestor; + r_markNewSharer; + r_setMRU; + o_popL1RequestQueue; + } + + transition(O, L1_GETS_Last_Token, I) { + k_dataOwnerFromL2CacheToL1Requestor; + r_markNewSharer; + r_setMRU; + o_popL1RequestQueue; + } + + transition(O, L1_GETX, I) { + a_broadcastLocalRequest; + k_dataAndAllTokensFromL2CacheToL1Requestor; + r_markNewSharer; + r_setMRU; + uu_profileMiss; + o_popL1RequestQueue; + } + + // Transitions from M + + transition(M, L2_Replacement, I) { + cc_dirtyReplacement; + rr_deallocateL2CacheBlock; + } + + // MRM_DEBUG: Give up all tokens even for GETS? ??? + transition(M, {Transient_GETX, Transient_GETS}, I) { + r_clearExclusive; + dd_sendDataWithAllTokens; + m_popRequestQueue; + } + + transition(M, {Persistent_GETS, Persistent_GETX}, I_L) { + ee_sendDataWithAllTokens; + l_popPersistentQueue; + } + + + transition(M, L1_GETS, O) { + k_dataFromL2CacheToL1Requestor; + r_markNewSharer; + r_setMRU; + o_popL1RequestQueue; + } + + transition(M, L1_GETX, I) { + k_dataAndAllTokensFromL2CacheToL1Requestor; + r_markNewSharer; + r_setMRU; + o_popL1RequestQueue; + } + + + //Transitions from locked states + + transition({I_L, S_L}, Ack) { + gg_bounceResponseToStarver; + n_popResponseQueue; + } + + transition({I_L, S_L}, {Data_Shared, Data_Owner, Data_All_Tokens}) { + gg_bounceResponseToStarver; + n_popResponseQueue; + } + + transition({I_L, S_L}, {Writeback_Tokens, Writeback_Shared_Data}) { + gg_bounceWBSharedToStarver; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition({I_L, S_L}, {Writeback_Owned, Writeback_All_Tokens}) { + gg_bounceWBOwnedToStarver; + h_updateFilterFromL1HintOrWB; + n_popResponseQueue; + } + + transition(S_L, L2_Replacement, I) { + c_cleanReplacement; + rr_deallocateL2CacheBlock; + } + + transition(I_L, L2_Replacement, I) { + rr_deallocateL2CacheBlock; + } + + transition(I_L, Own_Lock_or_Unlock, I) { + l_popPersistentQueue; + } + + transition(S_L, Own_Lock_or_Unlock, S) { + l_popPersistentQueue; + } + + transition({I_L, S_L}, {Transient_GETS_Last_Token, Transient_GETS, Transient_GETX}) { + r_clearExclusive; + m_popRequestQueue; + } + + transition(I_L, {L1_GETX, L1_GETS}) { + a_broadcastLocalRequest; + r_markNewSharer; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(S_L, L1_GETX, I_L) { + a_broadcastLocalRequest; + tt_sendLocalAckWithCollectedTokens; + r_markNewSharer; + r_setMRU; + uu_profileMiss; + o_popL1RequestQueue; + } + + transition(S_L, L1_GETS) { + k_dataFromL2CacheToL1Requestor; + r_markNewSharer; + r_setMRU; + o_popL1RequestQueue; + } + + transition(S_L, L1_GETS_Last_Token, I_L) { + k_dataFromL2CacheToL1Requestor; + r_markNewSharer; + r_setMRU; + o_popL1RequestQueue; + } + + transition(S_L, Persistent_GETX, I_L) { + e_sendAckWithCollectedTokens; + l_popPersistentQueue; + } + + transition(S_L, Persistent_GETS) { + l_popPersistentQueue; + } + + transition(I_L, {Persistent_GETX, Persistent_GETS}) { + l_popPersistentQueue; + } +} diff --git a/src/mem/protocol/MOESI_CMP_token-dir.sm b/src/mem/protocol/MOESI_CMP_token-dir.sm new file mode 100644 index 000000000..1592fd123 --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_token-dir.sm @@ -0,0 +1,435 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + + +machine(Directory, "Token protocol") { + + MessageBuffer requestFromDir, network="To", virtual_network="1", ordered="false"; + MessageBuffer responseFromDir, network="To", virtual_network="2", ordered="false"; + + MessageBuffer persistentToDir, network="From", virtual_network="3", ordered="true"; + MessageBuffer requestToDir, network="From", virtual_network="1", ordered="false"; + MessageBuffer responseToDir, network="From", virtual_network="2", ordered="false"; + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_O") { + // Base states + O, desc="Owner"; + NO, desc="Not Owner"; + L, desc="Locked"; + } + + // Events + enumeration(Event, desc="Directory events") { + GETX, desc="A GETX arrives"; + GETS, desc="A GETS arrives"; + Lockdown, desc="A lockdown request arrives"; + Unlockdown, desc="An un-lockdown request arrives"; + Data_Owner, desc="Data arrive"; + Ack_Owner, desc="Owner token arrived without data because it was clean"; + Tokens, desc="Tokens arrive"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + State DirectoryState, desc="Directory state"; + DataBlock DataBlk, desc="data for the block"; + int Tokens, default="max_tokens()", desc="Number of tokens for the line we're holding"; + + // The following state is provided to allow for bandwidth + // efficient directory-like operation. However all of this state + // is 'soft state' that does not need to be correct (as long as + // you're eventually willing to resort to broadcast.) + + Set Owner, desc="Probable Owner of the line. More accurately, the set of processors who need to see a GetS or GetO. We use a Set for convenience, but only one bit is set at a time."; + Set Sharers, desc="Probable sharers of the line. More accurately, the set of processors who need to see a GetX"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + + PersistentTable persistentTable, constructor_hack="i"; + + State getState(Address addr) { + return directory[addr].DirectoryState; + } + + void setState(Address addr, State state) { + directory[addr].DirectoryState := state; + + if (state == State:L) { + assert(directory[addr].Tokens == 0); + } + + // We have one or zero owners + assert((directory[addr].Owner.count() == 0) || (directory[addr].Owner.count() == 1)); + + // Make sure the token count is in range + assert(directory[addr].Tokens >= 0); + assert(directory[addr].Tokens <= max_tokens()); + + if (state == State:O) { + assert(directory[addr].Tokens >= 1); // Must have at least one token + // assert(directory[addr].Tokens >= (max_tokens() / 2)); // Only mostly true; this might not always hold + } + } + + // ** OUT_PORTS ** + out_port(responseNetwork_out, ResponseMsg, responseFromDir); + out_port(requestNetwork_out, RequestMsg, requestFromDir); + + // ** IN_PORTS ** + + in_port(persistentNetwork_in, PersistentMsg, persistentToDir) { + if (persistentNetwork_in.isReady()) { + peek(persistentNetwork_in, PersistentMsg) { + assert(in_msg.Destination.isElement(machineID)); + + if (distributedPersistentEnabled()) { + // Apply the lockdown or unlockdown message to the table + if (in_msg.Type == PersistentRequestType:GETX_PERSISTENT) { + persistentTable.persistentRequestLock(in_msg.Address, in_msg.Requestor, AccessType:Write); + } else if (in_msg.Type == PersistentRequestType:GETS_PERSISTENT) { + persistentTable.persistentRequestLock(in_msg.Address, in_msg.Requestor, AccessType:Read); + } else if (in_msg.Type == PersistentRequestType:DEACTIVATE_PERSISTENT) { + persistentTable.persistentRequestUnlock(in_msg.Address, in_msg.Requestor); + } else { + error("Invalid message"); + } + + // React to the message based on the current state of the table + if (persistentTable.isLocked(in_msg.Address)) { + trigger(Event:Lockdown, in_msg.Address); // locked + } else { + trigger(Event:Unlockdown, in_msg.Address); // unlocked + } + } + else { + if (in_msg.Type == PersistentRequestType:GETX_PERSISTENT) { + trigger(Event:Lockdown, in_msg.Address); // locked + } else if (in_msg.Type == PersistentRequestType:GETS_PERSISTENT) { + trigger(Event:Lockdown, in_msg.Address); // locked + } else if (in_msg.Type == PersistentRequestType:DEACTIVATE_PERSISTENT) { + trigger(Event:Unlockdown, in_msg.Address); // unlocked + } else { + error("Invalid message"); + } + } + } + } + } + + in_port(requestNetwork_in, RequestMsg, requestToDir) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:GETX, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + in_port(responseNetwork_in, ResponseMsg, responseToDir) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceResponseType:DATA_OWNER) { + trigger(Event:Data_Owner, in_msg.Address); + } else if ((in_msg.Type == CoherenceResponseType:ACK) || + (in_msg.Type == CoherenceResponseType:DATA_SHARED)) { + trigger(Event:Tokens, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:ACK_OWNER) { + trigger(Event:Ack_Owner, in_msg.Address); + } else { + DEBUG_EXPR(in_msg.Type); + error("Invalid message"); + } + } + } + } + + // Actions + + action(a_sendTokens, "a", desc="Send tokens to requestor") { + // Only send a message if we have tokens to send + if (directory[address].Tokens > 0) { + peek(requestNetwork_in, RequestMsg) { + // enqueue(responseNetwork_out, ResponseMsg, latency="DIRECTORY_CACHE_LATENCY") {// FIXME? + enqueue(responseNetwork_out, ResponseMsg, latency="DIRECTORY_LATENCY") {// FIXME? + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(in_msg.Requestor); + out_msg.Tokens := directory[in_msg.Address].Tokens; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + directory[address].Tokens := 0; + } + } + + action(aa_sendTokensToStarver, "\a", desc="Send tokens to starver") { + // Only send a message if we have tokens to send + if (directory[address].Tokens > 0) { + // enqueue(responseNetwork_out, ResponseMsg, latency="DIRECTORY_CACHE_LATENCY") {// FIXME? + enqueue(responseNetwork_out, ResponseMsg, latency="DIRECTORY_LATENCY") {// FIXME? + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.Tokens := directory[address].Tokens; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + directory[address].Tokens := 0; + } + } + + action(d_sendDataWithAllTokens, "d", desc="Send data and tokens to requestor") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(in_msg.Requestor); + assert(directory[address].Tokens > 0); + out_msg.Tokens := directory[in_msg.Address].Tokens; + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + directory[address].Tokens := 0; + } + + action(dd_sendDataWithAllTokensToStarver, "\d", desc="Send data and tokens to starver") { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(persistentTable.findSmallest(address)); + assert(directory[address].Tokens > 0); + out_msg.Tokens := directory[address].Tokens; + out_msg.DataBlk := directory[address].DataBlk; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + directory[address].Tokens := 0; + } + + action(f_incrementTokens, "f", desc="Increment the number of tokens we're tracking") { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Tokens >= 1); + directory[address].Tokens := directory[address].Tokens + in_msg.Tokens; + } + } + + action(j_popIncomingRequestQueue, "j", desc="Pop incoming request queue") { + requestNetwork_in.dequeue(); + } + + action(k_popIncomingResponseQueue, "k", desc="Pop incoming response queue") { + responseNetwork_in.dequeue(); + } + + action(l_popIncomingPersistentQueue, "l", desc="Pop incoming persistent queue") { + persistentNetwork_in.dequeue(); + } + + action(m_writeDataToMemory, "m", desc="Write dirty writeback to memory") { + peek(responseNetwork_in, ResponseMsg) { + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + action(n_checkIncomingMsg, "n", desc="Check incoming token message") { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Type == CoherenceResponseType:ACK_OWNER); + assert(in_msg.Dirty == false); + assert(in_msg.MessageSize == MessageSizeType:Writeback_Control); + assert(directory[in_msg.Address].DataBlk == in_msg.DataBlk); + } + } + + action(r_bounceResponse, "r", desc="Bounce response to starving processor") { + peek(responseNetwork_in, ResponseMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.Tokens := in_msg.Tokens; + out_msg.MessageSize := in_msg.MessageSize; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := in_msg.Dirty; + } + } + } + + action(s_bounceDatalessOwnerToken, "s", desc="Bounce clean owner token to starving processor") { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Type == CoherenceResponseType:ACK_OWNER); + assert(in_msg.Dirty == false); + assert(in_msg.MessageSize == MessageSizeType:Writeback_Control); + + // NOTE: The following check would not be valid in a real + // implementation. We include the data in the "dataless" + // message so we can assert the clean data matches the datablock + // in memory + assert(directory[in_msg.Address].DataBlk == in_msg.DataBlk); + + // Bounce the message, but "re-associate" the data and the owner + // token. In essence we're converting an ACK_OWNER message to a + // DATA_OWNER message, keeping the number of tokens the same. + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.DestMachine := MachineType:L1Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.Tokens := in_msg.Tokens; + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.Dirty := in_msg.Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + + // TRANSITIONS + + // Trans. from O + transition(O, GETX, NO) { + d_sendDataWithAllTokens; + j_popIncomingRequestQueue; + } + + transition(O, GETS, NO) { + d_sendDataWithAllTokens; + j_popIncomingRequestQueue; + } + + transition(O, Lockdown, L) { + dd_sendDataWithAllTokensToStarver; + l_popIncomingPersistentQueue; + } + + transition(O, Tokens) { + f_incrementTokens; + k_popIncomingResponseQueue; + } + + // Trans. from NO + transition(NO, GETX) { + a_sendTokens; + j_popIncomingRequestQueue; + } + + transition(NO, GETS) { + j_popIncomingRequestQueue; + } + + transition(NO, Lockdown, L) { + aa_sendTokensToStarver; + l_popIncomingPersistentQueue; + } + + transition(NO, Data_Owner, O) { + m_writeDataToMemory; + f_incrementTokens; + k_popIncomingResponseQueue; + } + + transition(NO, Ack_Owner, O) { + n_checkIncomingMsg; + f_incrementTokens; + k_popIncomingResponseQueue; + } + + transition(NO, Tokens) { + f_incrementTokens; + k_popIncomingResponseQueue; + } + + // Trans. from L + transition(L, {GETX, GETS}) { + j_popIncomingRequestQueue; + } + + transition(L, Lockdown) { + l_popIncomingPersistentQueue; + } + + // we could change this to write the data to memory and send it cleanly + transition(L, Data_Owner) { + r_bounceResponse; + k_popIncomingResponseQueue; + } + + transition(L, Tokens) { + r_bounceResponse; + k_popIncomingResponseQueue; + } + + transition(L, Ack_Owner) { + s_bounceDatalessOwnerToken; + k_popIncomingResponseQueue; + } + + + transition(L, Unlockdown, NO) { + l_popIncomingPersistentQueue; + } + +} diff --git a/src/mem/protocol/MOESI_CMP_token-msg.sm b/src/mem/protocol/MOESI_CMP_token-msg.sm new file mode 100644 index 000000000..2a75ce644 --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_token-msg.sm @@ -0,0 +1,123 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +// CoherenceRequestType +enumeration(CoherenceRequestType, desc="...") { + GETX, desc="Get eXclusive"; + GETS, desc="Get Shared"; +} + +// PersistentType +enumeration(PersistentRequestType, desc="...") { + GETX_PERSISTENT, desc="..."; + GETS_PERSISTENT, desc="..."; + DEACTIVATE_PERSISTENT,desc="..."; +} + +// CoherenceResponseType +enumeration(CoherenceResponseType, desc="...") { + DATA_OWNER, desc="Data"; + ACK_OWNER, desc="data-less owner token"; + DATA_SHARED, desc="Data"; + ACK, desc="ACKnowledgment"; + WB_TOKENS, desc="L1 to L2 writeback"; + WB_SHARED_DATA, desc="L1 to L2 writeback with data"; + WB_OWNED, desc="L1 to L2 writeback with data"; + INV, desc="L1 informing L2 of loss of all tokens"; +} + +// TriggerType +enumeration(TriggerType, desc="...") { + REQUEST_TIMEOUT, desc="See corresponding event"; + USE_TIMEOUT, desc="See corresponding event"; +} + +// TriggerMsg +structure(TriggerMsg, desc="...", interface="Message") { + Address Address, desc="Physical address for this request"; + TriggerType Type, desc="Type of trigger"; +} + +// PersistentMsg +structure(PersistentMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + PersistentRequestType Type, desc="Type of starvation request"; + MachineID Requestor, desc="Node who initiated the request"; + MachineType RequestorMachine, desc="Type of machine who requested"; + NetDest Destination, desc="Destination set"; + MachineType DestMachine, desc="type of destination component"; + MessageSizeType MessageSize, desc="size category of the message"; + AccessModeType AccessMode, desc="user/supervisor access type"; + PrefetchBit Prefetch, desc="Is this a prefetch request"; +} + +// RequestMsg +structure(RequestMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)"; + MachineID Requestor, desc="Node who initiated the request"; + MachineType RequestorMachine, desc="Type of machine who requested"; + MachineType DestMachine, desc="Type of destination machine"; + NetDest Destination, desc="Multicast destination mask"; + bool isLocal, desc="Is this request from a local L1"; + int RetryNum, desc="retry sequence number"; + MessageSizeType MessageSize, desc="size category of the message"; + AccessModeType AccessMode, desc="user/supervisor access type"; + PrefetchBit Prefetch, desc="Is this a prefetch request"; +} + +// ResponseMsg +structure(ResponseMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)"; + MachineID Sender, desc="Node who sent the data"; + MachineType SenderMachine, desc="component that is sending"; + NetDest Destination, desc="Node to whom the data is sent"; + MachineType DestMachine, desc="What component receives the data"; + int Tokens, desc="Number of tokens being transfered for this line"; + DataBlock DataBlk, desc="data for the cache line"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + MessageSizeType MessageSize, desc="size category of the message"; +} + +GenericRequestType convertToGenericType(CoherenceRequestType type) { + if(type == CoherenceRequestType:GETS) { + return GenericRequestType:GETS; + } else if(type == CoherenceRequestType:GETX) { + return GenericRequestType:GETX; + } else { + DEBUG_EXPR(type); + error("invalid CoherenceRequestType"); + } +} diff --git a/src/mem/protocol/MOESI_CMP_token.slicc b/src/mem/protocol/MOESI_CMP_token.slicc new file mode 100644 index 000000000..ae4a6d6ec --- /dev/null +++ b/src/mem/protocol/MOESI_CMP_token.slicc @@ -0,0 +1,5 @@ +MOESI_CMP_token-msg.sm +MOESI_CMP_token-L1cache.sm +MOESI_CMP_token-L2cache.sm +MOESI_CMP_token-dir.sm +standard_CMP-protocol.sm diff --git a/src/mem/protocol/MOESI_SMP_directory-cache.sm b/src/mem/protocol/MOESI_SMP_directory-cache.sm new file mode 100644 index 000000000..77edeb90c --- /dev/null +++ b/src/mem/protocol/MOESI_SMP_directory-cache.sm @@ -0,0 +1,981 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +machine(L1Cache, "Directory protocol") { + + MessageBuffer requestFromCache, network="To", virtual_network="0", ordered="false"; + MessageBuffer responseFromCache, network="To", virtual_network="2", ordered="false"; + MessageBuffer unblockFromCache, network="To", virtual_network="3", ordered="false"; + + MessageBuffer forwardToCache, network="From", virtual_network="1", ordered="false"; + MessageBuffer responseToCache, network="From", virtual_network="2", ordered="false"; + + // STATES + enumeration(State, desc="Cache states", default="L1Cache_State_I") { + // Base states + NP, desc="Not Present"; + I, desc="Idle"; + S, desc="Shared"; + O, desc="Owned"; + E, desc="Exclusive (clean)"; + M, desc="Modified (dirty)"; + MM, desc="Modified (dirty and locally modified)"; + + // Transient States + IM, "IM", desc="Issued GetX"; + SM, "SM", desc="Issued GetX, we still have an old copy of the line"; + OM, "OM", desc="Issued GetX, received data"; + IS, "IS", desc="Issued GetS"; + OI, "OI", desc="Issued PutO, waiting for ack"; + MI, "MI", desc="Issued PutX, waiting for ack"; + II, "II", desc="Issued PutX/O, saw Fwd_GETS or Fwd_GETX, waiting for ack"; + } + + // EVENTS + enumeration(Event, desc="Cache events") { + Load, desc="Load request from the processor"; + Ifetch, desc="I-fetch request from the processor"; + Store, desc="Store request from the processor"; + L2_Replacement, desc="Replacement"; + L1_to_L2, desc="L1 to L2 transfer"; + L2_to_L1D, desc="L2 to L1-Data transfer"; + L2_to_L1I, desc="L2 to L1-Instruction transfer"; + + // Requests + Own_GETX, desc="We observe our own GetX forwarded back to us"; + Fwd_GETX, desc="A GetX from another processor"; + Fwd_GETS, desc="A GetS from another processor"; + Inv, desc="Invalidations from the directory"; + + // Responses + Ack, desc="Received an ack message"; + Data, desc="Received a data message, responder has a shared copy"; + Exclusive_Data_Clean, desc="Received a data message, no other processor has it, data is clean"; + Exclusive_Data_Dirty, desc="Received a data message, no other processor has it, data is dirty"; + + Writeback_Ack, desc="Writeback O.K. from directory"; + Writeback_Nack, desc="Writeback not O.K. from directory"; + + // Triggers + All_acks, desc="Received all required data and message acks"; + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + DataBlock DataBlk, desc="data for the block"; + } + + // TBE fields + structure(TBE, desc="...") { + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="data for the block, required for concurrent writebacks"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + int NumPendingMsgs, default="0", desc="Number of acks/data messages that this processor is waiting for"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + MessageBuffer mandatoryQueue, abstract_chip_ptr="true", ordered="false"; + Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i"; + StoreBuffer storeBuffer, abstract_chip_ptr="true", constructor_hack="i"; + + TBETable TBEs, template_hack=""; + CacheMemory L1IcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1I"', abstract_chip_ptr="true"; + CacheMemory L1DcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1D"', abstract_chip_ptr="true"; + CacheMemory L2cacheMemory, template_hack="", constructor_hack='L2_CACHE_NUM_SETS_BITS,L2_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L2"', abstract_chip_ptr="true"; + + Entry getCacheEntry(Address addr), return_by_ref="yes" { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory[addr]; + } else if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory[addr]; + } else { + return L1IcacheMemory[addr]; + } + } + + void changePermission(Address addr, AccessPermission permission) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory.changePermission(addr, permission); + } else if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory.changePermission(addr, permission); + } else { + return L1IcacheMemory.changePermission(addr, permission); + } + } + + bool isCacheTagPresent(Address addr) { + return (L2cacheMemory.isTagPresent(addr) || L1DcacheMemory.isTagPresent(addr) || L1IcacheMemory.isTagPresent(addr)); + } + + State getState(Address addr) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + assert((L1IcacheMemory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + assert((L1DcacheMemory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + + if(TBEs.isPresent(addr)) { + return TBEs[addr].TBEState; + } else if (isCacheTagPresent(addr)) { + return getCacheEntry(addr).CacheState; + } + return State:NP; + } + + void setState(Address addr, State state) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + assert((L1IcacheMemory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + assert((L1DcacheMemory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + + if (TBEs.isPresent(addr)) { + TBEs[addr].TBEState := state; + } + + if (isCacheTagPresent(addr)) { + getCacheEntry(addr).CacheState := state; + + if (state == State:E) { + assert(getCacheEntry(addr).Dirty == false); + } + + if ((state == State:M) || (state == State:MM)) { + assert(getCacheEntry(addr).Dirty == true); + } + + // Set permission + if (state == State:MM) { + changePermission(addr, AccessPermission:Read_Write); + } else if ((state == State:S) || + (state == State:O) || + (state == State:M) || + (state == State:E) || + (state == State:SM) || + (state == State:OM)) { + changePermission(addr, AccessPermission:Read_Only); + } else { + changePermission(addr, AccessPermission:Invalid); + } + } + } + + Event mandatory_request_type_to_event(CacheRequestType type) { + if (type == CacheRequestType:LD) { + return Event:Load; + } else if (type == CacheRequestType:IFETCH) { + return Event:Ifetch; + } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { + return Event:Store; + } else { + error("Invalid CacheRequestType"); + } + } + + MessageBuffer triggerQueue, ordered="true"; + + // ** OUT_PORTS ** + + out_port(requestNetwork_out, RequestMsg, requestFromCache); + out_port(responseNetwork_out, ResponseMsg, responseFromCache); + out_port(unblockNetwork_out, ResponseMsg, unblockFromCache); + out_port(triggerQueue_out, TriggerMsg, triggerQueue); + + // ** IN_PORTS ** + + // Trigger Queue + in_port(triggerQueue_in, TriggerMsg, triggerQueue) { + if (triggerQueue_in.isReady()) { + peek(triggerQueue_in, TriggerMsg) { + if (in_msg.Type == TriggerType:ALL_ACKS) { + trigger(Event:All_acks, in_msg.Address); + } else { + error("Unexpected message"); + } + } + } + } + + // Nothing from the request network + + // Forward Network + in_port(forwardToCache_in, RequestMsg, forwardToCache) { + if (forwardToCache_in.isReady()) { + peek(forwardToCache_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETX) { + if (in_msg.Requestor == machineID) { + trigger(Event:Own_GETX, in_msg.Address); + } else { + trigger(Event:Fwd_GETX, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:Fwd_GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:INV) { + trigger(Event:Inv, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:WB_ACK) { + trigger(Event:Writeback_Ack, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:WB_NACK) { + trigger(Event:Writeback_Nack, in_msg.Address); + } else { + error("Unexpected message"); + } + } + } + } + + // Response Network + in_port(responseToCache_in, ResponseMsg, responseToCache) { + if (responseToCache_in.isReady()) { + peek(responseToCache_in, ResponseMsg) { + if (in_msg.Type == CoherenceResponseType:ACK) { + trigger(Event:Ack, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA) { + trigger(Event:Data, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE_CLEAN) { + trigger(Event:Exclusive_Data_Clean, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_EXCLUSIVE_DIRTY) { + trigger(Event:Exclusive_Data_Dirty, in_msg.Address); + } else { + error("Unexpected message"); + } + } + } + } + + // Nothing from the unblock network + + // Mandatory Queue + in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { + if (mandatoryQueue_in.isReady()) { + peek(mandatoryQueue_in, CacheMsg) { + + // Check for data access to blocks in I-cache and ifetchs to blocks in D-cache + + if (in_msg.Type == CacheRequestType:IFETCH) { + // ** INSTRUCTION ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, try to write it to the L2 + if (L2cacheMemory.cacheAvail(in_msg.Address)) { + trigger(Event:L1_to_L2, in_msg.Address); + } else { + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } + + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 fetches the line. We know it can't be in the L2 due to exclusion + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1IcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 + if (L2cacheMemory.isTagPresent(in_msg.Address)) { + // L2 has it (maybe not with the right permissions) + trigger(Event:L2_to_L1I, in_msg.Address); + } else { + // We have room, the L2 doesn't have it, so the L1 fetches the line + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } + } else { + // No room in the L1, so we need to make room + if (L2cacheMemory.cacheAvail(L1IcacheMemory.cacheProbe(in_msg.Address))) { + // The L2 has room, so we move the line from the L1 to the L2 + trigger(Event:L1_to_L2, L1IcacheMemory.cacheProbe(in_msg.Address)); + } else { + // The L2 does not have room, so we replace a line from the L2 + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(L1IcacheMemory.cacheProbe(in_msg.Address))); + } + } + } + } else { + // *** DATA ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, try to write it to the L2 + if (L2cacheMemory.cacheAvail(in_msg.Address)) { + trigger(Event:L1_to_L2, in_msg.Address); + } else { + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } + + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 fetches the line. We know it can't be in the L2 due to exclusion + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1DcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 + if (L2cacheMemory.isTagPresent(in_msg.Address)) { + // L2 has it (maybe not with the right permissions) + trigger(Event:L2_to_L1D, in_msg.Address); + } else { + // We have room, the L2 doesn't have it, so the L1 fetches the line + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } + } else { + // No room in the L1, so we need to make room + if (L2cacheMemory.cacheAvail(L1DcacheMemory.cacheProbe(in_msg.Address))) { + // The L2 has room, so we move the line from the L1 to the L2 + trigger(Event:L1_to_L2, L1DcacheMemory.cacheProbe(in_msg.Address)); + } else { + // The L2 does not have room, so we replace a line from the L2 + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(L1DcacheMemory.cacheProbe(in_msg.Address))); + } + } + } + } + } + } + } + + // ACTIONS + + action(a_issueGETS, "a", desc="Issue GETS") { + enqueue(requestNetwork_out, RequestMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Request_Control; + // TBEs[address].NumPendingMsgs := numberOfNodes(); // One from each other processor (n-1) plus the memory (+1) + } + } + + action(b_issueGETX, "b", desc="Issue GETX") { + enqueue(requestNetwork_out, RequestMsg, latency="(ISSUE_LATENCY-1)") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Request_Control; + // TBEs[address].NumPendingMsgs := numberOfNodes(); // One from each other processor (n-1) plus the memory (+1) + } + } + + action(d_issuePUTX, "d", desc="Issue PUTX") { + enqueue(requestNetwork_out, RequestMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTX; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + + action(dd_issuePUTO, "\d", desc="Issue PUTO") { + enqueue(requestNetwork_out, RequestMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTO; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + + action(e_sendData, "e", desc="Send data from cache to requestor") { + peek(forwardToCache_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.Acks := in_msg.Acks; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(ee_sendDataExclusive, "\e", desc="Send data from cache to requestor, don't keep a shared copy") { + peek(forwardToCache_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE_DIRTY; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.Acks := in_msg.Acks; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(f_sendAck, "f", desc="Send ack from cache to requestor") { + peek(forwardToCache_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.Acks := 0 - 1; // -1 + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + + action(g_sendUnblock, "g", desc="Send unblock to memory") { + enqueue(unblockNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:UNBLOCK; + out_msg.Sender := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Unblock_Control; + } + } + + action(gg_sendUnblockExclusive, "\g", desc="Send unblock exclusive to memory") { + enqueue(unblockNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:UNBLOCK_EXCLUSIVE; + out_msg.Sender := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Unblock_Control; + } + } + + action(h_load_hit, "h", desc="Notify sequencer the load completed.") { + DEBUG_EXPR(getCacheEntry(address).DataBlk); + sequencer.readCallback(address, getCacheEntry(address).DataBlk); + } + + action(hh_store_hit, "\h", desc="Notify sequencer that store completed.") { + DEBUG_EXPR(getCacheEntry(address).DataBlk); + sequencer.writeCallback(address, getCacheEntry(address).DataBlk); + getCacheEntry(address).Dirty := true; + } + + action(i_allocateTBE, "i", desc="Allocate TBE") { + check_allocate(TBEs); + TBEs.allocate(address); + TBEs[address].DataBlk := getCacheEntry(address).DataBlk; // Data only used for writebacks + TBEs[address].Dirty := getCacheEntry(address).Dirty; + } + + action(j_popTriggerQueue, "j", desc="Pop trigger queue.") { + triggerQueue_in.dequeue(); + } + + action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") { + mandatoryQueue_in.dequeue(); + } + + action(l_popForwardQueue, "l", desc="Pop forwareded request queue.") { + forwardToCache_in.dequeue(); + } + + action(m_decrementNumberOfMessages, "m", desc="Decrement the number of messages for which we're waiting") { + peek(responseToCache_in, ResponseMsg) { + TBEs[address].NumPendingMsgs := TBEs[address].NumPendingMsgs - in_msg.Acks; + } + } + + action(mm_decrementNumberOfMessages, "\m", desc="Decrement the number of messages for which we're waiting") { + peek(forwardToCache_in, RequestMsg) { + TBEs[address].NumPendingMsgs := TBEs[address].NumPendingMsgs - in_msg.Acks; + } + } + + action(n_popResponseQueue, "n", desc="Pop response queue") { + responseToCache_in.dequeue(); + } + + action(o_checkForCompletion, "o", desc="Check if we have received all the messages required for completion") { + if (TBEs[address].NumPendingMsgs == 0) { + enqueue(triggerQueue_out, TriggerMsg) { + out_msg.Address := address; + out_msg.Type := TriggerType:ALL_ACKS; + } + } + } + + action(q_sendDataFromTBEToCache, "q", desc="Send data from TBE to cache") { + peek(forwardToCache_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := TBEs[address].DataBlk; + out_msg.Dirty := TBEs[address].Dirty; + out_msg.Acks := in_msg.Acks; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(qq_sendDataFromTBEToMemory, "\q", desc="Send data from TBE to memory") { + enqueue(unblockNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Sender := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.Dirty := TBEs[address].Dirty; + if (TBEs[address].Dirty) { + out_msg.Type := CoherenceResponseType:WRITEBACK_DIRTY; + out_msg.DataBlk := TBEs[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } else { + out_msg.Type := CoherenceResponseType:WRITEBACK_CLEAN; + // NOTE: in a real system this would not send data. We send + // data here only so we can check it at the memory + out_msg.DataBlk := TBEs[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(s_deallocateTBE, "s", desc="Deallocate TBE") { + TBEs.deallocate(address); + } + + action(u_writeDataToCache, "u", desc="Write data to cache") { + peek(responseToCache_in, ResponseMsg) { + getCacheEntry(address).DataBlk := in_msg.DataBlk; + getCacheEntry(address).Dirty := in_msg.Dirty; + } + } + + action(v_writeDataToCacheVerify, "v", desc="Write data to cache, assert it was same as before") { + peek(responseToCache_in, ResponseMsg) { + assert(getCacheEntry(address).DataBlk == in_msg.DataBlk); + getCacheEntry(address).DataBlk := in_msg.DataBlk; + getCacheEntry(address).Dirty := in_msg.Dirty; + } + } + + action(kk_deallocateL1CacheBlock, "\k", desc="Deallocate cache block. Sets the cache to invalid, allowing a replacement in parallel with a fetch.") { + if (L1DcacheMemory.isTagPresent(address)) { + L1DcacheMemory.deallocate(address); + } else { + L1IcacheMemory.deallocate(address); + } + } + + action(ii_allocateL1DCacheBlock, "\i", desc="Set L1 D-cache tag equal to tag of block B.") { + if (L1DcacheMemory.isTagPresent(address) == false) { + L1DcacheMemory.allocate(address); + } + } + + action(jj_allocateL1ICacheBlock, "\j", desc="Set L1 I-cache tag equal to tag of block B.") { + if (L1IcacheMemory.isTagPresent(address) == false) { + L1IcacheMemory.allocate(address); + } + } + + action(vv_allocateL2CacheBlock, "\v", desc="Set L2 cache tag equal to tag of block B.") { + L2cacheMemory.allocate(address); + } + + action(rr_deallocateL2CacheBlock, "\r", desc="Deallocate L2 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + L2cacheMemory.deallocate(address); + } + + action(ss_copyFromL1toL2, "\s", desc="Copy data block from L1 (I or D) to L2") { + if (L1DcacheMemory.isTagPresent(address)) { + L2cacheMemory[address] := L1DcacheMemory[address]; + } else { + L2cacheMemory[address] := L1IcacheMemory[address]; + } + } + + action(tt_copyFromL2toL1, "\t", desc="Copy data block from L2 to L1 (I or D)") { + if (L1DcacheMemory.isTagPresent(address)) { + L1DcacheMemory[address] := L2cacheMemory[address]; + } else { + L1IcacheMemory[address] := L2cacheMemory[address]; + } + } + + action(uu_profileMiss, "\u", desc="Profile the demand miss") { + peek(mandatoryQueue_in, CacheMsg) { + profile_miss(in_msg, id); + } + } + + action(zz_recycleMandatoryQueue, "\z", desc="Send the head of the mandatory queue to the back of the queue.") { + mandatoryQueue_in.recycle(); + } + + //***************************************************** + // TRANSITIONS + //***************************************************** + + // Transitions for Load/Store/L2_Replacement from transient states + transition({IM, SM, OM, IS, OI, MI, II}, {Store, L2_Replacement}) { + zz_recycleMandatoryQueue; + } + + transition({IM, IS, OI, MI, II}, {Load, Ifetch}) { + zz_recycleMandatoryQueue; + } + + transition({IM, SM, OM, IS, OI, MI, II}, L1_to_L2) { + zz_recycleMandatoryQueue; + } + + // Transitions moving data between the L1 and L2 caches + transition({I, S, O, E, M, MM}, L1_to_L2) { + vv_allocateL2CacheBlock; + ss_copyFromL1toL2; // Not really needed for state I + kk_deallocateL1CacheBlock; + } + + transition({I, S, O, E, M, MM}, L2_to_L1D) { + ii_allocateL1DCacheBlock; + tt_copyFromL2toL1; // Not really needed for state I + rr_deallocateL2CacheBlock; + } + + transition({I, S, O, E, M, MM}, L2_to_L1I) { + jj_allocateL1ICacheBlock; + tt_copyFromL2toL1; // Not really needed for state I + rr_deallocateL2CacheBlock; + } + + // Transitions from Idle + transition({NP, I}, Load, IS) { + ii_allocateL1DCacheBlock; + i_allocateTBE; + a_issueGETS; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition({NP, I}, Ifetch, IS) { + jj_allocateL1ICacheBlock; + i_allocateTBE; + a_issueGETS; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition({NP, I}, Store, IM) { + ii_allocateL1DCacheBlock; + i_allocateTBE; + b_issueGETX; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I, L2_Replacement) { + rr_deallocateL2CacheBlock; + } + + transition({NP, I}, Inv) { + f_sendAck; + l_popForwardQueue; + } + + // Transitions from Shared + transition({S, SM}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(S, Store, SM) { + i_allocateTBE; + b_issueGETX; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(S, L2_Replacement, I) { + rr_deallocateL2CacheBlock; + } + + transition(S, Inv, I) { + f_sendAck; + l_popForwardQueue; + } + + // Transitions from Owned + transition({O, OM}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(O, Store, OM) { + i_allocateTBE; + b_issueGETX; + // p_decrementNumberOfMessagesByOne; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(O, L2_Replacement, OI) { + i_allocateTBE; + dd_issuePUTO; + rr_deallocateL2CacheBlock; + } + + transition(O, Fwd_GETX, I) { + e_sendData; + l_popForwardQueue; + } + + transition(O, Fwd_GETS) { + e_sendData; + l_popForwardQueue; + } + + // Transitions from MM + transition(MM, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(MM, Store) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(MM, L2_Replacement, MI) { + i_allocateTBE; + d_issuePUTX; + rr_deallocateL2CacheBlock; + } + + transition(MM, Fwd_GETX, I) { + e_sendData; + l_popForwardQueue; + } + + transition(MM, Fwd_GETS, I) { + ee_sendDataExclusive; + l_popForwardQueue; + } + + // Transitions from M + transition({E, M}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition({E, M}, Store, MM) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition({E, M}, L2_Replacement, MI) { + i_allocateTBE; + d_issuePUTX; + rr_deallocateL2CacheBlock; + } + + transition({E, M}, Fwd_GETX, I) { + e_sendData; + l_popForwardQueue; + } + + transition({E, M}, Fwd_GETS, O) { + e_sendData; + l_popForwardQueue; + } + + // Transitions from IM + + transition(IM, Inv) { + f_sendAck; + l_popForwardQueue; + } + + transition(IM, Ack) { + m_decrementNumberOfMessages; + o_checkForCompletion; + n_popResponseQueue; + } + + transition(IM, Data, OM) { + u_writeDataToCache; + m_decrementNumberOfMessages; + o_checkForCompletion; + n_popResponseQueue; + } + + // Transitions from SM + transition(SM, Inv, IM) { + f_sendAck; + l_popForwardQueue; + } + + transition(SM, Ack) { + m_decrementNumberOfMessages; + o_checkForCompletion; + n_popResponseQueue; + } + + transition(SM, Data, OM) { + v_writeDataToCacheVerify; + m_decrementNumberOfMessages; + o_checkForCompletion; + n_popResponseQueue; + } + + // Transitions from OM + transition(OM, Own_GETX) { + mm_decrementNumberOfMessages; + o_checkForCompletion; + l_popForwardQueue; + } + + transition(OM, Fwd_GETX, IM) { + e_sendData; + l_popForwardQueue; + } + + transition(OM, Fwd_GETS, OM) { + e_sendData; + l_popForwardQueue; + } + + transition(OM, Ack) { + m_decrementNumberOfMessages; + o_checkForCompletion; + n_popResponseQueue; + } + + transition(OM, All_acks, MM) { + hh_store_hit; + gg_sendUnblockExclusive; + s_deallocateTBE; + j_popTriggerQueue; + } + + // Transitions from IS + + transition(IS, Inv) { + f_sendAck; + l_popForwardQueue; + } + + transition(IS, Data, S) { + u_writeDataToCache; + m_decrementNumberOfMessages; + h_load_hit; + g_sendUnblock; + s_deallocateTBE; + n_popResponseQueue; + } + + transition(IS, Exclusive_Data_Clean, E) { + u_writeDataToCache; + m_decrementNumberOfMessages; + h_load_hit; + gg_sendUnblockExclusive; + s_deallocateTBE; + n_popResponseQueue; + } + + transition(IS, Exclusive_Data_Dirty, M) { + u_writeDataToCache; + m_decrementNumberOfMessages; + h_load_hit; + gg_sendUnblockExclusive; + s_deallocateTBE; + n_popResponseQueue; + } + + // Transitions from OI/MI + + transition(MI, Fwd_GETS) { + q_sendDataFromTBEToCache; + l_popForwardQueue; + } + + transition(MI, Fwd_GETX, II) { + q_sendDataFromTBEToCache; + l_popForwardQueue; + } + + transition(OI, Fwd_GETS) { + q_sendDataFromTBEToCache; + l_popForwardQueue; + } + + transition(OI, Fwd_GETX, II) { + q_sendDataFromTBEToCache; + l_popForwardQueue; + } + + transition({OI, MI}, Writeback_Ack, I) { + qq_sendDataFromTBEToMemory; + s_deallocateTBE; + l_popForwardQueue; + } + + transition(MI, Writeback_Nack, OI) { + // FIXME: This might cause deadlock by re-using the writeback + // channel, we should handle this case differently. + dd_issuePUTO; + l_popForwardQueue; + } + + // Transitions from II + transition(II, Writeback_Ack, I) { + g_sendUnblock; + s_deallocateTBE; + l_popForwardQueue; + } + + transition(II, Writeback_Nack, I) { + s_deallocateTBE; + l_popForwardQueue; + } + + transition(II, Inv) { + f_sendAck; + l_popForwardQueue; + } +} + diff --git a/src/mem/protocol/MOESI_SMP_directory-dir.sm b/src/mem/protocol/MOESI_SMP_directory-dir.sm new file mode 100644 index 000000000..b45600448 --- /dev/null +++ b/src/mem/protocol/MOESI_SMP_directory-dir.sm @@ -0,0 +1,495 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +machine(Directory, "Directory protocol") { + + MessageBuffer forwardFromDir, network="To", virtual_network="1", ordered="false"; + MessageBuffer responseFromDir, network="To", virtual_network="2", ordered="false"; + + MessageBuffer requestToDir, network="From", virtual_network="0", ordered="false"; + MessageBuffer unblockToDir, network="From", virtual_network="3", ordered="false"; + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_I") { + // Base states + I, desc="Invalid"; + S, desc="Shared"; + O, desc="Owner"; + M, desc="Modified"; + + IS, desc="Blocked, was in idle"; + SS, desc="Blocked, was in shared"; + OO, desc="Blocked, was in owned"; + MO, desc="Blocked, going to owner or maybe modified"; + MM, desc="Blocked, going to modified"; + + MI, desc="Blocked on a writeback"; + OS, desc="Blocked on a writeback"; + } + + // Events + enumeration(Event, desc="Directory events") { + GETX, desc="A GETX arrives"; + GETS, desc="A GETS arrives"; + PUTX, desc="A PUTX arrives"; + PUTO, desc="A PUTO arrives"; + Unblock, desc="An unblock message arrives"; + Last_Unblock, desc="An unblock message arrives, we're not waiting for any additional unblocks"; + Exclusive_Unblock, desc="The processor become the exclusive owner (E or M) of the line"; + Clean_Writeback, desc="The final message as part of a PutX/PutS, no data"; + Dirty_Writeback, desc="The final message as part of a PutX/PutS, contains data"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + State DirectoryState, desc="Directory state"; + DataBlock DataBlk, desc="data for the block"; + NetDest Sharers, desc="Sharers for this block"; + NetDest Owner, desc="Owner of this block"; + int WaitingUnblocks, desc="Number of acks we're waiting for"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + // External function + void profile_sharing(Address addr, AccessType type, NodeID requestor, Set sharers, Set owner); + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + + State getState(Address addr) { + return directory[addr].DirectoryState; + } + + void setState(Address addr, State state) { + if (directory.isPresent(addr)) { + + if ((state == State:I) || (state == State:IS)) { + assert(directory[addr].Owner.count() == 0); + assert(directory[addr].Sharers.count() == 0); + } + + if ((state == State:S) || (state == State:SS)) { + assert(directory[addr].Owner.count() == 0); + assert(directory[addr].Sharers.count() != 0); + } + + if ((state == State:O) || (state == State:OO)) { + assert(directory[addr].Owner.count() == 1); + assert(directory[addr].Sharers.isSuperset(directory[addr].Owner) == false); + } + + if (state == State:M) { + assert(directory[addr].Owner.count() == 1); + assert(directory[addr].Sharers.count() == 0); + } + + if ((state != State:SS) && (state != State:OO)) { + assert(directory[addr].WaitingUnblocks == 0); + } + + directory[addr].DirectoryState := state; + } + } + + // ** OUT_PORTS ** + out_port(forwardNetwork_out, RequestMsg, forwardFromDir); + out_port(responseNetwork_out, ResponseMsg, responseFromDir); + out_port(requestQueue_out, ResponseMsg, requestToDir); // For recycling requests + + // ** IN_PORTS ** + + in_port(unblockNetwork_in, ResponseMsg, unblockToDir) { + if (unblockNetwork_in.isReady()) { + peek(unblockNetwork_in, ResponseMsg) { + if (in_msg.Type == CoherenceResponseType:UNBLOCK) { + if (directory[in_msg.Address].WaitingUnblocks == 1) { + trigger(Event:Last_Unblock, in_msg.Address); + } else { + trigger(Event:Unblock, in_msg.Address); + } + } else if (in_msg.Type == CoherenceResponseType:UNBLOCK_EXCLUSIVE) { + trigger(Event:Exclusive_Unblock, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WRITEBACK_DIRTY) { + trigger(Event:Dirty_Writeback, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:WRITEBACK_CLEAN) { + trigger(Event:Clean_Writeback, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + in_port(requestQueue_in, RequestMsg, requestToDir) { + if (requestQueue_in.isReady()) { + peek(requestQueue_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTX) { + trigger(Event:PUTX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTO) { + trigger(Event:PUTO, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + // Actions + + action(a_sendWriteBackAck, "a", desc="Send writeback ack to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:WB_ACK; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(b_sendWriteBackNack, "b", desc="Send writeback nack to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:WB_NACK; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + } + + action(c_clearOwner, "c", desc="Clear the owner field") { + directory[address].Owner.clear(); + } + + action(cc_clearSharers, "\c", desc="Clear the sharers field") { + directory[address].Sharers.clear(); + } + + action(d_sendData, "d", desc="Send data to requestor") { + peek(requestQueue_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + + if (in_msg.Type == CoherenceRequestType:GETS && directory[address].Sharers.count() == 0) { + out_msg.Type := CoherenceResponseType:DATA_EXCLUSIVE_CLEAN; + } else { + out_msg.Type := CoherenceResponseType:DATA; + } + + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.Dirty := false; // By definition, the block is now clean + out_msg.Acks := directory[address].Sharers.count(); + if (directory[address].Sharers.isElement(in_msg.Requestor)) { + out_msg.Acks := out_msg.Acks - 1; + } + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + action(e_ownerIsUnblocker, "e", desc="The owner is now the unblocker") { + peek(unblockNetwork_in, ResponseMsg) { + directory[address].Owner.clear(); + directory[address].Owner.add(in_msg.Sender); + } + } + + action(f_forwardRequest, "f", desc="Forward request to owner") { + peek(requestQueue_in, RequestMsg) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := directory[in_msg.Address].Owner; + out_msg.Acks := directory[address].Sharers.count(); + if (directory[address].Sharers.isElement(in_msg.Requestor)) { + out_msg.Acks := out_msg.Acks - 1; + } + out_msg.MessageSize := MessageSizeType:Forwarded_Control; + } + } + } + + action(g_sendInvalidations, "g", desc="Send invalidations to sharers, not including the requester") { + peek(requestQueue_in, RequestMsg) { + if ((directory[in_msg.Address].Sharers.count() > 1) || + ((directory[in_msg.Address].Sharers.count() > 0) && (directory[in_msg.Address].Sharers.isElement(in_msg.Requestor) == false))) { + enqueue(forwardNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := directory[in_msg.Address].Sharers; + out_msg.Destination.remove(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Forwarded_Control; + } + } + } + } + + action(i_popIncomingRequestQueue, "i", desc="Pop incoming request queue") { + // Profile the request + peek(requestQueue_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETX) { + // profile_sharing(address, AccessType:Write, machineIDToNodeID(in_msg.Requestor), directory[address].Sharers, directory[address].Owner); + } else if (in_msg.Type == CoherenceRequestType:GETS) { + // profile_sharing(address, AccessType:Read, machineIDToNodeID(in_msg.Requestor), directory[address].Sharers, directory[address].Owner); + } + } + + requestQueue_in.dequeue(); + } + + action(j_popIncomingUnblockQueue, "j", desc="Pop incoming unblock queue") { + unblockNetwork_in.dequeue(); + } + + action(l_writeDataToMemory, "l", desc="Write PUTX/PUTO data to memory") { + peek(unblockNetwork_in, ResponseMsg) { + assert(in_msg.Dirty); + assert(in_msg.MessageSize == MessageSizeType:Writeback_Data); + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + action(ll_checkDataInMemory, "\l", desc="Check PUTX/PUTO data is same as in the memory") { + peek(unblockNetwork_in, ResponseMsg) { + assert(in_msg.Dirty == false); + assert(in_msg.MessageSize == MessageSizeType:Writeback_Control); + + // NOTE: The following check would not be valid in a real + // implementation. We include the data in the "dataless" + // message so we can assert the clean data matches the datablock + // in memory + assert(directory[in_msg.Address].DataBlk == in_msg.DataBlk); + } + } + + action(m_addUnlockerToSharers, "m", desc="Add the unlocker to the sharer list") { + peek(unblockNetwork_in, ResponseMsg) { + directory[address].Sharers.add(in_msg.Sender); + } + } + + action(n_incrementOutstanding, "n", desc="Increment outstanding requests") { + directory[address].WaitingUnblocks := directory[address].WaitingUnblocks + 1; + } + + action(o_decrementOutstanding, "o", desc="Decrement outstanding requests") { + directory[address].WaitingUnblocks := directory[address].WaitingUnblocks - 1; + assert(directory[address].WaitingUnblocks >= 0); + } + + // action(z_stall, "z", desc="Cannot be handled right now.") { + // Special name recognized as do nothing case + // } + + action(zz_recycleRequest, "\z", desc="Recycle the request queue") { + requestQueue_in.recycle(); + } + + // TRANSITIONS + + transition(I, GETX, MM) { + d_sendData; + i_popIncomingRequestQueue; + } + + transition(S, GETX, MM) { + d_sendData; + g_sendInvalidations; + i_popIncomingRequestQueue; + } + + transition(I, GETS, IS) { + d_sendData; + i_popIncomingRequestQueue; + } + + transition({S, SS}, GETS, SS) { + d_sendData; + n_incrementOutstanding; + i_popIncomingRequestQueue; + } + + transition({I, S, M}, PUTO) { + b_sendWriteBackNack; + i_popIncomingRequestQueue; + } + + transition({I, S, O}, PUTX) { + b_sendWriteBackNack; + i_popIncomingRequestQueue; + } + + transition(O, GETX, MM) { + f_forwardRequest; + g_sendInvalidations; + i_popIncomingRequestQueue; + } + + transition({O, OO}, GETS, OO) { + f_forwardRequest; + n_incrementOutstanding; + i_popIncomingRequestQueue; + } + + transition(M, GETX, MM) { + f_forwardRequest; + i_popIncomingRequestQueue; + } + + transition(M, GETS, MO) { + f_forwardRequest; + i_popIncomingRequestQueue; + } + + transition(M, PUTX, MI) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + transition(O, PUTO, OS) { + a_sendWriteBackAck; + i_popIncomingRequestQueue; + } + + transition({MM, MO, MI, OS}, {GETS, GETX, PUTO, PUTX}) { + zz_recycleRequest; + } + + transition({MM, MO}, Exclusive_Unblock, M) { + cc_clearSharers; + e_ownerIsUnblocker; + j_popIncomingUnblockQueue; + } + + transition(MO, Unblock, O) { + m_addUnlockerToSharers; + j_popIncomingUnblockQueue; + } + + transition({IS, SS, OO}, {GETX, PUTO, PUTX}) { + zz_recycleRequest; + } + + transition(IS, GETS) { + zz_recycleRequest; + } + + transition(IS, Unblock, S) { + m_addUnlockerToSharers; + j_popIncomingUnblockQueue; + } + + transition(IS, Exclusive_Unblock, M) { + cc_clearSharers; + e_ownerIsUnblocker; + j_popIncomingUnblockQueue; + } + + transition(SS, Unblock) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(SS, Last_Unblock, S) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(OO, Unblock) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(OO, Last_Unblock, O) { + m_addUnlockerToSharers; + o_decrementOutstanding; + j_popIncomingUnblockQueue; + } + + transition(MI, Dirty_Writeback, I) { + c_clearOwner; + cc_clearSharers; + l_writeDataToMemory; + j_popIncomingUnblockQueue; + } + + transition(OS, Dirty_Writeback, S) { + c_clearOwner; + l_writeDataToMemory; + j_popIncomingUnblockQueue; + } + + transition(MI, Clean_Writeback, I) { + c_clearOwner; + cc_clearSharers; + ll_checkDataInMemory; + j_popIncomingUnblockQueue; + } + + transition(OS, Clean_Writeback, S) { + c_clearOwner; + ll_checkDataInMemory; + j_popIncomingUnblockQueue; + } + + transition(MI, Unblock, M) { + j_popIncomingUnblockQueue; + } + + transition(OS, Unblock, O) { + j_popIncomingUnblockQueue; + } +} diff --git a/src/mem/protocol/MOESI_SMP_directory-msg.sm b/src/mem/protocol/MOESI_SMP_directory-msg.sm new file mode 100644 index 000000000..1b7424308 --- /dev/null +++ b/src/mem/protocol/MOESI_SMP_directory-msg.sm @@ -0,0 +1,89 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +// CoherenceRequestType +enumeration(CoherenceRequestType, desc="...") { + GETX, desc="Get eXclusive"; + GETS, desc="Get Shared"; + PUTX, desc="Put eXclusive"; + PUTO, desc="Put Owned"; + WB_ACK, desc="Writeback ack"; + WB_NACK, desc="Writeback neg. ack"; + INV, desc="Invalidation"; +} + +// CoherenceResponseType +enumeration(CoherenceResponseType, desc="...") { + ACK, desc="ACKnowledgment, responder doesn't have a copy"; + DATA, desc="Data"; + DATA_EXCLUSIVE_CLEAN, desc="Data, no other processor has a copy, data is clean"; + DATA_EXCLUSIVE_DIRTY, desc="Data, no other processor has a copy, data is dirty"; + UNBLOCK, desc="Unblock"; + UNBLOCK_EXCLUSIVE, desc="Unblock, we're in E/M"; + WRITEBACK_CLEAN, desc="Clean writeback (no data)"; + WRITEBACK_DIRTY, desc="Dirty writeback (contains data)"; +} + +// TriggerType +enumeration(TriggerType, desc="...") { + ALL_ACKS, desc="See corresponding event"; +} + +// TriggerMsg +structure(TriggerMsg, desc="...", interface="Message") { + Address Address, desc="Physical address for this request"; + TriggerType Type, desc="Type of trigger"; +} + +// RequestMsg (and also forwarded requests) +structure(RequestMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)"; + MachineID Requestor, desc="Node who initiated the request"; + NetDest Destination, desc="Multicast destination mask"; + int Acks, desc="How many acks to expect"; + MessageSizeType MessageSize, desc="size category of the message"; +} + +// ResponseMsg (and also unblock requests) +structure(ResponseMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)"; + MachineID Sender, desc="Node who sent the data"; + NetDest Destination, desc="Node to whom the data is sent"; + DataBlock DataBlk, desc="data for the cache line"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + int Acks, desc="How many acks to expect"; + MessageSizeType MessageSize, desc="size category of the message"; +} diff --git a/src/mem/protocol/MOESI_SMP_directory.slicc b/src/mem/protocol/MOESI_SMP_directory.slicc new file mode 100644 index 000000000..e82aaf7f7 --- /dev/null +++ b/src/mem/protocol/MOESI_SMP_directory.slicc @@ -0,0 +1,4 @@ +MOESI_SMP_directory-msg.sm +MOESI_SMP_directory-cache.sm +MOESI_SMP_directory-dir.sm +standard_SMP-protocol.sm diff --git a/src/mem/protocol/MOESI_SMP_token-cache.sm b/src/mem/protocol/MOESI_SMP_token-cache.sm new file mode 100644 index 000000000..e39f73b18 --- /dev/null +++ b/src/mem/protocol/MOESI_SMP_token-cache.sm @@ -0,0 +1,1734 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOESI_token-cache.sm 1.10 05/01/19 15:41:25-06:00 beckmann@emperor11.cs.wisc.edu $ + * + */ + +machine(L1Cache, "Token protocol") { + + MessageBuffer requestFromCache, network="To", virtual_network="1", ordered="false"; + MessageBuffer responseFromCache, network="To", virtual_network="0", ordered="false"; + MessageBuffer persistentFromCache, network="To", virtual_network="2", ordered="true"; + + MessageBuffer requestToCache, network="From", virtual_network="1", ordered="false"; + MessageBuffer responseToCache, network="From", virtual_network="0", ordered="false"; + MessageBuffer persistentToCache, network="From", virtual_network="2", ordered="true"; + + + // STATES + enumeration(State, desc="Cache states", default="L1Cache_State_I") { + // Base states + NP, "NP", desc="Not Present"; + I, "I", desc="Idle"; + S, "S", desc="Shared"; + O, "O", desc="Owned"; + M, "M", desc="Modified (dirty)"; + MM, "MM", desc="Modified (dirty and locally modified)"; + M_W, "M^W", desc="Modified (dirty), waiting"; + MM_W, "MM^W", desc="Modified (dirty and locally modified), waiting"; + + // Transient States + IM, "IM", desc="Issued GetX"; + SM, "SM", desc="Issued GetX, we still have an old copy of the line"; + OM, "OM", desc="Issued GetX, received data"; + IS, "IS", desc="Issued GetS"; + + // Locked states + I_L, "I^L", desc="Invalid, Locked"; + S_L, "S^L", desc="Shared, Locked"; + IM_L, "IM^L", desc="Invalid, Locked, trying to go to Modified"; + SM_L, "SM^L", desc="Shared, Locked, trying to go to Modified"; + IS_L, "IS^L", desc="Invalid, Locked, trying to go to Shared"; + } + + // EVENTS + enumeration(Event, desc="Cache events") { + Load, desc="Load request from the processor"; + Ifetch, desc="I-fetch request from the processor"; + Store, desc="Store request from the processor"; + L2_Replacement, desc="L2 Replacement"; + L1_to_L2, desc="L1 to L2 transfer"; + L2_to_L1D, desc="L2 to L1-Data transfer"; + L2_to_L1I, desc="L2 to L1-Instruction transfer"; + + // Responses + Data_Shared, desc="Received a data message, we are now a sharer"; + Data_Shared_All_Tokens, desc="Received a data message, we are now a sharer, we now have all the tokens"; + Data_Owner, desc="Received a data message, we are now the owner"; + Data_Owner_All_Tokens, desc="Received a data message, we are now the owner, we now have all the tokens"; + Ack, desc="Received an ack message"; + Ack_All_Tokens, desc="Received an ack message, we now have all the tokens"; + + // Requests + Transient_GETX, desc="A GetX from another processor"; + Transient_GETS, desc="A GetS from another processor"; + + // Lock/Unlock + Persistent_GETX, desc="Another processor has priority to read/write"; + Persistent_GETS, desc="Another processor has priority to read"; + Own_Lock_or_Unlock, desc="This processor now has priority"; + + // Triggers + Request_Timeout, desc="Timeout"; + Use_Timeout, desc="Timeout"; + + } + + // TYPES + + int getRetryThreshold(); + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + DataBlock DataBlk, desc="data for the block, required by CacheMemory"; + State CacheState, desc="cache state"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + int Tokens, desc="The number of tokens we're holding for the line"; + } + + // TBE fields + structure(TBE, desc="...") { + State TBEState, desc="Transient state"; + int IssueCount, default="0", desc="The number of times we've issued a request for this line."; + Address PC, desc="Program counter of request"; + AccessType AccessType, desc="Type of request (used for profiling)"; + Time IssueTime, desc="Time the request was issued"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + external_type(TimerTable, inport="yes") { + bool isReady(); + Address readyAddress(); + void set(Address, int); + void unset(Address); + bool isSet(Address); + } + + MessageBuffer mandatoryQueue, ordered="false", abstract_chip_ptr="true"; + Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i"; + + TBETable TBEs, template_hack=""; + CacheMemory L1IcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1I"', abstract_chip_ptr="true"; + CacheMemory L1DcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1D"', abstract_chip_ptr="true"; + CacheMemory L2cacheMemory, template_hack="", constructor_hack='L2_CACHE_NUM_SETS_BITS,L2_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L2"', abstract_chip_ptr="true"; + PersistentTable persistentTable, constructor_hack="i"; + TimerTable useTimerTable; + TimerTable reissueTimerTable; + + int outstandingRequests, default="0"; + int outstandingPersistentRequests, default="0"; + void profile_outstanding_request(int outstanding); + void profile_outstanding_persistent_request(int outstanding); + + int averageLatencyHysteresis, default="(8)"; // Constant that provides hysteresis for calculated the estimated average + int averageLatencyCounter, default="(500 << (*(m_L1Cache_averageLatencyHysteresis_vec[i])))"; + // int averageLatencyCounter, default="(250)"; + + int averageLatencyEstimate() { + return averageLatencyCounter >> averageLatencyHysteresis; + } + + void updateAverageLatencyEstimate(int latency) { + assert(latency >= 0); + + // By subtracting the current average and then adding the most + // recent sample, we calculate an estimate of the recent average. + // If we simply used a running sum and divided by the total number + // of entries, the estimate of the average would adapt very slowly + // after the execution has run for a long time. + averageLatencyCounter := averageLatencyCounter - averageLatencyEstimate() + latency; + } + + Entry getCacheEntry(Address addr), return_by_ref="yes" { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory[addr]; + } else if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory[addr]; + } else { + return L1IcacheMemory[addr]; + } + } + + int getTokens(Address addr) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory[addr].Tokens; + } else if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory[addr].Tokens; + } else if (L1IcacheMemory.isTagPresent(addr)) { + return L1IcacheMemory[addr].Tokens; + } else { + return 0; + } + } + + void changePermission(Address addr, AccessPermission permission) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory.changePermission(addr, permission); + } else if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory.changePermission(addr, permission); + } else { + return L1IcacheMemory.changePermission(addr, permission); + } + } + + bool isCacheTagPresent(Address addr) { + return (L2cacheMemory.isTagPresent(addr) || L1DcacheMemory.isTagPresent(addr) || L1IcacheMemory.isTagPresent(addr)); + } + + State getState(Address addr) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + assert((L1IcacheMemory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + assert((L1DcacheMemory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + + if (TBEs.isPresent(addr)) { + return TBEs[addr].TBEState; + } else if (isCacheTagPresent(addr)) { + return getCacheEntry(addr).CacheState; + } else if ((persistentTable.isLocked(addr) == true) && (persistentTable.findSmallest(addr) != machineID)) { + // Not in cache, in persistent table, but this processor isn't highest priority + return State:I_L; + } else { + return State:NP; + } + } + + void setState(Address addr, State state) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + assert((L1IcacheMemory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + assert((L1DcacheMemory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + + assert(outstandingPersistentRequests >= 0); + assert(outstandingRequests >= 0); + + if (useTimerTable.isSet(addr)) { + assert((state == State:M_W) || (state == State:MM_W)); + } else { + assert(state != State:M_W); + assert(state != State:MM_W); + } + + if (reissueTimerTable.isSet(addr)) { + assert((state == State:IS) || + (state == State:IM) || + (state == State:SM) || + (state == State:OM) || + (state == State:IS_L) || + (state == State:IM_L) || + (state == State:SM_L)); + } else if (TBEs.isPresent(addr) && TBEs[addr].IssueCount < getRetryThreshold()) { + // If the timer is not set, you better have issued a persistent request + assert(state != State:IS); + assert(state != State:IM); + assert(state != State:SM); + assert(state != State:OM); + assert(state != State:IS_L); + assert(state != State:IM_L); + assert(state != State:SM_L); + } + + if (TBEs.isPresent(addr) && (TBEs[addr].IssueCount > getRetryThreshold())) { + assert(reissueTimerTable.isSet(addr) == false); + } + + if (TBEs.isPresent(addr)) { + assert(state != State:I); + assert(state != State:S); + assert(state != State:O); + assert(state != State:MM); + assert(state != State:M); + TBEs[addr].TBEState := state; + } + + if (isCacheTagPresent(addr)) { + // Make sure the token count is in range + assert(getCacheEntry(addr).Tokens >= 0); + assert(getCacheEntry(addr).Tokens <= max_tokens()); + + if ((state == State:I_L) || + (state == State:IM_L) || + (state == State:IS_L)) { + // Make sure we have no tokens in the "Invalid, locked" states + if (isCacheTagPresent(addr)) { + assert(getCacheEntry(addr).Tokens == 0); + } + + // Make sure the line is locked + assert(persistentTable.isLocked(addr)); + + // But we shouldn't have highest priority for it + assert(persistentTable.findSmallest(addr) != machineID); + + } else if ((state == State:S_L) || + (state == State:SM_L)) { + // Make sure we have only one token in the "Shared, locked" states + assert(getCacheEntry(addr).Tokens == 1); + + // Make sure the line is locked... + assert(persistentTable.isLocked(addr)); + + // ...But we shouldn't have highest priority for it... + assert(persistentTable.findSmallest(addr) != machineID); + + // ...And it must be a GETS request + assert(persistentTable.typeOfSmallest(addr) == AccessType:Read); + + } else { + + // If there is an entry in the persistent table of this block, + // this processor needs to have an entry in the table for this + // block, and that entry better be the smallest (highest + // priority). Otherwise, the state should have been one of + // locked states + + if (persistentTable.isLocked(addr)) { + assert(persistentTable.findSmallest(addr) == machineID); + } + } + + // in M and E you have all the tokens + if (state == State:MM || state == State:M || state == State:MM_W || state == State:M_W) { + assert(getCacheEntry(addr).Tokens == max_tokens()); + } + + // in NP you have no tokens + if (state == State:NP) { + assert(getCacheEntry(addr).Tokens == 0); + } + + // You have at least one token in S-like states + if (state == State:S || state == State:SM) { + assert(getCacheEntry(addr).Tokens > 0); + } + + // You have at least half the token in O-like states + if (state == State:O && state == State:OM) { + assert(getCacheEntry(addr).Tokens >= 1); // Must have at least one token + assert(getCacheEntry(addr).Tokens >= (max_tokens() / 2)); // Only mostly true; this might not always hold + } + + getCacheEntry(addr).CacheState := state; + + // Set permission + if (state == State:MM || + state == State:MM_W) { + changePermission(addr, AccessPermission:Read_Write); + } else if ((state == State:S) || + (state == State:O) || + (state == State:M) || + (state == State:M_W) || + (state == State:SM) || + (state == State:SM_L) || + (state == State:OM)) { + changePermission(addr, AccessPermission:Read_Only); + } else { + changePermission(addr, AccessPermission:Invalid); + } + } + } + + Event mandatory_request_type_to_event(CacheRequestType type) { + if (type == CacheRequestType:LD) { + return Event:Load; + } else if (type == CacheRequestType:IFETCH) { + return Event:Ifetch; + } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { + return Event:Store; + } else { + error("Invalid CacheRequestType"); + } + } + + AccessType cache_request_type_to_access_type(CacheRequestType type) { + if ((type == CacheRequestType:LD) || (type == CacheRequestType:IFETCH)) { + return AccessType:Read; + } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { + return AccessType:Write; + } else { + error("Invalid CacheRequestType"); + } + } + + // ** OUT_PORTS ** + out_port(persistentNetwork_out, PersistentMsg, persistentFromCache); + out_port(requestNetwork_out, RequestMsg, requestFromCache); + out_port(responseNetwork_out, ResponseMsg, responseFromCache); + + // ** IN_PORTS ** + + // Use Timer + in_port(useTimerTable_in, Address, useTimerTable) { + if (useTimerTable_in.isReady()) { + trigger(Event:Use_Timeout, useTimerTable.readyAddress()); + } + } + + // Reissue Timer + in_port(reissueTimerTable_in, Address, reissueTimerTable) { + if (reissueTimerTable_in.isReady()) { + trigger(Event:Request_Timeout, reissueTimerTable.readyAddress()); + } + } + + // Persistent Network + in_port(persistentNetwork_in, PersistentMsg, persistentToCache) { + if (persistentNetwork_in.isReady()) { + peek(persistentNetwork_in, PersistentMsg) { + + // Apply the lockdown or unlockdown message to the table + if (in_msg.Type == PersistentRequestType:GETX_PERSISTENT) { + persistentTable.persistentRequestLock(in_msg.Address, in_msg.Requestor, AccessType:Write); + } else if (in_msg.Type == PersistentRequestType:GETS_PERSISTENT) { + persistentTable.persistentRequestLock(in_msg.Address, in_msg.Requestor, AccessType:Read); + } else if (in_msg.Type == PersistentRequestType:DEACTIVATE_PERSISTENT) { + persistentTable.persistentRequestUnlock(in_msg.Address, in_msg.Requestor); + } else { + error("Unexpected message"); + } + + // React to the message based on the current state of the table + if (persistentTable.isLocked(in_msg.Address)) { + if (persistentTable.findSmallest(in_msg.Address) == machineID) { + // Our Own Lock - this processor is highest priority + trigger(Event:Own_Lock_or_Unlock, in_msg.Address); + } else { + if (persistentTable.typeOfSmallest(in_msg.Address) == AccessType:Read) { + trigger(Event:Persistent_GETS, in_msg.Address); + } else { + trigger(Event:Persistent_GETX, in_msg.Address); + } + } + } else { + // Unlock case - no entries in the table + trigger(Event:Own_Lock_or_Unlock, in_msg.Address); + } + } + } + } + + + // Request Network + in_port(requestNetwork_in, RequestMsg, requestToCache) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:Transient_GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:Transient_GETS, in_msg.Address); + } else { + error("Unexpected message"); + } + } + } + } + + // Response Network + in_port(responseNetwork_in, ResponseMsg, responseToCache) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + + if (getTokens(in_msg.Address) + in_msg.Tokens != max_tokens()) { + if (in_msg.Type == CoherenceResponseType:ACK) { + trigger(Event:Ack, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_OWNER) { + trigger(Event:Data_Owner, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_SHARED) { + trigger(Event:Data_Shared, in_msg.Address); + } else { + error("Unexpected message"); + } + } else { + if (in_msg.Type == CoherenceResponseType:ACK) { + trigger(Event:Ack_All_Tokens, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_OWNER) { + trigger(Event:Data_Owner_All_Tokens, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_SHARED) { + trigger(Event:Data_Shared_All_Tokens, in_msg.Address); + } else { + error("Unexpected message"); + } + } + } + } + } + + // Mandatory Queue + in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { + if (mandatoryQueue_in.isReady()) { + peek(mandatoryQueue_in, CacheMsg) { + // Check for data access to blocks in I-cache and ifetchs to blocks in D-cache + + if (in_msg.Type == CacheRequestType:IFETCH) { + // ** INSTRUCTION ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, try to write it to the L2 + if (L2cacheMemory.cacheAvail(in_msg.Address)) { + trigger(Event:L1_to_L2, in_msg.Address); + } else { + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } + + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 fetches the line. We know it can't be in the L2 due to exclusion + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1IcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 + if (L2cacheMemory.isTagPresent(in_msg.Address)) { + // L2 has it (maybe not with the right permissions) + trigger(Event:L2_to_L1I, in_msg.Address); + } else { + // We have room, the L2 doesn't have it, so the L1 fetches the line + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } + } else { + // No room in the L1, so we need to make room + if (L2cacheMemory.cacheAvail(L1IcacheMemory.cacheProbe(in_msg.Address))) { + // The L2 has room, so we move the line from the L1 to the L2 + trigger(Event:L1_to_L2, L1IcacheMemory.cacheProbe(in_msg.Address)); + } else { + // The L2 does not have room, so we replace a line from the L2 + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(L1IcacheMemory.cacheProbe(in_msg.Address))); + } + } + } + } else { + // *** DATA ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, try to write it to the L2 + if (L2cacheMemory.cacheAvail(in_msg.Address)) { + trigger(Event:L1_to_L2, in_msg.Address); + } else { + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } + + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 fetches the line. We know it can't be in the L2 due to exclusion + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1DcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 + if (L2cacheMemory.isTagPresent(in_msg.Address)) { + // L2 has it (maybe not with the right permissions) + trigger(Event:L2_to_L1D, in_msg.Address); + } else { + // We have room, the L2 doesn't have it, so the L1 fetches the line + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } + } else { + // No room in the L1, so we need to make room + if (L2cacheMemory.cacheAvail(L1DcacheMemory.cacheProbe(in_msg.Address))) { + // The L2 has room, so we move the line from the L1 to the L2 + trigger(Event:L1_to_L2, L1DcacheMemory.cacheProbe(in_msg.Address)); + } else { + // The L2 does not have room, so we replace a line from the L2 + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(L1DcacheMemory.cacheProbe(in_msg.Address))); + } + } + } + } + } + } + } + + // ACTIONS + + action(a_issueRequest, "a", desc="Issue GETS or GETX request (transient or persistent)") { + + if (TBEs[address].IssueCount == 0) { + // Update outstanding requests + profile_outstanding_request(outstandingRequests); + outstandingRequests := outstandingRequests + 1; + } + + if (TBEs[address].IssueCount < getRetryThreshold()) { + // Issue a normal request + enqueue(requestNetwork_out, RequestMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Requestor := machineID; + out_msg.Destination.broadcast(MachineType:L1Cache); + out_msg.Destination.add(map_Address_to_Directory(address)); + + if (TBEs[address].AccessType == AccessType:Read) { + out_msg.Type := CoherenceRequestType:GETS; + } else { + out_msg.Type := CoherenceRequestType:GETX; + } + + if (TBEs[address].IssueCount == 0) { + out_msg.MessageSize := MessageSizeType:Request_Control; + } else { + out_msg.MessageSize := MessageSizeType:Reissue_Control; + } + } + + // Increment IssueCount + TBEs[address].IssueCount := TBEs[address].IssueCount + 1; + + // Set a wakeup timer + reissueTimerTable.set(address, 2*averageLatencyEstimate()); + + } else { + // Try to issue a Persistent Request + if (persistentTable.okToIssueStarving(address)) { + // Issue a persistent request + enqueue(persistentNetwork_out, PersistentMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + if (TBEs[address].AccessType == AccessType:Read) { + out_msg.Type := PersistentRequestType:GETS_PERSISTENT; + } else { + out_msg.Type := PersistentRequestType:GETX_PERSISTENT; + } + out_msg.Requestor := machineID; + out_msg.Destination.broadcast(MachineType:L1Cache); + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Persistent_Control; + } + persistentTable.markEntries(address); + + // Update outstanding requests + profile_outstanding_persistent_request(outstandingPersistentRequests); + outstandingPersistentRequests := outstandingPersistentRequests + 1; + + // Increment IssueCount + TBEs[address].IssueCount := TBEs[address].IssueCount + 1; + + // Do not schedule a wakeup, a persistent requests will always complete + + } else { + // We'd like to issue a persistent request, but are not allowed + // to issue a P.R. right now. This, we do not increment the + // IssueCount. + + + // Set a wakeup timer + reissueTimerTable.set(address, 10); + } + } + } + + action(b_bounceResponse, "b", desc="Bounce tokens and data to memory") { + peek(responseNetwork_in, ResponseMsg) { + // FIXME, should use a 3rd vnet + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.DestMachine := MachineType:Directory; + out_msg.Tokens := in_msg.Tokens; + out_msg.MessageSize := in_msg.MessageSize; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := in_msg.Dirty; + } + } + } + + action(c_cleanReplacement, "c", desc="Issue clean writeback") { + if (getCacheEntry(address).Tokens > 0) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.DestMachine := MachineType:Directory; + out_msg.Tokens := getCacheEntry(address).Tokens; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + getCacheEntry(address).Tokens := 0; + } + } + + action(cc_dirtyReplacement, "\c", desc="Issue dirty writeback") { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.DestMachine := MachineType:Directory; + out_msg.Tokens := getCacheEntry(address).Tokens; + out_msg.Dirty := getCacheEntry(address).Dirty; + if (getCacheEntry(address).Dirty) { + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.MessageSize := MessageSizeType:Writeback_Data; + } else { + out_msg.Type := CoherenceResponseType:ACK_OWNER; + // NOTE: in a real system this would not send data. We send + // data here only so we can check it at the memory + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.MessageSize := MessageSizeType:Writeback_Control; + } + } + getCacheEntry(address).Tokens := 0; + } + + action(d_sendDataWithToken, "d", desc="Send data and a token from cache to requestor") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_SHARED; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + out_msg.Tokens := 1; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + getCacheEntry(address).Tokens := getCacheEntry(address).Tokens - 1; + assert(getCacheEntry(address).Tokens >= 1); + } + + action(dd_sendDataWithAllTokens, "\d", desc="Send data and all tokens from cache to requestor") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + assert(getCacheEntry(address).Tokens >= 1); + out_msg.Tokens := getCacheEntry(address).Tokens; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + getCacheEntry(address).Tokens := 0; + } + + action(e_sendAckWithCollectedTokens, "e", desc="Send ack with the tokens we've collected thus far.") { + assert(persistentTable.findSmallest(address) != machineID); // Make sure we never bounce tokens to ourself + if (getCacheEntry(address).Tokens > 0) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.DestMachine := MachineType:L1Cache; + assert(getCacheEntry(address).Tokens >= 1); + out_msg.Tokens := getCacheEntry(address).Tokens; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + getCacheEntry(address).Tokens := 0; + } + + action(ee_sendDataWithAllTokens, "\e", desc="Send data and all tokens from cache to starver") { + assert(persistentTable.findSmallest(address) != machineID); // Make sure we never bounce tokens to ourself + assert(getCacheEntry(address).Tokens > 0); + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.DestMachine := MachineType:L1Cache; + assert(getCacheEntry(address).Tokens >= 1); + out_msg.Tokens := getCacheEntry(address).Tokens; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + getCacheEntry(address).Tokens := 0; + } + + action(f_sendAckWithAllButOneTokens, "f", desc="Send ack with all our tokens but one to starver.") { + assert(persistentTable.findSmallest(address) != machineID); // Make sure we never bounce tokens to ourself + assert(getCacheEntry(address).Tokens > 0); + if (getCacheEntry(address).Tokens > 1) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.DestMachine := MachineType:L1Cache; + assert(getCacheEntry(address).Tokens >= 1); + out_msg.Tokens := getCacheEntry(address).Tokens - 1; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + getCacheEntry(address).Tokens := 1; + } + + action(ff_sendDataWithAllButOneTokens, "\f", desc="Send data and out tokens but one to starver") { + assert(persistentTable.findSmallest(address) != machineID); // Make sure we never bounce tokens to ourself + assert(getCacheEntry(address).Tokens > 0); + if (getCacheEntry(address).Tokens > 1) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.DestMachine := MachineType:L1Cache; + assert(getCacheEntry(address).Tokens >= 1); + out_msg.Tokens := getCacheEntry(address).Tokens - 1; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Dirty := getCacheEntry(address).Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + getCacheEntry(address).Tokens := 1; + } + } + + action(g_bounceResponseToStarver, "g", desc="Redirect response to starving processor") { + assert(persistentTable.isLocked(address)); + peek(responseNetwork_in, ResponseMsg) { + assert(persistentTable.findSmallest(address) != machineID); // Make sure we never bounce tokens to ourself + // FIXME, should use a 3rd vnet in some cases + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.DestMachine := MachineType:L1Cache; + out_msg.Tokens := in_msg.Tokens; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := in_msg.Dirty; + out_msg.MessageSize := in_msg.MessageSize; + } + } + } + + action(h_load_hit, "h", desc="Notify sequencer the load completed.") { + DEBUG_EXPR(getCacheEntry(address).DataBlk); + sequencer.readCallback(address, getCacheEntry(address).DataBlk); + } + + action(hh_store_hit, "\h", desc="Notify sequencer that store completed.") { + DEBUG_EXPR(getCacheEntry(address).DataBlk); + sequencer.writeCallback(address, getCacheEntry(address).DataBlk); + getCacheEntry(address).Dirty := true; + } + + action(i_allocateTBE, "i", desc="Allocate TBE") { + check_allocate(TBEs); + TBEs.allocate(address); + TBEs[address].IssueCount := 0; + peek(mandatoryQueue_in, CacheMsg) { + TBEs[address].PC := in_msg.ProgramCounter; + TBEs[address].AccessType := cache_request_type_to_access_type(in_msg.Type); + } + TBEs[address].IssueTime := get_time(); + } + + action(j_unsetReissueTimer, "j", desc="Unset reissue timer.") { + if (reissueTimerTable.isSet(address)) { + reissueTimerTable.unset(address); + } + } + + action(jj_unsetUseTimer, "\j", desc="Unset use timer.") { + useTimerTable.unset(address); + } + + action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") { + mandatoryQueue_in.dequeue(); + } + + action(l_popPersistentQueue, "l", desc="Pop persistent queue.") { + persistentNetwork_in.dequeue(); + } + + action(m_popRequestQueue, "m", desc="Pop request queue.") { + requestNetwork_in.dequeue(); + } + + action(n_popResponseQueue, "n", desc="Pop response queue") { + responseNetwork_in.dequeue(); + } + + action(o_scheduleUseTimeout, "o", desc="Schedule a use timeout.") { + useTimerTable.set(address, 15); + } + + action(q_updateTokensFromResponse, "q", desc="Update the token count based on the incoming response message") { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Tokens != 0); + getCacheEntry(address).Tokens := getCacheEntry(address).Tokens + in_msg.Tokens; + } + } + + action(s_deallocateTBE, "s", desc="Deallocate TBE") { + outstandingRequests := outstandingRequests - 1; + if (TBEs[address].IssueCount > getRetryThreshold()) { + outstandingPersistentRequests := outstandingPersistentRequests - 1; + enqueue(persistentNetwork_out, PersistentMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := PersistentRequestType:DEACTIVATE_PERSISTENT; + out_msg.Requestor := machineID; + out_msg.Destination.broadcast(MachineType:L1Cache); + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Persistent_Control; + } + } + + // Update average latency + updateAverageLatencyEstimate(time_to_int(get_time()) - time_to_int(TBEs[address].IssueTime)); + + // Profile + profile_token_retry(address, TBEs[address].AccessType, TBEs[address].IssueCount); + TBEs.deallocate(address); + } + + action(t_sendAckWithCollectedTokens, "t", desc="Send ack with the tokens we've collected thus far.") { + if (getCacheEntry(address).Tokens > 0) { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + assert(getCacheEntry(address).Tokens >= 1); + out_msg.Tokens := getCacheEntry(address).Tokens; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + } + getCacheEntry(address).Tokens := 0; + } + + action(u_writeDataToCache, "u", desc="Write data to cache") { + peek(responseNetwork_in, ResponseMsg) { + getCacheEntry(address).DataBlk := in_msg.DataBlk; + getCacheEntry(address).Dirty := in_msg.Dirty; + } + } + + action(gg_deallocateL1CacheBlock, "\g", desc="Deallocate cache block. Sets the cache to invalid, allowing a replacement in parallel with a fetch.") { + if (L1DcacheMemory.isTagPresent(address)) { + L1DcacheMemory.deallocate(address); + } else { + L1IcacheMemory.deallocate(address); + } + } + + action(ii_allocateL1DCacheBlock, "\i", desc="Set L1 D-cache tag equal to tag of block B.") { + if (L1DcacheMemory.isTagPresent(address) == false) { + L1DcacheMemory.allocate(address); + } + } + + action(pp_allocateL1ICacheBlock, "\p", desc="Set L1 I-cache tag equal to tag of block B.") { + if (L1IcacheMemory.isTagPresent(address) == false) { + L1IcacheMemory.allocate(address); + } + } + + action(vv_allocateL2CacheBlock, "\v", desc="Set L2 cache tag equal to tag of block B.") { + L2cacheMemory.allocate(address); + } + + action(rr_deallocateL2CacheBlock, "\r", desc="Deallocate L2 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + L2cacheMemory.deallocate(address); + } + + action(ss_copyFromL1toL2, "\s", desc="Copy data block from L1 (I or D) to L2") { + if (L1DcacheMemory.isTagPresent(address)) { + L2cacheMemory[address] := L1DcacheMemory[address]; + } else { + L2cacheMemory[address] := L1IcacheMemory[address]; + } + } + + action(tt_copyFromL2toL1, "\t", desc="Copy data block from L2 to L1 (I or D)") { + if (L1DcacheMemory.isTagPresent(address)) { + L1DcacheMemory[address] := L2cacheMemory[address]; + } else { + L1IcacheMemory[address] := L2cacheMemory[address]; + } + } + + action(uu_profileMiss, "\u", desc="Profile the demand miss") { + peek(mandatoryQueue_in, CacheMsg) { + profile_miss(in_msg, id); + } + } + + action(w_assertIncomingDataAndCacheDataMatch, "w", desc="Assert that the incoming data and the data in the cache match") { + peek(responseNetwork_in, ResponseMsg) { + assert(getCacheEntry(address).DataBlk == in_msg.DataBlk); + } + } + + // action(z_stall, "z", desc="Stall") { + // } + + action(zz_recycleMandatoryQueue, "\z", desc="Send the head of the mandatory queue to the back of the queue.") { + mandatoryQueue_in.recycle(); + } + + //***************************************************** + // TRANSITIONS + //***************************************************** + + // Transitions for Load/Store/L2_Replacement from transient states + transition({IM, SM, OM, IS, IM_L, IS_L, I_L, S_L, SM_L, M_W, MM_W}, L2_Replacement) { + zz_recycleMandatoryQueue; + } + + transition({IM, SM, OM, IS, IM_L, IS_L, SM_L}, Store) { + zz_recycleMandatoryQueue; + } + + transition({IM, IS, IM_L, IS_L}, {Load, Ifetch}) { + zz_recycleMandatoryQueue; + } + + transition({IM, SM, OM, IS, I_L, IM_L, IS_L, S_L, SM_L}, {L1_to_L2, L2_to_L1D, L2_to_L1I}) { + zz_recycleMandatoryQueue; + } + + // Transitions moving data between the L1 and L2 caches + transition({I, S, O, M, MM, M_W, MM_W}, L1_to_L2) { + vv_allocateL2CacheBlock; + ss_copyFromL1toL2; + gg_deallocateL1CacheBlock; + } + + transition({I, S, O, M, MM, M_W, MM_W}, L2_to_L1D) { + ii_allocateL1DCacheBlock; + tt_copyFromL2toL1; + rr_deallocateL2CacheBlock; + } + + transition({I, S, O, M, MM, M_W, MM_W}, L2_to_L1I) { + pp_allocateL1ICacheBlock; + tt_copyFromL2toL1; + rr_deallocateL2CacheBlock; + } + + // Locks + transition({NP, I, S, O, M, MM, M_W, MM_W, IM, SM, OM, IS}, Own_Lock_or_Unlock) { + l_popPersistentQueue; + } + + // Transitions from NP + transition(NP, Load, IS) { + ii_allocateL1DCacheBlock; + i_allocateTBE; + a_issueRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(NP, Ifetch, IS) { + pp_allocateL1ICacheBlock; + i_allocateTBE; + a_issueRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(NP, Store, IM) { + ii_allocateL1DCacheBlock; + i_allocateTBE; + a_issueRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(NP, {Ack, Data_Shared, Data_Owner, Data_Owner_All_Tokens}) { + b_bounceResponse; + n_popResponseQueue; + } + + transition(NP, {Transient_GETX, Transient_GETS}) { + m_popRequestQueue; + } + + transition(NP, {Persistent_GETX, Persistent_GETS}, I_L) { + l_popPersistentQueue; + } + + // Transitions from Idle + transition(I, Load, IS) { + i_allocateTBE; + a_issueRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I, Ifetch, IS) { + i_allocateTBE; + a_issueRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I, Store, IM) { + i_allocateTBE; + a_issueRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I, L2_Replacement) { + c_cleanReplacement; // Only needed in some cases + rr_deallocateL2CacheBlock; + } + + transition(I, Transient_GETX) { + t_sendAckWithCollectedTokens; + m_popRequestQueue; + } + + transition(I, Transient_GETS) { + m_popRequestQueue; + } + + transition(I, {Persistent_GETX, Persistent_GETS}, I_L) { + e_sendAckWithCollectedTokens; + l_popPersistentQueue; + } + + transition(I_L, {Persistent_GETX, Persistent_GETS}) { + l_popPersistentQueue; + } + + transition(I, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(I, Data_Shared, S) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(I, Data_Owner, O) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(I, Data_Owner_All_Tokens, M) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + // Transitions from Shared + transition({S, SM, S_L, SM_L}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(S, Store, SM) { + i_allocateTBE; + a_issueRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(S, L2_Replacement, I) { + c_cleanReplacement; + rr_deallocateL2CacheBlock; + } + + transition(S, Transient_GETX, I) { + t_sendAckWithCollectedTokens; + m_popRequestQueue; + } + + transition(S, Transient_GETS) { + m_popRequestQueue; + } + + transition({S, S_L}, Persistent_GETX, I_L) { + e_sendAckWithCollectedTokens; + l_popPersistentQueue; + } + + transition(S, Persistent_GETS, S_L) { + f_sendAckWithAllButOneTokens; + l_popPersistentQueue; + } + + transition(S_L, Persistent_GETS) { + l_popPersistentQueue; + } + + transition(S, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(S, Data_Shared) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(S, Data_Owner, O) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(S, Data_Owner_All_Tokens, M) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + // Transitions from Owned + transition({O, OM}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(O, Store, OM) { + i_allocateTBE; + a_issueRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(O, L2_Replacement, I) { + cc_dirtyReplacement; + rr_deallocateL2CacheBlock; + } + + transition(O, Transient_GETX, I) { + dd_sendDataWithAllTokens; + m_popRequestQueue; + } + + transition(O, Persistent_GETX, I_L) { + ee_sendDataWithAllTokens; + l_popPersistentQueue; + } + + transition(O, Persistent_GETS, S_L) { + ff_sendDataWithAllButOneTokens; + l_popPersistentQueue; + } + + transition(O, Transient_GETS) { + d_sendDataWithToken; + m_popRequestQueue; + } + + transition(O, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(O, Ack_All_Tokens, M) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(O, Data_Shared) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(O, Data_Shared_All_Tokens, M) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + // Transitions from Modified + transition({MM, MM_W}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition({MM, MM_W}, Store) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(MM, L2_Replacement, I) { + cc_dirtyReplacement; + rr_deallocateL2CacheBlock; + } + + transition(MM, {Transient_GETX, Transient_GETS}, I) { + dd_sendDataWithAllTokens; + m_popRequestQueue; + } + + transition(MM_W, {Transient_GETX, Transient_GETS}) { // Ignore the request + m_popRequestQueue; + } + + // Implement the migratory sharing optimization, even for persistent requests + transition(MM, {Persistent_GETX, Persistent_GETS}, I_L) { + ee_sendDataWithAllTokens; + l_popPersistentQueue; + } + + // Implement the migratory sharing optimization, even for persistent requests + transition(MM_W, {Persistent_GETX, Persistent_GETS}, I_L) { + s_deallocateTBE; + ee_sendDataWithAllTokens; + jj_unsetUseTimer; + l_popPersistentQueue; + } + + transition(MM_W, Use_Timeout, MM) { + s_deallocateTBE; + jj_unsetUseTimer; + } + + // Transitions from Dirty Exclusive + transition({M, M_W}, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(M, Store, MM) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(M_W, Store, MM_W) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(M, L2_Replacement, I) { + cc_dirtyReplacement; + rr_deallocateL2CacheBlock; + } + + transition(M, Transient_GETX, I) { + dd_sendDataWithAllTokens; + m_popRequestQueue; + } + + transition(M, Transient_GETS, O) { + d_sendDataWithToken; + m_popRequestQueue; + } + + transition(M_W,{Transient_GETX, Transient_GETS}) { // Ignore the request + m_popRequestQueue; + } + + transition(M, Persistent_GETX, I_L) { + ee_sendDataWithAllTokens; + l_popPersistentQueue; + } + + transition(M, Persistent_GETS, S_L) { + ff_sendDataWithAllButOneTokens; + l_popPersistentQueue; + } + + transition(M_W, Persistent_GETX, I_L) { + s_deallocateTBE; + ee_sendDataWithAllTokens; + jj_unsetUseTimer; + l_popPersistentQueue; + } + + transition(M_W, Persistent_GETS, S_L) { + s_deallocateTBE; + ff_sendDataWithAllButOneTokens; + jj_unsetUseTimer; + l_popPersistentQueue; + } + + transition(M_W, Use_Timeout, M) { + s_deallocateTBE; + jj_unsetUseTimer; + } + + // Transient_GETX and Transient_GETS in transient states + transition(OM, {Transient_GETX, Transient_GETS}) { + m_popRequestQueue; // Even if we have the data, we can pretend we don't have it yet. + } + + transition(IS, Transient_GETX) { + t_sendAckWithCollectedTokens; + m_popRequestQueue; + } + + transition(IS, Transient_GETS) { + m_popRequestQueue; + } + + transition(IS, {Persistent_GETX, Persistent_GETS}, IS_L) { + e_sendAckWithCollectedTokens; + l_popPersistentQueue; + } + + transition(IS_L, {Persistent_GETX, Persistent_GETS}) { + l_popPersistentQueue; + } + + transition(IM, {Persistent_GETX, Persistent_GETS}, IM_L) { + e_sendAckWithCollectedTokens; + l_popPersistentQueue; + } + + transition(IM_L, {Persistent_GETX, Persistent_GETS}) { + l_popPersistentQueue; + } + + transition({SM, SM_L}, Persistent_GETX, IM_L) { + e_sendAckWithCollectedTokens; + l_popPersistentQueue; + } + + transition(SM, Persistent_GETS, SM_L) { + f_sendAckWithAllButOneTokens; + l_popPersistentQueue; + } + + transition(SM_L, Persistent_GETS) { + l_popPersistentQueue; + } + + transition(OM, Persistent_GETX, IM_L) { + ee_sendDataWithAllTokens; + l_popPersistentQueue; + } + + transition(OM, Persistent_GETS, SM_L) { + ff_sendDataWithAllButOneTokens; + l_popPersistentQueue; + } + + // Transitions from IM/SM + + transition({IM, SM}, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(IM, Data_Shared, SM) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(IM, Data_Owner, OM) { + u_writeDataToCache; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(IM, Data_Owner_All_Tokens, MM_W) { + u_writeDataToCache; + q_updateTokensFromResponse; + hh_store_hit; + o_scheduleUseTimeout; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(SM, Data_Shared) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(SM, Data_Owner, OM) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(SM, Data_Owner_All_Tokens, MM_W) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + hh_store_hit; + o_scheduleUseTimeout; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition({IM, SM}, Transient_GETX, IM) { + t_sendAckWithCollectedTokens; + m_popRequestQueue; + } + + transition({IM, SM}, Transient_GETS) { + m_popRequestQueue; + } + + transition({IM, SM}, Request_Timeout) { + j_unsetReissueTimer; + a_issueRequest; + } + + // Transitions from OM + + transition(OM, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(OM, Ack_All_Tokens, MM_W) { + q_updateTokensFromResponse; + hh_store_hit; + o_scheduleUseTimeout; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(OM, Data_Shared) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(OM, Data_Shared_All_Tokens, MM_W) { + w_assertIncomingDataAndCacheDataMatch; + q_updateTokensFromResponse; + hh_store_hit; + o_scheduleUseTimeout; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(OM, Request_Timeout) { + j_unsetReissueTimer; + a_issueRequest; + } + + // Transitions from IS + + transition(IS, Ack) { + q_updateTokensFromResponse; + n_popResponseQueue; + } + + transition(IS, Data_Shared, S) { + u_writeDataToCache; + q_updateTokensFromResponse; + h_load_hit; + s_deallocateTBE; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(IS, Data_Owner, O) { + u_writeDataToCache; + q_updateTokensFromResponse; + h_load_hit; + s_deallocateTBE; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(IS, Data_Owner_All_Tokens, M_W) { + u_writeDataToCache; + q_updateTokensFromResponse; + h_load_hit; + o_scheduleUseTimeout; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(IS, Request_Timeout) { + j_unsetReissueTimer; + a_issueRequest; + } + + // Transitions from I_L + + transition(I_L, Load, IS_L) { + ii_allocateL1DCacheBlock; + i_allocateTBE; + a_issueRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I_L, Ifetch, IS_L) { + pp_allocateL1ICacheBlock; + i_allocateTBE; + a_issueRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + transition(I_L, Store, IM_L) { + ii_allocateL1DCacheBlock; + i_allocateTBE; + a_issueRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + + // Transitions from S_L + + transition(S_L, Store, SM_L) { + i_allocateTBE; + a_issueRequest; + uu_profileMiss; + k_popMandatoryQueue; + } + + // Other transitions from *_L states + + transition({I_L, IM_L, IS_L, S_L, SM_L}, {Transient_GETS, Transient_GETX}) { + m_popRequestQueue; + } + + transition({I_L, IM_L, IS_L, S_L, SM_L}, Ack) { + g_bounceResponseToStarver; + n_popResponseQueue; + } + + transition({I_L, IM_L, S_L, SM_L}, {Data_Shared, Data_Owner}) { + g_bounceResponseToStarver; + n_popResponseQueue; + } + + transition({I_L, S_L}, Data_Owner_All_Tokens) { + g_bounceResponseToStarver; + n_popResponseQueue; + } + + transition(IS_L, Request_Timeout) { + j_unsetReissueTimer; + a_issueRequest; + } + + transition({IM_L, SM_L}, Request_Timeout) { + j_unsetReissueTimer; + a_issueRequest; + } + + // Opportunisticly Complete the memory operation in the following + // cases. Note: these transitions could just use + // g_bounceResponseToStarver, but if we have the data and tokens, we + // might as well complete the memory request while we have the + // chance (and then immediately forward on the data) + + transition(IM_L, Data_Owner_All_Tokens, I_L) { + u_writeDataToCache; + q_updateTokensFromResponse; + hh_store_hit; + ee_sendDataWithAllTokens; + s_deallocateTBE; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(SM_L, Data_Owner_All_Tokens, S_L) { + u_writeDataToCache; + q_updateTokensFromResponse; + hh_store_hit; + ff_sendDataWithAllButOneTokens; + s_deallocateTBE; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(IS_L, Data_Shared, I_L) { + u_writeDataToCache; + q_updateTokensFromResponse; + h_load_hit; + s_deallocateTBE; + e_sendAckWithCollectedTokens; + j_unsetReissueTimer; + j_unsetReissueTimer; + n_popResponseQueue; + } + + transition(IS_L, {Data_Owner, Data_Owner_All_Tokens}, I_L) { + u_writeDataToCache; + q_updateTokensFromResponse; + h_load_hit; + ee_sendDataWithAllTokens; + s_deallocateTBE; + j_unsetReissueTimer; + n_popResponseQueue; + } + + // Own_Lock_or_Unlock + + transition(I_L, Own_Lock_or_Unlock, I) { + l_popPersistentQueue; + } + + transition(S_L, Own_Lock_or_Unlock, S) { + l_popPersistentQueue; + } + + transition(IM_L, Own_Lock_or_Unlock, IM) { + l_popPersistentQueue; + } + + transition(IS_L, Own_Lock_or_Unlock, IS) { + l_popPersistentQueue; + } + + transition(SM_L, Own_Lock_or_Unlock, SM) { + l_popPersistentQueue; + } +} diff --git a/src/mem/protocol/MOESI_SMP_token-dir.sm b/src/mem/protocol/MOESI_SMP_token-dir.sm new file mode 100644 index 000000000..5464a25fc --- /dev/null +++ b/src/mem/protocol/MOESI_SMP_token-dir.sm @@ -0,0 +1,405 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOESI_token-dir.sm 1.5 04/11/17 14:07:50-06:00 mikem@emperor15.cs.wisc.edu $ + */ + +machine(Directory, "Token protocol") { + + MessageBuffer responseFromDir, network="To", virtual_network="0", ordered="false"; + + MessageBuffer responseToDir, network="From", virtual_network="0", ordered="false"; + MessageBuffer requestToDir, network="From", virtual_network="1", ordered="false"; + MessageBuffer persistentToDir, network="From", virtual_network="2", ordered="true"; + + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_O") { + // Base states + O, desc="Owner"; + NO, desc="Not Owner"; + L, desc="Locked"; + } + + // Events + enumeration(Event, desc="Directory events") { + GETX, desc="A GETX arrives"; + GETS, desc="A GETS arrives"; + Lockdown, desc="A lockdown request arrives"; + Unlockdown, desc="An un-lockdown request arrives"; + Data_Owner, desc="Data arrive, includes the owner token"; + Data_Shared, desc="Data arrive, does not include the owner token"; + Ack, desc="Tokens arrive"; + Ack_Owner, desc="Tokens arrive, including the owner token"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + State DirectoryState, desc="Directory state"; + DataBlock DataBlk, desc="data for the block"; + int Tokens, default="max_tokens()", desc="Number of tokens for the line we're holding"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + + PersistentTable persistentTable, constructor_hack="i"; + + State getState(Address addr) { + return directory[addr].DirectoryState; + } + + void setState(Address addr, State state) { + directory[addr].DirectoryState := state; + + if (state == State:L) { + assert(directory[addr].Tokens == 0); + } + + // Make sure the token count is in range + assert(directory[addr].Tokens >= 0); + assert(directory[addr].Tokens <= max_tokens()); + + if (state == State:O) { + assert(directory[addr].Tokens >= 1); // Must have at least one token + assert(directory[addr].Tokens >= (max_tokens() / 2)); // Only mostly true; this might not always hold + } + } + + // ** OUT_PORTS ** + out_port(responseNetwork_out, ResponseMsg, responseFromDir); + + // ** IN_PORTS ** + + in_port(persistentNetwork_in, PersistentMsg, persistentToDir) { + if (persistentNetwork_in.isReady()) { + peek(persistentNetwork_in, PersistentMsg) { + + // Apply the lockdown or unlockdown message to the table + if (in_msg.Type == PersistentRequestType:GETX_PERSISTENT) { + persistentTable.persistentRequestLock(in_msg.Address, in_msg.Requestor, AccessType:Write); + } else if (in_msg.Type == PersistentRequestType:GETS_PERSISTENT) { + persistentTable.persistentRequestLock(in_msg.Address, in_msg.Requestor, AccessType:Read); + } else if (in_msg.Type == PersistentRequestType:DEACTIVATE_PERSISTENT) { + persistentTable.persistentRequestUnlock(in_msg.Address, in_msg.Requestor); + } else { + error("Invalid message"); + } + + // React to the message based on the current state of the table + if (persistentTable.isLocked(in_msg.Address)) { + trigger(Event:Lockdown, in_msg.Address); // locked + } else { + trigger(Event:Unlockdown, in_msg.Address); // unlocked + } + } + } + } + + in_port(requestNetwork_in, RequestMsg, requestToDir) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:GETX, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + in_port(responseNetwork_in, ResponseMsg, responseToDir) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + if (in_msg.Type == CoherenceResponseType:DATA_OWNER) { + trigger(Event:Data_Owner, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:ACK) { + trigger(Event:Ack, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:DATA_SHARED) { + trigger(Event:Data_Shared, in_msg.Address); + } else if (in_msg.Type == CoherenceResponseType:ACK_OWNER) { + trigger(Event:Ack_Owner, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + + // Actions + + action(a_sendTokens, "a", desc="Send tokens to requestor") { + // Only send a message if we have tokens to send + if (directory[address].Tokens > 0) { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + out_msg.Tokens := directory[in_msg.Address].Tokens; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + } + directory[address].Tokens := 0; + } + } + + action(aa_sendTokensToStarver, "\a", desc="Send tokens to starver") { + // Only send a message if we have tokens to send + if (directory[address].Tokens > 0) { + enqueue(responseNetwork_out, ResponseMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.DestMachine := MachineType:L1Cache; + out_msg.Tokens := directory[address].Tokens; + out_msg.MessageSize := MessageSizeType:Response_Control; + } + directory[address].Tokens := 0; + } + } + + + action(d_sendDataWithAllTokens, "d", desc="Send data and tokens to requestor") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + assert(directory[address].Tokens > 0); + out_msg.Tokens := directory[in_msg.Address].Tokens; + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + directory[address].Tokens := 0; + } + + action(dd_sendDataWithAllTokensToStarver, "\d", desc="Send data and tokens to starver") { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.DestMachine := MachineType:L1Cache; + assert(directory[address].Tokens > 0); + out_msg.Tokens := directory[address].Tokens; + out_msg.DataBlk := directory[address].DataBlk; + out_msg.Dirty := false; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + directory[address].Tokens := 0; + } + + action(f_incrementTokens, "f", desc="Increment the number of tokens we're tracking") { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Tokens >= 1); + directory[address].Tokens := directory[address].Tokens + in_msg.Tokens; + } + } + + + action(j_popIncomingRequestQueue, "j", desc="Pop incoming request queue") { + requestNetwork_in.dequeue(); + } + + action(k_popIncomingResponseQueue, "k", desc="Pop incoming response queue") { + responseNetwork_in.dequeue(); + } + + action(l_popIncomingPersistentQueue, "l", desc="Pop incoming persistent queue") { + persistentNetwork_in.dequeue(); + } + + action(m_writeDataToMemory, "m", desc="Write dirty writeback to memory") { + peek(responseNetwork_in, ResponseMsg) { + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + action(n_checkIncomingMsg, "n", desc="Check incoming token message") { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Type == CoherenceResponseType:ACK_OWNER); + assert(in_msg.Dirty == false); + assert(in_msg.MessageSize == MessageSizeType:Writeback_Control); + assert(directory[in_msg.Address].DataBlk == in_msg.DataBlk); + } + } + + action(r_bounceResponse, "r", desc="Bounce response to starving processor") { + peek(responseNetwork_in, ResponseMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.DestMachine := MachineType:L1Cache; + out_msg.Tokens := in_msg.Tokens; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.Dirty := in_msg.Dirty; + out_msg.MessageSize := in_msg.MessageSize; + } + } + } + + action(s_bounceDatalessOwnerToken, "s", desc="Bounce clean owner token to starving processor") { + peek(responseNetwork_in, ResponseMsg) { + assert(in_msg.Type == CoherenceResponseType:ACK_OWNER); + assert(in_msg.Dirty == false); + assert(in_msg.MessageSize == MessageSizeType:Writeback_Control); + + // NOTE: The following check would not be valid in a real + // implementation. We include the data in the "dataless" + // message so we can assert the clean data matches the datablock + // in memory + assert(directory[in_msg.Address].DataBlk == in_msg.DataBlk); + + // Bounce the message, but "re-associate" the data and the owner + // token. In essence we're converting an ACK_OWNER message to a + // DATA_OWNER message, keeping the number of tokens the same. + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_OWNER; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + out_msg.Destination.add(persistentTable.findSmallest(address)); + out_msg.DestMachine := MachineType:L1Cache; + out_msg.Tokens := in_msg.Tokens; + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.Dirty := in_msg.Dirty; + out_msg.MessageSize := MessageSizeType:Response_Data; + } + } + } + + // TRANSITIONS + + // Trans. from O + transition(O, GETX, NO) { + d_sendDataWithAllTokens; + j_popIncomingRequestQueue; + } + + transition(O, GETS, NO) { + d_sendDataWithAllTokens; + // Since we found the owner, no need to forward + j_popIncomingRequestQueue; + } + + transition(O, Lockdown, L) { + dd_sendDataWithAllTokensToStarver; + l_popIncomingPersistentQueue; + } + + transition(O, {Data_Shared, Ack}) { + f_incrementTokens; + k_popIncomingResponseQueue; + } + + // Trans. from NO + transition(NO, GETX) { + a_sendTokens; + j_popIncomingRequestQueue; + } + + transition(NO, GETS) { + j_popIncomingRequestQueue; + } + + transition(NO, Lockdown, L) { + aa_sendTokensToStarver; + l_popIncomingPersistentQueue; + } + + transition(NO, Data_Owner, O) { + m_writeDataToMemory; + f_incrementTokens; + k_popIncomingResponseQueue; + } + + transition(NO, Ack_Owner, O) { + n_checkIncomingMsg; + f_incrementTokens; + k_popIncomingResponseQueue; + } + + transition(NO, {Data_Shared, Ack}) { + f_incrementTokens; + k_popIncomingResponseQueue; + } + + // Trans. from L + transition(L, {GETX, GETS}) { + j_popIncomingRequestQueue; + } + + transition(L, Lockdown) { + l_popIncomingPersistentQueue; + } + + transition(L, {Data_Owner, Data_Shared, Ack}) { + r_bounceResponse; + k_popIncomingResponseQueue; + } + + transition(L, Ack_Owner) { + s_bounceDatalessOwnerToken; + k_popIncomingResponseQueue; + } + + transition(L, Unlockdown, NO) { + l_popIncomingPersistentQueue; + } + +} diff --git a/src/mem/protocol/MOESI_SMP_token-msg.sm b/src/mem/protocol/MOESI_SMP_token-msg.sm new file mode 100644 index 000000000..98e27ec02 --- /dev/null +++ b/src/mem/protocol/MOESI_SMP_token-msg.sm @@ -0,0 +1,61 @@ +/* + * $Id: MOESI_token-msg.sm 1.3 04/06/05 22:43:20-00:00 kmoore@cottons.cs.wisc.edu $ + * + */ + +//int max_tokens(); + +// CoherenceRequestType +enumeration(CoherenceRequestType, desc="...") { + GETX, desc="Get eXclusive"; + GETS, desc="Get Shared"; +} + +// StarvationType +enumeration(PersistentRequestType, desc="...") { + GETX_PERSISTENT, desc="..."; + GETS_PERSISTENT, desc="..."; + DEACTIVATE_PERSISTENT, desc="..."; +} + +// CoherenceResponseType +enumeration(CoherenceResponseType, desc="...") { + DATA_OWNER, desc="Data, with the owner token"; + DATA_SHARED, desc="Data, without the owner token"; + ACK, desc="ACKnowledgment"; + ACK_OWNER, desc="ACKnowledgment, includes the clean owner token"; +} + +// StarvationMsg +structure(PersistentMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + PersistentRequestType Type, desc="Type of starvation request"; + MachineID Requestor, desc="Node who initiated the request"; + NetDest Destination, desc="Destination set"; + MessageSizeType MessageSize, desc="size category of the message"; +} + +// RequestMsg +structure(RequestMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)"; + MachineID Requestor, desc="Node who initiated the request"; + NetDest Destination, desc="Multicast destination mask"; + MachineType DestMachine, desc="What component receives the data"; + MessageSizeType MessageSize, desc="size category of the message"; +} + +// ResponseMsg +structure(ResponseMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)"; + MachineID Sender, desc="Node who sent the data"; + MachineType SenderMachine, desc="What component sent the data"; + NetDest Destination, desc="Node to whom the data is sent"; + MachineType DestMachine, desc="What component receives the data"; + int Tokens, desc="Number of tokens being transfered for this line"; + DataBlock DataBlk, desc="data for the cache line"; + bool Dirty, desc="Is the data dirty (different than memory)?"; + MessageSizeType MessageSize, desc="size category of the message"; +} + diff --git a/src/mem/protocol/MOESI_SMP_token.slicc b/src/mem/protocol/MOESI_SMP_token.slicc new file mode 100644 index 000000000..31822f997 --- /dev/null +++ b/src/mem/protocol/MOESI_SMP_token.slicc @@ -0,0 +1,4 @@ +MOESI_SMP_token-msg.sm +MOESI_SMP_token-cache.sm +MOESI_SMP_token-dir.sm +standard_SMP-protocol.sm diff --git a/src/mem/protocol/MOSI_SMP_bcast-cache.sm b/src/mem/protocol/MOSI_SMP_bcast-cache.sm new file mode 100644 index 000000000..6512b435a --- /dev/null +++ b/src/mem/protocol/MOSI_SMP_bcast-cache.sm @@ -0,0 +1,1000 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +machine(L1Cache, "MOSI Broadcast Optimized") { + + MessageBuffer addressFromCache, network="To", virtual_network="0", ordered="true"; + MessageBuffer dataFromCache, network="To", virtual_network="1", ordered="false"; + + MessageBuffer addressToCache, network="From", virtual_network="0", ordered="true"; + MessageBuffer dataToCache, network="From", virtual_network="1", ordered="false"; + + // STATES + + enumeration(State, desc="Cache states", default="L1Cache_State_I") { + NP, desc="Not Present"; + I, desc="Idle"; + S, desc="Shared"; + O, desc="Owned"; + M, desc="Modified", format="!b"; + IS_AD, "IS^AD", desc="idle, issued GETS, have not seen GETS or data yet"; + IM_AD, "IM^AD", desc="idle, issued GETX, have not seen GETX or data yet"; + SM_AD, "SM^AD",desc="shared, issued GETX, have not seen GETX or data yet"; + OM_A, "OM^A",desc="owned, issued GETX, have not seen GETX yet", format="!b"; + + IS_A, "IS^A",desc="idle, issued GETS, have not seen GETS, have seen data"; + IM_A, "IM^A",desc="idle, issued GETX, have not seen GETX, have seen data"; + SM_A, "SM^A",desc="shared, issued GETX, have not seen GETX, have seen data", format="!b"; + + MI_A, "MI^A", desc="modified, issued PUTX, have not seen PUTX yet"; + OI_A, "OI^A", desc="owned, issued PUTX, have not seen PUTX yet"; + II_A, "II^A", desc="modified, issued PUTX, have not seen PUTX, then saw other GETX", format="!b"; + + IS_D, "IS^D", desc="idle, issued GETS, have seen GETS, have not seen data yet"; + IS_D_I, "IS^D^I", desc="idle, issued GETS, have seen GETS, have not seen data, then saw other GETX"; + IM_D, "IM^D", desc="idle, issued GETX, have seen GETX, have not seen data yet"; + IM_D_O, "IM^D^O", desc="idle, issued GETX, have seen GETX, have not seen data yet, then saw other GETS"; + IM_D_I, "IM^D^I", desc="idle, issued GETX, have seen GETX, have not seen data yet, then saw other GETX"; + IM_D_OI, "IM^D^OI", desc="idle, issued GETX, have seen GETX, have not seen data yet, then saw other GETS, then saw other GETX"; + SM_D, "SM^D", desc="shared, issued GETX, have seen GETX, have not seen data yet"; + SM_D_O, "SM^D^O", desc="shared, issued GETX, have seen GETX, have not seen data yet, then saw other GETS"; + } + + // ** EVENTS ** + + enumeration(Event, desc="Cache events") { + // From processor + Load, desc="Load request from the processor"; + Ifetch, desc="I-fetch request from the processor"; + Store, desc="Store request from the processor"; + L1_to_L2, desc="L1 to L2 transfer"; + L2_to_L1D, desc="L2 to L1-Data transfer"; + L2_to_L1I, desc="L2 to L1-Instruction transfer"; + L2_Replacement, desc="L2 Replacement"; + + // From Address network + Own_GETS, desc="Occurs when we observe our own GETS request in the global order"; + Own_GET_INSTR, desc="Occurs when we observe our own GETInstr request in the global order"; + Own_GETX, desc="Occurs when we observe our own GETX request in the global order"; + Own_PUTX, desc="Occurs when we observe our own PUTX request in the global order", format="!r"; + Other_GETS, desc="Occurs when we observe a GETS request from another processor"; + Other_GET_INSTR, desc="Occurs when we observe a GETInstr request from another processor"; + Other_GETX, desc="Occurs when we observe a GETX request from another processor"; + Other_PUTX, desc="Occurs when we observe a PUTX request from another processor", format="!r"; + + // From Data network + Data, desc="Data for this block from the data network"; + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + DataBlock DataBlk, desc="data for the block"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="Buffer for the data block"; + NetDest ForwardIDs, desc="IDs of the processors to forward the block"; + Address ForwardAddress, desc="Address of request for forwarding"; + } + + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + TBETable TBEs, template_hack=""; + CacheMemory L1IcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1I"', abstract_chip_ptr="true"; + CacheMemory L1DcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1D"', abstract_chip_ptr="true"; + CacheMemory L2cacheMemory, template_hack="", constructor_hack='L2_CACHE_NUM_SETS_BITS,L2_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L2"', abstract_chip_ptr="true"; + + MessageBuffer mandatoryQueue, ordered="false", abstract_chip_ptr="true"; + Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i"; + StoreBuffer storeBuffer, abstract_chip_ptr="true", constructor_hack="i"; + + int cache_state_to_int(State state); + + Entry getCacheEntry(Address addr), return_by_ref="yes" { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory[addr]; + } else if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory[addr]; + } else { + return L1IcacheMemory[addr]; + } + } + + void changePermission(Address addr, AccessPermission permission) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory.changePermission(addr, permission); + } else if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory.changePermission(addr, permission); + } else { + return L1IcacheMemory.changePermission(addr, permission); + } + } + + bool isCacheTagPresent(Address addr) { + return (L2cacheMemory.isTagPresent(addr) || L1DcacheMemory.isTagPresent(addr) || L1IcacheMemory.isTagPresent(addr)); + } + + State getState(Address addr) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + assert((L1IcacheMemory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + assert((L1DcacheMemory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + + if(TBEs.isPresent(addr)) { + return TBEs[addr].TBEState; + } else if (isCacheTagPresent(addr)) { + return getCacheEntry(addr).CacheState; + } + return State:NP; + } + + void setState(Address addr, State state) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + assert((L1IcacheMemory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + assert((L1DcacheMemory.isTagPresent(addr) && L2cacheMemory.isTagPresent(addr)) == false); + + if (TBEs.isPresent(addr)) { + TBEs[addr].TBEState := state; + } + + if (isCacheTagPresent(addr)) { + getCacheEntry(addr).CacheState := state; + + // Set permission + if ((state == State:I) || (state == State:MI_A) || (state == State:II_A)) { + changePermission(addr, AccessPermission:Invalid); + } else if (state == State:S || state == State:O) { + changePermission(addr, AccessPermission:Read_Only); + } else if (state == State:M) { + changePermission(addr, AccessPermission:Read_Write); + } else { + changePermission(addr, AccessPermission:Busy); + } + } + } + + Event mandatory_request_type_to_event(CacheRequestType type) { + if (type == CacheRequestType:LD) { + return Event:Load; + } else if (type == CacheRequestType:IFETCH) { + return Event:Ifetch; + } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { + return Event:Store; + } else { + error("Invalid CacheRequestType"); + } + } + + // ** OUT_PORTS ** + + out_port(dataNetwork_out, DataMsg, dataFromCache); + out_port(addressNetwork_out, AddressMsg, addressFromCache); + + // ** IN_PORTS ** + + // Data Network + in_port(dataNetwork_in, DataMsg, dataToCache) { + if (dataNetwork_in.isReady()) { + peek(dataNetwork_in, DataMsg) { + trigger(Event:Data, in_msg.Address); + } + } + } + + // Address Network + in_port(addressNetwork_in, AddressMsg, addressToCache) { + if (addressNetwork_in.isReady()) { + peek(addressNetwork_in, AddressMsg) { + if (in_msg.Type == CoherenceRequestType:GETS) { + if (in_msg.Requestor == machineID) { + trigger(Event:Own_GETS, in_msg.Address); + } else { + trigger(Event:Other_GETS, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:GETX) { + if (in_msg.Requestor == machineID) { + trigger(Event:Own_GETX, in_msg.Address); + } else { + trigger(Event:Other_GETX, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:GET_INSTR) { + if (in_msg.Requestor == machineID) { + trigger(Event:Own_GET_INSTR, in_msg.Address); + } else { + trigger(Event:Other_GET_INSTR, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:PUTX) { + if (in_msg.Requestor == machineID) { + trigger(Event:Own_PUTX, in_msg.Address); + } else { + trigger(Event:Other_PUTX, in_msg.Address); + } + } else { + error("Unexpected message"); + } + } + } + } + + // Mandatory Queue + in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { + if (mandatoryQueue_in.isReady()) { + peek(mandatoryQueue_in, CacheMsg) { + + // Check for data access to blocks in I-cache and ifetchs to blocks in D-cache + + if (in_msg.Type == CacheRequestType:IFETCH) { + // ** INSTRUCTION ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, try to write it to the L2 + if (L2cacheMemory.cacheAvail(in_msg.Address)) { + trigger(Event:L1_to_L2, in_msg.Address); + } else { + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } else if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 fetches the line. We know it can't be in the L2 due to exclusion + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // not in any L1 + if (L1IcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 + if (L2cacheMemory.isTagPresent(in_msg.Address)) { + // L2 has it (maybe not with the right permissions) + trigger(Event:L2_to_L1I, in_msg.Address); + } else { + // We have room, the L2 doesn't have it, so the L1 fetches the line + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } + } else { + // No room in the L1, so we need to make room + if (L2cacheMemory.cacheAvail(L1IcacheMemory.cacheProbe(in_msg.Address))) { + // The L2 has room, so we move the line from the L1 to the L2 + trigger(Event:L1_to_L2, L1IcacheMemory.cacheProbe(in_msg.Address)); + } else { + // The L2 does not have room, so we replace a line from the L2 + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(L1IcacheMemory.cacheProbe(in_msg.Address))); + } + } + } + } else { + // *** DATA ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, try to write it to the L2 + if (L2cacheMemory.cacheAvail(in_msg.Address)) { + trigger(Event:L1_to_L2, in_msg.Address); + } else { + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } else if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 fetches the line. We know it can't be in the L2 due to exclusion + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // not in any L1 + if (L1DcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 + if (L2cacheMemory.isTagPresent(in_msg.Address)) { + // L2 has it (maybe not with the right permissions) + trigger(Event:L2_to_L1D, in_msg.Address); + } else { + // We have room, the L2 doesn't have it, so the L1 fetches the line + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } + } else { + // No room in the L1, so we need to make room + if (L2cacheMemory.cacheAvail(L1DcacheMemory.cacheProbe(in_msg.Address))) { + // The L2 has room, so we move the line from the L1 to the L2 + trigger(Event:L1_to_L2, L1DcacheMemory.cacheProbe(in_msg.Address)); + } else { + // The L2 does not have room, so we replace a line from the L2 + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(L1DcacheMemory.cacheProbe(in_msg.Address))); + } + } + } + } + } + } + } + + // ACTIONS + action(a_allocateTBE, "a", desc="Allocate TBE with Address=B, ForwardID=null, RetryCount=zero, ForwardIDRetryCount=zero, ForwardProgressBit=unset.") { + check_allocate(TBEs); + TBEs.allocate(address); + TBEs[address].ForwardIDs.clear(); + + // Keep the TBE state consistent with the cache state + if (isCacheTagPresent(address)) { + TBEs[address].TBEState := getCacheEntry(address).CacheState; + } + } + + action(c_allocateL1DCacheBlock, "c", desc="Set L1 D-cache tag equal to tag of block B.") { + if (L1DcacheMemory.isTagPresent(address) == false) { + L1DcacheMemory.allocate(address); + } + } + + action(c_allocateL1ICacheBlock, "c'", desc="Set L1 I-cache tag equal to tag of block B.") { + if (L1IcacheMemory.isTagPresent(address) == false) { + L1IcacheMemory.allocate(address); + } + } + + action(cc_allocateL2CacheBlock, "\c", desc="Set L2 cache tag equal to tag of block B.") { + if (L2cacheMemory.isTagPresent(address) == false) { + L2cacheMemory.allocate(address); + } + } + + action(d_deallocateTBE, "d", desc="Deallocate TBE.") { + TBEs.deallocate(address); + } + + action(e_recordForwardingInfo, "e", desc="Record ID of other processor in ForwardID.") { + peek(addressNetwork_in, AddressMsg){ + TBEs[address].ForwardIDs.add(in_msg.Requestor); + TBEs[address].ForwardAddress := in_msg.Address; + } + } + + action(f_issueGETS, "f", desc="Issue GETS.") { + enqueue(addressNetwork_out, AddressMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.CacheState := cache_state_to_int(getState(address)); + out_msg.Requestor := machineID; + out_msg.Destination.broadcast(MachineType:L1Cache); + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(g_issueGETX, "g", desc="Issue GETX.") { + enqueue(addressNetwork_out, AddressMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.CacheState := cache_state_to_int(getState(address)); + out_msg.Requestor := machineID; + out_msg.Destination.broadcast(MachineType:L1Cache); + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(h_load_hit, "h", desc="Notify sequencer the load completed.") { + DEBUG_EXPR(getCacheEntry(address).DataBlk); + sequencer.readCallback(address, getCacheEntry(address).DataBlk); + } + + action(hh_store_hit, "\h", desc="Notify sequencer that store completed.") { + DEBUG_EXPR(getCacheEntry(address).DataBlk); + sequencer.writeCallback(address, getCacheEntry(address).DataBlk); + } + + action(i_popAddressQueue, "i", desc="Pop incoming address queue.") { + addressNetwork_in.dequeue(); + } + + action(j_popDataQueue, "j", desc="Pop incoming data queue.") { + dataNetwork_in.dequeue(); + } + + action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") { + mandatoryQueue_in.dequeue(); + } + + action(m_deallocateL1CacheBlock, "m", desc="Deallocate L1 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + if (L1DcacheMemory.isTagPresent(address)) { + L1DcacheMemory.deallocate(address); + } else { + L1IcacheMemory.deallocate(address); + } + } + + action(mm_deallocateL2CacheBlock, "\m", desc="Deallocate L2 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + L2cacheMemory.deallocate(address); + } + + action(n_copyFromL1toL2, "n", desc="Copy data block from L1 (I or D) to L2") { + if (L1DcacheMemory.isTagPresent(address)) { + L2cacheMemory[address].DataBlk := L1DcacheMemory[address].DataBlk; + } else { + L2cacheMemory[address].DataBlk := L1IcacheMemory[address].DataBlk; + } + } + + action(nn_copyFromL2toL1, "\n", desc="Copy data block from L2 to L1 (I or D)") { + if (L1DcacheMemory.isTagPresent(address)) { + L1DcacheMemory[address].DataBlk := L2cacheMemory[address].DataBlk; + } else { + L1IcacheMemory[address].DataBlk := L2cacheMemory[address].DataBlk; + } + } + + action(o_cacheToForward, "o", desc="Send data from the cache to the processor indicated by ForwardIDs.") { + peek(dataNetwork_in, DataMsg){ + // This has a CACHE_RESPONSE_LATENCY latency because we want to avoid the + // timing strangeness that can occur if requests that source the + // data from the TBE are faster than data sourced from the cache + enqueue(dataNetwork_out, DataMsg, latency="CACHE_RESPONSE_LATENCY"){ + out_msg.Address := TBEs[address].ForwardAddress; + out_msg.Sender := machineID; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.Destination := TBEs[address].ForwardIDs; + out_msg.DestMachine := MachineType:L1Cache; + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + action(p_issuePUTX, "p", desc="Issue PUTX.") { + enqueue(addressNetwork_out, AddressMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTX; + out_msg.CacheState := cache_state_to_int(getState(address)); + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); // To memory + out_msg.Destination.add(machineID); // Back to us + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } + + action(q_writeDataFromCacheToTBE, "q", desc="Write data from the cache into the TBE.") { + TBEs[address].DataBlk := getCacheEntry(address).DataBlk; + DEBUG_EXPR(TBEs[address].DataBlk); + } + + action(r_cacheToRequestor, "r", desc="Send data from the cache to the requestor") { + peek(addressNetwork_in, AddressMsg) { + enqueue(dataNetwork_out, DataMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + out_msg.DataBlk := getCacheEntry(address).DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + DEBUG_EXPR(getCacheEntry(address).DataBlk); + } + } + + action(s_saveDataInTBE, "s", desc="Save data in data field of TBE.") { + peek(dataNetwork_in, DataMsg) { + TBEs[address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(TBEs[address].DataBlk); + } + } + + action(t_issueGET_INSTR, "t", desc="Issue GETInstr.") { + enqueue(addressNetwork_out, AddressMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GET_INSTR; + out_msg.CacheState := cache_state_to_int(getState(address)); + out_msg.Requestor := machineID; + out_msg.Destination.broadcast(MachineType:L1Cache); + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(w_writeDataFromTBEToCache, "w", desc="Write data from the TBE into the cache.") { + getCacheEntry(address).DataBlk := TBEs[address].DataBlk; + DEBUG_EXPR(getCacheEntry(address).DataBlk); + } + + action(x_profileMiss, "x", desc="Profile the demand miss") { + peek(mandatoryQueue_in, CacheMsg) { + profile_miss(in_msg, id); + } + } + + action(y_tbeToReq, "y", desc="Send data from the TBE to the requestor.") { + peek(addressNetwork_in, AddressMsg) { + enqueue(dataNetwork_out, DataMsg, latency="CACHE_RESPONSE_LATENCY") { // Either this or the PutX should have a real latency + out_msg.Address := address; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + out_msg.DataBlk := TBEs[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + // action(z_stall, "z", desc="Cannot be handled right now.") { + // Special name recognized as do nothing case + // } + + action(zz_recycleMandatoryQueue, "\z", desc="Send the head of the mandatory queue to the back of the queue.") { + mandatoryQueue_in.recycle(); + } + + // TRANSITIONS + + // Transitions from Idle + transition({NP, I}, Load, IS_AD) { + f_issueGETS; + c_allocateL1DCacheBlock; + a_allocateTBE; + x_profileMiss; + k_popMandatoryQueue; + } + + transition({NP, I}, Ifetch, IS_AD) { + t_issueGET_INSTR; + c_allocateL1ICacheBlock; + a_allocateTBE; + x_profileMiss; + k_popMandatoryQueue; + } + + transition({NP, I}, Store, IM_AD) { + g_issueGETX; + c_allocateL1DCacheBlock; + a_allocateTBE; + x_profileMiss; + k_popMandatoryQueue; + } + + transition(I, L2_Replacement) { + mm_deallocateL2CacheBlock; + } + + transition({NP, I}, { Other_GETS, Other_GET_INSTR, Other_GETX } ) { + i_popAddressQueue; + } + + // Transitions from Shared + transition(S, {Load,Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(S, Store, SM_AD) { + g_issueGETX; + a_allocateTBE; + x_profileMiss; + k_popMandatoryQueue; + } + + transition(S, L2_Replacement, I) { + mm_deallocateL2CacheBlock; + } + + transition(S, {Other_GETS, Other_GET_INSTR}) { + i_popAddressQueue; + } + + transition(S, Other_GETX, I) { + i_popAddressQueue; + } + + // Transitions from Owned + transition(O, {Load,Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(O, Store, OM_A){ + g_issueGETX; + a_allocateTBE; + x_profileMiss; + k_popMandatoryQueue; + } + + transition(O, L2_Replacement, OI_A) { + p_issuePUTX; + a_allocateTBE; + q_writeDataFromCacheToTBE;// the cache line is now empty + mm_deallocateL2CacheBlock; + } + + transition(O, {Other_GETS,Other_GET_INSTR}) { + r_cacheToRequestor; + i_popAddressQueue; + } + + transition(O, Other_GETX, I) { + r_cacheToRequestor; + i_popAddressQueue; + } + + // Transitions from Modified + transition(M, {Load,Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(M, Store) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(M, L2_Replacement, MI_A) { + p_issuePUTX; + a_allocateTBE; + q_writeDataFromCacheToTBE;// the cache line is now empty + mm_deallocateL2CacheBlock; + } + + transition(M, {Other_GETS,Other_GET_INSTR}, O) { + r_cacheToRequestor; + i_popAddressQueue; + } + + transition(M, Other_GETX, I) { + r_cacheToRequestor; + i_popAddressQueue; + } + + // Transitions moving data between the L1 and L2 caches + + transition({I, S, O, M}, L1_to_L2) { + cc_allocateL2CacheBlock; + n_copyFromL1toL2; // Not really needed for state I + m_deallocateL1CacheBlock; + } + + transition({I, S, O, M}, L2_to_L1D) { + c_allocateL1DCacheBlock; + nn_copyFromL2toL1; // Not really needed for state I + mm_deallocateL2CacheBlock; + } + + transition({I, S, O, M}, L2_to_L1I) { + c_allocateL1ICacheBlock; + nn_copyFromL2toL1; // Not really needed for state I + mm_deallocateL2CacheBlock; + } + + // Transitions for Load/Store/Replacement from transient states + + transition({IS_AD, IM_AD, IS_A, IM_A, SM_AD, OM_A, SM_A, IS_D, IS_D_I, IM_D, IM_D_O, IM_D_I, IM_D_OI, SM_D, SM_D_O}, {Load, Ifetch, Store, L2_Replacement, L1_to_L2, L2_to_L1D, L2_to_L1I}) { + zz_recycleMandatoryQueue; + } + + transition({MI_A, OI_A, II_A}, {Load, Ifetch, Store, L2_Replacement, L1_to_L2, L2_to_L1D, L2_to_L1I}) { + zz_recycleMandatoryQueue; + } + + // Always ignore PUTXs which we are not the owner of + transition({NP, I, S, O, M, IS_AD, IM_AD, SM_AD, OM_A, IS_A, IM_A, SM_A, MI_A, OI_A, II_A, IS_D, IS_D_I, IM_D, IM_D_O, IM_D_I, IM_D_OI, SM_D, SM_D_O }, Other_PUTX) { + i_popAddressQueue; + } + + // transitions from IS_AD + + transition(IS_AD, {Own_GETS,Own_GET_INSTR}, IS_D) { + i_popAddressQueue; + } + transition(IS_AD, {Other_GETS, Other_GET_INSTR, Other_GETX}) { + i_popAddressQueue; + } + transition(IS_AD, Data, IS_A) { + s_saveDataInTBE; + j_popDataQueue; + } + + + // Transitions from IM_AD + + transition(IM_AD, Own_GETX, IM_D) { + i_popAddressQueue; + } + transition(IM_AD, {Other_GETS, Other_GET_INSTR, Other_GETX}) { + i_popAddressQueue; + } + transition(IM_AD, Data, IM_A) { + s_saveDataInTBE; + j_popDataQueue; + } + + // Transitions from OM_A + + transition(OM_A, Own_GETX, M){ + hh_store_hit; + d_deallocateTBE; + i_popAddressQueue; + } + + transition(OM_A, {Other_GETS, Other_GET_INSTR}){ + r_cacheToRequestor; + i_popAddressQueue; + } + + transition(OM_A, Other_GETX, IM_AD){ + r_cacheToRequestor; + i_popAddressQueue; + } + + transition(OM_A, Data, IM_A) { // if we get data, we know we're going to lose block before we see own GETX + s_saveDataInTBE; + j_popDataQueue; + } + + // Transitions from SM_AD + + transition(SM_AD, Own_GETX, SM_D) { + i_popAddressQueue; + } + transition(SM_AD, {Other_GETS,Other_GET_INSTR}) { + i_popAddressQueue; + } + transition(SM_AD, Other_GETX, IM_AD) { + i_popAddressQueue; + } + transition(SM_AD, Data, SM_A) { + s_saveDataInTBE; + j_popDataQueue; + } + + + // Transitions from IS_A + + transition(IS_A, {Own_GETS,Own_GET_INSTR}, S) { + w_writeDataFromTBEToCache; + h_load_hit; + d_deallocateTBE; + i_popAddressQueue; + } + transition(IS_A, {Other_GETS, Other_GET_INSTR, Other_GETX}) { + i_popAddressQueue; + } + + // Transitions from IM_A + + transition(IM_A, Own_GETX, M) { + w_writeDataFromTBEToCache; + hh_store_hit; + d_deallocateTBE; + i_popAddressQueue; + } + transition(IM_A, {Other_GETS, Other_GET_INSTR, Other_GETX}) { + i_popAddressQueue; + } + + // Transitions from SM_A + + transition(SM_A, Own_GETX, M) { + w_writeDataFromTBEToCache; + hh_store_hit; + d_deallocateTBE; + i_popAddressQueue; + } + transition(SM_A, {Other_GETS,Other_GET_INSTR}) { + i_popAddressQueue; + } + transition(SM_A, Other_GETX, IM_A) { + i_popAddressQueue; + } + + + // Transitions from MI_A + + transition(MI_A, Own_PUTX, I) { + d_deallocateTBE; + i_popAddressQueue; + } + + transition(MI_A, {Other_GETS, Other_GET_INSTR}) { + y_tbeToReq; + i_popAddressQueue; + } + + transition(MI_A, Other_GETX, II_A) { + y_tbeToReq; + i_popAddressQueue; + } + + // Transitions from OI_A + + transition(OI_A, Own_PUTX, I) { + d_deallocateTBE; + i_popAddressQueue; + } + + transition(OI_A, {Other_GETS, Other_GET_INSTR}) { + y_tbeToReq; + i_popAddressQueue; + } + + transition(OI_A, Other_GETX, II_A) { + y_tbeToReq; + i_popAddressQueue; + } + + + // Transitions from II_A + + transition(II_A, Own_PUTX, I) { + d_deallocateTBE; + i_popAddressQueue; + } + + transition(II_A, {Other_GETS, Other_GET_INSTR, Other_GETX}) { + i_popAddressQueue; + } + + // Transitions from IS_D, IS_D_I + + transition({IS_D, IS_D_I}, {Other_GETS,Other_GET_INSTR}) { + i_popAddressQueue; + } + transition(IS_D, Other_GETX, IS_D_I) { + i_popAddressQueue; + } + transition(IS_D_I, Other_GETX) { + i_popAddressQueue; + } + transition(IS_D, Data, S) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + h_load_hit; + d_deallocateTBE; + j_popDataQueue; + } + + transition(IS_D_I, Data, I) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + h_load_hit; + d_deallocateTBE; + j_popDataQueue; + } + + // Transitions from IM_D, IM_D_O, IM_D_I, IM_D_OI + + transition( IM_D, {Other_GETS,Other_GET_INSTR}, IM_D_O ) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition( IM_D, Other_GETX, IM_D_I ) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition(IM_D_O, {Other_GETS,Other_GET_INSTR} ) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition(IM_D_O, Other_GETX, IM_D_OI) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition( {IM_D_I, IM_D_OI}, {Other_GETS, Other_GET_INSTR, Other_GETX} ) { + i_popAddressQueue; + } + + transition(IM_D, Data, M) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + hh_store_hit; + d_deallocateTBE; + j_popDataQueue; + } + + transition(IM_D_O, Data, O) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + hh_store_hit; + o_cacheToForward; + d_deallocateTBE; + j_popDataQueue; + } + + transition(IM_D_I, Data, I) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + hh_store_hit; + o_cacheToForward; + d_deallocateTBE; + j_popDataQueue; + } + + transition(IM_D_OI, Data, I) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + hh_store_hit; + o_cacheToForward; + d_deallocateTBE; + j_popDataQueue; + } + + // Transitions for SM_D, SM_D_O + + transition(SM_D, {Other_GETS,Other_GET_INSTR}, SM_D_O) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition(SM_D, Other_GETX, IM_D_I) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition(SM_D_O, {Other_GETS,Other_GET_INSTR}) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition(SM_D_O, Other_GETX, IM_D_OI) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition(SM_D, Data, M) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + hh_store_hit; + d_deallocateTBE; + j_popDataQueue; + } + + transition(SM_D_O, Data, O) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + hh_store_hit; + o_cacheToForward; + d_deallocateTBE; + j_popDataQueue; + } +} diff --git a/src/mem/protocol/MOSI_SMP_bcast-dir.sm b/src/mem/protocol/MOSI_SMP_bcast-dir.sm new file mode 100644 index 000000000..7cccaf9d3 --- /dev/null +++ b/src/mem/protocol/MOSI_SMP_bcast-dir.sm @@ -0,0 +1,267 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +machine(Directory, "MOSI Broadcast Optimized") { + + + MessageBuffer addressFromDir, network="To", virtual_network="0", ordered="true"; + MessageBuffer dataFromDir, network="To", virtual_network="1", ordered="false"; + + MessageBuffer addressToDir, network="From", virtual_network="0", ordered="true"; + MessageBuffer dataToDir, network="From", virtual_network="1", ordered="false"; + + + enumeration(State, desc="Directory states", default="Directory_State_C") { + C, desc="Cold - no processor has requested this line"; + I, desc="Idle"; + S, desc="Shared"; + SS, desc="Shared, 2 or more shares"; + OS, desc="Owned by a cache"; + OSS, desc="Owned by a cache, present in at least 3 caches"; + M, desc="Modified", format="!b"; + } + + // ** EVENTS ** + + enumeration(Event, desc="Directory events") { + // From Address network + OtherAddress, desc="We saw an address msg to someone else"; + GETS, desc="A GETS arrives"; + GET_INSTR, desc="A GETInstr arrives"; + GETX, desc="A GETX arrives", format="!r"; + PUTX_Owner, desc="A PUTX arrives, requestor is owner"; + PUTX_NotOwner, desc="A PUTX arrives, requestor is not owner", format="!r"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + State DirectoryState, desc="Directory state"; + bool DirOwner, default="true", desc="Is dir owner?"; + MachineID ProcOwner, desc="Processor Owner"; + DataBlock DataBlk, desc="data for the block"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + + void profile_request(int cache_state, State directory_state, GenericRequestType request_type); + + State getState(Address addr) { + if (directory.isPresent(addr)) { + return directory[addr].DirectoryState; + } + return State:C; + } + + void setState(Address addr, State state) { + if (directory.isPresent(addr)) { + directory[addr].DirectoryState := state; + } + } + + // ** OUT_PORTS ** + + out_port(dataNetwork_out, DataMsg, dataFromDir); + out_port(addressNetwork_out, AddressMsg, addressFromDir); + + // ** IN_PORTS ** + + // Address Network + in_port(addressNetwork_in, AddressMsg, addressToDir) { + if (addressNetwork_in.isReady()) { + peek(addressNetwork_in, AddressMsg) { + if(map_Address_to_Directory(in_msg.Address) != machineID) { + trigger(Event:OtherAddress, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GET_INSTR) { + trigger(Event:GET_INSTR, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTX) { + if (in_msg.Requestor == directory[in_msg.Address].ProcOwner && directory[in_msg.Address].DirOwner == false) { + trigger(Event:PUTX_Owner, in_msg.Address); + } else { + trigger(Event:PUTX_NotOwner, in_msg.Address); + } + } else { + error("unexpected message"); + } + } + } + } + + // *** ACTIONS *** + + action(d_sendDataMsg, "d", desc="Send data message to requestor") { + peek(addressNetwork_in, AddressMsg) { + enqueue(dataNetwork_out, DataMsg, latency="MEMORY_LATENCY") { + out_msg.Address := in_msg.Address; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + DEBUG_EXPR(in_msg.Requestor); + DEBUG_EXPR(out_msg.DataBlk); + } + } + } + + action(j_popAddressQueue, "j", desc="Pop address queue.") { + addressNetwork_in.dequeue(); + } + + action(p_profile, "p", desc="Profile this transition.") { + peek(addressNetwork_in, AddressMsg) { + profile_request(in_msg.CacheState, getState(address), convertToGenericType(in_msg.Type)); + } + } + + action(m_setOwnerRequestor, "m", desc="Set owner = requestor") { + peek(addressNetwork_in, AddressMsg) { + directory[in_msg.Address].ProcOwner := in_msg.Requestor; + directory[in_msg.Address].DirOwner := false; + } + } + + action(r_writeDataFromRequest, "r", desc="Write request data to memory") { + peek(addressNetwork_in, AddressMsg) { + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + action(x_setOwnerToDirectory, "x", desc="Set owner equal to the directory"){ + peek(addressNetwork_in, AddressMsg) { + directory[in_msg.Address].DirOwner := true; + } + } + + // TRANSITIONS + + // Ignore all address and data messages not bound for us + transition(C, OtherAddress) { + j_popAddressQueue; + } + + // PUTX_NotOwner Transitions + transition({I, S, SS, OS, OSS, M}, PUTX_NotOwner) { + p_profile; + j_popAddressQueue; + } + + // Transitions from Idle + transition({C, I}, {GETS,GET_INSTR}, S) { + d_sendDataMsg; + p_profile; + j_popAddressQueue; + } + + transition({C, I}, GETX, M) { + d_sendDataMsg; + m_setOwnerRequestor; + p_profile; + j_popAddressQueue + } + + // Transitions from Shared + transition({S, SS}, {GETS,GET_INSTR}, SS) { + d_sendDataMsg; + p_profile; + j_popAddressQueue; + } + + transition({S, SS}, GETX, M) { + d_sendDataMsg; + m_setOwnerRequestor; + p_profile; + j_popAddressQueue; + } + + // Transitions from Owned + transition({OS, OSS}, {GETS,GET_INSTR}, OSS) { + p_profile; + j_popAddressQueue; + } + + transition({OS, OSS}, GETX, M) { + m_setOwnerRequestor; + p_profile; + j_popAddressQueue; + } + + transition(OS, PUTX_Owner, S) { + x_setOwnerToDirectory; + r_writeDataFromRequest; + p_profile; + j_popAddressQueue; + } + + transition(OSS, PUTX_Owner, SS) { + x_setOwnerToDirectory; + r_writeDataFromRequest; + p_profile; + j_popAddressQueue; + } + + // Transitions from Modified + transition(M, {GETS,GET_INSTR}, OS) { + p_profile; + j_popAddressQueue; + } + + transition(M, GETX) { + m_setOwnerRequestor; + p_profile; + j_popAddressQueue; + } + + transition(M, PUTX_Owner, I) { + x_setOwnerToDirectory; + r_writeDataFromRequest; + p_profile; + j_popAddressQueue; + } + +} diff --git a/src/mem/protocol/MOSI_SMP_bcast-msg.sm b/src/mem/protocol/MOSI_SMP_bcast-msg.sm new file mode 100644 index 000000000..263cf6f18 --- /dev/null +++ b/src/mem/protocol/MOSI_SMP_bcast-msg.sm @@ -0,0 +1,79 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +// CoherenceRequestType +enumeration(CoherenceRequestType, desc="...") { + GET_INSTR, desc="Get Instruction"; + GETS, desc="Get Shared"; + GETX, desc="Get eXclusive"; + PUTX, desc="Put eXclusive"; +} + +// AddressMsg +structure(AddressMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)"; + int CacheState, default="1000", desc="Hack to transfer the cache's state for profiling"; // The default of 1000 will generate an error if we forget to set this + MachineID Requestor, desc="Node who initiated the request"; + NetDest Destination, desc="Multicast destination mask"; + DataBlock DataBlk, desc="data for the cache line"; // This is used for PutX and Downgrades only + MessageSizeType MessageSize, desc="size category of the message"; +} + +// DataMsg +structure(DataMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + MachineID Sender, desc="Node who sent the data"; + // MachineType SenderType, desc="Component who sent data"; + NetDest Destination, desc="Node to whom the data is sent"; + MachineType DestMachine, desc="What component receives the data"; + DataBlock DataBlk, desc="data for the cache line"; + MessageSizeType MessageSize, desc="size category of the message"; +} + +GenericRequestType convertToGenericType(CoherenceRequestType type) { + if(type == CoherenceRequestType:PUTX) { + return GenericRequestType:PUTX; + } else if(type == CoherenceRequestType:GETS) { + return GenericRequestType:GETS; + } else if(type == CoherenceRequestType:GET_INSTR) { + return GenericRequestType:GET_INSTR; + } else if(type == CoherenceRequestType:GETX) { + return GenericRequestType:GETX; + } else { + DEBUG_EXPR(type); + error("invalid CoherenceRequestType"); + } +} + diff --git a/src/mem/protocol/MOSI_SMP_bcast.slicc b/src/mem/protocol/MOSI_SMP_bcast.slicc new file mode 100644 index 000000000..ac8b85d30 --- /dev/null +++ b/src/mem/protocol/MOSI_SMP_bcast.slicc @@ -0,0 +1,4 @@ +MOSI_SMP_bcast-msg.sm +MOSI_SMP_bcast-cache.sm +MOSI_SMP_bcast-dir.sm +standard_SMP-protocol.sm diff --git a/src/mem/protocol/MOSI_SMP_bcast_1level-cache.sm b/src/mem/protocol/MOSI_SMP_bcast_1level-cache.sm new file mode 100644 index 000000000..b44e502c0 --- /dev/null +++ b/src/mem/protocol/MOSI_SMP_bcast_1level-cache.sm @@ -0,0 +1,921 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +machine(L1Cache, "MOSI Broadcast Optimized") { + + MessageBuffer addressFromCache, network="To", virtual_network="0", ordered="true"; + MessageBuffer dataFromCache, network="To", virtual_network="1", ordered="false"; + + MessageBuffer addressToCache, network="From", virtual_network="0", ordered="true"; + MessageBuffer dataToCache, network="From", virtual_network="1", ordered="false"; + + // STATES + + enumeration(State, desc="Cache states", default="L1Cache_State_I") { + NP, desc="Not Present"; + I, desc="Idle"; + S, desc="Shared"; + O, desc="Owned"; + M, desc="Modified", format="!b"; + IS_AD, "IS^AD", desc="idle, issued GETS, have not seen GETS or data yet"; + IM_AD, "IM^AD", desc="idle, issued GETX, have not seen GETX or data yet"; + SM_AD, "SM^AD",desc="shared, issued GETX, have not seen GETX or data yet"; + OM_A, "OM^A",desc="owned, issued GETX, have not seen GETX yet", format="!b"; + + IS_A, "IS^A",desc="idle, issued GETS, have not seen GETS, have seen data"; + IM_A, "IM^A",desc="idle, issued GETX, have not seen GETX, have seen data"; + SM_A, "SM^A",desc="shared, issued GETX, have not seen GETX, have seen data", format="!b"; + + MI_A, "MI^A", desc="modified, issued PUTX, have not seen PUTX yet"; + OI_A, "OI^A", desc="owned, issued PUTX, have not seen PUTX yet"; + II_A, "II^A", desc="modified, issued PUTX, have not seen PUTX, then saw other GETX", format="!b"; + + IS_D, "IS^D", desc="idle, issued GETS, have seen GETS, have not seen data yet"; + IS_D_I, "IS^D^I", desc="idle, issued GETS, have seen GETS, have not seen data, then saw other GETX"; + IM_D, "IM^D", desc="idle, issued GETX, have seen GETX, have not seen data yet"; + IM_D_O, "IM^D^O", desc="idle, issued GETX, have seen GETX, have not seen data yet, then saw other GETS"; + IM_D_I, "IM^D^I", desc="idle, issued GETX, have seen GETX, have not seen data yet, then saw other GETX"; + IM_D_OI, "IM^D^OI", desc="idle, issued GETX, have seen GETX, have not seen data yet, then saw other GETS, then saw other GETX"; + SM_D, "SM^D", desc="shared, issued GETX, have seen GETX, have not seen data yet"; + SM_D_O, "SM^D^O", desc="shared, issued GETX, have seen GETX, have not seen data yet, then saw other GETS"; + } + + // ** EVENTS ** + + enumeration(Event, desc="Cache events") { + // From processor + Load, desc="Load request from the processor"; + Ifetch, desc="I-fetch request from the processor"; + Store, desc="Store request from the processor"; + Replacement, desc="Replacement"; + Load_prefetch, desc="Read only prefetch"; + Store_prefetch, desc="Read write prefetch", format="!r"; + + // From Address network + Own_GETS, desc="Occurs when we observe our own GETS request in the global order"; + Own_GET_INSTR, desc="Occurs when we observe our own GETInstr request in the global order"; + Own_GETX, desc="Occurs when we observe our own GETX request in the global order"; + Own_PUTX, desc="Occurs when we observe our own PUTX request in the global order", format="!r"; + Other_GETS, desc="Occurs when we observe a GETS request from another processor"; + Other_GET_INSTR, desc="Occurs when we observe a GETInstr request from another processor"; + Other_GETX, desc="Occurs when we observe a GETX request from another processor"; + Other_PUTX, desc="Occurs when we observe a PUTX request from another processor", format="!r"; + + // From Data network + Data, desc="Data for this block from the data network"; + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + DataBlock DataBlk, desc="data for the block"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="Buffer for the data block"; + NetDest ForwardIDs, desc="IDs of the processors to forward the block"; + Address ForwardAddress, desc="Address of request for forwarding"; + bool isPrefetch, desc="Set if this request is a prefetch"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + MessageBuffer mandatoryQueue, ordered="false", abstract_chip_ptr="true"; + MessageBuffer optionalQueue, ordered="true", abstract_chip_ptr="true"; + Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i"; + StoreBuffer storeBuffer, abstract_chip_ptr="true", constructor_hack="i"; + + + TBETable TBEs, template_hack=""; + CacheMemory cacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_unified"', abstract_chip_ptr="true"; + + int cache_state_to_int(State state); + + State getState(Address addr) { + if(TBEs.isPresent(addr)) { + return TBEs[addr].TBEState; + } else if (cacheMemory.isTagPresent(addr)) { + return cacheMemory[addr].CacheState; + } + return State:NP; + } + + void setState(Address addr, State state) { + if (TBEs.isPresent(addr)) { + TBEs[addr].TBEState := state; + } + if (cacheMemory.isTagPresent(addr)) { + cacheMemory[addr].CacheState := state; + + // Set permission + if ((state == State:I) || (state == State:MI_A) || (state == State:II_A)) { + cacheMemory.changePermission(addr, AccessPermission:Invalid); + } else if (state == State:S || state == State:O) { + cacheMemory.changePermission(addr, AccessPermission:Read_Only); + } else if (state == State:M) { + cacheMemory.changePermission(addr, AccessPermission:Read_Write); + } else { + cacheMemory.changePermission(addr, AccessPermission:Busy); + } + } + } + + // ** OUT_PORTS ** + + out_port(dataNetwork_out, DataMsg, dataFromCache); + out_port(addressNetwork_out, AddressMsg, addressFromCache); + + // ** IN_PORTS ** + + // Data Network + in_port(dataNetwork_in, DataMsg, dataToCache) { + if (dataNetwork_in.isReady()) { + peek(dataNetwork_in, DataMsg) { + trigger(Event:Data, in_msg.Address); + } + } + } + + // Address Network + in_port(addressNetwork_in, AddressMsg, addressToCache) { + if (addressNetwork_in.isReady()) { + peek(addressNetwork_in, AddressMsg) { + if (in_msg.Type == CoherenceRequestType:GETS) { + if (in_msg.Requestor == machineID) { + trigger(Event:Own_GETS, in_msg.Address); + } else { + trigger(Event:Other_GETS, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:GETX) { + if (in_msg.Requestor == machineID) { + trigger(Event:Own_GETX, in_msg.Address); + } else { + trigger(Event:Other_GETX, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:GET_INSTR) { + if (in_msg.Requestor == machineID) { + trigger(Event:Own_GET_INSTR, in_msg.Address); + } else { + trigger(Event:Other_GET_INSTR, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:PUTX) { + if (in_msg.Requestor == machineID) { + trigger(Event:Own_PUTX, in_msg.Address); + } else { + trigger(Event:Other_PUTX, in_msg.Address); + } + } else { + error("Unexpected message"); + } + } + } + } + + // Mandatory Queue + in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { + if (mandatoryQueue_in.isReady()) { + peek(mandatoryQueue_in, CacheMsg) { + if (cacheMemory.cacheAvail(in_msg.Address) == false) { + trigger(Event:Replacement, cacheMemory.cacheProbe(in_msg.Address)); + } else { + if (in_msg.Type == CacheRequestType:LD) { + trigger(Event:Load, in_msg.Address); + } else if (in_msg.Type == CacheRequestType:IFETCH) { + trigger(Event:Ifetch, in_msg.Address); + } else if ((in_msg.Type == CacheRequestType:ST) || (in_msg.Type == CacheRequestType:ATOMIC)) { + trigger(Event:Store, in_msg.Address); + } else { + error("Invalid CacheRequestType"); + } + } + } + } + } + + // Optional Queue + in_port(optionalQueue_in, CacheMsg, optionalQueue, desc="...") { + if (optionalQueue_in.isReady()) { + peek(optionalQueue_in, CacheMsg) { + if (cacheMemory.cacheAvail(in_msg.Address) == false) { + trigger(Event:Replacement, cacheMemory.cacheProbe(in_msg.Address)); + } else { + if ((in_msg.Type == CacheRequestType:LD) || (in_msg.Type == CacheRequestType:IFETCH)) { + trigger(Event:Load_prefetch, in_msg.Address); + } else if ((in_msg.Type == CacheRequestType:ST) || (in_msg.Type == CacheRequestType:ATOMIC)) { + trigger(Event:Store_prefetch, in_msg.Address); + } else { + error("Invalid CacheRequestType"); + } + } + } + } + } + + // ACTIONS + action(a_allocateTBE, "a", desc="Allocate TBE with Address=B, ForwardID=null, RetryCount=zero, ForwardIDRetryCount=zero, ForwardProgressBit=unset.") { + check_allocate(TBEs); + TBEs.allocate(address); + TBEs[address].isPrefetch := false; + TBEs[address].ForwardIDs.clear(); + + // Keep the TBE state consistent with the cache state + if (cacheMemory.isTagPresent(address)) { + TBEs[address].TBEState := cacheMemory[address].CacheState; + } + } + + + action(b_setPrefetchBit, "b", desc="Set prefetch bit in TBE.") { + TBEs[address].isPrefetch := true; + } + + action(c_allocateCacheBlock, "c", desc="Set cache tag equal to tag of block B.") { + if (cacheMemory.isTagPresent(address) == false) { + cacheMemory.allocate(address); + } + } + + action(d_deallocateTBE, "d", desc="Deallocate TBE.") { + TBEs.deallocate(address); + } + + action(e_recordForwardingInfo, "e", desc="Record ID of other processor in ForwardID.") { + peek(addressNetwork_in, AddressMsg){ + TBEs[address].ForwardIDs.add(in_msg.Requestor); + TBEs[address].ForwardAddress := in_msg.Address; + } + } + + action(f_issueGETS, "f", desc="Issue GETS.") { + enqueue(addressNetwork_out, AddressMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.CacheState := cache_state_to_int(getState(address)); + out_msg.Requestor := machineID; + out_msg.Destination.broadcast(MachineType:L1Cache); + out_msg.Destination.add(map_Address_to_Directory(address)); // To memory + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(g_issueGETX, "g", desc="Issue GETX.") { + enqueue(addressNetwork_out, AddressMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.CacheState := cache_state_to_int(getState(address)); + out_msg.Requestor := machineID; + out_msg.Destination.broadcast(MachineType:L1Cache); + out_msg.Destination.add(map_Address_to_Directory(address)); // To memory + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(h_load_hit, "h", desc="If not prefetch, notify sequencer the load completed.") { + DEBUG_EXPR(cacheMemory[address].DataBlk); + if((TBEs.isPresent(address) == false) || (TBEs[address].isPrefetch == false)) { + // Non-prefetch + sequencer.readCallback(address, cacheMemory[address].DataBlk); + } else { + // Prefetch - don't call back + } + } + + action(hh_store_hit, "\h", desc="If not prefetch, notify sequencer that store completed.") { + DEBUG_EXPR(cacheMemory[address].DataBlk); + if((TBEs.isPresent(address) == false) || (TBEs[address].isPrefetch == false)) { + // Non-prefetch + sequencer.writeCallback(address, cacheMemory[address].DataBlk); + } else { + // Prefetch - don't call back + } + } + + action(i_popAddressQueue, "i", desc="Pop incoming address queue.") { + addressNetwork_in.dequeue(); + } + + action(j_popDataQueue, "j", desc="Pop incoming data queue.") { + dataNetwork_in.dequeue(); + } + + action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") { + mandatoryQueue_in.dequeue(); + } + + action(l_popOptionalQueue, "l", desc="Pop optional queue.") { + optionalQueue_in.dequeue(); + } + + + action(o_cacheToForward, "o", desc="Send data from the cache to the processor indicated by ForwardIDs.") { + peek(dataNetwork_in, DataMsg){ + // This has a CACHE_RESPONSE_LATENCY latency because we want to avoid the + // timing strangeness that can occur if requests that source the + // data from the TBE are faster than data sourced from the cache + enqueue(dataNetwork_out, DataMsg, latency="CACHE_RESPONSE_LATENCY"){ + out_msg.Address := TBEs[address].ForwardAddress; + out_msg.Sender := machineID; + out_msg.DataBlk := cacheMemory[address].DataBlk; + out_msg.Destination := TBEs[address].ForwardIDs; + out_msg.DestMachine := MachineType:L1Cache; + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + action(p_issuePUTX, "p", desc="Issue PUTX.") { + enqueue(addressNetwork_out, AddressMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTX; + out_msg.CacheState := cache_state_to_int(getState(address)); + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); // To memory + out_msg.Destination.add(machineID); // Back to us + out_msg.DataBlk := cacheMemory[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } + + action(q_writeDataFromCacheToTBE, "q", desc="Write data from the cache into the TBE.") { + TBEs[address].DataBlk := cacheMemory[address].DataBlk; + DEBUG_EXPR(TBEs[address].DataBlk); + } + + action(r_cacheToRequestor, "r", desc="Send data from the cache to the requestor") { + peek(addressNetwork_in, AddressMsg) { + enqueue(dataNetwork_out, DataMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + out_msg.DataBlk := cacheMemory[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + DEBUG_EXPR(cacheMemory[address].DataBlk); + } + } + + + action(s_saveDataInTBE, "s", desc="Save data in data field of TBE.") { + peek(dataNetwork_in, DataMsg) { + TBEs[address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(TBEs[address].DataBlk); + } + } + + action(t_issueGET_INSTR, "t", desc="Issue GETInstr.") { + enqueue(addressNetwork_out, AddressMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GET_INSTR; + out_msg.CacheState := cache_state_to_int(getState(address)); + out_msg.Requestor := machineID; + out_msg.Destination.broadcast(MachineType:L1Cache); + out_msg.Destination.add(map_Address_to_Directory(address)); // To memory + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(w_writeDataFromTBEToCache, "w", desc="Write data from the TBE into the cache.") { + cacheMemory[address].DataBlk := TBEs[address].DataBlk; + DEBUG_EXPR(cacheMemory[address].DataBlk); + } + + action(y_tbeToReq, "y", desc="Send data from the TBE to the requestor.") { + peek(addressNetwork_in, AddressMsg) { + enqueue(dataNetwork_out, DataMsg, latency="CACHE_RESPONSE_LATENCY") { // Either this or the PutX should have a real latency + out_msg.Address := address; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + out_msg.DataBlk := TBEs[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + action(ff_deallocateCacheBlock, "\f", desc="Deallocate cache block. Sets the cache to invalid, allowing a replacement in parallel with a fetch.") { + cacheMemory.deallocate(address); + } + + action(z_stall, "z", desc="Cannot be handled right now.") { + // Special name recognized as do nothing case + } + + // TRANSITIONS + + // Transitions from Idle + transition({NP, I}, Load, IS_AD) { + f_issueGETS; + c_allocateCacheBlock; + a_allocateTBE; + k_popMandatoryQueue; + } + + transition({NP, I}, Ifetch, IS_AD) { + t_issueGET_INSTR; + c_allocateCacheBlock; + a_allocateTBE; + k_popMandatoryQueue; + } + + transition({NP, I}, Load_prefetch, IS_AD) { + f_issueGETS; + c_allocateCacheBlock; + a_allocateTBE; + b_setPrefetchBit; + l_popOptionalQueue; + } + + transition({NP, I}, Store, IM_AD) { + g_issueGETX; + c_allocateCacheBlock; + a_allocateTBE; + k_popMandatoryQueue; + } + + transition({NP, I}, Store_prefetch, IM_AD) { + g_issueGETX; + c_allocateCacheBlock; + a_allocateTBE; + b_setPrefetchBit; + l_popOptionalQueue; + } + + transition(I, Replacement) { + ff_deallocateCacheBlock; // the cache line is now in NotPresent + } + + transition({NP, I}, { Other_GETS, Other_GET_INSTR, Other_GETX } ) { + i_popAddressQueue; + } + + // Transitions from Shared + transition(S, {Load,Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(S, Load_prefetch) { + l_popOptionalQueue; + } + + transition(S, Store, SM_AD) { + g_issueGETX; + a_allocateTBE; + k_popMandatoryQueue; + } + + transition(S, Store_prefetch, IM_AD) { + g_issueGETX; + a_allocateTBE; + b_setPrefetchBit; // Must be after allocate TBE + l_popOptionalQueue; + } + + transition(S, Replacement, I) { + ff_deallocateCacheBlock; // the cache line is now in NotPresent + } + + transition(S, {Other_GETS, Other_GET_INSTR}) { + i_popAddressQueue; + } + + transition(S, Other_GETX, I) { + i_popAddressQueue; + } + + // Transitions from Owned + transition(O, {Load,Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(O, Store, OM_A){ + g_issueGETX; + a_allocateTBE; + k_popMandatoryQueue; + } + + transition(O, Load_prefetch) { + l_popOptionalQueue; + } + + transition(O, Store_prefetch, OM_A) { + g_issueGETX; + a_allocateTBE; + b_setPrefetchBit; + l_popOptionalQueue; + } + + transition(O, Replacement, OI_A) { + p_issuePUTX; + a_allocateTBE; + q_writeDataFromCacheToTBE;// the cache line is now empty + ff_deallocateCacheBlock; // the cache line is now in NotPresent + } + + transition(O, {Other_GETS,Other_GET_INSTR}) { + r_cacheToRequestor; + i_popAddressQueue; + } + + transition(O, Other_GETX, I) { + r_cacheToRequestor; + i_popAddressQueue; + } + + // Transitions from Modified + transition(M, {Load,Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(M, Store) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(M, {Load_prefetch,Store_prefetch}) { + l_popOptionalQueue; + } + + transition(M, Replacement, MI_A) { + p_issuePUTX; + a_allocateTBE; + q_writeDataFromCacheToTBE;// the cache line is now empty + ff_deallocateCacheBlock; // the cache line is now in NotPresent + } + + transition(M, {Other_GETS,Other_GET_INSTR}, O) { + r_cacheToRequestor; + i_popAddressQueue; + } + + transition(M, Other_GETX, I) { + r_cacheToRequestor; + i_popAddressQueue; + } + + + // Transitions for Load/Store/Replacement from transient states + + transition({IS_AD, IM_AD, IS_A, IM_A, SM_AD, OM_A, SM_A, IS_D, IS_D_I, IM_D, IM_D_O, IM_D_I, IM_D_OI, SM_D, SM_D_O}, {Load, Ifetch, Store, Replacement}) { + z_stall; + } + + transition({IS_AD, IM_AD, IS_A, IM_A, SM_AD, OM_A, SM_A, IS_D, IM_D, IM_D_O, SM_D, SM_D_O}, Load_prefetch) { + l_popOptionalQueue; + } + + transition({IS_D_I, IM_D_I, IM_D_OI}, Load_prefetch) { + z_stall; + } + + transition({IM_AD, SM_AD, OM_A, IM_A, SM_A, IM_D, SM_D}, Store_prefetch) { + l_popOptionalQueue; + } + + transition({IS_AD, IS_A, IS_D, IS_D_I, IM_D_O, IM_D_I, IM_D_OI, SM_D_O}, Store_prefetch) { + z_stall; + } + + transition({MI_A, OI_A, II_A}, {Load, Ifetch, Store, Load_prefetch, Store_prefetch, Replacement}) { + z_stall; + } + + // Always ignore PUTXs which we are not the owner of + transition({NP, I, S, O, M, IS_AD, IM_AD, SM_AD, OM_A, IS_A, IM_A, SM_A, MI_A, OI_A, II_A, IS_D, IS_D_I, IM_D, IM_D_O, IM_D_I, IM_D_OI, SM_D, SM_D_O }, Other_PUTX) { + i_popAddressQueue; + } + + // transitions from IS_AD + + transition(IS_AD, {Own_GETS,Own_GET_INSTR}, IS_D) { + i_popAddressQueue; + } + transition(IS_AD, {Other_GETS, Other_GET_INSTR, Other_GETX}) { + i_popAddressQueue; + } + transition(IS_AD, Data, IS_A) { + s_saveDataInTBE; + j_popDataQueue; + } + + + // Transitions from IM_AD + + transition(IM_AD, Own_GETX, IM_D) { + i_popAddressQueue; + } + transition(IM_AD, {Other_GETS, Other_GET_INSTR, Other_GETX}) { + i_popAddressQueue; + } + transition(IM_AD, Data, IM_A) { + s_saveDataInTBE; + j_popDataQueue; + } + + // Transitions from OM_A + + transition(OM_A, Own_GETX, M){ + hh_store_hit; + d_deallocateTBE; + i_popAddressQueue; + } + + transition(OM_A, {Other_GETS, Other_GET_INSTR}){ + r_cacheToRequestor; + i_popAddressQueue; + } + + transition(OM_A, Other_GETX, IM_AD){ + r_cacheToRequestor; + i_popAddressQueue; + } + + transition(OM_A, Data, IM_A) { // if we get data, we know we're going to lose block before we see own GETX + s_saveDataInTBE; + j_popDataQueue; + } + + // Transitions from SM_AD + + transition(SM_AD, Own_GETX, SM_D) { + i_popAddressQueue; + } + transition(SM_AD, {Other_GETS,Other_GET_INSTR}) { + i_popAddressQueue; + } + transition(SM_AD, Other_GETX, IM_AD) { + i_popAddressQueue; + } + transition(SM_AD, Data, SM_A) { + s_saveDataInTBE; + j_popDataQueue; + } + + + // Transitions from IS_A + + transition(IS_A, {Own_GETS,Own_GET_INSTR}, S) { + w_writeDataFromTBEToCache; + h_load_hit; + d_deallocateTBE; + i_popAddressQueue; + } + transition(IS_A, {Other_GETS, Other_GET_INSTR, Other_GETX}) { + i_popAddressQueue; + } + + // Transitions from IM_A + + transition(IM_A, Own_GETX, M) { + w_writeDataFromTBEToCache; + hh_store_hit; + d_deallocateTBE; + i_popAddressQueue; + } + transition(IM_A, {Other_GETS, Other_GET_INSTR, Other_GETX}) { + i_popAddressQueue; + } + + // Transitions from SM_A + + transition(SM_A, Own_GETX, M) { + w_writeDataFromTBEToCache; + hh_store_hit; + d_deallocateTBE; + i_popAddressQueue; + } + transition(SM_A, {Other_GETS,Other_GET_INSTR}) { + i_popAddressQueue; + } + transition(SM_A, Other_GETX, IM_A) { + i_popAddressQueue; + } + + + // Transitions from MI_A + + transition(MI_A, Own_PUTX, I) { + d_deallocateTBE; + i_popAddressQueue; + } + + transition(MI_A, {Other_GETS, Other_GET_INSTR}) { + y_tbeToReq; + i_popAddressQueue; + } + + transition(MI_A, Other_GETX, II_A) { + y_tbeToReq; + i_popAddressQueue; + } + + // Transitions from OI_A + + transition(OI_A, Own_PUTX, I) { + d_deallocateTBE; + i_popAddressQueue; + } + + transition(OI_A, {Other_GETS, Other_GET_INSTR}) { + y_tbeToReq; + i_popAddressQueue; + } + + transition(OI_A, Other_GETX, II_A) { + y_tbeToReq; + i_popAddressQueue; + } + + + // Transitions from II_A + + transition(II_A, Own_PUTX, I) { + d_deallocateTBE; + i_popAddressQueue; + } + + transition(II_A, {Other_GETS, Other_GET_INSTR, Other_GETX}) { + i_popAddressQueue; + } + + // Transitions from IS_D, IS_D_I + + transition({IS_D, IS_D_I}, {Other_GETS,Other_GET_INSTR}) { + i_popAddressQueue; + } + transition(IS_D, Other_GETX, IS_D_I) { + i_popAddressQueue; + } + transition(IS_D_I, Other_GETX) { + i_popAddressQueue; + } + transition(IS_D, Data, S) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + h_load_hit; + d_deallocateTBE; + j_popDataQueue; + } + + transition(IS_D_I, Data, I) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + h_load_hit; + d_deallocateTBE; + j_popDataQueue; + } + + // Transitions from IM_D, IM_D_O, IM_D_I, IM_D_OI + + transition( IM_D, {Other_GETS,Other_GET_INSTR}, IM_D_O ) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition( IM_D, Other_GETX, IM_D_I ) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition(IM_D_O, {Other_GETS,Other_GET_INSTR} ) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition(IM_D_O, Other_GETX, IM_D_OI) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition( {IM_D_I, IM_D_OI}, {Other_GETS, Other_GET_INSTR, Other_GETX} ) { + i_popAddressQueue; + } + + transition(IM_D, Data, M) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + hh_store_hit; + d_deallocateTBE; + j_popDataQueue; + } + + transition(IM_D_O, Data, O) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + hh_store_hit; + o_cacheToForward; + d_deallocateTBE; + j_popDataQueue; + } + + transition(IM_D_I, Data, I) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + hh_store_hit; + o_cacheToForward; + d_deallocateTBE; + j_popDataQueue; + } + + transition(IM_D_OI, Data, I) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + hh_store_hit; + o_cacheToForward; + d_deallocateTBE; + j_popDataQueue; + } + + // Transitions for SM_D, SM_D_O + + transition(SM_D, {Other_GETS,Other_GET_INSTR}, SM_D_O) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition(SM_D, Other_GETX, IM_D_I) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition(SM_D_O, {Other_GETS,Other_GET_INSTR}) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition(SM_D_O, Other_GETX, IM_D_OI) { + e_recordForwardingInfo; + i_popAddressQueue; + } + + transition(SM_D, Data, M) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + hh_store_hit; + d_deallocateTBE; + j_popDataQueue; + } + + transition(SM_D_O, Data, O) { + s_saveDataInTBE; + w_writeDataFromTBEToCache; + hh_store_hit; + o_cacheToForward; + d_deallocateTBE; + j_popDataQueue; + } + +} diff --git a/src/mem/protocol/MOSI_SMP_bcast_1level.slicc b/src/mem/protocol/MOSI_SMP_bcast_1level.slicc new file mode 100644 index 000000000..d683743a3 --- /dev/null +++ b/src/mem/protocol/MOSI_SMP_bcast_1level.slicc @@ -0,0 +1,4 @@ +MOSI_SMP_bcast-msg.sm +MOSI_SMP_bcast_1level-cache.sm +MOSI_SMP_bcast-dir.sm +standard_1level_SMP-protocol.sm diff --git a/src/mem/protocol/MOSI_SMP_bcast_m-dir.sm b/src/mem/protocol/MOSI_SMP_bcast_m-dir.sm new file mode 100644 index 000000000..e241ea25c --- /dev/null +++ b/src/mem/protocol/MOSI_SMP_bcast_m-dir.sm @@ -0,0 +1,345 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +machine(Directory, "MOSI Broadcast Optimized") { + + + MessageBuffer addressFromDir, network="To", virtual_network="0", ordered="true"; + MessageBuffer dataFromDir, network="To", virtual_network="1", ordered="false"; + + MessageBuffer addressToDir, network="From", virtual_network="0", ordered="true"; + MessageBuffer dataToDir, network="From", virtual_network="1", ordered="false"; + + + enumeration(State, desc="Directory states", default="Directory_State_C") { + C, desc="Cold - no processor has requested this line"; + I, desc="Idle"; + S, desc="Shared"; + SS, desc="Shared, 2 or more shares"; + OS, desc="Owned by a cache"; + OSS, desc="Owned by a cache, present in at least 3 caches"; + M, desc="Modified", format="!b"; + } + + // ** EVENTS ** + + enumeration(Event, desc="Directory events") { + // From Address network + OtherAddress, desc="We saw an address msg to someone else"; + GETS, desc="A GETS arrives"; + GET_INSTR, desc="A GETInstr arrives"; + GETX, desc="A GETX arrives", format="!r"; + PUTX_Owner, desc="A PUTX arrives, requestor is owner"; + PUTX_NotOwner, desc="A PUTX arrives, requestor is not owner", format="!r"; + Memory_Data, desc="Fetched data from memory arrives"; + Memory_Ack, desc="Writeback Ack from memory arrives"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + State DirectoryState, desc="Directory state"; + bool DirOwner, default="true", desc="Is dir owner?"; + MachineID ProcOwner, desc="Processor Owner"; + DataBlock DataBlk, desc="data for the block"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + // to simulate detailed DRAM + external_type(MemoryControl, inport="yes", outport="yes") { + + } + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + MemoryControl memBuffer, constructor_hack="i"; + + void profile_request(int cache_state, State directory_state, GenericRequestType request_type); + + State getState(Address addr) { + if (directory.isPresent(addr)) { + return directory[addr].DirectoryState; + } + return State:C; + } + + void setState(Address addr, State state) { + if (directory.isPresent(addr)) { + directory[addr].DirectoryState := state; + } + } + + // ** OUT_PORTS ** + + out_port(dataNetwork_out, DataMsg, dataFromDir); + out_port(addressNetwork_out, AddressMsg, addressFromDir); + out_port(memQueue_out, MemoryMsg, memBuffer); + + + // ** IN_PORTS ** + + // Address Network + in_port(addressNetwork_in, AddressMsg, addressToDir) { + if (addressNetwork_in.isReady()) { + peek(addressNetwork_in, AddressMsg) { + if(map_Address_to_Directory(in_msg.Address) != machineID) { + trigger(Event:OtherAddress, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GET_INSTR) { + trigger(Event:GET_INSTR, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:PUTX) { + if (in_msg.Requestor == directory[in_msg.Address].ProcOwner && directory[in_msg.Address].DirOwner == false) { + trigger(Event:PUTX_Owner, in_msg.Address); + } else { + trigger(Event:PUTX_NotOwner, in_msg.Address); + } + } else { + error("unexpected message"); + } + } + } + } + + // off-chip memory request/response is done + in_port(memQueue_in, MemoryMsg, memBuffer) { + if (memQueue_in.isReady()) { + peek(memQueue_in, MemoryMsg) { + if (in_msg.Type == MemoryRequestType:MEMORY_READ) { + trigger(Event:Memory_Data, in_msg.Address); + } else if (in_msg.Type == MemoryRequestType:MEMORY_WB) { + trigger(Event:Memory_Ack, in_msg.Address); + } else { + DEBUG_EXPR(in_msg.Type); + error("Invalid message"); + } + } + } + } + + // *** ACTIONS *** + + action(d_sendDataMsg, "d", desc="Send data message to requestor") { + peek(memQueue_in, MemoryMsg) { + enqueue(dataNetwork_out, DataMsg, latency="1") { + out_msg.Address := in_msg.Address; + out_msg.Sender := machineID; + out_msg.Destination.add(in_msg.OriginalRequestorMachId); + out_msg.DestMachine := MachineType:L1Cache; + //out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + DEBUG_EXPR(in_msg.OriginalRequestorMachId); + DEBUG_EXPR(out_msg.DataBlk); + } + } + } + + action(j_popAddressQueue, "j", desc="Pop address queue.") { + addressNetwork_in.dequeue(); + } + + action(l_popMemQueue, "q", desc="Pop off-chip request queue") { + memQueue_in.dequeue(); + } + + action(p_profile, "p", desc="Profile this transition.") { + peek(addressNetwork_in, AddressMsg) { + profile_request(in_msg.CacheState, getState(address), convertToGenericType(in_msg.Type)); + } + } + + action(m_setOwnerRequestor, "m", desc="Set owner = requestor") { + peek(addressNetwork_in, AddressMsg) { + directory[in_msg.Address].ProcOwner := in_msg.Requestor; + directory[in_msg.Address].DirOwner := false; + } + } + + action(qf_queueMemoryFetchRequest, "qf", desc="Queue off-chip fetch request") { + peek(addressNetwork_in, AddressMsg) { + enqueue(memQueue_out, MemoryMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := MemoryRequestType:MEMORY_READ; + out_msg.Sender := machineID; + out_msg.OriginalRequestorMachId := in_msg.Requestor; + out_msg.DataBlk := directory[in_msg.Address].DataBlk; + out_msg.MessageSize := in_msg.MessageSize; + //out_msg.Prefetch := in_msg.Prefetch; + DEBUG_EXPR(out_msg); + } + } + } + + action(qw_queueMemoryWBRequest, "qw", desc="Queue off-chip writeback request") { + peek(addressNetwork_in, AddressMsg) { + enqueue(memQueue_out, MemoryMsg, latency="1") { + out_msg.Address := address; + out_msg.Type := MemoryRequestType:MEMORY_WB; + out_msg.Sender := machineID; + out_msg.OriginalRequestorMachId := in_msg.Requestor; + out_msg.DataBlk := in_msg.DataBlk; + out_msg.MessageSize := in_msg.MessageSize; + //out_msg.Prefetch := in_msg.Prefetch; + DEBUG_EXPR(out_msg); + } + } + } + + action(r_writeDataFromRequest, "r", desc="Write request data to memory") { + peek(addressNetwork_in, AddressMsg) { + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + action(x_setOwnerToDirectory, "x", desc="Set owner equal to the directory"){ + peek(addressNetwork_in, AddressMsg) { + directory[in_msg.Address].DirOwner := true; + } + } + + // TRANSITIONS + + // Ignore all address and data messages not bound for us + transition(C, OtherAddress) { + j_popAddressQueue; + } + + // PUTX_NotOwner Transitions + transition({I, S, SS, OS, OSS, M}, PUTX_NotOwner) { + p_profile; + j_popAddressQueue; + } + + // Transitions from Idle + transition({C, I}, {GETS,GET_INSTR}, S) { + //d_sendDataMsg; + qf_queueMemoryFetchRequest; + p_profile; + j_popAddressQueue; + } + + transition({C, I}, GETX, M) { + //d_sendDataMsg; + qf_queueMemoryFetchRequest; + m_setOwnerRequestor; + p_profile; + j_popAddressQueue + } + + // Transitions from Shared + transition({S, SS}, {GETS,GET_INSTR}, SS) { + //d_sendDataMsg; + qf_queueMemoryFetchRequest; + p_profile; + j_popAddressQueue; + } + + transition({S, SS}, GETX, M) { + //d_sendDataMsg; + qf_queueMemoryFetchRequest; + m_setOwnerRequestor; + p_profile; + j_popAddressQueue; + } + + // Transitions from Owned + transition({OS, OSS}, {GETS,GET_INSTR}, OSS) { + p_profile; + j_popAddressQueue; + } + + transition({OS, OSS}, GETX, M) { + m_setOwnerRequestor; + p_profile; + j_popAddressQueue; + } + + transition(OS, PUTX_Owner, S) { + x_setOwnerToDirectory; + r_writeDataFromRequest; + qw_queueMemoryWBRequest; + p_profile; + j_popAddressQueue; + } + + transition(OSS, PUTX_Owner, SS) { + x_setOwnerToDirectory; + r_writeDataFromRequest; + qw_queueMemoryWBRequest; + p_profile; + j_popAddressQueue; + } + + // Transitions from Modified + transition(M, {GETS,GET_INSTR}, OS) { + p_profile; + j_popAddressQueue; + } + + transition(M, GETX) { + m_setOwnerRequestor; + p_profile; + j_popAddressQueue; + } + + transition(M, PUTX_Owner, I) { + x_setOwnerToDirectory; + r_writeDataFromRequest; + qw_queueMemoryWBRequest; + p_profile; + j_popAddressQueue; + } + + transition({C, I, S, SS, OS, OSS, M}, Memory_Data) { + d_sendDataMsg; + l_popMemQueue; + } + + transition({C, I, S, SS, OS, OSS, M}, Memory_Ack) { + //a_sendAck; + l_popMemQueue; + } + +} diff --git a/src/mem/protocol/MOSI_SMP_bcast_m.slicc b/src/mem/protocol/MOSI_SMP_bcast_m.slicc new file mode 100644 index 000000000..090df74d9 --- /dev/null +++ b/src/mem/protocol/MOSI_SMP_bcast_m.slicc @@ -0,0 +1,4 @@ +MOSI_SMP_bcast-msg.sm +MOSI_SMP_bcast-cache.sm +MOSI_SMP_bcast_m-dir.sm +standard_SMP-protocol.sm diff --git a/src/mem/protocol/MOSI_SMP_directory_1level-cache.sm b/src/mem/protocol/MOSI_SMP_directory_1level-cache.sm new file mode 100644 index 000000000..f780bb473 --- /dev/null +++ b/src/mem/protocol/MOSI_SMP_directory_1level-cache.sm @@ -0,0 +1,838 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOSI_directory_1level-cache.sm 1.18 04/09/07 13:52:52-05:00 mikem@maya.cs.wisc.edu $ + * + */ + +machine(L1Cache, "MOSI Directory Optimized") { + + MessageBuffer requestFromCache, network="To", virtual_network="0", ordered="false"; + MessageBuffer responseFromCache, network="To", virtual_network="2", ordered="false"; + + MessageBuffer forwardedRequestToCache, network="From", virtual_network="1", ordered="true"; + MessageBuffer responseToCache, network="From", virtual_network="2", ordered="false"; + + // STATES + enumeration(State, desc="Cache states", default="L1Cache_State_I") { + // Base states + I, desc="Idle"; + S, desc="Shared"; + O, desc="Owned"; + M, desc="Modified", format="!b"; + + // Transient States + MI, desc="modified, issued PUTX, have not seen response yet"; + OI, desc="owned, issued PUTX, have not seen response yet"; + + IS, desc="idle, issued GETS, have not seen response yet"; + ISI, desc="idle, issued GETS, saw INV, have not seen data for GETS yet", format="!b"; + + IM, desc="idle, issued GETX, have not seen response yet"; + IMI, desc="idle, issued GETX, saw forwarded GETX"; + IMO, desc="idle, issued GETX, saw forwarded GETS"; + IMOI, desc="idle, issued GETX, saw forwarded GETS, saw forwarded GETX"; + + // Note: OM is a strange state, because it is waiting for the line + // to be stolen away, or look like it has been stolen away. The + // common case is that we see a forward from the directory that is + // really from us, we forwarded the data to our dataqueue, and + // everythings works fine. + + OM, desc="owned, issued GETX, have not seen response yet"; + } + + // EVENTS + enumeration(Event, desc="Cache events") { + Load, desc="Load request from the processor"; + Load_prefetch, desc="Load prefetch request from the processor"; + Ifetch, desc="I-fetch request from the processor"; + Store_prefetch, desc="Store prefetch request from the processor"; + Store, desc="Store request from the processor"; + Replacement, desc="Replacement", format="!r"; + + Forwarded_GETS, "Forwarded GETS", desc="Directory forwards GETS to us"; + Forwarded_GETX, "Forwarded GETX", desc="Directory forwards GETX to us"; + INV, "INV", desc="Invalidation", format="!r"; + + Proc_ack, "Proc ack", desc="Ack from proc"; + Proc_last_ack, "Proc last ack", desc="Last ack", format="!r"; + + Data_ack_0, "Data ack 0", desc="Data with ack count = 0"; + Data_ack_not_0, "Data ack not 0", desc="Data with ack count != 0 (but haven't seen all acks first"; + Data_ack_not_0_last, "Data ack not 0 last", desc="Data with ack count != 0 after having received all acks"; + + Dir_WB_ack, "WB ack", desc="Writeback ack from dir"; + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + DataBlock DataBlk, desc="data for the block"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="Buffer for the data block"; + int NumPendingAcks, desc="Number of acks that this processor is waiting for"; + NetDest ForwardGetS_IDs, desc="Set of the processors to forward the block"; + MachineID ForwardGetX_ID, desc="ID of the processor to forward the block"; + int ForwardGetX_AckCount, desc="Number of acks the GetX we are forwarded needs"; + bool isPrefetch, desc="Set if this was caused by a prefetch"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + MessageBuffer mandatoryQueue, ordered="false", abstract_chip_ptr="true"; + MessageBuffer optionalQueue, ordered="false", abstract_chip_ptr="true"; + Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i"; + StoreBuffer storeBuffer, abstract_chip_ptr="true", constructor_hack="i"; + + TBETable TBEs, template_hack=""; + CacheMemory cacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_unified L1"', abstract_chip_ptr="true"; + + State getState(Address addr) { + if(TBEs.isPresent(addr)) { + return TBEs[addr].TBEState; + } else if (cacheMemory.isTagPresent(addr)) { + return cacheMemory[addr].CacheState; + } + return State:I; + } + + void setState(Address addr, State state) { + if (TBEs.isPresent(addr)) { + TBEs[addr].TBEState := state; + } + if (cacheMemory.isTagPresent(addr)) { + cacheMemory[addr].CacheState := state; + + // Set permission + if ((state == State:I) || (state == State:MI) || (state == State:OI)) { + cacheMemory.changePermission(addr, AccessPermission:Invalid); + } else if (state == State:S || state == State:O) { + cacheMemory.changePermission(addr, AccessPermission:Read_Only); + } else if (state == State:M) { + cacheMemory.changePermission(addr, AccessPermission:Read_Write); + } else { + cacheMemory.changePermission(addr, AccessPermission:Busy); + } + } + } + + // ** OUT_PORTS ** + + out_port(requestNetwork_out, RequestMsg, requestFromCache); + out_port(responseNetwork_out, ResponseMsg, responseFromCache); + + // ** IN_PORTS ** + + // Response Network + in_port(responseNetwork_in, ResponseMsg, responseToCache) { + if (responseNetwork_in.isReady()) { + peek(responseNetwork_in, ResponseMsg) { + if(in_msg.Type == CoherenceResponseType:DATA) { + if(in_msg.NumPendingAcks == 0) { + trigger(Event:Data_ack_0, in_msg.Address); + } else { + if(in_msg.NumPendingAcks + TBEs[in_msg.Address].NumPendingAcks != 0) { + trigger(Event:Data_ack_not_0, in_msg.Address); + } else { + trigger(Event:Data_ack_not_0_last, in_msg.Address); + } + } + } else if(in_msg.Type == CoherenceResponseType:ACK) { + if(TBEs[in_msg.Address].NumPendingAcks != 1){ + trigger(Event:Proc_ack, in_msg.Address); + } else { + trigger(Event:Proc_last_ack, in_msg.Address); + } + } + } + } + } + + // Forwarded Request network + in_port(forwardedRequestNetwork_in, RequestMsg, forwardedRequestToCache) { + if(forwardedRequestNetwork_in.isReady()) { + peek(forwardedRequestNetwork_in, RequestMsg) { + if(in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:Forwarded_GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:Forwarded_GETX, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:INV) { + trigger(Event:INV, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:WB_ACK) { + trigger(Event:Dir_WB_ack, in_msg.Address); + } else { + error("Invalid forwarded request type"); + } + } + } + } + + // Mandatory Queue + in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { + if (mandatoryQueue_in.isReady()) { + peek(mandatoryQueue_in, CacheMsg) { + if (cacheMemory.cacheAvail(in_msg.Address) == false) { + trigger(Event:Replacement, cacheMemory.cacheProbe(in_msg.Address)); + } else { + if (in_msg.Type == CacheRequestType:LD) { + trigger(Event:Load, in_msg.Address); + } else if (in_msg.Type == CacheRequestType:IFETCH) { + trigger(Event:Ifetch, in_msg.Address); + } else if ((in_msg.Type == CacheRequestType:ST) || (in_msg.Type == CacheRequestType:ATOMIC)) { + trigger(Event:Store, in_msg.Address); + } else { + error("Invalid CacheRequestType"); + } + } + } + } + } + + // Optional Queue + in_port(optionalQueue_in, CacheMsg, optionalQueue, desc="...") { + if (optionalQueue_in.isReady()) { + peek(optionalQueue_in, CacheMsg) { + if (cacheMemory.cacheAvail(in_msg.Address) == false) { + trigger(Event:Replacement, cacheMemory.cacheProbe(in_msg.Address)); + } else { + if (in_msg.Type == CacheRequestType:LD) { + trigger(Event:Load_prefetch, in_msg.Address); + } else if (in_msg.Type == CacheRequestType:IFETCH) { + trigger(Event:Load_prefetch, in_msg.Address); + } else if ((in_msg.Type == CacheRequestType:ST) || (in_msg.Type == CacheRequestType:ATOMIC)) { + trigger(Event:Store_prefetch, in_msg.Address); + } else { + error("Invalid CacheRequestType"); + } + } + } + } + } + + // ACTIONS + + action(a_issueGETS, "a", desc="Issue GETS") { + enqueue(requestNetwork_out, RequestMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(b_issueGETX, "b", desc="Issue GETX") { + enqueue(requestNetwork_out, RequestMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(d_issuePUTX, "d", desc="Issue PUTX") { + enqueue(requestNetwork_out, RequestMsg, latency="ISSUE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTX; + out_msg.Requestor := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.DataBlk := cacheMemory[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } + + action(e_dataFromCacheToRequestor, "e", desc="Send data from cache to requestor") { + peek(forwardedRequestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="CACHE_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.NumPendingAcks := in_msg.NumPendingAcks; // Needed when in state O and we see a GetX + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := cacheMemory[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + action(g_allocateCacheBlock, "g", desc="Allocate cache block") { + if (cacheMemory.isTagPresent(address) == false) { + cacheMemory.allocate(address); + } + } + + action(h_load_hit, "h", desc="If not prefetch, notify sequencer the load completed.") { + DEBUG_EXPR(cacheMemory[address].DataBlk); + if((TBEs.isPresent(address) == false) || (TBEs[address].isPrefetch == false)) { + // Non-prefetch + sequencer.readCallback(address, cacheMemory[address].DataBlk); + } else { + // Prefetch - don't call back + } + } + + action(hh_store_hit, "\h", desc="If not prefetch, notify sequencer that store completed.") { + DEBUG_EXPR(cacheMemory[address].DataBlk); + if((TBEs.isPresent(address) == false) || (TBEs[address].isPrefetch == false)) { + // Non-prefetch + sequencer.writeCallback(address, cacheMemory[address].DataBlk); + } else { + // Prefetch - don't call back + } + } + + action(i_allocateTBE, "i", desc="Allocate TBE (isPrefetch=0, number of invalidates=0)") { + check_allocate(TBEs); + TBEs.allocate(address); + TBEs[address].NumPendingAcks := 0; // default value + TBEs[address].isPrefetch := false; + TBEs[address].ForwardGetS_IDs.clear(); + } + + action(j_setPrefetchBit, "j", desc="Set prefetch bit") { + TBEs[address].isPrefetch := true; + } + + action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") { + mandatoryQueue_in.dequeue(); + } + + action(l_popForwardedRequestQueue, "l", desc="Pop incoming forwarded request queue") { + forwardedRequestNetwork_in.dequeue(); + } + + action(m_popOptionalQueue, "m", desc="Pop optional queue") { + optionalQueue_in.dequeue(); + } + + action(o_popIncomingResponseQueue, "o", desc="Pop Incoming Response queue") { + responseNetwork_in.dequeue(); + } + + action(p_addNumberOfPendingAcks, "p", desc="Add number of pending acks to TBE") { + peek(responseNetwork_in, ResponseMsg) { + DEBUG_EXPR(TBEs[address].NumPendingAcks); + TBEs[address].NumPendingAcks := TBEs[address].NumPendingAcks + in_msg.NumPendingAcks; + DEBUG_EXPR(in_msg.NumPendingAcks); + DEBUG_EXPR(TBEs[address].NumPendingAcks); + } + } + + action(q_decrementNumberOfPendingAcks, "q", desc="Decrement number of pending invalidations by one") { + DEBUG_EXPR(TBEs[address].NumPendingAcks); + TBEs[address].NumPendingAcks := TBEs[address].NumPendingAcks - 1; + DEBUG_EXPR(TBEs[address].NumPendingAcks); + } + + action(s_deallocateTBE, "s", desc="Deallocate TBE") { + TBEs.deallocate(address); + } + + action(t_sendAckToInvalidator, "t", desc="Send ack to invalidator") { + peek(forwardedRequestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + DEBUG_EXPR(out_msg.Destination); + out_msg.NumPendingAcks := 0; + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + + action(u_writeDataToCache, "u", desc="Write data to cache") { + peek(responseNetwork_in, ResponseMsg) { + cacheMemory[address].DataBlk := in_msg.DataBlk; + } + } + + action(x_copyDataFromCacheToTBE, "x", desc="Copy data from cache to TBE") { + TBEs[address].DataBlk := cacheMemory[address].DataBlk; + } + + action(y_dataFromTBEToRequestor, "y", desc="Send data from TBE to requestor") { + peek(forwardedRequestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.NumPendingAcks := in_msg.NumPendingAcks; // Needed when in state MS and we see a GetX + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := TBEs[address].DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + action(z_stall, "z", desc="Stall") { + } + + action(dd_recordGetSForwardID, "\d", desc="Record forwarded GetS for future forwarding") { + peek(forwardedRequestNetwork_in, RequestMsg) { + TBEs[address].ForwardGetS_IDs.add(in_msg.Requestor); + } + } + + action(ee_dataFromCacheToGetSForwardIDs, "\e", desc="Send data from cache to GetS ForwardIDs") { + // FIXME - In some cases this should be from the TBE, not the cache. + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination := TBEs[address].ForwardGetS_IDs; + out_msg.DestMachine := MachineType:L1Cache; + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := cacheMemory[address].DataBlk; + out_msg.NumPendingAcks := 0; + out_msg.MessageSize := MessageSizeType:Data; + } + } + + action(ff_deallocateCacheBlock, "\f", desc="Deallocate cache block. Sets the cache to invalid, allowing a replacement in parallel with a fetch.") { + cacheMemory.deallocate(address); + } + + action(gg_dataFromCacheToGetXForwardID, "\g", desc="Send data from cache to GetX ForwardID") { + // FIXME - In some cases this should be from the TBE, not the cache. + enqueue(responseNetwork_out, ResponseMsg, latency="NULL_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:L1Cache; + out_msg.Destination.add(TBEs[address].ForwardGetX_ID); + out_msg.DestMachine := MachineType:L1Cache; + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := cacheMemory[address].DataBlk; + out_msg.NumPendingAcks := TBEs[address].ForwardGetX_AckCount; + out_msg.MessageSize := MessageSizeType:Data; + } + } + + action(ii_recordGetXForwardID, "\i", desc="Record forwarded GetX and ack count for future forwarding") { + peek(forwardedRequestNetwork_in, RequestMsg) { + TBEs[address].ForwardGetX_ID := in_msg.Requestor; + TBEs[address].ForwardGetX_AckCount := in_msg.NumPendingAcks; + } + } + + //***************************************************** + // TRANSITIONS + //***************************************************** + + // Transitions for Load/Store/Prefetch/Replacement from transient states + transition({OM, OI, IS, ISI, IM, IMO, IMOI, IMI, MI}, {Load, Load_prefetch, Ifetch, Store, Store_prefetch, Replacement}) { + z_stall; + } + + // Transitions from Idle + transition(I, {Load,Ifetch}, IS) { + g_allocateCacheBlock; + i_allocateTBE; + a_issueGETS; + k_popMandatoryQueue; + } + + transition(I, {Load_prefetch}, IS) { + g_allocateCacheBlock; + i_allocateTBE; + j_setPrefetchBit; + a_issueGETS; + m_popOptionalQueue; + } + + transition(I, Store, IM) { + g_allocateCacheBlock; + i_allocateTBE; + b_issueGETX; + k_popMandatoryQueue; + } + + transition(I, Store_prefetch, IM) { + g_allocateCacheBlock; + i_allocateTBE; + j_setPrefetchBit; + b_issueGETX; + m_popOptionalQueue; + } + + transition(I, Replacement) { + ff_deallocateCacheBlock; + } + + transition(I, INV) { + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + // Transitions from Shared + transition({S, O}, {Load,Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition({S, O, M}, Load_prefetch) { + m_popOptionalQueue; + } + + transition(S, Store, IM) { + i_allocateTBE; + b_issueGETX; + k_popMandatoryQueue; + } + + transition(S, Store_prefetch, IM) { + i_allocateTBE; + j_setPrefetchBit; + b_issueGETX; + m_popOptionalQueue; + } + + transition(S, Replacement, I) { + ff_deallocateCacheBlock; + } + + transition(S, INV, I) { + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + // Transitions from Modified + transition(M, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(M, Store) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(M, Store_prefetch) { + m_popOptionalQueue; + } + + transition(M, Replacement, MI) { + i_allocateTBE; + d_issuePUTX; + x_copyDataFromCacheToTBE; + ff_deallocateCacheBlock; + } + + transition(M, Forwarded_GETS, O) { + e_dataFromCacheToRequestor; + l_popForwardedRequestQueue; + } + + transition(M, Forwarded_GETX, I) { + e_dataFromCacheToRequestor; + l_popForwardedRequestQueue; + } + + // Transitions from O + transition(O, Store, OM) { + i_allocateTBE; + b_issueGETX; + k_popMandatoryQueue; + } + + transition(O, Store_prefetch, OM) { + i_allocateTBE; + j_setPrefetchBit; + b_issueGETX; + m_popOptionalQueue; + } + + transition(O, Replacement, OI){ + i_allocateTBE; + d_issuePUTX; + x_copyDataFromCacheToTBE; + ff_deallocateCacheBlock; + } + + transition(O, Forwarded_GETS) { + e_dataFromCacheToRequestor; + l_popForwardedRequestQueue; + } + + transition(O, Forwarded_GETX, I) { + e_dataFromCacheToRequestor; + l_popForwardedRequestQueue; + } + + // transitions from OI + + transition(OI, Forwarded_GETS) { + y_dataFromTBEToRequestor; + l_popForwardedRequestQueue; + } + + transition(OI, Forwarded_GETX) { + y_dataFromTBEToRequestor; + l_popForwardedRequestQueue; + } + + transition(OI, Dir_WB_ack, I) { + s_deallocateTBE; + l_popForwardedRequestQueue; + } + + // Transitions from IS + + transition(IS, INV, ISI) { + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + transition(IS, Data_ack_0, S) { + u_writeDataToCache; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // transitions from ISI + + // in ISI, could get data from the Proc whose GETX caused INV to go from IS to ISI + // or, could get data from Dir if Dir's data lost race to Dir's INV + // or, could get data from Dir, if my GETS took forever to get to Dir, and the GETX + // processor already wrote it back + + transition(ISI, Data_ack_0, I) { + u_writeDataToCache; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(ISI, INV) { + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + // Transitions from IM + + transition(IM, INV) { // do not need to go to IMI, since INV is for earlier epoch + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + transition({IM, IMO}, Forwarded_GETS, IMO) { + dd_recordGetSForwardID; + l_popForwardedRequestQueue; + } + + transition(IM, Forwarded_GETX, IMI) { + ii_recordGetXForwardID; + l_popForwardedRequestQueue; + } + + transition(IM, {Data_ack_0, Data_ack_not_0_last}, M) { + u_writeDataToCache; + hh_store_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IM, Data_ack_not_0) { + u_writeDataToCache; + p_addNumberOfPendingAcks; + o_popIncomingResponseQueue; + } + + transition(IM, Proc_ack) { + q_decrementNumberOfPendingAcks; + o_popIncomingResponseQueue; + } + + transition(IM, Proc_last_ack, M) { + hh_store_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // transitions from IMO + + transition(IMO, Forwarded_GETX, IMOI) { + ii_recordGetXForwardID; + l_popForwardedRequestQueue; + } + + transition(IMO, {Data_ack_0, Data_ack_not_0_last}, O) { + u_writeDataToCache; + hh_store_hit; + ee_dataFromCacheToGetSForwardIDs; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IMO, Data_ack_not_0) { + u_writeDataToCache; + p_addNumberOfPendingAcks; + o_popIncomingResponseQueue; + } + + transition(IMO, Proc_ack) { + q_decrementNumberOfPendingAcks; + o_popIncomingResponseQueue; + } + + transition(IMO, Proc_last_ack, O) { + hh_store_hit; + ee_dataFromCacheToGetSForwardIDs; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // transitions from IMI + + transition(IMI, {Data_ack_0, Data_ack_not_0_last}, I) { + u_writeDataToCache; + hh_store_hit; + gg_dataFromCacheToGetXForwardID; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IMI, Data_ack_not_0) { + u_writeDataToCache; + p_addNumberOfPendingAcks; + o_popIncomingResponseQueue; + } + + transition(IMI, Proc_ack) { + q_decrementNumberOfPendingAcks; + o_popIncomingResponseQueue; + } + + transition(IMI, Proc_last_ack, I) { + hh_store_hit; + gg_dataFromCacheToGetXForwardID; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // transitions from IMOI + + transition(IMOI, {Data_ack_0, Data_ack_not_0_last}, I) { + u_writeDataToCache; + hh_store_hit; + ee_dataFromCacheToGetSForwardIDs; + gg_dataFromCacheToGetXForwardID; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(IMOI, Data_ack_not_0) { + u_writeDataToCache; + p_addNumberOfPendingAcks; + o_popIncomingResponseQueue; + } + + transition(IMOI, Proc_ack) { + q_decrementNumberOfPendingAcks; + o_popIncomingResponseQueue; + } + + transition(IMOI, Proc_last_ack, I) { + hh_store_hit; + ee_dataFromCacheToGetSForwardIDs; + gg_dataFromCacheToGetXForwardID; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // Transitions from OM + transition(OM, Proc_ack) { + q_decrementNumberOfPendingAcks; + o_popIncomingResponseQueue; + } + + transition(OM, Forwarded_GETS) { + e_dataFromCacheToRequestor; + l_popForwardedRequestQueue; + } + + transition(OM, Forwarded_GETX, IM) { + e_dataFromCacheToRequestor; + l_popForwardedRequestQueue; + } + + // Transitions from MI + + transition(MI, Forwarded_GETS) { + y_dataFromTBEToRequestor; + l_popForwardedRequestQueue; + } + + transition(MI, Forwarded_GETX) { + y_dataFromTBEToRequestor; + l_popForwardedRequestQueue; + } + + transition(MI, Dir_WB_ack, I) { + s_deallocateTBE; + l_popForwardedRequestQueue; + } +} diff --git a/src/mem/protocol/MOSI_SMP_directory_1level-dir.sm b/src/mem/protocol/MOSI_SMP_directory_1level-dir.sm new file mode 100644 index 000000000..fa91b1baa --- /dev/null +++ b/src/mem/protocol/MOSI_SMP_directory_1level-dir.sm @@ -0,0 +1,333 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOSI_directory-dir.sm 1.14 04/09/07 13:52:52-05:00 mikem@maya.cs.wisc.edu $ + */ + +machine(Directory, "MOSI Directory Optimized") { + + + MessageBuffer forwardedRequestFromDir, network="To", virtual_network="1", ordered="true"; + MessageBuffer responseFromDir, network="To", virtual_network="2", ordered="false"; + MessageBuffer requestToDir, network="From", virtual_network="0", ordered="false"; + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_I") { + // Base states + I, desc="Idle"; + S, desc="Shared"; + O, desc="Owned"; + M, desc="Modified", format="!b"; + } + + // Events + enumeration(Event, desc="Directory events") { + GETS, desc="A GETS arrives"; + GETX_Owner, desc="A GETX arrives, requestor is owner"; + GETX_NotOwner, desc="A GETX arrives, requestor is not owner"; + PUTX_Owner, "PUTX (requestor is owner)", desc="A PUTX arrives, requestor is owner"; + PUTX_NotOwner, "PUTX (requestor not owner)",desc="A PUTX arrives, requestor is not owner"; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + State DirectoryState, desc="Directory state"; + NetDest Sharers, desc="Set of sharers"; + bool DirOwner, default="true", desc="Is dir owner?"; + MachineID ProcOwner, desc="Processor owner"; + DataBlock DataBlk, desc="data for the block"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + + State getState(Address addr) { + if (directory.isPresent(addr)) { + return directory[addr].DirectoryState; + } + return State:I; + } + + void setState(Address addr, State state) { + if (directory.isPresent(addr)) { + directory[addr].DirectoryState := state; + } + } + + // ** OUT_PORTS ** + out_port(forwardedRequestNetwork_out, RequestMsg, forwardedRequestFromDir); + out_port(responseNetwork_out, ResponseMsg, responseFromDir); + + // ** IN_PORTS ** + + in_port(requestNetwork_in, RequestMsg, requestToDir) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + if(directory[in_msg.Address].DirOwner == false && in_msg.Requestor == directory[in_msg.Address].ProcOwner) { + trigger(Event:GETX_Owner, in_msg.Address); + } else { + trigger(Event:GETX_NotOwner, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:PUTX) { + if (directory[in_msg.Address].DirOwner == false && in_msg.Requestor == directory[in_msg.Address].ProcOwner) { + trigger(Event:PUTX_Owner, in_msg.Address); + } else { + trigger(Event:PUTX_NotOwner, in_msg.Address); + } + } else { + error("Invalid message"); + } + } + } + } + + + // Actions + + // a_addRequestorToSharers + + action(a_addRequestorToSharers, "a", desc="Add requestor to list of sharers") { + peek(requestNetwork_in, RequestMsg) { + directory[address].Sharers.add(in_msg.Requestor); + DEBUG_EXPR(directory[address].Sharers); + } + } + + // b_dataToRequestor + + action(b_dataToRequestor, "b", desc="Send data to requestor") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.Sender := machineID; + out_msg.SenderMachine := MachineType:Directory; + if(in_msg.Type == CoherenceRequestType:GETX) { + out_msg.NumPendingAcks := directory[address].Sharers.count(); + } else { + out_msg.NumPendingAcks := 0; // don't need to send pending ack count to GETS requestor + } + out_msg.Destination.add(in_msg.Requestor); + out_msg.DestMachine := MachineType:L1Cache; + out_msg.DataBlk := directory[address].DataBlk; + DEBUG_EXPR(out_msg.NumPendingAcks); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + // d_forwardRequestToOwner + + action(d_forwardRequestToOwner, "d", desc="Forward request to owner") { + peek(requestNetwork_in, RequestMsg) { + enqueue(forwardedRequestNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination.add(directory[address].ProcOwner); + DEBUG_EXPR(out_msg.Destination); + + if(in_msg.Type == CoherenceRequestType:GETX) { + out_msg.NumPendingAcks := directory[address].Sharers.count(); + } else { + out_msg.NumPendingAcks := 0; // don't need to send pending ack count to GETS requestor + } + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + + action(f_setOwnerToRequestor, "f", desc="Set owner equal to requestor") { + peek(requestNetwork_in, RequestMsg) { + directory[address].ProcOwner := in_msg.Requestor; + directory[address].DirOwner := false; + } + DEBUG_EXPR(directory[address].ProcOwner); + } + + action(g_clearSharers, "g", desc="Clear list of sharers") { + directory[address].Sharers.clear(); + } + + // currently done via multicast message + + action(h_invToSharers, "h", desc="Send INVs to all sharers") { + peek(requestNetwork_in, RequestMsg) { + if(directory[address].Sharers.count() != 0){ + enqueue(forwardedRequestNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.Requestor := in_msg.Requestor; + out_msg.Destination := directory[address].Sharers; + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + DEBUG_EXPR(directory[address].Sharers); + } + + action(j_popIncomingRequestQueue, "j", desc="Pop incoming request queue") { + requestNetwork_in.dequeue(); + } + + action(l_writeRequestDataToMemory, "l", desc="Write PUTX/DWN data to memory") { + peek(requestNetwork_in, RequestMsg) { + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + action(n_writebackAckToRequestor, "n", desc="Send WB_ack to requestor") { + peek(requestNetwork_in, RequestMsg) { + // This needs to be DIRECTORY_LATENCY to keep the queue fifo + enqueue(forwardedRequestNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:WB_ACK; + out_msg.Requestor := machineID; + out_msg.Destination.add(in_msg.Requestor); + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + + action(p_clearOwner, "p", desc="Clear owner") { + directory[address].DirOwner := true; // set owner equal to dir + } + + action(r_addOwnerToSharers, "r", desc="Add owner to list of sharers") { + directory[address].Sharers.add(directory[address].ProcOwner); + } + + action(t_removeOwnerFromSharers, "t", desc="Remove owner from list of sharers") { + directory[address].Sharers.remove(directory[address].ProcOwner); + } + + action(u_removeRequestorFromSharers, "u", desc="Remove requestor from list of sharers") { + peek(requestNetwork_in, RequestMsg) { + directory[address].Sharers.remove(in_msg.Requestor); + } + } + + // TRANSITIONS + + transition({I, S, M, O}, PUTX_NotOwner) { + n_writebackAckToRequestor; + j_popIncomingRequestQueue; + } + + // Transitions from Idle + transition(I, GETS, S) { + a_addRequestorToSharers; + b_dataToRequestor; + j_popIncomingRequestQueue; + } + + transition(I, GETX_NotOwner, M) { + f_setOwnerToRequestor; + b_dataToRequestor; + j_popIncomingRequestQueue; + } + + // Transitions from Shared + transition(S, GETS) { + a_addRequestorToSharers; + b_dataToRequestor; + j_popIncomingRequestQueue; + } + + transition(S, GETX_NotOwner, M) { + u_removeRequestorFromSharers; + b_dataToRequestor; + f_setOwnerToRequestor; + h_invToSharers; + g_clearSharers; + j_popIncomingRequestQueue; + } + + // Transitions from Owned + transition(O, GETS) { + a_addRequestorToSharers; + d_forwardRequestToOwner; + j_popIncomingRequestQueue; + } + + transition(O, {GETX_NotOwner, GETX_Owner}, M) { + u_removeRequestorFromSharers; + t_removeOwnerFromSharers; + d_forwardRequestToOwner; + f_setOwnerToRequestor; + h_invToSharers; + g_clearSharers; + j_popIncomingRequestQueue; + } + + transition(O, PUTX_Owner, S) { + u_removeRequestorFromSharers; + l_writeRequestDataToMemory; + n_writebackAckToRequestor; + p_clearOwner; + j_popIncomingRequestQueue; + } + + + // Transitions from Modified + transition(M, GETS, O) { + a_addRequestorToSharers; + r_addOwnerToSharers; + d_forwardRequestToOwner; + j_popIncomingRequestQueue; + } + + transition(M, GETX_NotOwner) { + d_forwardRequestToOwner; + f_setOwnerToRequestor; + j_popIncomingRequestQueue; + } + + transition(M, PUTX_Owner, I) { + l_writeRequestDataToMemory; + n_writebackAckToRequestor; + p_clearOwner; + j_popIncomingRequestQueue; + } +} diff --git a/src/mem/protocol/MOSI_SMP_directory_1level-msg.sm b/src/mem/protocol/MOSI_SMP_directory_1level-msg.sm new file mode 100644 index 000000000..b827ab05e --- /dev/null +++ b/src/mem/protocol/MOSI_SMP_directory_1level-msg.sm @@ -0,0 +1,74 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id: MOSI_directory-msg.sm 1.9 04/08/09 16:11:38-05:00 mikem@maya.cs.wisc.edu $ + * + */ + +// CoherenceRequestType +enumeration(CoherenceRequestType, desc="...") { + GETX, desc="Get eXclusive"; + GETS, desc="Get Shared"; + GET_INSTR, desc="Get Instruction"; + PUTX, desc="Put eXclusive"; + INV, desc="INValidate"; + WB_ACK, desc="Write Back ACKnowledgment"; +} + +// CoherenceResponseType +enumeration(CoherenceResponseType, desc="...") { + ACK, desc="ACKnowledgment"; + NACK, desc="Negative ACKnowledgment"; + DATA, desc="Data"; +} + +// RequestMsg +structure(RequestMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)"; + MachineID Requestor, desc="Node who initiated the request"; + NetDest Destination, desc="Multicast destination mask"; + DataBlock DataBlk, desc="Data for the cache line (if PUTX)"; + int NumPendingAcks, desc="Number of acks to wait for"; // Needed for forwarded responses only + MessageSizeType MessageSize, desc="size category of the message"; +} + +// ResponseMsg +structure(ResponseMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)"; + MachineID Sender, desc="Node who sent the data"; + MachineType SenderMachine, desc="What component sent the data"; + NetDest Destination, desc="Node to whom the data is sent"; + MachineType DestMachine, desc="What component receives the data"; + DataBlock DataBlk, desc="data for the cache line"; + int NumPendingAcks, desc="Number of acks to wait for"; + MessageSizeType MessageSize, desc="size category of the message"; +} diff --git a/src/mem/protocol/MOSI_SMP_directory_1level.slicc b/src/mem/protocol/MOSI_SMP_directory_1level.slicc new file mode 100644 index 000000000..250c72ae7 --- /dev/null +++ b/src/mem/protocol/MOSI_SMP_directory_1level.slicc @@ -0,0 +1,4 @@ +MOSI_SMP_directory_1level-msg.sm +MOSI_SMP_directory_1level-cache.sm +MOSI_SMP_directory_1level-dir.sm +standard_1level_SMP-protocol.sm diff --git a/src/mem/protocol/MSI_MOSI_CMP_directory-L1cache.sm b/src/mem/protocol/MSI_MOSI_CMP_directory-L1cache.sm new file mode 100644 index 000000000..c16a2fe80 --- /dev/null +++ b/src/mem/protocol/MSI_MOSI_CMP_directory-L1cache.sm @@ -0,0 +1,799 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + + +machine(L1Cache, "MSI Directory L1 Cache CMP") { + + // NODE L1 CACHE + // From this node's L1 cache TO the network + // a local L1 -> this L2 bank, currently ordered with directory forwarded requests + MessageBuffer requestFromL1Cache, network="To", virtual_network="0", ordered="true"; + MessageBuffer dummyFrom1, network="To", virtual_network="1", ordered="false"; // dummy buffer that shouldn't be used + MessageBuffer dummyFrom2, network="To", virtual_network="2", ordered="false"; // dummy buffer that shouldn't be used + // a local L1 -> this L2 bank + MessageBuffer responseFromL1Cache, network="To", virtual_network="3", ordered="false"; + MessageBuffer dummyFrom4, network="To", virtual_network="4", ordered="false"; // dummy buffer that shouldn't be used + + + // To this node's L1 cache FROM the network + MessageBuffer dummyTo0, network="From", virtual_network="0", ordered="false"; // dummy buffer that shouldn't be used + MessageBuffer dummyTo1, network="From", virtual_network="1", ordered="false"; // dummy buffer that shouldn't be used + // a L2 bank -> this L1 + MessageBuffer requestToL1Cache, network="From", virtual_network="2", ordered="true"; + // a L2 bank -> this L1 + MessageBuffer responseToL1Cache, network="From", virtual_network="3", ordered="false"; + MessageBuffer dummyTo4, network="From", virtual_network="4", ordered="false"; // dummy buffer that shouldn't be used + + // STATES + enumeration(State, desc="Cache states", default="L1Cache_State_L1_I") { + // Base states + NP, desc="Not present in either cache"; + L1_I, desc="a L1 cache entry Idle"; + L1_S, desc="a L1 cache entry Shared"; + L1_M, desc="a L1 cache entry Modified", format="!b"; + + // Transient States + L1_IS, desc="L1 idle, issued GETS, have not seen response yet"; + L1_ISI, desc="L1 idle, issued GETS, saw INV, still waiting for data"; + L1_IM, desc="L1 idle, issued GETX, have not seen response yet"; + L1_IMI, desc="L1 idle, issued GETX, saw INV, still waiting for data"; + L1_IMS, desc="L1 idle, issued GETX, saw DownGrade, still waiting for data"; + L1_IMSI, desc="L1 idle, issued GETX, saw DownGrade, saw INV, still waiting for data"; + + L1_SI, desc="issued PUTS, waiting for response"; + L1_MI, desc="issued PUTX, waiting for response"; + } + + // EVENTS + enumeration(Event, desc="Cache events") { + // L1 events + Load, desc="Load request from the home processor"; + Ifetch, desc="I-fetch request from the home processor"; + Store, desc="Store request from the home processor"; + + // L1 is required to send response to the L2 immediately + L1_INV, "INV", desc="L1 Invalidation of M data", format="!r"; + L1_INV_S, "INV", desc="L1 Invalidation of S data", format="!r"; + L1_DownGrade, "Force DownGrade", desc="L2 cache forces an L1 cache in M to downgrade to S and writeback result"; + + // receiving of data + L1_Data, "Data", desc="Data in response to an L1 request, transistion to M or S depending on request"; + L1_Data_S, "Data S", desc="Data in response to an L1 request, write data then transistion to S"; + L1_Data_I, "Data I", desc="Data in response to an L1 request, write data then transistion to I"; + + // receiving of acks + L1_PutAck, "Put Ack", desc="PutS or PutX ack from L2"; + + // internal generated request + // L1 request to replace block, results in either a PUTS or PUTX request + L1_Replacement, desc="L1 Replacement", format="!r"; + // Currently same as replacement, request initiated when block is in the wrong L1 cache + L1_WriteBack, desc="on-chip L1 cache must write back to shared L2"; + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry" ) { + State CacheState, desc="cache state"; + DataBlock DataBlk, desc="data for the block"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="Buffer for the data block"; + bool isPrefetch, desc="Set if this was caused by a prefetch"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + TBETable L1_TBEs, template_hack=""; + + CacheMemory L1IcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1I"', abstract_chip_ptr="true"; + CacheMemory L1DcacheMemory, template_hack="", constructor_hack='L1_CACHE_NUM_SETS_BITS,L1_CACHE_ASSOC,MachineType_L1Cache,int_to_string(i)+"_L1D"', abstract_chip_ptr="true"; + + MessageBuffer mandatoryQueue, ordered="false", rank="100", abstract_chip_ptr="true"; + // the optionalQueue doesn't have to be ordered for correctness + // however inforcing order ensures the prefetches reach the L2 in order + MessageBuffer optionalQueue, ordered="true", rank="101", abstract_chip_ptr="true"; + + Sequencer sequencer, abstract_chip_ptr="true", constructor_hack="i"; + + int cache_state_to_int(State state); + + // inclusive cache returns L1 entries only + Entry getL1CacheEntry(Address addr), return_by_ref="yes" { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory[addr]; + } else { + return L1IcacheMemory[addr]; + } + } + + void changeL1Permission(Address addr, AccessPermission permission) { + if (L1DcacheMemory.isTagPresent(addr)) { + return L1DcacheMemory.changePermission(addr, permission); + } else if(L1IcacheMemory.isTagPresent(addr)) { + return L1IcacheMemory.changePermission(addr, permission); + } else { + error("cannot change permission, L1 block not present"); + } + } + + bool isL1CacheTagPresent(Address addr) { + return (L1DcacheMemory.isTagPresent(addr) || L1IcacheMemory.isTagPresent(addr)); + } + + State getState(Address addr) { + if((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == true){ + DEBUG_EXPR(id); + DEBUG_EXPR(addr); + } + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + + if(L1_TBEs.isPresent(addr)) { + return L1_TBEs[addr].TBEState; + } else if (isL1CacheTagPresent(addr)) { + return getL1CacheEntry(addr).CacheState; + } + return State:NP; + } + + string getStateStr(Address addr) { + return L1Cache_State_to_string(getState(addr)); + } + + // when is this called? + void setState(Address addr, State state) { + assert((L1DcacheMemory.isTagPresent(addr) && L1IcacheMemory.isTagPresent(addr)) == false); + + // MUST CHANGE + if(L1_TBEs.isPresent(addr)) { + L1_TBEs[addr].TBEState := state; + } + + if (isL1CacheTagPresent(addr)) { + getL1CacheEntry(addr).CacheState := state; + + // Set permission + if (state == State:L1_I || state == State:L1_SI || state == State:L1_MI) { + changeL1Permission(addr, AccessPermission:Invalid); + } else if (state == State:L1_S) { + changeL1Permission(addr, AccessPermission:Read_Only); + } else if (state == State:L1_M) { + changeL1Permission(addr, AccessPermission:Read_Write); + } else { + changeL1Permission(addr, AccessPermission:Busy); + } + } + } + + Event mandatory_request_type_to_event(CacheRequestType type) { + if (type == CacheRequestType:LD) { + return Event:Load; + } else if (type == CacheRequestType:IFETCH) { + return Event:Ifetch; + } else if ((type == CacheRequestType:ST) || (type == CacheRequestType:ATOMIC)) { + return Event:Store; + } else { + error("Invalid CacheRequestType"); + } + } + + // ** OUT_PORTS ** + // All ports are to the same CMP network, queue id numbers determine IntraChip Switch location + + out_port(requestIntraChipL1Network_out, RequestMsg, requestFromL1Cache); + out_port(responseIntraChipL1Network_out, ResponseMsg, responseFromL1Cache); + + // ** IN_PORTS ** + in_port(dummyTo0_in, RequestMsg, dummyTo0) { + if (dummyTo0_in.isReady()) { + peek(dummyTo0_in, RequestMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(machineID); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.RequestorMachId); + } + error("dummyTo0 port should not be used"); + } + } + + in_port(dummyTo1_in, RequestMsg, dummyTo1) { + if (dummyTo1_in.isReady()) { + peek(dummyTo1_in, RequestMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(machineID); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.RequestorMachId); + } + error("dummyTo1 port should not be used"); + } + } + + in_port(dummyTo4_in, ResponseMsg, dummyTo4) { + if (dummyTo4_in.isReady()) { + peek(dummyTo4_in, ResponseMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(machineID); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.SenderMachId); + } + error("dummyTo4 port should not be used"); + } + } + + // Response IntraChip L1 Network - response msg to this L1 cache + in_port(responseIntraChipL1Network_in, ResponseMsg, responseToL1Cache) { + if (responseIntraChipL1Network_in.isReady()) { + peek(responseIntraChipL1Network_in, ResponseMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.Destination); + DEBUG_EXPR(in_msg.SenderMachId); + DEBUG_EXPR(machineID); + assert(in_msg.Destination.isElement(machineID)); + if(machineIDToMachineType(in_msg.SenderMachId) == MachineType:L2Cache) { + if(in_msg.Type == CoherenceResponseType:DATA) { + trigger(Event:L1_Data, in_msg.Address); // L1 now has data in its desired state + } else if(in_msg.Type == CoherenceResponseType:DATA_S) { + trigger(Event:L1_Data_S, in_msg.Address); // L1 now has data but must imediately move to S state + } else if(in_msg.Type == CoherenceResponseType:DATA_I) { + trigger(Event:L1_Data_I, in_msg.Address); // L1 now has data but must imediately move to INV state + } else if(in_msg.Type == CoherenceResponseType:ACK) { + trigger(Event:L1_PutAck, in_msg.Address); + } else { + error("Invalid L1 response type"); + } + } else { + error("A non-L2 cache sent a response to a L1 cache"); + } + } + } + } + + // Request InterChip network - request from this L1 cache to the shared L2 + in_port(requestIntraChipL1Network_in, RequestMsg, requestToL1Cache) { + if(requestIntraChipL1Network_in.isReady()) { + peek(requestIntraChipL1Network_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if(machineIDToMachineType(in_msg.RequestorMachId) == MachineType:L2Cache) { + if(in_msg.Type == CoherenceRequestType:L1_DG) { + trigger(Event:L1_DownGrade, in_msg.Address); // Force L1 to downgrade to S state + } else if (in_msg.Type == CoherenceRequestType:INV) { + trigger(Event:L1_INV, in_msg.Address); // L1 must invalidate it's modified version + } else if (in_msg.Type == CoherenceRequestType:INV_S) { + trigger(Event:L1_INV_S, in_msg.Address); // L1 must invalidate it's shared version + } else { + error("Invalid forwarded request type"); + } + } else { + error("A non-L2 cache sent a request to a L1 cache"); + } + } + } + } + + // Mandatory Queue betweens Node's CPU and it's L1 caches + in_port(mandatoryQueue_in, CacheMsg, mandatoryQueue, desc="...") { + if (mandatoryQueue_in.isReady()) { + peek(mandatoryQueue_in, CacheMsg) { + + // Check for data access to blocks in I-cache and ifetchs to blocks in D-cache + + if (in_msg.Type == CacheRequestType:IFETCH) { + // ** INSTRUCTION ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, put the request on the queue to the shared L2 + trigger(Event:L1_WriteBack, in_msg.Address); + } + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 asks the L2 for it. + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1IcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 so let's see if the L2 has it + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // No room in the L1, so we need to make room in the L1 + trigger(Event:L1_Replacement, L1IcacheMemory.cacheProbe(in_msg.Address)); + } + } + } else { + // *** DATA ACCESS *** + + // Check to see if it is in the OTHER L1 + if (L1IcacheMemory.isTagPresent(in_msg.Address)) { + // The block is in the wrong L1, put the request on the queue to the shared L2 + trigger(Event:L1_WriteBack, in_msg.Address); + } + if (L1DcacheMemory.isTagPresent(in_msg.Address)) { + // The tag matches for the L1, so the L1 ask the L2 for it + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + if (L1DcacheMemory.cacheAvail(in_msg.Address)) { + // L1 does't have the line, but we have space for it in the L1 let's see if the L2 has it + trigger(mandatory_request_type_to_event(in_msg.Type), in_msg.Address); + } else { + // No room in the L1, so we need to make room in the L1 + trigger(Event:L1_Replacement, L1DcacheMemory.cacheProbe(in_msg.Address)); + } + } + } + } + } + } + + // ACTIONS + action(a_issueGETS, "a", desc="Issue GETS") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.RequestorMachId := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.L1CacheStateStr := getStateStr(address); + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + } + } + } + + action(b_issueGETX, "b", desc="Issue GETX") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.RequestorMachId := machineID; + DEBUG_EXPR(machineID); + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.L1CacheStateStr := getStateStr(address); + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + } + } + } + + action(c_issueUPGRADE, "c", desc="Issue GETX") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:UPGRADE; + out_msg.RequestorMachId := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.L1CacheStateStr := getStateStr(address); + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + } + } + } + + action(f_issueGETINSTR, "g", desc="Issue GETINSTR") { + peek(mandatoryQueue_in, CacheMsg) { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GET_INSTR; + out_msg.RequestorMachId := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.L1CacheStateStr := getStateStr(address); + out_msg.Prefetch := in_msg.Prefetch; + out_msg.AccessMode := in_msg.AccessMode; + } + } + } + + action(d_issuePUTX, "d", desc="Issue PUTX") { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTX; + out_msg.RequestorMachId := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + DEBUG_EXPR(out_msg.DataBlk); + out_msg.MessageSize := MessageSizeType:Data; + out_msg.L1CacheStateStr := getStateStr(address); + } + } + + action(q_issuePUTS, "q", desc="Issue PUTS") { + enqueue(requestIntraChipL1Network_out, RequestMsg, latency="L1_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTS; + out_msg.RequestorMachId := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + out_msg.L1CacheStateStr := getStateStr(address); + } + } + + // L1 responding to a L2 request with data + action(e_dataFromL1CacheToL2Cache, "e", desc="Send data from L1 cache to L2 Cache") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.DataBlk := getL1CacheEntry(address).DataBlk; + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + DEBUG_EXPR(out_msg.DataBlk); + out_msg.MessageSize := MessageSizeType:Data; + } + } + + action(f_dataFromTBEToL2Cache, "f", desc="Send data from L1_TBE to L2 Cache") { + peek(requestIntraChipL1Network_in, RequestMsg) { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + out_msg.DataBlk := L1_TBEs[in_msg.Address].DataBlk; + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + DEBUG_EXPR(out_msg.DataBlk); + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + // L1 responding to a L2 request with an invadiation ack + action(t_sendInvAckToL2Cache, "t", desc="Send Invadiation ack to L2 Cache") { + enqueue(responseIntraChipL1Network_out, ResponseMsg, latency="L1_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:INV_ACK; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(map_L1CacheMachId_to_L2Cache(address, machineID)); + DEBUG_EXPR(address); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(h_load_hit, "h", desc="If not prefetch, notify sequencer the load completed.") { + DEBUG_EXPR(getL1CacheEntry(address).DataBlk); + sequencer.readCallback(address, getL1CacheEntry(address).DataBlk); + } + + action(hh_store_hit, "\h", desc="If not prefetch, notify sequencer that store completed.") { + DEBUG_EXPR(getL1CacheEntry(address).DataBlk); + sequencer.writeCallback(address, getL1CacheEntry(address).DataBlk); + } + + action(i_allocateTBE, "i", desc="Allocate TBE (isPrefetch=0, number of invalidates=0)") { + check_allocate(L1_TBEs); + L1_TBEs.allocate(address); + L1_TBEs[address].isPrefetch := false; + } + + action(k_popMandatoryQueue, "k", desc="Pop mandatory queue.") { + mandatoryQueue_in.dequeue(); + } + + action(l_popRequestQueue, "l", desc="Pop incoming request queue and profile the delay within this virtual network") { + profileMsgDelay(2, requestIntraChipL1Network_in.dequeue_getDelayCycles()); + } + + action(o_popIncomingResponseQueue, "o", desc="Pop Incoming Response queue and profile the delay within this virtual network") { + profileMsgDelay(3, responseIntraChipL1Network_in.dequeue_getDelayCycles()); + } + + action(s_deallocateTBE, "s", desc="Deallocate TBE") { + L1_TBEs.deallocate(address); + } + + action(u_writeDataToL1Cache, "u", desc="Write data to cache") { + peek(responseIntraChipL1Network_in, ResponseMsg) { + getL1CacheEntry(address).DataBlk := in_msg.DataBlk; + } + } + + action(x_copyDataFromL1CacheToTBE, "x", desc="Copy data from cache to TBE") { + L1_TBEs[address].DataBlk := getL1CacheEntry(address).DataBlk; + } + + action(z_stall, "z", desc="Stall") { + } + + action(ff_deallocateL1CacheBlock, "\f", desc="Deallocate L1 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + if (L1DcacheMemory.isTagPresent(address)) { + L1DcacheMemory.deallocate(address); + } else { + L1IcacheMemory.deallocate(address); + } + } + + action(oo_allocateL1DCacheBlock, "\o", desc="Set L1 D-cache tag equal to tag of block B.") { + if (L1DcacheMemory.isTagPresent(address) == false) { + L1DcacheMemory.allocate(address); + } + } + + action(pp_allocateL1ICacheBlock, "\p", desc="Set L1 I-cache tag equal to tag of block B.") { + if (L1IcacheMemory.isTagPresent(address) == false) { + L1IcacheMemory.allocate(address); + } + } + + //***************************************************** + // TRANSITIONS + //***************************************************** + + // Transitions for Load/Store/Replacement/WriteBack from transient states + transition({L1_IS, L1_IM, L1_ISI, L1_IMI, L1_IMS, L1_IMSI, L1_SI, L1_MI}, {Load, Ifetch, Store, L1_Replacement, L1_WriteBack}) { + z_stall; + } + + // Transitions from Idle + transition({NP,L1_I}, {L1_Replacement, L1_WriteBack}) { + ff_deallocateL1CacheBlock; + } + + transition({NP,L1_I}, Load, L1_IS) { + oo_allocateL1DCacheBlock; + i_allocateTBE; + a_issueGETS; + k_popMandatoryQueue; + } + + transition({NP,L1_I}, Ifetch, L1_IS) { + pp_allocateL1ICacheBlock; + i_allocateTBE; + f_issueGETINSTR; + k_popMandatoryQueue; + } + + transition({NP,L1_I}, Store, L1_IM) { + oo_allocateL1DCacheBlock; + i_allocateTBE; + b_issueGETX; + k_popMandatoryQueue; + } + + // Transitions from Shared + transition({L1_S}, {Load,Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(L1_S, Store, L1_IM) { + i_allocateTBE; + c_issueUPGRADE; + k_popMandatoryQueue; + } + + transition(L1_S, {L1_Replacement,L1_WriteBack}, L1_SI) { + i_allocateTBE; + q_issuePUTS; + x_copyDataFromL1CacheToTBE; + ff_deallocateL1CacheBlock; + } + + transition(L1_S, L1_INV_S, L1_I) { + t_sendInvAckToL2Cache; + l_popRequestQueue; + } + + // Transitions from Modified + transition(L1_M, {Load, Ifetch}) { + h_load_hit; + k_popMandatoryQueue; + } + + transition(L1_M, Store) { + hh_store_hit; + k_popMandatoryQueue; + } + + transition(L1_M, {L1_Replacement, L1_WriteBack}, L1_MI) { + i_allocateTBE; + d_issuePUTX; + x_copyDataFromL1CacheToTBE; + ff_deallocateL1CacheBlock; + } + + transition(L1_M, L1_INV, L1_I) { + e_dataFromL1CacheToL2Cache; + l_popRequestQueue; + } + + transition(L1_M, L1_DownGrade, L1_S) { + e_dataFromL1CacheToL2Cache; + l_popRequestQueue; + } + + // Transitions from L1_IS + transition(L1_IS, L1_INV_S, L1_ISI) { + t_sendInvAckToL2Cache; + l_popRequestQueue; + } + + transition(L1_IS, L1_Data, L1_S) { + u_writeDataToL1Cache; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(L1_IS, L1_Data_I, L1_I) { + u_writeDataToL1Cache; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // Transitions from L1_ISI + transition(L1_ISI, L1_Data, L1_I) { + u_writeDataToL1Cache; + h_load_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // Transitions from L1_IM + transition(L1_IM, L1_INV, L1_IMI) { // we don't have to respond immediately because we know the data is coming + l_popRequestQueue; + } + + transition(L1_IM, L1_INV_S) { + t_sendInvAckToL2Cache; + l_popRequestQueue; + } + + transition(L1_IM, L1_DownGrade, L1_IMS) { + l_popRequestQueue; + } + + transition(L1_IM, L1_Data, L1_M) { + u_writeDataToL1Cache; + hh_store_hit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(L1_IM, L1_Data_S, L1_S) { + u_writeDataToL1Cache; + hh_store_hit; + s_deallocateTBE; + e_dataFromL1CacheToL2Cache; + o_popIncomingResponseQueue; + } + + transition(L1_IM, L1_Data_I, L1_I) { + u_writeDataToL1Cache; + hh_store_hit; + s_deallocateTBE; + e_dataFromL1CacheToL2Cache; + o_popIncomingResponseQueue; + } + + // Transitions from L1_IMI - data should arrive and no request are possilbe + transition(L1_IMI, L1_Data, L1_I) { + u_writeDataToL1Cache; + hh_store_hit; + s_deallocateTBE; + e_dataFromL1CacheToL2Cache; + o_popIncomingResponseQueue; + } + + // Transitions from L1_IMS + transition(L1_IMS, L1_Data, L1_S) { + u_writeDataToL1Cache; + hh_store_hit; + s_deallocateTBE; + e_dataFromL1CacheToL2Cache; + o_popIncomingResponseQueue; + } + + transition(L1_IMS, L1_INV_S, L1_IMSI) { + l_popRequestQueue; + } + + // Transitions from L1_IMSI + transition(L1_IMSI, L1_Data, L1_I) { + u_writeDataToL1Cache; + hh_store_hit; + s_deallocateTBE; + e_dataFromL1CacheToL2Cache; + o_popIncomingResponseQueue; + } + + // Transitions from L1_SI + transition(L1_SI, L1_INV_S) { + t_sendInvAckToL2Cache; + l_popRequestQueue; + } + + transition(L1_SI, L1_PutAck, L1_I) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // Transitions from L1_MI + transition(L1_MI, L1_INV) { + f_dataFromTBEToL2Cache; + l_popRequestQueue; + } + + transition(L1_MI, L1_DownGrade, L1_SI) { + f_dataFromTBEToL2Cache; + l_popRequestQueue; + } + + transition(L1_MI, L1_PutAck, L1_I) { + s_deallocateTBE; + o_popIncomingResponseQueue; + } +} + + + diff --git a/src/mem/protocol/MSI_MOSI_CMP_directory-L2cache.sm b/src/mem/protocol/MSI_MOSI_CMP_directory-L2cache.sm new file mode 100644 index 000000000..d68efc819 --- /dev/null +++ b/src/mem/protocol/MSI_MOSI_CMP_directory-L2cache.sm @@ -0,0 +1,2191 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +machine(L2Cache, "MOSI Directory L2 Cache CMP") { + + // L2 BANK QUEUES + // From local bank of L2 cache TO the network + MessageBuffer dummyFrom0, network="To", virtual_network="0", ordered="false"; // dummy buffer that shouldn't be used + MessageBuffer DirRequestFromL2Cache, network="To", virtual_network="1", ordered="false"; // this L2 bank -> mod-directory + MessageBuffer L1RequestFromL2Cache, network="To", virtual_network="2", ordered="true"; // this L2 bank -> a local L1 + MessageBuffer responseFromL2Cache, network="To", virtual_network="3", ordered="false"; // this L2 bank -> a local L1 || mod-directory + MessageBuffer finalAckFromL2Cache, network="To", virtual_network="4", ordered="false"; // this L2 bank -> mod-directory + + // FROM the network to this local bank of L2 cache + //MessageBuffer L1RequestToL2Cache, network="From", virtual_network="1", ordered="true"; // a local L1 -> this L2 bank + MessageBuffer L1RequestToL2Cache, network="From", virtual_network="0", ordered="true"; // a local L1 -> this L2 bank + MessageBuffer dummyTo1, network="From", virtual_network="1", ordered="false"; // dummy buffer that shouldn't be used + MessageBuffer forwardedRequestToL2Cache, network="From", virtual_network="2", ordered="true"; // mod-directory -> this L2 bank + MessageBuffer responseToL2Cache, network="From", virtual_network="3", ordered="false"; // a local L1 || mod-directory -> this L2 bank + MessageBuffer dummyTo4, network="From", virtual_network="4", ordered="false"; // dummy buffer that shouldn't be used + + // STATES + enumeration(State, desc="L2 Cache states", default="L2Cache_State_L2_NP") { + // Base states + L2_NP, desc="Not present in either cache"; + L2_I, desc="L2 cache entry Idle"; + L2_S, desc="L2 cache entry Shared, not present in any local L1s"; + L2_O, desc="L2 cache entry Owned, not present in any local L1s"; + L2_M, desc="L2 cache entry Modified, not present in any L1s", format="!b"; + L2_SS, desc="L2 cache entry Shared, also present in one or more L1s"; + L2_SO, desc="L2 cache entry Owned, also present in one or more L1s or ext L2s"; + L2_MT, desc="L2 cache entry Modified in a local L1, assume L2 copy stale", format="!b"; + + // Transient States + + // Transient States from I + L2_IS, desc="L2 idle, issued GETS, have not seen response yet"; + L2_ISZ, desc="L2 idle, issued GETS, saw a L1_GETX, have not seen data for GETS yet", format="!b"; + L2_ISI, desc="L2 idle, issued GETS, saw INV, have not seen data for GETS yet", format="!b"; + L2_IMV, desc="L2 idle, issued GETX, valid int L1, have not seen response(s) yet"; + L2_MV, desc="L2 modified, a valid old L1 copy exist, external world gave write permission"; + L2_IM, desc="L2 idle, issued GETX, no valid int L1, have not seen response(s) yet"; + L2_IMO, desc="L2 idle, issued GETX, saw forwarded GETS"; + L2_IMI, desc="L2 idle, issued GETX, saw forwarded GETX"; + L2_IMZ, desc="L2 idle, issued GETX, saw another L1_GETX"; + L2_IMOI, desc="L2 idle, issued GETX, saw GETS, saw forwarded GETX"; + L2_IMOZ, desc="L2 idle, issued GETX, saw GETS, then a L1_GETX"; + + // Invalidation steps for S -> I + L2_SIC, desc="L2 shared, L2_INV, valid L1 copies exist, issued invalidates, have not seen responses yet"; + L2_SIV, desc="L2 shared, L2_Replacement, valid L1 copies exist, issued invalidates, have not seen responses yet"; + + // Invalidation steps for M -> I for L2 Repalcement + L2_MIV, desc="L2 modified, a valid L1 copy exist, issued forced writeback, have not seen the response yet"; + L2_MIN, desc="L2 modified, no valid L1 copies, issued PUTX, have not seen response yet"; + + // Invalidation steps for M -> I for a Forwarded GetX + L2_MIC, desc="L2 modified, a valid L1 copy exist, issued forced writeback, have not seen the response yet"; + + // In MT state and see another L1_GETX request + L2_MIT, desc="L2 modified, a valid L1 copy exist, saw L1_GETX, issued INV, have not seen the response yet"; + + // Downgrade steps for M -> SO + L2_MO, desc="L2 modified, a valid L1 copy exist, issued downgrade request, have not seen response yet"; + L2_MOIC, desc="L2 modified, a valid L1 copy exist, issued downgrade request, saw INV, have not seen response yet"; + L2_MOICR, desc="L2 modified, a valid L1 copy exist, issued invalidate request, saw INV, have not seen response yet"; + L2_MOZ, desc="L2 modified, a valid L1 copy exist, issued downgrade request, saw L1_GETX, have not seen response yet"; + + // Invalidation steps for O/SO -> I for L2 Replacement + L2_OIV, desc="L2 owned, valid L1 copies exist, issued invalidates, have not seen responses yet from L1s"; + L2_OIN, desc="L2 owned, no valid L1 copies, issued PUTX, have not seen response yet from dir"; + + // Invalidation steps for SO -> I for a Forwarded GetX + L2_OIC, desc="L2 owned, valid L1 copies exist, issued invalidates, have not seen responses yet from L1s"; + + // Strange OM states + // Note: strange states, because it is waiting for the line + // to be stolen away, or look like it has been stolen away. The + // common case is that we see a forward from the directory that is + // really from us, we forwarded the data to our dataqueue, and + // everythings works fine. + L2_OMV, desc="L2 owned and valid L1 copies, issued GETX and invalidates, have not seen responses yet"; + L2_OM, desc="L2 owned and no valid L1 copies, issued GETX, have not seen response yet"; + } + + // EVENTS + enumeration(Event, desc="L2 Cache events") { + // L2 events + + // events initiated by the local L1s + L1_GET_INSTR, desc="a L1I GET INSTR request for a block maped to us"; + L1_GETS, desc="a L1D GETS request for a block maped to us"; + L1_GETX, desc="a L1D GETX request for a block maped to us"; + L1_UPGRADE, desc="a L1D UPGRADE request for a block maped to us"; + L1_UPGRADE_no_others, desc="a L1D UPGRADE request for a block maped to us, requestor is the only on-chip sharer"; + L1_PUTX, desc="a L1D PUTX request for a block maped to us (L1 replacement of a modified block)"; + L1_PUTX_last, desc="a L1D PUTX request for a block maped to us (L1 replacement of a modified block) last sharer"; + L1_PUTX_old, desc="an old L1D PUTX request for a block maped to us (L1 replacement of a modified block)"; + L1_PUTS, desc="a L1 replacement of a shared block", format="!r"; + L1_PUTS_last, desc="a L1 replacement of the last local L1 shared block", format="!r"; + L1_PUTS_old, desc="an old L1 replacement of a shared block", format="!r"; + + // events of local L1 responses + Proc_int_ack, "Proc on-chip L1 Cache ack", desc="Ack from on-chip L1 Cache"; + Proc_last_int_ack, "Proc last on-chip L1 Cache ack", desc="Last on-chip L1 Cache ack", format="!r"; + + Data_int_ack, "Data int ack", desc="Received modified data from L1 now proceed in handling miss"; + + // events initiated by the external L2s + Forwarded_GETS, "Forwarded GETS", desc="Directory forwards Inter-chip GETS to us"; + Forwarded_GET_INSTR, "Forwarded GETINSTR", desc="Inter-chip Forwarded GETINSTR"; + Forwarded_GETX, "Forwarded GETX", desc="Directory forwards Inter-chip GETX to us"; + L2_INV, "L2_INV", desc="L2 Invalidation initiated from other L2", format="!r"; + + // events initiated by this L2 + L2_Replacement, desc="L2 Replacement", format="!r"; + + // events of external L2 responses + Proc_ext_ack, "Proc off-chip ack", desc="Ack from off-chip"; + Proc_last_ext_ack, "Proc last off-chip ack", desc="Last off-chip ack", format="!r"; + + Data_ext_ack_0, "Data ack 0", desc="Data with ack count = 0"; + Data_ext_ack_not_0, "Data ack not 0", desc="Data with ack count != 0 (but haven't seen all acks first"; + // Data_ext_ack_not_0_last: is when the requestor has seen all acks but the directory has not, therefore + // the directory must be told that we now have the data + Data_ext_ack_not_0_last, "Data ack not 0 last", desc="Data with ack count != 0 after having received all acks"; + + Dir_WB_ack, "WB ack", desc="Writeback ack from dir"; + Dir_exe_ack, "Only copy", desc="Directory tells us we already have exclusive permission, go directly to MT state"; + } + + // TYPES + + // CacheEntry + structure(Entry, desc="...", interface="AbstractCacheEntry") { + State CacheState, desc="cache state"; + NetDest Sharers, desc="tracks the L1 shares on-chip"; + DataBlock DataBlk, desc="data for the block"; + } + + // TBE fields + structure(TBE, desc="...") { + Address Address, desc="Physical address for this TBE"; + State TBEState, desc="Transient state"; + DataBlock DataBlk, desc="Buffer for the data block"; + int NumPendingExtAcks, desc="Number of ext acks that this L2 bank is waiting for"; + int NumPendingIntAcks, desc="Number of int acks that this L2 bank is waiting for"; + NetDest Forward_GetS_IDs, desc="Set of the external processors to forward the block"; + NetDest L1_GetS_IDs, desc="Set of the internal processors that want the block in shared state"; + MachineID Forward_GetX_ID, desc="ID of the L2 cache to forward the block"; + MachineID L1_GetX_ID, desc="ID of the L1 cache to forward the block to once we get a response"; + MachineID InvalidatorID, desc="ID of the L2 cache (needed for L2_SS -> L2_I)"; + int ForwardGetX_AckCount, desc="Number of acks the GetX we are forwarded needs"; + bool isPrefetch, desc="Set if this was caused by a prefetch"; + bool isThreeHop, desc="is this request a three hop"; + bool validForwardedGetXId, desc="Indicate whether a forwarded GetX ID is valid"; + bool validInvalidator, desc="Indicate whether an invalidator is valid"; + bool isInternalRequestOnly, desc="Is internal request only, i.e. only L1s"; + } + + external_type(CacheMemory) { + bool cacheAvail(Address); + Address cacheProbe(Address); + void allocate(Address); + void deallocate(Address); + Entry lookup(Address); + void changePermission(Address, AccessPermission); + bool isTagPresent(Address); + void setMRU(Address); + } + + external_type(TBETable) { + TBE lookup(Address); + void allocate(Address); + void deallocate(Address); + bool isPresent(Address); + } + + TBETable L2_TBEs, template_hack=""; + + CacheMemory L2cacheMemory, template_hack="", constructor_hack='L2_CACHE_NUM_SETS_BITS,L2_CACHE_ASSOC,MachineType_L2Cache,int_to_string(i)'; + + // inclusive cache, returns L2 entries only + Entry getL2CacheEntry(Address addr), return_by_ref="yes" { + return L2cacheMemory[addr]; + } + + void changeL2Permission(Address addr, AccessPermission permission) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory.changePermission(addr, permission); + } + } + + string getCoherenceRequestTypeStr(CoherenceRequestType type) { + return CoherenceRequestType_to_string(type); + } + + bool isL2CacheTagPresent(Address addr) { + return (L2cacheMemory.isTagPresent(addr)); + } + + bool isOneSharerLeft(Address addr, MachineID requestor) { + assert(L2cacheMemory[addr].Sharers.isElement(requestor)); + return (L2cacheMemory[addr].Sharers.count() == 1); + } + + bool isSharer(Address addr, MachineID requestor) { + if (L2cacheMemory.isTagPresent(addr)) { + return L2cacheMemory[addr].Sharers.isElement(requestor); + } else { + return false; + } + } + + void addSharer(Address addr, MachineID requestor) { + DEBUG_EXPR(machineID); + DEBUG_EXPR(requestor); + DEBUG_EXPR(addr); + assert(map_L1CacheMachId_to_L2Cache(addr, requestor) == machineID); + L2cacheMemory[addr].Sharers.add(requestor); + } + + State getState(Address addr) { + if(L2_TBEs.isPresent(addr)) { + return L2_TBEs[addr].TBEState; + } else if (isL2CacheTagPresent(addr)) { + return getL2CacheEntry(addr).CacheState; + } + return State:L2_NP; + } + + string getStateStr(Address addr) { + return L2Cache_State_to_string(getState(addr)); + } + + // when is this called + void setState(Address addr, State state) { + + // MUST CHANGE + if (L2_TBEs.isPresent(addr)) { + L2_TBEs[addr].TBEState := state; + } + + if (isL2CacheTagPresent(addr)) { + getL2CacheEntry(addr).CacheState := state; + + // Set permission + if (state == State:L2_I || + state == State:L2_SIC || state == State:L2_SIV || + state == State:L2_MIV || state == State:L2_MIN || state == State:L2_MIC || state == State:L2_MIT || + state == State:L2_OIV || state == State:L2_OIN || state == State:L2_OIC) { + changeL2Permission(addr, AccessPermission:Invalid); + } else if (state == State:L2_S || state == State:L2_O || state == State:L2_SS || state == State:L2_SO) { + changeL2Permission(addr, AccessPermission:Read_Only); + } else if (state == State:L2_OM || state == State:L2_OMV) { + changeL2Permission(addr, AccessPermission:ReadUpgradingToWrite); + } else if (state == State:L2_M) { + changeL2Permission(addr, AccessPermission:Read_Write); + } else if (state == State:L2_MT) { + changeL2Permission(addr, AccessPermission:Stale); + } else { + changeL2Permission(addr, AccessPermission:Busy); + } + } + } + + Event L1Cache_request_type_to_event(CoherenceRequestType type, Address addr, MachineID requestor) { + if(type == CoherenceRequestType:GETS) { + return Event:L1_GETS; + } else if(type == CoherenceRequestType:GET_INSTR) { + return Event:L1_GET_INSTR; + } else if (type == CoherenceRequestType:GETX) { + return Event:L1_GETX; + } else if (type == CoherenceRequestType:UPGRADE) { + if (isSharer(addr, requestor)) { + if (isOneSharerLeft(addr, requestor)) { + return Event:L1_UPGRADE_no_others; + } else { + return Event:L1_UPGRADE; + } + } else { // possible that we removed the line from the L2 before we could process the UPGRADE request + return Event:L1_GETX; + } + } else if (type == CoherenceRequestType:PUTX) { + if (isSharer(addr, requestor)) { + if (isOneSharerLeft(addr, requestor)) { + return Event:L1_PUTX_last; + } else { + return Event:L1_PUTX; + } + } else { + return Event:L1_PUTX_old; + } + } else if (type == CoherenceRequestType:PUTS) { + if (isSharer(addr, requestor)) { + if (isOneSharerLeft(addr, requestor)) { + return Event:L1_PUTS_last; + } else { + return Event:L1_PUTS; + } + } else { // possible that we removed the line from the L2 before we could process the L1_PUTS request + return Event:L1_PUTS_old; + } + } else { + DEBUG_EXPR(addr); + DEBUG_EXPR(type); + error("Invalid L1 forwarded request type"); + } + } + + // ** OUT_PORTS ** + // All ports output to the same CMP network, NI determines where to route msg + + out_port(L1RequestIntraChipL2Network_out, RequestMsg, L1RequestFromL2Cache); + out_port(DirRequestIntraChipL2Network_out, RequestMsg, DirRequestFromL2Cache); + out_port(responseIntraChipL2Network_out, ResponseMsg, responseFromL2Cache); + out_port(finalAckIntraChipL2Network_out, ResponseMsg, finalAckFromL2Cache); + + // ** IN_PORTS ** + + in_port(dummyTo1_in, RequestMsg, dummyTo1) { + if (dummyTo1_in.isReady()) { + peek(dummyTo1_in, RequestMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(id); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.RequestorMachId); + } + error("dummyTo1 port should not be used"); + } + } + + in_port(dummyTo4_in, ResponseMsg, dummyTo4) { + if (dummyTo4_in.isReady()) { + peek(dummyTo4_in, ResponseMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(id); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.SenderMachId); + } + error("dummyTo4 port should not be used"); + } + } + + // Response IntraChip L2 Network - response msg to this particular L2 bank + in_port(responseIntraChipL2Network_in, ResponseMsg, responseToL2Cache) { + if (responseIntraChipL2Network_in.isReady()) { + peek(responseIntraChipL2Network_in, ResponseMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(id); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.SenderMachId); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(in_msg.NumPendingExtAcks); + // test wether it's from a local L1 or an off chip source + assert(in_msg.Destination.isElement(machineID)); + if(machineIDToMachineType(in_msg.SenderMachId) == MachineType:L1Cache) { + if(in_msg.Type == CoherenceResponseType:DATA) { + if(L2_TBEs[in_msg.Address].NumPendingIntAcks == 1) { + trigger(Event:Data_int_ack, in_msg.Address); // L1 now has data and all on-chip acks + } else { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(L2_TBEs[in_msg.Address].NumPendingIntAcks); + error("Invalid L1 sent data when L2 wasn't expecting it"); + } + } else if(in_msg.Type == CoherenceResponseType:INV_ACK) { + if(L2_TBEs.isPresent(in_msg.Address)) { // FIXME - possible to get a L1 ack after the transaction is completed + if(L2_TBEs[in_msg.Address].NumPendingIntAcks == 1) { + trigger(Event:Proc_last_int_ack, in_msg.Address); // L1 now has all on-chip acks + } else { + trigger(Event:Proc_int_ack, in_msg.Address); // process on-chip ack + } + } + } + } else { // external message + if(in_msg.Type == CoherenceResponseType:DATA) { + if(in_msg.NumPendingExtAcks == 0) { + trigger(Event:Data_ext_ack_0, in_msg.Address); // L2 now has data and all off-chip acks + } else { + if(in_msg.NumPendingExtAcks + L2_TBEs[in_msg.Address].NumPendingExtAcks != 0) { + trigger(Event:Data_ext_ack_not_0, in_msg.Address); + } else { + trigger(Event:Data_ext_ack_not_0_last, in_msg.Address); + } + } + } else if(in_msg.Type == CoherenceResponseType:ACK) { + if(L2_TBEs[in_msg.Address].NumPendingExtAcks != 1){ + trigger(Event:Proc_ext_ack, in_msg.Address); + } else { + trigger(Event:Proc_last_ext_ack, in_msg.Address); + } + } + } + } + } // if not ready, do nothing + } + + // Forwarded Request from Directory + in_port(forwardedRequestIntraChipL2Network_in, RequestMsg, forwardedRequestToL2Cache) { + if(forwardedRequestIntraChipL2Network_in.isReady()) { + peek(forwardedRequestIntraChipL2Network_in, RequestMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(id); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.RequestorMachId); + DEBUG_EXPR(in_msg.Type); + assert(in_msg.Destination.isElement(machineID)); + if(in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:Forwarded_GETS, in_msg.Address); // L2 + } else if(in_msg.Type == CoherenceRequestType:GET_INSTR) { + trigger(Event:Forwarded_GET_INSTR, in_msg.Address); // L2 + } else if (in_msg.Type == CoherenceRequestType:GETX) { + trigger(Event:Forwarded_GETX, in_msg.Address); // L2 + } else if (in_msg.Type == CoherenceRequestType:INV) { + trigger(Event:L2_INV, in_msg.Address); // L2 + } else if (in_msg.Type == CoherenceRequestType:WB_ACK) { + trigger(Event:Dir_WB_ack, in_msg.Address); // L2 + } else if (in_msg.Type == CoherenceRequestType:EXE_ACK) { + trigger(Event:Dir_exe_ack, in_msg.Address); // L2 + } else { + error("Invalid L2 forwarded request type"); + } + } + } + } + + // L1 Request + in_port(L1RequestIntraChipL2Network_in, RequestMsg, L1RequestToL2Cache) { + if(L1RequestIntraChipL2Network_in.isReady()) { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(id); + DEBUG_EXPR(version); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.RequestorMachId); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(in_msg.Destination); + assert(machineIDToMachineType(in_msg.RequestorMachId) == MachineType:L1Cache); + assert(in_msg.Destination.isElement(machineID)); + if (L2cacheMemory.isTagPresent(in_msg.Address)) { + // The L2 contains the block, so proceeded with handling the request + trigger(L1Cache_request_type_to_event(in_msg.Type, in_msg.Address, in_msg.RequestorMachId), in_msg.Address); + } else { + if (L2cacheMemory.cacheAvail(in_msg.Address)) { + // L2 does't have the line, but we have space for it in the L2 + trigger(L1Cache_request_type_to_event(in_msg.Type, in_msg.Address, in_msg.RequestorMachId), in_msg.Address); + } else { + // No room in the L2, so we need to make room before handling the request + trigger(Event:L2_Replacement, L2cacheMemory.cacheProbe(in_msg.Address)); + } + } + } + } + } + + // ACTIONS + + action(a_issueGETS, "a", desc="Issue GETS") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(DirRequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETS; + out_msg.RequestorMachId := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.L1CacheStateStr := in_msg.L1CacheStateStr; + out_msg.L2CacheStateStr := getStateStr(address); + } + } + } + + action(b_issueGETX, "b", desc="Issue GETX") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(DirRequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GETX; + out_msg.RequestorMachId := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.L1CacheStateStr := in_msg.L1CacheStateStr; + out_msg.L2CacheStateStr := getStateStr(address); + } + } + } + + // finalAck issued from the response queue + action(c_finalAckToDirIfNeeded, "c", desc="Send FinalAck to dir if this is response to 3-hop xfer") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + DEBUG_EXPR(in_msg); + if(machineIDToMachineType(in_msg.SenderMachId) == MachineType:L2Cache) { + enqueue(finalAckIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY"){ + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:FINALACK; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + DEBUG_EXPR(out_msg); + } + } + } + } + + // finalAck issued from TBE + action(n_sendFinalAckIfThreeHop, "n", desc=""){ + peek(responseIntraChipL2Network_in, ResponseMsg){ + DEBUG_EXPR(in_msg); + if(L2_TBEs[address].isThreeHop == true){ + enqueue(finalAckIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY"){ + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:FINALACK; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + DEBUG_EXPR(out_msg); + } + } + } + } + + action(mm_rememberIfFinalAckNeeded, "\m", desc=""){ + peek(responseIntraChipL2Network_in, ResponseMsg){ + if(machineIDToMachineType(in_msg.SenderMachId) == MachineType:L2Cache){ + L2_TBEs[address].isThreeHop := true; + } + } + } + + action(d_issuePUTX, "d", desc="Issue PUTX") { + enqueue(DirRequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:PUTX; + out_msg.RequestorMachId := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + DEBUG_EXPR(out_msg.Address); + DEBUG_EXPR(out_msg.Destination); + DEBUG_EXPR(out_msg.DataBlk); + out_msg.MessageSize := MessageSizeType:Data; + out_msg.L1CacheStateStr := "NA"; + out_msg.L2CacheStateStr := getStateStr(address); + } + } + + action(f_issueGETINSTR, "f", desc="Issue GETINSTR") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(DirRequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:GET_INSTR; + out_msg.RequestorMachId := machineID; + out_msg.Destination.add(map_Address_to_Directory(address)); + out_msg.MessageSize := MessageSizeType:Control; + out_msg.L1CacheStateStr := in_msg.L1CacheStateStr; + out_msg.L2CacheStateStr := getStateStr(address); + } + } + } + + // DELAYED RESPONSES - Sorced from a TBE entry + // TBE -> L1 + action(h_issueLoadHit, "h", desc="If not prefetch, notify sequencer the load completed.") { + DEBUG_EXPR(getL2CacheEntry(address).DataBlk); + if((L2_TBEs.isPresent(address) == false) || (L2_TBEs[address].isPrefetch == false)) { + // Non-prefetch + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.SenderMachId := machineID; + out_msg.Destination := L2_TBEs[address].L1_GetS_IDs; // could be multiple internal nodes + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } else { + // Prefetch - don't issue hit msg + } + } + + action(oo_issueLoadHitInv, "\o", desc="If not prefetch, notify sequencer the load completed.") { + DEBUG_EXPR(getL2CacheEntry(address).DataBlk); + if((L2_TBEs.isPresent(address) == false) || (L2_TBEs[address].isPrefetch == false)) { + // Non-prefetch + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_I; + out_msg.SenderMachId := machineID; + out_msg.Destination := L2_TBEs[address].L1_GetS_IDs; // could be multiple internal nodes + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } else { + // Prefetch - don't issue hit msg + } + + } + + action(hh_issueStoreHit, "\h", desc="If not prefetch, issue store hit message to local L1 requestor") { + DEBUG_EXPR(getL2CacheEntry(address).DataBlk); + if((L2_TBEs.isPresent(address) == false) || (L2_TBEs[address].isPrefetch == false)) { + // Non-prefetch + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(L2_TBEs[address].L1_GetX_ID); // a single node + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } else { + // Prefetch - don't issue hit msg + } + } + + action(pp_issueStoreHitInv, "\p", desc="If not prefetch, issue store hit message to local L1 requestor") { + DEBUG_EXPR(getL2CacheEntry(address).DataBlk); + if((L2_TBEs.isPresent(address) == false) || (L2_TBEs[address].isPrefetch == false)) { + // Non-prefetch + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_I; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(L2_TBEs[address].L1_GetX_ID); // a single node + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } else { + // Prefetch - don't issue hit msg + } + } + + action(cc_issueStoreHitDG, "\c", desc="If not prefetch, issue store hit message to local L1 requestor") { + DEBUG_EXPR(getL2CacheEntry(address).DataBlk); + if((L2_TBEs.isPresent(address) == false) || (L2_TBEs[address].isPrefetch == false)) { + // Non-prefetch + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA_S; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(L2_TBEs[address].L1_GetX_ID); // a single node + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } else { + // Prefetch - don't issue hit msg + } + } + + action(w_sendPutAckToL1Cache, "w", desc="send acknowledgement of an L1 replacement") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(in_msg.RequestorMachId); // a single node + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + + // TBE -> L1s and L2s + action(ee_dataFromL2CacheToGetSIDs, "\e", desc="Send data from cache to all GetS IDs") { + // FIXME - In some cases this should be from the TBE, not the cache. + // may send to other mod-L2s + if (L2_TBEs[address].Forward_GetS_IDs.count() > 0) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.SenderMachId := machineID; + out_msg.Destination := L2_TBEs[address].Forward_GetS_IDs; // external nodes + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.NumPendingExtAcks := 0; + DEBUG_EXPR(out_msg.Address); + DEBUG_EXPR(out_msg.Destination); + DEBUG_EXPR(out_msg.DataBlk); + out_msg.MessageSize := MessageSizeType:Data; + } + } + // may send to local L1s + if (L2_TBEs[address].L1_GetS_IDs.count() > 0) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.SenderMachId := machineID; + out_msg.Destination := L2_TBEs[address].L1_GetS_IDs; // internal nodes + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + // TBE -> L2s only + action(bb_dataFromL2CacheToGetSForwardIDs, "\b", desc="Send data from cache to GetS ForwardIDs") { + // FIXME - In some cases this should be from the TBE, not the cache. + if ((L2_TBEs[address].Forward_GetS_IDs.count() > 0) || (L2_TBEs[address].L1_GetS_IDs.count() > 0)) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.SenderMachId := machineID; + out_msg.Destination := L2_TBEs[address].Forward_GetS_IDs; // external nodes + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.NumPendingExtAcks := 0; + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + // TBE -> L2 only + action(gg_dataFromL2CacheToGetXForwardID, "\g", desc="Send data from cache to GetX ForwardID") { + // FIXME - In some cases this should be from the TBE, not the cache. + if (L2_TBEs[address].validForwardedGetXId) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(L2_TBEs[address].Forward_GetX_ID); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.NumPendingExtAcks := L2_TBEs[address].ForwardGetX_AckCount; + DEBUG_EXPR(out_msg.Address); + DEBUG_EXPR(out_msg.Destination); + DEBUG_EXPR(out_msg.DataBlk); + DEBUG_EXPR(out_msg.NumPendingExtAcks); + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + // IMMEDIATE RESPONSES directly from the ForwardRequest queue + // ForwardRequest -> L2 + action(e_dataFromL2CacheToL2Requestor, "e", desc="Send data from cache to requestor") { + peek(forwardedRequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.SenderMachId := machineID; + out_msg.NumPendingExtAcks := in_msg.NumPendingExtAcks; // Needed when in state O and we see a GetX + out_msg.Destination.add(in_msg.RequestorMachId); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + DEBUG_EXPR(out_msg.Address); + DEBUG_EXPR(out_msg.Destination); + DEBUG_EXPR(out_msg.DataBlk); + DEBUG_EXPR(out_msg.NumPendingExtAcks); + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + // ForwardRequest -> L1 + action(k_dataFromL2CacheToL1Requestor, "k", desc="Send data from cache to L1 requestor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(in_msg.RequestorMachId); + DEBUG_EXPR(out_msg.Destination); + out_msg.DataBlk := getL2CacheEntry(address).DataBlk; + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + // OTHER ACTIONS + action(i_allocateTBE, "i", desc="Allocate TBE for internal/external request(isPrefetch=0, number of invalidates=0)") { + check_allocate(L2_TBEs); + L2_TBEs.allocate(address); + L2_TBEs[address].NumPendingIntAcks := 0; // default value + L2_TBEs[address].NumPendingExtAcks := 0; // default value + L2_TBEs[address].isPrefetch := false; + L2_TBEs[address].isThreeHop := false; + L2_TBEs[address].Forward_GetS_IDs.clear(); + L2_TBEs[address].L1_GetS_IDs.clear(); + L2_TBEs[address].validInvalidator := false; + L2_TBEs[address].validForwardedGetXId := false; + L2_TBEs[address].isInternalRequestOnly := false; + } + + action(s_deallocateTBE, "s", desc="Deallocate external TBE") { + L2_TBEs.deallocate(address); + } + + action(jj_popL1RequestQueue, "\j", desc="Pop incoming L1 request queue") { + profileMsgDelay(0, L1RequestIntraChipL2Network_in.dequeue_getDelayCycles()); + } + + action(l_popForwardedRequestQueue, "l", desc="Pop incoming forwarded request queue") { + profileMsgDelay(2, forwardedRequestIntraChipL2Network_in.dequeue_getDelayCycles()); + } + + action(o_popIncomingResponseQueue, "o", desc="Pop Incoming Response queue") { + profileMsgDelay(3, responseIntraChipL2Network_in.dequeue_getDelayCycles()); + } + + action(p_addNumberOfPendingExtAcks, "p", desc="Add number of pending acks to TBE") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + DEBUG_EXPR(L2_TBEs[address].NumPendingExtAcks); + L2_TBEs[address].NumPendingExtAcks := L2_TBEs[address].NumPendingExtAcks + in_msg.NumPendingExtAcks; + DEBUG_EXPR(in_msg.NumPendingExtAcks); + DEBUG_EXPR(L2_TBEs[address].NumPendingExtAcks); + } + } + + action(q_decrementNumberOfPendingExtAcks, "q", desc="Decrement number of pending ext invalidations by one") { + DEBUG_EXPR(L2_TBEs[address].NumPendingExtAcks); + L2_TBEs[address].NumPendingExtAcks := L2_TBEs[address].NumPendingExtAcks - 1; + DEBUG_EXPR(L2_TBEs[address].NumPendingExtAcks); + } + + action(r_decrementNumberOfPendingIntAcks, "r", desc="Decrement number of pending int invalidations by one") { + DEBUG_EXPR(L2_TBEs[address].NumPendingExtAcks); + L2_TBEs[address].NumPendingIntAcks := L2_TBEs[address].NumPendingIntAcks - 1; + DEBUG_EXPR(L2_TBEs[address].NumPendingExtAcks); + } + + action(t_sendAckToInvalidator, "t", desc="Send ack to invalidator") { + peek(forwardedRequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(in_msg.RequestorMachId); + DEBUG_EXPR(out_msg.Destination); + out_msg.NumPendingExtAcks := 0; + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + + action(u_writeDataFromResponseQueueToL2Cache, "u", desc="Write data from response queue to cache") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + getL2CacheEntry(address).DataBlk := in_msg.DataBlk; + } + } + + // FIXME - probably need to change this to a seperate low priority request queue + action(m_writeDataFromRequestQueueToL2Cache, "m", desc="Write data from response queue to cache") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + getL2CacheEntry(address).DataBlk := in_msg.DataBlk; + } + } + + action(x_copyDataFromL2CacheToTBE, "x", desc="Copy data from cache to TBE") { + L2_TBEs[address].DataBlk := getL2CacheEntry(address).DataBlk; + } + + action(y_dataFromTBEToRequestor, "y", desc="Send data from TBE to requestor") { + peek(forwardedRequestIntraChipL2Network_in, RequestMsg) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.SenderMachId := machineID; + out_msg.NumPendingExtAcks := in_msg.NumPendingExtAcks; + out_msg.Destination.add(in_msg.RequestorMachId); + out_msg.DataBlk := L2_TBEs[address].DataBlk; + DEBUG_EXPR(out_msg.Address); + DEBUG_EXPR(out_msg.Destination); + DEBUG_EXPR(out_msg.DataBlk); + DEBUG_EXPR(out_msg.NumPendingExtAcks); + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + action(zz_sendAckToQueuedInvalidator, "\z", desc="Send ack to invalidator") { + if (L2_TBEs[address].validInvalidator) { + enqueue(responseIntraChipL2Network_out, ResponseMsg, latency="L2_RESPONSE_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:ACK; + out_msg.SenderMachId := machineID; + out_msg.Destination.add(L2_TBEs[address].InvalidatorID); + DEBUG_EXPR(out_msg.Destination); + out_msg.NumPendingExtAcks := 0; + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + + action(z_stall, "z", desc="Stall") { + } + + action(yy_recordInvalidatorID, "\y", desc="Record Invalidator for future response") { + peek(forwardedRequestIntraChipL2Network_in, RequestMsg) { + L2_TBEs[address].InvalidatorID := in_msg.RequestorMachId; + L2_TBEs[address].validInvalidator := true; + } + } + + action(dd_recordGetSForwardID, "\d", desc="Record forwarded GetS for future forwarding") { + peek(forwardedRequestIntraChipL2Network_in, RequestMsg) { + L2_TBEs[address].Forward_GetS_IDs.add(in_msg.RequestorMachId); + } + } + + action(ss_recordGetSL1ID, "\s", desc="Record forwarded L1 GetS for load response") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2_TBEs[address].L1_GetS_IDs.add(in_msg.RequestorMachId); + } + } + + action(ii_recordGetXForwardID, "\i", desc="Record forwarded GetX and ack count for future forwarding") { + peek(forwardedRequestIntraChipL2Network_in, RequestMsg) { + L2_TBEs[address].Forward_GetX_ID := in_msg.RequestorMachId; + L2_TBEs[address].ForwardGetX_AckCount := in_msg.NumPendingExtAcks; + L2_TBEs[address].validForwardedGetXId := true; + } + } + + action(xx_recordGetXL1ID, "\x", desc="Record L1 GetX for store response") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2_TBEs[address].L1_GetX_ID := in_msg.RequestorMachId; + } + } + + action(set_setMRU, "\set", desc="set the MRU entry") { + L2cacheMemory.setMRU(address); + } + + action(bbb_setPendingIntAcksToSharers, "\bb", desc="Set number of pending acks equal to number of sharers") { + L2_TBEs[address].NumPendingIntAcks := L2cacheMemory[address].Sharers.count(); + } + + action(ddd_setPendingIntAcksToOne, "\dd", desc="Set number of pending acks equal to one") { + L2_TBEs[address].NumPendingIntAcks := 1; + } + + action(ccc_setPendingIntAcksMinusOne, "\cc", desc="Set number of pending acks equal to number of sharers minus one") { + L2_TBEs[address].NumPendingIntAcks := L2cacheMemory[address].Sharers.count() - 1; + } + + action(qq_allocateL2CacheBlock, "\q", desc="Set L2 cache tag equal to tag of block B.") { + if (L2cacheMemory.isTagPresent(address) == false) { + L2cacheMemory.allocate(address); + } + } + + action(rr_deallocateL2CacheBlock, "\r", desc="Deallocate L2 cache block. Sets the cache to not present, allowing a replacement in parallel with a fetch.") { + L2cacheMemory.deallocate(address); + } + + action(uu_profileMiss, "\u", desc="Profile the demand miss") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + profile_L2Cache_miss(convertToGenericType(in_msg.Type), in_msg.AccessMode, MessageSizeTypeToInt(in_msg.MessageSize), in_msg.Prefetch, L1CacheMachIDToProcessorNum(in_msg.RequestorMachId)); + } + } + + action(ww_profileMissNoDir, "\w", desc="Profile this transition at the L2 because Dir won't see the request") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + profile_request(in_msg.L1CacheStateStr, getStateStr(address), "NA", getCoherenceRequestTypeStr(in_msg.Type)); + } + } + + action(v_issueInvalidateIntL1copyRequest, "v", desc="invalidate the L1 M copy") { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.RequestorMachId := machineID; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(tt_issueSharedInvalidateIntL1copiesRequest, "\t", desc="invalidate all L1 S copies") { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV_S; + out_msg.RequestorMachId := machineID; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(vv_issueInvalidateOtherIntL1copiesRequest, "\v", desc="invalidate other L1 copies not the local requestor") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + if ((L2cacheMemory[address].Sharers.count() > 1) || (L2cacheMemory[address].Sharers.isElement(in_msg.RequestorMachId) != true)) { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV_S; + out_msg.RequestorMachId := machineID; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.Destination.remove(in_msg.RequestorMachId); + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + } + + action(g_issueDownGradeIntL1copiesRequest, "g", desc="DownGrade L1 copy") { + enqueue(L1RequestIntraChipL2Network_out, RequestMsg, latency="L2_REQUEST_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:L1_DG; + out_msg.RequestorMachId := machineID; + out_msg.Destination := L2cacheMemory[address].Sharers; + out_msg.MessageSize := MessageSizeType:Control; + } + } + + action(nn_addSharer, "\n", desc="Add L1 sharer to list") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + addSharer(address, in_msg.RequestorMachId); + } + } + + action(kk_removeRequestSharer, "\k", desc="Remove L1 Request sharer from list") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2cacheMemory[address].Sharers.remove(in_msg.RequestorMachId); + } + } + + action(aa_removeResponseSharer, "\a", desc="Remove L1 Response sharer from list") { + peek(responseIntraChipL2Network_in, ResponseMsg) { + L2cacheMemory[address].Sharers.remove(in_msg.SenderMachId); + } + } + + action(ll_clearSharers, "\l", desc="Remove all L1 sharers from list") { + peek(L1RequestIntraChipL2Network_in, RequestMsg) { + L2cacheMemory[address].Sharers.clear(); + } + } + + //***************************************************** + // TRANSITIONS + //***************************************************** + + //=============================================== + // STALLS + + // Stalls L2 Replacement and L1 PUT for all transient states + transition({L2_IS, L2_ISZ, L2_ISI, L2_IMV, L2_MV, L2_IM, L2_IMO, L2_IMI, L2_IMZ, L2_IMOI, L2_IMOZ, + L2_SIV, L2_SIC, + L2_MIV, L2_MIN, L2_MIC, L2_MIT, L2_MO, L2_MOIC, L2_MOICR, L2_MOZ, + L2_OIV, L2_OIN, L2_OIC, L2_OMV, L2_OM}, + {L2_Replacement, L1_PUTX, L1_PUTX_last, L1_PUTS, L1_PUTS_last, L1_PUTX_old, L1_PUTS_old, }) { + z_stall; + } + + //=============================================== + // old L1_PUT requests + + transition({L2_NP, L2_I, L2_S, L2_SS, L2_M, L2_MT, L2_O, L2_SO}, {L1_PUTX_old, L1_PUTS_old}) { + w_sendPutAckToL1Cache; + jj_popL1RequestQueue; + } + + //=============================================== + // BASE STATE - I + + // Transitions from I (Idle) + transition({L2_NP,L2_I}, L2_Replacement) { + rr_deallocateL2CacheBlock; + } + + transition({L2_NP,L2_I}, L2_INV) { // could see an invalidate from the directory, but not Forwards + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + transition({L2_NP,L2_I}, L1_GETS, L2_IS) { + qq_allocateL2CacheBlock; + ll_clearSharers; + nn_addSharer; + i_allocateTBE; + ss_recordGetSL1ID; + a_issueGETS; + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition({L2_NP,L2_I}, L1_GET_INSTR, L2_IS) { + qq_allocateL2CacheBlock; + ll_clearSharers; + nn_addSharer; + i_allocateTBE; + ss_recordGetSL1ID; + f_issueGETINSTR; + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition({L2_NP,L2_I}, {L1_GETX, L1_UPGRADE, L1_UPGRADE_no_others}, L2_IM) { // UPGRADE possible because L2_Replacement have higher priority + qq_allocateL2CacheBlock; + ll_clearSharers; + nn_addSharer; + i_allocateTBE; + xx_recordGetXL1ID; + b_issueGETX; + uu_profileMiss; + jj_popL1RequestQueue; + } + + // Transitions from L2_IS + // could see L2_INVs or more L1 requests + transition(L2_IS, L2_INV, L2_ISI) { // could see an invalidate from the directory, but not Forwards + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + transition(L2_IS, Data_ext_ack_0, L2_SS) { + u_writeDataFromResponseQueueToL2Cache; + h_issueLoadHit; + c_finalAckToDirIfNeeded; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(L2_IS, {L1_GETS,L1_GET_INSTR}) { + set_setMRU; + ww_profileMissNoDir; + nn_addSharer; + ss_recordGetSL1ID; + jj_popL1RequestQueue; + } + + transition(L2_IS, L1_GETX, L2_ISZ) { // don't go there, just go to stall state + z_stall; + } + + // Transitions from L2_ISZ + // could see L2_INVs or more L1 requests + // stall all L1 requests, wait for data + transition(L2_ISZ, L2_INV, L2_ISI) { // could see an invalidate from the directory, but not Forwards + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + transition(L2_ISZ, Data_ext_ack_0, L2_SS) { + u_writeDataFromResponseQueueToL2Cache; + h_issueLoadHit; + c_finalAckToDirIfNeeded; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(L2_ISZ, {L1_GETS, L1_GET_INSTR, L1_GETX}) { + z_stall; + } + + // Transitions from L2_ISI, already sent the invalidate ack so can imediately go to I + // - in ISI, could get data from the Proc whose GETX caused INV to go from IS to ISI + // or, could get data from Dir if Dir's data lost race to Dir's INV + // or, could get data from Dir, if my GETS took forever to get to Dir, and the GETX + // processor already wrote it back + transition(L2_ISI, Data_ext_ack_0, L2_I) { + u_writeDataFromResponseQueueToL2Cache; + oo_issueLoadHitInv; + c_finalAckToDirIfNeeded; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(L2_ISI, L2_INV) { // could see an invalidate from the directory, but not Forwards + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + transition(L2_ISI, {L1_GETS, L1_GET_INSTR, L1_GETX}) { // stall all L1 requests + z_stall; + } + + // Transitions from L2_IMV, waiting for int_acks + // currently stall all request + // could see forwards and/or more L1 requests + transition(L2_IMV, L2_INV) { // could see an invalidate for SS + yy_recordInvalidatorID; + l_popForwardedRequestQueue; + } + + // stall all Forwarded request + transition(L2_IMV, {Forwarded_GETS, Forwarded_GET_INSTR, Forwarded_GETX}) { + z_stall; + } + + // stall all L1 request + transition(L2_IMV, {L1_GETS, L1_GET_INSTR, L1_GETX, L1_UPGRADE, L1_UPGRADE_no_others}) { + z_stall; + } + + transition(L2_IMV, {Data_ext_ack_0, Data_ext_ack_not_0_last}, L2_MV) { + u_writeDataFromResponseQueueToL2Cache; + c_finalAckToDirIfNeeded; + o_popIncomingResponseQueue; + } + + transition(L2_IMV, Data_ext_ack_not_0) { + u_writeDataFromResponseQueueToL2Cache; + p_addNumberOfPendingExtAcks; + mm_rememberIfFinalAckNeeded; + o_popIncomingResponseQueue; + } + + transition(L2_IMV, Proc_ext_ack) { + q_decrementNumberOfPendingExtAcks; + o_popIncomingResponseQueue; + } + + transition(L2_IMV, Proc_last_ext_ack, L2_MV) { + n_sendFinalAckIfThreeHop; + o_popIncomingResponseQueue; + } + + transition(L2_IMV, Proc_int_ack) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks; + o_popIncomingResponseQueue; + } + + transition(L2_IMV, Proc_last_int_ack, L2_IM) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks; + o_popIncomingResponseQueue; + zz_sendAckToQueuedInvalidator; + } + + // Transitions from L2_MV, waiting for int_acks + // external world gave us write permission + + // stall all Forwarded request + transition(L2_MV, {Forwarded_GETS, Forwarded_GET_INSTR, Forwarded_GETX}) { + z_stall; + } + + // stall all L1 request + transition(L2_MV, {L1_GETS, L1_GET_INSTR, L1_GETX, L1_UPGRADE, L1_UPGRADE_no_others}) { + z_stall; + } + + transition(L2_MV, Proc_int_ack) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks; + o_popIncomingResponseQueue; + } + + transition(L2_MV, Proc_last_int_ack, L2_MT) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks; + hh_issueStoreHit; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // Transitions from L2_IM, waiting for external data before going to MT state + // could see forwards and/or more L1 requests + transition(L2_IM, L2_INV) { // could see an invalidate from the directory (earlier epoch) + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + transition(L2_IM, {Forwarded_GETS,Forwarded_GET_INSTR}, L2_IMO) { // could see Forwards, if directory responses get out-of-order + dd_recordGetSForwardID; + l_popForwardedRequestQueue; + } + + transition(L2_IM, {L1_GETS,L1_GET_INSTR}, L2_IMO) { + set_setMRU; + ww_profileMissNoDir; + nn_addSharer; + ss_recordGetSL1ID; + jj_popL1RequestQueue; + } + + transition(L2_IM, Forwarded_GETX, L2_IMI) { // could see Forwards, if directory requests get ahead of responses + ii_recordGetXForwardID; + l_popForwardedRequestQueue; + } + + transition(L2_IM, L1_GETX, L2_IMZ) { // don't go there, just go to stall state + z_stall; + } + + transition(L2_IM, {Data_ext_ack_0, Data_ext_ack_not_0_last}, L2_MT) { + u_writeDataFromResponseQueueToL2Cache; + hh_issueStoreHit; + c_finalAckToDirIfNeeded; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(L2_IM, Data_ext_ack_not_0) { + u_writeDataFromResponseQueueToL2Cache; + p_addNumberOfPendingExtAcks; + mm_rememberIfFinalAckNeeded; + o_popIncomingResponseQueue; + } + + transition(L2_IM, Proc_ext_ack) { + q_decrementNumberOfPendingExtAcks; + o_popIncomingResponseQueue; + } + + transition(L2_IM, Proc_last_ext_ack, L2_MT) { + hh_issueStoreHit; + n_sendFinalAckIfThreeHop; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // transitions from L2_IMO + transition(L2_IMO, L2_INV) { // could see an invalidate from the directory (earlier epoch) + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + transition(L2_IMO, {Forwarded_GETS,Forwarded_GET_INSTR}) { // could see Forwards + dd_recordGetSForwardID; + l_popForwardedRequestQueue; + } + + transition(L2_IMO, Forwarded_GETX, L2_IMOI) { // could see Forwards + ii_recordGetXForwardID; + l_popForwardedRequestQueue; + } + + transition(L2_IMO, {L1_GETS,L1_GET_INSTR}) { + set_setMRU; + ww_profileMissNoDir; + nn_addSharer; + ss_recordGetSL1ID; + jj_popL1RequestQueue; + } + + transition(L2_IMO, L1_GETX, L2_IMOZ) { + z_stall; + } + + transition(L2_IMO, {Data_ext_ack_0, Data_ext_ack_not_0_last}, L2_MO) { + u_writeDataFromResponseQueueToL2Cache; + cc_issueStoreHitDG; + ddd_setPendingIntAcksToOne; + c_finalAckToDirIfNeeded; + o_popIncomingResponseQueue; + } + + transition(L2_IMO, Data_ext_ack_not_0) { + u_writeDataFromResponseQueueToL2Cache; + p_addNumberOfPendingExtAcks; + mm_rememberIfFinalAckNeeded; + o_popIncomingResponseQueue; + } + + transition(L2_IMO, Proc_ext_ack) { + q_decrementNumberOfPendingExtAcks; + o_popIncomingResponseQueue; + } + + transition(L2_IMO, Proc_last_ext_ack, L2_MO) { + n_sendFinalAckIfThreeHop; + cc_issueStoreHitDG; + ddd_setPendingIntAcksToOne; + o_popIncomingResponseQueue; + } + + // transitions from L2_IMI + // the directory put us in this state so it should tell us nothing (i.e. don't worry about INV or Forwards) + // stall all L1 request + transition(L2_IMI, {Data_ext_ack_0, Data_ext_ack_not_0_last}, L2_MIC) { + u_writeDataFromResponseQueueToL2Cache; + pp_issueStoreHitInv; + ddd_setPendingIntAcksToOne; + c_finalAckToDirIfNeeded; + o_popIncomingResponseQueue; + } + + transition(L2_IMI, Data_ext_ack_not_0) { + u_writeDataFromResponseQueueToL2Cache; + p_addNumberOfPendingExtAcks; + mm_rememberIfFinalAckNeeded; + o_popIncomingResponseQueue; + } + + transition(L2_IMI, Proc_ext_ack) { + q_decrementNumberOfPendingExtAcks; + o_popIncomingResponseQueue; + } + + transition(L2_IMI, Proc_last_ext_ack, L2_MIC) { + n_sendFinalAckIfThreeHop; + pp_issueStoreHitInv; + ddd_setPendingIntAcksToOne; + o_popIncomingResponseQueue; + } + + transition(L2_IMI, {L1_GETS, L1_GET_INSTR, L1_GETX}) { // stall all L1 requests + z_stall; + } + + // transistions from L2_IMZ + // just wait for all acks and data + // stall on all requests + // NOTE: A performance option might be possible to go into M state instead of MT + transition(L2_IMZ, {Data_ext_ack_0, Data_ext_ack_not_0_last}, L2_MT) { + u_writeDataFromResponseQueueToL2Cache; + hh_issueStoreHit; + c_finalAckToDirIfNeeded; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(L2_IMZ, Data_ext_ack_not_0) { + u_writeDataFromResponseQueueToL2Cache; + p_addNumberOfPendingExtAcks; + mm_rememberIfFinalAckNeeded; + o_popIncomingResponseQueue; + } + + transition(L2_IMZ, Proc_ext_ack) { + q_decrementNumberOfPendingExtAcks; + o_popIncomingResponseQueue; + } + + transition(L2_IMZ, Proc_last_ext_ack, L2_MT) { + hh_issueStoreHit; + n_sendFinalAckIfThreeHop; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(L2_IMZ, L2_INV) { // could see an invalidate from the directory (earlier epoch) + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + transition(L2_IMZ, {Forwarded_GETS, Forwarded_GET_INSTR, Forwarded_GETX, L1_GETS, L1_GET_INSTR, L1_GETX}) { + z_stall; + } + + // transitions from L2_IMOI + // the directory put us in this state so it should tell us nothing (i.e. don't worry about INV or Forwards) + // stall all L1 requests + transition(L2_IMOI, {Data_ext_ack_0, Data_ext_ack_not_0_last}, L2_MOICR) { + u_writeDataFromResponseQueueToL2Cache; + pp_issueStoreHitInv; + ddd_setPendingIntAcksToOne; + c_finalAckToDirIfNeeded; + o_popIncomingResponseQueue; + } + + transition(L2_IMOI, Data_ext_ack_not_0) { + u_writeDataFromResponseQueueToL2Cache; + p_addNumberOfPendingExtAcks; + mm_rememberIfFinalAckNeeded; + o_popIncomingResponseQueue; + } + + transition(L2_IMOI, Proc_ext_ack) { + q_decrementNumberOfPendingExtAcks; + o_popIncomingResponseQueue; + } + + transition(L2_IMOI, Proc_last_ext_ack, L2_MOICR) { + n_sendFinalAckIfThreeHop; + pp_issueStoreHitInv; + ddd_setPendingIntAcksToOne; + o_popIncomingResponseQueue; + } + + transition(L2_IMOI, {L1_GETS, L1_GET_INSTR, L1_GETX}) { // stall all L1 requests + z_stall; + } + + // transitions from L2_IMOZ + // just wait for all acks and data + // stall on all requests + transition(L2_IMOZ, L2_INV) { // could see an invalidate from the directory (earlier epoch) + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + transition(L2_IMOZ, {Data_ext_ack_0, Data_ext_ack_not_0_last}, L2_MOZ) { + u_writeDataFromResponseQueueToL2Cache; + cc_issueStoreHitDG; + ddd_setPendingIntAcksToOne; + c_finalAckToDirIfNeeded; + o_popIncomingResponseQueue; + } + + transition(L2_IMOZ, Data_ext_ack_not_0) { + u_writeDataFromResponseQueueToL2Cache; + p_addNumberOfPendingExtAcks; + mm_rememberIfFinalAckNeeded; + o_popIncomingResponseQueue; + } + + transition(L2_IMOZ, Proc_ext_ack) { + q_decrementNumberOfPendingExtAcks; + o_popIncomingResponseQueue; + } + + transition(L2_IMOZ, Proc_last_ext_ack, L2_MOZ) { + cc_issueStoreHitDG; + ddd_setPendingIntAcksToOne; + n_sendFinalAckIfThreeHop; + o_popIncomingResponseQueue; + } + + // stall on all requests + transition(L2_IMOZ, {Forwarded_GETS, Forwarded_GET_INSTR, Forwarded_GETX, L1_GETS, L1_GET_INSTR, L1_GETX}) { + z_stall; + } + + // =============================================== + // BASE STATE - S + // Transitions from S, no L1 copies + transition(L2_S, L2_Replacement, L2_I) { + rr_deallocateL2CacheBlock; + } + + transition(L2_S, L2_INV, L2_I) { // could see an invalidate from the directory, but not Forwards + t_sendAckToInvalidator; + l_popForwardedRequestQueue; + } + + transition(L2_S, {L1_GETS, L1_GET_INSTR}, L2_SS) { + set_setMRU; + ww_profileMissNoDir; + nn_addSharer; + k_dataFromL2CacheToL1Requestor; + jj_popL1RequestQueue; + } + + transition(L2_S, L1_GETX, L2_IM) { + set_setMRU; + nn_addSharer; + i_allocateTBE; + xx_recordGetXL1ID; + b_issueGETX; + uu_profileMiss; + jj_popL1RequestQueue; + } + + // BASE STATE - SS + // Transitions from SS, L1 copies + transition(L2_SS, L2_Replacement, L2_SIV) { + i_allocateTBE; // for internal request + bbb_setPendingIntAcksToSharers; + tt_issueSharedInvalidateIntL1copiesRequest; + } + + transition(L2_SS, L2_INV, L2_SIC) { + i_allocateTBE; // for internal request + yy_recordInvalidatorID; + bbb_setPendingIntAcksToSharers; + tt_issueSharedInvalidateIntL1copiesRequest; + l_popForwardedRequestQueue; + } + + transition(L2_SS, {L1_GETS, L1_GET_INSTR}) { + set_setMRU; + ww_profileMissNoDir; + nn_addSharer; + k_dataFromL2CacheToL1Requestor; + jj_popL1RequestQueue; + } + + transition(L2_SS, L1_UPGRADE_no_others, L2_IM) { + set_setMRU; + i_allocateTBE; // for both ext. and int. + xx_recordGetXL1ID; + b_issueGETX; // for external + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition(L2_SS, L1_UPGRADE, L2_IMV) { + set_setMRU; + i_allocateTBE; // for both ext. and int. + xx_recordGetXL1ID; + ccc_setPendingIntAcksMinusOne; + vv_issueInvalidateOtherIntL1copiesRequest; // for internal + b_issueGETX; // for external + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition(L2_SS, L1_GETX, L2_IMV) { + set_setMRU; + i_allocateTBE; // for both ext. and int. + xx_recordGetXL1ID; + bbb_setPendingIntAcksToSharers; + vv_issueInvalidateOtherIntL1copiesRequest; // for internal + nn_addSharer; + b_issueGETX; // for external + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition(L2_SS, L1_PUTS) { + ww_profileMissNoDir; + w_sendPutAckToL1Cache; + kk_removeRequestSharer; + jj_popL1RequestQueue; + } + + transition(L2_SS, L1_PUTS_last, L2_S) { + ww_profileMissNoDir; + w_sendPutAckToL1Cache; + kk_removeRequestSharer; + jj_popL1RequestQueue; + } + + // Transitions from SIC - Initiated by an invalidate + transition(L2_SIC, Proc_int_ack) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks; + o_popIncomingResponseQueue; + } + + transition(L2_SIC, Proc_last_int_ack, L2_I) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks; + o_popIncomingResponseQueue; + zz_sendAckToQueuedInvalidator; + s_deallocateTBE; + } + + transition(L2_SIC, L2_INV) { // could see an invalidate from the directory, but not Forwards + l_popForwardedRequestQueue; // ignore: already know an ack must be sent to the directory + } + + transition(L2_SIC, {L1_GETS, L1_GET_INSTR, L1_UPGRADE, L1_UPGRADE_no_others, L1_GETX}) { // stall on all L1 requests + z_stall; + } + + // Transitions from SIV - initiated by a L2_Replacement + transition(L2_SIV, Proc_int_ack) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks; + o_popIncomingResponseQueue; + } + + transition(L2_SIV, Proc_last_int_ack, L2_I) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks; + o_popIncomingResponseQueue; + s_deallocateTBE; + rr_deallocateL2CacheBlock; + } + + transition(L2_SIV, L2_INV) { // could see an invalidate from the directory, but not Forwards + z_stall; // guarenteed to receive all acks thus moving the state to I where the L2_INV can be handled + } + + transition(L2_SIV, {L1_GETS, L1_GET_INSTR, L1_UPGRADE, L1_UPGRADE_no_others, L1_GETX}) { // stall on all L1 requests + z_stall; + } + + // =============================================== + // BASE STATE - M + // Transitions from M, no L1 copies + transition(L2_M, L2_Replacement, L2_MIN) { + i_allocateTBE; + d_issuePUTX; + x_copyDataFromL2CacheToTBE; + rr_deallocateL2CacheBlock; + } + + transition(L2_M, {Forwarded_GETS,Forwarded_GET_INSTR}, L2_O) { // can see forwards, not inv + e_dataFromL2CacheToL2Requestor; + l_popForwardedRequestQueue; + } + + transition(L2_M, Forwarded_GETX, L2_I) { // can see forwards, not inv + e_dataFromL2CacheToL2Requestor; + l_popForwardedRequestQueue; + } + + transition(L2_M, {L1_GETS, L1_GET_INSTR}, L2_SO) { // FIXME FOR BETTER PERFORMANCE - an E state would be nice here + set_setMRU; + ww_profileMissNoDir; + nn_addSharer; + k_dataFromL2CacheToL1Requestor; + jj_popL1RequestQueue; + } + + transition(L2_M, L1_GETX, L2_MT) { + set_setMRU; + ww_profileMissNoDir; + nn_addSharer; + k_dataFromL2CacheToL1Requestor; + jj_popL1RequestQueue; + } + + // BASE STATE - MT + // Transitions from MT, M L1 copy + transition(L2_MT, L2_Replacement, L2_MIV) { + i_allocateTBE; + bbb_setPendingIntAcksToSharers; + v_issueInvalidateIntL1copyRequest; + } + + transition(L2_MT, {Forwarded_GETS, Forwarded_GET_INSTR}, L2_MO) { // can see forwards, not inv + i_allocateTBE; + bbb_setPendingIntAcksToSharers; + g_issueDownGradeIntL1copiesRequest; + dd_recordGetSForwardID; + l_popForwardedRequestQueue; + } + + transition(L2_MT, {L1_GETS, L1_GET_INSTR}, L2_MO) { + set_setMRU; + ww_profileMissNoDir; + i_allocateTBE; + bbb_setPendingIntAcksToSharers; + g_issueDownGradeIntL1copiesRequest; + ss_recordGetSL1ID; + nn_addSharer; + jj_popL1RequestQueue; + } + + transition(L2_MT, Forwarded_GETX, L2_MIC) { // can see forwards, not inv + i_allocateTBE; + bbb_setPendingIntAcksToSharers; + v_issueInvalidateIntL1copyRequest; + ii_recordGetXForwardID; + l_popForwardedRequestQueue; + } + + transition(L2_MT, L1_GETX, L2_MIT) { + set_setMRU; + ww_profileMissNoDir; + i_allocateTBE; + bbb_setPendingIntAcksToSharers; + v_issueInvalidateIntL1copyRequest; + nn_addSharer; + xx_recordGetXL1ID; + jj_popL1RequestQueue; + } + + transition(L2_MT, L1_PUTX_last, L2_M) { + ww_profileMissNoDir; + w_sendPutAckToL1Cache; + kk_removeRequestSharer; + m_writeDataFromRequestQueueToL2Cache; + jj_popL1RequestQueue; + } + + // Transitions from L2_MIV, waiting for local L1 response + transition(L2_MIV, Data_int_ack, L2_MIN) { + aa_removeResponseSharer; + u_writeDataFromResponseQueueToL2Cache; + bb_dataFromL2CacheToGetSForwardIDs; // likely won't send any messages + gg_dataFromL2CacheToGetXForwardID; // likely won't send any messages + d_issuePUTX; + x_copyDataFromL2CacheToTBE; + rr_deallocateL2CacheBlock; + o_popIncomingResponseQueue; + } + + transition(L2_MIV, {Forwarded_GETS,Forwarded_GET_INSTR}) { // could see Forwards + dd_recordGetSForwardID; + l_popForwardedRequestQueue; + } + + transition(L2_MIV, Forwarded_GETX) { // could see Forwards + ii_recordGetXForwardID; + l_popForwardedRequestQueue; + } + + transition(L2_MIV, {L1_GETS, L1_GET_INSTR, L1_GETX}) { // stall on all L1 requests + z_stall; + } + + // Transitions from L2_MIN, waiting for directory ack + transition(L2_MIN, {Forwarded_GETS,Forwarded_GET_INSTR}) { // could see Forwards + y_dataFromTBEToRequestor; + l_popForwardedRequestQueue; + } + + transition(L2_MIN, Forwarded_GETX) { // could see Forwards + y_dataFromTBEToRequestor; + l_popForwardedRequestQueue; + } + + transition(L2_MIN, Dir_WB_ack, L2_I) { + s_deallocateTBE; + l_popForwardedRequestQueue; + } + + transition(L2_MIN, {L1_GETS, L1_GET_INSTR, L1_GETX}) { // stall all L1 requests + z_stall; + } + + // Transitions from L2_MIC, waiting for local L1 response + // Directory put us in this state with a forwarded GetX + // therefore we shouldn't see anymore forwards + // we stall on all L1 requests + transition(L2_MIC, Data_int_ack, L2_I) { + aa_removeResponseSharer; + u_writeDataFromResponseQueueToL2Cache; + gg_dataFromL2CacheToGetXForwardID; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(L2_MIC, {L1_GETS, L1_GET_INSTR, L1_GETX}) { // stall all L1 requests + z_stall; + } + + // Transitions from L2_MIT, waiting for local L1 response + // A local L1 request put us in this state, so any request are possible + // we currently stall all requests because of the ugly recursive path it could lead us on + // removing some of the blocking here could have major performance benefits + // however one must be careful not to violate cache coherence + transition(L2_MIT, Data_int_ack, L2_MT) { + aa_removeResponseSharer; + u_writeDataFromResponseQueueToL2Cache; + hh_issueStoreHit; // internal requestor + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // stall all requests + transition(L2_MIT, {Forwarded_GETS, Forwarded_GET_INSTR, Forwarded_GETX, L1_GETS, L1_GET_INSTR, L1_GETX}) { + z_stall; + } + + // Transistion from L2_MO, waiting for local L1 data response + // a GetS request put us in this state + // stall must stall if we get a GETX request + transition(L2_MO, Data_int_ack, L2_SO) { + u_writeDataFromResponseQueueToL2Cache; + ee_dataFromL2CacheToGetSIDs; // could be an internal or external requestor + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(L2_MO, {Forwarded_GETS, Forwarded_GET_INSTR}) { // can see forwards, not inv + dd_recordGetSForwardID; + l_popForwardedRequestQueue; + } + + transition(L2_MO, {L1_GETS, L1_GET_INSTR}) { + set_setMRU; + ww_profileMissNoDir; + nn_addSharer; + ss_recordGetSL1ID; + jj_popL1RequestQueue; + } + + transition(L2_MO, Forwarded_GETX, L2_MOIC) { // can see forwards, not inv + ii_recordGetXForwardID; + l_popForwardedRequestQueue; + } + + transition(L2_MO, {L1_GETX, L1_UPGRADE, L1_UPGRADE_no_others}, L2_MOZ) { // don't go there, just go to a stall state + z_stall; + } + + // Transistion from L2_MOIC + // a Forwarded_GETX put us here so we should not see any more forwards + // stall on all L1 requests, once data is received send new data to all queued up L1 shares + // then immediately send invalidate request to those new L1 shared copies + // + // KEY DIFFERENCE: L2_MOICR assumes the L1 data responder moved to I state and removes the sharer, + // while L2_MOIC assumes the L1 data responder moved to S state and doesn't remove the sharer + transition(L2_MOIC, Data_int_ack, L2_OIC) { // need only one ack + u_writeDataFromResponseQueueToL2Cache; + ee_dataFromL2CacheToGetSIDs; + bbb_setPendingIntAcksToSharers; + tt_issueSharedInvalidateIntL1copiesRequest; + o_popIncomingResponseQueue; + } + + transition(L2_MOIC, {L1_GETS, L1_GET_INSTR, L1_GETX, L1_UPGRADE, L1_UPGRADE_no_others}) { + z_stall; + } + + // Transistion from L2_MOICR + // a Forwarded_GETX put us here so we should not see any more forwards + // stall on all L1 requests, once data is received send new data to all queued up L1 shares + // then immediately send invalidate request to those new L1 shared copies + // + // KEY DIFFERENCE: L2_MOICR assumes the L1 data responder moved to I state and removes the sharer, + // while L2_MOIC assumes the L1 data responder moved to S state and doesn't remove the sharer + transition(L2_MOICR, Data_int_ack, L2_OIC) { // need only one ack + aa_removeResponseSharer; + u_writeDataFromResponseQueueToL2Cache; + ee_dataFromL2CacheToGetSIDs; + bbb_setPendingIntAcksToSharers; + tt_issueSharedInvalidateIntL1copiesRequest; + o_popIncomingResponseQueue; + } + + transition(L2_MOICR, {L1_GETS, L1_GET_INSTR, L1_GETX}) { + z_stall; + } + + // L2_MOZ + // simply wait on data + // stall on everything + transition(L2_MOZ, Data_int_ack, L2_SO) { + u_writeDataFromResponseQueueToL2Cache; + ee_dataFromL2CacheToGetSIDs; // could be an internal or external requestor + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + // stall everything + transition(L2_MOZ, {Forwarded_GETS, Forwarded_GET_INSTR, Forwarded_GETX, L1_GETS, L1_GET_INSTR, L1_GETX, L1_UPGRADE, L1_UPGRADE_no_others}) { + z_stall; + } + + // =============================================== + // BASE STATE - O + // Transitions from L2_O, only block cached on the chip + transition(L2_O, L2_Replacement, L2_OIN){ + i_allocateTBE; + x_copyDataFromL2CacheToTBE; + d_issuePUTX; + rr_deallocateL2CacheBlock; + } + + transition(L2_O, {Forwarded_GETS,Forwarded_GET_INSTR}) { + e_dataFromL2CacheToL2Requestor; + l_popForwardedRequestQueue; + } + + transition(L2_O, Forwarded_GETX, L2_I) { + e_dataFromL2CacheToL2Requestor; + l_popForwardedRequestQueue; + } + + transition(L2_O, {L1_GETS, L1_GET_INSTR}, L2_SO) { + set_setMRU; + ww_profileMissNoDir; + nn_addSharer; + k_dataFromL2CacheToL1Requestor; + jj_popL1RequestQueue; + } + + transition(L2_O, L1_GETX, L2_OM) { + set_setMRU; + nn_addSharer; + i_allocateTBE; + xx_recordGetXL1ID; + b_issueGETX; + uu_profileMiss; + jj_popL1RequestQueue; + } + + // BASE STATE - SO + // Transitions from L2_SO, other valid L1 cached copies + transition(L2_SO, L2_Replacement, L2_OIV){ + i_allocateTBE; + x_copyDataFromL2CacheToTBE; + bbb_setPendingIntAcksToSharers; + tt_issueSharedInvalidateIntL1copiesRequest; + } + + transition(L2_SO, {Forwarded_GETS,Forwarded_GET_INSTR}) { + e_dataFromL2CacheToL2Requestor; + l_popForwardedRequestQueue; + } + + transition(L2_SO, Forwarded_GETX, L2_OIC) { + i_allocateTBE; + bbb_setPendingIntAcksToSharers; + ii_recordGetXForwardID; + tt_issueSharedInvalidateIntL1copiesRequest; + l_popForwardedRequestQueue; + } + + transition(L2_SO, {L1_GETS, L1_GET_INSTR}) { + set_setMRU; + ww_profileMissNoDir; + nn_addSharer; + k_dataFromL2CacheToL1Requestor; + jj_popL1RequestQueue; + } + + transition(L2_SO, L1_UPGRADE, L2_OMV) { + set_setMRU; + nn_addSharer; + i_allocateTBE; + xx_recordGetXL1ID; + ccc_setPendingIntAcksMinusOne; + vv_issueInvalidateOtherIntL1copiesRequest; // for internal + b_issueGETX; // for external + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition(L2_SO, L1_UPGRADE_no_others, L2_OM) { + set_setMRU; + i_allocateTBE; + xx_recordGetXL1ID; + b_issueGETX; // for external + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition(L2_SO, L1_GETX, L2_OMV) { + set_setMRU; + i_allocateTBE; + xx_recordGetXL1ID; + bbb_setPendingIntAcksToSharers; + vv_issueInvalidateOtherIntL1copiesRequest; + nn_addSharer; + b_issueGETX; // for external + uu_profileMiss; + jj_popL1RequestQueue; + } + + transition(L2_SO, {L1_PUTS, L1_PUTX}) { // PUTX possible because L2 downgraded before seeing PUTX + ww_profileMissNoDir; + w_sendPutAckToL1Cache; + kk_removeRequestSharer; + jj_popL1RequestQueue; + } + + transition(L2_SO, {L1_PUTS_last, L1_PUTX_last}, L2_O) { // PUTX possible because L2 downgraded before seeing PUTX + ww_profileMissNoDir; + w_sendPutAckToL1Cache; + kk_removeRequestSharer; + jj_popL1RequestQueue; + } + + // Transitions from L2_OIV + // L2 replacement put us here, we must stall all L1 requests + transition(L2_OIV, {Forwarded_GETS, Forwarded_GET_INSTR}) { + y_dataFromTBEToRequestor; + l_popForwardedRequestQueue; + } + + transition(L2_OIV, Forwarded_GETX) { + z_stall; + } + + transition(L2_OIV, Proc_int_ack) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks + o_popIncomingResponseQueue; + } + + transition(L2_OIV, Proc_last_int_ack, L2_OIN) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks + o_popIncomingResponseQueue; + d_issuePUTX; + rr_deallocateL2CacheBlock; + } + + transition(L2_OIV, {L1_UPGRADE, L1_UPGRADE_no_others, L1_GETX, L1_GETS, L1_GET_INSTR}) { // stall L1 requests + z_stall; + } + + // transitions from L2_OIN + // L2 replacement put us here, we must stall all L1 requests + transition(L2_OIN, {Forwarded_GETS, Forwarded_GET_INSTR, Forwarded_GETX}) { + y_dataFromTBEToRequestor; + l_popForwardedRequestQueue; + } + + transition(L2_OIN, Dir_WB_ack, L2_I) { + s_deallocateTBE; + l_popForwardedRequestQueue; + } + + transition(L2_OIN, {L1_UPGRADE, L1_UPGRADE_no_others, L1_GETX, L1_GETS, L1_GET_INSTR}) { // stall L1 requests + z_stall; + } + + // transitions from L2_OIC + // directory put us in this state, should not see any forwards + // we must stall all L1 requests + transition(L2_OIC, Proc_int_ack) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks + o_popIncomingResponseQueue; + } + + transition(L2_OIC, Proc_last_int_ack, L2_I) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks + gg_dataFromL2CacheToGetXForwardID; + s_deallocateTBE; + o_popIncomingResponseQueue; + } + + transition(L2_OIC, {L1_UPGRADE, L1_UPGRADE_no_others, L1_GETX, L1_GETS, L1_GET_INSTR}) { // stall L1 requests + z_stall; + } + + // Transitions from L2_OMV, + // int_acks needed + // waiting to see our Forwarded GETX from the directory + // if we see the Forwarded GETX before all invalidates received, stall + // stall all L1 requests + transition(L2_OMV, Proc_int_ack) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks; + o_popIncomingResponseQueue; + } + + transition(L2_OMV, Proc_last_int_ack, L2_OM) { + aa_removeResponseSharer; + r_decrementNumberOfPendingIntAcks; + o_popIncomingResponseQueue; + } + + transition(L2_OMV, Proc_ext_ack) { + q_decrementNumberOfPendingExtAcks; + o_popIncomingResponseQueue; + } + + transition(L2_OMV, {Forwarded_GETS, Forwarded_GET_INSTR}) { // these are GetS that beat us to the directory + e_dataFromL2CacheToL2Requestor; + l_popForwardedRequestQueue; + } + + transition(L2_OMV, Dir_exe_ack, L2_MV) { + l_popForwardedRequestQueue; + } + + transition(L2_OMV, Forwarded_GETX) { // the Forwarded GetX may or may not be ours, we can't respond until int_acks received + z_stall; + } + + transition(L2_OMV, {L1_UPGRADE, L1_UPGRADE_no_others, L1_GETS, L1_GET_INSTR, L1_GETX}) { // must stall all L1 requests + z_stall; + } + + // Transitions from L2_OM, + // all L1 copies invalid, no int_acks needed + // waiting to see our Forwarded GETX from the directory + // once we see the Forwarded GETX, we can move to IM and wait for the data_ack + // stall all L1 requests + transition(L2_OM, Proc_ext_ack) { + q_decrementNumberOfPendingExtAcks; + o_popIncomingResponseQueue; + } + + transition(L2_OM, {Forwarded_GETS, Forwarded_GET_INSTR}) { // these are GetS that beat us to the directory + e_dataFromL2CacheToL2Requestor; + l_popForwardedRequestQueue; + } + + transition(L2_OM, Forwarded_GETX, L2_IM) { // the Forwarded GetX may or may not be ours + e_dataFromL2CacheToL2Requestor; // we're probably sending a message to ourselves here, but not guarenteed + l_popForwardedRequestQueue; + } + + transition(L2_OM, Dir_exe_ack, L2_MT) { // Directory tells us we already have an exclusive copy + hh_issueStoreHit; + s_deallocateTBE; + l_popForwardedRequestQueue; + } + + transition(L2_OM, {L1_UPGRADE, L1_UPGRADE_no_others, L1_GETS, L1_GET_INSTR, L1_GETX}) { // must stall all L1 requests + z_stall; + } + +} diff --git a/src/mem/protocol/MSI_MOSI_CMP_directory-dir.sm b/src/mem/protocol/MSI_MOSI_CMP_directory-dir.sm new file mode 100644 index 000000000..435bf0eff --- /dev/null +++ b/src/mem/protocol/MSI_MOSI_CMP_directory-dir.sm @@ -0,0 +1,497 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +machine(Directory, "MOSI Directory Optimized") { + + // ** OUT QUEUES ** + MessageBuffer dummyFrom0, network="To", virtual_network="0", ordered="false"; // dummy buffer that shouldn't be used + MessageBuffer dummyFrom1, network="To", virtual_network="1", ordered="false"; // dummy buffer that shouldn't be used + // Dir -> mod-L2 bank - Must be true for the 'opt' and 'GS' protocols BE CAREFUL HERE!!! + MessageBuffer forwardedRequestFromDir, network="To", virtual_network="2", ordered="true"; + MessageBuffer responseFromDir, network="To", virtual_network="3", ordered="false"; // Dir -> mod-L2 bank + MessageBuffer dummyFrom4, network="To", virtual_network="4", ordered="false"; // dummy buffer that shouldn't be used + + // ** IN QUEUES ** + MessageBuffer dummyTo0, network="From", virtual_network="0", ordered="false"; // dummy buffer that shouldn't be used + MessageBuffer requestToDir, network="From", virtual_network="1", ordered="false"; // a mod-L2 bank -> this Dir + MessageBuffer dummyTo2, network="From", virtual_network="2", ordered="false"; // dummy buffer that shouldn't be used + MessageBuffer dummyTo3, network="From", virtual_network="3", ordered="false"; // dummy buffer that shouldn't be used + MessageBuffer finalAckToDir, network="From", virtual_network="4", ordered="false"; // a mod-L2 bank -> this Dir + + // STATES + enumeration(State, desc="Directory states", default="Directory_State_NP") { + // Base states + NP, desc="Not present"; + I, desc="Idle"; + S, desc="Shared"; + O, desc="Owned"; + M, desc="Modified", format="!b"; + OO, desc="transient state of O->GetS/GetInstr->O"; + OM, desc="transient state of O->GetX->M"; + MO, desc="transient state of M->GetS/GetInstr->O"; + MM, desc="transient state of M->GetX->M"; + } + + // Events + enumeration(Event, desc="Directory events") { + GETS, desc="A GETS arrives"; + GET_INSTR, desc=""; + GETX_Owner, desc="A GETX arrives, requestor is owner"; + GETX_NotOwner, desc="A GETX arrives, requestor is not owner"; + PUTX_Owner, "PUTX (requestor is owner)", desc="A PUTX arrives, requestor is owner"; + PUTX_NotOwner, "PUTX (requestor not owner)",desc="A PUTX arrives, requestor is not owner"; + FinalAck, desc=""; + } + + // TYPES + + // DirectoryEntry + structure(Entry, desc="...") { + State DirectoryState, desc="Directory state"; + Set Sharers, desc="Set of sharers - must be L2 caches"; // Note this is a Set and not a NetDest for space concerns + bool DirOwner, default="true", desc="Is dir owner?"; + NodeID ProcOwner, default="0", desc="Processor owner"; // Note this is an int for space concerns + DataBlock DataBlk, desc="data for the block"; + } + + external_type(DirectoryMemory) { + Entry lookup(Address); + bool isPresent(Address); + } + + // ** OBJECTS ** + + DirectoryMemory directory, constructor_hack="i"; + + State getState(Address addr) { + if (directory.isPresent(addr)) { + return directory[addr].DirectoryState; + } + return State:NP; + } + + string getDirStateStr(Address addr) { + return Directory_State_to_string(getState(addr)); + } + + string getRequestTypeStr(CoherenceRequestType type) { + return CoherenceRequestType_to_string(type); + } + + void setState(Address addr, State state) { + if (directory.isPresent(addr)) { + DEBUG_EXPR(addr); + DEBUG_EXPR(directory[addr].DirectoryState); + directory[addr].DirectoryState := state; + DEBUG_EXPR(directory[addr].DirectoryState); + DEBUG_EXPR(state); + } + } + + // ** OUT_PORTS ** + out_port(forwardedRequestNetwork_out, RequestMsg, forwardedRequestFromDir); + out_port(responseNetwork_out, ResponseMsg, responseFromDir); + out_port(ownRequestQueue_out, RequestMsg, requestToDir); + + // ** IN_PORTS ** + in_port(dummyTo0_in, RequestMsg, dummyTo0) { + if (dummyTo0_in.isReady()) { + peek(dummyTo0_in, RequestMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(id); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.RequestorMachId); + } + error("dummyTo0 port should not be used"); + } + } + in_port(dummyTo2_in, RequestMsg, dummyTo2) { + if (dummyTo2_in.isReady()) { + peek(dummyTo2_in, RequestMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(id); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.RequestorMachId); + } + error("dummyTo2 port should not be used"); + } + } + + in_port(dummyTo3_in, RequestMsg, dummyTo3) { + if (dummyTo3_in.isReady()) { + peek(dummyTo3_in, RequestMsg) { + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(id); + DEBUG_EXPR(in_msg.Type); + DEBUG_EXPR(getState(in_msg.Address)); + DEBUG_EXPR(in_msg.RequestorMachId); + } + error("dummyTo3 port should not be used"); + } + } + + in_port(finalAckNetwork_in, ResponseMsg, finalAckToDir){ + if(finalAckNetwork_in.isReady()){ + peek(finalAckNetwork_in, ResponseMsg){ + assert(in_msg.Destination.isElement(machineID)); + if(in_msg.Type == CoherenceResponseType:FINALACK){ + trigger(Event:FinalAck, in_msg.Address); + } else { + error("Invalid message"); + } + } + } + } + + in_port(requestNetwork_in, RequestMsg, requestToDir) { + if (requestNetwork_in.isReady()) { + peek(requestNetwork_in, RequestMsg) { + assert(in_msg.Destination.isElement(machineID)); + if (in_msg.Type == CoherenceRequestType:GETS) { + trigger(Event:GETS, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GET_INSTR) { + trigger(Event:GET_INSTR, in_msg.Address); + } else if (in_msg.Type == CoherenceRequestType:GETX) { + if(directory[in_msg.Address].DirOwner == false && + L2CacheMachIDToChipID(in_msg.RequestorMachId) == directory[in_msg.Address].ProcOwner) { + trigger(Event:GETX_Owner, in_msg.Address); + } else { + trigger(Event:GETX_NotOwner, in_msg.Address); + } + } else if (in_msg.Type == CoherenceRequestType:PUTX) { + if (directory[in_msg.Address].DirOwner == false && + L2CacheMachIDToChipID(in_msg.RequestorMachId) == directory[in_msg.Address].ProcOwner) { + trigger(Event:PUTX_Owner, in_msg.Address); + } else { + trigger(Event:PUTX_NotOwner, in_msg.Address); + } + } else { + error("Invalid message"); + } + } + } + } + + + // Actions + + // a_addRequestorToSharers + + action(a_addRequestorToSharers, "a", desc="Add requestor to list of sharers") { + peek(requestNetwork_in, RequestMsg) { + directory[address].Sharers.add(L2CacheMachIDToChipID(in_msg.RequestorMachId)); + DEBUG_EXPR(directory[address].Sharers); + } + } + + // b_dataToRequestor + + action(b_dataToRequestor, "b", desc="Send data to requestor") { + peek(requestNetwork_in, RequestMsg) { + enqueue(responseNetwork_out, ResponseMsg, latency="MEMORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceResponseType:DATA; + out_msg.SenderMachId := machineID; + if(in_msg.Type == CoherenceRequestType:GETX) { + DEBUG_EXPR(directory[address].Sharers); + DEBUG_EXPR(directory[address].Sharers.count()); + out_msg.NumPendingExtAcks := directory[address].Sharers.count(); + } else { + out_msg.NumPendingExtAcks := 0; // don't need to send pending ack count to GETS requestor + } + out_msg.Destination.add(in_msg.RequestorMachId); + out_msg.DataBlk := directory[address].DataBlk; + DEBUG_EXPR(out_msg.Address); + DEBUG_EXPR(out_msg.DataBlk); + DEBUG_EXPR(out_msg.NumPendingExtAcks); + DEBUG_EXPR(out_msg.Destination); + out_msg.MessageSize := MessageSizeType:Data; + } + } + } + + // d_forwardRequestToOwner + + action(d_forwardRequestToOwner, "d", desc="Forward request to owner") { + peek(requestNetwork_in, RequestMsg) { + enqueue(forwardedRequestNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := in_msg.Type; + out_msg.RequestorMachId := in_msg.RequestorMachId; + out_msg.Destination.add(map_L2ChipId_to_L2Cache(out_msg.Address, directory[address].ProcOwner)); + DEBUG_EXPR(out_msg.Destination); + + if(in_msg.Type == CoherenceRequestType:GETX) { + out_msg.NumPendingExtAcks := directory[address].Sharers.count(); + } else { + out_msg.NumPendingExtAcks := 0; // don't need to send pending ack count to GETS requestor + } + out_msg.MessageSize := MessageSizeType:Control; + DEBUG_EXPR(out_msg.Address); + DEBUG_EXPR(out_msg.NumPendingExtAcks); + DEBUG_EXPR(out_msg.Destination); + } + } + } + + action(f_setOwnerToRequestor, "f", desc="Set owner equal to requestor") { + peek(requestNetwork_in, RequestMsg) { + directory[address].ProcOwner := L2CacheMachIDToChipID(in_msg.RequestorMachId); + directory[address].DirOwner := false; + } + DEBUG_EXPR(directory[address].ProcOwner); + } + + action(g_clearSharers, "g", desc="Clear list of sharers") { + directory[address].Sharers.clear(); + } + + // currently done via multicast message + + action(h_invToSharers, "h", desc="Send INVs to all sharers") { + peek(requestNetwork_in, RequestMsg) { + DEBUG_EXPR(directory[address].Sharers.count()); + if(directory[address].Sharers.count() != 0){ + enqueue(forwardedRequestNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:INV; + out_msg.RequestorMachId := in_msg.RequestorMachId; + DEBUG_EXPR(directory[address].Sharers); + out_msg.Destination := getMultiStaticL2BankNetDest(address, directory[address].Sharers); + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + DEBUG_EXPR(directory[address].Sharers); + } + + action(j_popIncomingRequestQueue, "j", desc="Pop incoming request queue") { + profileMsgDelay(1, requestNetwork_in.dequeue_getDelayCycles()); + } + + action(l_writeRequestDataToMemory, "l", desc="Write PUTX/DWN data to memory") { + peek(requestNetwork_in, RequestMsg) { + directory[in_msg.Address].DataBlk := in_msg.DataBlk; + DEBUG_EXPR(in_msg.Address); + DEBUG_EXPR(in_msg.DataBlk); + } + } + + action(n_writebackAckToRequestor, "n", desc="Send WB_ack to requestor") { + peek(requestNetwork_in, RequestMsg) { + // This needs to be DIRECTORY_LATENCY to keep the queue fifo + enqueue(forwardedRequestNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:WB_ACK; + out_msg.RequestorMachId := machineID; + out_msg.Destination.add(in_msg.RequestorMachId); + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + + action(m_forwardExclusiveRequestToOwner, "m", desc="Send EXE_ack to requestor") { + peek(requestNetwork_in, RequestMsg) { + // This needs to be DIRECTORY_LATENCY to keep the queue fifo + enqueue(forwardedRequestNetwork_out, RequestMsg, latency="DIRECTORY_LATENCY") { + out_msg.Address := address; + out_msg.Type := CoherenceRequestType:EXE_ACK; + out_msg.RequestorMachId := machineID; + out_msg.Destination.add(in_msg.RequestorMachId); + out_msg.MessageSize := MessageSizeType:Control; + } + } + } + + action(uu_profile, "u/", desc="Profile this transition.") { + peek(requestNetwork_in, RequestMsg) { + profile_request(in_msg.L1CacheStateStr, in_msg.L2CacheStateStr, getDirStateStr(address), getRequestTypeStr(in_msg.Type)); + } + } + + action(p_clearOwner, "p", desc="Clear owner") { + directory[address].DirOwner := true; // set owner equal to dir + } + + action(r_addOwnerToSharers, "r", desc="Add owner to list of sharers") { + DEBUG_EXPR(directory[address].ProcOwner); + directory[address].Sharers.add(directory[address].ProcOwner); + DEBUG_EXPR(directory[address].Sharers); + } + + action(t_removeOwnerFromSharers, "t", desc="Remove owner from list of sharers") { + DEBUG_EXPR(directory[address].ProcOwner); + directory[address].Sharers.remove(directory[address].ProcOwner); + DEBUG_EXPR(directory[address].Sharers); + } + + action(u_removeRequestorFromSharers, "u", desc="Remove requestor from list of sharers") { + peek(requestNetwork_in, RequestMsg) { + DEBUG_EXPR(in_msg.RequestorMachId); + directory[address].Sharers.remove(L2CacheMachIDToChipID(in_msg.RequestorMachId)); + DEBUG_EXPR(directory[address].Sharers); + } + } + + action(x_recycleRequest, "x", desc=""){ + peek(requestNetwork_in, RequestMsg) { + enqueue(ownRequestQueue_out, RequestMsg, latency="RECYCLE_LATENCY"){ + out_msg := in_msg; + } + } + } + + action(hh_popFinalAckQueue, "\h", desc=""){ + profileMsgDelay(4, finalAckNetwork_in.dequeue_getDelayCycles()); + } + + //action(z_stall, "z", desc=""){ + //} + + // TRANSITIONS + + transition({OM,MM}, FinalAck, M){ + hh_popFinalAckQueue; + } + transition({OO,MO}, FinalAck, O){ + hh_popFinalAckQueue; + } + + transition({OO, OM, MO, MM}, {GETS, GET_INSTR, GETX_Owner, GETX_NotOwner, PUTX_Owner}){ + x_recycleRequest; + j_popIncomingRequestQueue; + // z_stall; + } + + // --------------------------- + + transition({NP, I, S, M, O, OO, OM, MO, MM}, PUTX_NotOwner) { + uu_profile; + n_writebackAckToRequestor; + j_popIncomingRequestQueue; + } + + // Transitions from Idle + transition({NP,I}, {GETS,GET_INSTR}, S) { + uu_profile; + a_addRequestorToSharers; + b_dataToRequestor; + j_popIncomingRequestQueue; + } + + transition({NP,I}, GETX_NotOwner, M) { + uu_profile; + f_setOwnerToRequestor; + b_dataToRequestor; + j_popIncomingRequestQueue; + } + + // Transitions from Shared + transition(S, {GETS,GET_INSTR}) { + uu_profile; + a_addRequestorToSharers; + b_dataToRequestor; + j_popIncomingRequestQueue; + } + + transition(S, GETX_NotOwner, M) { + uu_profile; + u_removeRequestorFromSharers; + b_dataToRequestor; + f_setOwnerToRequestor; + h_invToSharers; + g_clearSharers; + j_popIncomingRequestQueue; + } + + // Transitions from Owned + transition(O, {GETS,GET_INSTR}, OO) { + uu_profile; + a_addRequestorToSharers; + d_forwardRequestToOwner; + j_popIncomingRequestQueue; + } + + transition(O, {GETX_NotOwner, GETX_Owner}, OM) { + uu_profile; + u_removeRequestorFromSharers; + t_removeOwnerFromSharers; + d_forwardRequestToOwner; + f_setOwnerToRequestor; + h_invToSharers; + g_clearSharers; + j_popIncomingRequestQueue; + } + + transition(O, PUTX_Owner, S) { + uu_profile; + u_removeRequestorFromSharers; + l_writeRequestDataToMemory; + n_writebackAckToRequestor; + p_clearOwner; + j_popIncomingRequestQueue; + } + + + // Transitions from Modified + transition(M, {GETS,GET_INSTR}, MO) { + uu_profile; + a_addRequestorToSharers; + r_addOwnerToSharers; + d_forwardRequestToOwner; + j_popIncomingRequestQueue; + } + + transition(M, GETX_NotOwner, MM) { + uu_profile; + d_forwardRequestToOwner; + f_setOwnerToRequestor; + j_popIncomingRequestQueue; + } + + transition(M, GETX_Owner) { + uu_profile; + m_forwardExclusiveRequestToOwner; + j_popIncomingRequestQueue; + } + + transition(M, PUTX_Owner, I) { + uu_profile; + l_writeRequestDataToMemory; + n_writebackAckToRequestor; + p_clearOwner; + j_popIncomingRequestQueue; + } +} diff --git a/src/mem/protocol/MSI_MOSI_CMP_directory-msg.sm b/src/mem/protocol/MSI_MOSI_CMP_directory-msg.sm new file mode 100644 index 000000000..80ab0f246 --- /dev/null +++ b/src/mem/protocol/MSI_MOSI_CMP_directory-msg.sm @@ -0,0 +1,115 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +// CoherenceRequestType +enumeration(CoherenceRequestType, desc="...") { + GETX, desc="Get eXclusive"; + UPGRADE, desc="UPGRADE to exclusive"; + GETS, desc="Get Shared"; + GET_INSTR, desc="Get Instruction"; + PUTX, desc="Put eXclusive"; + PUTS, desc="Put Shared"; + INV, desc="INValidate"; + INV_S, desc="INValidate the shared version"; + L1_DG, desc="L1 cache DownGrade"; + WB_ACK, desc="Write Back ACKnowledgment"; + EXE_ACK, desc="EXclusivE ACKnowledgment"; +} + +// CoherenceResponseType +enumeration(CoherenceResponseType, desc="...") { + ACK, desc="ACKnowledgment"; + INV_ACK, desc="INValidation ACKnowledgment"; + DG_ACK, desc="DownGrade ACKnowledgment"; + NACK, desc="Negative ACKnowledgment"; + DATA, desc="Data"; + DATA_S, desc="Data to L1 cache, then imediately go to shared state"; + DATA_I, desc="Data to L1 cache, then imediately go to inv state"; + FINALACK, desc=""; +} + +// RequestMsg +structure(RequestMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceRequestType Type, desc="Type of request (GetS, GetX, PutX, etc)"; + AccessModeType AccessMode, desc="user/supervisor access type"; + MachineID RequestorMachId, desc="What component request"; + NetDest Destination, desc="What components receive the request, includes MachineType and num"; + DataBlock DataBlk, desc="Data for the cache line (if PUTX)"; + int NumPendingExtAcks, desc="Number of acks to wait for"; // Needed for forwarded responses only + MessageSizeType MessageSize, desc="size category of the message"; + string L1CacheStateStr, desc="describes L1 cache block state"; + string L2CacheStateStr, desc="describes L2 cache block state"; + PrefetchBit Prefetch, desc="Is this a prefetch request"; +} + +// ResponseMsg +structure(ResponseMsg, desc="...", interface="NetworkMessage") { + Address Address, desc="Physical address for this request"; + CoherenceResponseType Type, desc="Type of response (Ack, Data, etc)"; + MachineID SenderMachId, desc="What component sent the data"; + NetDest Destination, desc="Node to whom the data is sent"; + DataBlock DataBlk, desc="data for the cache line"; + int NumPendingExtAcks, desc="Number of acks to wait for"; + MessageSizeType MessageSize, desc="size category of the message"; +} + +GenericRequestType convertToGenericType(CoherenceRequestType type) { + if(type == CoherenceRequestType:PUTX) { + return GenericRequestType:PUTX; + } else if(type == CoherenceRequestType:GETS) { + return GenericRequestType:GETS; + } else if(type == CoherenceRequestType:GET_INSTR) { + return GenericRequestType:GET_INSTR; + } else if(type == CoherenceRequestType:GETX) { + return GenericRequestType:GETX; + } else if(type == CoherenceRequestType:UPGRADE) { + return GenericRequestType:UPGRADE; + } else if(type == CoherenceRequestType:PUTS) { + return GenericRequestType:PUTS; + } else if(type == CoherenceRequestType:INV) { + return GenericRequestType:INV; + } else if(type == CoherenceRequestType:INV_S) { + return GenericRequestType:INV_S; + } else if(type == CoherenceRequestType:L1_DG) { + return GenericRequestType:DOWNGRADE; + } else if(type == CoherenceRequestType:WB_ACK) { + return GenericRequestType:WB_ACK; + } else if(type == CoherenceRequestType:EXE_ACK) { + return GenericRequestType:EXE_ACK; + } else { + DEBUG_EXPR(type); + error("invalid CoherenceRequestType"); + } +} diff --git a/src/mem/protocol/MSI_MOSI_CMP_directory.slicc b/src/mem/protocol/MSI_MOSI_CMP_directory.slicc new file mode 100644 index 000000000..8960b6486 --- /dev/null +++ b/src/mem/protocol/MSI_MOSI_CMP_directory.slicc @@ -0,0 +1,8 @@ +# protocol briefly described in +# doc/MSI_MOSI_CMP_directory_2level-protocol-description.txt + +MSI_MOSI_CMP_directory-msg.sm +MSI_MOSI_CMP_directory-L1cache.sm +MSI_MOSI_CMP_directory-L2cache.sm +MSI_MOSI_CMP_directory-dir.sm +standard_CMP-protocol.sm diff --git a/src/mem/protocol/RubySlicc_ComponentMapping.sm b/src/mem/protocol/RubySlicc_ComponentMapping.sm new file mode 100644 index 000000000..0c205ac22 --- /dev/null +++ b/src/mem/protocol/RubySlicc_ComponentMapping.sm @@ -0,0 +1,62 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Mapping functions + +// NodeID map_address_to_node(Address addr); +MachineID map_Address_to_Directory(Address addr); +NodeID map_Address_to_DirectoryNode(Address addr); +MachineID map_Address_to_CentralArbiterNode(Address addr); +NodeID oldmap_L1RubyNode_to_L2Cache(Address addr, NodeID L1RubyNode); +MachineID map_L1CacheMachId_to_L2Cache(Address addr, MachineID L1CacheMachId); +MachineID map_L2ChipId_to_L2Cache(Address addr, NodeID L2ChipId); +// MachineID map_L1RubyNode_to_Arb(NodeID L1RubyNode); + +MachineID getL1MachineID(NodeID L1RubyNode); +NodeID getChipID(MachineID L2machID); +MachineID getCollectorDest(MachineID L1machID); +MachineID getCollectorL1Cache(MachineID colID); +NetDest getMultiStaticL2BankNetDest(Address addr, Set sharers); +bool isL1OnChip(MachineID L1machID, NodeID L2NodeID); +bool isL2OnChip(MachineID L2machID, NodeID L2NodeID); + +int getNumBanksInBankSet(); +NodeID machineIDToNodeID(MachineID machID); +NodeID machineIDToVersion(MachineID machID); +MachineType machineIDToMachineType(MachineID machID); +NodeID L1CacheMachIDToProcessorNum(MachineID machID); +NodeID L2CacheMachIDToChipID(MachineID machID); +Set getOtherLocalL1IDs(MachineID L1); +Set getLocalL1IDs(MachineID L1); +Set getExternalL1IDs(MachineID L1); +NetDest getAllPertinentL2Banks(Address addr); +bool isLocalProcessor(MachineID thisId, MachineID tarId); + +GenericMachineType ConvertMachToGenericMach(MachineType machType); + diff --git a/src/mem/protocol/RubySlicc_Defines.sm b/src/mem/protocol/RubySlicc_Defines.sm new file mode 100644 index 000000000..9bafebf10 --- /dev/null +++ b/src/mem/protocol/RubySlicc_Defines.sm @@ -0,0 +1,34 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Hack, no node object since base class has them +NodeID id, no_chip_object="yes", no_vector="yes", abstract_chip_ptr="true"; +NodeID version, no_chip_object="yes", no_vector="yes", abstract_chip_ptr="true"; +MachineID machineID, no_chip_object="yes", no_vector="yes", abstract_chip_ptr="true"; + diff --git a/src/mem/protocol/RubySlicc_Exports.sm b/src/mem/protocol/RubySlicc_Exports.sm new file mode 100644 index 000000000..36622cb40 --- /dev/null +++ b/src/mem/protocol/RubySlicc_Exports.sm @@ -0,0 +1,339 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +// defines +external_type(int, primitive="yes", default="0"); +external_type(bool, primitive="yes", default="false"); +external_type(string, primitive="yes"); +external_type(uint64, primitive="yes"); +external_type(Time, primitive="yes", default="0"); +external_type(Address); + + +// Declarations of external types that are common to all protocols + +// AccessPermission +enumeration(AccessPermission, desc="...", default="AccessPermission_NotPresent") { + Busy, desc="No Read or Write"; + Read_Only, desc="Read Only"; + Read_Write, desc="Read/Write"; + Invalid, desc="Invalid"; + NotPresent, desc="NotPresent"; + OnHold, desc="Holding a place in dnuca cache"; + ReadUpgradingToWrite, desc="Read only, but trying to get Read/Write"; + Stale, desc="local L1 has a modified copy, assume L2 copy is stale data"; +} + +// TesterStatus +enumeration(TesterStatus, desc="...") { + Idle, desc="Idle"; + Action_Pending, desc="Action Pending"; + Ready, desc="Ready"; + Check_Pending, desc="Check Pending"; +} + +// SpecifiedGeneratorTypes +enumeration(SpecifiedGeneratorType, desc="...") { + DetermGETXGenerator, desc="deterministic GETX Tester"; + DetermInvGenerator, desc="deterministic all shared then invalidate Tester"; + DetermSeriesGETSGenerator, desc="deterministic Series of GETSs Tester for prefetcher tuning"; +} + +// RequestGeneratorStatus +enumeration(RequestGeneratorStatus, desc="...") { + Thinking, desc="Doing work between release and next acquire"; + Test_Pending, desc="Test pending"; + Before_Swap, desc="We're about to perform the swap"; + Swap_Pending, desc="The swap used for test-and-send is pending"; + Holding, desc="We are holding the lock performing the critical section"; + Release_Pending, desc="The write for the release is pending"; + Done, desc="Done, waiting for end of run"; +} + +// DetermGETXGeneratorStatus +enumeration(DetermGETXGeneratorStatus, desc="...") { + Thinking, desc="Doing work before next action"; + Store_Pending, desc="Store pending"; + Done, desc="Done, waiting for end of run"; +} + +// DetermGETXGeneratorStatus +enumeration(DetermInvGeneratorStatus, desc="...") { + Thinking, desc="Doing work before next action"; + Store_Pending, desc="Store pending"; + Load_Complete, desc="Load complete"; + Load_Pending, desc="Load pending"; + Done, desc="Done, waiting for end of run"; +} + +// DetermSeriesGETSGeneratorStatus +enumeration(DetermSeriesGETSGeneratorStatus, desc="...") { + Thinking, desc="Doing work before next action"; + Load_Pending, desc="Load pending"; + Done, desc="Done, waiting for end of run"; +} + +// LockStatus +enumeration(LockStatus, desc="...") { + Unlocked, desc="Lock is not held"; + Locked, desc="Lock is held"; +} + +// SequencerStatus +enumeration(SequencerStatus, desc="...") { + Idle, desc="Idle"; + Pending, desc="Pending"; +} + +enumeration(TransitionResult, desc="...") { + Valid, desc="Valid transition"; + ResourceStall, desc="Stalled due to insufficient resources"; + ProtocolStall, desc="Protocol specified stall"; +} + +// CacheRequestType +enumeration(CacheRequestType, desc="...", default="CacheRequestType_NULL") { + LD, desc="Load"; + ST, desc="Store"; + ATOMIC, desc="Atomic Load/Store"; + IFETCH, desc="Instruction fetch"; + IO, desc="I/O"; + REPLACEMENT, desc="Replacement"; + COMMIT, desc="Commit version"; + LD_XACT, desc="Transactional Load"; + LDX_XACT, desc="Transactional Load-Intend-To-Modify"; + ST_XACT, desc="Transactional Store"; + BEGIN_XACT, desc="Begin Transaction"; + COMMIT_XACT, desc="Commit Transaction"; + ABORT_XACT, desc="Abort Transaction"; + NULL, desc="Invalid request type"; +} + +enumeration(GenericRequestType, desc="...", default="GenericRequestType_NULL") { + GETS, desc="gets request"; + GET_INSTR, desc="get instr request"; + GETX, desc="getx request"; + UPGRADE, desc="upgrade request"; + DOWNGRADE, desc="downgrade request"; + INV, desc="invalidate request"; + INV_S, desc="invalidate shared copy request"; + PUTS, desc="puts request"; + PUTO, desc="puto request"; + PUTX, desc="putx request"; + L2_PF, desc="L2 prefetch"; + LD, desc="Load"; + ST, desc="Store"; + ATOMIC, desc="Atomic Load/Store"; + IFETCH, desc="Instruction fetch"; + IO, desc="I/O"; + NACK, desc="Nack"; + REPLACEMENT, desc="Replacement"; + WB_ACK, desc="WriteBack ack"; + EXE_ACK, desc="Execlusive ack"; + COMMIT, desc="Commit version"; + LD_XACT, desc="Transactional Load"; + LDX_XACT, desc="Transactional Load-Intend-Modify"; + ST_XACT, desc="Transactional Store"; + BEGIN_XACT, desc="Begin Transaction"; + COMMIT_XACT, desc="Commit Transaction"; + ABORT_XACT, desc="Abort Transaction"; + NULL, desc="null request type"; +} + +enumeration(GenericMachineType, desc="...", default="GenericMachineType_NULL") { + L1Cache, desc="L1 Cache Mach"; + L2Cache, desc="L2 Cache Mach"; + L3Cache, desc="L3 Cache Mach"; + Directory, desc="Directory Mach"; + Collector, desc="Collector Mach"; + L1Cache_wCC, desc="L1 Cache Mach with Cache Coherence (used for miss latency profile)"; + L2Cache_wCC, desc="L1 Cache Mach with Cache Coherence (used for miss latency profile)"; + NULL, desc="null mach type"; +} + +// MessageSizeType +enumeration(MessageSizeType, default="MessageSizeType_Undefined", desc="...") { + Undefined, desc="Undefined"; + Control, desc="Control Message"; + Data, desc="Data Message"; + Request_Control, desc="Request"; + Reissue_Control, desc="Reissued request"; + Response_Data, desc="data response"; + ResponseL2hit_Data, desc="data response"; + ResponseLocal_Data, desc="data response"; + Response_Control, desc="non-data response"; + Writeback_Data, desc="Writeback data"; + Writeback_Control, desc="Writeback control"; + Forwarded_Control, desc="Forwarded control"; + Invalidate_Control, desc="Invalidate control"; + Unblock_Control, desc="Unblock control"; + Persistent_Control, desc="Persistent request activation messages"; + Completion_Control, desc="Completion messages"; +} + +// AccessType +enumeration(AccessType, desc="...") { + Read, desc="Reading from cache"; + Write, desc="Writing to cache"; +} + +// AccessModeType +enumeration(AccessModeType, default="AccessModeType_UserMode", desc="...") { + SupervisorMode, desc="Supervisor mode"; + UserMode, desc="User mode"; +} + +enumeration(PrefetchBit, default="PrefetchBit_No", desc="...") { + No, desc="No, not a prefetch"; + Yes, desc="Yes, a prefetch"; + L1_HW, desc="This is a L1 hardware prefetch"; + L2_HW, desc="This is a L2 hardware prefetch"; +} + +// CacheMsg +structure(CacheMsg, desc="...", interface="Message") { + Address Address, desc="Line address for this request"; + Address PhysicalAddress, desc="Physical address for this request"; + CacheRequestType Type, desc="Type of request (LD, ST, etc)"; + Address ProgramCounter, desc="Program counter of the instruction that caused the miss"; + AccessModeType AccessMode, desc="user/supervisor access type"; + int Size, desc="size in bytes of access"; + PrefetchBit Prefetch, desc="Is this a prefetch request"; + // following field only used for MVC + int Version, desc="Version associated with this request"; + // trans mem fields + //bool Aborted, desc="This flag is set if the request is from an aborted xact."; + Address LogicalAddress, desc="Virtual address for this request"; + //int TransactionLevel, desc="Transaction Level of this request"; + //uint64 SequenceNumber, desc="Sequence number of this request"; + int ThreadID, desc="The SMT thread that initiated this request"; + uint64 Timestamp, desc="The transaction timestamp of this request. Last commit time if request is non-transactional"; + bool ExposedAction, desc="Is this request part of an exposed action"; + //uint64 RequestTime, desc="The cycle in which this request was issued"; +} + + + + +// MaskPredictorType +enumeration(MaskPredictorType, "MaskPredictorType_Undefined", desc="...") { + Undefined, desc="Undefined"; + AlwaysUnicast, desc="AlwaysUnicast"; + TokenD, desc="TokenD"; + AlwaysBroadcast, desc="AlwaysBroadcast"; + TokenB, desc="TokenB"; + TokenNull, desc="TokenNull"; + Random, desc="Random"; + Pairwise, desc="Pairwise"; + Owner, desc="Owner"; + BroadcastIfShared, desc="Broadcast-If-Shared"; + BroadcastCounter, desc="Broadcast Counter"; + Group, desc="Group"; + Counter, desc="Counter"; + StickySpatial, desc="StickySpatial"; + OwnerBroadcast, desc="Owner/Broadcast Hybrid"; + OwnerGroup, desc="Owner/Group Hybrid"; + OwnerBroadcastMod, desc="Owner/Broadcast Hybrid-Mod"; + OwnerGroupMod, desc="Owner/Group Hybrid-Mod"; + LastNMasks, desc="Last N Masks"; + BandwidthAdaptive, desc="Bandwidth Adaptive"; +} + +// MaskPredictorIndex +enumeration(MaskPredictorIndex, "MaskPredictorIndex_Undefined", desc="...") { + Undefined, desc="Undefined"; + DataBlock, desc="Data Block"; + PC, desc="Program Counter"; +} + +// MaskPredictorTraining +enumeration(MaskPredictorTraining, "MaskPredictorTraining_Undefined", desc="...") { + Undefined, desc="Undefined"; + None, desc="None"; + Implicit, desc="Implicit"; + Explicit, desc="Explicit"; + Both, desc="Both"; +} + +// Network Topologies +enumeration(TopologyType, desc="...") { + CROSSBAR, desc="One node per chip, single switch crossbar"; + HIERARCHICAL_SWITCH, desc="One node per chip, totally ordered hierarchical tree switched network"; + TORUS_2D, desc="One node per chip, 2D torus"; + PT_TO_PT, desc="One node per chip, Point to Point Network"; + FILE_SPECIFIED, desc="described by the file NETWORK_FILE"; +} + +// DNUCA AllocationStrategy +enumeration(AllocationStrategy, desc="...") { + InMiddle, desc=""; + InInvCorners, desc=""; + InSharedSides, desc=""; + StaticDist, desc=""; + RandomBank, desc=""; + FrequencyBank, desc=""; + FrequencyBlock, desc=""; + LRUBlock, desc=""; +} + +// DNUCA SearchMechanism +enumeration(SearchMechanism, desc="...") { + Perfect, desc=""; + PartialTag, desc=""; + BloomFilter, desc=""; + Random, desc=""; + None, desc=""; +} + +// DNUCA link type +enumeration(LinkType, desc="...") { + RC_1500UM, desc=""; + RC_2500UM, desc=""; + TL_9000UM, desc=""; + TL_11000UM, desc=""; + TL_13000UM, desc=""; + NO_ENERGY, desc=""; + NULL, desc=""; +} + +// transient request type +enumeration(TransientRequestType, desc="...", default="TransientRequestType_Undefined") { + Undefined, desc=""; + OffChip, desc=""; + OnChip, desc=""; + LocalTransient, desc=""; +} + + + diff --git a/src/mem/protocol/RubySlicc_MemControl.sm b/src/mem/protocol/RubySlicc_MemControl.sm new file mode 100644 index 000000000..a51bf09d4 --- /dev/null +++ b/src/mem/protocol/RubySlicc_MemControl.sm @@ -0,0 +1,67 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +// MemoryRequestType used in MemoryMsg + +enumeration(MemoryRequestType, desc="...") { + + // Southbound request: from directory to memory cache + // or directory to memory or memory cache to memory + MEMORY_READ, desc="Read request to memory"; + MEMORY_WB, desc="Write back data to memory"; + + // response from memory to directory + // (These are currently unused!) + MEMORY_DATA, desc="Data read from memory"; + MEMORY_ACK, desc="Write to memory acknowledgement"; +} + + +// Message to and from Memory Control + +structure(MemoryMsg, desc="...", interface="Message") { + Address Address, desc="Physical address for this request"; + MemoryRequestType Type, desc="Type of memory request (MEMORY_READ or MEMORY_WB)"; + MachineID Sender, desc="What component sent the data"; + MachineID OriginalRequestorMachId, desc="What component originally requested"; + DataBlock DataBlk, desc="Data to writeback"; + MessageSizeType MessageSize, desc="size category of the message"; + // Not all fields used by all protocols: + PrefetchBit Prefetch, desc="Is this a prefetch request"; + bool ReadX, desc="Exclusive"; + int Acks, desc="How many acks to expect"; + + +} + diff --git a/src/mem/protocol/RubySlicc_Profiler.sm b/src/mem/protocol/RubySlicc_Profiler.sm new file mode 100644 index 000000000..7a7fbdae1 --- /dev/null +++ b/src/mem/protocol/RubySlicc_Profiler.sm @@ -0,0 +1,64 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Profiler function + +void profileStore(NodeID node, bool needCLB); +void profileCacheCLBsize(int size, int numStaleI); +void profileMemoryCLBsize(int size, int numStaleI); + +// used by 2level exclusive cache protocols +void profile_miss(CacheMsg msg, NodeID id); + +// used by non-fast path protocols +void profile_L1Cache_miss(CacheMsg msg, NodeID l1cacheID); + +// used by CMP protocols +void profile_L2Cache_miss(GenericRequestType requestType, AccessModeType type, int msgSize, PrefetchBit pfBit, NodeID l2cacheID); +void profile_request(string L1CacheStateStr, string L2CacheStateStr, string directoryStateStr, string requestTypeStr); +void profileMessageReordering(bool wasReordered); +void profileMessageReorderingByNetwork(int vnet, bool wasReordered); +void profile_token_retry(Address addr, AccessType type, int count); +void profile_persistent_prediction(Address addr, AccessType type); +void profile_filter_action(int act); +void profile_multicast_retry(Address addr, int count); +void profile_outstanding_request(int outstanding); +void profile_outstanding_persistent_request(int outstanding); +// void profile_overlapping_persistent_request(int overlapping); +void profile_average_latency_estimate(int latency); + +// profile the total message delay of a message across a virtual network +void profileMsgDelay(int virtualNetwork, int delayCycles); + +// used by transactional-memory protocols +void profile_transaction(int numStores); +void profile_trans_wb(); +void profileOverflow(Address addr, MachineID mach); + + diff --git a/src/mem/protocol/RubySlicc_Types.sm b/src/mem/protocol/RubySlicc_Types.sm new file mode 100644 index 000000000..a948322dd --- /dev/null +++ b/src/mem/protocol/RubySlicc_Types.sm @@ -0,0 +1,168 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// External Types + +external_type(DataBlock, desc="..."){ + void clear(); +} + +external_type(MessageBuffer, buffer="yes", inport="yes", outport="yes"); + +external_type(OutPort, primitive="yes"); + +external_type(InPort, primitive="yes") { + bool isReady(); + void dequeue(); + int dequeue_getDelayCycles(); + void recycle(); + bool isEmpty(); +} + +external_type(NodeID, default="0"); +external_type(MachineID); + +external_type(StoreBuffer); + + +external_type(Set, non_obj="yes") { + void setSize(int); + void add(NodeID); + void addSet(Set); + void remove(NodeID); + void removeSet(Set); + void broadcast(); + void addRandom(); + void clear(); + int count(); + bool isElement(NodeID); + bool isEqual(Set); + bool isSuperset(Set); + bool intersectionIsEmpty(Set); + NodeID smallestElement(); +} + +external_type(NetDest, non_obj="yes") { + void setSize(int); + void setSize(int, int); + void add(NodeID); + void add(MachineID); + void addSet(Set); + void addNetDest(NetDest); + void setNetDest(MachineType, Set); + void remove(NodeID); + void remove(MachineID); + void removeSet(Set); + void removeNetDest(NetDest); + void broadcast(); + void broadcast(MachineType); + void addRandom(); + void clear(); + Set toSet(); + int count(); + bool isElement(NodeID); + bool isElement(MachineID); + bool isSuperset(Set); + bool isSuperset(NetDest); + bool isEmpty(); + bool intersectionIsEmpty(Set); + bool intersectionIsEmpty(NetDest); + MachineID smallestElement(MachineType); +} + +external_type(PersistentTable) { + void persistentRequestLock(Address, MachineID, AccessType); + void persistentRequestUnlock(Address, MachineID); + bool okToIssueStarving(Address); + MachineID findSmallest(Address); + AccessType typeOfSmallest(Address); + void markEntries(Address); + bool isLocked(Address); + int countStarvingForAddress(Address); + int countReadStarvingForAddress(Address); +} + +external_type(NodePersistentTable) { + void persistentRequestLock(Address, NodeID, AccessType); + void persistentRequestUnlock(Address, NodeID); + bool okToIssueStarving(Address); + NodeID findSmallest(Address); + AccessType typeOfSmallest(Address); + void markEntries(Address); + bool isLocked(Address); + int countStarvingForAddress(Address); + int countReadStarvingForAddress(Address); +} + +external_type(Sequencer) { + void readCallback(Address, DataBlock, GenericMachineType, PrefetchBit, int); + void writeCallback(Address, DataBlock, GenericMachineType, PrefetchBit, int); + void readCallback(Address, DataBlock, GenericMachineType, PrefetchBit); + void writeCallback(Address, DataBlock, GenericMachineType, PrefetchBit); + void readCallback(Address, DataBlock); + void writeCallback(Address, DataBlock); + void readCallback(Address); + void writeCallback(Address); + void readCallbackAbort(Address, int); + void writeCallbackAbort(Address, int); + void readConflictCallback(Address); + void writeConflictCallback(Address); + void xactCallback(Address); + void updateCurrentVersion(); + void updateLastCommittedVersion(); + void systemRecovery(); + void systemRestart(); + void checkCoherence(Address); + void profileNack(Address, int, int, uint64); + void resetRequestTime(Address, int); + bool isReadAborted(Address, int); + bool isWriteAborted(Address, int); +} + +external_type(TimerTable, inport="yes") { + bool isReady(); + Address readyAddress(); + void set(Address, int); + void unset(Address); + bool isSet(Address); +} + +external_type(GenericBloomFilter) { + + void clear(int); + void increment(Address, int); + void decrement(Address, int); + void set(Address, int); + void unset(Address, int); + + bool isSet(Address, int); + int getCount(Address, int); +} + + diff --git a/src/mem/protocol/RubySlicc_Util.sm b/src/mem/protocol/RubySlicc_Util.sm new file mode 100644 index 000000000..7f7ebf5ed --- /dev/null +++ b/src/mem/protocol/RubySlicc_Util.sm @@ -0,0 +1,61 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Miscallaneous Functions + +void error(string msg); +void assert(bool condition); +int random(int number); +Time get_time(); +Time zero_time(); +NodeID intToID(int nodenum); +int IDToInt(NodeID id); +int addressToInt(Address addr); +int MessageSizeTypeToInt(MessageSizeType size_type); +bool multicast_retry(); +int numberOfNodes(); +int numberOfL1CachePerChip(); +int getAddThenMod(int addend1, int addend2, int modulus); +int time_to_int(Time time); +Time getTimeModInt(Time time, int modulus); +Time getTimePlusInt(Time addend1, int addend2); +Time getTimeMinusTime(Time t1, Time t2); +Time getPreviousDelayedCycles(Time t1, Time t2); +void WARN_ERROR_TIME(Time time); +void procProfileCoherenceRequest(NodeID node, bool needCLB); +void dirProfileCoherenceRequest(NodeID node, bool needCLB); +bool isPerfectProtocol(); +bool L1trainsPrefetcher(); +int max_tokens(); +int N_tokens(); +bool distributedPersistentEnabled(); +Address setOffset(Address addr, int offset); +Address makeLineAddress(Address addr); + + diff --git a/src/mem/protocol/RubySlicc_interfaces.slicc b/src/mem/protocol/RubySlicc_interfaces.slicc new file mode 100644 index 000000000..280a84763 --- /dev/null +++ b/src/mem/protocol/RubySlicc_interfaces.slicc @@ -0,0 +1,7 @@ +RubySlicc_Exports.sm +RubySlicc_Types.sm +RubySlicc_Util.sm +RubySlicc_ComponentMapping.sm +RubySlicc_Profiler.sm +RubySlicc_Defines.sm +RubySlicc_MemControl.sm diff --git a/src/mem/protocol/doc/MSI_MOSI_CMP_directory_2level-protocol-description.txt b/src/mem/protocol/doc/MSI_MOSI_CMP_directory_2level-protocol-description.txt new file mode 100644 index 000000000..ef13e5cc1 --- /dev/null +++ b/src/mem/protocol/doc/MSI_MOSI_CMP_directory_2level-protocol-description.txt @@ -0,0 +1,49 @@ +MSI_dir_L1_MOSI_dir_L2_SNUCA_CMP +--------------------------------------- + + +CMP System structure: + +A CMP (Chip MultiProcessor) System is composed of one or more CMP chips. A CMP chip consists of some number of processors, each with private L1 I+D caches and one shared L2 cache. The shared L2 cache can be sub-divided into banks where each bank as a separate cache controller. One global interconnect is defined that connects all the components in the system including the L1 caches, the L2 cache banks, and the memories/directories. + +High-level Protocol Description: + +- The L1 has 3 stable states: M, S, I + +- L2 maintains strict inclusion and has full knowledge of on-chip L1 sharers via individual bits in the cache tag. The stable +states are below: + + M,O,S,I : normal meanings + SS : L2 entry shared, also present in one or more L1s or ext L2s + SO : L2 entry owned, also present in one or more L1s or ext L2s + MT : L2 entry modified in a local L1. L2 copy is stale + +- The protocol has a strict on-chip hierarchy where the L1 caches only communicate with their on-chip L2 cache, while off-chip the L2 cache communicates with the directory as well as directly responding to the other L2 caches. + +High Level Event Description: + +On a L1 GETS miss - +> The L2 controller will satisfy the miss immediately if in M, S, SS, or OS. +> If the L2 controller is in MT, the L2 controller will issue a down-grade request to the on-chip L1 sharer. The L1 sharer will then downgrade to S and copy it's dirty block to the L2. The L2 will then forward that copy to the L1 GETS requestor. +> If not present, the L2 will issue a GETS to the directory. The directory will either forward the request, or respond with data. If forwarded to another L2 controller, the L2 owner will respond with data directly to the L2 requestor which would then respond to the original L1 requestor. + +On a L1 GETX miss - +> The L2 controller will satisfy the miss immediately if in M +> If the L2 controller is in MT, the L2 controller will issue an invalidate request to the on-chip L1 sharer. The L1 sharer will then invalidate the block and writeback it's dirty block to the L2. The L2 will then forward that copy to the L1 GETX requestor. +> If the L2 controller is in O, S, or I, the L2 controller will issue a GETX request to the directory. The L2 will wait to receive the data and the necessary number of acks before responding to the L1 GETX requestor with the data and exclusive permission. +> If the L2 controller is in SS or SO, the L2 controller will issue a GETX request to the directory as well as invalidate requests to the on-chip L1 copies. The L2 will wait to receive the data and the necessary number of acks (both on and off chip) before responding to the L1 GETX requestor with the data and exclusive permission. + +Other minute details: + + The L2 acknowledges all L1 replacements + The L1 acknowledges L2 invalidation requests + The L2 sends an ack to the directory on a 3-hop transfer + The forward request network (Dir -> L2 -> L1) is assumed to be pt-to-pt ordered + The L1 request network (L1 -> L2) is assumed to be pt-to-pt ordered + All other networks are unordered + +Ruby Implementation Details: + +- The new component network must be used with this protocol +- Each ruby node contains a L1 cache controller, 0 or more L2 cache controllers, and 0 or more directories. There must be at least one directory in the system and at least one L2 cache controller per chip. The directories are statically partitioned by address across the entire system (SMP), while the L2Cache banks are statically partitioned by address across a single chip. There must be some power of 2 for each machine type. +- BUG ALERT: There is a single pool of TBE Entries for the L2 Controller. Therefore it is possible to encounter a resource deadlock for these entries between satisfying L1 requests and directory requests. diff --git a/src/mem/protocol/doc/MSI_dir_L1_MOSI_dir_L2_SNUCA_CMP-protocol-description.txt b/src/mem/protocol/doc/MSI_dir_L1_MOSI_dir_L2_SNUCA_CMP-protocol-description.txt new file mode 100644 index 000000000..ef13e5cc1 --- /dev/null +++ b/src/mem/protocol/doc/MSI_dir_L1_MOSI_dir_L2_SNUCA_CMP-protocol-description.txt @@ -0,0 +1,49 @@ +MSI_dir_L1_MOSI_dir_L2_SNUCA_CMP +--------------------------------------- + + +CMP System structure: + +A CMP (Chip MultiProcessor) System is composed of one or more CMP chips. A CMP chip consists of some number of processors, each with private L1 I+D caches and one shared L2 cache. The shared L2 cache can be sub-divided into banks where each bank as a separate cache controller. One global interconnect is defined that connects all the components in the system including the L1 caches, the L2 cache banks, and the memories/directories. + +High-level Protocol Description: + +- The L1 has 3 stable states: M, S, I + +- L2 maintains strict inclusion and has full knowledge of on-chip L1 sharers via individual bits in the cache tag. The stable +states are below: + + M,O,S,I : normal meanings + SS : L2 entry shared, also present in one or more L1s or ext L2s + SO : L2 entry owned, also present in one or more L1s or ext L2s + MT : L2 entry modified in a local L1. L2 copy is stale + +- The protocol has a strict on-chip hierarchy where the L1 caches only communicate with their on-chip L2 cache, while off-chip the L2 cache communicates with the directory as well as directly responding to the other L2 caches. + +High Level Event Description: + +On a L1 GETS miss - +> The L2 controller will satisfy the miss immediately if in M, S, SS, or OS. +> If the L2 controller is in MT, the L2 controller will issue a down-grade request to the on-chip L1 sharer. The L1 sharer will then downgrade to S and copy it's dirty block to the L2. The L2 will then forward that copy to the L1 GETS requestor. +> If not present, the L2 will issue a GETS to the directory. The directory will either forward the request, or respond with data. If forwarded to another L2 controller, the L2 owner will respond with data directly to the L2 requestor which would then respond to the original L1 requestor. + +On a L1 GETX miss - +> The L2 controller will satisfy the miss immediately if in M +> If the L2 controller is in MT, the L2 controller will issue an invalidate request to the on-chip L1 sharer. The L1 sharer will then invalidate the block and writeback it's dirty block to the L2. The L2 will then forward that copy to the L1 GETX requestor. +> If the L2 controller is in O, S, or I, the L2 controller will issue a GETX request to the directory. The L2 will wait to receive the data and the necessary number of acks before responding to the L1 GETX requestor with the data and exclusive permission. +> If the L2 controller is in SS or SO, the L2 controller will issue a GETX request to the directory as well as invalidate requests to the on-chip L1 copies. The L2 will wait to receive the data and the necessary number of acks (both on and off chip) before responding to the L1 GETX requestor with the data and exclusive permission. + +Other minute details: + + The L2 acknowledges all L1 replacements + The L1 acknowledges L2 invalidation requests + The L2 sends an ack to the directory on a 3-hop transfer + The forward request network (Dir -> L2 -> L1) is assumed to be pt-to-pt ordered + The L1 request network (L1 -> L2) is assumed to be pt-to-pt ordered + All other networks are unordered + +Ruby Implementation Details: + +- The new component network must be used with this protocol +- Each ruby node contains a L1 cache controller, 0 or more L2 cache controllers, and 0 or more directories. There must be at least one directory in the system and at least one L2 cache controller per chip. The directories are statically partitioned by address across the entire system (SMP), while the L2Cache banks are statically partitioned by address across a single chip. There must be some power of 2 for each machine type. +- BUG ALERT: There is a single pool of TBE Entries for the L2 Controller. Therefore it is possible to encounter a resource deadlock for these entries between satisfying L1 requests and directory requests. diff --git a/src/mem/protocol/standard_1level_SMP-protocol.sm b/src/mem/protocol/standard_1level_SMP-protocol.sm new file mode 100644 index 000000000..bfaca8466 --- /dev/null +++ b/src/mem/protocol/standard_1level_SMP-protocol.sm @@ -0,0 +1,39 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +// global protocol features +global(Protocol, desc="Global properties of this protocol", + interface = "AbstractProtocol") { + bool TwoLevelCache := false; +} + diff --git a/src/mem/protocol/standard_CMP-protocol.sm b/src/mem/protocol/standard_CMP-protocol.sm new file mode 100644 index 000000000..dbd7e4ef5 --- /dev/null +++ b/src/mem/protocol/standard_CMP-protocol.sm @@ -0,0 +1,36 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// global protocol features +global(Protocol, desc="Global properties of this protocol", + interface = "AbstractProtocol") { + bool TwoLevelCache := true; + bool CMP := true; +} + diff --git a/src/mem/protocol/standard_SMP-protocol.sm b/src/mem/protocol/standard_SMP-protocol.sm new file mode 100644 index 000000000..9a2c6ef39 --- /dev/null +++ b/src/mem/protocol/standard_SMP-protocol.sm @@ -0,0 +1,39 @@ + +/* + * Copyright (c) 1999-2005 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +// global protocol features +global(Protocol, desc="Global properties of this protocol", + interface = "AbstractProtocol") { + bool TwoLevelCache := true; +} + diff --git a/src/mem/ruby/Decomissioning_note b/src/mem/ruby/Decomissioning_note new file mode 100644 index 000000000..9b083d196 --- /dev/null +++ b/src/mem/ruby/Decomissioning_note @@ -0,0 +1,10 @@ + +1. While decomissioning log_tm + + a. I left lots of transaction related stuff ( Xact.... ) in commands.C + Made the minimal changes to compile it properly + + +make[1]: *** No rule to make target `amd64-linux/generated/MI_example/obj/PartialAddressFilter.o', needed by `amd64-linux/generated/MI_example/bin/libruby.so + + diff --git a/src/mem/ruby/FakeSimicsDataTypes.hh b/src/mem/ruby/FakeSimicsDataTypes.hh new file mode 100644 index 000000000..b6fcda95c --- /dev/null +++ b/src/mem/ruby/FakeSimicsDataTypes.hh @@ -0,0 +1,63 @@ +#ifndef FAKE_SIMICS_DATA_TYPES_H +#define FAKE_SIMICS_DATA_TYPES_H + +typedef struct attr_value attr_value_t; + +typedef enum { + Sim_Val_Invalid = 0, + Sim_Val_String = 1, + Sim_Val_Integer = 2, + Sim_Val_Floating = 3, + Sim_Val_List = 4, + Sim_Val_Data = 5, + Sim_Val_Nil = 6, + Sim_Val_Object = 7 +} attr_kind_t; + +typedef struct attr_list attr_list_t; + +struct attr_list { + int size; + struct attr_value *vector; +}; + +struct attr_value { + attr_kind_t kind; + union { + const char *string; /* Sim_Val_String */ + unsigned long long integer; /* Sim_Val_Integer */ + double floating; /* Sim_Val_Floating */ + void *object; /* Sim_Val_Object */ + attr_list_t list; /* Sim_Val_List */ + } u; +}; + +typedef enum { +Sim_Set_Ok, +Sim_Set_Need_Integer, +Sim_Set_Need_Floating, +Sim_Set_Need_String, +Sim_Set_Need_List, +Sim_Set_Need_Data, +Sim_Set_Need_Object, +Sim_Set_Object_Not_Found, +Sim_Set_Interface_Not_Found, +Sim_Set_Illegal_Value, +Sim_Set_Illegal_Type, +Sim_Set_Illegal_Index, +Sim_Set_Attribute_Not_Found, +Sim_Set_Not_Writable, +Sim_Set_Ignored +} set_error_t; + + +typedef attr_value_t (*get_attr_t)(void *ptr, + void *obj, + attr_value_t *idx); + +typedef set_error_t (*set_attr_t)(void *ptr, + void *obj, + attr_value_t *val, + attr_value_t *idx); + +#endif // #ifndef FAKE_SIMICS_DATA_TYPES_H diff --git a/src/mem/ruby/README.debugging b/src/mem/ruby/README.debugging new file mode 100644 index 000000000..48a5e62f0 --- /dev/null +++ b/src/mem/ruby/README.debugging @@ -0,0 +1,104 @@ +# ------ Debugging the Ruby Tester ------ + +You can compile Ruby with debugging turned on. + +cd ruby +[vim or emacs] Makefile + +Change OPT_FLAGS to "-g -O0" (the first OPT_FLAGS line). Make +sure all the other OPT_FLAGS lines are commented out. + +Change DEBUG_FLAGS to "-DRUBY_DEBUG=true". (Just uncomment the +first DEBUG_FLAGS line, and comment out the second DEBUG_FLAGS +line.) + +You can choose which component or components to debug, and the +level of verbosity. For example, + +"x86-linux/generated/MOSI_SMP_bcast/bin/tester.exec -l 100000 -v med -c n" + +gives you debugging information about the network component at +the medium verbosity level. -v selects the verbosity, which may +be low, med, high, or none. -c selects the component or +components. + +"x86-linux/generated/MOSI_SMP_bcast/bin/tester.exec -l 100000 -v med -c nSt" + +debugs the network, the sequencer, and the tester. + +For a list of the components you can debug, just run the tester with +no arguments, and it will display a list of valid components. The +components are defined in ruby/common/Debug.def. + +The protocol debug trace is especially useful for debugging cache coherence protocols. This must be enabled at compile-time by ensuring that PROTOCOL_DEBUG_TRACE is set to true for rubyconfig.defaults (if running in Simics) or tester.defaults. You must specify the time to start tracing. The following starts the protocol trace immediately (at time 1) + +"x86-linux/generated/MOSI_SMP_bcast/bin/tester.exec -l 100000 -s 1" + +Also, if something seems to be wrong and you're not sure where to +start looking, it may help to run the tester for a longer time, +e.g. + +"x86-linux/generated/MOSI_SMP_bcast/bin/tester.exec -l 500000" + +This may help because some problems eventually show up as +deadlock, but the tester has to run for a long time before a +deadlock is detected. + +Once your simulator has succeeded on the tester for a certain +number of cycles, say 1000000, you may want to set the +RANDOMIZATION variable in ruby/config/tester.defaults to "true" +for more thorough testing. However, RANDOMIZATION may not work in +all situations because it overrides some of the ordering in the +network and may break your simulator in ways you don't like. For +example, messages are added to MessageBuffers with random +latency. + +By default the tester driver is a generator that issues random store +and load requests. This driver does a good job of stressing the +cache coherency protocol by issuing racy store requests from multiple +processors to a cache line then checks the stores with a single load. + +Other tester drivers are available. By setting the g_SYNTHETIC_DRIVER +to true in ruby/config/tester.defaults, you enable a tester that generates +racy lock requests for a number of locks indicated by g_synthetic_locks. + +Another tester driver is a series of non-racy deterministic testers. By +setting the g_DETERMINISTIC_DRIVER in ruby/config/tester.defaults to true, +you enable the deterministic tester. The deterministic tester can do +different types of deterministic tests as specified by g_SpecifiedGenerator +string. The deterministic tester works best when RANDOMIZATION is set to +false. To easily track the queues being used with the deterministic tester, +use the following debug flags "-v low -c nq". + +# ------ Debugging Ruby in Simics ------ + +When you're running Simics, the debugging components and +verbosity levels are the same. However, the way you communicate +with Ruby changes. + +See the README.quickstart for information on compiling the Ruby +module and loading it into Simics. Once you've got Simics +running, with the Ruby module loaded, you can set up Ruby +debugging. + +To set the debugging verbosity level, run: + +simics> ruby0.debug-verb med + +To set the debugging components, run: (see common/Debug.def for complete list + of component shortcuts) + +simics> ruby0.debug-filter n + +(NOTE: sometimes simics will interpret a single letter as a +command; e.g. expanding "p" into "print". If simics gives you an +error when setting the debug filter, try setting it like so: +simics> ruby0.debug-filter "n") + +This gives the same kind of debugging information as running the +tester with "-v med -c n". + +You can also send the debugging output to a file (may be a good +idea, since there's a lot of it). To do this, run: + +simics> ruby0.debug-output-file diff --git a/src/mem/ruby/buffers/MessageBuffer.cc b/src/mem/ruby/buffers/MessageBuffer.cc new file mode 100644 index 000000000..ff2547f0f --- /dev/null +++ b/src/mem/ruby/buffers/MessageBuffer.cc @@ -0,0 +1,363 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +#include "MessageBuffer.hh" +#include "RubyConfig.hh" + +MessageBuffer::MessageBuffer() +{ + m_msg_counter = 0; + m_consumer_ptr = NULL; + m_ordering_set = false; + m_strict_fifo = true; + m_size = 0; + m_max_size = -1; + m_last_arrival_time = 0; + m_randomization = true; + m_size_last_time_size_checked = 0; + m_time_last_time_size_checked = 0; + m_time_last_time_enqueue = 0; + m_time_last_time_pop = 0; + m_size_at_cycle_start = 0; + m_msgs_this_cycle = 0; + m_not_avail_count = 0; + m_priority_rank = 0; +} + +MessageBuffer::MessageBuffer(const Chip* chip_ptr) // The chip_ptr is ignored, but could be used for extra debugging +{ + m_msg_counter = 0; + m_consumer_ptr = NULL; + m_ordering_set = false; + m_strict_fifo = true; + m_size = 0; + m_max_size = -1; + m_last_arrival_time = 0; + m_randomization = true; + m_size_last_time_size_checked = 0; + m_time_last_time_size_checked = 0; + m_time_last_time_enqueue = 0; + m_time_last_time_pop = 0; + m_size_at_cycle_start = 0; + m_msgs_this_cycle = 0; + m_not_avail_count = 0; + m_priority_rank = 0; +} + +int MessageBuffer::getSize() +{ + if(m_time_last_time_size_checked == g_eventQueue_ptr->getTime()){ + return m_size_last_time_size_checked; + } else { + m_time_last_time_size_checked = g_eventQueue_ptr->getTime(); + m_size_last_time_size_checked = m_size; + return m_size; + } +} + +bool MessageBuffer::areNSlotsAvailable(int n) +{ + + // fast path when message buffers have infinite size + if(m_max_size == -1) { + return true; + } + + // determine my correct size for the current cycle + // pop operations shouldn't effect the network's visible size until next cycle, + // but enqueue operations effect the visible size immediately + int current_size = max(m_size_at_cycle_start, m_size); + if (m_time_last_time_pop < g_eventQueue_ptr->getTime()) { // no pops this cycle - m_size is correct + current_size = m_size; + } else { + if (m_time_last_time_enqueue < g_eventQueue_ptr->getTime()) { // no enqueues this cycle - m_size_at_cycle_start is correct + current_size = m_size_at_cycle_start; + } else { // both pops and enqueues occured this cycle - add new enqueued msgs to m_size_at_cycle_start + current_size = m_size_at_cycle_start+m_msgs_this_cycle; + } + } + + // now compare the new size with our max size + if(current_size+n <= m_max_size){ + return true; + } else { + DEBUG_MSG(QUEUE_COMP,MedPrio,n); + DEBUG_MSG(QUEUE_COMP,MedPrio,current_size); + DEBUG_MSG(QUEUE_COMP,MedPrio,m_size); + DEBUG_MSG(QUEUE_COMP,MedPrio,m_max_size); + m_not_avail_count++; + return false; + } +} + +const MsgPtr MessageBuffer::getMsgPtrCopy() const +{ + assert(isReady()); + + MsgPtr temp_msg; + temp_msg = *(m_prio_heap.peekMin().m_msgptr.ref()); + assert(temp_msg.ref() != NULL); + return temp_msg; +} + +const Message* MessageBuffer::peekAtHeadOfQueue() const +{ + const Message* msg_ptr; + DEBUG_NEWLINE(QUEUE_COMP,MedPrio); + + DEBUG_MSG(QUEUE_COMP,MedPrio,"Peeking at head of queue " + m_name + " time: " + + int_to_string(g_eventQueue_ptr->getTime()) + "."); + assert(isReady()); + + msg_ptr = m_prio_heap.peekMin().m_msgptr.ref(); + assert(msg_ptr != NULL); + + DEBUG_EXPR(QUEUE_COMP,MedPrio,*msg_ptr); + DEBUG_NEWLINE(QUEUE_COMP,MedPrio); + return msg_ptr; +} + +// FIXME - move me somewhere else +int random_time() +{ + int time = 1; + time += random() & 0x3; // [0...3] + if ((random() & 0x7) == 0) { // 1 in 8 chance + time += 100 + (random() % 0xf); // 100 + [1...15] + } + return time; +} + +void MessageBuffer::enqueue(const MsgPtr& message, Time delta) +{ + DEBUG_NEWLINE(QUEUE_COMP,HighPrio); + DEBUG_MSG(QUEUE_COMP,HighPrio,"enqueue " + m_name + " time: " + + int_to_string(g_eventQueue_ptr->getTime()) + "."); + DEBUG_EXPR(QUEUE_COMP,MedPrio,message); + DEBUG_NEWLINE(QUEUE_COMP,HighPrio); + + m_msg_counter++; + m_size++; + + // record current time incase we have a pop that also adjusts my size + if (m_time_last_time_enqueue < g_eventQueue_ptr->getTime()) { + m_msgs_this_cycle = 0; // first msg this cycle + m_time_last_time_enqueue = g_eventQueue_ptr->getTime(); + } + m_msgs_this_cycle++; + + // ASSERT(m_max_size == -1 || m_size <= m_max_size + 1); + // the plus one is a kluge because of a SLICC issue + + if (!m_ordering_set) { + WARN_EXPR(*this); + WARN_EXPR(m_name); + ERROR_MSG("Ordering property of this queue has not been set"); + } + + // Calculate the arrival time of the message, that is, the first + // cycle the message can be dequeued. + assert(delta>0); + Time current_time = g_eventQueue_ptr->getTime(); + Time arrival_time = 0; + if (!RANDOMIZATION || (m_randomization == false)) { + // No randomization + arrival_time = current_time + delta; + + } else { + // Randomization - ignore delta + if (m_strict_fifo) { + if (m_last_arrival_time < current_time) { + m_last_arrival_time = current_time; + } + arrival_time = m_last_arrival_time + random_time(); + } else { + arrival_time = current_time + random_time(); + } + } + + // Check the arrival time + assert(arrival_time > current_time); + if (m_strict_fifo) { + if (arrival_time >= m_last_arrival_time) { + + } else { + WARN_EXPR(*this); + WARN_EXPR(m_name); + WARN_EXPR(current_time); + WARN_EXPR(delta); + WARN_EXPR(arrival_time); + WARN_EXPR(m_last_arrival_time); + ERROR_MSG("FIFO ordering violated"); + } + } + m_last_arrival_time = arrival_time; + + // compute the delay cycles and set enqueue time + Message* msg_ptr = NULL; + msg_ptr = message.mod_ref(); + assert(msg_ptr != NULL); + assert(g_eventQueue_ptr->getTime() >= msg_ptr->getLastEnqueueTime()); // ensure we aren't dequeued early + msg_ptr->setDelayedCycles((g_eventQueue_ptr->getTime() - msg_ptr->getLastEnqueueTime())+msg_ptr->getDelayedCycles()); + msg_ptr->setLastEnqueueTime(arrival_time); + + // Insert the message into the priority heap + MessageBufferNode thisNode(arrival_time, m_msg_counter, message); + m_prio_heap.insert(thisNode); + + DEBUG_NEWLINE(QUEUE_COMP,HighPrio); + DEBUG_MSG(QUEUE_COMP,HighPrio,"enqueue " + m_name + + " with arrival_time " + int_to_string(arrival_time) + + " cur_time: " + int_to_string(g_eventQueue_ptr->getTime()) + "."); + DEBUG_EXPR(QUEUE_COMP,MedPrio,message); + DEBUG_NEWLINE(QUEUE_COMP,HighPrio); + + // Schedule the wakeup + if (m_consumer_ptr != NULL) { + g_eventQueue_ptr->scheduleEventAbsolute(m_consumer_ptr, arrival_time); + } else { + WARN_EXPR(*this); + WARN_EXPR(m_name); + ERROR_MSG("No consumer"); + } +} + +int MessageBuffer::dequeue_getDelayCycles(MsgPtr& message) +{ + int delay_cycles = -1; // null value + + dequeue(message); + + // get the delay cycles + delay_cycles = setAndReturnDelayCycles(message); + + assert(delay_cycles >= 0); + return delay_cycles; +} + +void MessageBuffer::dequeue(MsgPtr& message) +{ + DEBUG_MSG(QUEUE_COMP,MedPrio,"dequeue from " + m_name); + message = m_prio_heap.peekMin().m_msgptr; + + pop(); + DEBUG_EXPR(QUEUE_COMP,MedPrio,message); +} + +int MessageBuffer::dequeue_getDelayCycles() +{ + int delay_cycles = -1; // null value + + // get MsgPtr of the message about to be dequeued + MsgPtr message = m_prio_heap.peekMin().m_msgptr; + + // get the delay cycles + delay_cycles = setAndReturnDelayCycles(message); + + dequeue(); + + assert(delay_cycles >= 0); + return delay_cycles; +} + +void MessageBuffer::pop() +{ + DEBUG_MSG(QUEUE_COMP,MedPrio,"pop from " + m_name); + assert(isReady()); + Time ready_time = m_prio_heap.extractMin().m_time; + // record previous size and time so the current buffer size isn't adjusted until next cycle + if (m_time_last_time_pop < g_eventQueue_ptr->getTime()) { + m_size_at_cycle_start = m_size; + m_time_last_time_pop = g_eventQueue_ptr->getTime(); + } + m_size--; +} + +void MessageBuffer::clear() +{ + while(m_prio_heap.size() > 0){ + m_prio_heap.extractMin(); + } + + ASSERT(m_prio_heap.size() == 0); + + m_msg_counter = 0; + m_size = 0; + m_time_last_time_enqueue = 0; + m_time_last_time_pop = 0; + m_size_at_cycle_start = 0; + m_msgs_this_cycle = 0; +} + +void MessageBuffer::recycle() +{ + // const int RECYCLE_LATENCY = 3; + DEBUG_MSG(QUEUE_COMP,MedPrio,"recycling " + m_name); + assert(isReady()); + MessageBufferNode node = m_prio_heap.extractMin(); + node.m_time = g_eventQueue_ptr->getTime() + RECYCLE_LATENCY; + m_prio_heap.insert(node); + g_eventQueue_ptr->scheduleEventAbsolute(m_consumer_ptr, g_eventQueue_ptr->getTime() + RECYCLE_LATENCY); +} + +int MessageBuffer::setAndReturnDelayCycles(MsgPtr& message) +{ + int delay_cycles = -1; // null value + + // get the delay cycles of the message at the top of the queue + Message* msg_ptr = message.ref(); + + // this function should only be called on dequeue + // ensure the msg hasn't been enqueued + assert(msg_ptr->getLastEnqueueTime() <= g_eventQueue_ptr->getTime()); + msg_ptr->setDelayedCycles((g_eventQueue_ptr->getTime() - msg_ptr->getLastEnqueueTime())+msg_ptr->getDelayedCycles()); + delay_cycles = msg_ptr->getDelayedCycles(); + + assert(delay_cycles >= 0); + return delay_cycles; +} + +void MessageBuffer::print(ostream& out) const +{ + out << "[MessageBuffer: "; + if (m_consumer_ptr != NULL) { + out << " consumer-yes "; + } + out << m_prio_heap << "] " << m_name << endl; +} + +void MessageBuffer::printStats(ostream& out) +{ + out << "MessageBuffer: " << m_name << " stats - msgs:" << m_msg_counter << " full:" << m_not_avail_count << endl; +} + diff --git a/src/mem/ruby/buffers/MessageBuffer.hh b/src/mem/ruby/buffers/MessageBuffer.hh new file mode 100644 index 000000000..6851423c3 --- /dev/null +++ b/src/mem/ruby/buffers/MessageBuffer.hh @@ -0,0 +1,156 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + * Description: Unordered buffer of messages that can be inserted such + * that they can be dequeued after a given delta time has expired. + * + */ + +#ifndef MESSAGEBUFFER_H +#define MESSAGEBUFFER_H + +#include "Global.hh" +#include "MessageBufferNode.hh" +#include "Consumer.hh" +#include "EventQueue.hh" +#include "Message.hh" +#include "PrioHeap.hh" +#include "util.hh" + +class Chip; + +class MessageBuffer { +public: + // Constructors + MessageBuffer(); + MessageBuffer(const Chip* chip_ptr); // The chip_ptr is ignored, but could be used for extra debugging + + // Use Default Destructor + // ~MessageBuffer() + + // Public Methods + + static void printConfig(ostream& out) {} + + // TRUE if head of queue timestamp <= SystemTime + bool isReady() const { + return ((m_prio_heap.size() > 0) && + (m_prio_heap.peekMin().m_time <= g_eventQueue_ptr->getTime())); + } + + bool areNSlotsAvailable(int n); + int getPriority() { return m_priority_rank; } + void setPriority(int rank) { m_priority_rank = rank; } + void setConsumer(Consumer* consumer_ptr) { ASSERT(m_consumer_ptr==NULL); m_consumer_ptr = consumer_ptr; } + void setDescription(const string& name) { m_name = name; } + string getDescription() { return m_name;} + + Consumer* getConsumer() { return m_consumer_ptr; } + + const Message* peekAtHeadOfQueue() const; + const Message* peek() const { return peekAtHeadOfQueue(); } + const MsgPtr getMsgPtrCopy() const; + const MsgPtr& peekMsgPtr() const { assert(isReady()); return m_prio_heap.peekMin().m_msgptr; } + const MsgPtr& peekMsgPtrEvenIfNotReady() const {return m_prio_heap.peekMin().m_msgptr; } + + void enqueue(const MsgPtr& message) { enqueue(message, 1); } + void enqueue(const MsgPtr& message, Time delta); + // void enqueueAbsolute(const MsgPtr& message, Time absolute_time); + int dequeue_getDelayCycles(MsgPtr& message); // returns delay cycles of the message + void dequeue(MsgPtr& message); + int dequeue_getDelayCycles(); // returns delay cycles of the message + void dequeue() { pop(); } + void pop(); + void recycle(); + bool isEmpty() const { return m_prio_heap.size() == 0; } + + void setOrdering(bool order) { m_strict_fifo = order; m_ordering_set = true; } + void setSize(int size) {m_max_size = size;} + int getSize(); + void setRandomization(bool random_flag) { m_randomization = random_flag; } + + void clear(); + + void print(ostream& out) const; + void printStats(ostream& out); + void clearStats() { m_not_avail_count = 0; m_msg_counter = 0; } + +private: + // Private Methods + int setAndReturnDelayCycles(MsgPtr& message); + + // Private copy constructor and assignment operator + MessageBuffer(const MessageBuffer& obj); + MessageBuffer& operator=(const MessageBuffer& obj); + + // Data Members (m_ prefix) + Consumer* m_consumer_ptr; // Consumer to signal a wakeup(), can be NULL + PrioHeap m_prio_heap; + string m_name; + + int m_max_size; + int m_size; + + Time m_time_last_time_size_checked; + int m_size_last_time_size_checked; + + // variables used so enqueues appear to happen imediately, while pop happen the next cycle + Time m_time_last_time_enqueue; + Time m_time_last_time_pop; + int m_size_at_cycle_start; + int m_msgs_this_cycle; + + int m_not_avail_count; // count the # of times I didn't have N slots available + int m_msg_counter; + int m_priority_rank; + bool m_strict_fifo; + bool m_ordering_set; + bool m_randomization; + Time m_last_arrival_time; +}; + +// Output operator declaration +//template +ostream& operator<<(ostream& out, const MessageBuffer& obj); + +// ******************* Definitions ******************* + +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const MessageBuffer& obj) +{ + obj.print(out); + out << flush; + return out; +} + +#endif //MESSAGEBUFFER_H diff --git a/src/mem/ruby/buffers/MessageBufferNode.cc b/src/mem/ruby/buffers/MessageBufferNode.cc new file mode 100644 index 000000000..c84347a38 --- /dev/null +++ b/src/mem/ruby/buffers/MessageBufferNode.cc @@ -0,0 +1,48 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * EventQueueNode.C + * + * Description: See EventQueueNode.h + * + * $Id: MessageBufferNode.C,v 3.1 2001/02/02 16:57:54 sorin Exp $ + * + */ + +#include "MessageBufferNode.hh" + +void MessageBufferNode::print(ostream& out) const +{ + out << "["; + out << m_time << ", "; + out << m_msg_counter << ", "; + out << m_msgptr << "; "; + out << "]"; +} diff --git a/src/mem/ruby/buffers/MessageBufferNode.hh b/src/mem/ruby/buffers/MessageBufferNode.hh new file mode 100644 index 000000000..c562c45eb --- /dev/null +++ b/src/mem/ruby/buffers/MessageBufferNode.hh @@ -0,0 +1,88 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MESSAGEBUFFERNODE_H +#define MESSAGEBUFFERNODE_H + +#include "Global.hh" +#include "Message.hh" + +class MessageBufferNode { +public: + // Constructors + MessageBufferNode() { m_time = 0; m_msg_counter = 0; } + MessageBufferNode(const Time& time, int counter, const MsgPtr& msgptr) + { m_time = time; m_msgptr = msgptr; m_msg_counter = counter; } + // Destructor + //~MessageBufferNode(); + + // Public Methods + void print(ostream& out) const; +private: + // Private Methods + + // Default copy constructor and assignment operator + // MessageBufferNode(const MessageBufferNode& obj); + // MessageBufferNode& operator=(const MessageBufferNode& obj); + + // Data Members (m_ prefix) +public: + Time m_time; + int m_msg_counter; // FIXME, should this be a 64-bit value? + MsgPtr m_msgptr; +}; + +// Output operator declaration +ostream& operator<<(ostream& out, const MessageBufferNode& obj); + +// ******************* Definitions ******************* + +inline extern bool node_less_then_eq(const MessageBufferNode& n1, const MessageBufferNode& n2); + +inline extern +bool node_less_then_eq(const MessageBufferNode& n1, const MessageBufferNode& n2) +{ + if (n1.m_time == n2.m_time) { + assert(n1.m_msg_counter != n2.m_msg_counter); + return (n1.m_msg_counter <= n2.m_msg_counter); + } else { + return (n1.m_time <= n2.m_time); + } +} + +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const MessageBufferNode& obj) +{ + obj.print(out); + out << flush; + return out; +} + +#endif //MESSAGEBUFFERNODE_H diff --git a/src/mem/ruby/common/Address.cc b/src/mem/ruby/common/Address.cc new file mode 100644 index 000000000..5d38faae0 --- /dev/null +++ b/src/mem/ruby/common/Address.cc @@ -0,0 +1,68 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +#include "Address.hh" + +void Address::output(ostream& out) const +{ + // Note: this outputs addresses in the form "ffff", not "0xffff". + // This code should always be able to write out addresses in a + // format that can be read in by the below input() method. Please + // don't change this without talking to Milo first. + out << hex << m_address << dec; +} + +void Address::input(istream& in) +{ + // Note: this only works with addresses in the form "ffff", not + // "0xffff". This code should always be able to read in addresses + // written out by the above output() method. Please don't change + // this without talking to Milo first. + in >> hex >> m_address >> dec; +} + +Address::Address(const Address& obj) +{ + m_address = obj.m_address; +} + +Address& Address::operator=(const Address& obj) +{ + if (this == &obj) { + // assert(false); + } else { + m_address = obj.m_address; + } + return *this; +} + diff --git a/src/mem/ruby/common/Address.hh b/src/mem/ruby/common/Address.hh new file mode 100644 index 000000000..3c3c5ad30 --- /dev/null +++ b/src/mem/ruby/common/Address.hh @@ -0,0 +1,255 @@ + +/* + * Copyright (c) 1999 by Mark Hill and David Wood for the Wisconsin + * Multifacet Project. ALL RIGHTS RESERVED. + * + * ##HEADER## + * + * This software is furnished under a license and may be used and + * copied only in accordance with the terms of such license and the + * inclusion of the above copyright notice. This software or any + * other copies thereof or any derivative works may not be provided or + * otherwise made available to any other persons. Title to and + * ownership of the software is retained by Mark Hill and David Wood. + * Any use of this software must include the above copyright notice. + * + * THIS SOFTWARE IS PROVIDED "AS IS". THE LICENSOR MAKES NO + * WARRANTIES ABOUT ITS CORRECTNESS OR PERFORMANCE. + * */ + +/* + * $Id$ + */ + +#ifndef ADDRESS_H +#define ADDRESS_H + +#include +#include "Global.hh" +#include "RubyConfig.hh" +#include "NodeID.hh" +#include "MachineID.hh" + +const int ADDRESS_WIDTH = 64; // address width in bytes + +class Address; +typedef Address PhysAddress; +typedef Address VirtAddress; + +class Address { +public: + // Constructors + Address() { m_address = 0; } + explicit Address(physical_address_t address) { m_address = address; } + + Address(const Address& obj); + Address& operator=(const Address& obj); + + // Destructor + // ~Address(); + + // Public Methods + + void setAddress(physical_address_t address) { m_address = address; } + physical_address_t getAddress() const {return m_address;} + // selects bits inclusive + physical_address_t bitSelect(int small, int big) const; + physical_address_t maskLowOrderBits(int number) const; + physical_address_t maskHighOrderBits(int number) const; + physical_address_t shiftLowOrderBits(int number) const; + physical_address_t getLineAddress() const + { return bitSelect(RubyConfig::dataBlockBits(), ADDRESS_WIDTH); } + physical_address_t getOffset() const + { return bitSelect(0, RubyConfig::dataBlockBits()-1); } + + void makeLineAddress() { m_address = maskLowOrderBits(RubyConfig::dataBlockBits()); } + // returns the next stride address based on line address + void makeNextStrideAddress( int stride) { + m_address = maskLowOrderBits(RubyConfig::dataBlockBits()) + + RubyConfig::dataBlockBytes()*stride; + } + void makePageAddress() { m_address = maskLowOrderBits(RubyConfig::pageSizeBits()); } + int getBankSetNum() const; + int getBankSetDist() const; + + Index memoryModuleIndex() const; + + void print(ostream& out) const; + void output(ostream& out) const; + void input(istream& in); + + void setOffset( int offset ){ + // first, zero out the offset bits + makeLineAddress(); + m_address |= (physical_address_t) offset; + } + +private: + // Private Methods + + // Private copy constructor and assignment operator + // Address(const Address& obj); + // Address& operator=(const Address& obj); + + // Data Members (m_ prefix) + physical_address_t m_address; +}; + +inline +Address line_address(const Address& addr) { Address temp(addr); temp.makeLineAddress(); return temp; } + +inline +Address next_stride_address(const Address& addr, int stride) { + Address temp = addr; + temp.makeNextStrideAddress(stride); + temp.setAddress(temp.maskHighOrderBits(ADDRESS_WIDTH-RubyConfig::memorySizeBits())); // surpress wrap-around problem + return temp; +} + +inline +Address page_address(const Address& addr) { Address temp(addr); temp.makePageAddress(); return temp; } + +// Output operator declaration +ostream& operator<<(ostream& out, const Address& obj); +// comparison operator declaration +bool operator==(const Address& obj1, const Address& obj2); +bool operator!=(const Address& obj1, const Address& obj2); +bool operator<(const Address& obj1, const Address& obj2); +/* Address& operator=(const physical_address_t address); */ + +inline +bool operator<(const Address& obj1, const Address& obj2) +{ + return obj1.getAddress() < obj2.getAddress(); +} + +// ******************* Definitions ******************* + +// Output operator definition +inline +ostream& operator<<(ostream& out, const Address& obj) +{ + obj.print(out); + out << flush; + return out; +} + +inline +bool operator==(const Address& obj1, const Address& obj2) +{ + return (obj1.getAddress() == obj2.getAddress()); +} + +inline +bool operator!=(const Address& obj1, const Address& obj2) +{ + return (obj1.getAddress() != obj2.getAddress()); +} + +inline +physical_address_t Address::bitSelect(int small, int big) const // rips bits inclusive +{ + physical_address_t mask; + assert(big >= small); + + if (big >= ADDRESS_WIDTH - 1) { + return (m_address >> small); + } else { + mask = ~((physical_address_t)~0 << (big + 1)); + // FIXME - this is slow to manipulate a 64-bit number using 32-bits + physical_address_t partial = (m_address & mask); + return (partial >> small); + } +} + +inline +physical_address_t Address::maskLowOrderBits(int number) const +{ + physical_address_t mask; + + if (number >= ADDRESS_WIDTH - 1) { + mask = ~0; + } else { + mask = (physical_address_t)~0 << number; + } + return (m_address & mask); +} + +inline +physical_address_t Address::maskHighOrderBits(int number) const +{ + physical_address_t mask; + + if (number >= ADDRESS_WIDTH - 1) { + mask = ~0; + } else { + mask = (physical_address_t)~0 >> number; + } + return (m_address & mask); +} + +inline +physical_address_t Address::shiftLowOrderBits(int number) const +{ + return (m_address >> number); +} + +inline +integer_t Address::memoryModuleIndex() const +{ + integer_t index = bitSelect(RubyConfig::dataBlockBits()+RubyConfig::memoryBits(), ADDRESS_WIDTH); + assert (index >= 0); + if (index >= RubyConfig::memoryModuleBlocks()) { + cerr << " memoryBits: " << RubyConfig::memoryBits() << " memorySizeBits: " << RubyConfig::memorySizeBits() + << " Address: " << "[" << hex << "0x" << m_address << "," << " line 0x" << maskLowOrderBits(RubyConfig::dataBlockBits()) << dec << "]" << flush + << "error: limit exceeded. " << + " dataBlockBits: " << RubyConfig::dataBlockBits() << + " memoryModuleBlocks: " << RubyConfig::memoryModuleBlocks() << + " index: " << index << endl; + } + assert (index < RubyConfig::memoryModuleBlocks()); + return index; + + // Index indexHighPortion = address.bitSelect(MEMORY_SIZE_BITS-1, PAGE_SIZE_BITS+NUMBER_OF_MEMORY_MODULE_BITS); + // Index indexLowPortion = address.bitSelect(DATA_BLOCK_BITS, PAGE_SIZE_BITS-1); + + //Index index = indexLowPortion | (indexHighPortion << (PAGE_SIZE_BITS - DATA_BLOCK_BITS)); + + /* + Round-robin mapping of addresses, at page size granularity + +ADDRESS_WIDTH MEMORY_SIZE_BITS PAGE_SIZE_BITS DATA_BLOCK_BITS + | | | | + \ / \ / \ / \ / 0 + ----------------------------------------------------------------------- + | unused |xxxxxxxxxxxxxxx| |xxxxxxxxxxxxxxx| | + | |xxxxxxxxxxxxxxx| |xxxxxxxxxxxxxxx| | + ----------------------------------------------------------------------- + indexHighPortion indexLowPortion + <-------> + NUMBER_OF_MEMORY_MODULE_BITS + */ +} + +inline +void Address::print(ostream& out) const +{ + out << "[" << hex << "0x" << m_address << "," << " line 0x" << maskLowOrderBits(RubyConfig::dataBlockBits()) << dec << "]" << flush; +} + +class Address; +namespace __gnu_cxx { + template <> struct hash
+ { + size_t operator()(const Address &s) const { return (size_t) s.getAddress(); } + }; +} +namespace std { + template <> struct equal_to
+ { + bool operator()(const Address& s1, const Address& s2) const { return s1 == s2; } + }; +} + +#endif //ADDRESS_H + diff --git a/src/mem/ruby/common/BigSet.cc b/src/mem/ruby/common/BigSet.cc new file mode 100644 index 000000000..e16284f15 --- /dev/null +++ b/src/mem/ruby/common/BigSet.cc @@ -0,0 +1,249 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "Set.hh" +#include "RubyConfig.hh" + +Set::Set() +{ + setSize(RubyConfig::numberOfProcessors()); +} + +Set::Set(int size) +{ + setSize(size); +} + +void Set::add(NodeID index) +{ + m_bits[index] = Present; +} + +void Set::addSet(const Set& set) +{ + assert(m_bits.size() == set.getSize()); + for (int i=0; i> 1); // Shift the random number to look at the next bit + } +} + +void Set::remove(NodeID index) +{ + m_bits[index] = NotPresent; +} + +void Set::removeSet(const Set& set) +{ + assert(m_bits.size() == set.getSize()); + for (int i=0; i 0); + for (int i=0; i m_bits; // This is an vector of uint8 to reduce the size of the set +}; + +// Output operator declaration +ostream& operator<<(ostream& out, const Set& obj); + +// ******************* Definitions ******************* + +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const Set& obj) +{ + obj.print(out); + out << flush; + return out; +} + +#endif //SET_H + diff --git a/src/mem/ruby/common/Consumer.hh b/src/mem/ruby/common/Consumer.hh new file mode 100644 index 000000000..bd51af7ba --- /dev/null +++ b/src/mem/ruby/common/Consumer.hh @@ -0,0 +1,89 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + * Description: This is the virtual base class of all classes that can + * be the targets of wakeup events. There is only two methods, + * wakeup() and print() and no data members. + * + */ + +#ifndef CONSUMER_H +#define CONSUMER_H + +#include "Global.hh" +#include "EventQueue.hh" + +class MessageBuffer; + +class Consumer { +public: + // Constructors + Consumer() { m_last_scheduled_wakeup = 0; m_last_wakeup = 0; m_out_link_vec.setSize(0); } + + // Destructor + virtual ~Consumer() { } + + // Public Methods - pure virtual methods + void triggerWakeup() { Time time = g_eventQueue_ptr->getTime(); if (m_last_wakeup != time) { wakeup(); m_last_wakeup = time; }} + virtual void wakeup() = 0; + virtual void print(ostream& out) const = 0; + const Time& getLastScheduledWakeup() const { return m_last_scheduled_wakeup; } + void setLastScheduledWakeup(const Time& time) { m_last_scheduled_wakeup = time; } + Vector< Vector > getOutBuffers() { return m_out_link_vec; } + +protected: + Vector< Vector > m_out_link_vec; + +private: + // Private Methods + + // Data Members (m_ prefix) + Time m_last_scheduled_wakeup; + Time m_last_wakeup; +}; + +// Output operator declaration +inline extern +ostream& operator<<(ostream& out, const Consumer& obj); + +// ******************* Definitions ******************* + +// Output operator definition +inline extern +ostream& operator<<(ostream& out, const Consumer& obj) +{ + obj.print(out); + out << flush; + return out; +} + +#endif //CONSUMER_H diff --git a/src/mem/ruby/common/DataBlock.cc b/src/mem/ruby/common/DataBlock.cc new file mode 100644 index 000000000..c4d6d7a33 --- /dev/null +++ b/src/mem/ruby/common/DataBlock.cc @@ -0,0 +1,91 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +#include "DataBlock.hh" + +DataBlock::DataBlock() +{ + if (DATA_BLOCK || XACT_MEMORY) { + m_data.setSize(RubyConfig::dataBlockBytes()); + } + clear(); +} + +DataBlock::~DataBlock() +{ + +} + +void DataBlock::clear() +{ + int size = m_data.size(); + for (int i = 0; i < size; i++) { + m_data[i] = 0; + } +} + +bool DataBlock::equal(const DataBlock& obj) const +{ + bool value = true; + int size = m_data.size(); + for (int i = 0; i < size; i++) { + value = value && (m_data[i] == obj.m_data[i]); + } + return value; +} + +void DataBlock::print(ostream& out) const +{ + int size = m_data.size(); + for (int i = 0; i < size; i+=4) { + out << hex << *((uint32*)(&(m_data[i]))) << " "; + } + out << dec << "]" << flush; +} + +uint8 DataBlock::getByte(int whichByte) const +{ + if (DATA_BLOCK || XACT_MEMORY) { + return m_data[whichByte]; + } else { + return 0; + } +} + +void DataBlock::setByte(int whichByte, uint8 data) +{ + if (DATA_BLOCK || XACT_MEMORY) { + m_data[whichByte] = data; + } +} + diff --git a/src/mem/ruby/common/DataBlock.hh b/src/mem/ruby/common/DataBlock.hh new file mode 100644 index 000000000..aae364078 --- /dev/null +++ b/src/mem/ruby/common/DataBlock.hh @@ -0,0 +1,82 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DATABLOCK_H +#define DATABLOCK_H + +#include "Global.hh" +#include "RubyConfig.hh" +#include "Vector.hh" + +class DataBlock { +public: + // Constructors + DataBlock(); + + // Destructor + ~DataBlock(); + + // Public Methods + void clear(); + uint8 getByte(int whichByte) const; + void setByte(int whichByte, uint8 data); + bool equal(const DataBlock& obj) const; + void print(ostream& out) const; + +private: + // Private Methods + + // Data Members (m_ prefix) + Vector m_data; +}; + +// Output operator declaration +ostream& operator<<(ostream& out, const DataBlock& obj); + +bool operator==(const DataBlock& obj1, const DataBlock& obj2); + + +// ******************* Definitions ******************* + +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const DataBlock& obj) +{ + obj.print(out); + out << flush; + return out; +} + +extern inline +bool operator==(const DataBlock& obj1,const DataBlock& obj2) +{ + return (obj1.equal(obj2)); +} + +#endif //DATABLOCK_H diff --git a/src/mem/ruby/common/Debug.cc b/src/mem/ruby/common/Debug.cc new file mode 100644 index 000000000..f0319ceb8 --- /dev/null +++ b/src/mem/ruby/common/Debug.cc @@ -0,0 +1,369 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +#include +#include + +#include "Global.hh" +#include "Debug.hh" +#include "EventQueue.hh" + +class Debug; +extern Debug* g_debug_ptr; +std::ostream * debug_cout_ptr; + +// component character list +const char DEFINE_COMP_CHAR[] = +{ +#undef DEFINE_COMP +#define DEFINE_COMP(component, character, description) character, +#include "Debug.def" +}; + +// component description list +const char* DEFINE_COMP_DESCRIPTION[] = +{ +#undef DEFINE_COMP +#define DEFINE_COMP(component, character, description) description, +#include "Debug.def" +}; + +extern "C" void changeDebugVerbosity(VerbosityLevel vb); +extern "C" void changeDebugFilter(int filter); + +void changeDebugVerbosity(VerbosityLevel vb) +{ + g_debug_ptr->setVerbosity(vb); +} + +void changeDebugFilter(int filter) +{ + g_debug_ptr->setFilter(filter); +} + +Debug::Debug( const char *filterString, const char *verboseString, + Time filterStartTime, const char *filename ) +{ + m_verbosityLevel = No_Verb; + clearFilter(); + debug_cout_ptr = &cout; + + m_starting_cycle = filterStartTime; + setFilterString( filterString ); + setVerbosityString( verboseString ); + setDebugOutputFile( filename ); +} + +Debug::~Debug() +{ +} + +void Debug::printVerbosity(ostream& out) const +{ + switch (getVerbosity()) { + case No_Verb: + out << "verbosity = No_Verb" << endl; + break; + case Low_Verb: + out << "verbosity = Low_Verb" << endl; + break; + case Med_Verb: + out << "verbosity = Med_Verb" << endl; + break; + case High_Verb: + out << "verbosity = High_Verb" << endl; + break; + default: + out << "verbosity = unknown" << endl; + } +} + +bool Debug::validDebug(int module, PriorityLevel priority) +{ + int local_module = (1 << module); + if(m_filter & local_module) { + if (g_eventQueue_ptr == NULL || + g_eventQueue_ptr->getTime() >= m_starting_cycle) { + switch(m_verbosityLevel) { + case No_Verb: + return false; + break; + case Low_Verb: + if(priority == HighPrio) { + return true; + }else{ + return false; + } + break; + case Med_Verb: + if(priority == HighPrio || priority == MedPrio ) { + return true; + }else{ + return false; + } + break; + case High_Verb: + return true; + break; + } + } + } + return false; +} + +void Debug::setDebugTime(Time t) +{ + m_starting_cycle = t; +} + +void Debug::setVerbosity(VerbosityLevel vb) +{ + m_verbosityLevel = vb; +} + +void Debug::setFilter(int filter) +{ + m_filter = filter; +} + +bool Debug::checkVerbosityString(const char *verb_str) +{ + if (verb_str == NULL) { + cerr << "Error: unrecognized verbosity (use none, low, med, high): NULL" << endl; + return true; // error + } else if ( (string(verb_str) == "none") || + (string(verb_str) == "low") || + (string(verb_str) == "med") || + (string(verb_str) == "high") ) { + return false; + } + cerr << "Error: unrecognized verbosity (use none, low, med, high): NULL" << endl; + return true; // error +} + +bool Debug::setVerbosityString(const char *verb_str) +{ + bool check_fails = checkVerbosityString(verb_str); + if (check_fails) { + return true; // error + } + if (string(verb_str) == "none") { + setVerbosity(No_Verb); + } else if (string(verb_str) == "low") { + setVerbosity(Low_Verb); + } else if (string(verb_str) == "med") { + setVerbosity(Med_Verb); + } else if (string(verb_str) == "high") { + setVerbosity(High_Verb); + } else { + cerr << "Error: unrecognized verbosity (use none, low, med, high): " << verb_str << endl; + return true; // error + } + return false; // no error +} + +bool Debug::checkFilter(char ch) +{ + for (int i=0; i +#include + +extern std::ostream * debug_cout_ptr; + +// component enumeration +enum DebugComponents +{ +#undef DEFINE_COMP +#define DEFINE_COMP(component, character, description) component, +#include "Debug.def" + NUMBER_OF_COMPS +}; + +enum PriorityLevel {HighPrio, MedPrio, LowPrio}; +enum VerbosityLevel {No_Verb, Low_Verb, Med_Verb, High_Verb}; + +class Debug { +public: + // Constructors + Debug( const char *filterString, const char *verboseString, + Time filterStartTime, const char *filename ); + + // Destructor + ~Debug(); + + // Public Methods + bool validDebug(int module, PriorityLevel priority); + void printVerbosity(ostream& out) const; + void setVerbosity(VerbosityLevel vb); + static bool checkVerbosityString(const char *verb_str); + bool setVerbosityString(const char *); + VerbosityLevel getVerbosity() const { return m_verbosityLevel; } + void setFilter(int); + static bool checkFilter( char); + static bool checkFilterString(const char *); + bool setFilterString(const char *); + void setDebugTime(Time); + Time getDebugTime() const { return m_starting_cycle; } + bool addFilter(char); + void clearFilter(); + void allFilter(); + void print(ostream& out) const; + /* old school debugging "vararg": sends messages to screen and log */ + void debugMsg( const char *fmt, ... ); + + void setDebugOutputFile (const char * filename); + void closeDebugOutputFile (); + static void usageInstructions(void); + +private: + // Private Methods + + // Private copy constructor and assignment operator + Debug(const Debug& obj); + Debug& operator=(const Debug& obj); + + // Data Members (m_ prefix) + VerbosityLevel m_verbosityLevel; + int m_filter; + Time m_starting_cycle; + + std::fstream m_fout; +}; + +// Output operator declaration +ostream& operator<<(ostream& out, const Debug& obj); + +// ******************* Definitions ******************* + +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const Debug& obj) +{ + obj.print(out); + out << flush; + return out; +} + +const bool ERROR_MESSAGE_FLAG = true; +const bool WARNING_MESSAGE_FLAG = true; + +#ifdef RUBY_NO_ASSERT +const bool ASSERT_FLAG = false; +#else +const bool ASSERT_FLAG = true; +#endif + +#undef assert +#define assert(EXPR) ASSERT(EXPR) +#undef ASSERT +#define ASSERT(EXPR)\ +{\ + if (ASSERT_FLAG) {\ + if (!(EXPR)) {\ + cerr << "failed assertion '"\ + << #EXPR << "' at fn "\ + << __PRETTY_FUNCTION__ << " in "\ + << __FILE__ << ":"\ + << __LINE__ << endl << flush;\ + (* debug_cout_ptr) << "failed assertion '"\ + << #EXPR << "' at fn "\ + << __PRETTY_FUNCTION__ << " in "\ + << __FILE__ << ":"\ + << __LINE__ << endl << flush;\ + if(isatty(STDIN_FILENO)) {\ + cerr << "At this point you might want to attach a debug to ";\ + cerr << "the running and get to the" << endl;\ + cerr << "crash site; otherwise press enter to continue" << endl;\ + cerr << "PID: " << getpid();\ + cerr << endl << flush; \ + char c; \ + cin.get(c); \ + }\ + abort();\ + }\ + }\ +} + +#define BREAK(X)\ +{\ + cerr << "breakpoint '"\ + << #X << "' reached at fn "\ + << __PRETTY_FUNCTION__ << " in "\ + << __FILE__ << ":"\ + << __LINE__ << endl << flush;\ + if(isatty(STDIN_FILENO)) {\ + cerr << "press enter to continue" << endl;\ + cerr << "PID: " << getpid();\ + cerr << endl << flush; \ + char c; \ + cin.get(c); \ + }\ +} + +#define ERROR_MSG(MESSAGE)\ +{\ + if (ERROR_MESSAGE_FLAG) {\ + cerr << "Fatal Error: in fn "\ + << __PRETTY_FUNCTION__ << " in "\ + << __FILE__ << ":"\ + << __LINE__ << ": "\ + << (MESSAGE) << endl << flush;\ + (* debug_cout_ptr) << "Fatal Error: in fn "\ + << __PRETTY_FUNCTION__ << " in "\ + << __FILE__ << ":"\ + << __LINE__ << ": "\ + << (MESSAGE) << endl << flush;\ + abort();\ + }\ +} + +#define WARN_MSG(MESSAGE)\ +{\ + if (WARNING_MESSAGE_FLAG) {\ + cerr << "Warning: in fn "\ + << __PRETTY_FUNCTION__ << " in "\ + << __FILE__ << ":"\ + << __LINE__ << ": "\ + << (MESSAGE) << endl << flush;\ + (* debug_cout_ptr) << "Warning: in fn "\ + << __PRETTY_FUNCTION__ << " in "\ + << __FILE__ << ":"\ + << __LINE__ << ": "\ + << (MESSAGE) << endl << flush;\ + }\ +} + +#define WARN_EXPR(EXPR)\ +{\ + if (WARNING_MESSAGE_FLAG) {\ + cerr << "Warning: in fn "\ + << __PRETTY_FUNCTION__ << " in "\ + << __FILE__ << ":"\ + << __LINE__ << ": "\ + << #EXPR << " is "\ + << (EXPR) << endl << flush;\ + (* debug_cout_ptr) << "Warning: in fn "\ + << __PRETTY_FUNCTION__ << " in "\ + << __FILE__ << ":"\ + << __LINE__ << ": "\ + << #EXPR << " is "\ + << (EXPR) << endl << flush;\ + }\ +} + +#define DEBUG_MSG(module, priority, MESSAGE)\ +{\ + if (RUBY_DEBUG) {\ + if (g_debug_ptr->validDebug(module, priority)) {\ + (* debug_cout_ptr) << "Debug: in fn "\ + << __PRETTY_FUNCTION__\ + << " in " << __FILE__ << ":"\ + << __LINE__ << ": "\ + << (MESSAGE) << endl << flush;\ + }\ + }\ +} + +#define DEBUG_EXPR(module, priority, EXPR)\ +{\ + if (RUBY_DEBUG) {\ + if (g_debug_ptr->validDebug(module, priority)) {\ + (* debug_cout_ptr) << "Debug: in fn "\ + << __PRETTY_FUNCTION__\ + << " in " << __FILE__ << ":"\ + << __LINE__ << ": "\ + << #EXPR << " is "\ + << (EXPR) << endl << flush;\ + }\ + }\ +} + +#define DEBUG_NEWLINE(module, priority)\ +{\ + if (RUBY_DEBUG) {\ + if (g_debug_ptr->validDebug(module, priority)) {\ + (* debug_cout_ptr) << endl << flush;\ + }\ + }\ +} + +#define DEBUG_SLICC(priority, LINE, MESSAGE)\ +{\ + if (RUBY_DEBUG) {\ + if (g_debug_ptr->validDebug(SLICC_COMP, priority)) {\ + (* debug_cout_ptr) << (LINE) << (MESSAGE) << endl << flush;\ + }\ + }\ +} + +#define DEBUG_OUT( rest... ) \ +{\ + if (RUBY_DEBUG) {\ + cout << "Debug: in fn "\ + << __PRETTY_FUNCTION__\ + << " in " << __FILE__ << ":"\ + << __LINE__ << ": "; \ + g_debug_ptr->debugMsg(rest); \ + }\ +} + +#define ERROR_OUT( rest... ) \ +{\ + if (ERROR_MESSAGE_FLAG) {\ + cout << "error: in fn "\ + << __PRETTY_FUNCTION__ << " in "\ + << __FILE__ << ":"\ + << __LINE__ << ": ";\ + g_debug_ptr->debugMsg(rest); \ + }\ +} + +#endif //DEBUG_H + diff --git a/src/mem/ruby/common/Driver.cc b/src/mem/ruby/common/Driver.cc new file mode 100644 index 000000000..019ac6403 --- /dev/null +++ b/src/mem/ruby/common/Driver.cc @@ -0,0 +1,39 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "Driver.hh" + +Driver::Driver() +{ +} + +// still need to be defined for subclasses +Driver::~Driver() +{ +} diff --git a/src/mem/ruby/common/Driver.hh b/src/mem/ruby/common/Driver.hh new file mode 100644 index 000000000..911cb742b --- /dev/null +++ b/src/mem/ruby/common/Driver.hh @@ -0,0 +1,98 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DRIVER_H +#define DRIVER_H + +#include "Global.hh" +#include "Consumer.hh" +#include "NodeID.hh" +#include "CacheRequestType.hh" + +class System; +class SubBlock; +class Address; +class MachineID; +class SimicsHypervisor; + +class Driver { +public: + // Constructors + Driver(); + + // Destructor + virtual ~Driver() = 0; + + // Public Methods + virtual void get_network_config() {} + virtual void hitCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread) = 0; // Called by sequencer + virtual void conflictCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread) { assert(0); } // Called by sequencer + virtual integer_t getInstructionCount(int procID) const { return 1; } + virtual integer_t getCycleCount(int procID) const { return 1; } + virtual SimicsHypervisor * getHypervisor() { return NULL; } + virtual void notifySendNack( int procID, const Address & addr, uint64 remote_timestamp, const MachineID & remote_id) { assert(0); }; //Called by Sequencer + virtual void notifyReceiveNack( int procID, const Address & addr, uint64 remote_timestamp, const MachineID & remote_id) { assert(0); }; //Called by Sequencer + virtual void notifyReceiveNackFinal( int procID, const Address & addr) { assert(0); }; // Called by Sequencer + virtual void notifyTrapStart( int procID, const Address & handlerPC, int threadID, int smtThread ) { assert(0); } //called by Sequencer + virtual void notifyTrapComplete( int procID, const Address & newPC, int smtThread ) {assert(0); } // called by Sequencer + virtual int getOpalTransactionLevel(int procID, int thread) const { + cout << "Driver.hh getOpalTransactionLevel() " << endl; + return 0; } //called by Sequencer + virtual void addThreadDependency(int procID, int requestor_thread, int conflict_thread) const { assert(0);} + virtual uint64 getOpalTime(int procID) const{ return 0; } //called by Sequencer + virtual uint64 getOpalTimestamp(int procID, int thread) const{ + cout << "Driver.hh getOpalTimestamp " << endl; + return 0; } // called by Sequencer + virtual int inTransaction(int procID, int thread ) const{ + cout << "Driver.hh inTransaction " << endl; +return false; } //called by Sequencer + virtual void printDebug(){} //called by Sequencer + + virtual void printStats(ostream& out) const = 0; + virtual void clearStats() = 0; + + virtual void printConfig(ostream& out) const = 0; + + //virtual void abortCallback(NodeID proc){} + + virtual integer_t readPhysicalMemory(int procID, physical_address_t address, + int len ){ ASSERT(0); return 0; } + + virtual void writePhysicalMemory( int procID, physical_address_t address, + integer_t value, int len ){ ASSERT(0); } + +protected: + // accessible by subclasses + +private: + // inaccessible by subclasses + +}; + +#endif //DRIVER_H diff --git a/src/mem/ruby/common/Global.cc b/src/mem/ruby/common/Global.cc new file mode 100644 index 000000000..e60cd4fd3 --- /dev/null +++ b/src/mem/ruby/common/Global.cc @@ -0,0 +1,35 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "Global.hh" + +EventQueue* g_eventQueue_ptr = NULL; +System* g_system_ptr = NULL; +Debug* g_debug_ptr = NULL; + diff --git a/src/mem/ruby/common/Global.hh b/src/mem/ruby/common/Global.hh new file mode 100644 index 000000000..eaec05d46 --- /dev/null +++ b/src/mem/ruby/common/Global.hh @@ -0,0 +1,110 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + * */ + +#ifndef GLOBAL_H +#define GLOBAL_H + +#ifdef SINGLE_LEVEL_CACHE +const bool TWO_LEVEL_CACHE = false; +#define L1I_CACHE_MEMBER_VARIABLE m_L1Cache_cacheMemory_vec[m_version] // currently all protocols require L1s == nodes +#define L1D_CACHE_MEMBER_VARIABLE m_L1Cache_cacheMemory_vec[m_version] // " +#define L2_CACHE_MEMBER_VARIABLE m_L1Cache_cacheMemory_vec[m_version] // " +#define L2_CACHE_VARIABLE m_L1Cache_cacheMemory_vec +#else +const bool TWO_LEVEL_CACHE = true; +#ifdef IS_CMP +#define L1I_CACHE_MEMBER_VARIABLE m_L1Cache_L1IcacheMemory_vec[m_version] +#define L1D_CACHE_MEMBER_VARIABLE m_L1Cache_L1DcacheMemory_vec[m_version] +#define L2_CACHE_MEMBER_VARIABLE m_L2Cache_L2cacheMemory_vec[m_version] +#define L2_CACHE_VARIABLE m_L2Cache_L2cacheMemory_vec +#else // not IS_CMP +#define L1I_CACHE_MEMBER_VARIABLE m_L1Cache_L1IcacheMemory_vec[m_version] // currently all protocols require L1s == nodes +#define L1D_CACHE_MEMBER_VARIABLE m_L1Cache_L1DcacheMemory_vec[m_version] // " +// #define L2_CACHE_MEMBER_VARIABLE m_L1Cache_L2cacheMemory_vec[m_version] // old exclusive caches don't support L2s != nodes +#define L2_CACHE_MEMBER_VARIABLE m_L1Cache_cacheMemory_vec[m_version] // old exclusive caches don't support L2s != nodes +#define L2_CACHE_VARIABLE m_L1Cache_L2cacheMemory_vec +#endif // IS_CMP +#endif //SINGLE_LEVEL_CACHE + +#define DIRECTORY_MEMBER_VARIABLE m_Directory_directory_vec[m_version] +#define TBE_TABLE_MEMBER_VARIABLE m_L1Cache_TBEs_vec[m_version] + +typedef unsigned char uint8; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +typedef signed char int8; +typedef int int32; +typedef long long int64; + +typedef long long integer_t; +typedef unsigned long long uinteger_t; + +typedef int64 Time; +typedef uint64 physical_address_t; +typedef uint64 la_t; +typedef uint64 pa_t; +typedef integer_t simtime_t; + +// external includes for all classes +#include "std-includes.hh" +#include "Debug.hh" + +// simple type declarations +typedef Time LogicalTime; +typedef int64 Index; // what the address bit ripper returns +typedef int word; // one word of a cache line +typedef unsigned int uint; +typedef int SwitchID; +typedef int LinkID; + +class EventQueue; +extern EventQueue* g_eventQueue_ptr; + +class System; +extern System* g_system_ptr; + +class Debug; +extern Debug* g_debug_ptr; + +// FIXME: this is required by the contructor of Directory_Entry.h. It can't go +// into slicc_util.h because it opens a can of ugly worms +extern inline int max_tokens() +{ + return 1024; +} + + +#endif //GLOBAL_H + diff --git a/src/mem/ruby/common/Histogram.cc b/src/mem/ruby/common/Histogram.cc new file mode 100644 index 000000000..9c5e8e623 --- /dev/null +++ b/src/mem/ruby/common/Histogram.cc @@ -0,0 +1,185 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +#include "Histogram.hh" + +Histogram::Histogram(int binsize, int bins) +{ + m_binsize = binsize; + m_bins = bins; + clear(); +} + +Histogram::~Histogram() +{ +} + +void Histogram::clear(int binsize, int bins) +{ + m_binsize = binsize; + clear(bins); +} + +void Histogram::clear(int bins) +{ + m_bins = bins; + m_largest_bin = 0; + m_max = 0; + m_data.setSize(m_bins); + for (int i = 0; i < m_bins; i++) { + m_data[i] = 0; + } + m_count = 0; + m_max = 0; + + m_sumSamples = 0; + m_sumSquaredSamples = 0; +} + + +void Histogram::add(int64 value) +{ + assert(value >= 0); + m_max = max(m_max, value); + m_count++; + + m_sumSamples += value; + m_sumSquaredSamples += (value*value); + + int index; + if (m_binsize == -1) { + // This is a log base 2 histogram + if (value == 0) { + index = 0; + } else { + index = int(log(double(value))/log(2.0))+1; + if (index >= m_data.size()) { + index = m_data.size()-1; + } + } + } else { + // This is a linear histogram + while (m_max >= (m_bins * m_binsize)) { + for (int i = 0; i < m_bins/2; i++) { + m_data[i] = m_data[i*2] + m_data[i*2 + 1]; + } + for (int i = m_bins/2; i < m_bins; i++) { + m_data[i] = 0; + } + m_binsize *= 2; + } + index = value/m_binsize; + } + assert(index >= 0); + m_data[index]++; + m_largest_bin = max(m_largest_bin, index); +} + +void Histogram::add(const Histogram& hist) +{ + assert(hist.getBins() == m_bins); + assert(hist.getBinSize() == -1); // assume log histogram + assert(m_binsize == -1); + + for (int j = 0; j < hist.getData(0); j++) { + add(0); + } + + for (int i = 1; i < m_bins; i++) { + for (int j = 0; j < hist.getData(i); j++) { + add(1<<(i-1)); // account for the + 1 index + } + } + +} + +// Computation of standard deviation of samples a1, a2, ... aN +// variance = [SUM {ai^2} - (SUM {ai})^2/N]/(N-1) +// std deviation equals square root of variance +double Histogram::getStandardDeviation() const +{ + double variance; + if(m_count > 1){ + variance = (double)(m_sumSquaredSamples - m_sumSamples*m_sumSamples/m_count)/(m_count - 1); + } else { + return 0; + } + return sqrt(variance); +} + +void Histogram::print(ostream& out) const +{ + printWithMultiplier(out, 1.0); +} + +void Histogram::printPercent(ostream& out) const +{ + if (m_count == 0) { + printWithMultiplier(out, 0.0); + } else { + printWithMultiplier(out, 100.0/double(m_count)); + } +} + +void Histogram::printWithMultiplier(ostream& out, double multiplier) const +{ + if (m_binsize == -1) { + out << "[binsize: log2 "; + } else { + out << "[binsize: " << m_binsize << " "; + } + out << "max: " << m_max << " "; + out << "count: " << m_count << " "; + // out << "total: " << m_sumSamples << " "; + if (m_count == 0) { + out << "average: NaN |"; + out << "standard deviation: NaN |"; + } else { + out << "average: " << setw(5) << ((double) m_sumSamples)/m_count << " | "; + out << "standard deviation: " << getStandardDeviation() << " |"; + } + for (int i = 0; i < m_bins && i <= m_largest_bin; i++) { + if (multiplier == 1.0) { + out << " " << m_data[i]; + } else { + out << " " << double(m_data[i]) * multiplier; + } + } + out << " ]"; +} + +bool node_less_then_eq(const Histogram* n1, const Histogram* n2) +{ + return (n1->size() > n2->size()); +} diff --git a/src/mem/ruby/common/Histogram.hh b/src/mem/ruby/common/Histogram.hh new file mode 100644 index 000000000..e48efc35f --- /dev/null +++ b/src/mem/ruby/common/Histogram.hh @@ -0,0 +1,104 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + * Description: The histogram class implements a simple histogram + * + */ + +#ifndef HISTOGRAM_H +#define HISTOGRAM_H + +#include "Global.hh" +#include "Vector.hh" + +class Histogram { +public: + // Constructors + Histogram(int binsize = 1, int bins = 50); + + // Destructor + ~Histogram(); + + // Public Methods + + void add(int64 value); + void add(const Histogram& hist); + void clear() { clear(m_bins); } + void clear(int bins); + void clear(int binsize, int bins); + int64 size() const { return m_count; } + int getBins() const { return m_bins; } + int getBinSize() const { return m_binsize; } + int64 getTotal() const { return m_sumSamples; } + int64 getData(int index) const { return m_data[index]; } + + void printWithMultiplier(ostream& out, double multiplier) const; + void printPercent(ostream& out) const; + void print(ostream& out) const; +private: + // Private Methods + + // Private copy constructor and assignment operator + // Histogram(const Histogram& obj); + // Histogram& operator=(const Histogram& obj); + + // Data Members (m_ prefix) + Vector m_data; + int64 m_max; // the maximum value seen so far + int64 m_count; // the number of elements added + int m_binsize; // the size of each bucket + int m_bins; // the number of buckets + int m_largest_bin; // the largest bin used + + int64 m_sumSamples; // the sum of all samples + int64 m_sumSquaredSamples; // the sum of the square of all samples + + double getStandardDeviation() const; +}; + +bool node_less_then_eq(const Histogram* n1, const Histogram* n2); + +// Output operator declaration +ostream& operator<<(ostream& out, const Histogram& obj); + +// ******************* Definitions ******************* + +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const Histogram& obj) +{ + obj.print(out); + out << flush; + return out; +} + +#endif //HISTOGRAM_H diff --git a/src/mem/ruby/common/Message.cc b/src/mem/ruby/common/Message.cc new file mode 100644 index 000000000..baad8ac9b --- /dev/null +++ b/src/mem/ruby/common/Message.cc @@ -0,0 +1,34 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +#include "Message.hh" diff --git a/src/mem/ruby/common/NetDest.cc b/src/mem/ruby/common/NetDest.cc new file mode 100644 index 000000000..79a6078e9 --- /dev/null +++ b/src/mem/ruby/common/NetDest.cc @@ -0,0 +1,259 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * NetDest.C + * + * Description: See NetDest.h + * + * $Id$ + * + */ + +#include "NetDest.hh" +#include "RubyConfig.hh" +#include "Protocol.hh" + +NetDest::NetDest() +{ + setSize(); +} + +void NetDest::add(MachineID newElement) +{ + m_bits[vecIndex(newElement)].add(bitIndex(newElement.num)); +} + +void NetDest::addNetDest(const NetDest& netDest) +{ + assert(m_bits.size() == netDest.getSize()); + for (int i = 0; i < m_bits.size(); i++) { + m_bits[i].addSet(netDest.m_bits[i]); + } +} + +void NetDest::addRandom() +{ + int i = random()%m_bits.size(); + m_bits[i].addRandom(); +} + +void NetDest::setNetDest(MachineType machine, const Set& set) +{ + // assure that there is only one set of destinations for this machine + assert(MachineType_base_level((MachineType)(machine+1)) - MachineType_base_level(machine) == 1); + m_bits[MachineType_base_level(machine)] = set; +} + +void NetDest::remove(MachineID oldElement) +{ + m_bits[vecIndex(oldElement)].remove(bitIndex(oldElement.num)); +} + +void NetDest::removeNetDest(const NetDest& netDest) +{ + assert(m_bits.size() == netDest.getSize()); + for (int i = 0; i < m_bits.size(); i++) { + m_bits[i].removeSet(netDest.m_bits[i]); + + } +} + +void NetDest::clear() +{ + for (int i = 0; i < m_bits.size(); i++) { + m_bits[i].clear(); + } +} + +void NetDest::broadcast() +{ + for (MachineType machine = MachineType_FIRST; machine < MachineType_NUM; ++machine) { + broadcast(machine); + } +} + +void NetDest::broadcast(MachineType machineType) { + + for (int i = 0; i < MachineType_base_count(machineType); i++) { + MachineID mach = {machineType, i}; + add(mach); + } +} + +//For Princeton Network +Vector NetDest::getAllDest() { + Vector dest; + dest.clear(); + for (int i=0; i 0); + for (int i=0; i getAllDest(); + + NodeID smallestElement() const; + MachineID smallestElement(MachineType machine) const; + + void setSize(); + int getSize() const { return m_bits.size(); } + + // get element for a index + NodeID elementAt(MachineID index); + + void print(ostream& out) const; + +private: + + // Private Methods + // returns a value >= MachineType_base_level("this machine") and < MachineType_base_level("next highest machine") + int vecIndex(MachineID m) const { + int vec_index = MachineType_base_level(m.type); + assert(vec_index < m_bits.size()); + return vec_index; + } + + NodeID bitIndex(NodeID index) const { + return index; + } + + // Data Members (m_ prefix) + Vector < Set > m_bits; // a Vector of bit vectors - i.e. Sets + +}; + +// Output operator declaration +ostream& operator<<(ostream& out, const NetDest& obj); + +// ******************* Definitions ******************* + +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const NetDest& obj) +{ + obj.print(out); + out << flush; + return out; +} + +#endif //NETDEST_H + diff --git a/src/mem/ruby/common/OptBigSet.cc b/src/mem/ruby/common/OptBigSet.cc new file mode 100644 index 000000000..51214e936 --- /dev/null +++ b/src/mem/ruby/common/OptBigSet.cc @@ -0,0 +1,576 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Set.C + * + * Description: See Set.h + * + * $Id: BigSet.C 1.9 05/01/19 13:12:25-06:00 mikem@maya.cs.wisc.edu $ + * + */ + +// modified (rewritten) 05/20/05 by Dan Gibson to accomimdate FASTER >32 bit +// set sizes + +#include "Set.hh" +#include "RubyConfig.hh" + +#if __amd64__ || __LP64__ +#define __64BITS__ +#else +#define __32BITS__ +#endif + +Set::Set() +{ + m_p_nArray = NULL; + setSize(RubyConfig::numberOfProcessors()); +} + +// copy constructor +Set::Set(const Set& obj) { + m_p_nArray = NULL; + setSize(obj.m_nSize); + + // copy from the host to this array + for(int i=0; i0); + setSize(size); +} + +Set::~Set() { + if( (m_p_nArray != (&m_p_nArray_Static[0])) && (m_p_nArray != NULL)) + delete [] m_p_nArray; + m_p_nArray = NULL; +} + + +// /* +// * This function should set the bit corresponding to index +// * to 1. +// */ + +// void Set::add(NodeID index) +// { +// assert(index= 0); + +// #ifdef __32BITS__ +// m_p_nArray[index>>5] |= (1 << (index & 0x01F)); +// #else +// m_p_nArray[index>>6] |= (((unsigned long) 1) << (index & 0x03F)); +// #endif // __32BITS__ + +// } + +/* + * This function should set all the bits in the current set + * that are already set in the parameter set + */ +void Set::addSet(const Set& set) +{ + assert(getSize()==set.getSize()); + for(int i=0; i> 1; + } + } +#else + long mask = 0x7FFFFFFFFFFFFFFF; + + // the number of populated spaces in the higest-order array slot is: + // m_nSize % 64, so the uppermost 64 - m_nSize%64 bits should be + // cleared + + if((m_nSize % 64) != 0) { + for(int j=0; j<64-(m_nSize&0x03F); j++) { + m_p_nArray[m_nArrayLen-1] &= mask; + mask = mask >> 1; + } + } +#endif // __32BITS__ + +} + +// /* +// * This function unsets the bit associated with index +// */ +// void Set::remove(NodeID index) +// { +// assert(index=0); + +// #ifdef __32BITS__ +// m_p_nArray[index>>5] &= ~(0x00000001 << (index & 0x01F)); +// #else +// m_p_nArray[index>>6] &= ~(((unsigned long) 0x0000000000000001) << (index & 0x03F)); +// #endif // __32BITS__ + +// } + + +/* + * This function clears bits that are =1 in the parameter set + */ +void Set::removeSet(const Set& set) +{ + + assert(m_nSize==set.m_nSize); + for(int i=0; i> 1; + } + } +#else + long mask = 0x7FFFFFFFFFFFFFFF; + + // the number of populated spaces in the higest-order array slot is: + // m_nSize % 64, so the uppermost 64 - m_nSize%64 bits should be + // cleared + + if((m_nSize % 64) != 0) { + for(int j=0; j<64-(m_nSize&0x03F); j++) { + m_p_nArray[m_nArrayLen-1] &= mask; + mask = mask >> 1; + } + } +#endif // __32BITS__ + +} + +/* + * This function returns the population count of 1's in the set + */ +int Set::count() const +{ + int counter = 0; + long mask; + for( int i=0; i 0); + long x; + for( int i=0; i> 1; + } +#else + for( int j=0; j<64; j++) { + if(x & 0x0000000000000001) { + return 64*i+j; + } + + x = x >> 1; + } +#endif // __32BITS__ + + ERROR_MSG("No smallest element of an empty set."); + } + } + + ERROR_MSG("No smallest element of an empty set."); + + return 0; +} + +/* + * this function returns true iff all bits are set + */ +bool Set::isBroadcast() const +{ + // check the fully-loaded words by equal to 0xffffffff + // only the last word may not be fully loaded, it is not + // fully loaded iff m_nSize % 32 or 64 !=0 => fully loaded iff + // m_nSize % 32 or 64 == 0 + +#ifdef __32BITS__ + for(int i=0; i< (((m_nSize % 32)==0) ? m_nArrayLen : m_nArrayLen-1); i++) { + if(m_p_nArray[i]!=-1) { + return false; + } + } + + // now check the last word, which may not be fully loaded + long mask = 1; + for(int j=0; j< (m_nSize % 32); j++) { + if((mask & m_p_nArray[m_nArrayLen-1])==0) { + return false; + } + mask = mask << 1; + } +#else + for(int i=0; i< (((m_nSize % 64)==0) ? m_nArrayLen : m_nArrayLen-1); i++) { + if(m_p_nArray[i]!=-1) { + return false; + } + } + + // now check the last word, which may not be fully loaded + long mask = 1; + for(int j=0; j< (m_nSize % 64); j++) { + if((mask & m_p_nArray[m_nArrayLen-1])==0) { + return false; + } + mask = mask << 1; + } + +#endif // __32BITS__ + + return true; +} + +/* + * this function returns true iff no bits are set + */ +bool Set::isEmpty() const +{ + + // here we can simply check if all = 0, since we ensure + // that "extra slots" are all zero + for(int i=0; i< m_nArrayLen ; i++) { + if(m_p_nArray[i]!=0) { + return false; + } + } + + return true; +} + +// returns the logical OR of "this" set and orSet +Set Set::OR(const Set& orSet) const +{ + Set result(m_nSize); + assert(m_nSize == orSet.m_nSize); + for(int i=0; i< m_nArrayLen; i++) { + result.m_p_nArray[i] = m_p_nArray[i] | orSet.m_p_nArray[i]; + } + + return result; + +} + +// returns the logical AND of "this" set and andSet +Set Set::AND(const Set& andSet) const +{ + Set result(m_nSize); + assert(m_nSize == andSet.m_nSize); + + for(int i=0; i< m_nArrayLen; i++) { + result.m_p_nArray[i] = m_p_nArray[i] & andSet.m_p_nArray[i]; + } + + return result; +} + +// // Returns true if the intersection of the two sets is non-empty +// bool Set::intersectionIsNotEmpty(const Set& other_set) const +// { +// assert(m_nSize == other_set.m_nSize); +// for(int i=0; i< m_nArrayLen; i++) { +// if(m_p_nArray[i] & other_set.m_p_nArray[i]) { +// return true; +// } +// } + +// return false; + +// } + +// // Returns true if the intersection of the two sets is empty +// bool Set::intersectionIsEmpty(const Set& other_set) const +// { +// assert(m_nSize == other_set.m_nSize); +// for(int i=0; i< m_nArrayLen; i++) { +// if(m_p_nArray[i] & other_set.m_p_nArray[i]) { +// return false; +// } +// } + +// return true; + +// } + +/* + * Returns false if a bit is set in the parameter set that is + * NOT set in this set + */ +bool Set::isSuperset(const Set& test) const +{ + assert(m_nSize == test.m_nSize); + + for(int i=0;i>5] & (0x00000001 << (element & 0x01F)))!=0); +// #else +// result = ((m_p_nArray[element>>6] & (((unsigned long) 0x0000000000000001) << (element & 0x03F)))!=0); +// #endif // __32BITS__ + +// return result; +// } + +/* + * "Supposed" to return the node id of the (n+1)th set + * bit, IE n=0 => returns nodeid of first set bit, BUT + * since BigSet.C behaves strangely, this implementation + * will behave strangely just for reverse compatability. + * + * Was originally implemented for the flight data recorder + * FDR + */ + +// NodeID Set::elementAt(int n) const +// { +// if(isElement(n)) return (NodeID) true; +// else return 0; + +// /* +// int match = -1; +// for(int i=0;i=0; i--) { +#ifdef __32BITS__ + sprintf(buff,"%08X ",m_p_nArray[i]); +#else + sprintf(buff,"0x %016llX ",m_p_nArray[i]); +#endif // __32BITS__ + out << buff; + } + out << " ]"; + +} + + diff --git a/src/mem/ruby/common/OptBigSet.hh b/src/mem/ruby/common/OptBigSet.hh new file mode 100644 index 000000000..a57a07e13 --- /dev/null +++ b/src/mem/ruby/common/OptBigSet.hh @@ -0,0 +1,202 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Set.h + * + * Description: + * + * $Id: BigSet.h 1.6 05/01/19 13:12:25-06:00 mikem@maya.cs.wisc.edu $ + * + */ + +// modified by Dan Gibson on 05/20/05 to accomidate FASTER +// >32 set lengths, using an array of ints w/ 32 bits/int + +// NOTE: Never include this file directly, this should only be +// included from Set.h + +#ifndef SET_H +#define SET_H + +#include "Global.hh" +#include "Vector.hh" +#include "NodeID.hh" +#include "RubyConfig.hh" + +// gibson 05/20/05 +// enum PresenceBit {NotPresent, Present}; + +class Set { +public: + // Constructors + // creates and empty set + Set(); + Set (int size); + + // used during the replay mechanism + // Set(const char *str); + + Set(const Set& obj); + Set& operator=(const Set& obj); + + // Destructor + ~Set(); + + // Public Methods + + inline void add(NodeID index) + { +#ifdef __32BITS__ + m_p_nArray[index>>5] |= (1 << (index & 0x01F)); +#else + m_p_nArray[index>>6] |= (((unsigned long) 1) << (index & 0x03F)); +#endif // __32BITS__ + } + + void addSet(const Set& set); + void addRandom(); + + inline void remove(NodeID index) + { +#ifdef __32BITS__ + m_p_nArray[index>>5] &= ~(0x00000001 << (index & 0x01F)); +#else + m_p_nArray[index>>6] &= ~(((unsigned long) 0x0000000000000001) << (index & 0x03F)); +#endif // __32BITS__ + } + + + void removeSet(const Set& set); + + inline void clear() { for(int i=0; i>5] & (0x00000001 << (element & 0x01F)))!=0); +#else + return ((m_p_nArray[element>>6] & (((unsigned long) 0x0000000000000001) << (element & 0x03F)))!=0); +#endif // __32BITS__ + + } + + bool isBroadcast() const; + bool isEmpty() const; + + NodeID smallestElement() const; + + // int size() const; + void setSize (int size); + + // get element for a index + inline NodeID elementAt(int index) const + { + if(isElement(index)) return (NodeID) true; + else return 0; + } + + // gibson 05/20/05 + int getSize() const { return m_nSize; } + + // DEPRECATED METHODS + void addToSet(NodeID newElement) { add(newElement); } // Deprecated + void removeFromSet(NodeID newElement) { remove(newElement); } // Deprecated + void clearSet() { clear(); } // Deprecated + void setBroadcast() { broadcast(); } // Deprecated + bool presentInSet(NodeID element) const { return isElement(element); } // Deprecated + + void print(ostream& out) const; +private: + // Private Methods + + // Data Members (m_ prefix) + // gibson 05/20/05 + // Vector m_bits; // This is an vector of uint8 to reduce the size of the set + + int m_nSize; // the number of bits in this set + int m_nArrayLen; // the number of 32-bit words that are held in the array + + // Changed 5/24/05 for static allocation of array + // note that "long" corresponds to 32 bits on a 32-bit machine, + // 64 bits if the -m64 parameter is passed to g++, which it is + // for an AMD opteron under our configuration + + long * m_p_nArray; // an word array to hold the bits in the set + long m_p_nArray_Static[NUMBER_WORDS_PER_SET]; +}; + +// Output operator declaration +ostream& operator<<(ostream& out, const Set& obj); + +// ******************* Definitions ******************* + +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const Set& obj) +{ + obj.print(out); + out << flush; + return out; +} + +#endif //SET_H + diff --git a/src/mem/ruby/common/Set.cc b/src/mem/ruby/common/Set.cc new file mode 100644 index 000000000..1f5b49f90 --- /dev/null +++ b/src/mem/ruby/common/Set.cc @@ -0,0 +1,231 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Set.C + * + * Description: See Set.h + * + * $Id$ + * + */ + +#include "Set.hh" +#include "RubyConfig.hh" + +#ifdef OPTBIGSET +#include "OptBigSet.cc" +#else + +#ifdef BIGSET +#include "BigSet.cc" // code to supports sets larger than 32 +#else + +Set::Set() +{ + setSize(RubyConfig::numberOfChips()); +} + +Set::Set(int size) +{ + setSize(size); +} + +bool Set::isEqual(const Set& set) +{ + return (m_bits == set.m_bits); +} + +void Set::add(NodeID index) +{ + assert((m_bits & m_mask) == m_bits); // check for any bits outside the range + assert(index < m_size); + m_bits |= (1 << index); + assert((m_bits & m_mask) == m_bits); // check for any bits outside the range +} + +void Set::addSet(const Set& set) +{ + assert(m_size == set.m_size); + m_bits |= set.m_bits; + assert((m_bits & m_mask) == m_bits); // check for any bits outside the range +} + +void Set::addRandom() +{ + m_bits |= random(); + m_bits &= m_mask; + assert((m_bits & m_mask) == m_bits); // check for any bits outside the range +} + +void Set::remove(NodeID index) +{ + assert(index < m_size); + m_bits &= ~(1 << index); + assert((m_bits & m_mask) == m_bits); // check for any bits outside the range +} + +void Set::removeSet(const Set& set) +{ + assert(m_size == set.m_size); + m_bits &= ~(set.m_bits); + assert((m_bits & m_mask) == m_bits); // check for any bits outside the range +} + +void Set::clear() +{ + m_bits = 0; +} + +void Set::broadcast() +{ + m_bits = m_mask; +} + +int Set::count() const +{ + int counter = 0; + for (int i=0; i 0); + int counter = 0; + for (int i=0; i 0); + int offset = m_address.getOffset(); + for(int i=0; isetByte(i, data.getByte(offset+i)); + } +} + +void SubBlock::internalMergeTo(DataBlock& data) const +{ + int size = getSize(); + assert(size > 0); + int offset = m_address.getOffset(); + for(int i=0; igetByte(i)); // This will detect crossing a cache line boundary + } +} + +void SubBlock::print(ostream& out) const +{ + out << "[" << m_address << ", " << getSize() << ", " << m_data << "]"; +} + + + diff --git a/src/mem/ruby/common/SubBlock.hh b/src/mem/ruby/common/SubBlock.hh new file mode 100644 index 000000000..43f91e191 --- /dev/null +++ b/src/mem/ruby/common/SubBlock.hh @@ -0,0 +1,105 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +#ifndef SubBlock_H +#define SubBlock_H + +#include "Global.hh" +#include "Address.hh" +#include "RubyConfig.hh" +#include "DataBlock.hh" +#include "Vector.hh" + +class SubBlock { +public: + // Constructors + SubBlock() { } + SubBlock(const Address& addr, int size); + SubBlock(const Address& addr, const Address& logicalAddress, int size); + + // Destructor + ~SubBlock() { } + + // Public Methods + const Address& getAddress() const { return m_address; } + const Address& getLogicalAddress() const { return m_logicalAddress; } + void setAddress(const Address& addr) { m_address = addr; } + void setLogicalAddress(const Address& addr) { m_logicalAddress = addr; } + + int getSize() const { return m_data.size(); } + void setSize(int size) { m_data.setSize(size); } + uint8 getByte(int offset) const { return m_data[offset]; } + void setByte(int offset, uint8 data) { m_data[offset] = data; } + + // Shorthands + uint8 readByte() const { return getByte(0); } + void writeByte(uint8 data) { setByte(0, data); } + + // Merging to and from DataBlocks - We only need to worry about + // updates when we are using DataBlocks + void mergeTo(DataBlock& data) const { if (DATA_BLOCK) { internalMergeTo(data); } } + void mergeFrom(const DataBlock& data) { if (DATA_BLOCK) { internalMergeFrom(data); } } + + void print(ostream& out) const; +private: + // Private Methods + // SubBlock(const SubBlock& obj); + // SubBlock& operator=(const SubBlock& obj); + // bool bytePresent(const Address& addr) { return ((addr.getAddress() >= m_address.getAddress()) && (addr.getAddress() < (m_address.getAddress()+getSize()))); } + // uint8 getByte(const Address& addr) { return m_data[addr.getAddress() - m_address.getAddress()]; } + + void internalMergeTo(DataBlock& data) const; + void internalMergeFrom(const DataBlock& data); + + // Data Members (m_ prefix) + Address m_address; + Address m_logicalAddress; + Vector m_data; +}; + +// Output operator declaration +ostream& operator<<(ostream& out, const SubBlock& obj); + +// ******************* Definitions ******************* + +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const SubBlock& obj) +{ + obj.print(out); + out << flush; + return out; +} + +#endif //SubBlock_H diff --git a/src/mem/ruby/config/RubyConfig.cc b/src/mem/ruby/config/RubyConfig.cc new file mode 100644 index 000000000..fe4e3be8f --- /dev/null +++ b/src/mem/ruby/config/RubyConfig.cc @@ -0,0 +1,193 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * RubyConfig.C + * + * Description: See RubyConfig.h + * + * $Id$ + * + */ + +#include "RubyConfig.hh" +#include "protocol_name.hh" +#include "util.hh" +#include "interface.hh" +#include "Protocol.hh" + +#define CHECK_POWER_OF_2(N) { if (!is_power_of_2(N)) { ERROR_MSG(#N " must be a power of 2."); }} +#define CHECK_ZERO(N) { if (N != 0) { ERROR_MSG(#N " must be zero at initialization."); }} +#define CHECK_NON_ZERO(N) { if (N == 0) { ERROR_MSG(#N " must be non-zero."); }} + + +void RubyConfig::init() +{ + // MemoryControl: + CHECK_NON_ZERO(MEM_BUS_CYCLE_MULTIPLIER); + CHECK_NON_ZERO(BANKS_PER_RANK); + CHECK_NON_ZERO(RANKS_PER_DIMM); + CHECK_NON_ZERO(DIMMS_PER_CHANNEL); + CHECK_NON_ZERO(BANK_QUEUE_SIZE); + CHECK_NON_ZERO(BANK_BUSY_TIME); + CHECK_NON_ZERO(MEM_CTL_LATENCY); + CHECK_NON_ZERO(REFRESH_PERIOD); + CHECK_NON_ZERO(BASIC_BUS_BUSY_TIME); + + CHECK_POWER_OF_2(BANKS_PER_RANK); + CHECK_POWER_OF_2(RANKS_PER_DIMM); + CHECK_POWER_OF_2(DIMMS_PER_CHANNEL); + + CHECK_NON_ZERO(g_MEMORY_SIZE_BYTES); + CHECK_NON_ZERO(g_DATA_BLOCK_BYTES); + CHECK_NON_ZERO(g_PAGE_SIZE_BYTES); + CHECK_NON_ZERO(g_NUM_PROCESSORS); + CHECK_NON_ZERO(g_PROCS_PER_CHIP); + if(g_NUM_SMT_THREADS == 0){ //defaults to single-threaded + g_NUM_SMT_THREADS = 1; + } + if (g_NUM_L2_BANKS == 0) { // defaults to number of ruby nodes + g_NUM_L2_BANKS = g_NUM_PROCESSORS; + } + if (g_NUM_MEMORIES == 0) { // defaults to number of ruby nodes + g_NUM_MEMORIES = g_NUM_PROCESSORS; + } + + CHECK_ZERO(g_MEMORY_SIZE_BITS); + CHECK_ZERO(g_DATA_BLOCK_BITS); + CHECK_ZERO(g_PAGE_SIZE_BITS); + CHECK_ZERO(g_NUM_PROCESSORS_BITS); + CHECK_ZERO(g_NUM_CHIP_BITS); + CHECK_ZERO(g_NUM_L2_BANKS_BITS); + CHECK_ZERO(g_NUM_MEMORIES_BITS); + CHECK_ZERO(g_PROCS_PER_CHIP_BITS); + CHECK_ZERO(g_NUM_L2_BANKS_PER_CHIP); + CHECK_ZERO(g_NUM_L2_BANKS_PER_CHIP_BITS); + CHECK_ZERO(g_NUM_MEMORIES_BITS); + CHECK_ZERO(g_MEMORY_MODULE_BLOCKS); + CHECK_ZERO(g_MEMORY_MODULE_BITS); + CHECK_ZERO(g_NUM_MEMORIES_PER_CHIP); + + CHECK_POWER_OF_2(g_MEMORY_SIZE_BYTES); + CHECK_POWER_OF_2(g_DATA_BLOCK_BYTES); + CHECK_POWER_OF_2(g_NUM_PROCESSORS); + CHECK_POWER_OF_2(g_NUM_L2_BANKS); + CHECK_POWER_OF_2(g_NUM_MEMORIES); + CHECK_POWER_OF_2(g_PROCS_PER_CHIP); + + ASSERT(g_NUM_PROCESSORS >= g_PROCS_PER_CHIP); // obviously can't have less processors than procs/chip + g_NUM_CHIPS = g_NUM_PROCESSORS/g_PROCS_PER_CHIP; + ASSERT(g_NUM_L2_BANKS >= g_NUM_CHIPS); // cannot have a single L2cache across multiple chips + + g_NUM_L2_BANKS_PER_CHIP = g_NUM_L2_BANKS/g_NUM_CHIPS; + + ASSERT(L2_CACHE_NUM_SETS_BITS > log_int(g_NUM_L2_BANKS_PER_CHIP)); // cannot have less than one set per bank + L2_CACHE_NUM_SETS_BITS = L2_CACHE_NUM_SETS_BITS - log_int(g_NUM_L2_BANKS_PER_CHIP); + + if (g_NUM_CHIPS > g_NUM_MEMORIES) { + g_NUM_MEMORIES_PER_CHIP = 1; // some chips have a memory, others don't + } else { + g_NUM_MEMORIES_PER_CHIP = g_NUM_MEMORIES/g_NUM_CHIPS; + } + + g_NUM_CHIP_BITS = log_int(g_NUM_CHIPS); + g_MEMORY_SIZE_BITS = log_int(g_MEMORY_SIZE_BYTES); + g_DATA_BLOCK_BITS = log_int(g_DATA_BLOCK_BYTES); + g_PAGE_SIZE_BITS = log_int(g_PAGE_SIZE_BYTES); + g_NUM_PROCESSORS_BITS = log_int(g_NUM_PROCESSORS); + g_NUM_L2_BANKS_BITS = log_int(g_NUM_L2_BANKS); + g_NUM_L2_BANKS_PER_CHIP_BITS = log_int(g_NUM_L2_BANKS_PER_CHIP); + g_NUM_MEMORIES_BITS = log_int(g_NUM_MEMORIES); + g_PROCS_PER_CHIP_BITS = log_int(g_PROCS_PER_CHIP); + + g_MEMORY_MODULE_BITS = g_MEMORY_SIZE_BITS - g_DATA_BLOCK_BITS - g_NUM_MEMORIES_BITS; + g_MEMORY_MODULE_BLOCKS = (int64(1) << g_MEMORY_MODULE_BITS); + + if ((!Protocol::m_CMP) && (g_PROCS_PER_CHIP > 1)) { + ERROR_MSG("Non-CMP protocol should set g_PROCS_PER_CHIP to 1"); + } + + // Randomize the execution + srandom(g_RANDOM_SEED); +} + +int RubyConfig::L1CacheNumToL2Base(NodeID L1CacheNum) +{ + return L1CacheNum/g_PROCS_PER_CHIP; +} + +static void print_parameters(ostream& out) +{ + +#define PARAM(NAME) { out << #NAME << ": " << NAME << endl; } +#define PARAM_UINT(NAME) { out << #NAME << ": " << NAME << endl; } +#define PARAM_ULONG(NAME) { out << #NAME << ": " << NAME << endl; } +#define PARAM_BOOL(NAME) { out << #NAME << ": " << bool_to_string(NAME) << endl; } +#define PARAM_DOUBLE(NAME) { out << #NAME << ": " << NAME << endl; } +#define PARAM_STRING(NAME) { assert(NAME != NULL); out << #NAME << ": " << string(NAME) << endl; } +#define PARAM_ARRAY(PTYPE, NAME, ARRAY_SIZE) \ + { \ + out << #NAME << ": ("; \ + for (int i = 0; i < ARRAY_SIZE; i++) { \ + if (i != 0) { \ + out << ", "; \ + } \ + out << NAME[i]; \ + } \ + out << ")" << endl; \ + } \ + + +#include CONFIG_VAR_FILENAME +#undef PARAM +#undef PARAM_UINT +#undef PARAM_ULONG +#undef PARAM_BOOL +#undef PARAM_DOUBLE +#undef PARAM_STRING +#undef PARAM_ARRAY +} + +void RubyConfig::printConfiguration(ostream& out) { + out << "Ruby Configuration" << endl; + out << "------------------" << endl; + + out << "protocol: " << CURRENT_PROTOCOL << endl; + SIMICS_print_version(out); + out << "compiled_at: " << __TIME__ << ", " << __DATE__ << endl; + out << "RUBY_DEBUG: " << bool_to_string(RUBY_DEBUG) << endl; + + char buffer[100]; + gethostname(buffer, 50); + out << "hostname: " << buffer << endl; + + print_parameters(out); +} + + diff --git a/src/mem/ruby/config/RubyConfig.hh b/src/mem/ruby/config/RubyConfig.hh new file mode 100644 index 000000000..b2cc745bc --- /dev/null +++ b/src/mem/ruby/config/RubyConfig.hh @@ -0,0 +1,157 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * RubyConfig.h + * + * Description: This class has only static members and class methods, + * and thus should never need to be instantiated. + * + * $Id$ + * + */ + +#ifndef RUBYCONFIG_H +#define RUBYCONFIG_H + +#include "Global.hh" +#define CONFIG_VAR_FILENAME "config.include" +#include "vardecl.hh" +#include "NodeID.hh" + +#define MEMORY_LATENCY RubyConfig::memoryResponseLatency() +#define ABORT_DELAY m_chip_ptr->getTransactionManager(m_version)->getAbortDelay() + +// Set paramterization +/* + * This defines the number of longs (32-bits on 32 bit machines, + * 64-bit on 64-bit AMD machines) to use to hold the set... + * the default is 4, allowing 128 or 256 different members + * of the set. + * + * This should never need to be changed for correctness reasons, + * though increasing it will increase performance for larger + * set sizes at the cost of a (much) larger memory footprint + * + */ +const int NUMBER_WORDS_PER_SET = 4; + +class RubyConfig { +public: + + // CACHE BLOCK CONFIG VARIBLES + static int dataBlockBits() { return g_DATA_BLOCK_BITS; } + static int dataBlockBytes() { return g_DATA_BLOCK_BYTES; } + + // SUPPORTED PHYSICAL MEMORY CONFIG VARIABLES + static int pageSizeBits() { return g_PAGE_SIZE_BITS; } + static int pageSizeBytes() { return g_PAGE_SIZE_BYTES; } + static int memorySizeBits() { return g_MEMORY_SIZE_BITS; } + static int64 memorySizeBytes() { return g_MEMORY_SIZE_BYTES; } + static int memoryModuleBits() { return g_MEMORY_MODULE_BITS; } + static int64 memoryModuleBlocks() { return g_MEMORY_MODULE_BLOCKS; } + + // returns number of SMT threads per physical processor + static int numberofSMTThreads() { return g_NUM_SMT_THREADS; } + // defines the number of simics processors (power of 2) + static int numberOfProcessors() { return g_NUM_PROCESSORS; } + static int procsPerChipBits() { return g_PROCS_PER_CHIP_BITS; } + static int numberOfProcsPerChip() { return g_PROCS_PER_CHIP; } + static int numberOfChips() { return g_NUM_CHIPS; } + + // MACHINE INSTANIATION CONFIG VARIABLES + // ------------------------------------- + // L1 CACHE MACHINES + // defines the number of L1banks - idependent of ruby chips (power of 2) + // NOTE - no protocols currently supports L1s != processors, just a placeholder + static int L1CacheBits() { return g_NUM_PROCESSORS_BITS; } + static int numberOfL1Cache() { return g_NUM_PROCESSORS; } + static int L1CachePerChipBits() { return procsPerChipBits() ; } // L1s != processors not currently supported + static int numberOfL1CachePerChip() { return numberOfProcsPerChip(); } // L1s != processors not currently supported + static int numberOfL1CachePerChip(NodeID myNodeID) { return numberOfL1CachePerChip(); } + static int L1CacheTransitionsPerCycle() { return L1CACHE_TRANSITIONS_PER_RUBY_CYCLE; } + + // L2 CACHE MACHINES + // defines the number of L2banks/L2Caches - idependent of ruby chips (power of 2) + static int L2CacheBits() { return g_NUM_L2_BANKS_BITS; } + static int numberOfL2Cache() { return g_NUM_L2_BANKS; } + static int L1CacheNumToL2Base(NodeID L1RubyNodeID); + static int L2CachePerChipBits() { return g_NUM_L2_BANKS_PER_CHIP_BITS; } + static int numberOfL2CachePerChip() { return g_NUM_L2_BANKS_PER_CHIP; } + static int numberOfL2CachePerChip(NodeID myNodeID) { return numberOfL2CachePerChip(); } + static int L2CacheTransitionsPerCycle() { return L2CACHE_TRANSITIONS_PER_RUBY_CYCLE; } + + // DIRECTORY/MEMORY MACHINES + // defines the number of ruby memories - idependent of ruby chips (power of 2) + static int memoryBits() { return g_NUM_MEMORIES_BITS; } + static int numberOfDirectory() { return numberOfMemories(); } + static int numberOfMemories() { return g_NUM_MEMORIES; } + static int numberOfDirectoryPerChip() { return g_NUM_MEMORIES_PER_CHIP; } + static int numberOfDirectoryPerChip(NodeID myNodeID) { return g_NUM_MEMORIES_PER_CHIP; } + static int DirectoryTransitionsPerCycle() { return DIRECTORY_TRANSITIONS_PER_RUBY_CYCLE; } + + // PERSISTENT ARBITER MACHINES + static int numberOfPersistentArbiter() { return numberOfMemories(); } + static int numberOfPersistentArbiterPerChip() {return numberOfDirectoryPerChip(); } + static int numberOfPersistentArbiterPerChip(NodeID myNodeID) {return numberOfDirectoryPerChip(myNodeID); } + static int PersistentArbiterTransitionsPerCycle() { return L2CACHE_TRANSITIONS_PER_RUBY_CYCLE; } + + // ---- END MACHINE SPECIFIC VARIABLES ---- + + // VARIABLE MEMORY RESPONSE LATENCY + // *** NOTE *** This is where variation is added to the simulation + // see Alameldeen et al. HPCA 2003 for further details + static int memoryResponseLatency() { return MEMORY_RESPONSE_LATENCY_MINUS_2+(random() % 5); } + + static void init(); + static void printConfiguration(ostream& out); + + // Memory Controller + static int memBusCycleMultiplier () { return MEM_BUS_CYCLE_MULTIPLIER; } + static int banksPerRank () { return BANKS_PER_RANK; } + static int ranksPerDimm () { return RANKS_PER_DIMM; } + static int dimmsPerChannel () { return DIMMS_PER_CHANNEL; } + static int bankBit0 () { return BANK_BIT_0; } + static int rankBit0 () { return RANK_BIT_0; } + static int dimmBit0 () { return DIMM_BIT_0; } + static int bankQueueSize () { return BANK_QUEUE_SIZE; } + static int bankBusyTime () { return BANK_BUSY_TIME; } + static int rankRankDelay () { return RANK_RANK_DELAY; } + static int readWriteDelay () { return READ_WRITE_DELAY; } + static int basicBusBusyTime () { return BASIC_BUS_BUSY_TIME; } + static int memCtlLatency () { return MEM_CTL_LATENCY; } + static int refreshPeriod () { return REFRESH_PERIOD; } + static int tFaw () { return TFAW; } + static int memRandomArbitrate () { return MEM_RANDOM_ARBITRATE; } + static int memFixedDelay () { return MEM_FIXED_DELAY; } + +private: +}; + +#endif //RUBYCONFIG_H diff --git a/src/mem/ruby/config/config.include b/src/mem/ruby/config/config.include new file mode 100644 index 000000000..f853fb72b --- /dev/null +++ b/src/mem/ruby/config/config.include @@ -0,0 +1,323 @@ +// +// This file has been modified by Kevin Moore and Dan Nussbaum of the +// Scalable Systems Research Group at Sun Microsystems Laboratories +// (http://research.sun.com/scalable/) to support the Adaptive +// Transactional Memory Test Platform (ATMTP). For information about +// ATMTP, see the GEMS website: http://www.cs.wisc.edu/gems/. +// +// Please send email to atmtp-interest@sun.com with feedback, questions, or +// to request future announcements about ATMTP. +// +// ---------------------------------------------------------------------- +// +// File modification date: 2008-02-23 +// +// ---------------------------------------------------------------------- +// +// ATMTP is distributed as part of the GEMS software toolset and is +// available for use and modification under the terms of version 2 of the +// GNU General Public License. The GNU General Public License is contained +// in the file $GEMS/LICENSE. +// +// Multifacet GEMS is free software; you can redistribute it and/or modify +// it under the terms of version 2 of the GNU General Public License as +// published by the Free Software Foundation. +// +// Multifacet GEMS is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with the Multifacet GEMS; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// +// ---------------------------------------------------------------------- +// + +// see rubyconfig.defaults for some explanations + +PARAM( g_RANDOM_SEED ); + +// Maximum number of cycles a request is can be outstanding before the +// Sequencer of StoreBuffer declares we're in deadlock/livelock +PARAM( g_DEADLOCK_THRESHOLD ); +PARAM_BOOL( RANDOMIZATION ); +PARAM_BOOL( g_SYNTHETIC_DRIVER ); +PARAM_BOOL( g_DETERMINISTIC_DRIVER ); + +// FOR MOESI_CMP_token +PARAM_BOOL( g_FILTERING_ENABLED ); +PARAM_BOOL( g_DISTRIBUTED_PERSISTENT_ENABLED ); +PARAM_BOOL( g_DYNAMIC_TIMEOUT_ENABLED ); +PARAM( g_RETRY_THRESHOLD ); +PARAM( g_FIXED_TIMEOUT_LATENCY ); + +PARAM( g_trace_warmup_length ); +PARAM_DOUBLE( g_bash_bandwidth_adaptive_threshold ); + +PARAM( g_tester_length ); +PARAM( g_synthetic_locks ); +PARAM( g_deterministic_addrs ); +// Specified Generator: See SpecifiedGeneratorType in external.sm for valid values +PARAM_STRING( g_SpecifiedGenerator ); +PARAM( g_callback_counter ); +PARAM( g_NUM_COMPLETIONS_BEFORE_PASS ); + +PARAM( g_NUM_SMT_THREADS ); + +PARAM( g_think_time ); +PARAM( g_hold_time ); +PARAM( g_wait_time ); + +// For debugging purposes, one can enable a trace of all the protocol +// state machine changes. Unfortunately, the code to generate the +// trace is protocol specific. To enable the code for some of the +// standard protocols, +// 1. change "PROTOCOL_DEBUG_TRACE = true" +// 2. enable debug in Makefile +// 3. use the "--start 1" command line parameter or +// "g_debug_ptr->setDebugTime(1)" to beging the following to set the +// debug begin time +// +// this use to be ruby/common/Global.h + +PARAM_BOOL( PROTOCOL_DEBUG_TRACE ); +// a string for filtering debugging output (for all g_debug vars see Debug.h) +PARAM_STRING( DEBUG_FILTER_STRING ); +// filters debugging messages based on priority (low, med, high) +PARAM_STRING( DEBUG_VERBOSITY_STRING ); +// filters debugging messages based on a ruby time +PARAM_ULONG( DEBUG_START_TIME ); +// sends debugging messages to a output filename +PARAM_STRING( DEBUG_OUTPUT_FILENAME ); + +// defines relative (integer) clock multipliers between ruby, opal, and simics +PARAM( SIMICS_RUBY_MULTIPLIER ); +PARAM( OPAL_RUBY_MULTIPLIER ); + +PARAM_BOOL( TRANSACTION_TRACE_ENABLED ); +PARAM_BOOL( USER_MODE_DATA_ONLY ); +PARAM_BOOL( PROFILE_HOT_LINES ); + +// PROFILE_ALL_INSTRUCTIONS is used if you want Ruby to profile all instructions executed +// The following need to be true for this to work correctly: +// 1. Disable istc and dstc for this simulation run +// 2. Add the following line to the object "sim" in the checkpoint you run from: +// instruction_profile_line_size: 4 +// This is used to have simics report back all instruction requests + +// For more details on how to find out how to interpret the output physical instruction +// address, please read the document in the simics-howto directory +PARAM_BOOL( PROFILE_ALL_INSTRUCTIONS ); + +// Set the following variable to true if you want a complete trace of +// PCs (physical address of program counters, with executing processor IDs) +// to be printed to stdout. Make sure to direct the simics output to a file. +// Otherwise, the run will take a really long time! +// A long run may write a file that can exceed the OS limit on file length +PARAM_BOOL( PRINT_INSTRUCTION_TRACE ); +PARAM( g_DEBUG_CYCLE ); + +// Don't allow any datablocks to enter the STC +PARAM_BOOL( BLOCK_STC ); + +// Make the entire memory system perfect +PARAM_BOOL( PERFECT_MEMORY_SYSTEM ); +PARAM( PERFECT_MEMORY_SYSTEM_LATENCY ); + +PARAM_BOOL( DATA_BLOCK ); // Define NO_DATA_BLOCK to make the DataBlock take zero space + +PARAM_BOOL( REMOVE_SINGLE_CYCLE_DCACHE_FAST_PATH ); + +// ********************************************* +// CACHE & MEMORY PARAMETERS +// ********************************************* + +PARAM_BOOL( g_SIMICS ); + +PARAM( L1_CACHE_ASSOC ); +PARAM( L1_CACHE_NUM_SETS_BITS ); +PARAM( L2_CACHE_ASSOC ); +PARAM( L2_CACHE_NUM_SETS_BITS ); + +PARAM_ULONG( g_MEMORY_SIZE_BYTES ); +PARAM( g_DATA_BLOCK_BYTES ); +// The following page size parameter is used by the stride prefetcher +PARAM( g_PAGE_SIZE_BYTES ); +PARAM_STRING( g_REPLACEMENT_POLICY ); + +PARAM( g_NUM_PROCESSORS ); +PARAM( g_NUM_L2_BANKS ); +PARAM( g_NUM_MEMORIES ); +PARAM( g_PROCS_PER_CHIP ); + +// The following group of parameters are calculated. They must +// _always_ be left at zero. +PARAM( g_NUM_CHIPS ); +PARAM( g_NUM_CHIP_BITS ); +PARAM( g_MEMORY_SIZE_BITS ); +PARAM( g_DATA_BLOCK_BITS ); +PARAM( g_PAGE_SIZE_BITS ); +PARAM( g_NUM_PROCESSORS_BITS ); +PARAM( g_PROCS_PER_CHIP_BITS ); +PARAM( g_NUM_L2_BANKS_BITS ); +PARAM( g_NUM_L2_BANKS_PER_CHIP_BITS ); +PARAM( g_NUM_L2_BANKS_PER_CHIP ); +PARAM( g_NUM_MEMORIES_BITS ); +PARAM( g_NUM_MEMORIES_PER_CHIP ); +PARAM( g_MEMORY_MODULE_BITS ); +PARAM_ULONG( g_MEMORY_MODULE_BLOCKS ); + +// determines the mapping between L2 banks and sets within L2 banks +PARAM_BOOL( MAP_L2BANKS_TO_LOWEST_BITS ); + +// TIMING PARAMETERS +PARAM( DIRECTORY_CACHE_LATENCY ); + +PARAM( NULL_LATENCY ); +PARAM( ISSUE_LATENCY ); +PARAM( CACHE_RESPONSE_LATENCY ); +PARAM( L2_RESPONSE_LATENCY ); +PARAM( L2_TAG_LATENCY ); +PARAM( L1_RESPONSE_LATENCY ); +PARAM( MEMORY_RESPONSE_LATENCY_MINUS_2 ); +PARAM( DIRECTORY_LATENCY ); +PARAM( NETWORK_LINK_LATENCY ); +PARAM( COPY_HEAD_LATENCY ); +PARAM( ON_CHIP_LINK_LATENCY ); +PARAM( RECYCLE_LATENCY ); +PARAM( L2_RECYCLE_LATENCY ); +PARAM( TIMER_LATENCY ); +PARAM( TBE_RESPONSE_LATENCY ); +PARAM_BOOL( PERIODIC_TIMER_WAKEUPS ); + +// constants used by TM protocols +PARAM_BOOL( PROFILE_EXCEPTIONS ); +PARAM_BOOL( PROFILE_XACT ); +PARAM_BOOL( PROFILE_NONXACT ); +PARAM_BOOL( XACT_DEBUG ); +PARAM ( XACT_DEBUG_LEVEL ); +PARAM_BOOL( XACT_MEMORY ); +PARAM_BOOL( XACT_ENABLE_TOURMALINE ); +PARAM( XACT_NUM_CURRENT ); +PARAM( XACT_LAST_UPDATE ); +PARAM_BOOL( XACT_ISOLATION_CHECK ); +PARAM_BOOL( PERFECT_FILTER ); +PARAM_STRING( READ_WRITE_FILTER ); +PARAM_BOOL( PERFECT_VIRTUAL_FILTER ); +PARAM_STRING( VIRTUAL_READ_WRITE_FILTER ); +PARAM_BOOL( PERFECT_SUMMARY_FILTER ); +PARAM_STRING( SUMMARY_READ_WRITE_FILTER ); +PARAM_BOOL( XACT_EAGER_CD ); +PARAM_BOOL( XACT_LAZY_VM ); +PARAM_STRING( XACT_CONFLICT_RES ); +PARAM_BOOL( XACT_VISUALIZER ); +PARAM( XACT_COMMIT_TOKEN_LATENCY ) ; +PARAM_BOOL( XACT_NO_BACKOFF ); +PARAM ( XACT_LOG_BUFFER_SIZE ); +PARAM ( XACT_STORE_PREDICTOR_HISTORY); +PARAM ( XACT_STORE_PREDICTOR_ENTRIES); +PARAM ( XACT_STORE_PREDICTOR_THRESHOLD); +PARAM ( XACT_FIRST_ACCESS_COST ); +PARAM ( XACT_FIRST_PAGE_ACCESS_COST ); +PARAM_BOOL( ENABLE_MAGIC_WAITING ); +PARAM_BOOL( ENABLE_WATCHPOINT ); +PARAM_BOOL( XACT_ENABLE_VIRTUALIZATION_LOGTM_SE ); + +// ATMTP +PARAM_BOOL( ATMTP_ENABLED ); +PARAM_BOOL( ATMTP_ABORT_ON_NON_XACT_INST ); +PARAM_BOOL( ATMTP_ALLOW_SAVE_RESTORE_IN_XACT ); +PARAM( ATMTP_XACT_MAX_STORES ); +PARAM( ATMTP_DEBUG_LEVEL ); + +// constants used by CMP protocols +PARAM( L1_REQUEST_LATENCY ); +PARAM( L2_REQUEST_LATENCY ); +PARAM_BOOL( SINGLE_ACCESS_L2_BANKS ); // hack to simulate multi-cycle L2 bank accesses + +// Ruby cycles between when a sequencer issues a miss it arrives at +// the L1 cache controller +PARAM( SEQUENCER_TO_CONTROLLER_LATENCY ); + +// Number of transitions each controller state machines can complete per cycle +PARAM( L1CACHE_TRANSITIONS_PER_RUBY_CYCLE ); +PARAM( L2CACHE_TRANSITIONS_PER_RUBY_CYCLE ); +PARAM( DIRECTORY_TRANSITIONS_PER_RUBY_CYCLE ); + +// Maximum number of requests (including prefetches) outstanding from +// the sequencer (Note: this also include items buffered in the store +// buffer) +PARAM( g_SEQUENCER_OUTSTANDING_REQUESTS ); + +// Number of TBEs available for demand misses, prefetches, and replacements +PARAM( NUMBER_OF_TBES ); +PARAM( NUMBER_OF_L1_TBES ); +PARAM( NUMBER_OF_L2_TBES ); + +// NOTE: Finite buffering allows us to simulate a wormhole routed network +// with idealized flow control. All message buffers within the network (i.e. +// the switch's input and output buffers) are set to the size specified below +// by the PROTOCOL_BUFFER_SIZE +PARAM_BOOL( FINITE_BUFFERING ); +PARAM( FINITE_BUFFER_SIZE ); // Zero is unbounded buffers +// Number of requests buffered between the sequencer and the L1 conroller +// This can be more accurately simulated in Opal, therefore it's set to an +// infinite number +// Only effects the simualtion when FINITE_BUFFERING is enabled +PARAM( PROCESSOR_BUFFER_SIZE ); +// The PROTOCOL_BUFFER_SIZE limits the size of all other buffers connecting to +// Controllers. Controlls the number of request issued by the L2 HW Prefetcher +PARAM( PROTOCOL_BUFFER_SIZE ); + +// Enable the TSO (Total Store Order) memory model +PARAM_BOOL( TSO ); // Note: This also disables the "write" STCs + +// NETWORK PARAMETERS + +// Network Topology: See TopologyType in external.sm for valid values +PARAM_STRING( g_NETWORK_TOPOLOGY ); + +// Cache Design specifies file prefix for topology +PARAM_STRING( g_CACHE_DESIGN ); + +PARAM( g_endpoint_bandwidth ); +PARAM_BOOL( g_adaptive_routing ); +PARAM( NUMBER_OF_VIRTUAL_NETWORKS ); +PARAM( FAN_OUT_DEGREE ); +PARAM_BOOL( g_PRINT_TOPOLOGY ); + +// transactional memory +PARAM( XACT_LENGTH ); +PARAM( XACT_SIZE ); +PARAM( ABORT_RETRY_TIME ); + +// Princeton Network (Garnet) +PARAM_BOOL( g_GARNET_NETWORK ); +PARAM_BOOL( g_DETAIL_NETWORK ); +PARAM_BOOL( g_NETWORK_TESTING ); +PARAM( g_FLIT_SIZE ); +PARAM( g_NUM_PIPE_STAGES ); +PARAM( g_VCS_PER_CLASS ); +PARAM( g_BUFFER_SIZE ); + +// MemoryControl: +PARAM( MEM_BUS_CYCLE_MULTIPLIER ); +PARAM( BANKS_PER_RANK ); +PARAM( RANKS_PER_DIMM ); +PARAM( DIMMS_PER_CHANNEL ); +PARAM( BANK_BIT_0 ); +PARAM( RANK_BIT_0 ); +PARAM( DIMM_BIT_0 ); +PARAM( BANK_QUEUE_SIZE ); +PARAM( BANK_BUSY_TIME ); +PARAM( RANK_RANK_DELAY ); +PARAM( READ_WRITE_DELAY ); +PARAM( BASIC_BUS_BUSY_TIME ); +PARAM( MEM_CTL_LATENCY ); +PARAM( REFRESH_PERIOD ); +PARAM( TFAW ); +PARAM( MEM_RANDOM_ARBITRATE ); +PARAM( MEM_FIXED_DELAY ); + diff --git a/src/mem/ruby/config/rubyconfig.defaults b/src/mem/ruby/config/rubyconfig.defaults new file mode 100644 index 000000000..3b86b4645 --- /dev/null +++ b/src/mem/ruby/config/rubyconfig.defaults @@ -0,0 +1,466 @@ +// +// This file has been modified by Kevin Moore and Dan Nussbaum of the +// Scalable Systems Research Group at Sun Microsystems Laboratories +// (http://research.sun.com/scalable/) to support the Adaptive +// Transactional Memory Test Platform (ATMTP). For information about +// ATMTP, see the GEMS website: http://www.cs.wisc.edu/gems/. +// +// Please send email to atmtp-interest@sun.com with feedback, questions, or +// to request future announcements about ATMTP. +// +// ---------------------------------------------------------------------- +// +// File modification date: 2008-02-23 +// +// ---------------------------------------------------------------------- +// +// ATMTP is distributed as part of the GEMS software toolset and is +// available for use and modification under the terms of version 2 of the +// GNU General Public License. The GNU General Public License is contained +// in the file $GEMS/LICENSE. +// +// Multifacet GEMS is free software; you can redistribute it and/or modify +// it under the terms of version 2 of the GNU General Public License as +// published by the Free Software Foundation. +// +// Multifacet GEMS is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with the Multifacet GEMS; if not, write to the Free Software Foundation, +// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// +// ---------------------------------------------------------------------- +// + +g_RANDOM_SEED: 1 +g_SIMICS: true + +g_DEADLOCK_THRESHOLD: 500000 + +// determines how many Simics cycles advance for every Ruby cycle +// (does not apply when running Opal) +SIMICS_RUBY_MULTIPLIER: 4 + +// corresponding parameter when using Opal+Ruby+Simics +OPAL_RUBY_MULTIPLIER: 1 + + +// Ruby cycles between when a sequencer issues a request and it arrives at +// the L1 cache controller +// +// ** important ** this parameter determines the L2 hit latency when +// using the SMP protocols with a combined L1/L2 controller (-cache.sm) +// +SEQUENCER_TO_CONTROLLER_LATENCY: 4 + + +// When set to false, the L1 cache structures are probed for a hit in Sequencer.C +// If a request hits, it is *not* issued to the cache controller +// When set to true, all processor data requests issue to cache controller +// +// ** important ** this parameter must be set to false for proper L1/L2 hit timing +// for the SMP protocols with combined L1/L2 controllers (-cache.sm) +// +REMOVE_SINGLE_CYCLE_DCACHE_FAST_PATH: false + + +// When running with Opal in SMT configurations, this indicates the number of threads per physical processor +g_NUM_SMT_THREADS: 1 + + +// Maximum number of requests (including SW prefetches) outstanding from +// the sequencer (Note: this also include items buffered in the store +// buffer) +g_SEQUENCER_OUTSTANDING_REQUESTS: 16 + + +PROTOCOL_DEBUG_TRACE: true +DEBUG_FILTER_STRING: none +DEBUG_VERBOSITY_STRING: none +DEBUG_START_TIME: 0 +DEBUG_OUTPUT_FILENAME: none + + +TRANSACTION_TRACE_ENABLED: false +USER_MODE_DATA_ONLY: false +PROFILE_HOT_LINES: false + +PROFILE_ALL_INSTRUCTIONS: false +PRINT_INSTRUCTION_TRACE: false +g_DEBUG_CYCLE: 0 +BLOCK_STC: false +PERFECT_MEMORY_SYSTEM: false +PERFECT_MEMORY_SYSTEM_LATENCY: 0 +DATA_BLOCK: false + + +// ********************************************* +// CACHE & MEMORY PARAMETERS +// ********************************************* + + +L1_CACHE_ASSOC: 4 +L1_CACHE_NUM_SETS_BITS: 8 +L2_CACHE_ASSOC: 4 +L2_CACHE_NUM_SETS_BITS: 16 + +// 32 bits = 4 GB address space +g_MEMORY_SIZE_BYTES: 4294967296 +g_DATA_BLOCK_BYTES: 64 +g_PAGE_SIZE_BYTES: 4096 +g_REPLACEMENT_POLICY: PSEDUO_LRU // currently, only other option is LRU + +g_PROCS_PER_CHIP: 1 + + +// set automatically +g_NUM_PROCESSORS: 0 +g_NUM_L2_BANKS: 0 +g_NUM_MEMORIES: 0 + +// The following group of parameters are calculated. They must +// _always_ be left at zero. +g_NUM_CHIPS: 0 +g_NUM_CHIP_BITS: 0 +g_MEMORY_SIZE_BITS: 0 +g_DATA_BLOCK_BITS: 0 +g_PAGE_SIZE_BITS: 0 +g_NUM_PROCESSORS_BITS: 0 +g_PROCS_PER_CHIP_BITS: 0 +g_NUM_L2_BANKS_BITS: 0 +g_NUM_L2_BANKS_PER_CHIP: 0 +g_NUM_L2_BANKS_PER_CHIP_BITS: 0 +g_NUM_MEMORIES_BITS: 0 +g_NUM_MEMORIES_PER_CHIP: 0 +g_MEMORY_MODULE_BITS: 0 +g_MEMORY_MODULE_BLOCKS: 0 + + +// For certain CMP protocols, determines whether the lowest bits of a block address +// are used to index to a L2 cache bank or into the sets of a +// single bank +// lowest highest +// true: g_DATA_BLOCK_BITS | g_NUM_L2_BANKS_PER_CHIP_BITS | L2_CACHE_NUM_SETS_BITS +// false: g_DATA_BLOCK_BITS | L2_CACHE_NUM_SETS_BITS | g_NUM_L2_BANKS_PER_CHIP_BITS +MAP_L2BANKS_TO_LOWEST_BITS: false + + + +// TIMING PARAMETERS -- many of these are protocol specific. See SLICC files +// to determine where they apply + +MEMORY_RESPONSE_LATENCY_MINUS_2: 158 // determines memory response latency +DIRECTORY_CACHE_LATENCY: 6 +NULL_LATENCY: 1 +ISSUE_LATENCY: 2 +CACHE_RESPONSE_LATENCY: 12 +L1_RESPONSE_LATENCY: 3 +L2_RESPONSE_LATENCY: 6 +L2_TAG_LATENCY: 6 +DIRECTORY_LATENCY: 80 +NETWORK_LINK_LATENCY: 1 +COPY_HEAD_LATENCY: 4 +ON_CHIP_LINK_LATENCY: 1 +RECYCLE_LATENCY: 10 +L2_RECYCLE_LATENCY: 5 +TIMER_LATENCY: 10000 +TBE_RESPONSE_LATENCY: 1 +PERIODIC_TIMER_WAKEUPS: true + + +// constants used by CMP protocols +// cache bank access times +L1_REQUEST_LATENCY: 2 +L2_REQUEST_LATENCY: 4 + + + + +// Number of transitions each controller state machines can complete per cycle +// i.e. the number of ports to each controller +// L1cache is the sum of the L1I and L1D cache ports +L1CACHE_TRANSITIONS_PER_RUBY_CYCLE: 32 +// Note: if SINGLE_ACCESS_L2_BANKS is enabled, this will probably enforce a +// much greater constraint on the concurrency of a L2 cache bank +L2CACHE_TRANSITIONS_PER_RUBY_CYCLE: 32 +DIRECTORY_TRANSITIONS_PER_RUBY_CYCLE: 32 + + +// Number of TBEs available for demand misses, ALL prefetches, and replacements +// used by one-level protocols +NUMBER_OF_TBES: 128 +// two-level protocols +NUMBER_OF_L1_TBES: 32 +NUMBER_OF_L2_TBES: 32 + +// TSO is deprecated +TSO: false + + +// ** INTERCONECT PARAMETERS ** +// +g_PRINT_TOPOLOGY: true +g_NETWORK_TOPOLOGY: HIERARCHICAL_SWITCH +g_CACHE_DESIGN: NUCA // specifies file prefix for FILE_SPECIFIED topology +FAN_OUT_DEGREE: 4 // for HIERARCHICAL SWITCH topology + +g_adaptive_routing: true +NUMBER_OF_VIRTUAL_NETWORKS: 4 + +// bandwidth unit is 1/1000 byte per cycle. the following parameter is multiplied by +// topology specific link weights +g_endpoint_bandwidth: 10000 + + +// ** finite buffering parameters +// +// note: Finite buffering allows us to simulate a realistic virtual cut-through +// routed network with idealized flow control. this feature is NOT heavily tested +FINITE_BUFFERING: false +// All message buffers within the network (i.e. the switch's input and +// output buffers) are set to the size specified below by the FINITE_BUFFER_SIZE +FINITE_BUFFER_SIZE: 3 +// g_SEQUENCER_OUTSTANDING_REQUESTS (above) controlls the number of demand requests +// issued by the sequencer. The PROCESSOR_BUFFER_SIZE controlls the +// number of requests in the mandatory queue +// Only effects the simualtion when FINITE_BUFFERING is enabled +PROCESSOR_BUFFER_SIZE: 10 +// The PROTOCOL_BUFFER_SIZE limits the size of all other buffers connecting to +// Controllers. Controlls the number of request issued by the L2 HW Prefetcher +PROTOCOL_BUFFER_SIZE: 32 +// ** end finite buffering parameters + + +// (deprecated) +// Allows on a single accesses to a multi-cycle L2 bank. +// Ensures the cache array is only accessed once for every L2_REQUEST_LATENCY +// number of cycles. However the TBE table can be accessed in parallel. +SINGLE_ACCESS_L2_BANKS: true + + +// constants used by TM protocols +PROFILE_EXCEPTIONS: false +PROFILE_XACT: true +PROFILE_NONXACT: false +XACT_DEBUG: true +XACT_DEBUG_LEVEL: 1 +//XACT_MEMORY: true // set to true for TM protocols. set it HERE for lazy systems to register the proper SIMICS interfaces +XACT_MEMORY: false +XACT_ENABLE_TOURMALINE: false // perfect memory system +XACT_NUM_CURRENT: 0 // must be 0 +XACT_LAST_UPDATE: 0 // must be 0 +XACT_ISOLATION_CHECK: false // Checks whether each memory access preserves transaction isolation +PERFECT_FILTER: true // If true, use perfect physical read/write filters +READ_WRITE_FILTER: Perfect_ +PERFECT_VIRTUAL_FILTER: true // If true, use perfect virtual read/write filters +VIRTUAL_READ_WRITE_FILTER: Perfect_ +PERFECT_SUMMARY_FILTER: true // If true, use perfect summary read/write filters +SUMMARY_READ_WRITE_FILTER: Perfect_ +XACT_EAGER_CD: true +XACT_LAZY_VM: false +XACT_CONFLICT_RES: BASE +XACT_COMMIT_TOKEN_LATENCY: 0 +XACT_VISUALIZER: false +XACT_NO_BACKOFF: false +XACT_LOG_BUFFER_SIZE: 0 +XACT_STORE_PREDICTOR_ENTRIES: 256 +XACT_STORE_PREDICTOR_HISTORY: 256 +XACT_STORE_PREDICTOR_THRESHOLD: 4 +XACT_FIRST_ACCESS_COST: 0 +XACT_FIRST_PAGE_ACCESS_COST: 0 +ENABLE_MAGIC_WAITING: false +ENABLE_WATCHPOINT: false +XACT_ENABLE_VIRTUALIZATION_LOGTM_SE: false +// g_NETWORK_TOPOLOGY: FILE_SPECIFIED +// NUMBER_OF_VIRTUAL_NETWORKS: 5 +// L2_REQUEST_LATENCY: 15 +// SEQUENCER_TO_CONTROLLER_LATENCY: 3 +// L2_RESPONSE_LATENCY: 20 +// L2_TAG_LATENCY: 6 +// MEMORY_RESPONSE_LATENCY_MINUS_2: 448 +// RECYCLE_LATENCY: 1 +// g_MEMORY_SIZE_BYTES: 268435456 +// REMOVE_SINGLE_CYCLE_DCACHE_FAST_PATH: true + +// ATMTP +ATMTP_ENABLED: false +ATMTP_ABORT_ON_NON_XACT_INST: false +ATMTP_ALLOW_SAVE_RESTORE_IN_XACT: false +ATMTP_XACT_MAX_STORES: 32 +ATMTP_DEBUG_LEVEL: 0 + +// MOESI_CMP_token parameters (some might be deprecated) +g_FILTERING_ENABLED: false +g_DISTRIBUTED_PERSISTENT_ENABLED: true +g_RETRY_THRESHOLD: 1 +g_DYNAMIC_TIMEOUT_ENABLED: true +g_FIXED_TIMEOUT_LATENCY: 300 + + +// tester parameters (overridden by testerconfig.defaults) +// +// injects random message delays to excite protocol races +RANDOMIZATION: false +g_SYNTHETIC_DRIVER: false +g_DETERMINISTIC_DRIVER: false +g_trace_warmup_length: 1000000 +g_bash_bandwidth_adaptive_threshold: 0.75 + +g_tester_length: 0 +// # of synthetic locks == 16 * 128 +g_synthetic_locks: 2048 +g_deterministic_addrs: 1 +g_SpecifiedGenerator: DetermInvGenerator +g_callback_counter: 0 +g_NUM_COMPLETIONS_BEFORE_PASS: 0 +// parameters used by locking synthetic tester +g_think_time: 5 +g_hold_time: 5 +g_wait_time: 5 + +// Princeton Network (Garnet) +g_GARNET_NETWORK: false +g_DETAIL_NETWORK: false +g_NETWORK_TESTING: false +g_FLIT_SIZE: 16 +g_NUM_PIPE_STAGES: 4 +g_VCS_PER_CLASS: 4 +g_BUFFER_SIZE: 4 + +/////////////////////////////////////////////////////////////////////////////// +// +// MemoryControl: + +// Basic cycle time of the memory controller. This defines the period which is +// used as the memory channel clock period, the address bus bit time, and the +// memory controller cycle time. +// Assuming a 200 MHz memory channel (DDR-400, which has 400 bits/sec data), +// and a 2 GHz Ruby clock: +MEM_BUS_CYCLE_MULTIPLIER: 10 + +// How many internal banks in each DRAM chip: +BANKS_PER_RANK: 8 + +// How many sets of DRAM chips per DIMM. +RANKS_PER_DIMM: 2 + +// How many DIMMs per channel. (Currently the only thing that +// matters is the number of ranks per channel, i.e. the product +// of this parameter and RANKS_PER_DIMM. But if and when this is +// expanded to do FB-DIMMs, the distinction between the two +// will matter.) +DIMMS_PER_CHANNEL: 2 + +// Which bits to use to find the bank, rank, and DIMM numbers. +// You could choose to have the bank bits, rank bits, and DIMM bits +// in any order; here they are in that order. +// For these defaults, we assume this format for addresses: +// Offset within line: [5:0] +// Memory controller #: [7:6] +// Bank: [10:8] +// Rank: [11] +// DIMM: [12] +// Row addr / Col addr: [top:13] +// If you get these bits wrong, then some banks won't see any +// requests; you need to check for this in the .stats output. +BANK_BIT_0: 8 +RANK_BIT_0: 11 +DIMM_BIT_0: 12 + +// Number of entries max in each bank queues; set to whatever you want. +// If it is too small, you will see in the .stats file a lot of delay +// time spent in the common input queue. +BANK_QUEUE_SIZE: 12 + +// Bank cycle time (tRC) measured in memory cycles: +BANK_BUSY_TIME: 11 + +// This is how many memory address cycles to delay between reads to +// different ranks of DRAMs to allow for clock skew: +RANK_RANK_DELAY: 1 + +// This is how many memory address cycles to delay between a read +// and a write. This is based on two things: (1) the data bus is +// used one cycle earlier in the operation; (2) a round-trip wire +// delay from the controller to the DIMM that did the reading. +READ_WRITE_DELAY: 2 + +// Basic address and data bus occupancy. If you are assuming a +// 16-byte-wide data bus (pairs of DIMMs side-by-side), then +// the data bus occupancy matches the address bus occupancy at +// two cycles. But if the channel is only 8 bytes wide, you +// need to increase this bus occupancy time to 4 cycles. +BASIC_BUS_BUSY_TIME: 2 + +// Latency to returning read request or writeback acknowledgement. +// Measured in memory address cycles. +// This equals tRCD + CL + AL + (four bit times) +// + (round trip on channel) +// + (memory control internal delays) +// It's going to be an approximation, so pick what you like. +// Note: The fact that latency is a constant, and does not depend on two +// low-order address bits, implies that our memory controller either: +// (a) tells the DRAM to read the critical word first, and sends the +// critical word first back to the CPU, or (b) waits until it has +// seen all four bit times on the data wires before sending anything +// back. Either is plausible. If (a), remove the "four bit times" +// term from the calculation above. +MEM_CTL_LATENCY: 12 + +// refresh_period is the number of memory cycles between refresh +// of row x in bank n and refresh of row x+1 in bank n. For DDR-400, +// this is typically 7.8 usec for commercial systems; after 8192 such +// refreshes, this will have refreshed the whole chip in 64 msec. If +// we have a 5 nsec memory clock, 7800 / 5 = 1560 cycles. The memory +// controller will divide this by the total number of banks, and kick +// off a refresh to *somebody* every time that amount is counted +// down to zero. (There will be some rounding error there, but it +// should have minimal effect.) +REFRESH_PERIOD: 1560 + +// tFAW is a DRAM chip parameter which restricts the number of +// activates that can be done within a certain window of time. +// The window is specified here in terms of number of memory +// controller cycles. At most four activates may be done during +// any such sliding window. If this number is set to be no more +// than 4 * BASIC_BUS_BUSY_TIME, it will have no effect. +// It is typical in real systems for tFAW to have no effect, but +// it may be useful in throttling power. Set to zero to ignore. +TFAW: 0 + +// By default, the memory controller uses round-robin to arbitrate +// between ready bank queues for use of the address bus. If you +// wish to add randomness to the system, set this parameter to +// one instead, and it will restart the round-robin pointer at a +// random bank number each cycle. If you want additional +// nondeterminism, set the parameter to some integer n >= 2, and +// it will in addition add a n% chance each cycle that a ready bank +// will be delayed an additional cycle. Note that if you are +// in MEM_FIXED_DELAY mode (see below), MEM_RANDOM_ARBITRATE=1 will +// have no effect, but MEM_RANDOM_ARBITRATE=2 or more will. +MEM_RANDOM_ARBITRATE: 0 + +// The following parameter, if nonzero, will disable the memory +// controller and instead give every request a fixed latency. The +// nonzero value specified here is measured in memory cycles and is +// just added to MEM_CTL_LATENCY. It will also show up in the stats +// file as a contributor to memory_delays_stalled_at_head_of_bank_queue. +MEM_FIXED_DELAY: 0 + +// If instead of DDR-400, you wanted DDR-800, the channel gets faster +// but the basic operation of the DRAM core is unchanged. +// Busy times appear to double just because they are measured +// in smaller clock cycles. The performance advantage comes because +// the bus busy times don't actually quite double. +// You would use something like these values: +// +// MEM_BUS_CYCLE_MULTIPLIER: 5 +// BANK_BUSY_TIME: 22 +// RANK_RANK_DELAY: 2 +// READ_WRITE_DELAY: 3 +// BASIC_BUS_BUSY_TIME: 3 +// MEM_CTL_LATENCY: 20 +// REFRESH_PERIOD: 3120 diff --git a/src/mem/ruby/config/tester.defaults b/src/mem/ruby/config/tester.defaults new file mode 100644 index 000000000..ea83a1443 --- /dev/null +++ b/src/mem/ruby/config/tester.defaults @@ -0,0 +1,60 @@ + +// +// This file contains tester specific changes to the rubyconfig.defaults +// parameter values. +// +// Please: - Add new variables only to rubyconfig.defaults file. +// - Change them here only when necessary. + +g_SIMICS: false +DATA_BLOCK: true +RANDOMIZATION: true +g_SYNTHETIC_DRIVER: true +g_DETERMINISTIC_DRIVER: false +g_DEADLOCK_THRESHOLD: 500000 +g_SpecifiedGenerator: DetermGETXGenerator + +PROTOCOL_DEBUG_TRACE: true + +// +// Generic cache parameters +// + +// Cache sizes are smaller for the random tester to increase the amount +// of false sharing. +L1_CACHE_ASSOC: 2 +L1_CACHE_NUM_SETS_BITS: 2 +L2_CACHE_ASSOC: 2 +L2_CACHE_NUM_SETS_BITS: 5 + +g_MEMORY_SIZE_BYTES: 1048576 + +// XACT MEMORY +XACT_LENGTH: 2000 +XACT_SIZE: 1000 +ABORT_RETRY_TIME: 400 +XACT_ISOLATION_CHECK: true +L2CACHE_TRANSITIONS_PER_RUBY_CYCLE: 1000 +DIRECTORY_TRANSITIONS_PER_RUBY_CYCLE: 1000 +PERFECT_FILTER: true // If true, use perfect read/write filters +READ_WRITE_FILTER: Perfect_ + +//g_NETWORK_TOPOLOGY: FILE_SPECIFIED +RECYCLE_LATENCY: 1 +//NUMBER_OF_VIRTUAL_NETWORKS: 5 +//g_NUM_MEMORIES: 16 +L2CACHE_TRANSITIONS_PER_RUBY_CYCLE: 1000 +DIRECTORY_TRANSITIONS_PER_RUBY_CYCLE: 1000 +//g_PROCS_PER_CHIP: 16 +//g_NUM_L2_BANKS: 16 +//g_endpoint_bandwidth: 10000 +//g_NUM_PROCESSORS: 16 +//g_NUM_SMT_THREADS: 1 +//g_GARNET_NETWORK: true +//g_DETAIL_NETWORK: true +//g_NETWORK_TESTING: false +//g_FLIT_SIZE: 32 +//g_NUM_PIPE_STAGES: 5 +//g_VCS_PER_CLASS: 2 +//g_BUFFER_SIZE: 4 + diff --git a/src/mem/ruby/eventqueue/EventQueue.cc b/src/mem/ruby/eventqueue/EventQueue.cc new file mode 100644 index 000000000..0eef53530 --- /dev/null +++ b/src/mem/ruby/eventqueue/EventQueue.cc @@ -0,0 +1,120 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + */ + +#include "EventQueue.hh" +#include "RubyConfig.hh" +#include "Consumer.hh" +#include "Profiler.hh" +#include "System.hh" +#include "PrioHeap.hh" +#include "EventQueueNode.hh" + +// Class public method definitions + +EventQueue::EventQueue() +{ + m_prio_heap_ptr = NULL; + init(); +} + +EventQueue::~EventQueue() +{ + delete m_prio_heap_ptr; +} + +void EventQueue::init() +{ + m_globalTime = 1; + m_timeOfLastRecovery = 1; + m_prio_heap_ptr = new PrioHeap; + m_prio_heap_ptr->init(); +} + +bool EventQueue::isEmpty() const +{ + return (m_prio_heap_ptr->size() == 0); +} + +void EventQueue::scheduleEventAbsolute(Consumer* consumer, Time timeAbs) +{ + // Check to see if this is a redundant wakeup + // Time time = timeDelta + m_globalTime; + ASSERT(consumer != NULL); + if (consumer->getLastScheduledWakeup() != timeAbs) { + // This wakeup is not redundant + EventQueueNode thisNode; + thisNode.m_consumer_ptr = consumer; + assert(timeAbs > m_globalTime); + thisNode.m_time = timeAbs; + m_prio_heap_ptr->insert(thisNode); + consumer->setLastScheduledWakeup(timeAbs); + } +} + +void EventQueue::triggerEvents(Time t) +{ + EventQueueNode thisNode; + + while(m_prio_heap_ptr->size() > 0 && m_prio_heap_ptr->peekMin().m_time <= t) { + m_globalTime = m_prio_heap_ptr->peekMin().m_time; + thisNode = m_prio_heap_ptr->extractMin(); + assert(thisNode.m_consumer_ptr != NULL); + DEBUG_EXPR(EVENTQUEUE_COMP,MedPrio,*(thisNode.m_consumer_ptr)); + DEBUG_EXPR(EVENTQUEUE_COMP,MedPrio,thisNode.m_time); + thisNode.m_consumer_ptr->triggerWakeup(); + } + m_globalTime = t; +} + +void EventQueue::triggerAllEvents() +{ + // FIXME - avoid repeated code + EventQueueNode thisNode; + + while(m_prio_heap_ptr->size() > 0) { + m_globalTime = m_prio_heap_ptr->peekMin().m_time; + thisNode = m_prio_heap_ptr->extractMin(); + assert(thisNode.m_consumer_ptr != NULL); + DEBUG_EXPR(EVENTQUEUE_COMP,MedPrio,*(thisNode.m_consumer_ptr)); + DEBUG_EXPR(EVENTQUEUE_COMP,MedPrio,thisNode.m_time); + thisNode.m_consumer_ptr->triggerWakeup(); + } +} + +// Class private method definitions + +void +EventQueue::print(ostream& out) const +{ + out << "[Event Queue: " << *m_prio_heap_ptr << "]"; +} diff --git a/src/mem/ruby/eventqueue/EventQueue.hh b/src/mem/ruby/eventqueue/EventQueue.hh new file mode 100644 index 000000000..476e0d24a --- /dev/null +++ b/src/mem/ruby/eventqueue/EventQueue.hh @@ -0,0 +1,118 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + * Description: The EventQueue class implements an event queue which + * can be trigger events, allowing our simulation to be event driven. + * + * Currently, the only event we support is a Consumer being signaled + * by calling the consumer's wakeup() routine. Adding the event to + * the queue does not require a virtual function call, though calling + * wakeup() is a virtual function call. + * + * The method triggerEvents() is called with a global time. All + * events which are before or at this time are triggered in timestamp + * order. No ordering is enforced for events scheduled to occur at + * the same time. Events scheduled to wakeup the same consumer at the + * same time are combined into a single event. + * + * The method scheduleConsumerWakeup() is called with a global time + * and a consumer pointer. The event queue will call the wakeup() + * method of the consumer at the appropriate time. + * + * This implementation of EventQueue uses a dynamically sized array + * managed as a heap. The algorithms used has O(lg n) for insert and + * O(lg n) for extract minimum element. (Based on chapter 7 of Cormen, + * Leiserson, and Rivest.) The array is dynamically sized and is + * automatically doubled in size when necessary. + * + */ + +#ifndef EVENTQUEUE_H +#define EVENTQUEUE_H + +#include "Global.hh" +#include "Vector.hh" + +class Consumer; +template class PrioHeap; +class EventQueueNode; + +class EventQueue { +public: + // Constructors + EventQueue(); + + // Destructor + ~EventQueue(); + + // Public Methods + + Time getTime() const { return m_globalTime; } + void scheduleEvent(Consumer* consumer, Time timeDelta) { scheduleEventAbsolute(consumer, timeDelta + m_globalTime); } + void scheduleEventAbsolute(Consumer* consumer, Time timeAbs); + void triggerEvents(Time t); // called to handle all events <= time t + void triggerAllEvents(); + void print(ostream& out) const; + bool isEmpty() const; + + Time getTimeOfLastRecovery() {return m_timeOfLastRecovery;} + void setTimeOfLastRecovery(Time t) {m_timeOfLastRecovery = t;} + + // Private Methods +private: + // Private copy constructor and assignment operator + void init(); + EventQueue(const EventQueue& obj); + EventQueue& operator=(const EventQueue& obj); + + // Data Members (m_ prefix) + PrioHeap* m_prio_heap_ptr; + Time m_globalTime; + Time m_timeOfLastRecovery; +}; + +// Output operator declaration +inline extern +ostream& operator<<(ostream& out, const EventQueue& obj); + +// ******************* Definitions ******************* + +// Output operator definition +inline extern +ostream& operator<<(ostream& out, const EventQueue& obj) +{ + obj.print(out); + out << flush; + return out; +} + +#endif //EVENTQUEUE_H diff --git a/src/mem/ruby/eventqueue/EventQueueNode.cc b/src/mem/ruby/eventqueue/EventQueueNode.cc new file mode 100644 index 000000000..b0027506b --- /dev/null +++ b/src/mem/ruby/eventqueue/EventQueueNode.cc @@ -0,0 +1,47 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +#include "EventQueueNode.hh" + +void EventQueueNode::print(ostream& out) const +{ + out << "["; + out << "Time=" << m_time; + if (m_consumer_ptr != NULL) { + out << " Consumer=" << m_consumer_ptr; + } else { + out << " Consumer=NULL"; + } + out << "]"; +} diff --git a/src/mem/ruby/eventqueue/EventQueueNode.hh b/src/mem/ruby/eventqueue/EventQueueNode.hh new file mode 100644 index 000000000..eff7ff37e --- /dev/null +++ b/src/mem/ruby/eventqueue/EventQueueNode.hh @@ -0,0 +1,98 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +#ifndef EVENTQUEUENODE_H +#define EVENTQUEUENODE_H + +#include "Global.hh" +class Consumer; + +class EventQueueNode { +public: + // Constructors + EventQueueNode() { m_time = 0; m_consumer_ptr = NULL; } + + // Destructor + //~EventQueueNode(); + + // Public Methods + void print(ostream& out) const; + + // Assignment operator and copy constructor since the default + // constructors confuse purify when long longs are present. + EventQueueNode& operator=(const EventQueueNode& obj) { + m_time = obj.m_time; + m_consumer_ptr = obj.m_consumer_ptr; + return *this; + } + + EventQueueNode(const EventQueueNode& obj) { + m_time = obj.m_time; + m_consumer_ptr = obj.m_consumer_ptr; + } +private: + // Private Methods + + // Default copy constructor and assignment operator + // EventQueueNode(const EventQueueNode& obj); + + // Data Members (m_ prefix) +public: + Time m_time; + Consumer* m_consumer_ptr; +}; + +// Output operator declaration +ostream& operator<<(ostream& out, const EventQueueNode& obj); + +// ******************* Definitions ******************* + +inline extern bool node_less_then_eq(const EventQueueNode& n1, const EventQueueNode& n2); + +inline extern +bool node_less_then_eq(const EventQueueNode& n1, const EventQueueNode& n2) +{ + return (n1.m_time <= n2.m_time); +} + +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const EventQueueNode& obj) +{ + obj.print(out); + out << flush; + return out; +} + +#endif //EVENTQUEUENODE_H diff --git a/src/mem/ruby/eventqueue/EventQueue_tester.cc b/src/mem/ruby/eventqueue/EventQueue_tester.cc new file mode 100644 index 000000000..5e54aa7e0 --- /dev/null +++ b/src/mem/ruby/eventqueue/EventQueue_tester.cc @@ -0,0 +1,89 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +#include "EventQueue.hh" +#include "Consumer.hh" + +//static int global_counter = 0; + +class TestConsumer1 : public Consumer { +public: + TestConsumer1(int description) { m_description = description; } + ~TestConsumer1() { } + void wakeup() { cout << "Wakeup#1: " << m_description << endl; } + // void wakeup() { global_counter++; } + void print(ostream& out) const { out << "1:" << m_description << endl; } + +private: + int m_description; +}; + +class TestConsumer2 : public Consumer { +public: + TestConsumer2(int description) { m_description = description; } + ~TestConsumer2() { } + void wakeup() { cout << "Wakeup#2: " << m_description << endl; } + // void wakeup() { global_counter++; } + void print(ostream& out) const { out << "2:" << m_description << endl; } +private: + int m_description; +}; + +int main() +{ + EventQueue q; + const int SIZE = 200; + const int MAX_TIME = 10000; + int numbers[SIZE]; + Consumer* consumers[SIZE]; + + for (int i=0; i +#include +#include + +// Maurice +// extern "C" { +// #include "simics/api.hh" +// }; + +#include "FakeSimicsDataTypes.hh" + +#include "confio.hh" +#include "initvar.hh" + +// A generated file containing the default parameters in string form +// The defaults are stored in the variable global_default_param +#include "default_param.hh" + +attr_value_t ruby_session_get( void *id, void *obj, + attr_value_t *idx ) { + attr_value_t ret; + + // all session attributes default to return invalid + ret.kind = Sim_Val_Invalid; + return ret; +} + +set_error_t ruby_session_set( void *id, void *obj, + attr_value_t *val, attr_value_t *idx ) { + const char *command = (const char *) id; + // Add new ruby commands to this function + +#if 0 // Eventually add these commands back in + if (!strcmp(command, "dump-stats" ) ) { + char* filename = (char*) val->u.string; + if(strcmp(filename, "")){ + ruby_dump_stats(filename); + } else { + ruby_dump_stats(NULL); + } + return Sim_Set_Ok; + } else if (!strcmp(command, "dump-short-stats" ) ) { + char* filename = (char*) val->u.string; + if(strcmp(filename, "")){ + ruby_dump_short_stats(filename); + } else { + ruby_dump_short_stats(NULL); + } + return Sim_Set_Ok; + } else if (!strcmp(command, "periodic-stats-file" ) ) { + char* filename = (char*) val->u.string; + ruby_set_periodic_stats_file(filename); + return Sim_Set_Ok; + } else if (!strcmp(command, "periodic-stats-interval" ) ) { + int interval = val->u.integer; + ruby_set_periodic_stats_interval(interval); + return Sim_Set_Ok; + } else if (!strcmp(command, "clear-stats" ) ) { + ruby_clear_stats(); + return Sim_Set_Ok; + } else if (!strcmp(command, "debug-verb" ) ) { + char* new_verbosity = (char*) val->u.string; + ruby_change_debug_verbosity(new_verbosity); + return Sim_Set_Ok; + } else if (!strcmp(command, "debug-filter" ) ) { + char* new_debug_filter = (char*) val->u.string; + ruby_change_debug_filter(new_debug_filter); + return Sim_Set_Ok; + } else if (!strcmp(command, "debug-output-file" ) ) { + char* new_filename = (char*) val->u.string; + ruby_set_debug_output_file(new_filename); + return Sim_Set_Ok; + } else if (!strcmp(command, "debug-start-time" ) ) { + char* new_start_time = (char*) val->u.string; + ruby_set_debug_start_time(new_start_time); + return Sim_Set_Ok; + } else if (!strcmp(command, "load-caches" ) ) { + char* filename = (char*) val->u.string; + ruby_load_caches(filename); + return Sim_Set_Ok; + } else if (!strcmp(command, "save-caches" ) ) { + char* filename = (char*) val->u.string; + ruby_save_caches(filename); + return Sim_Set_Ok; + } else if (!strcmp(command, "dump-cache" ) ) { + int cpuNumber = val->u.integer; + ruby_dump_cache(cpuNumber); + return Sim_Set_Ok; + } else if (!strcmp(command, "dump-cache-data" ) ) { + int cpuNumber = val->u.list.vector[0].u.integer; + char *filename = (char*) val->u.list.vector[1].u.string; + ruby_dump_cache_data( cpuNumber, filename ); + return Sim_Set_Ok; + } else if (!strcmp(command, "tracer-output-file" ) ) { + char* new_filename = (char*) val->u.string; + ruby_set_tracer_output_file(new_filename); + return Sim_Set_Ok; + } else if (!strcmp(command, "xact-visualizer-file" ) ) { + char* new_filename = (char*) val->u.string; + ruby_xact_visualizer_file(new_filename); + return Sim_Set_Ok; + } + fprintf( stderr, "error: unrecognized command: %s\n", command ); +#endif + return Sim_Set_Illegal_Value; +} + +static initvar_t *ruby_initvar_obj = NULL; + +//*************************************************************************** +static void init_generate_values( void ) +{ + /* update generated values, based on input configuration */ +} + +//*************************************************************************** +void init_variables( void ) +{ + // allocate the "variable initialization" package + ruby_initvar_obj = new initvar_t( "ruby", "../../../ruby/", + global_default_param, + &init_simulator, + &init_generate_values, + &ruby_session_get, + &ruby_session_set ); +} + +void init_simulator() +{ + // Set things to NULL to make sure we don't de-reference them + // without a seg. fault. + g_system_ptr = NULL; + g_debug_ptr = NULL; + g_eventQueue_ptr = NULL; + + cout << "Ruby Timing Mode" << endl; +#ifndef MULTIFACET_NO_OPT_WARN + cerr << "Warning: optimizations not enabled." << endl; +#endif + + if (g_SIMICS) { + // LUKE - if we don't set the default SMT threads in condor scripts, + // set it now + if(g_NUM_SMT_THREADS == 0){ + g_NUM_SMT_THREADS = 1; + } + if(g_NUM_PROCESSORS == 0){ + //only set to default if value not set in condor scripts + // Account for SMT systems also + g_NUM_PROCESSORS = SIMICS_number_processors()/g_NUM_SMT_THREADS; + } + } + + RubyConfig::init(); + + g_debug_ptr = new Debug( DEBUG_FILTER_STRING, + DEBUG_VERBOSITY_STRING, + DEBUG_START_TIME, + DEBUG_OUTPUT_FILENAME ); + + cout << "Creating event queue..." << endl; + g_eventQueue_ptr = new EventQueue; + cout << "Creating event queue done" << endl; + + cout << "Creating system..." << endl; + cout << " Processors: " << RubyConfig::numberOfProcessors() << endl; + + g_system_ptr = new System; + cout << "Creating system done" << endl; + + // if opal is loaded, its static interface object (inst) will be non-null, + // and the opal object needs to be notified that ruby is now loaded. + // "1" indicates a load and should be replaced with an enumerated type. + if (OpalInterface::inst != NULL) { + OpalInterface::inst->notify( 1 ); + } + +#ifdef CONTIGUOUS_ADDRESSES + if(g_SIMICS) { + cout << "Establishing Contiguous Address Space Mappings..." << flush; + g_p_ca_translator = new ContiguousAddressTranslator(); + assert(g_p_ca_translator!=NULL); + if(g_p_ca_translator->AddressesAreContiguous()) { + cout << "Physical Memory Addresses are already contiguous." << endl; + delete g_p_ca_translator; + g_p_ca_translator = NULL; + } else { + cout << "Done." << endl; + } + } else { + g_p_ca_translator = NULL; + } +#endif // #ifdef CONTIGUOUS_ADDRESSES + + cout << "Ruby initialization complete" << endl; +} + +void init_opal_interface( mf_ruby_api_t *api ) +{ + OpalInterface::installInterface( api ); +} + +int init_use_snoop() +{ + if (g_SIMICS) { + // The "snoop interface" defined by simics allows ruby to see store + // data values (from simics). If DATA_BLOCK is defined, we are tracking + // data, so we need to install the snoop interface. + return ((DATA_BLOCK == true) || (XACT_MEMORY)); + } else { + return (0); + } +} + +void destroy_simulator() +{ + cout << "Deleting system..." << endl; + delete g_system_ptr; + cout << "Deleting system done" << endl; + + cout << "Deleting event queue..." << endl; + delete g_eventQueue_ptr; + cout << "Deleting event queue done" << endl; + + delete g_debug_ptr; +} + +/*-------------------------------------------------------------------------+ + | DG: These are the external load and unload hooks that will be called by | + | M5 in phase 1 integration, and possibly afterwards, too. | + +-------------------------------------------------------------------------*/ + +extern "C" +int OnLoadRuby() { + init_variables(); + return 0; +} + +extern "C" +int OnInitRuby() { + init_simulator(); + return 0; +} + +extern "C" +int OnUnloadRuby() { + destroy_simulator(); + return 0; +} + diff --git a/src/mem/ruby/init.hh b/src/mem/ruby/init.hh new file mode 100644 index 000000000..36d975b3e --- /dev/null +++ b/src/mem/ruby/init.hh @@ -0,0 +1,56 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * init.h + * + * Description: + * + * $Id$ + * + */ + +#ifndef INIT_H +#define INIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern void init_variables(); +extern void init_simulator(); +extern void init_opal_interface( mf_ruby_api_t *api ); +extern int init_use_snoop(); +extern void destroy_simulator(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif //INIT_H diff --git a/src/mem/ruby/interfaces/OpalInterface.cc b/src/mem/ruby/interfaces/OpalInterface.cc new file mode 100644 index 000000000..362c7bcb6 --- /dev/null +++ b/src/mem/ruby/interfaces/OpalInterface.cc @@ -0,0 +1,446 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + */ + +/*------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------*/ + +#include "OpalInterface.hh" +#include "interface.hh" +#include "System.hh" +#include "SubBlock.hh" +#include "mf_api.hh" +#include "Chip.hh" +#include "RubyConfig.hh" +//#include "XactIsolationChecker.hh" //gem5:Arka for decomissioning ruby/log_tm +// #include "TransactionInterfaceManager.hh" +//#include "TransactionVersionManager.hh" //gem5:Arka for decomissioning ruby/log_tm +#include "Sequencer.hh" + +/*------------------------------------------------------------------------*/ +/* Forward declarations */ +/*------------------------------------------------------------------------*/ + +static CacheRequestType get_request_type( OpalMemop_t opaltype ); +static OpalMemop_t get_opal_request_type( CacheRequestType type ); + +/// The static opalinterface instance +OpalInterface *OpalInterface::inst = NULL; + +/*------------------------------------------------------------------------*/ +/* Constructor(s) / destructor */ +/*------------------------------------------------------------------------*/ + +//************************************************************************** +OpalInterface::OpalInterface(System* sys_ptr) { + clearStats(); + ASSERT( inst == NULL ); + inst = this; + m_opal_intf = NULL; +} + +/*------------------------------------------------------------------------*/ +/* Public methods */ +/*------------------------------------------------------------------------*/ + +//************************************************************************** +void OpalInterface::printConfig(ostream& out) const { + out << "Opal_ruby_multiplier: " << OPAL_RUBY_MULTIPLIER << endl; + out << endl; +} + +void OpalInterface::printStats(ostream& out) const { + out << endl; + out << "Opal Interface Stats" << endl; + out << "----------------------" << endl; + out << endl; +} + +//************************************************************************** +void OpalInterface::clearStats() { +} + +//************************************************************************** +integer_t OpalInterface::getInstructionCount(int procID) const { + return ((*m_opal_intf->getInstructionCount)(procID)); +} + +//************************************************************************* +uint64 OpalInterface::getOpalTime(int procID) const { + return ((*m_opal_intf->getOpalTime)(procID)); +} + +/************ For WATTCH power stats ************************************/ +//************************************************************************* +void OpalInterface::incrementL2Access(int procID) const{ + ((*m_opal_intf->incrementL2Access)(procID)); +} + +//************************************************************************* +void OpalInterface::incrementPrefetcherAccess(int procID, int num_prefetches, int isinstr) const{ + ((*m_opal_intf->incrementPrefetcherAccess)(procID, num_prefetches, isinstr)); +} +/******************** END WATTCH power stats ****************************/ + +// Notifies Opal of an L2 miss +//************************************************************************* +void OpalInterface::notifyL2Miss(int procID, physical_address_t physicalAddr, OpalMemop_t type, int tagexists) const{ + ((*m_opal_intf->notifyL2Miss)(procID, physicalAddr, type, tagexists)); +} + +/****************************************************************** + * void hitCallback(int cpuNumber) + * Called by Sequencer. Calls opal. + ******************************************************************/ + +//************************************************************************** +void OpalInterface::hitCallback(NodeID proc, SubBlock& data, CacheRequestType type, int thread) { + // notify opal that the transaction has completed + (*m_opal_intf->hitCallback)( proc, data.getAddress().getAddress(), get_opal_request_type(type), thread ); +} + +//************************************************************************** +// Useful functions if Ruby needs to read/write physical memory when running with Opal +integer_t OpalInterface::readPhysicalMemory(int procID, + physical_address_t address, + int len ){ + return SIMICS_read_physical_memory(procID, address, len); +} + +//************************************************************************** +void OpalInterface::writePhysicalMemory( int procID, + physical_address_t address, + integer_t value, + int len ){ + SIMICS_write_physical_memory(procID, address, value, len); +} + +//*************************************************************** +// notifies Opal to print debug info +void OpalInterface::printDebug(){ + (*m_opal_intf->printDebug)(); +} + +//*************************************************************** + +/****************************************************************** + * Called by opal's memory operations (memop.C) + * May call Sequencer. + ******************************************************************/ + +//**************************************************************************** +int OpalInterface::getNumberOutstanding( int cpuNumber ){ + Sequencer* targetSequencer_ptr = g_system_ptr->getChip(cpuNumber/RubyConfig::numberOfProcsPerChip())->getSequencer(cpuNumber%RubyConfig::numberOfProcsPerChip()); + + return targetSequencer_ptr->getNumberOutstanding(); +} + +//**************************************************************************** +int OpalInterface::getNumberOutstandingDemand( int cpuNumber ){ + Sequencer* targetSequencer_ptr = g_system_ptr->getChip(cpuNumber/RubyConfig::numberOfProcsPerChip())->getSequencer(cpuNumber%RubyConfig::numberOfProcsPerChip()); + + return targetSequencer_ptr->getNumberOutstandingDemand(); +} + +//**************************************************************************** +int OpalInterface::getNumberOutstandingPrefetch( int cpuNumber ){ + Sequencer* targetSequencer_ptr = g_system_ptr->getChip(cpuNumber/RubyConfig::numberOfProcsPerChip())->getSequencer(cpuNumber%RubyConfig::numberOfProcsPerChip()); + + return targetSequencer_ptr->getNumberOutstandingPrefetch(); +} + +//************************************************************************** +int OpalInterface::isReady( int cpuNumber, la_t logicalAddr, pa_t physicalAddr, OpalMemop_t typeOfRequest, int thread ) { + // Send request to sequencer + Sequencer* targetSequencer_ptr = g_system_ptr->getChip(cpuNumber/RubyConfig::numberOfProcsPerChip())->getSequencer(cpuNumber%RubyConfig::numberOfProcsPerChip()); + + // FIXME - some of these fields have bogus values sinced isReady() + // doesn't need them. However, it would be cleaner if all of these + // fields were exact. + + return (targetSequencer_ptr->isReady(CacheMsg(Address( physicalAddr ), + Address( physicalAddr ), + get_request_type(typeOfRequest), + Address(0), + AccessModeType_UserMode, // User/supervisor mode + 0, // Size in bytes of request + PrefetchBit_No, // Not a prefetch + 0, // Version number + Address(logicalAddr), // Virtual Address + thread, // SMT thread + 0, // TM specific - timestamp of memory request + false // TM specific - whether request is part of escape action + ) + )); +} + +// FIXME: duplicated code should be avoided +//************************************************************************** +void OpalInterface::makeRequest(int cpuNumber, la_t logicalAddr, pa_t physicalAddr, + int requestSize, OpalMemop_t typeOfRequest, + la_t virtualPC, int isPriv, int thread) { + // Issue the request to the sequencer. + // set access type (user/supervisor) + AccessModeType access_mode; + if (isPriv) { + access_mode = AccessModeType_SupervisorMode; + } else { + access_mode = AccessModeType_UserMode; + } + + // Send request to sequencer + Sequencer* targetSequencer_ptr = g_system_ptr->getChip(cpuNumber/RubyConfig::numberOfProcsPerChip())->getSequencer(cpuNumber%RubyConfig::numberOfProcsPerChip()); + + targetSequencer_ptr->makeRequest(CacheMsg(Address( physicalAddr ), + Address( physicalAddr ), + get_request_type(typeOfRequest), + Address(virtualPC), + access_mode, // User/supervisor mode + requestSize, // Size in bytes of request + PrefetchBit_No, // Not a prefetch + 0, // Version number + Address(logicalAddr), // Virtual Address + thread, // SMT thread + 0, // TM specific - timestamp of memory request + false // TM specific - whether request is part of escape action + ) + ); +} + + +//************************************************************************** +void OpalInterface::makePrefetch(int cpuNumber, la_t logicalAddr, pa_t physicalAddr, + int requestSize, OpalMemop_t typeOfRequest, + la_t virtualPC, int isPriv, int thread) { + DEBUG_MSG(SEQUENCER_COMP,MedPrio,"Making prefetch request"); + + // Issue the request to the sequencer. + // set access type (user/supervisor) + AccessModeType access_mode; + if (isPriv) { + access_mode = AccessModeType_SupervisorMode; + } else { + access_mode = AccessModeType_UserMode; + } + + // make the prefetch + Sequencer* targetSequencer_ptr = g_system_ptr->getChip(cpuNumber/RubyConfig::numberOfProcsPerChip())->getSequencer(cpuNumber%RubyConfig::numberOfProcsPerChip()); + targetSequencer_ptr->makeRequest(CacheMsg(Address( physicalAddr ), + Address( physicalAddr ), + get_request_type(typeOfRequest), + Address(virtualPC), + access_mode, + requestSize, + PrefetchBit_Yes, // True means this is a prefetch + 0, // Version number + Address(logicalAddr), // Virtual Address + thread, // SMT thread + 0, // TM specific - timestamp of memory request + false // TM specific - whether request is part of escape action + ) + ); + return; +} + +//************************************************************************** +int OpalInterface::staleDataRequest( int cpuNumber, pa_t physicalAddr, + int requestSize, int8 *buffer ) { + // Find sequencer + Sequencer* targetSequencer_ptr = g_system_ptr->getChip(cpuNumber/RubyConfig::numberOfProcsPerChip())->getSequencer(cpuNumber%RubyConfig::numberOfProcsPerChip()); + assert(targetSequencer_ptr != NULL); + + // query the cache for stale data values (if any) + bool hit = false; + //hit = targetSequencer_ptr->staleDataAccess( Address(physicalAddr), + // requestSize, buffer ); + + return hit; +} + +//************************************************************************** +void OpalInterface::notify( int status ) { + if (OpalInterface::inst == NULL) { + if (status == 1) { + // notify system that opal is now loaded + g_system_ptr->opalLoadNotify(); + } else { + return; + } + } + + // opal interface must be allocated now + ASSERT( OpalInterface::inst != NULL ); + if ( status == 0 ) { + + } else if ( status == 1 ) { + // install notification: query opal for its interface + OpalInterface::inst->queryOpalInterface(); + if ( OpalInterface::inst->m_opal_intf != NULL ) { + cout << "OpalInterface: installation successful." << endl; + // test: (*(OpalInterface::inst->m_opal_intf->hitCallback))( 0, 0xFFULL ); + } + } else if ( status == 2 ) { + // unload notification + // NOTE: this is not tested, as we can't unload ruby or opal right now. + OpalInterface::inst->removeOpalInterface(); + } +} + +// advance ruby time +//************************************************************************** +int OpalInterface::s_advance_counter = 0; + +void OpalInterface::advanceTime( void ) { + s_advance_counter++; + if (s_advance_counter == OPAL_RUBY_MULTIPLIER) { + Time time = g_eventQueue_ptr->getTime() + 1; + DEBUG_EXPR(NODE_COMP, HighPrio, time); + g_eventQueue_ptr->triggerEvents(time); + s_advance_counter = 0; + } +} + +// return ruby's time +//************************************************************************** +unsigned long long OpalInterface::getTime( void ) { + return (g_eventQueue_ptr->getTime()); +} + +// print's Ruby outstanding request table +void OpalInterface::printProgress(int cpuNumber){ + Sequencer* targetSequencer_ptr = g_system_ptr->getChip(cpuNumber/RubyConfig::numberOfProcsPerChip())->getSequencer(cpuNumber%RubyConfig::numberOfProcsPerChip()); + assert(targetSequencer_ptr != NULL); + + targetSequencer_ptr->printProgress(cout); +} + +// Non-method helper function +//************************************************************************** +static CacheRequestType get_request_type( OpalMemop_t opaltype ) { + CacheRequestType type; + + if (opaltype == OPAL_LOAD) { + type = CacheRequestType_LD; + } else if (opaltype == OPAL_STORE){ + type = CacheRequestType_ST; + } else if (opaltype == OPAL_IFETCH){ + type = CacheRequestType_IFETCH; + } else if (opaltype == OPAL_ATOMIC){ + type = CacheRequestType_ATOMIC; + } else { + ERROR_MSG("Error: Strange memory transaction type: not a LD or a ST"); + } + return type; +} + +//************************************************************************** +static OpalMemop_t get_opal_request_type( CacheRequestType type ) { + OpalMemop_t opal_type; + + if(type == CacheRequestType_LD){ + opal_type = OPAL_LOAD; + } + else if( type == CacheRequestType_ST){ + opal_type = OPAL_STORE; + } + else if( type == CacheRequestType_IFETCH){ + opal_type = OPAL_IFETCH; + } + else if( type == CacheRequestType_ATOMIC){ + opal_type = OPAL_ATOMIC; + } + else{ + ERROR_MSG("Error: Strange memory transaction type: not a LD or a ST"); + } + + //cout << "get_opal_request_type() CacheRequestType[ " << type << " ] opal_type[ " << opal_type << " ] " << endl; + return opal_type; +} + +//************************************************************************** +void OpalInterface::removeOpalInterface( void ) { + cout << "ruby: opal uninstalled. reinstalling timing model." << endl; + SIMICS_install_timing_model(); +} + +//************************************************************************** +bool OpalInterface::isOpalLoaded( void ) { + if (!g_SIMICS) { + return false; + } else { + mf_opal_api_t *opal_interface = SIMICS_get_opal_interface(); + if ( opal_interface == NULL ) { + return false; + } else { + return true; + } + } +} + +//************************************************************************** +void OpalInterface::queryOpalInterface( void ) { + m_opal_intf = SIMICS_get_opal_interface(); + if ( m_opal_intf == NULL ) { + WARN_MSG("error: OpalInterface: opal does not implement mf-opal-api interface.\n"); + } else { + // opal is loaded -- remove the timing_model interface + cout << "Ruby: ruby-opal link established. removing timing_model." << endl; + SIMICS_remove_timing_model(); + + if (m_opal_intf->notifyCallback != NULL) { + cout << "opalinterface: doing notify callback\n"; + (*m_opal_intf->notifyCallback)( 1 ); + } else { + // 2/27/2005, removed spurious error message (MRM) + // cout << "error: opalinterface: mf-opal-api has NULL notify callback.\n"; + } + } +} + +// install the opal interface to simics +//************************************************************************** +void OpalInterface::installInterface( mf_ruby_api_t *api ) { + // install ruby interface + api->isReady = &OpalInterface::isReady; + api->makeRequest = &OpalInterface::makeRequest; + api->makePrefetch = &OpalInterface::makePrefetch; + api->advanceTime = &OpalInterface::advanceTime; + api->getTime = &OpalInterface::getTime; + api->staleDataRequest = &OpalInterface::staleDataRequest; + api->notifyCallback = &OpalInterface::notify; + api->getNumberOutstanding = &OpalInterface::getNumberOutstanding; + api->getNumberOutstandingDemand = &OpalInterface::getNumberOutstandingDemand; + api->getNumberOutstandingPrefetch = &OpalInterface::getNumberOutstandingPrefetch; + api->printProgress = &OpalInterface::printProgress; +} diff --git a/src/mem/ruby/interfaces/OpalInterface.hh b/src/mem/ruby/interfaces/OpalInterface.hh new file mode 100644 index 000000000..4bc63d15a --- /dev/null +++ b/src/mem/ruby/interfaces/OpalInterface.hh @@ -0,0 +1,214 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * $Id$ + * + * Description: + * + */ + +#ifndef OpalInterface_H +#define OpalInterface_H + +/*------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------*/ + +#include "Global.hh" +#include "Driver.hh" +#include "mf_api.hh" +#include "CacheRequestType.hh" + +/*------------------------------------------------------------------------*/ +/* Class declaration(s) */ +/*------------------------------------------------------------------------*/ + +class System; +class TransactionInterfaceManager; +class Sequencer; + +/** + * the processor model (opal) calls these OpalInterface APIs to access + * the memory hierarchy (ruby). + * @see pseq_t + * @author cmauer + * @version $Id$ + */ +class OpalInterface : public Driver { +public: + // Constructors + OpalInterface(System* sys_ptr); + + // Destructor + // ~OpalInterface(); + + integer_t getInstructionCount(int procID) const; + void hitCallback( NodeID proc, SubBlock& data, CacheRequestType type, int thread ); + void printStats(ostream& out) const; + void clearStats(); + void printConfig(ostream& out) const; + void print(ostream& out) const; + + integer_t readPhysicalMemory(int procID, physical_address_t address, + int len ); + + void writePhysicalMemory( int procID, physical_address_t address, + integer_t value, int len ); + uint64 getOpalTime(int procID) const; + + // for WATTCH power + void incrementL2Access(int procID) const; + void incrementPrefetcherAccess(int procID, int num_prefetches, int isinstr) const; + + // notifies Opal of an L2 miss + void notifyL2Miss(int procID, physical_address_t physicalAddr, OpalMemop_t type, int tagexists) const; + + void printDebug(); + + /// The static opalinterface instance + static OpalInterface *inst; + + /// static methods + static int getNumberOutstanding(int cpuNumber); + static int getNumberOutstandingDemand(int cpuNumber); + static int getNumberOutstandingPrefetch( int cpuNumber ); + + /* returns true if the sequencer is able to handle more requests. + This implements "back-pressure" by which the processor knows + not to issue more requests if the network or cache's limits are reached. + */ + static int isReady( int cpuNumber, la_t logicalAddr, pa_t physicalAddr, OpalMemop_t typeOfRequest, int thread ); + + /* + makeRequest performs the coherence transactions necessary to get the + physical address in the cache with the correct permissions. More than + one request can be outstanding to ruby, but only one per block address. + The size of the cache line is defined to Intf_CacheLineSize. + When a request is finished (e.g. the cache contains physical address), + ruby calls completedRequest(). No request can be bigger than + Opal_CacheLineSize. It is illegal to request non-aligned memory + locations. A request of size 2 must be at an even byte, a size 4 must + be at a byte address half-word aligned, etc. Requests also can't cross a + cache-line boundaries. + */ + static void makeRequest(int cpuNumber, la_t logicalAddr, pa_t physicalAddr, + int requestSize, OpalMemop_t typeOfRequest, + la_t virtualPC, int isPriv, int thread); + + /* prefetch a given block... + */ + static void makePrefetch(int cpuNumber, la_t logicalAddr, pa_t physicalAddr, + int requestSize, OpalMemop_t typeOfRequest, + la_t virtualPC, int isPriv, int thread); + + /* + * request data from the cache, even if it's state is "Invalid". + */ + static int staleDataRequest( int cpuNumber, pa_t physicalAddr, + int requestSize, int8 *buffer ); + + /* notify ruby of opal's status + */ + static void notify( int status ); + + /* + * advance ruby one cycle + */ + static void advanceTime( void ); + + /* + * return ruby's cycle count. + */ + static unsigned long long getTime( void ); + + /* prints Ruby's outstanding request table */ + static void printProgress(int cpuNumber); + + /* + * initialize / install the inter-module interface + */ + static void installInterface( mf_ruby_api_t *api ); + + /* + * Test if opal is loaded or not + */ + static bool isOpalLoaded( void ); + + /* + * query opal for its api + */ + void queryOpalInterface( void ); + + /* + * remove the opal interface (opal is unloaded). + */ + void removeOpalInterface( void ); + + /* + * set the opal interface (used if stand-alone testing) + */ + void setOpalInterface( mf_opal_api_t *opal_intf ) { + m_opal_intf = opal_intf; + } + + /** + * Signal an abort + */ + //void abortCallback(NodeID proc); + +private: + // Private Methods + + // Private copy constructor and assignment operator + OpalInterface(const OpalInterface& obj); + OpalInterface& operator=(const OpalInterface& obj); + + // Data Members (m_ prefix) + mf_opal_api_t *m_opal_intf; + Time m_simicsStartTime; + + static int s_advance_counter; +}; + +// Output operator declaration +ostream& operator<<(ostream& out, const OpalInterface& obj); + +// ******************* Definitions ******************* + +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const OpalInterface& obj) +{ +// obj.print(out); + out << flush; + return out; +} + +#endif // OpalInterface_H diff --git a/src/mem/ruby/interfaces/mf_api.hh b/src/mem/ruby/interfaces/mf_api.hh new file mode 100644 index 000000000..c04a39308 --- /dev/null +++ b/src/mem/ruby/interfaces/mf_api.hh @@ -0,0 +1,165 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/*------------------------------------------------------------------------*/ +/* Includes */ +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +/* Macro declarations */ +/*------------------------------------------------------------------------*/ + +#ifndef _MF_MEMORY_API_H_ +#define _MF_MEMORY_API_H_ + +#ifdef SIMICS30 +#ifndef pa_t +typedef physical_address_t pa_t; +typedef physical_address_t la_t; +#endif +#endif + +/** + * Defines types of memory requests + */ +typedef enum OpalMemop { + OPAL_LOAD, + OPAL_STORE, + OPAL_IFETCH, + OPAL_ATOMIC, +} OpalMemop_t; + +/*------------------------------------------------------------------------*/ +/* Class declaration(s) */ +/*------------------------------------------------------------------------*/ + +/** +* structure which provides an interface between ruby and opal. +*/ +typedef struct mf_opal_api { + /** + * @name Methods + */ + //@{ + /** + * notify processor model that data from address address is available at proc + */ + void (*hitCallback)( int cpuNumber, pa_t phys_address, OpalMemop_t type, int thread ); + + /** + * notify opal that ruby is loaded, or removed + */ + void (*notifyCallback)( int status ); + + /** + * query for the number of instructions executed on a given processor. + */ + integer_t (*getInstructionCount)( int cpuNumber ); + + // for printing out debug info on crash + void (*printDebug)(); + + /** query Opal for the current time */ + uint64 (*getOpalTime)(int cpuNumber); + + /** For WATTCH power stats */ + // Called whenever L2 is accessed + void (*incrementL2Access)(int cpuNumber); + // Called whenever prefetcher is accessed + void (*incrementPrefetcherAccess)(int cpuNumber, int num_prefetches, int isinstr); + + /* Called whenever there's an L2 miss */ + void (*notifyL2Miss)(int cpuNumber, physical_address_t physicalAddr, OpalMemop_t type, int tagexists); + + //@} +} mf_opal_api_t; + +typedef struct mf_ruby_api { + /** + * @name Methods + */ + //@{ + /** + * Check to see if the system is ready for more requests + */ + int (*isReady)( int cpuNumber, la_t logicalAddr, pa_t physicalAddr, OpalMemop_t typeOfRequest, int thread ); + + /** + * Make a 'mandatory' request to the memory hierarchy + */ + void (*makeRequest)( int cpuNumber, la_t logicalAddr, pa_t physicalAddr, + int requestSize, OpalMemop_t typeOfRequest, + la_t virtualPC, int isPriv, int thread); + + /** + * Make a prefetch request to the memory hierarchy + */ + void (*makePrefetch)( int cpuNumber, la_t logicalAddr, pa_t physicalAddr, + int requestSize, OpalMemop_t typeOfRequest, + la_t virtualPC, int isPriv, int thread); + + /** + * Ask the memory hierarchy for 'stale' data that can be used for speculation + * Returns true (1) if the tag matches, false (0) if not. + */ + int (*staleDataRequest)( int cpuNumber, pa_t physicalAddr, + int requestSize, int8 *buffer ); + + /** + * Advance ruby's cycle time one step + */ + void (*advanceTime)( void ); + + /** + * Get ruby's cycle time count. + */ + uint64 (*getTime)( void ); + + /** prints Ruby's outstanding request table */ + void (*printProgress)(int cpuNumber); + + /** + * notify ruby that opal is loaded, or removed + */ + void (*notifyCallback)( int status ); + + // Returns the number of outstanding request + int (*getNumberOutstanding)(int cpuNumber); + + // Returns the number of outstanding demand requests + int (*getNumberOutstandingDemand)(int cpuNumber ); + + // Returns the number of outstanding prefetch request + int (*getNumberOutstandingPrefetch)(int cpuNumber ); + + + //@} +} mf_ruby_api_t; + +#endif //_MF_MEMORY_API_H_ diff --git a/src/mem/ruby/network/Network.hh b/src/mem/ruby/network/Network.hh new file mode 100644 index 000000000..662e54e93 --- /dev/null +++ b/src/mem/ruby/network/Network.hh @@ -0,0 +1,148 @@ + +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Network.h + * + * Description: The Network class is the base class for classes that + * implement the interconnection network between components + * (processor/cache components and memory/directory components). The + * interconnection network as described here is not a physical + * network, but a programming concept used to implement all + * communication between components. Thus parts of this 'network' + * will model the on-chip connections between cache controllers and + * directory controllers as well as the links between chip and network + * switches. + * + * $Id$ + * */ + +#ifndef NETWORK_H +#define NETWORK_H + +#include "Global.hh" +#include "NodeID.hh" +#include "MessageSizeType.hh" + +class NetDest; +class MessageBuffer; +class Throttle; + +class Network { +public: + // Constructors + Network() {} + + // Destructor + virtual ~Network() {} + + // Public Methods + + static Network* createNetwork(int nodes); + + // returns the queue requested for the given component + virtual MessageBuffer* getToNetQueue(NodeID id, bool ordered, int netNumber) = 0; + virtual MessageBuffer* getFromNetQueue(NodeID id, bool ordered, int netNumber) = 0; + virtual const Vector* getThrottles(NodeID id) const { return NULL; } + + virtual int getNumNodes() {return 1;} + + virtual void makeOutLink(SwitchID src, NodeID dest, const NetDest& routing_table_entry, int link_latency, int link_weight, int bw_multiplier, bool isReconfiguration) = 0; + virtual void makeInLink(SwitchID src, NodeID dest, const NetDest& routing_table_entry, int link_latency, int bw_multiplier, bool isReconfiguration) = 0; + virtual void makeInternalLink(SwitchID src, NodeID dest, const NetDest& routing_table_entry, int link_latency, int link_weight, int bw_multiplier, bool isReconfiguration) = 0; + + virtual void reset() = 0; + + virtual void printStats(ostream& out) const = 0; + virtual void clearStats() = 0; + virtual void printConfig(ostream& out) const = 0; + virtual void print(ostream& out) const = 0; + +private: + + // Private Methods + // Private copy constructor and assignment operator + Network(const Network& obj); + Network& operator=(const Network& obj); + + // Data Members (m_ prefix) +}; + +// Output operator declaration +ostream& operator<<(ostream& out, const Network& obj); + +// ******************* Definitions ******************* + +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const Network& obj) +{ + obj.print(out); + out << flush; + return out; +} + +// Code to map network message size types to an integer number of bytes +const int CONTROL_MESSAGE_SIZE = 8; +const int DATA_MESSAGE_SIZE = (64+8); + +extern inline +int MessageSizeType_to_int(MessageSizeType size_type) +{ + switch(size_type) { + case MessageSizeType_Undefined: + ERROR_MSG("Can't convert Undefined MessageSizeType to integer"); + break; + case MessageSizeType_Control: + case MessageSizeType_Request_Control: + case MessageSizeType_Reissue_Control: + case MessageSizeType_Response_Control: + case MessageSizeType_Writeback_Control: + case MessageSizeType_Forwarded_Control: + case MessageSizeType_Invalidate_Control: + case MessageSizeType_Unblock_Control: + case MessageSizeType_Persistent_Control: + case MessageSizeType_Completion_Control: + return CONTROL_MESSAGE_SIZE; + break; + case MessageSizeType_Data: + case MessageSizeType_Response_Data: + case MessageSizeType_ResponseLocal_Data: + case MessageSizeType_ResponseL2hit_Data: + case MessageSizeType_Writeback_Data: + return DATA_MESSAGE_SIZE; + break; + default: + ERROR_MSG("Invalid range for type MessageSizeType"); + break; + } + return 0; +} + +#endif //NETWORK_H diff --git a/src/mem/ruby/network/garnet-fixed-pipeline/CreditLink_d.hh b/src/mem/ruby/network/garnet-fixed-pipeline/CreditLink_d.hh new file mode 100644 index 000000000..e3a9b7d2d --- /dev/null +++ b/src/mem/ruby/network/garnet-fixed-pipeline/CreditLink_d.hh @@ -0,0 +1,17 @@ +/* + * CreditLink_d.h + * + * Niket Agarwal, Princeton University + * + * */ +#ifndef CREDIT_LINK_D_H +#define CREDIT_LINK_D_H + +#include "NetworkLink_d.hh" + +class CreditLink_d : public NetworkLink_d { +public: + CreditLink_d(int id):NetworkLink_d(id) {} +}; + +#endif diff --git a/src/mem/ruby/network/garnet-fixed-pipeline/GarnetNetwork_d.cc b/src/mem/ruby/network/garnet-fixed-pipeline/GarnetNetwork_d.cc new file mode 100644 index 000000000..43f9a31bd --- /dev/null +++ b/src/mem/ruby/network/garnet-fixed-pipeline/GarnetNetwork_d.cc @@ -0,0 +1,349 @@ +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * GarnetNetwork_d.C + * + * Niket Agarwal, Princeton University + * + * */ + +#include "GarnetNetwork_d.hh" +#include "MachineType.hh" +#include "NetworkInterface_d.hh" +#include "MessageBuffer.hh" +#include "Router_d.hh" +#include "Topology.hh" +#include "NetworkLink_d.hh" +#include "CreditLink_d.hh" +#include "NetDest.hh" + +GarnetNetwork_d::GarnetNetwork_d(int nodes) +{ + m_nodes = MachineType_base_number(MachineType_NUM); // Total nodes in network + m_virtual_networks = NUMBER_OF_VIRTUAL_NETWORKS; // Number of virtual networks = number of message classes in the coherence protocol + m_ruby_start = 0; + m_flits_recieved = 0; + m_flits_injected = 0; + m_network_latency = 0.0; + m_queueing_latency = 0.0; + + m_router_ptr_vector.clear(); + + // Allocate to and from queues + m_toNetQueues.setSize(m_nodes); // Queues that are getting messages from protocol + m_fromNetQueues.setSize(m_nodes); // Queues that are feeding the protocol + m_in_use.setSize(m_virtual_networks); + m_ordered.setSize(m_virtual_networks); + for (int i = 0; i < m_virtual_networks; i++) + { + m_in_use[i] = false; + m_ordered[i] = false; + } + + for (int node = 0; node < m_nodes; node++) + { + //Setting how many vitual message buffers will there be per Network Queue + m_toNetQueues[node].setSize(m_virtual_networks); + m_fromNetQueues[node].setSize(m_virtual_networks); + + for (int j = 0; j < m_virtual_networks; j++) + { + m_toNetQueues[node][j] = new MessageBuffer(); // Instantiating the Message Buffers that interact with the coherence protocol + m_fromNetQueues[node][j] = new MessageBuffer(); + } + } + + // Setup the network switches + m_topology_ptr = new Topology(this, m_nodes); + + int number_of_routers = m_topology_ptr->numSwitches(); + for (int i=0; iaddNode(m_toNetQueues[i], m_fromNetQueues[i]); + m_ni_ptr_vector.insertAtBottom(ni); + } + m_topology_ptr->createLinks(false); // false because this isn't a reconfiguration + for(int i = 0; i < m_router_ptr_vector.size(); i++) + { + m_router_ptr_vector[i]->init(); + } +} + +GarnetNetwork_d::~GarnetNetwork_d() +{ + for (int i = 0; i < m_nodes; i++) + { + m_toNetQueues[i].deletePointers(); + m_fromNetQueues[i].deletePointers(); + } + m_router_ptr_vector.deletePointers(); + m_ni_ptr_vector.deletePointers(); + m_link_ptr_vector.deletePointers(); + m_creditlink_ptr_vector.deletePointers(); + delete m_topology_ptr; +} + +void GarnetNetwork_d::reset() +{ + for (int node = 0; node < m_nodes; node++) + { + for (int j = 0; j < m_virtual_networks; j++) + { + m_toNetQueues[node][j]->clear(); + m_fromNetQueues[node][j]->clear(); + } + } +} + +/* + * This function creates a link from the Network Interface (NI) into the Network. + * It creates a Network Link from the NI to a Router and a Credit Link from + * the Router to the NI +*/ + +void GarnetNetwork_d::makeInLink(NodeID src, SwitchID dest, const NetDest& routing_table_entry, int link_latency, int bw_multiplier, bool isReconfiguration) +{ + assert(src < m_nodes); + + if(!isReconfiguration) + { + NetworkLink_d *net_link = new NetworkLink_d(m_link_ptr_vector.size(), link_latency, this); + CreditLink_d *credit_link = new CreditLink_d(m_creditlink_ptr_vector.size()); + m_link_ptr_vector.insertAtBottom(net_link); + m_creditlink_ptr_vector.insertAtBottom(credit_link); + + m_router_ptr_vector[dest]->addInPort(net_link, credit_link); + m_ni_ptr_vector[src]->addOutPort(net_link, credit_link); + } + else + { + ERROR_MSG("Fatal Error:: Reconfiguration not allowed here"); + // do nothing + } +} + +/* + * This function creates a link from the Network to a NI. + * It creates a Network Link from a Router to the NI and + * a Credit Link from NI to the Router +*/ + +void GarnetNetwork_d::makeOutLink(SwitchID src, NodeID dest, const NetDest& routing_table_entry, int link_latency, int link_weight, int bw_multiplier, bool isReconfiguration) +{ + assert(dest < m_nodes); + assert(src < m_router_ptr_vector.size()); + assert(m_router_ptr_vector[src] != NULL); + + if(!isReconfiguration) + { + NetworkLink_d *net_link = new NetworkLink_d(m_link_ptr_vector.size(), link_latency, this); + CreditLink_d *credit_link = new CreditLink_d(m_creditlink_ptr_vector.size()); + m_link_ptr_vector.insertAtBottom(net_link); + m_creditlink_ptr_vector.insertAtBottom(credit_link); + + m_router_ptr_vector[src]->addOutPort(net_link, routing_table_entry, link_weight, credit_link); + m_ni_ptr_vector[dest]->addInPort(net_link, credit_link); + } + else + { + ERROR_MSG("Fatal Error:: Reconfiguration not allowed here"); + //do nothing + } +} + +/* + * This function creates a internal network links +*/ + +void GarnetNetwork_d::makeInternalLink(SwitchID src, SwitchID dest, const NetDest& routing_table_entry, int link_latency, int link_weight, int bw_multiplier, bool isReconfiguration) +{ + if(!isReconfiguration) + { + NetworkLink_d *net_link = new NetworkLink_d(m_link_ptr_vector.size(), link_latency, this); + CreditLink_d *credit_link = new CreditLink_d(m_creditlink_ptr_vector.size()); + m_link_ptr_vector.insertAtBottom(net_link); + m_creditlink_ptr_vector.insertAtBottom(credit_link); + + m_router_ptr_vector[dest]->addInPort(net_link, credit_link); + m_router_ptr_vector[src]->addOutPort(net_link, routing_table_entry, link_weight, credit_link); + } + else + { + ERROR_MSG("Fatal Error:: Reconfiguration not allowed here"); + // do nothing + } +} + +void GarnetNetwork_d::checkNetworkAllocation(NodeID id, bool ordered, int network_num) +{ + ASSERT(id < m_nodes); + ASSERT(network_num < m_virtual_networks); + + if (ordered) + { + m_ordered[network_num] = true; + } + m_in_use[network_num] = true; +} + +MessageBuffer* GarnetNetwork_d::getToNetQueue(NodeID id, bool ordered, int network_num) +{ + checkNetworkAllocation(id, ordered, network_num); + return m_toNetQueues[id][network_num]; +} + +MessageBuffer* GarnetNetwork_d::getFromNetQueue(NodeID id, bool ordered, int network_num) +{ + checkNetworkAllocation(id, ordered, network_num); + return m_fromNetQueues[id][network_num]; +} + +void GarnetNetwork_d::clearStats() +{ + m_ruby_start = g_eventQueue_ptr->getTime(); +} + +Time GarnetNetwork_d::getRubyStartTime() +{ + return m_ruby_start; +} + +void GarnetNetwork_d::printStats(ostream& out) const +{ double average_link_utilization = 0; + Vector average_vc_load; + average_vc_load.setSize(m_virtual_networks*NetworkConfig::getVCsPerClass()); + + for(int i = 0; i < m_virtual_networks*NetworkConfig::getVCsPerClass(); i++) + { + average_vc_load[i] = 0; + } + + out << endl; + out << "Network Stats" << endl; + out << "-------------" << endl; + out << endl; + for(int i = 0; i < m_link_ptr_vector.size(); i++) + { + average_link_utilization += (double(m_link_ptr_vector[i]->getLinkUtilization())) / (double(g_eventQueue_ptr->getTime()-m_ruby_start)); + + Vector vc_load = m_link_ptr_vector[i]->getVcLoad(); + for(int j = 0; j < vc_load.size(); j++) + { + assert(vc_load.size() == NetworkConfig::getVCsPerClass()*m_virtual_networks); + average_vc_load[j] += vc_load[j]; + } + } + average_link_utilization = average_link_utilization/m_link_ptr_vector.size(); + out << "Average Link Utilization :: " << average_link_utilization << " flits/cycle" << endl; + out << "-------------" << endl; + + for(int i = 0; i < NetworkConfig::getVCsPerClass()*NUMBER_OF_VIRTUAL_NETWORKS; i++) + { + average_vc_load[i] = (double(average_vc_load[i]) / (double(g_eventQueue_ptr->getTime()) - m_ruby_start)); + out << "Average VC Load [" << i << "] = " << average_vc_load[i] << " flits/cycle " << endl; + } + out << "-------------" << endl; + + // out << "Total flits injected = " << m_flits_injected << endl; + // out << "Total flits recieved = " << m_flits_recieved << endl; + out << "Average network latency = " << ((double) m_network_latency/ (double) m_flits_recieved)<< endl; + // out << "Average queueing latency = " << ((double) m_queueing_latency/ (double) m_flits_recieved)<< endl; + // out << "Average latency = " << ((double) (m_queueing_latency + m_network_latency) / (double) m_flits_recieved)<< endl; + out << "-------------" << endl; + + double m_total_link_power = 0.0; + double m_total_router_power = 0.0; + + for(int i = 0; i < m_link_ptr_vector.size(); i++) + { + m_total_link_power += m_link_ptr_vector[i]->calculate_power(); + } + + for(int i = 0; i < m_router_ptr_vector.size(); i++) + { + m_total_router_power += m_router_ptr_vector[i]->calculate_power(); + } + out << "Total Link Power = " << m_total_link_power << " W " << endl; + out << "Total Router Power = " << m_total_router_power << " W " <printConfig(out); + } + for(int i = 0; i < m_router_ptr_vector.size(); i++) + { + m_router_ptr_vector[i]->printConfig(out); + } + if (g_PRINT_TOPOLOGY) + { + m_topology_ptr->printConfig(out); + } +} + +void GarnetNetwork_d::print(ostream& out) const +{ + out << "[GarnetNetwork_d]"; +} diff --git a/src/mem/ruby/network/garnet-fixed-pipeline/GarnetNetwork_d.hh b/src/mem/ruby/network/garnet-fixed-pipeline/GarnetNetwork_d.hh new file mode 100644 index 000000000..34486eab8 --- /dev/null +++ b/src/mem/ruby/network/garnet-fixed-pipeline/GarnetNetwork_d.hh @@ -0,0 +1,142 @@ +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * GarnetNetwork_d.h + * + * Niket Agarwal, Princeton University + * + * */ + +#ifndef GARNETNETWORK_D_H +#define GARNETNETWORK_D_H + +#include "NetworkHeader.hh" +#include "Vector.hh" +#include "NetworkConfig.hh" +#include "Network.hh" + +class NetworkInterface_d; +class MessageBuffer; +class Router_d; +class Topology; +class NetDest; +class NetworkLink_d; +class CreditLink_d; + +class GarnetNetwork_d : public Network{ +public: + GarnetNetwork_d(int nodes); + + ~GarnetNetwork_d(); + + int getNumNodes(){ return m_nodes;} + + // returns the queue requested for the given component + MessageBuffer* getToNetQueue(NodeID id, bool ordered, int network_num); + MessageBuffer* getFromNetQueue(NodeID id, bool ordered, int network_num); + + void clearStats(); + void printStats(ostream& out) const; + void printConfig(ostream& out) const; + void print(ostream& out) const; + + inline void increment_injected_flits() + { + m_flits_injected++; + } + inline void increment_recieved_flits() + { + m_flits_recieved++; + } + inline void increment_network_latency(Time latency) + { + m_network_latency += latency; + } + inline void increment_queueing_latency(Time latency) + { + m_queueing_latency += latency; + } + + bool isVNetOrdered(int vnet) + { + return m_ordered[vnet]; + } + bool validVirtualNetwork(int vnet) { return m_in_use[vnet]; } + + Time getRubyStartTime(); + + void reset(); + + // Methods used by Topology to setup the network + void makeOutLink(SwitchID src, NodeID dest, const NetDest& routing_table_entry, int link_latency, int link_weight, int bw_multiplier, bool isReconfiguration); + void makeInLink(SwitchID src, NodeID dest, const NetDest& routing_table_entry, int link_latency, int bw_multiplier, bool isReconfiguration); + void makeInternalLink(SwitchID src, NodeID dest, const NetDest& routing_table_entry, int link_latency, int link_weight, int bw_multiplier, bool isReconfiguration); + +private: + void checkNetworkAllocation(NodeID id, bool ordered, int network_num); + +// Private copy constructor and assignment operator + GarnetNetwork_d(const GarnetNetwork_d& obj); + GarnetNetwork_d& operator=(const GarnetNetwork_d& obj); + +/***********Data Members*************/ + int m_virtual_networks; + int m_nodes; + int m_flits_recieved, m_flits_injected; + double m_network_latency, m_queueing_latency; + + Vector m_in_use; + Vector m_ordered; + + Vector > m_toNetQueues; + Vector > m_fromNetQueues; + + Vector m_router_ptr_vector; // All Routers in Network + Vector m_link_ptr_vector; // All links in the network + Vector m_creditlink_ptr_vector; // All links in the network + Vector m_ni_ptr_vector; // All NI's in Network + + Topology* m_topology_ptr; + Time m_ruby_start; +}; + +// Output operator declaration +ostream& operator<<(ostream& out, const GarnetNetwork_d& obj); + +// ******************* Definitions ******************* +// Output operator definition +extern inline +ostream& operator<<(ostream& out, const GarnetNetwork_d& obj) +{ + obj.print(out); + out << flush; + return out; +} + +#endif //GARNETNETWORK_D_H diff --git a/src/mem/ruby/network/garnet-fixed-pipeline/InputUnit_d.cc b/src/mem/ruby/network/garnet-fixed-pipeline/InputUnit_d.cc new file mode 100644 index 000000000..bedd801d5 --- /dev/null +++ b/src/mem/ruby/network/garnet-fixed-pipeline/InputUnit_d.cc @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * InputUnit_d.C + * + * Niket Agarwal, Princeton University + * + * */ + +#include "InputUnit_d.hh" +#include "Router_d.hh" + +InputUnit_d::InputUnit_d(int id, Router_d *router) +{ + m_id = id; + m_router = router; + m_num_vcs = m_router->get_num_vcs(); + + m_num_buffer_reads = 0; + m_num_buffer_writes = 0; + + creditQueue = new flitBuffer_d(); + // Instantiating the virtual channels + m_vcs.setSize(m_num_vcs); + for(int i=0; i < m_num_vcs; i++) + { + m_vcs[i] = new VirtualChannel_d(i); + } +} + +InputUnit_d::~InputUnit_d() +{ + delete creditQueue; + m_vcs.deletePointers(); +} + +void InputUnit_d::wakeup() +{ + flit_d *t_flit; + if(m_in_link->isReady()) + { + t_flit = m_in_link->consumeLink(); + int vc = t_flit->get_vc(); + if((t_flit->get_type() == HEAD_) || (t_flit->get_type() == HEAD_TAIL_)) + { + assert(m_vcs[vc]->get_state() == IDLE_); + m_router->route_req(t_flit, this, vc); // Do the route computation for this vc + m_vcs[vc]->set_enqueue_time(g_eventQueue_ptr->getTime()); + } + else + { + t_flit->advance_stage(SA_); + m_router->swarb_req(); + } + m_vcs[vc]->insertFlit(t_flit); // write flit into input buffer + m_num_buffer_writes++; + m_num_buffer_reads++; // same as read because any flit that is written will be read only once + } +} + + +void InputUnit_d::printConfig(ostream& out) +{ + out << endl; + out << "InputUnit Configuration" << endl; + out << "---------------------" << endl; + out << "id = " << m_id << endl; + out << "In link is " << m_in_link->get_id() << endl; +} diff --git a/src/mem/ruby/network/garnet-fixed-pipeline/InputUnit_d.hh b/src/mem/ruby/network/garnet-fixed-pipeline/InputUnit_d.hh new file mode 100644 index 000000000..c22363fb1 --- /dev/null +++ b/src/mem/ruby/network/garnet-fixed-pipeline/InputUnit_d.hh @@ -0,0 +1,172 @@ +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * InputUnit_d.h + * + * Niket Agarwal, Princeton University + * + * */ + +#ifndef INPUT_UNIT_D_H +#define INPUT_UNIT_D_H + +#include "NetworkHeader.hh" +#include "flitBuffer_d.hh" +#include "Consumer.hh" +#include "Vector.hh" +#include "VirtualChannel_d.hh" +#include "NetworkLink_d.hh" +#include "CreditLink_d.hh" + +class Router_d; + +class InputUnit_d : public Consumer { +public: + InputUnit_d(int id, Router_d *router); + ~InputUnit_d(); + + void wakeup(); + void printConfig(ostream& out); + flitBuffer_d* getCreditQueue() { return creditQueue; } + void print(ostream& out) const {}; + + inline int get_inlink_id() + { + return m_in_link->get_id(); + } + + inline void set_vc_state(VC_state_type state, int vc) + { + m_vcs[vc]->set_state(state); + } + inline void set_enqueue_time(int invc, Time time) + { + m_vcs[invc]->set_enqueue_time(time); + } + inline Time get_enqueue_time(int invc) + { + return m_vcs[invc]->get_enqueue_time(); + } + inline void update_credit(int in_vc, int credit) + { + m_vcs[in_vc]->update_credit(credit); + } + + inline bool has_credits(int vc) + { + return m_vcs[vc]->has_credits(); + } + + inline void increment_credit(int in_vc, bool free_signal) + { + flit_d *t_flit = new flit_d(in_vc, free_signal); + creditQueue->insert(t_flit); + g_eventQueue_ptr->scheduleEvent(m_credit_link, 1); + } + + inline int get_outvc(int invc) + { + return m_vcs[invc]->get_outvc(); + } + + inline void updateRoute(int vc, int outport) + { + m_vcs[vc]->set_outport(outport); + m_vcs[vc]->set_state(VC_AB_); + } + + inline void grant_vc(int in_vc, int out_vc) + { + m_vcs[in_vc]->grant_vc(out_vc); + } + + inline flit_d* peekTopFlit(int vc) + { + return m_vcs[vc]->peekTopFlit(); + } + + inline flit_d* getTopFlit(int vc) + { + return m_vcs[vc]->getTopFlit(); + } + + inline bool need_stage(int vc, VC_state_type state, flit_stage stage) + { + return m_vcs[vc]->need_stage(state, stage); + } + + inline bool need_stage_nextcycle(int vc, VC_state_type state, flit_stage stage) + { + return m_vcs[vc]->need_stage_nextcycle(state, stage); + } + + inline bool isReady(int invc) + { + return m_vcs[invc]->isReady(); + } + + inline int get_route(int vc) + { + return m_vcs[vc]->get_route(); + } + inline void set_in_link(NetworkLink_d *link) + { + m_in_link = link; + } + + inline void set_credit_link(CreditLink_d *credit_link) + { + m_credit_link = credit_link; + } + + inline double get_buf_read_count() + { + return m_num_buffer_reads; + } + + inline double get_buf_write_count() + { + return m_num_buffer_writes; + } + +private: + int m_id; + int m_num_vcs; + double m_num_buffer_writes, m_num_buffer_reads; + + Router_d *m_router; + NetworkLink_d *m_in_link; + CreditLink_d *m_credit_link; + flitBuffer_d *creditQueue; + + // Virtual channels + Vector m_vcs; // [vc] +}; + +#endif diff --git a/src/mem/ruby/network/garnet-fixed-pipeline/NetworkHeader.hh b/src/mem/ruby/network/garnet-fixed-pipeline/NetworkHeader.hh new file mode 100644 index 000000000..6a212ce99 --- /dev/null +++ b/src/mem/ruby/network/garnet-fixed-pipeline/NetworkHeader.hh @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * NetworkHeader.h + * + * Niket Agarwal, Princeton University + * + * */ + +#ifndef NETWORK_HEADER_H +#define NETWORK_HEADER_H + +#include "Global.hh" +#include "NodeID.hh" + +using namespace std; +using namespace __gnu_cxx; + +enum flit_type {HEAD_, BODY_, TAIL_, HEAD_TAIL_, NUM_FLIT_TYPE_}; +enum VC_state_type {IDLE_, VC_AB_, ACTIVE_, NUM_VC_STATE_TYPE_}; +enum flit_stage {I_, VA_, SA_, ST_, LT_, NUM_FLIT_STAGE_}; + +#define NETCONFIG_DEFAULTS "netconfig.defaults" + +#define INFINITE_ 10000 + +#endif + diff --git a/src/mem/ruby/network/garnet-fixed-pipeline/NetworkInterface_d.cc b/src/mem/ruby/network/garnet-fixed-pipeline/NetworkInterface_d.cc new file mode 100644 index 000000000..edf2d4b95 --- /dev/null +++ b/src/mem/ruby/network/garnet-fixed-pipeline/NetworkInterface_d.cc @@ -0,0 +1,351 @@ +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * NetworkInterface_d.C + * + * Niket Agarwal, Princeton University + * + * */ + +#include "NetworkInterface_d.hh" +#include "MessageBuffer.hh" +#include "flitBuffer_d.hh" +#include "NetworkMessage.hh" + +NetworkInterface_d::NetworkInterface_d(int id, int virtual_networks, GarnetNetwork_d *network_ptr) +{ + m_id = id; + m_net_ptr = network_ptr; + m_virtual_networks = virtual_networks; + m_vc_per_vnet = NetworkConfig::getVCsPerClass(); + m_num_vcs = m_vc_per_vnet*m_virtual_networks; + + m_vc_round_robin = 0; + m_ni_buffers.setSize(m_num_vcs); + m_ni_enqueue_time.setSize(m_num_vcs); + inNode_ptr.setSize(m_virtual_networks); + outNode_ptr.setSize(m_virtual_networks); + creditQueue = new flitBuffer_d(); + + for(int i =0; i < m_num_vcs; i++) + { + m_ni_buffers[i] = new flitBuffer_d(); // instantiating the NI flit buffers + m_ni_enqueue_time[i] = INFINITE_; + } + m_vc_allocator.setSize(m_virtual_networks); // 1 allocator per virtual net + for(int i = 0; i < m_virtual_networks; i++) + { + m_vc_allocator[i] = 0; + } + + for(int i = 0; i < m_num_vcs; i++) + { + m_out_vc_state.insertAtBottom(new OutVcState_d(i)); + m_out_vc_state[i]->setState(IDLE_, g_eventQueue_ptr->getTime()); + } +} + +NetworkInterface_d::~NetworkInterface_d() +{ + m_out_vc_state.deletePointers(); + m_ni_buffers.deletePointers(); + delete creditQueue; + delete outSrcQueue; +} + +void NetworkInterface_d::addInPort(NetworkLink_d *in_link, CreditLink_d *credit_link) +{ + inNetLink = in_link; + in_link->setLinkConsumer(this); + m_ni_credit_link = credit_link; + credit_link->setSourceQueue(creditQueue); +} + +void NetworkInterface_d::addOutPort(NetworkLink_d *out_link, CreditLink_d *credit_link) +{ + m_credit_link = credit_link; + credit_link->setLinkConsumer(this); + + outNetLink = out_link; + outSrcQueue = new flitBuffer_d(); + out_link->setSourceQueue(outSrcQueue); +} + +void NetworkInterface_d::addNode(Vector& in, Vector& out) +{ + ASSERT(in.size() == m_virtual_networks); + inNode_ptr = in; + outNode_ptr = out; + for (int j = 0; j < m_virtual_networks; j++) + { + inNode_ptr[j]->setConsumer(this); // So that protocol injects messages into the NI + } +} + +bool NetworkInterface_d::flitisizeMessage(MsgPtr msg_ptr, int vnet) +{ + NetworkMessage *net_msg_ptr = dynamic_cast(msg_ptr.ref()); + NetDest net_msg_dest = net_msg_ptr->getInternalDestination(); + Vector dest_nodes = net_msg_dest.getAllDest(); // gets all the destinations associated with this message. + + int num_flits = (int) ceil((double) MessageSizeType_to_int(net_msg_ptr->getMessageSize())/NetworkConfig::getFlitSize() ); // Number of flits is dependent on the link bandwidth available. This is expressed in terms of bytes/cycle or the flit size + + for(int ctr = 0; ctr < dest_nodes.size(); ctr++) // loop because we will be converting all multicast messages into unicast messages + { + int vc = calculateVC(vnet); // this will return a free output virtual channel + if(vc == -1) + { + return false ; + } + MsgPtr new_msg_ptr = *(msg_ptr.ref()); + NodeID destID = dest_nodes[ctr]; + + NetworkMessage *new_net_msg_ptr = dynamic_cast(new_msg_ptr.ref()); + if(dest_nodes.size() > 1) + { + NetDest personal_dest; + for(int m = 0; m < (int) MachineType_NUM; m++) + { + if((destID >= MachineType_base_number((MachineType) m)) && destID < MachineType_base_number((MachineType) (m+1))) + { + // calculating the NetDest associated with this destination ID + personal_dest.clear(); + personal_dest.add((MachineID) {(MachineType) m, (destID - MachineType_base_number((MachineType) m))}); + new_net_msg_ptr->getInternalDestination() = personal_dest; + break; + } + } + net_msg_dest.removeNetDest(personal_dest); + net_msg_ptr->getInternalDestination().removeNetDest(personal_dest); // removing the destination from the original message to reflect that a message with this particular destination has been flitisized and an output vc is acquired + } + for(int i = 0; i < num_flits; i++) + { + m_net_ptr->increment_injected_flits(); + flit_d *fl = new flit_d(i, vc, vnet, num_flits, new_msg_ptr); + fl->set_delay(g_eventQueue_ptr->getTime() - (msg_ptr.ref())->getTime()); + m_ni_buffers[vc]->insert(fl); + } + m_ni_enqueue_time[vc] = g_eventQueue_ptr->getTime(); + m_out_vc_state[vc]->setState(ACTIVE_, g_eventQueue_ptr->getTime()); + } + return true ; +} + +// Looking for a free output vc +int NetworkInterface_d::calculateVC(int vnet) +{ + for(int i = 0; i < m_vc_per_vnet; i++) + { + int delta = m_vc_allocator[vnet]; + m_vc_allocator[vnet]++; + if(m_vc_allocator[vnet] == m_vc_per_vnet) + m_vc_allocator[vnet] = 0; + + if(m_out_vc_state[(vnet*m_vc_per_vnet) + delta]->isInState(IDLE_, g_eventQueue_ptr->getTime())) + { + return ((vnet*m_vc_per_vnet) + delta); + } + } + return -1; +} + +/* + * The NI wakeup checks whether there are any ready messages in the protocol buffer. If yes, + * it picks that up, flitisizes it into a number of flits and puts it into an output buffer + * and schedules the output link. On a wakeup it also checks whether there are flits in the + * input link. If yes, it picks them up and if the flit is a tail, the NI inserts the + * corresponding message into the protocol buffer. It also checks for credits being sent + * by the downstream router. + */ + +void NetworkInterface_d::wakeup() +{ + DEBUG_EXPR(NETWORK_COMP, MedPrio, m_id); + DEBUG_MSG(NETWORK_COMP, MedPrio, "NI WOKE UP"); + DEBUG_EXPR(NETWORK_COMP, MedPrio, g_eventQueue_ptr->getTime()); + + MsgPtr msg_ptr; + + //Checking for messages coming from the protocol + for (int vnet = 0; vnet < m_virtual_networks; vnet++) // can pick up a message/cycle for each virtual net + { + while(inNode_ptr[vnet]->isReady()) // Is there a message waiting + { + msg_ptr = inNode_ptr[vnet]->peekMsgPtr(); + if(flitisizeMessage(msg_ptr, vnet)) + { + inNode_ptr[vnet]->pop(); + } + else + { + break; + } + } + } + + scheduleOutputLink(); + checkReschedule(); + +/*********** Picking messages destined for this NI **********/ + + if(inNetLink->isReady()) + { + flit_d *t_flit = inNetLink->consumeLink(); + bool free_signal = false; + if(t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) + { + free_signal = true; + if(!NetworkConfig::isNetworkTesting()) // When we are doing network only testing, the messages do not have to be buffered into the message buffers + { + outNode_ptr[t_flit->get_vnet()]->enqueue(t_flit->get_msg_ptr(), 1); // enqueueing for protocol buffer. This is not required when doing network only testing + } + } + flit_d *credit_flit = new flit_d(t_flit->get_vc(), free_signal); // Simply send a credit back since we are not buddering this flit in the NI + creditQueue->insert(credit_flit); + g_eventQueue_ptr->scheduleEvent(m_ni_credit_link, 1); + + m_net_ptr->increment_recieved_flits(); + int network_delay = g_eventQueue_ptr->getTime() - t_flit->get_enqueue_time(); + int queueing_delay = t_flit->get_delay(); + m_net_ptr->increment_network_latency(network_delay); + m_net_ptr->increment_queueing_latency(queueing_delay); + delete t_flit; + } + + /****************** Checking for credit link *******/ + + if(m_credit_link->isReady()) + { + flit_d *t_flit = m_credit_link->consumeLink(); + m_out_vc_state[t_flit->get_vc()]->increment_credit(); + if(t_flit->is_free_signal()) + { + m_out_vc_state[t_flit->get_vc()]->setState(IDLE_, g_eventQueue_ptr->getTime()); + } + delete t_flit; + } +} + +// This function look at the NI buffers and if some buffer has flits which are ready to traverse the link in the next cycle and also the downstream output vc associated with this flit has buffers left, the link is scheduled for the next cycle + +void NetworkInterface_d::scheduleOutputLink() +{ + int vc = m_vc_round_robin; + m_vc_round_robin++; + if(m_vc_round_robin == m_num_vcs) + m_vc_round_robin = 0; + + for(int i = 0; i < m_num_vcs; i++) + { + vc++; + if(vc == m_num_vcs) + vc = 0; + if(m_ni_buffers[vc]->isReady() && m_out_vc_state[vc]->has_credits()) // models buffer backpressure + { + bool is_candidate_vc = true; + int t_vnet = get_vnet(vc); + int vc_base = t_vnet * m_vc_per_vnet; + + if(m_net_ptr->isVNetOrdered(t_vnet)) + { + for (int vc_offset = 0; vc_offset < m_vc_per_vnet; vc_offset++) + { + int t_vc = vc_base + vc_offset; + if(m_ni_buffers[t_vc]->isReady()) + { + if(m_ni_enqueue_time[t_vc] < m_ni_enqueue_time[vc]) + { + is_candidate_vc = false; + break; + } + } + } + } + if(!is_candidate_vc) + continue; + + m_out_vc_state[vc]->decrement_credit(); + flit_d *t_flit = m_ni_buffers[vc]->getTopFlit(); // Just removing the flit + t_flit->set_time(g_eventQueue_ptr->getTime() + 1); + outSrcQueue->insert(t_flit); + g_eventQueue_ptr->scheduleEvent(outNetLink, 1); // schedule the out link + + if(t_flit->get_type() == TAIL_ || t_flit->get_type() == HEAD_TAIL_) + { + m_ni_enqueue_time[vc] = INFINITE_; + } + return; + } + } +} + +int NetworkInterface_d::get_vnet(int vc) +{ + for(int i = 0; i < NUMBER_OF_VIRTUAL_NETWORKS; i++) + { + if(vc >= (i*m_vc_per_vnet) && vc < ((i+1)*m_vc_per_vnet)) + { + return i; + } + } + ERROR_MSG("Could not determine vc"); + return -1; +} + +void NetworkInterface_d::checkReschedule() +{ + for(int vnet = 0; vnet < m_virtual_networks; vnet++) + { + if(inNode_ptr[vnet]->isReady()) // Is there a message waiting + { + g_eventQueue_ptr->scheduleEvent(this, 1); + return; + } + } + for(int vc = 0; vc < m_num_vcs; vc++) + { + if(m_ni_buffers[vc]->isReadyForNext()) + { + g_eventQueue_ptr->scheduleEvent(this, 1); + return; + } + } +} + +void NetworkInterface_d::printConfig(ostream& out) const +{ + out << "[Network Interface " << m_id << "] - "; + out << "[inLink " << inNetLink->get_id() << "] - "; + out << "[outLink " << outNetLink->get_id() << "]" << endl; +} + +void NetworkInterface_d::print(ostream& out) const +{ + out << "[Network Interface]"; +} diff --git a/src/mem/ruby/network/garnet-fixed-pipeline/NetworkInterface_d.hh b/src/mem/ruby/network/garnet-fixed-pipeline/NetworkInterface_d.hh new file mode 100644 index 000000000..c776d343c --- /dev/null +++ b/src/mem/ruby/network/garnet-fixed-pipeline/NetworkInterface_d.hh @@ -0,0 +1,96 @@ +/* + * Copyright (c) 1999-2008 Mark D. Hill and David A. Wood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * NetworkInterface_d.h + * + * Niket Agarwal, Princeton University + * + * */ + +#ifndef NET_INTERFACE_D_H +#define NET_INTERFACE_D_H + +#include "NetworkHeader.hh" +#include "GarnetNetwork_d.hh" +#include "Vector.hh" +#include "Consumer.hh" +#include "Message.hh" +#include "NetworkLink_d.hh" +#include "CreditLink_d.hh" +#include "OutVcState_d.hh" + +class NetworkMessage; +class MessageBuffer; +class flitBuffer_d; + +class NetworkInterface_d : public Consumer { +public: + NetworkInterface_d(int id, int virtual_networks, GarnetNetwork_d* network_ptr); + + ~NetworkInterface_d(); + + void addInPort(NetworkLink_d *in_link, CreditLink_d *credit_link); + void addOutPort(NetworkLink_d *out_link, CreditLink_d *credit_link); + + void wakeup(); + void addNode(Vector &inNode, Vector &outNode); + void printConfig(ostream& out) const; + void print(ostream& out) const; + int get_vnet(int vc); + +private: +/**************Data Members*************/ + GarnetNetwork_d *m_net_ptr; + int m_virtual_networks, m_num_vcs, m_vc_per_vnet; + NodeID m_id; + Vector m_out_vc_state; + Vector m_vc_allocator; + int m_vc_round_robin; // For round robin scheduling + flitBuffer_d *outSrcQueue; // For modelling link contention + flitBuffer_d *creditQueue; + + NetworkLink_d *inNetLink; + NetworkLink_d *outNetLink; + CreditLink_d *m_credit_link; + CreditLink_d *m_ni_credit_link; + + // Input Flit Buffers + Vector m_ni_buffers; // The flit buffers which will serve the Consumer + Vector