blob: a87e6b51d07db5b7250c98e69d0938ec524cdcca [file] [log] [blame]
# Copyright (c) 2020-2021 ARM Limited
# All rights reserved.
#
# The license below extends only to copyright in the software and shall
# not be construed as granting a license to any other intellectual
# property including but not limited to intellectual property relating
# to a hardware implementation of the functionality of the software
# licensed hereunder. You may use the software subject to the license
# terms below provided that you ensure that this notice is replicated
# unmodified and in its entirety in all distributions of the software,
# modified or unmodified, in source code or in binary form.
#
# Copyright (c) 1999-2008 Mark D. Hill and David A. Wood
# Copyright (c) 2009 The Hewlett-Packard Development Company
# 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.
from collections import OrderedDict
from slicc.util import PairContainer
from slicc.symbols.Symbol import Symbol
from slicc.symbols.Var import Var
class DataMember(Var):
def __init__(self, symtab, ident, location, type, code, pairs,
machine, init_code):
super().__init__(symtab, ident, location, type, code, pairs, machine)
self.init_code = init_code
self.real_c_type = self.type.c_ident
if "template" in pairs:
self.real_c_type += pairs["template"]
class Enumeration(PairContainer):
def __init__(self, ident, pairs):
super().__init__(pairs)
self.ident = ident
self.primary = False
class Type(Symbol):
def __init__(self, table, ident, location, pairs, machine=None):
super().__init__(table, ident, location, pairs)
self.c_ident = ident
self.abstract_ident = ""
if machine:
if self.isExternal or self.isPrimitive:
if "external_name" in self:
self.c_ident = self["external_name"]
else:
# Append with machine name
self.c_ident = "%s_%s" % (machine, ident)
self.pairs.setdefault("desc", "No description avaliable")
# check for interface that this Type implements
if "interface" in self:
interface = self["interface"]
if interface in ("Message"):
self["message"] = "yes"
# FIXME - all of the following id comparisons are fragile hacks
if self.ident in ("CacheMemory"):
self["cache"] = "yes"
if self.ident in ("TBETable"):
self["tbe"] = "yes"
if self.ident == "TimerTable":
self["timer"] = "yes"
if self.ident == "DirectoryMemory":
self["dir"] = "yes"
if self.ident == "PersistentTable":
self["persistent"] = "yes"
if self.ident == "Prefetcher":
self["prefetcher"] = "yes"
self.isMachineType = (ident == "MachineType")
self.isStateDecl = ("state_decl" in self)
self.statePermPairs = []
self.data_members = OrderedDict()
self.methods = {}
self.enums = OrderedDict()
@property
def isPrimitive(self):
return "primitive" in self
@property
def isMessage(self):
return "message" in self
@property
def isBuffer(self):
return "buffer" in self
@property
def isInPort(self):
return "inport" in self
@property
def isOutPort(self):
return "outport" in self
@property
def isEnumeration(self):
return "enumeration" in self
@property
def isExternal(self):
return "external" in self
@property
def isGlobal(self):
return "global" in self
@property
def isInterface(self):
return "interface" in self
# Return false on error
def addDataMember(self, ident, type, pairs, init_code):
if ident in self.data_members:
return False
member = DataMember(self.symtab, ident, self.location, type,
"m_%s" % ident, pairs, None, init_code)
self.data_members[ident] = member
self.symtab.registerSym(ident, member)
return True
def dataMemberType(self, ident):
return self.data_members[ident].type
def methodId(self, name, param_type_vec):
return '_'.join([name] + [ pt.c_ident for pt in param_type_vec ])
def methodIdAbstract(self, name, param_type_vec):
return '_'.join([name] + [ pt.abstract_ident for pt in param_type_vec ])
def statePermPairAdd(self, state_name, perm_name):
self.statePermPairs.append([state_name, perm_name])
def addFunc(self, func):
ident = self.methodId(func.ident, func.param_types)
if ident in self.methods:
return False
self.methods[ident] = func
return True
def addEnum(self, ident, pairs):
if ident in self.enums:
return False
self.enums[ident] = Enumeration(ident, pairs)
# Add default
if "default" not in self:
self["default"] = "%s_NUM" % self.c_ident
return True
## Used to check if an enum has been already used and therefore
## should not be used again.
def checkEnum(self, ident):
if ident in self.enums and not self.enums[ident].primary:
self.enums[ident].primary = True
return True
return False
def writeCodeFiles(self, path, includes):
if self.isExternal:
# Do nothing
pass
elif self.isEnumeration:
self.printEnumHH(path)
self.printEnumCC(path)
else:
# User defined structs and messages
self.printTypeHH(path)
self.printTypeCC(path)
def printTypeHH(self, path):
code = self.symtab.codeFormatter()
code('''
#ifndef __${{self.c_ident}}_HH__
#define __${{self.c_ident}}_HH__
#include <iostream>
#include "mem/ruby/slicc_interface/RubySlicc_Util.hh"
''')
for dm in self.data_members.values():
if not dm.type.isPrimitive:
code('#include "mem/ruby/protocol/$0.hh"', dm.type.c_ident)
parent = ""
if "interface" in self:
code('#include "mem/ruby/protocol/$0.hh"', self["interface"])
parent = " : public %s" % self["interface"]
code('''
namespace gem5
{
namespace ruby
{
$klass ${{self.c_ident}}$parent
{
public:
${{self.c_ident}}
''', klass="class")
if self.isMessage:
code('(Tick curTime) : %s(curTime) {' % self["interface"])
else:
code('()\n\t\t{')
code.indent()
if not self.isGlobal:
code.indent()
for dm in self.data_members.values():
ident = dm.ident
if "default" in dm:
# look for default value
code('m_$ident = ${{dm["default"]}}; // default for this field')
elif "default" in dm.type:
# Look for the type default
tid = dm.real_c_type
code('m_$ident = ${{dm.type["default"]}};')
code(' // default value of $tid')
else:
code('// m_$ident has no default')
code.dedent()
code('}')
# ******** Copy constructor ********
code('${{self.c_ident}}(const ${{self.c_ident}}&) = default;')
# ******** Assignment operator ********
code('${{self.c_ident}}')
code('&operator=(const ${{self.c_ident}}&) = default;')
# ******** Full init constructor ********
if not self.isGlobal:
params = [ 'const %s& local_%s' % (dm.real_c_type, dm.ident) \
for dm in self.data_members.values() ]
params = ', '.join(params)
if self.isMessage:
params = "const Tick curTime, " + params
code('${{self.c_ident}}($params)')
# Call superclass constructor
if "interface" in self:
if self.isMessage:
code(' : ${{self["interface"]}}(curTime)')
else:
code(' : ${{self["interface"]}}()')
code('{')
code.indent()
for dm in self.data_members.values():
code('m_${{dm.ident}} = local_${{dm.ident}};')
code.dedent()
code('}')
# create a clone member
if self.isMessage:
code('''
MsgPtr
clone() const
{
return std::shared_ptr<Message>(new ${{self.c_ident}}(*this));
}
''')
else:
code('''
${{self.c_ident}}*
clone() const
{
return new ${{self.c_ident}}(*this);
}
''')
if not self.isGlobal:
# const Get methods for each field
code('// Const accessors methods for each field')
for dm in self.data_members.values():
code('''
/** \\brief Const accessor method for ${{dm.ident}} field.
* \\return ${{dm.ident}} field
*/
const ${{dm.real_c_type}}&
get${{dm.ident}}() const
{
return m_${{dm.ident}};
}
''')
# Non-const Get methods for each field
code('// Non const Accessors methods for each field')
for dm in self.data_members.values():
code('''
/** \\brief Non-const accessor method for ${{dm.ident}} field.
* \\return ${{dm.ident}} field
*/
${{dm.real_c_type}}&
get${{dm.ident}}()
{
return m_${{dm.ident}};
}
''')
#Set methods for each field
code('// Mutator methods for each field')
for dm in self.data_members.values():
code('''
/** \\brief Mutator method for ${{dm.ident}} field */
void
set${{dm.ident}}(const ${{dm.real_c_type}}& local_${{dm.ident}})
{
m_${{dm.ident}} = local_${{dm.ident}};
}
''')
code('void print(std::ostream& out) const;')
code.dedent()
code(' //private:')
code.indent()
# Data members for each field
for dm in self.data_members.values():
if "abstract" not in dm:
const = ""
init = ""
# global structure
if self.isGlobal:
const = "static const "
# init value
if dm.init_code:
# only global structure can have init value here
assert self.isGlobal
init = " = %s" % (dm.init_code)
if "desc" in dm:
code('/** ${{dm["desc"]}} */')
code('$const${{dm.real_c_type}} m_${{dm.ident}}$init;')
# Prototypes for methods defined for the Type
for item in self.methods:
proto = self.methods[item].prototype
if proto:
code('$proto')
code.dedent()
code('};')
code('''
inline ::std::ostream&
operator<<(::std::ostream& out, const ${{self.c_ident}}& obj)
{
obj.print(out);
out << ::std::flush;
return out;
}
} // namespace ruby
} // namespace gem5
#endif // __${{self.c_ident}}_HH__
''')
code.write(path, "%s.hh" % self.c_ident)
def printTypeCC(self, path):
code = self.symtab.codeFormatter()
code('''
#include <iostream>
#include <memory>
#include "mem/ruby/protocol/${{self.c_ident}}.hh"
#include "mem/ruby/system/RubySystem.hh"
namespace gem5
{
namespace ruby
{
/** \\brief Print the state of this object */
void
${{self.c_ident}}::print(std::ostream& out) const
{
out << "[${{self.c_ident}}: ";
''')
# For each field
code.indent()
for dm in self.data_members.values():
if dm.type.c_ident == "Addr":
code('''
out << "${{dm.ident}} = " << printAddress(m_${{dm.ident}}) << " ";''')
else:
code('out << "${{dm.ident}} = " << m_${{dm.ident}} << " ";''')
code.dedent()
# Trailer
code('''
out << "]";
}''')
# print the code for the methods in the type
for item in self.methods:
code(self.methods[item].generateCode())
code('''
} // namespace ruby
} // namespace gem5
''')
code.write(path, "%s.cc" % self.c_ident)
def printEnumHH(self, path):
code = self.symtab.codeFormatter()
code('''
#ifndef __${{self.c_ident}}_HH__
#define __${{self.c_ident}}_HH__
#include <iostream>
#include <string>
''')
if self.isStateDecl:
code('#include "mem/ruby/protocol/AccessPermission.hh"')
if self.isMachineType:
code('#include <functional>')
code('#include "base/logging.hh"')
code('#include "mem/ruby/common/Address.hh"')
code('#include "mem/ruby/common/TypeDefines.hh"')
code('''
namespace gem5
{
namespace ruby
{
''')
if self.isMachineType:
code('struct MachineID;')
code('''
// Class definition
/** \\enum ${{self.c_ident}}
* \\brief ${{self.desc}}
*/
enum ${{self.c_ident}} {
${{self.c_ident}}_FIRST,
''')
code.indent()
# For each field
for i,(ident,enum) in enumerate(self.enums.items()):
desc = enum.get("desc", "No description avaliable")
if i == 0:
init = ' = %s_FIRST' % self.c_ident
else:
init = ''
code('${{self.c_ident}}_${{enum.ident}}$init, /**< $desc */')
code.dedent()
code('''
${{self.c_ident}}_NUM
};
// Code to convert from a string to the enumeration
${{self.c_ident}} string_to_${{self.c_ident}}(const ::std::string& str);
// Code to convert state to a string
::std::string ${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj);
// Code to increment an enumeration type
${{self.c_ident}} &operator++(${{self.c_ident}} &e);
''')
# MachineType hack used to set the base component id for each Machine
if self.isMachineType:
code('''
int ${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj);
MachineType ${{self.c_ident}}_from_base_level(int);
int ${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj);
int ${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj);
''')
for enum in self.enums.values():
code('''
MachineID get${{enum.ident}}MachineID(NodeID RubyNode);
''')
if self.isStateDecl:
code('''
// Code to convert the current state to an access permission
AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj);
''')
code('''
::std::ostream&
operator<<(::std::ostream& out, const ${{self.c_ident}}& obj);
} // namespace ruby
} // namespace gem5
''')
if self.isMachineType:
code('''
// define a hash function for the MachineType class
namespace std {
template<>
struct hash<gem5::ruby::MachineType>
{
std::size_t
operator()(const gem5::ruby::MachineType &mtype) const
{
return hash<size_t>()(static_cast<size_t>(mtype));
}
};
}
''')
# Trailer
code('''
#endif // __${{self.c_ident}}_HH__
''')
code.write(path, "%s.hh" % self.c_ident)
def printEnumCC(self, path):
code = self.symtab.codeFormatter()
code('''
#include <cassert>
#include <iostream>
#include <string>
#include "base/logging.hh"
#include "mem/ruby/protocol/${{self.c_ident}}.hh"
''')
if self.isStateDecl:
code('''
namespace gem5
{
namespace ruby
{
// Code to convert the current state to an access permission
AccessPermission ${{self.c_ident}}_to_permission(const ${{self.c_ident}}& obj)
{
switch(obj) {
''')
# For each case
code.indent()
for statePerm in self.statePermPairs:
code(' case ${{self.c_ident}}_${{statePerm[0]}}:')
code(' return AccessPermission_${{statePerm[1]}};')
code.dedent()
code ('''
default:
panic("Unknown state access permission converstion for ${{self.c_ident}}");
}
// Appease the compiler since this function has a return value
return AccessPermission_Invalid;
}
} // namespace ruby
} // namespace gem5
''')
if self.isMachineType:
for enum in self.enums.values():
if enum.primary:
code('#include "mem/ruby/protocol/${{enum.ident}}'
'_Controller.hh"')
code('#include "mem/ruby/common/MachineID.hh"')
code('''
namespace gem5
{
namespace ruby
{
// Code for output operator
::std::ostream&
operator<<(::std::ostream& out, const ${{self.c_ident}}& obj)
{
out << ${{self.c_ident}}_to_string(obj);
out << ::std::flush;
return out;
}
// Code to convert state to a string
std::string
${{self.c_ident}}_to_string(const ${{self.c_ident}}& obj)
{
switch(obj) {
''')
# For each field
code.indent()
for enum in self.enums.values():
code(' case ${{self.c_ident}}_${{enum.ident}}:')
code(' return "${{enum.ident}}";')
code.dedent()
# Trailer
code('''
default:
panic("Invalid range for type ${{self.c_ident}}");
}
// Appease the compiler since this function has a return value
return "";
}
// Code to convert from a string to the enumeration
${{self.c_ident}}
string_to_${{self.c_ident}}(const std::string& str)
{
''')
# For each field
start = ""
code.indent()
for enum in self.enums.values():
code('${start}if (str == "${{enum.ident}}") {')
code(' return ${{self.c_ident}}_${{enum.ident}};')
start = "} else "
code.dedent()
code('''
} else {
panic("Invalid string conversion for %s, type ${{self.c_ident}}", str);
}
}
// Code to increment an enumeration type
${{self.c_ident}}&
operator++(${{self.c_ident}}& e)
{
assert(e < ${{self.c_ident}}_NUM);
return e = ${{self.c_ident}}(e+1);
}
''')
# MachineType hack used to set the base level and number of
# components for each Machine
if self.isMachineType:
code('''
/** \\brief returns the base vector index for each machine type to be
* used by NetDest
*
* \\return the base vector index for each machine type to be used by NetDest
* \\see NetDest.hh
*/
int
${{self.c_ident}}_base_level(const ${{self.c_ident}}& obj)
{
switch(obj) {
''')
# For each field
code.indent()
for i,enum in enumerate(self.enums.values()):
code(' case ${{self.c_ident}}_${{enum.ident}}:')
code(' return $i;')
code.dedent()
# total num
code('''
case ${{self.c_ident}}_NUM:
return ${{len(self.enums)}};
default:
panic("Invalid range for type ${{self.c_ident}}");
}
// Appease the compiler since this function has a return value
return -1;
}
/** \\brief returns the machine type for each base vector index used by NetDest
*
* \\return the MachineType
*/
MachineType
${{self.c_ident}}_from_base_level(int type)
{
switch(type) {
''')
# For each field
code.indent()
for i,enum in enumerate(self.enums.values()):
code(' case $i:')
code(' return ${{self.c_ident}}_${{enum.ident}};')
code.dedent()
# Trailer
code('''
default:
panic("Invalid range for type ${{self.c_ident}}");
}
}
/** \\brief The return value indicates the number of components created
* before a particular machine\'s components
*
* \\return the base number of components for each machine
*/
int
${{self.c_ident}}_base_number(const ${{self.c_ident}}& obj)
{
int base = 0;
switch(obj) {
''')
# For each field
code.indent()
code(' case ${{self.c_ident}}_NUM:')
for enum in reversed(list(self.enums.values())):
# Check if there is a defined machine with this type
if enum.primary:
code(' base += ${{enum.ident}}_Controller::getNumControllers();')
else:
code(' base += 0;')
code(' [[fallthrough]];')
code(' case ${{self.c_ident}}_${{enum.ident}}:')
code(' break;')
code.dedent()
code('''
default:
panic("Invalid range for type ${{self.c_ident}}");
}
return base;
}
/** \\brief returns the total number of components for each machine
* \\return the total number of components for each machine
*/
int
${{self.c_ident}}_base_count(const ${{self.c_ident}}& obj)
{
switch(obj) {
''')
# For each field
for enum in self.enums.values():
code('case ${{self.c_ident}}_${{enum.ident}}:')
if enum.primary:
code('return ${{enum.ident}}_Controller::getNumControllers();')
else:
code('return 0;')
# total num
code('''
case ${{self.c_ident}}_NUM:
default:
panic("Invalid range for type ${{self.c_ident}}");
}
// Appease the compiler since this function has a return value
return -1;
}
''')
for enum in self.enums.values():
code('''
MachineID
get${{enum.ident}}MachineID(NodeID RubyNode)
{
MachineID mach = {MachineType_${{enum.ident}}, RubyNode};
return mach;
}
''')
code('''
} // namespace ruby
} // namespace gem5
''')
# Write the file
code.write(path, "%s.cc" % self.c_ident)
__all__ = [ "Type" ]