diff options
Diffstat (limited to 'src')
704 files changed, 168398 insertions, 0 deletions
diff --git a/src/Doxyfile b/src/Doxyfile new file mode 100644 index 000000000..ed4245dd2 --- /dev/null +++ b/src/Doxyfile @@ -0,0 +1,1127 @@ +# Doxyfile 1.3.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = M5 + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doxygen + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en +# (Japanese with English messages), Korean, Korean-en, Norwegian, Polish, Portuguese, +# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is used +# as the annotated text. Otherwise, the brief description is used as-is. If left +# blank, the following values are used ("$name" is automatically replaced with the +# name of the entity): "The $name class" "The $name widget" "The $name file" +# "is" "provides" "specifies" "contains" "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = YES + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = . + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl *.cs *.php *.php3 *.inc + +FILE_PATTERNS = *.c \ + *.cc \ + *.h \ + *.hh \ + *.doxygen + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = build \ + configs \ + setup \ + PENDING \ + RESYNC + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = YES + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = */BitKeeper/* \ + */Attic/* \ + */SCCS/* + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 3 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = doxygen/footer.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse the +# parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superseded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes that +# lay further from the root node will be omitted. Note that setting this option to +# 1 or 2 may greatly reduce the computation time needed for large code bases. Also +# note that a graph may be further truncated if the graph's image dimensions are +# not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH and MAX_DOT_GRAPH_HEIGHT). +# If 0 is used for the depth value (the default), the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/src/SConscript b/src/SConscript new file mode 100644 index 000000000..9f88bbeea --- /dev/null +++ b/src/SConscript @@ -0,0 +1,422 @@ +# -*- mode:python -*- + +# Copyright (c) 2004-2005 The Regents of The University of Michigan +# 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. +# +# Authors: Steve Reinhardt + +import os +import sys +from os.path import isdir + +# This file defines how to build a particular configuration of M5 +# based on variable settings in the 'env' build environment. + +# Import build environment variable from SConstruct. +Import('env') + +################################################### +# +# Define needed sources. +# +################################################### + +# Base sources used by all configurations. + +base_sources = Split(''' + base/annotate.cc + base/circlebuf.cc + base/cprintf.cc + base/fast_alloc.cc + base/fifo_buffer.cc + base/hostinfo.cc + base/hybrid_pred.cc + base/inifile.cc + base/intmath.cc + base/match.cc + base/misc.cc + base/output.cc + base/pollevent.cc + base/range.cc + base/random.cc + base/sat_counter.cc + base/socket.cc + base/statistics.cc + base/str.cc + base/time.cc + base/trace.cc + base/traceflags.cc + base/userinfo.cc + base/compression/lzss_compression.cc + base/loader/aout_object.cc + base/loader/ecoff_object.cc + base/loader/elf_object.cc + base/loader/object_file.cc + base/loader/symtab.cc + base/stats/events.cc + base/stats/statdb.cc + base/stats/visit.cc + base/stats/text.cc + + cpu/activity.cc + cpu/base.cc + cpu/cpuevent.cc + cpu/exetrace.cc + cpu/func_unit.cc + cpu/op_class.cc + cpu/pc_event.cc + cpu/quiesce_event.cc + cpu/static_inst.cc + cpu/simple_thread.cc + cpu/thread_state.cc + + mem/bridge.cc + mem/bus.cc + mem/dram.cc + mem/mem_object.cc + mem/packet.cc + mem/physical.cc + mem/port.cc + mem/tport.cc + + mem/cache/base_cache.cc + mem/cache/cache.cc + mem/cache/coherence/coherence_protocol.cc + mem/cache/coherence/uni_coherence.cc + mem/cache/miss/blocking_buffer.cc + mem/cache/miss/miss_queue.cc + mem/cache/miss/mshr.cc + mem/cache/miss/mshr_queue.cc + mem/cache/prefetch/base_prefetcher.cc + mem/cache/prefetch/ghb_prefetcher.cc + mem/cache/prefetch/prefetcher.cc + mem/cache/prefetch/stride_prefetcher.cc + mem/cache/prefetch/tagged_prefetcher.cc + mem/cache/tags/base_tags.cc + mem/cache/tags/cache_tags.cc + mem/cache/tags/fa_lru.cc + mem/cache/tags/iic.cc + mem/cache/tags/lru.cc + mem/cache/tags/repl/gen.cc + mem/cache/tags/repl/repl.cc + mem/cache/tags/split.cc + mem/cache/tags/split_lifo.cc + mem/cache/tags/split_lru.cc + + mem/cache/cache_builder.cc + + sim/builder.cc + sim/debug.cc + sim/eventq.cc + sim/faults.cc + sim/main.cc + python/swig/cc_main_wrap.cc + sim/param.cc + sim/root.cc + sim/serialize.cc + sim/sim_events.cc + sim/sim_object.cc + sim/startup.cc + sim/stat_context.cc + sim/stat_control.cc + sim/system.cc + sim/trace_context.cc + ''') + +# Old FullCPU sources +full_cpu_sources = Split(''' + encumbered/cpu/full/bpred.cc + encumbered/cpu/full/commit.cc + encumbered/cpu/full/cpu.cc + encumbered/cpu/full/create_vector.cc + encumbered/cpu/full/cv_spec_state.cc + encumbered/cpu/full/dd_queue.cc + encumbered/cpu/full/dep_link.cc + encumbered/cpu/full/dispatch.cc + encumbered/cpu/full/dyn_inst.cc + encumbered/cpu/full/execute.cc + encumbered/cpu/full/fetch.cc + encumbered/cpu/full/floss_reasons.cc + encumbered/cpu/full/fu_pool.cc + encumbered/cpu/full/inst_fifo.cc + encumbered/cpu/full/instpipe.cc + encumbered/cpu/full/issue.cc + encumbered/cpu/full/ls_queue.cc + encumbered/cpu/full/machine_queue.cc + encumbered/cpu/full/pipetrace.cc + encumbered/cpu/full/readyq.cc + encumbered/cpu/full/reg_info.cc + encumbered/cpu/full/rob_station.cc + encumbered/cpu/full/spec_memory.cc + encumbered/cpu/full/spec_state.cc + encumbered/cpu/full/storebuffer.cc + encumbered/cpu/full/writeback.cc + encumbered/cpu/full/iq/iq_station.cc + encumbered/cpu/full/iq/iqueue.cc + encumbered/cpu/full/iq/segmented/chain_info.cc + encumbered/cpu/full/iq/segmented/chain_wire.cc + encumbered/cpu/full/iq/segmented/iq_seg.cc + encumbered/cpu/full/iq/segmented/iq_segmented.cc + encumbered/cpu/full/iq/segmented/seg_chain.cc + encumbered/cpu/full/iq/seznec/iq_seznec.cc + encumbered/cpu/full/iq/standard/iq_standard.cc + ''') + +trace_reader_sources = Split(''' + cpu/trace/reader/mem_trace_reader.cc + cpu/trace/reader/ibm_reader.cc + cpu/trace/reader/itx_reader.cc + cpu/trace/reader/m5_reader.cc + cpu/trace/opt_cpu.cc + cpu/trace/trace_cpu.cc + ''') + + + +# MySql sources +mysql_sources = Split(''' + base/mysql.cc + base/stats/mysql.cc + ''') + +# Full-system sources +full_system_sources = Split(''' + base/crc.cc + base/inet.cc + base/remote_gdb.cc + + cpu/intr_control.cc + cpu/profile.cc + + dev/alpha_console.cc + dev/baddev.cc + dev/disk_image.cc + dev/etherbus.cc + dev/etherdump.cc + dev/etherint.cc + dev/etherlink.cc + dev/etherpkt.cc + dev/ethertap.cc + dev/i8254xGBe.cc + dev/ide_ctrl.cc + dev/ide_disk.cc + dev/io_device.cc + dev/isa_fake.cc + dev/ns_gige.cc + dev/pciconfigall.cc + dev/pcidev.cc + dev/pcifake.cc + dev/pktfifo.cc + dev/platform.cc + dev/simconsole.cc + dev/simple_disk.cc + dev/tsunami.cc + dev/tsunami_cchip.cc + dev/tsunami_io.cc + dev/tsunami_fake.cc + dev/tsunami_pchip.cc + + dev/uart.cc + dev/uart8250.cc + + kern/kernel_stats.cc + kern/system_events.cc + kern/linux/events.cc + kern/linux/linux_syscalls.cc + kern/linux/printk.cc + + mem/vport.cc + + sim/pseudo_inst.cc + ''') + #dev/sinic.cc + + +if env['TARGET_ISA'] == 'alpha': + full_system_sources += Split(''' + kern/tru64/dump_mbuf.cc + kern/tru64/printf.cc + kern/tru64/tru64_events.cc + kern/tru64/tru64_syscalls.cc + ''') + +# turbolaser encumbered sources +turbolaser_sources = Split(''' + encumbered/dev/dma.cc + encumbered/dev/etherdev.cc + encumbered/dev/scsi.cc + encumbered/dev/scsi_ctrl.cc + encumbered/dev/scsi_disk.cc + encumbered/dev/scsi_none.cc + encumbered/dev/tlaser_clock.cc + encumbered/dev/tlaser_ipi.cc + encumbered/dev/tlaser_mbox.cc + encumbered/dev/tlaser_mc146818.cc + encumbered/dev/tlaser_node.cc + encumbered/dev/tlaser_pcia.cc + encumbered/dev/tlaser_pcidev.cc + encumbered/dev/tlaser_serial.cc + encumbered/dev/turbolaser.cc + encumbered/dev/uart8530.cc + ''') + +# Syscall emulation (non-full-system) sources +syscall_emulation_sources = Split(''' + mem/translating_port.cc + mem/page_table.cc + sim/process.cc + sim/syscall_emul.cc + ''') + +#if env['TARGET_ISA'] == 'alpha': +# syscall_emulation_sources += Split(''' +# kern/tru64/tru64.cc +# ''') + +alpha_eio_sources = Split(''' + encumbered/eio/exolex.cc + encumbered/eio/libexo.cc + encumbered/eio/eio.cc + ''') + +if env['TARGET_ISA'] == 'alpha': + syscall_emulation_sources += alpha_eio_sources + +memtest_sources = Split(''' + cpu/memtest/memtest.cc + ''') + +# Include file paths are rooted in this directory. SCons will +# automatically expand '.' to refer to both the source directory and +# the corresponding build directory to pick up generated include +# files. +env.Append(CPPPATH=Dir('.')) + +# Add a flag defining what THE_ISA should be for all compilation +env.Append(CPPDEFINES=[('THE_ISA','%s_ISA' % env['TARGET_ISA'].upper())]) + +arch_sources = SConscript('arch/SConscript', exports = 'env') + +cpu_sources = SConscript('cpu/SConscript', exports = 'env') + +# This is outside of cpu/SConscript since the source directory isn't +# underneath 'cpu'. +if 'FullCPU' in env['CPU_MODELS']: + cpu_sources += full_cpu_sources + +# Set up complete list of sources based on configuration. +sources = base_sources + arch_sources + cpu_sources + +if env['FULL_SYSTEM']: + sources += full_system_sources + if env['ALPHA_TLASER']: + sources += turbolaser_sources +else: + sources += syscall_emulation_sources + +if env['USE_MYSQL']: + sources += mysql_sources + +for opt in env.ExportOptions: + env.ConfigFile(opt) + +################################################### +# +# Special build rules. +# +################################################### + +# base/traceflags.{cc,hh} are generated from base/traceflags.py. +# $TARGET.base will expand to "<build-dir>/base/traceflags". +env.Command(Split('base/traceflags.hh base/traceflags.cc'), + 'base/traceflags.py', + 'python $SOURCE $TARGET.base') + +SConscript('python/SConscript', exports = ['env']) + +# This function adds the specified sources to the given build +# environment, and returns a list of all the corresponding SCons +# Object nodes (including an extra one for date.cc). We explicitly +# add the Object nodes so we can set up special dependencies for +# date.cc. +def make_objs(sources, env): + objs = [env.Object(s) for s in sources] + # make date.cc depend on all other objects so it always gets + # recompiled whenever anything else does + date_obj = env.Object('base/date.cc') + env.Depends(date_obj, objs) + objs.append(date_obj) + return objs + +################################################### +# +# Define binaries. Each different build type (debug, opt, etc.) gets +# a slightly different build environment. +# +################################################### + +# List of constructed environments to pass back to SConstruct +envList = [] + +# Function to create a new build environment as clone of current +# environment 'env' with modified object suffix and optional stripped +# binary. Additional keyword arguments are appended to corresponding +# build environment vars. +def makeEnv(label, objsfx, strip = False, **kwargs): + newEnv = env.Copy(OBJSUFFIX=objsfx) + newEnv.Label = label + newEnv.Append(**kwargs) + exe = 'm5.' + label # final executable + bin = exe + '.bin' # executable w/o appended Python zip archive + newEnv.Program(bin, make_objs(sources, newEnv)) + if strip: + stripped_bin = bin + '.stripped' + newEnv.Command(stripped_bin, bin, 'strip $SOURCE -o $TARGET') + bin = stripped_bin + targets = newEnv.Concat(exe, [bin, 'python/m5py.zip']) + newEnv.M5Binary = targets[0] + envList.append(newEnv) + +# Debug binary +makeEnv('debug', '.do', + CCFLAGS = Split('-g3 -gdwarf-2 -O0'), + CPPDEFINES = 'DEBUG') + +# Optimized binary +makeEnv('opt', '.o', + CCFLAGS = Split('-g -O3')) + +# "Fast" binary +makeEnv('fast', '.fo', strip = True, + CCFLAGS = Split('-O3'), + CPPDEFINES = 'NDEBUG') + +# Profiled binary +makeEnv('prof', '.po', + CCFLAGS = Split('-O3 -g -pg'), + LINKFLAGS = '-pg') + +Return('envList') diff --git a/src/arch/SConscript b/src/arch/SConscript new file mode 100644 index 000000000..59cea6211 --- /dev/null +++ b/src/arch/SConscript @@ -0,0 +1,164 @@ +# -*- mode:python -*- + +# Copyright (c) 2006 The Regents of The University of Michigan +# 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. +# +# Authors: Steve Reinhardt + +import os.path, sys + +# Import build environment variable from SConstruct. +Import('env') + +# Right now there are no source files immediately in this directory +sources = [] + +################################################################# +# +# ISA "switch header" generation. +# +# Auto-generate arch headers that include the right ISA-specific +# header based on the setting of THE_ISA preprocessor variable. +# +################################################################# + +# List of headers to generate +isa_switch_hdrs = Split(''' + arguments.hh + faults.hh + isa_traits.hh + process.hh + regfile.hh + stacktrace.hh + syscallreturn.hh + tlb.hh + types.hh + utility.hh + vtophys.hh + ''') + +# Generate the header. target[0] is the full path of the output +# header to generate. 'source' is a dummy variable, since we get the +# list of ISAs from env['ALL_ISA_LIST']. +def gen_switch_hdr(target, source, env): + fname = str(target[0]) + basename = os.path.basename(fname) + f = open(fname, 'w') + f.write('#include "arch/isa_specific.hh"\n') + cond = '#if' + for isa in env['ALL_ISA_LIST']: + f.write('%s THE_ISA == %s_ISA\n#include "arch/%s/%s"\n' + % (cond, isa.upper(), isa, basename)) + cond = '#elif' + f.write('#else\n#error "THE_ISA not set"\n#endif\n') + f.close() + return 0 + +# String to print when generating header +def gen_switch_hdr_string(target, source, env): + return "Generating ISA switch header " + str(target[0]) + +# Build SCons Action object. 'varlist' specifies env vars that this +# action depends on; when env['ALL_ISA_LIST'] changes these actions +# should get re-executed. +switch_hdr_action = Action(gen_switch_hdr, gen_switch_hdr_string, + varlist=['ALL_ISA_LIST']) + +# Instantiate actions for each header +for hdr in isa_switch_hdrs: + env.Command(hdr, [], switch_hdr_action) + +################################################################# +# +# Include architecture-specific files. +# +################################################################# + +# +# Build a SCons scanner for ISA files +# +import SCons.Scanner + +isa_scanner = SCons.Scanner.Classic("ISAScan", + [".isa", ".ISA"], + "SRCDIR", + r'^\s*##include\s+"([\w/.-]*)"') + +env.Append(SCANNERS = isa_scanner) + +# +# Now create a Builder object that uses isa_parser.py to generate C++ +# output from the ISA description (*.isa) files. +# + +# Convert to File node to fix path +isa_parser = File('isa_parser.py') +cpu_models_file = File('../cpu/cpu_models.py') + +# This sucks in the defintions of the CpuModel objects. +execfile(cpu_models_file.srcnode().abspath) + +# Several files are generated from the ISA description. +# We always get the basic decoder and header file. +isa_desc_gen_files = Split('decoder.cc decoder.hh') +# We also get an execute file for each selected CPU model. +isa_desc_gen_files += [CpuModel.dict[cpu].filename + for cpu in env['CPU_MODELS']] + +# Also include the CheckerCPU as one of the models if it is being +# enabled via command line. +if env['USE_CHECKER']: + isa_desc_gen_files += [CpuModel.dict['CheckerCPU'].filename] + +# The emitter patches up the sources & targets to include the +# autogenerated files as targets and isa parser itself as a source. +def isa_desc_emitter(target, source, env): + return (isa_desc_gen_files, [isa_parser, cpu_models_file] + source) + +# Pieces are in place, so create the builder. +python = sys.executable # use same Python binary used to run scons + +# Also include the CheckerCPU as one of the models if it is being +# enabled via command line. +if env['USE_CHECKER']: + isa_desc_builder = Builder(action=python + ' $SOURCES $TARGET.dir $CPU_MODELS CheckerCPU', + emitter = isa_desc_emitter) +else: + isa_desc_builder = Builder(action=python + ' $SOURCES $TARGET.dir $CPU_MODELS', + emitter = isa_desc_emitter) + +env.Append(BUILDERS = { 'ISADesc' : isa_desc_builder }) + +# +# Now include other ISA-specific sources from the ISA subdirectories. +# + +isa = env['TARGET_ISA'] # someday this may be a list of ISAs + +# Let the target architecture define what additional sources it needs +sources += SConscript(os.path.join(isa, 'SConscript'), exports = 'env') + +Return('sources') diff --git a/src/arch/alpha/SConscript b/src/arch/alpha/SConscript new file mode 100644 index 000000000..216c88cc7 --- /dev/null +++ b/src/arch/alpha/SConscript @@ -0,0 +1,96 @@ +# -*- mode:python -*- + +# Copyright (c) 2004-2005 The Regents of The University of Michigan +# 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. +# +# Authors: Gabe Black +# Steve Reinhardt + +import os +import sys +from os.path import isdir + +# This file defines how to build a particular configuration of M5 +# based on variable settings in the 'env' build environment. + +# Import build environment variable from SConstruct. +Import('env') + +################################################### +# +# Define needed sources. +# +################################################### + +# Base sources used by all configurations. +base_sources = Split(''' + faults.cc + isa_traits.cc + ''') + +# Full-system sources +full_system_sources = Split(''' + tlb.cc + arguments.cc + ev5.cc + osfpal.cc + stacktrace.cc + vtophys.cc + system.cc + freebsd/system.cc + linux/system.cc + tru64/system.cc + ''') + + +# Syscall emulation (non-full-system) sources +syscall_emulation_sources = Split(''' + linux/linux.cc + linux/process.cc + tru64/tru64.cc + tru64/process.cc + process.cc + ''') + +# Set up complete list of sources based on configuration. +sources = base_sources + +if env['FULL_SYSTEM']: + sources += full_system_sources +else: + sources += syscall_emulation_sources + +# Convert file names to SCons File objects. This takes care of the +# path relative to the top of the directory tree. +sources = [File(s) for s in sources] + +# Add in files generated by the ISA description. +isa_desc_files = env.ISADesc('isa/main.isa') +# Only non-header files need to be compiled. +isa_desc_sources = [f for f in isa_desc_files if not f.path.endswith('.hh')] +sources += isa_desc_sources + +Return('sources') diff --git a/src/arch/alpha/aout_machdep.h b/src/arch/alpha/aout_machdep.h new file mode 100644 index 000000000..58991256a --- /dev/null +++ b/src/arch/alpha/aout_machdep.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +#ifndef __AOUT_MACHDEP_H__ +#define __AOUT_MACHDEP_H__ + +/// +/// Funky Alpha 64-bit a.out header used for PAL code. +/// +struct aout_exechdr { + uint16_t magic; ///< magic number + uint16_t vstamp; ///< version stamp? + uint16_t bldrev; ///< ??? + uint16_t padcell; ///< padding + uint64_t tsize; ///< text segment size + uint64_t dsize; ///< data segment size + uint64_t bsize; ///< bss segment size + uint64_t entry; ///< entry point + uint64_t text_start; ///< text base address + uint64_t data_start; ///< data base address + uint64_t bss_start; ///< bss base address + uint32_t gprmask; ///< GPR mask (unused, AFAIK) + uint32_t fprmask; ///< FPR mask (unused, AFAIK) + uint64_t gp_value; ///< global pointer reg value +}; + +#define AOUT_LDPGSZ 8192 + +#define N_GETMAGIC(ex) ((ex).magic) + +#define N_BADMAX + +#define N_TXTADDR(ex) ((ex).text_start) +#define N_DATADDR(ex) ((ex).data_start) +#define N_BSSADDR(ex) ((ex).bss_start) + +#define N_TXTOFF(ex) \ + (N_GETMAGIC(ex) == ZMAGIC ? 0 : sizeof(struct aout_exechdr)) + +#define N_DATOFF(ex) N_ALIGN(ex, N_TXTOFF(ex) + (ex).tsize) + +#endif /* !__AOUT_MACHDEP_H__*/ diff --git a/src/arch/alpha/arguments.cc b/src/arch/alpha/arguments.cc new file mode 100644 index 000000000..9f9002003 --- /dev/null +++ b/src/arch/alpha/arguments.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include "arch/alpha/arguments.hh" +#include "arch/alpha/vtophys.hh" +#include "cpu/thread_context.hh" +#include "mem/vport.hh" + +using namespace AlphaISA; + +AlphaArguments::Data::~Data() +{ + while (!data.empty()) { + delete [] data.front(); + data.pop_front(); + } +} + +char * +AlphaArguments::Data::alloc(size_t size) +{ + char *buf = new char[size]; + data.push_back(buf); + return buf; +} + +uint64_t +AlphaArguments::getArg(bool fp) +{ + if (number < 6) { + if (fp) + return tc->readFloatRegBits(16 + number); + else + return tc->readIntReg(16 + number); + } else { + Addr sp = tc->readIntReg(30); + VirtualPort *vp = tc->getVirtPort(tc); + uint64_t arg = vp->read<uint64_t>(sp + (number-6) * sizeof(uint64_t)); + tc->delVirtPort(vp); + return arg; + } +} + diff --git a/src/arch/alpha/arguments.hh b/src/arch/alpha/arguments.hh new file mode 100644 index 000000000..d977d48d6 --- /dev/null +++ b/src/arch/alpha/arguments.hh @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __ARCH_ALPHA_ARGUMENTS_HH__ +#define __ARCH_ALPHA_ARGUMENTS_HH__ + +#include <assert.h> + +#include "arch/alpha/vtophys.hh" +#include "base/refcnt.hh" +#include "sim/host.hh" + +class ThreadContext; + +namespace AlphaISA { + +class AlphaArguments +{ + protected: + ThreadContext *tc; + int number; + uint64_t getArg(bool fp = false); + + protected: + class Data : public RefCounted + { + public: + Data(){} + ~Data(); + + private: + std::list<char *> data; + + public: + char *alloc(size_t size); + }; + + RefCountingPtr<Data> data; + + public: + AlphaArguments(ThreadContext *ctx, int n = 0) + : tc(ctx), number(n), data(NULL) + { assert(number >= 0); data = new Data;} + AlphaArguments(const AlphaArguments &args) + : tc(args.tc), number(args.number), data(args.data) {} + ~AlphaArguments() {} + + ThreadContext *getThreadContext() const { return tc; } + + const AlphaArguments &operator=(const AlphaArguments &args) { + tc = args.tc; + number = args.number; + data = args.data; + return *this; + } + + AlphaArguments &operator++() { + ++number; + assert(number >= 0); + return *this; + } + + AlphaArguments operator++(int) { + AlphaArguments args = *this; + ++number; + assert(number >= 0); + return args; + } + + AlphaArguments &operator--() { + --number; + assert(number >= 0); + return *this; + } + + AlphaArguments operator--(int) { + AlphaArguments args = *this; + --number; + assert(number >= 0); + return args; + } + + const AlphaArguments &operator+=(int index) { + number += index; + assert(number >= 0); + return *this; + } + + const AlphaArguments &operator-=(int index) { + number -= index; + assert(number >= 0); + return *this; + } + + AlphaArguments operator[](int index) { + return AlphaArguments(tc, index); + } + + template <class T> + operator T() { + assert(sizeof(T) <= sizeof(uint64_t)); + T data = static_cast<T>(getArg()); + return data; + } + + template <class T> + operator T *() { + T *buf = (T *)data->alloc(sizeof(T)); + CopyData(tc, buf, getArg(), sizeof(T)); + return buf; + } + + operator char *() { + char *buf = data->alloc(2048); + CopyStringOut(tc, buf, getArg(), 2048); + return buf; + } +}; + +}; // namespace AlphaISA + +#endif // __ARCH_ALPHA_ARGUMENTS_HH__ diff --git a/src/arch/alpha/ecoff_machdep.h b/src/arch/alpha/ecoff_machdep.h new file mode 100644 index 000000000..9341b8ff7 --- /dev/null +++ b/src/arch/alpha/ecoff_machdep.h @@ -0,0 +1,73 @@ +/* + * Taken from NetBSD arch/alpha/ecoff_machdep.h + */ + +/* $NetBSD: ecoff_machdep.h,v 1.5 1999/04/27 02:32:33 cgd Exp $ */ + +/* + * Copyright (c) 1994 Adam Glass + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Glass. + * 4. The name of the Author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Adam Glass ``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 Adam Glass 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. + */ + +// +// Define COFF/ECOFF integer type sizes +// +typedef int16_t coff_short; +typedef uint16_t coff_ushort; +typedef int32_t coff_int; +typedef uint32_t coff_uint; +typedef int64_t coff_long; +typedef uint64_t coff_ulong; +typedef uint64_t coff_addr; + +#define ECOFF_LDPGSZ 4096 + +#define ECOFF_PAD \ + coff_ushort bldrev; /* XXX */ + +#define ECOFF_MACHDEP \ + coff_uint gprmask; \ + coff_uint fprmask; \ + coff_ulong gp_value + +#define ECOFF_MAGIC_ALPHA 0603 +#define ECOFF_MAGIC_NETBSD_ALPHA 0605 +#define ECOFF_BADMAG(ep) \ + ((ep)->f.f_magic != ECOFF_MAGIC_ALPHA && \ + (ep)->f.f_magic != ECOFF_MAGIC_NETBSD_ALPHA) + +#define ECOFF_FLAG_EXEC 0002 +#define ECOFF_SEGMENT_ALIGNMENT(ep) \ + (((ep)->f.f_flags & ECOFF_FLAG_EXEC) == 0 ? 8 : 16) + +#define ECOFF_FLAG_OBJECT_TYPE_MASK 0x3000 +#define ECOFF_OBJECT_TYPE_NO_SHARED 0x1000 +#define ECOFF_OBJECT_TYPE_SHARABLE 0x2000 +#define ECOFF_OBJECT_TYPE_CALL_SHARED 0x3000 + diff --git a/src/arch/alpha/ev5.cc b/src/arch/alpha/ev5.cc new file mode 100644 index 000000000..7595423c3 --- /dev/null +++ b/src/arch/alpha/ev5.cc @@ -0,0 +1,594 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +#include "arch/alpha/faults.hh" +#include "arch/alpha/isa_traits.hh" +#include "arch/alpha/osfpal.hh" +#include "arch/alpha/tlb.hh" +#include "base/kgdb.h" +#include "base/remote_gdb.hh" +#include "base/stats/events.hh" +#include "config/full_system.hh" +#include "cpu/base.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" +#include "kern/kernel_stats.hh" +#include "sim/debug.hh" +#include "sim/sim_exit.hh" + +#if FULL_SYSTEM + +using namespace EV5; + +//////////////////////////////////////////////////////////////////////// +// +// Machine dependent functions +// +void +AlphaISA::initCPU(ThreadContext *tc, int cpuId) +{ + initIPRs(tc, cpuId); + + tc->setIntReg(16, cpuId); + tc->setIntReg(0, cpuId); + + AlphaFault *reset = new ResetFault; + + tc->setPC(tc->readMiscReg(IPR_PAL_BASE) + reset->vect()); + tc->setNextPC(tc->readPC() + sizeof(MachInst)); + + delete reset; +} + +//////////////////////////////////////////////////////////////////////// +// +// +// +void +AlphaISA::initIPRs(ThreadContext *tc, int cpuId) +{ + for (int i = 0; i < NumInternalProcRegs; ++i) { + tc->setMiscReg(i, 0); + } + + tc->setMiscReg(IPR_PAL_BASE, PalBase); + tc->setMiscReg(IPR_MCSR, 0x6); + tc->setMiscReg(IPR_PALtemp16, cpuId); +} + + +template <class CPU> +void +AlphaISA::processInterrupts(CPU *cpu) +{ + //Check if there are any outstanding interrupts + //Handle the interrupts + int ipl = 0; + int summary = 0; + + cpu->checkInterrupts = false; + + if (cpu->readMiscReg(IPR_ASTRR)) + panic("asynchronous traps not implemented\n"); + + if (cpu->readMiscReg(IPR_SIRR)) { + for (int i = INTLEVEL_SOFTWARE_MIN; + i < INTLEVEL_SOFTWARE_MAX; i++) { + if (cpu->readMiscReg(IPR_SIRR) & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; + summary |= (ULL(1) << i); + } + } + } + + uint64_t interrupts = cpu->intr_status(); + + if (interrupts) { + for (int i = INTLEVEL_EXTERNAL_MIN; + i < INTLEVEL_EXTERNAL_MAX; i++) { + if (interrupts & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = i; + summary |= (ULL(1) << i); + } + } + } + + if (ipl && ipl > cpu->readMiscReg(IPR_IPLR)) { + cpu->setMiscReg(IPR_ISR, summary); + cpu->setMiscReg(IPR_INTID, ipl); + cpu->trap(new InterruptFault); + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + cpu->readMiscReg(IPR_IPLR), ipl, summary); + } + +} + +template <class CPU> +void +AlphaISA::zeroRegisters(CPU *cpu) +{ + // Insure ISA semantics + // (no longer very clean due to the change in setIntReg() in the + // cpu model. Consider changing later.) + cpu->thread->setIntReg(ZeroReg, 0); + cpu->thread->setFloatReg(ZeroReg, 0.0); +} + +Fault +SimpleThread::hwrei() +{ + if (!inPalMode()) + return new UnimplementedOpcodeFault; + + setNextPC(readMiscReg(AlphaISA::IPR_EXC_ADDR)); + + if (!misspeculating()) { + if (kernelStats) + kernelStats->hwrei(); + + cpu->checkInterrupts = true; + } + + // FIXME: XXX check for interrupts? XXX + return NoFault; +} + +int +AlphaISA::MiscRegFile::getInstAsid() +{ + return EV5::ITB_ASN_ASN(ipr[IPR_ITB_ASN]); +} + +int +AlphaISA::MiscRegFile::getDataAsid() +{ + return EV5::DTB_ASN_ASN(ipr[IPR_DTB_ASN]); +} + +AlphaISA::MiscReg +AlphaISA::MiscRegFile::readIpr(int idx, Fault &fault, ThreadContext *tc) +{ + uint64_t retval = 0; // return value, default 0 + + switch (idx) { + case AlphaISA::IPR_PALtemp0: + case AlphaISA::IPR_PALtemp1: + case AlphaISA::IPR_PALtemp2: + case AlphaISA::IPR_PALtemp3: + case AlphaISA::IPR_PALtemp4: + case AlphaISA::IPR_PALtemp5: + case AlphaISA::IPR_PALtemp6: + case AlphaISA::IPR_PALtemp7: + case AlphaISA::IPR_PALtemp8: + case AlphaISA::IPR_PALtemp9: + case AlphaISA::IPR_PALtemp10: + case AlphaISA::IPR_PALtemp11: + case AlphaISA::IPR_PALtemp12: + case AlphaISA::IPR_PALtemp13: + case AlphaISA::IPR_PALtemp14: + case AlphaISA::IPR_PALtemp15: + case AlphaISA::IPR_PALtemp16: + case AlphaISA::IPR_PALtemp17: + case AlphaISA::IPR_PALtemp18: + case AlphaISA::IPR_PALtemp19: + case AlphaISA::IPR_PALtemp20: + case AlphaISA::IPR_PALtemp21: + case AlphaISA::IPR_PALtemp22: + case AlphaISA::IPR_PALtemp23: + case AlphaISA::IPR_PAL_BASE: + + case AlphaISA::IPR_IVPTBR: + case AlphaISA::IPR_DC_MODE: + case AlphaISA::IPR_MAF_MODE: + case AlphaISA::IPR_ISR: + case AlphaISA::IPR_EXC_ADDR: + case AlphaISA::IPR_IC_PERR_STAT: + case AlphaISA::IPR_DC_PERR_STAT: + case AlphaISA::IPR_MCSR: + case AlphaISA::IPR_ASTRR: + case AlphaISA::IPR_ASTER: + case AlphaISA::IPR_SIRR: + case AlphaISA::IPR_ICSR: + case AlphaISA::IPR_ICM: + case AlphaISA::IPR_DTB_CM: + case AlphaISA::IPR_IPLR: + case AlphaISA::IPR_INTID: + case AlphaISA::IPR_PMCTR: + // no side-effect + retval = ipr[idx]; + break; + + case AlphaISA::IPR_CC: + retval |= ipr[idx] & ULL(0xffffffff00000000); + retval |= tc->getCpuPtr()->curCycle() & ULL(0x00000000ffffffff); + break; + + case AlphaISA::IPR_VA: + retval = ipr[idx]; + break; + + case AlphaISA::IPR_VA_FORM: + case AlphaISA::IPR_MM_STAT: + case AlphaISA::IPR_IFAULT_VA_FORM: + case AlphaISA::IPR_EXC_MASK: + case AlphaISA::IPR_EXC_SUM: + retval = ipr[idx]; + break; + + case AlphaISA::IPR_DTB_PTE: + { + AlphaISA::PTE &pte = tc->getDTBPtr()->index(!tc->misspeculating()); + + retval |= ((u_int64_t)pte.ppn & ULL(0x7ffffff)) << 32; + retval |= ((u_int64_t)pte.xre & ULL(0xf)) << 8; + retval |= ((u_int64_t)pte.xwe & ULL(0xf)) << 12; + retval |= ((u_int64_t)pte.fonr & ULL(0x1)) << 1; + retval |= ((u_int64_t)pte.fonw & ULL(0x1))<< 2; + retval |= ((u_int64_t)pte.asma & ULL(0x1)) << 4; + retval |= ((u_int64_t)pte.asn & ULL(0x7f)) << 57; + } + break; + + // write only registers + case AlphaISA::IPR_HWINT_CLR: + case AlphaISA::IPR_SL_XMIT: + case AlphaISA::IPR_DC_FLUSH: + case AlphaISA::IPR_IC_FLUSH: + case AlphaISA::IPR_ALT_MODE: + case AlphaISA::IPR_DTB_IA: + case AlphaISA::IPR_DTB_IAP: + case AlphaISA::IPR_ITB_IA: + case AlphaISA::IPR_ITB_IAP: + fault = new UnimplementedOpcodeFault; + break; + + default: + // invalid IPR + fault = new UnimplementedOpcodeFault; + break; + } + + return retval; +} + +#ifdef DEBUG +// Cause the simulator to break when changing to the following IPL +int break_ipl = -1; +#endif + +Fault +AlphaISA::MiscRegFile::setIpr(int idx, uint64_t val, ThreadContext *tc) +{ + uint64_t old; + + if (tc->misspeculating()) + return NoFault; + + switch (idx) { + case AlphaISA::IPR_PALtemp0: + case AlphaISA::IPR_PALtemp1: + case AlphaISA::IPR_PALtemp2: + case AlphaISA::IPR_PALtemp3: + case AlphaISA::IPR_PALtemp4: + case AlphaISA::IPR_PALtemp5: + case AlphaISA::IPR_PALtemp6: + case AlphaISA::IPR_PALtemp7: + case AlphaISA::IPR_PALtemp8: + case AlphaISA::IPR_PALtemp9: + case AlphaISA::IPR_PALtemp10: + case AlphaISA::IPR_PALtemp11: + case AlphaISA::IPR_PALtemp12: + case AlphaISA::IPR_PALtemp13: + case AlphaISA::IPR_PALtemp14: + case AlphaISA::IPR_PALtemp15: + case AlphaISA::IPR_PALtemp16: + case AlphaISA::IPR_PALtemp17: + case AlphaISA::IPR_PALtemp18: + case AlphaISA::IPR_PALtemp19: + case AlphaISA::IPR_PALtemp20: + case AlphaISA::IPR_PALtemp21: + case AlphaISA::IPR_PALtemp22: + case AlphaISA::IPR_PAL_BASE: + case AlphaISA::IPR_IC_PERR_STAT: + case AlphaISA::IPR_DC_PERR_STAT: + case AlphaISA::IPR_PMCTR: + // write entire quad w/ no side-effect + ipr[idx] = val; + break; + + case AlphaISA::IPR_CC_CTL: + // This IPR resets the cycle counter. We assume this only + // happens once... let's verify that. + assert(ipr[idx] == 0); + ipr[idx] = 1; + break; + + case AlphaISA::IPR_CC: + // This IPR only writes the upper 64 bits. It's ok to write + // all 64 here since we mask out the lower 32 in rpcc (see + // isa_desc). + ipr[idx] = val; + break; + + case AlphaISA::IPR_PALtemp23: + // write entire quad w/ no side-effect + old = ipr[idx]; + ipr[idx] = val; + if (tc->getKernelStats()) + tc->getKernelStats()->context(old, val, tc); + break; + + case AlphaISA::IPR_DTB_PTE: + // write entire quad w/ no side-effect, tag is forthcoming + ipr[idx] = val; + break; + + case AlphaISA::IPR_EXC_ADDR: + // second least significant bit in PC is always zero + ipr[idx] = val & ~2; + break; + + case AlphaISA::IPR_ASTRR: + case AlphaISA::IPR_ASTER: + // only write least significant four bits - privilege mask + ipr[idx] = val & 0xf; + break; + + case AlphaISA::IPR_IPLR: +#ifdef DEBUG + if (break_ipl != -1 && break_ipl == (val & 0x1f)) + debug_break(); +#endif + + // only write least significant five bits - interrupt level + ipr[idx] = val & 0x1f; + if (tc->getKernelStats()) + tc->getKernelStats()->swpipl(ipr[idx]); + break; + + case AlphaISA::IPR_DTB_CM: + if (val & 0x18) { + if (tc->getKernelStats()) + tc->getKernelStats()->mode(Kernel::user, tc); + } else { + if (tc->getKernelStats()) + tc->getKernelStats()->mode(Kernel::kernel, tc); + } + + case AlphaISA::IPR_ICM: + // only write two mode bits - processor mode + ipr[idx] = val & 0x18; + break; + + case AlphaISA::IPR_ALT_MODE: + // only write two mode bits - processor mode + ipr[idx] = val & 0x18; + break; + + case AlphaISA::IPR_MCSR: + // more here after optimization... + ipr[idx] = val; + break; + + case AlphaISA::IPR_SIRR: + // only write software interrupt mask + ipr[idx] = val & 0x7fff0; + break; + + case AlphaISA::IPR_ICSR: + ipr[idx] = val & ULL(0xffffff0300); + break; + + case AlphaISA::IPR_IVPTBR: + case AlphaISA::IPR_MVPTBR: + ipr[idx] = val & ULL(0xffffffffc0000000); + break; + + case AlphaISA::IPR_DC_TEST_CTL: + ipr[idx] = val & 0x1ffb; + break; + + case AlphaISA::IPR_DC_MODE: + case AlphaISA::IPR_MAF_MODE: + ipr[idx] = val & 0x3f; + break; + + case AlphaISA::IPR_ITB_ASN: + ipr[idx] = val & 0x7f0; + break; + + case AlphaISA::IPR_DTB_ASN: + ipr[idx] = val & ULL(0xfe00000000000000); + break; + + case AlphaISA::IPR_EXC_SUM: + case AlphaISA::IPR_EXC_MASK: + // any write to this register clears it + ipr[idx] = 0; + break; + + case AlphaISA::IPR_INTID: + case AlphaISA::IPR_SL_RCV: + case AlphaISA::IPR_MM_STAT: + case AlphaISA::IPR_ITB_PTE_TEMP: + case AlphaISA::IPR_DTB_PTE_TEMP: + // read-only registers + return new UnimplementedOpcodeFault; + + case AlphaISA::IPR_HWINT_CLR: + case AlphaISA::IPR_SL_XMIT: + case AlphaISA::IPR_DC_FLUSH: + case AlphaISA::IPR_IC_FLUSH: + // the following are write only + ipr[idx] = val; + break; + + case AlphaISA::IPR_DTB_IA: + // really a control write + ipr[idx] = 0; + + tc->getDTBPtr()->flushAll(); + break; + + case AlphaISA::IPR_DTB_IAP: + // really a control write + ipr[idx] = 0; + + tc->getDTBPtr()->flushProcesses(); + break; + + case AlphaISA::IPR_DTB_IS: + // really a control write + ipr[idx] = val; + + tc->getDTBPtr()->flushAddr(val, + DTB_ASN_ASN(ipr[AlphaISA::IPR_DTB_ASN])); + break; + + case AlphaISA::IPR_DTB_TAG: { + struct AlphaISA::PTE pte; + + // FIXME: granularity hints NYI... + if (DTB_PTE_GH(ipr[AlphaISA::IPR_DTB_PTE]) != 0) + panic("PTE GH field != 0"); + + // write entire quad + ipr[idx] = val; + + // construct PTE for new entry + pte.ppn = DTB_PTE_PPN(ipr[AlphaISA::IPR_DTB_PTE]); + pte.xre = DTB_PTE_XRE(ipr[AlphaISA::IPR_DTB_PTE]); + pte.xwe = DTB_PTE_XWE(ipr[AlphaISA::IPR_DTB_PTE]); + pte.fonr = DTB_PTE_FONR(ipr[AlphaISA::IPR_DTB_PTE]); + pte.fonw = DTB_PTE_FONW(ipr[AlphaISA::IPR_DTB_PTE]); + pte.asma = DTB_PTE_ASMA(ipr[AlphaISA::IPR_DTB_PTE]); + pte.asn = DTB_ASN_ASN(ipr[AlphaISA::IPR_DTB_ASN]); + + // insert new TAG/PTE value into data TLB + tc->getDTBPtr()->insert(val, pte); + } + break; + + case AlphaISA::IPR_ITB_PTE: { + struct AlphaISA::PTE pte; + + // FIXME: granularity hints NYI... + if (ITB_PTE_GH(val) != 0) + panic("PTE GH field != 0"); + + // write entire quad + ipr[idx] = val; + + // construct PTE for new entry + pte.ppn = ITB_PTE_PPN(val); + pte.xre = ITB_PTE_XRE(val); + pte.xwe = 0; + pte.fonr = ITB_PTE_FONR(val); + pte.fonw = ITB_PTE_FONW(val); + pte.asma = ITB_PTE_ASMA(val); + pte.asn = ITB_ASN_ASN(ipr[AlphaISA::IPR_ITB_ASN]); + + // insert new TAG/PTE value into data TLB + tc->getITBPtr()->insert(ipr[AlphaISA::IPR_ITB_TAG], pte); + } + break; + + case AlphaISA::IPR_ITB_IA: + // really a control write + ipr[idx] = 0; + + tc->getITBPtr()->flushAll(); + break; + + case AlphaISA::IPR_ITB_IAP: + // really a control write + ipr[idx] = 0; + + tc->getITBPtr()->flushProcesses(); + break; + + case AlphaISA::IPR_ITB_IS: + // really a control write + ipr[idx] = val; + + tc->getITBPtr()->flushAddr(val, + ITB_ASN_ASN(ipr[AlphaISA::IPR_ITB_ASN])); + break; + + default: + // invalid IPR + return new UnimplementedOpcodeFault; + } + + // no error... + return NoFault; +} + + +void +AlphaISA::copyIprs(ThreadContext *src, ThreadContext *dest) +{ + for (int i = IPR_Base_DepTag; i < NumInternalProcRegs; ++i) { + dest->setMiscReg(i, src->readMiscReg(i)); + } +} + + +/** + * Check for special simulator handling of specific PAL calls. + * If return value is false, actual PAL call will be suppressed. + */ +bool +SimpleThread::simPalCheck(int palFunc) +{ + if (kernelStats) + kernelStats->callpal(palFunc, tc); + + switch (palFunc) { + case PAL::halt: + halt(); + if (--System::numSystemsRunning == 0) + exitSimLoop("all cpus halted"); + break; + + case PAL::bpt: + case PAL::bugchk: + if (system->breakpoint()) + return false; + break; + } + + return true; +} + +#endif // FULL_SYSTEM diff --git a/src/arch/alpha/ev5.hh b/src/arch/alpha/ev5.hh new file mode 100644 index 000000000..4dd225786 --- /dev/null +++ b/src/arch/alpha/ev5.hh @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + * Ali Saidi + */ + +#ifndef __ARCH_ALPHA_EV5_HH__ +#define __ARCH_ALPHA_EV5_HH__ + +#include "config/alpha_tlaser.hh" +#include "arch/alpha/isa_traits.hh" + +namespace EV5 { + +//It seems like a safe assumption EV5 only applies to alpha +using namespace AlphaISA; + +#if ALPHA_TLASER +const uint64_t AsnMask = ULL(0x7f); +#else +const uint64_t AsnMask = ULL(0xff); +#endif + +const int VAddrImplBits = 43; +const Addr VAddrImplMask = (ULL(1) << VAddrImplBits) - 1; +const Addr VAddrUnImplMask = ~VAddrImplMask; +inline Addr VAddrImpl(Addr a) { return a & VAddrImplMask; } +inline Addr VAddrVPN(Addr a) { return a >> AlphaISA::PageShift; } +inline Addr VAddrOffset(Addr a) { return a & AlphaISA::PageOffset; } +inline Addr VAddrSpaceEV5(Addr a) { return a >> 41 & 0x3; } +inline Addr VAddrSpaceEV6(Addr a) { return a >> 41 & 0x7f; } + +#if ALPHA_TLASER +inline bool PAddrIprSpace(Addr a) { return a >= ULL(0xFFFFF00000); } +const int PAddrImplBits = 40; +#else +inline bool PAddrIprSpace(Addr a) { return a >= ULL(0xFFFFFF00000); } +const int PAddrImplBits = 44; // for Tsunami +#endif +const Addr PAddrImplMask = (ULL(1) << PAddrImplBits) - 1; +const Addr PAddrUncachedBit39 = ULL(0x8000000000); +const Addr PAddrUncachedBit40 = ULL(0x10000000000); +const Addr PAddrUncachedBit43 = ULL(0x80000000000); +const Addr PAddrUncachedMask = ULL(0x807ffffffff); // Clear PA<42:35> +inline Addr Phys2K0Seg(Addr addr) +{ +#if !ALPHA_TLASER + if (addr & PAddrUncachedBit43) { + addr &= PAddrUncachedMask; + addr |= PAddrUncachedBit40; + } +#endif + return addr | AlphaISA::K0SegBase; +} + +inline int DTB_ASN_ASN(uint64_t reg) { return reg >> 57 & AsnMask; } +inline Addr DTB_PTE_PPN(uint64_t reg) +{ return reg >> 32 & (ULL(1) << PAddrImplBits - AlphaISA::PageShift) - 1; } +inline int DTB_PTE_XRE(uint64_t reg) { return reg >> 8 & 0xf; } +inline int DTB_PTE_XWE(uint64_t reg) { return reg >> 12 & 0xf; } +inline int DTB_PTE_FONR(uint64_t reg) { return reg >> 1 & 0x1; } +inline int DTB_PTE_FONW(uint64_t reg) { return reg >> 2 & 0x1; } +inline int DTB_PTE_GH(uint64_t reg) { return reg >> 5 & 0x3; } +inline int DTB_PTE_ASMA(uint64_t reg) { return reg >> 4 & 0x1; } + +inline int ITB_ASN_ASN(uint64_t reg) { return reg >> 4 & AsnMask; } +inline Addr ITB_PTE_PPN(uint64_t reg) +{ return reg >> 32 & (ULL(1) << PAddrImplBits - AlphaISA::PageShift) - 1; } +inline int ITB_PTE_XRE(uint64_t reg) { return reg >> 8 & 0xf; } +inline bool ITB_PTE_FONR(uint64_t reg) { return reg >> 1 & 0x1; } +inline bool ITB_PTE_FONW(uint64_t reg) { return reg >> 2 & 0x1; } +inline int ITB_PTE_GH(uint64_t reg) { return reg >> 5 & 0x3; } +inline bool ITB_PTE_ASMA(uint64_t reg) { return reg >> 4 & 0x1; } + +inline uint64_t MCSR_SP(uint64_t reg) { return reg >> 1 & 0x3; } + +inline bool ICSR_SDE(uint64_t reg) { return reg >> 30 & 0x1; } +inline int ICSR_SPE(uint64_t reg) { return reg >> 28 & 0x3; } +inline bool ICSR_FPE(uint64_t reg) { return reg >> 26 & 0x1; } + +inline uint64_t ALT_MODE_AM(uint64_t reg) { return reg >> 3 & 0x3; } +inline uint64_t DTB_CM_CM(uint64_t reg) { return reg >> 3 & 0x3; } +inline uint64_t ICM_CM(uint64_t reg) { return reg >> 3 & 0x3; } + +const uint64_t MM_STAT_BAD_VA_MASK = ULL(0x0020); +const uint64_t MM_STAT_DTB_MISS_MASK = ULL(0x0010); +const uint64_t MM_STAT_FONW_MASK = ULL(0x0008); +const uint64_t MM_STAT_FONR_MASK = ULL(0x0004); +const uint64_t MM_STAT_ACV_MASK = ULL(0x0002); +const uint64_t MM_STAT_WR_MASK = ULL(0x0001); +inline int Opcode(AlphaISA::MachInst inst) { return inst >> 26 & 0x3f; } +inline int Ra(AlphaISA::MachInst inst) { return inst >> 21 & 0x1f; } + +const Addr PalBase = 0x4000; +const Addr PalMax = 0x10000; + +/* namespace EV5 */ } + +#endif // __ARCH_ALPHA_EV5_HH__ diff --git a/src/arch/alpha/faults.cc b/src/arch/alpha/faults.cc new file mode 100644 index 000000000..eef4361fd --- /dev/null +++ b/src/arch/alpha/faults.cc @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Kevin Lim + */ + +#include "arch/alpha/faults.hh" +#include "cpu/thread_context.hh" +#include "cpu/base.hh" +#include "base/trace.hh" +#if FULL_SYSTEM +#include "arch/alpha/ev5.hh" +#else +#include "sim/process.hh" +#include "mem/page_table.hh" +#endif + +namespace AlphaISA +{ + +FaultName MachineCheckFault::_name = "mchk"; +FaultVect MachineCheckFault::_vect = 0x0401; +FaultStat MachineCheckFault::_count; + +FaultName AlignmentFault::_name = "unalign"; +FaultVect AlignmentFault::_vect = 0x0301; +FaultStat AlignmentFault::_count; + +FaultName ResetFault::_name = "reset"; +FaultVect ResetFault::_vect = 0x0001; +FaultStat ResetFault::_count; + +FaultName ArithmeticFault::_name = "arith"; +FaultVect ArithmeticFault::_vect = 0x0501; +FaultStat ArithmeticFault::_count; + +#if !FULL_SYSTEM +FaultName PageTableFault::_name = "page_table_fault"; +FaultVect PageTableFault::_vect = 0x0000; +FaultStat PageTableFault::_count; +#endif + +FaultName InterruptFault::_name = "interrupt"; +FaultVect InterruptFault::_vect = 0x0101; +FaultStat InterruptFault::_count; + +FaultName NDtbMissFault::_name = "dtb_miss_single"; +FaultVect NDtbMissFault::_vect = 0x0201; +FaultStat NDtbMissFault::_count; + +FaultName PDtbMissFault::_name = "dtb_miss_double"; +FaultVect PDtbMissFault::_vect = 0x0281; +FaultStat PDtbMissFault::_count; + +FaultName DtbPageFault::_name = "dfault"; +FaultVect DtbPageFault::_vect = 0x0381; +FaultStat DtbPageFault::_count; + +FaultName DtbAcvFault::_name = "dfault"; +FaultVect DtbAcvFault::_vect = 0x0381; +FaultStat DtbAcvFault::_count; + +FaultName DtbAlignmentFault::_name = "unalign"; +FaultVect DtbAlignmentFault::_vect = 0x0301; +FaultStat DtbAlignmentFault::_count; + +FaultName ItbMissFault::_name = "itbmiss"; +FaultVect ItbMissFault::_vect = 0x0181; +FaultStat ItbMissFault::_count; + +FaultName ItbPageFault::_name = "itbmiss"; +FaultVect ItbPageFault::_vect = 0x0181; +FaultStat ItbPageFault::_count; + +FaultName ItbAcvFault::_name = "iaccvio"; +FaultVect ItbAcvFault::_vect = 0x0081; +FaultStat ItbAcvFault::_count; + +FaultName UnimplementedOpcodeFault::_name = "opdec"; +FaultVect UnimplementedOpcodeFault::_vect = 0x0481; +FaultStat UnimplementedOpcodeFault::_count; + +FaultName FloatEnableFault::_name = "fen"; +FaultVect FloatEnableFault::_vect = 0x0581; +FaultStat FloatEnableFault::_count; + +FaultName PalFault::_name = "pal"; +FaultVect PalFault::_vect = 0x2001; +FaultStat PalFault::_count; + +FaultName IntegerOverflowFault::_name = "intover"; +FaultVect IntegerOverflowFault::_vect = 0x0501; +FaultStat IntegerOverflowFault::_count; + +#if FULL_SYSTEM + +void AlphaFault::invoke(ThreadContext * tc) +{ + FaultBase::invoke(tc); + countStat()++; + + // exception restart address + if (setRestartAddress() || !tc->inPalMode()) + tc->setMiscReg(AlphaISA::IPR_EXC_ADDR, tc->readPC()); + + if (skipFaultingInstruction()) { + // traps... skip faulting instruction. + tc->setMiscReg(AlphaISA::IPR_EXC_ADDR, + tc->readMiscReg(AlphaISA::IPR_EXC_ADDR) + 4); + } + + tc->setPC(tc->readMiscReg(AlphaISA::IPR_PAL_BASE) + vect()); + tc->setNextPC(tc->readPC() + sizeof(MachInst)); +} + +void ArithmeticFault::invoke(ThreadContext * tc) +{ + FaultBase::invoke(tc); + panic("Arithmetic traps are unimplemented!"); +} + +void DtbFault::invoke(ThreadContext * tc) +{ + // Set fault address and flags. Even though we're modeling an + // EV5, we use the EV6 technique of not latching fault registers + // on VPTE loads (instead of locking the registers until IPR_VA is + // read, like the EV5). The EV6 approach is cleaner and seems to + // work with EV5 PAL code, but not the other way around. + if (!tc->misspeculating() + && !(reqFlags & VPTE) && !(reqFlags & NO_FAULT)) { + // set VA register with faulting address + tc->setMiscReg(AlphaISA::IPR_VA, vaddr); + + // set MM_STAT register flags + tc->setMiscReg(AlphaISA::IPR_MM_STAT, + (((EV5::Opcode(tc->getInst()) & 0x3f) << 11) + | ((EV5::Ra(tc->getInst()) & 0x1f) << 6) + | (flags & 0x3f))); + + // set VA_FORM register with faulting formatted address + tc->setMiscReg(AlphaISA::IPR_VA_FORM, + tc->readMiscReg(AlphaISA::IPR_MVPTBR) | (vaddr.vpn() << 3)); + } + + AlphaFault::invoke(tc); +} + +void ItbFault::invoke(ThreadContext * tc) +{ + if (!tc->misspeculating()) { + tc->setMiscReg(AlphaISA::IPR_ITB_TAG, pc); + tc->setMiscReg(AlphaISA::IPR_IFAULT_VA_FORM, + tc->readMiscReg(AlphaISA::IPR_IVPTBR) | + (AlphaISA::VAddr(pc).vpn() << 3)); + } + + AlphaFault::invoke(tc); +} + +#else //!FULL_SYSTEM + +void PageTableFault::invoke(ThreadContext *tc) +{ + Process *p = tc->getProcessPtr(); + + // address is higher than the stack region or in the current stack region + if (vaddr > p->stack_base || vaddr > p->stack_min) + FaultBase::invoke(tc); + + // We've accessed the next page + if (vaddr > p->stack_min - PageBytes) { + warn("Increasing stack %#x:%#x to %#x:%#x because of access to %#x", + p->stack_min, p->stack_base, p->stack_min - PageBytes, + p->stack_base, vaddr); + p->stack_min -= PageBytes; + if (p->stack_base - p->stack_min > 8*1024*1024) + fatal("Over max stack size for one thread\n"); + p->pTable->allocate(p->stack_min, PageBytes); + } else { + FaultBase::invoke(tc); + } +} + +#endif + +} // namespace AlphaISA + diff --git a/src/arch/alpha/faults.hh b/src/arch/alpha/faults.hh new file mode 100644 index 000000000..3ef4d5521 --- /dev/null +++ b/src/arch/alpha/faults.hh @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Kevin Lim + */ + +#ifndef __ALPHA_FAULTS_HH__ +#define __ALPHA_FAULTS_HH__ + +#include "arch/alpha/pagetable.hh" +#include "sim/faults.hh" + +// The design of the "name" and "vect" functions is in sim/faults.hh + +namespace AlphaISA +{ + +typedef const Addr FaultVect; + +class AlphaFault : public FaultBase +{ + protected: + virtual bool skipFaultingInstruction() {return false;} + virtual bool setRestartAddress() {return true;} + public: +#if FULL_SYSTEM + void invoke(ThreadContext * tc); +#endif + virtual FaultVect vect() = 0; + virtual FaultStat & countStat() = 0; +}; + +class MachineCheckFault : public AlphaFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} + bool isMachineCheckFault() {return true;} +}; + +class AlignmentFault : public AlphaFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} + bool isAlignmentFault() {return true;} +}; + +#if !FULL_SYSTEM +class PageTableFault : public AlphaFault +{ + private: + Addr vaddr; + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + PageTableFault(Addr va) + : vaddr(va) {} + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} + void invoke(ThreadContext * tc); +}; + +static inline Fault genPageTableFault(Addr va) +{ + return new PageTableFault(va); +} +#endif + +static inline Fault genMachineCheckFault() +{ + return new MachineCheckFault; +} + +static inline Fault genAlignmentFault() +{ + return new AlignmentFault; +} + +class ResetFault : public AlphaFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class ArithmeticFault : public AlphaFault +{ + protected: + bool skipFaultingInstruction() {return true;} + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +#if FULL_SYSTEM + void invoke(ThreadContext * tc); +#endif +}; + +class InterruptFault : public AlphaFault +{ + protected: + bool setRestartAddress() {return false;} + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class DtbFault : public AlphaFault +{ +#if FULL_SYSTEM + private: + AlphaISA::VAddr vaddr; + uint32_t reqFlags; + uint64_t flags; + public: + DtbFault(AlphaISA::VAddr _vaddr, uint32_t _reqFlags, uint64_t _flags) + : vaddr(_vaddr), reqFlags(_reqFlags), flags(_flags) + { } +#endif + FaultName name() = 0; + FaultVect vect() = 0; + FaultStat & countStat() = 0; +#if FULL_SYSTEM + void invoke(ThreadContext * tc); +#endif +}; + +class NDtbMissFault : public DtbFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: +#if FULL_SYSTEM + NDtbMissFault(AlphaISA::VAddr vaddr, uint32_t reqFlags, uint64_t flags) + : DtbFault(vaddr, reqFlags, flags) + { } +#endif + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class PDtbMissFault : public DtbFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: +#if FULL_SYSTEM + PDtbMissFault(AlphaISA::VAddr vaddr, uint32_t reqFlags, uint64_t flags) + : DtbFault(vaddr, reqFlags, flags) + { } +#endif + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class DtbPageFault : public DtbFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: +#if FULL_SYSTEM + DtbPageFault(AlphaISA::VAddr vaddr, uint32_t reqFlags, uint64_t flags) + : DtbFault(vaddr, reqFlags, flags) + { } +#endif + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class DtbAcvFault : public DtbFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: +#if FULL_SYSTEM + DtbAcvFault(AlphaISA::VAddr vaddr, uint32_t reqFlags, uint64_t flags) + : DtbFault(vaddr, reqFlags, flags) + { } +#endif + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class DtbAlignmentFault : public DtbFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: +#if FULL_SYSTEM + DtbAlignmentFault(AlphaISA::VAddr vaddr, uint32_t reqFlags, uint64_t flags) + : DtbFault(vaddr, reqFlags, flags) + { } +#endif + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class ItbFault : public AlphaFault +{ + private: + Addr pc; + public: + ItbFault(Addr _pc) + : pc(_pc) + { } + FaultName name() = 0; + FaultVect vect() = 0; + FaultStat & countStat() = 0; +#if FULL_SYSTEM + void invoke(ThreadContext * tc); +#endif +}; + +class ItbMissFault : public ItbFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + ItbMissFault(Addr pc) + : ItbFault(pc) + { } + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class ItbPageFault : public ItbFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + ItbPageFault(Addr pc) + : ItbFault(pc) + { } + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class ItbAcvFault : public ItbFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + ItbAcvFault(Addr pc) + : ItbFault(pc) + { } + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class UnimplementedOpcodeFault : public AlphaFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class FloatEnableFault : public AlphaFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class PalFault : public AlphaFault +{ + protected: + bool skipFaultingInstruction() {return true;} + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class IntegerOverflowFault : public AlphaFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +} // AlphaISA namespace + +#endif // __FAULTS_HH__ diff --git a/src/arch/alpha/freebsd/system.cc b/src/arch/alpha/freebsd/system.cc new file mode 100644 index 000000000..99be25057 --- /dev/null +++ b/src/arch/alpha/freebsd/system.cc @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ben Nash + */ + +/** + * @file + * Modifications for the FreeBSD kernel. + * Based on kern/linux/linux_system.cc. + * + */ + +#include "arch/alpha/system.hh" +#include "arch/alpha/freebsd/system.hh" +#include "base/loader/symtab.hh" +#include "cpu/thread_context.hh" +#include "mem/physical.hh" +#include "mem/port.hh" +#include "arch/isa_traits.hh" +#include "sim/builder.hh" +#include "sim/byteswap.hh" +#include "arch/vtophys.hh" + +#define TIMER_FREQUENCY 1193180 + +using namespace std; +using namespace AlphaISA; + +FreebsdAlphaSystem::FreebsdAlphaSystem(Params *p) + : AlphaSystem(p) +{ + /** + * Any time DELAY is called just skip the function. + * Shouldn't we actually emulate the delay? + */ + skipDelayEvent = addKernelFuncEvent<SkipFuncEvent>("DELAY"); + skipCalibrateClocks = + addKernelFuncEvent<SkipCalibrateClocksEvent>("calibrate_clocks"); +} + + +FreebsdAlphaSystem::~FreebsdAlphaSystem() +{ + delete skipDelayEvent; + delete skipCalibrateClocks; +} + + +void +FreebsdAlphaSystem::doCalibrateClocks(ThreadContext *tc) +{ + Addr ppc_vaddr = 0; + Addr timer_vaddr = 0; + + ppc_vaddr = (Addr)tc->readIntReg(ArgumentReg1); + timer_vaddr = (Addr)tc->readIntReg(ArgumentReg2); + + virtPort.write(ppc_vaddr, (uint32_t)Clock::Frequency); + virtPort.write(timer_vaddr, (uint32_t)TIMER_FREQUENCY); +} + + +void +FreebsdAlphaSystem::SkipCalibrateClocksEvent::process(ThreadContext *tc) +{ + SkipFuncEvent::process(tc); + ((FreebsdAlphaSystem *)tc->getSystemPtr())->doCalibrateClocks(tc); +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) + + Param<Tick> boot_cpu_frequency; + SimObjectParam<PhysicalMemory *> physmem; + SimpleEnumParam<System::MemoryMode> mem_mode; + + Param<string> kernel; + Param<string> console; + Param<string> pal; + + Param<string> boot_osflags; + Param<string> readfile; + Param<string> symbolfile; + Param<unsigned int> init_param; + + Param<uint64_t> system_type; + Param<uint64_t> system_rev; + +END_DECLARE_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) + +BEGIN_INIT_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) + + INIT_PARAM(boot_cpu_frequency, "Frequency of the boot CPU"), + INIT_PARAM(physmem, "phsyical memory"), + INIT_ENUM_PARAM(mem_mode, "Memory Mode, (1=atomic, 2=timing)", + System::MemoryModeStrings), + INIT_PARAM(kernel, "file that contains the kernel code"), + INIT_PARAM(console, "file that contains the console code"), + INIT_PARAM(pal, "file that contains palcode"), + INIT_PARAM_DFLT(boot_osflags, "flags to pass to the kernel during boot", + "a"), + INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), + INIT_PARAM_DFLT(symbolfile, "file to read symbols from", ""), + INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), + INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 34), + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10) + +END_INIT_SIM_OBJECT_PARAMS(FreebsdAlphaSystem) + +CREATE_SIM_OBJECT(FreebsdAlphaSystem) +{ + AlphaSystem::Params *p = new AlphaSystem::Params; + p->name = getInstanceName(); + p->boot_cpu_frequency = boot_cpu_frequency; + p->physmem = physmem; + p->mem_mode = mem_mode; + p->kernel_path = kernel; + p->console_path = console; + p->palcode = pal; + p->boot_osflags = boot_osflags; + p->init_param = init_param; + p->readfile = readfile; + p->symbolfile = symbolfile; + p->system_type = system_type; + p->system_rev = system_rev; + return new FreebsdAlphaSystem(p); +} + +REGISTER_SIM_OBJECT("FreebsdAlphaSystem", FreebsdAlphaSystem) + diff --git a/src/arch/alpha/freebsd/system.hh b/src/arch/alpha/freebsd/system.hh new file mode 100644 index 000000000..e0d874e8f --- /dev/null +++ b/src/arch/alpha/freebsd/system.hh @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ben Nash + */ + +#ifndef __KERN_FREEBSD_FREEBSD_SYSTEM_HH__ +#define __KERN_FREEBSD_FREEBSD_SYSTEM_HH__ + +#include "kern/system_events.hh" + +class FreebsdAlphaSystem : public AlphaSystem +{ + private: + class SkipCalibrateClocksEvent : public SkipFuncEvent + { + public: + SkipCalibrateClocksEvent(PCEventQueue *q, const std::string &desc, + Addr addr) + : SkipFuncEvent(q, desc, addr) {} + virtual void process(ThreadContext *tc); + }; + + SkipFuncEvent *skipDelayEvent; + SkipCalibrateClocksEvent *skipCalibrateClocks; + + public: + FreebsdAlphaSystem(Params *p); + ~FreebsdAlphaSystem(); + void doCalibrateClocks(ThreadContext *tc); + +}; + +#endif // __KERN_FREEBSD_FREEBSD_SYSTEM_HH__ diff --git a/src/arch/alpha/isa/branch.isa b/src/arch/alpha/isa/branch.isa new file mode 100644 index 000000000..7438e7e18 --- /dev/null +++ b/src/arch/alpha/isa/branch.isa @@ -0,0 +1,266 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2005 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Control transfer instructions +// + +output header {{ + + /** + * Base class for instructions whose disassembly is not purely a + * function of the machine instruction (i.e., it depends on the + * PC). This class overrides the disassemble() method to check + * the PC and symbol table values before re-using a cached + * disassembly string. This is necessary for branches and jumps, + * where the disassembly string includes the target address (which + * may depend on the PC and/or symbol table). + */ + class PCDependentDisassembly : public AlphaStaticInst + { + protected: + /// Cached program counter from last disassembly + mutable Addr cachedPC; + /// Cached symbol table pointer from last disassembly + mutable const SymbolTable *cachedSymtab; + + /// Constructor + PCDependentDisassembly(const char *mnem, ExtMachInst _machInst, + OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), + cachedPC(0), cachedSymtab(0) + { + } + + const std::string & + disassemble(Addr pc, const SymbolTable *symtab) const; + }; + + /** + * Base class for branches (PC-relative control transfers), + * conditional or unconditional. + */ + class Branch : public PCDependentDisassembly + { + protected: + /// Displacement to target address (signed). + int32_t disp; + + /// Constructor. + Branch(const char *mnem, ExtMachInst _machInst, OpClass __opClass) + : PCDependentDisassembly(mnem, _machInst, __opClass), + disp(BRDISP << 2) + { + } + + Addr branchTarget(Addr branchPC) const; + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + /** + * Base class for jumps (register-indirect control transfers). In + * the Alpha ISA, these are always unconditional. + */ + class Jump : public PCDependentDisassembly + { + protected: + + /// Displacement to target address (signed). + int32_t disp; + + public: + /// Constructor + Jump(const char *mnem, ExtMachInst _machInst, OpClass __opClass) + : PCDependentDisassembly(mnem, _machInst, __opClass), + disp(BRDISP) + { + } + + Addr branchTarget(ThreadContext *tc) const; + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + Addr + Branch::branchTarget(Addr branchPC) const + { + return branchPC + 4 + disp; + } + + Addr + Jump::branchTarget(ThreadContext *tc) const + { + Addr NPC = tc->readPC() + 4; + uint64_t Rb = tc->readIntReg(_srcRegIdx[0]); + return (Rb & ~3) | (NPC & 1); + } + + const std::string & + PCDependentDisassembly::disassemble(Addr pc, + const SymbolTable *symtab) const + { + if (!cachedDisassembly || + pc != cachedPC || symtab != cachedSymtab) + { + if (cachedDisassembly) + delete cachedDisassembly; + + cachedDisassembly = + new std::string(generateDisassembly(pc, symtab)); + cachedPC = pc; + cachedSymtab = symtab; + } + + return *cachedDisassembly; + } + + std::string + Branch::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + // There's only one register arg (RA), but it could be + // either a source (the condition for conditional + // branches) or a destination (the link reg for + // unconditional branches) + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + ss << ","; + } + else if (_numDestRegs > 0) { + printReg(ss, _destRegIdx[0]); + ss << ","; + } + +#ifdef SS_COMPATIBLE_DISASSEMBLY + if (_numSrcRegs == 0 && _numDestRegs == 0) { + printReg(ss, 31); + ss << ","; + } +#endif + + Addr target = pc + 4 + disp; + + std::string str; + if (symtab && symtab->findSymbol(target, str)) + ss << str; + else + ccprintf(ss, "0x%x", target); + + return ss.str(); + } + + std::string + Jump::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + +#ifdef SS_COMPATIBLE_DISASSEMBLY + if (_numDestRegs == 0) { + printReg(ss, 31); + ss << ","; + } +#endif + + if (_numDestRegs > 0) { + printReg(ss, _destRegIdx[0]); + ss << ","; + } + + ccprintf(ss, "(r%d)", RB); + + return ss.str(); + } +}}; + +def template JumpOrBranchDecode {{ + return (RA == 31) + ? (StaticInst *)new %(class_name)s(machInst) + : (StaticInst *)new %(class_name)sAndLink(machInst); +}}; + +def format CondBranch(code) {{ + code = 'bool cond;\n' + code + '\nif (cond) NPC = NPC + disp;\n'; + iop = InstObjParams(name, Name, 'Branch', CodeBlock(code), + ('IsDirectControl', 'IsCondControl')) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +let {{ +def UncondCtrlBase(name, Name, base_class, npc_expr, flags): + # Declare basic control transfer w/o link (i.e. link reg is R31) + nolink_code = 'NPC = %s;\n' % npc_expr + nolink_iop = InstObjParams(name, Name, base_class, + CodeBlock(nolink_code), flags) + header_output = BasicDeclare.subst(nolink_iop) + decoder_output = BasicConstructor.subst(nolink_iop) + exec_output = BasicExecute.subst(nolink_iop) + + # Generate declaration of '*AndLink' version, append to decls + link_code = 'Ra = NPC & ~3;\n' + nolink_code + link_iop = InstObjParams(name, Name + 'AndLink', base_class, + CodeBlock(link_code), flags) + header_output += BasicDeclare.subst(link_iop) + decoder_output += BasicConstructor.subst(link_iop) + exec_output += BasicExecute.subst(link_iop) + + # need to use link_iop for the decode template since it is expecting + # the shorter version of class_name (w/o "AndLink") + + return (header_output, decoder_output, + JumpOrBranchDecode.subst(nolink_iop), exec_output) +}}; + +def format UncondBranch(*flags) {{ + flags += ('IsUncondControl', 'IsDirectControl') + (header_output, decoder_output, decode_block, exec_output) = \ + UncondCtrlBase(name, Name, 'Branch', 'NPC + disp', flags) +}}; + +def format Jump(*flags) {{ + flags += ('IsUncondControl', 'IsIndirectControl') + (header_output, decoder_output, decode_block, exec_output) = \ + UncondCtrlBase(name, Name, 'Jump', '(Rb & ~3) | (NPC & 1)', flags) +}}; + + diff --git a/src/arch/alpha/isa/decoder.isa b/src/arch/alpha/isa/decoder.isa new file mode 100644 index 000000000..4fc9da3f3 --- /dev/null +++ b/src/arch/alpha/isa/decoder.isa @@ -0,0 +1,838 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2006 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// The actual decoder specification +// + +decode OPCODE default Unknown::unknown() { + + format LoadAddress { + 0x08: lda({{ Ra = Rb + disp; }}); + 0x09: ldah({{ Ra = Rb + (disp << 16); }}); + } + + format LoadOrNop { + 0x0a: ldbu({{ Ra.uq = Mem.ub; }}); + 0x0c: ldwu({{ Ra.uq = Mem.uw; }}); + 0x0b: ldq_u({{ Ra = Mem.uq; }}, ea_code = {{ EA = (Rb + disp) & ~7; }}); + 0x23: ldt({{ Fa = Mem.df; }}); + 0x2a: ldl_l({{ Ra.sl = Mem.sl; }}, mem_flags = LOCKED); + 0x2b: ldq_l({{ Ra.uq = Mem.uq; }}, mem_flags = LOCKED); +#ifdef USE_COPY + 0x20: MiscPrefetch::copy_load({{ EA = Ra; }}, + {{ fault = xc->copySrcTranslate(EA); }}, + inst_flags = [IsMemRef, IsLoad, IsCopy]); +#endif + } + + format LoadOrPrefetch { + 0x28: ldl({{ Ra.sl = Mem.sl; }}); + 0x29: ldq({{ Ra.uq = Mem.uq; }}, pf_flags = EVICT_NEXT); + // IsFloating flag on lds gets the prefetch to disassemble + // using f31 instead of r31... funcitonally it's unnecessary + 0x22: lds({{ Fa.uq = s_to_t(Mem.ul); }}, + pf_flags = PF_EXCLUSIVE, inst_flags = IsFloating); + } + + format Store { + 0x0e: stb({{ Mem.ub = Ra<7:0>; }}); + 0x0d: stw({{ Mem.uw = Ra<15:0>; }}); + 0x2c: stl({{ Mem.ul = Ra<31:0>; }}); + 0x2d: stq({{ Mem.uq = Ra.uq; }}); + 0x0f: stq_u({{ Mem.uq = Ra.uq; }}, {{ EA = (Rb + disp) & ~7; }}); + 0x26: sts({{ Mem.ul = t_to_s(Fa.uq); }}); + 0x27: stt({{ Mem.df = Fa; }}); +#ifdef USE_COPY + 0x24: MiscPrefetch::copy_store({{ EA = Rb; }}, + {{ fault = xc->copy(EA); }}, + inst_flags = [IsMemRef, IsStore, IsCopy]); +#endif + } + + format StoreCond { + 0x2e: stl_c({{ Mem.ul = Ra<31:0>; }}, + {{ + uint64_t tmp = write_result; + // see stq_c + Ra = (tmp == 0 || tmp == 1) ? tmp : Ra; + }}, mem_flags = LOCKED, inst_flags = IsStoreConditional); + 0x2f: stq_c({{ Mem.uq = Ra; }}, + {{ + uint64_t tmp = write_result; + // If the write operation returns 0 or 1, then + // this was a conventional store conditional, + // and the value indicates the success/failure + // of the operation. If another value is + // returned, then this was a Turbolaser + // mailbox access, and we don't update the + // result register at all. + Ra = (tmp == 0 || tmp == 1) ? tmp : Ra; + }}, mem_flags = LOCKED, inst_flags = IsStoreConditional); + } + + format IntegerOperate { + + 0x10: decode INTFUNC { // integer arithmetic operations + + 0x00: addl({{ Rc.sl = Ra.sl + Rb_or_imm.sl; }}); + 0x40: addlv({{ + uint32_t tmp = Ra.sl + Rb_or_imm.sl; + // signed overflow occurs when operands have same sign + // and sign of result does not match. + if (Ra.sl<31:> == Rb_or_imm.sl<31:> && tmp<31:> != Ra.sl<31:>) + fault = new IntegerOverflowFault; + Rc.sl = tmp; + }}); + 0x02: s4addl({{ Rc.sl = (Ra.sl << 2) + Rb_or_imm.sl; }}); + 0x12: s8addl({{ Rc.sl = (Ra.sl << 3) + Rb_or_imm.sl; }}); + + 0x20: addq({{ Rc = Ra + Rb_or_imm; }}); + 0x60: addqv({{ + uint64_t tmp = Ra + Rb_or_imm; + // signed overflow occurs when operands have same sign + // and sign of result does not match. + if (Ra<63:> == Rb_or_imm<63:> && tmp<63:> != Ra<63:>) + fault = new IntegerOverflowFault; + Rc = tmp; + }}); + 0x22: s4addq({{ Rc = (Ra << 2) + Rb_or_imm; }}); + 0x32: s8addq({{ Rc = (Ra << 3) + Rb_or_imm; }}); + + 0x09: subl({{ Rc.sl = Ra.sl - Rb_or_imm.sl; }}); + 0x49: sublv({{ + uint32_t tmp = Ra.sl - Rb_or_imm.sl; + // signed overflow detection is same as for add, + // except we need to look at the *complemented* + // sign bit of the subtrahend (Rb), i.e., if the initial + // signs are the *same* then no overflow can occur + if (Ra.sl<31:> != Rb_or_imm.sl<31:> && tmp<31:> != Ra.sl<31:>) + fault = new IntegerOverflowFault; + Rc.sl = tmp; + }}); + 0x0b: s4subl({{ Rc.sl = (Ra.sl << 2) - Rb_or_imm.sl; }}); + 0x1b: s8subl({{ Rc.sl = (Ra.sl << 3) - Rb_or_imm.sl; }}); + + 0x29: subq({{ Rc = Ra - Rb_or_imm; }}); + 0x69: subqv({{ + uint64_t tmp = Ra - Rb_or_imm; + // signed overflow detection is same as for add, + // except we need to look at the *complemented* + // sign bit of the subtrahend (Rb), i.e., if the initial + // signs are the *same* then no overflow can occur + if (Ra<63:> != Rb_or_imm<63:> && tmp<63:> != Ra<63:>) + fault = new IntegerOverflowFault; + Rc = tmp; + }}); + 0x2b: s4subq({{ Rc = (Ra << 2) - Rb_or_imm; }}); + 0x3b: s8subq({{ Rc = (Ra << 3) - Rb_or_imm; }}); + + 0x2d: cmpeq({{ Rc = (Ra == Rb_or_imm); }}); + 0x6d: cmple({{ Rc = (Ra.sq <= Rb_or_imm.sq); }}); + 0x4d: cmplt({{ Rc = (Ra.sq < Rb_or_imm.sq); }}); + 0x3d: cmpule({{ Rc = (Ra.uq <= Rb_or_imm.uq); }}); + 0x1d: cmpult({{ Rc = (Ra.uq < Rb_or_imm.uq); }}); + + 0x0f: cmpbge({{ + int hi = 7; + int lo = 0; + uint64_t tmp = 0; + for (int i = 0; i < 8; ++i) { + tmp |= (Ra.uq<hi:lo> >= Rb_or_imm.uq<hi:lo>) << i; + hi += 8; + lo += 8; + } + Rc = tmp; + }}); + } + + 0x11: decode INTFUNC { // integer logical operations + + 0x00: and({{ Rc = Ra & Rb_or_imm; }}); + 0x08: bic({{ Rc = Ra & ~Rb_or_imm; }}); + 0x20: bis({{ Rc = Ra | Rb_or_imm; }}); + 0x28: ornot({{ Rc = Ra | ~Rb_or_imm; }}); + 0x40: xor({{ Rc = Ra ^ Rb_or_imm; }}); + 0x48: eqv({{ Rc = Ra ^ ~Rb_or_imm; }}); + + // conditional moves + 0x14: cmovlbs({{ Rc = ((Ra & 1) == 1) ? Rb_or_imm : Rc; }}); + 0x16: cmovlbc({{ Rc = ((Ra & 1) == 0) ? Rb_or_imm : Rc; }}); + 0x24: cmoveq({{ Rc = (Ra == 0) ? Rb_or_imm : Rc; }}); + 0x26: cmovne({{ Rc = (Ra != 0) ? Rb_or_imm : Rc; }}); + 0x44: cmovlt({{ Rc = (Ra.sq < 0) ? Rb_or_imm : Rc; }}); + 0x46: cmovge({{ Rc = (Ra.sq >= 0) ? Rb_or_imm : Rc; }}); + 0x64: cmovle({{ Rc = (Ra.sq <= 0) ? Rb_or_imm : Rc; }}); + 0x66: cmovgt({{ Rc = (Ra.sq > 0) ? Rb_or_imm : Rc; }}); + + // For AMASK, RA must be R31. + 0x61: decode RA { + 31: amask({{ Rc = Rb_or_imm & ~ULL(0x17); }}); + } + + // For IMPLVER, RA must be R31 and the B operand + // must be the immediate value 1. + 0x6c: decode RA { + 31: decode IMM { + 1: decode INTIMM { + // return EV5 for FULL_SYSTEM and EV6 otherwise + 1: implver({{ +#if FULL_SYSTEM + Rc = 1; +#else + Rc = 2; +#endif + }}); + } + } + } + +#if FULL_SYSTEM + // The mysterious 11.25... + 0x25: WarnUnimpl::eleven25(); +#endif + } + + 0x12: decode INTFUNC { + 0x39: sll({{ Rc = Ra << Rb_or_imm<5:0>; }}); + 0x34: srl({{ Rc = Ra.uq >> Rb_or_imm<5:0>; }}); + 0x3c: sra({{ Rc = Ra.sq >> Rb_or_imm<5:0>; }}); + + 0x02: mskbl({{ Rc = Ra & ~(mask( 8) << (Rb_or_imm<2:0> * 8)); }}); + 0x12: mskwl({{ Rc = Ra & ~(mask(16) << (Rb_or_imm<2:0> * 8)); }}); + 0x22: mskll({{ Rc = Ra & ~(mask(32) << (Rb_or_imm<2:0> * 8)); }}); + 0x32: mskql({{ Rc = Ra & ~(mask(64) << (Rb_or_imm<2:0> * 8)); }}); + + 0x52: mskwh({{ + int bv = Rb_or_imm<2:0>; + Rc = bv ? (Ra & ~(mask(16) >> (64 - 8 * bv))) : Ra; + }}); + 0x62: msklh({{ + int bv = Rb_or_imm<2:0>; + Rc = bv ? (Ra & ~(mask(32) >> (64 - 8 * bv))) : Ra; + }}); + 0x72: mskqh({{ + int bv = Rb_or_imm<2:0>; + Rc = bv ? (Ra & ~(mask(64) >> (64 - 8 * bv))) : Ra; + }}); + + 0x06: extbl({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8))< 7:0>; }}); + 0x16: extwl({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8))<15:0>; }}); + 0x26: extll({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8))<31:0>; }}); + 0x36: extql({{ Rc = (Ra.uq >> (Rb_or_imm<2:0> * 8)); }}); + + 0x5a: extwh({{ + Rc = (Ra << (64 - (Rb_or_imm<2:0> * 8))<5:0>)<15:0>; }}); + 0x6a: extlh({{ + Rc = (Ra << (64 - (Rb_or_imm<2:0> * 8))<5:0>)<31:0>; }}); + 0x7a: extqh({{ + Rc = (Ra << (64 - (Rb_or_imm<2:0> * 8))<5:0>); }}); + + 0x0b: insbl({{ Rc = Ra< 7:0> << (Rb_or_imm<2:0> * 8); }}); + 0x1b: inswl({{ Rc = Ra<15:0> << (Rb_or_imm<2:0> * 8); }}); + 0x2b: insll({{ Rc = Ra<31:0> << (Rb_or_imm<2:0> * 8); }}); + 0x3b: insql({{ Rc = Ra << (Rb_or_imm<2:0> * 8); }}); + + 0x57: inswh({{ + int bv = Rb_or_imm<2:0>; + Rc = bv ? (Ra.uq<15:0> >> (64 - 8 * bv)) : 0; + }}); + 0x67: inslh({{ + int bv = Rb_or_imm<2:0>; + Rc = bv ? (Ra.uq<31:0> >> (64 - 8 * bv)) : 0; + }}); + 0x77: insqh({{ + int bv = Rb_or_imm<2:0>; + Rc = bv ? (Ra.uq >> (64 - 8 * bv)) : 0; + }}); + + 0x30: zap({{ + uint64_t zapmask = 0; + for (int i = 0; i < 8; ++i) { + if (Rb_or_imm<i:>) + zapmask |= (mask(8) << (i * 8)); + } + Rc = Ra & ~zapmask; + }}); + 0x31: zapnot({{ + uint64_t zapmask = 0; + for (int i = 0; i < 8; ++i) { + if (!Rb_or_imm<i:>) + zapmask |= (mask(8) << (i * 8)); + } + Rc = Ra & ~zapmask; + }}); + } + + 0x13: decode INTFUNC { // integer multiplies + 0x00: mull({{ Rc.sl = Ra.sl * Rb_or_imm.sl; }}, IntMultOp); + 0x20: mulq({{ Rc = Ra * Rb_or_imm; }}, IntMultOp); + 0x30: umulh({{ + uint64_t hi, lo; + mul128(Ra, Rb_or_imm, hi, lo); + Rc = hi; + }}, IntMultOp); + 0x40: mullv({{ + // 32-bit multiply with trap on overflow + int64_t Rax = Ra.sl; // sign extended version of Ra.sl + int64_t Rbx = Rb_or_imm.sl; + int64_t tmp = Rax * Rbx; + // To avoid overflow, all the upper 32 bits must match + // the sign bit of the lower 32. We code this as + // checking the upper 33 bits for all 0s or all 1s. + uint64_t sign_bits = tmp<63:31>; + if (sign_bits != 0 && sign_bits != mask(33)) + fault = new IntegerOverflowFault; + Rc.sl = tmp<31:0>; + }}, IntMultOp); + 0x60: mulqv({{ + // 64-bit multiply with trap on overflow + uint64_t hi, lo; + mul128(Ra, Rb_or_imm, hi, lo); + // all the upper 64 bits must match the sign bit of + // the lower 64 + if (!((hi == 0 && lo<63:> == 0) || + (hi == mask(64) && lo<63:> == 1))) + fault = new IntegerOverflowFault; + Rc = lo; + }}, IntMultOp); + } + + 0x1c: decode INTFUNC { + 0x00: decode RA { 31: sextb({{ Rc.sb = Rb_or_imm< 7:0>; }}); } + 0x01: decode RA { 31: sextw({{ Rc.sw = Rb_or_imm<15:0>; }}); } + 0x32: ctlz({{ + uint64_t count = 0; + uint64_t temp = Rb; + if (temp<63:32>) temp >>= 32; else count += 32; + if (temp<31:16>) temp >>= 16; else count += 16; + if (temp<15:8>) temp >>= 8; else count += 8; + if (temp<7:4>) temp >>= 4; else count += 4; + if (temp<3:2>) temp >>= 2; else count += 2; + if (temp<1:1>) temp >>= 1; else count += 1; + if ((temp<0:0>) != 0x1) count += 1; + Rc = count; + }}, IntAluOp); + + 0x33: cttz({{ + uint64_t count = 0; + uint64_t temp = Rb; + if (!(temp<31:0>)) { temp >>= 32; count += 32; } + if (!(temp<15:0>)) { temp >>= 16; count += 16; } + if (!(temp<7:0>)) { temp >>= 8; count += 8; } + if (!(temp<3:0>)) { temp >>= 4; count += 4; } + if (!(temp<1:0>)) { temp >>= 2; count += 2; } + if (!(temp<0:0> & ULL(0x1))) count += 1; + Rc = count; + }}, IntAluOp); + + format FailUnimpl { + 0x30: ctpop(); + 0x31: perr(); + 0x34: unpkbw(); + 0x35: unpkbl(); + 0x36: pkwb(); + 0x37: pklb(); + 0x38: minsb8(); + 0x39: minsw4(); + 0x3a: minub8(); + 0x3b: minuw4(); + 0x3c: maxub8(); + 0x3d: maxuw4(); + 0x3e: maxsb8(); + 0x3f: maxsw4(); + } + + format BasicOperateWithNopCheck { + 0x70: decode RB { + 31: ftoit({{ Rc = Fa.uq; }}, FloatCvtOp); + } + 0x78: decode RB { + 31: ftois({{ Rc.sl = t_to_s(Fa.uq); }}, + FloatCvtOp); + } + } + } + } + + // Conditional branches. + format CondBranch { + 0x39: beq({{ cond = (Ra == 0); }}); + 0x3d: bne({{ cond = (Ra != 0); }}); + 0x3e: bge({{ cond = (Ra.sq >= 0); }}); + 0x3f: bgt({{ cond = (Ra.sq > 0); }}); + 0x3b: ble({{ cond = (Ra.sq <= 0); }}); + 0x3a: blt({{ cond = (Ra.sq < 0); }}); + 0x38: blbc({{ cond = ((Ra & 1) == 0); }}); + 0x3c: blbs({{ cond = ((Ra & 1) == 1); }}); + + 0x31: fbeq({{ cond = (Fa == 0); }}); + 0x35: fbne({{ cond = (Fa != 0); }}); + 0x36: fbge({{ cond = (Fa >= 0); }}); + 0x37: fbgt({{ cond = (Fa > 0); }}); + 0x33: fble({{ cond = (Fa <= 0); }}); + 0x32: fblt({{ cond = (Fa < 0); }}); + } + + // unconditional branches + format UncondBranch { + 0x30: br(); + 0x34: bsr(IsCall); + } + + // indirect branches + 0x1a: decode JMPFUNC { + format Jump { + 0: jmp(); + 1: jsr(IsCall); + 2: ret(IsReturn); + 3: jsr_coroutine(IsCall, IsReturn); + } + } + + // Square root and integer-to-FP moves + 0x14: decode FP_SHORTFUNC { + // Integer to FP register moves must have RB == 31 + 0x4: decode RB { + 31: decode FP_FULLFUNC { + format BasicOperateWithNopCheck { + 0x004: itofs({{ Fc.uq = s_to_t(Ra.ul); }}, FloatCvtOp); + 0x024: itoft({{ Fc.uq = Ra.uq; }}, FloatCvtOp); + 0x014: FailUnimpl::itoff(); // VAX-format conversion + } + } + } + + // Square root instructions must have FA == 31 + 0xb: decode FA { + 31: decode FP_TYPEFUNC { + format FloatingPointOperate { +#if SS_COMPATIBLE_FP + 0x0b: sqrts({{ + if (Fb < 0.0) + fault = new ArithmeticFault; + Fc = sqrt(Fb); + }}, FloatSqrtOp); +#else + 0x0b: sqrts({{ + if (Fb.sf < 0.0) + fault = new ArithmeticFault; + Fc.sf = sqrt(Fb.sf); + }}, FloatSqrtOp); +#endif + 0x2b: sqrtt({{ + if (Fb < 0.0) + fault = new ArithmeticFault; + Fc = sqrt(Fb); + }}, FloatSqrtOp); + } + } + } + + // VAX-format sqrtf and sqrtg are not implemented + 0xa: FailUnimpl::sqrtfg(); + } + + // IEEE floating point + 0x16: decode FP_SHORTFUNC_TOP2 { + // The top two bits of the short function code break this + // space into four groups: binary ops, compares, reserved, and + // conversions. See Table 4-12 of AHB. There are different + // special cases in these different groups, so we decode on + // these top two bits first just to select a decode strategy. + // Most of these instructions may have various trapping and + // rounding mode flags set; these are decoded in the + // FloatingPointDecode template used by the + // FloatingPointOperate format. + + // add/sub/mul/div: just decode on the short function code + // and source type. All valid trapping and rounding modes apply. + 0: decode FP_TRAPMODE { + // check for valid trapping modes here + 0,1,5,7: decode FP_TYPEFUNC { + format FloatingPointOperate { +#if SS_COMPATIBLE_FP + 0x00: adds({{ Fc = Fa + Fb; }}); + 0x01: subs({{ Fc = Fa - Fb; }}); + 0x02: muls({{ Fc = Fa * Fb; }}, FloatMultOp); + 0x03: divs({{ Fc = Fa / Fb; }}, FloatDivOp); +#else + 0x00: adds({{ Fc.sf = Fa.sf + Fb.sf; }}); + 0x01: subs({{ Fc.sf = Fa.sf - Fb.sf; }}); + 0x02: muls({{ Fc.sf = Fa.sf * Fb.sf; }}, FloatMultOp); + 0x03: divs({{ Fc.sf = Fa.sf / Fb.sf; }}, FloatDivOp); +#endif + + 0x20: addt({{ Fc = Fa + Fb; }}); + 0x21: subt({{ Fc = Fa - Fb; }}); + 0x22: mult({{ Fc = Fa * Fb; }}, FloatMultOp); + 0x23: divt({{ Fc = Fa / Fb; }}, FloatDivOp); + } + } + } + + // Floating-point compare instructions must have the default + // rounding mode, and may use the default trapping mode or + // /SU. Both trapping modes are treated the same by M5; the + // only difference on the real hardware (as far a I can tell) + // is that without /SU you'd get an imprecise trap if you + // tried to compare a NaN with something else (instead of an + // "unordered" result). + 1: decode FP_FULLFUNC { + format BasicOperateWithNopCheck { + 0x0a5, 0x5a5: cmpteq({{ Fc = (Fa == Fb) ? 2.0 : 0.0; }}, + FloatCmpOp); + 0x0a7, 0x5a7: cmptle({{ Fc = (Fa <= Fb) ? 2.0 : 0.0; }}, + FloatCmpOp); + 0x0a6, 0x5a6: cmptlt({{ Fc = (Fa < Fb) ? 2.0 : 0.0; }}, + FloatCmpOp); + 0x0a4, 0x5a4: cmptun({{ // unordered + Fc = (!(Fa < Fb) && !(Fa == Fb) && !(Fa > Fb)) ? 2.0 : 0.0; + }}, FloatCmpOp); + } + } + + // The FP-to-integer and integer-to-FP conversion insts + // require that FA be 31. + 3: decode FA { + 31: decode FP_TYPEFUNC { + format FloatingPointOperate { + 0x2f: decode FP_ROUNDMODE { + format FPFixedRounding { + // "chopped" i.e. round toward zero + 0: cvttq({{ Fc.sq = (int64_t)trunc(Fb); }}, + Chopped); + // round to minus infinity + 1: cvttq({{ Fc.sq = (int64_t)floor(Fb); }}, + MinusInfinity); + } + default: cvttq({{ Fc.sq = (int64_t)nearbyint(Fb); }}); + } + + // The cvtts opcode is overloaded to be cvtst if the trap + // mode is 2 or 6 (which are not valid otherwise) + 0x2c: decode FP_FULLFUNC { + format BasicOperateWithNopCheck { + // trap on denorm version "cvtst/s" is + // simulated same as cvtst + 0x2ac, 0x6ac: cvtst({{ Fc = Fb.sf; }}); + } + default: cvtts({{ Fc.sf = Fb; }}); + } + + // The trapping mode for integer-to-FP conversions + // must be /SUI or nothing; /U and /SU are not + // allowed. The full set of rounding modes are + // supported though. + 0x3c: decode FP_TRAPMODE { + 0,7: cvtqs({{ Fc.sf = Fb.sq; }}); + } + 0x3e: decode FP_TRAPMODE { + 0,7: cvtqt({{ Fc = Fb.sq; }}); + } + } + } + } + } + + // misc FP operate + 0x17: decode FP_FULLFUNC { + format BasicOperateWithNopCheck { + 0x010: cvtlq({{ + Fc.sl = (Fb.uq<63:62> << 30) | Fb.uq<58:29>; + }}); + 0x030: cvtql({{ + Fc.uq = (Fb.uq<31:30> << 62) | (Fb.uq<29:0> << 29); + }}); + + // We treat the precise & imprecise trapping versions of + // cvtql identically. + 0x130, 0x530: cvtqlv({{ + // To avoid overflow, all the upper 32 bits must match + // the sign bit of the lower 32. We code this as + // checking the upper 33 bits for all 0s or all 1s. + uint64_t sign_bits = Fb.uq<63:31>; + if (sign_bits != 0 && sign_bits != mask(33)) + fault = new IntegerOverflowFault; + Fc.uq = (Fb.uq<31:30> << 62) | (Fb.uq<29:0> << 29); + }}); + + 0x020: cpys({{ // copy sign + Fc.uq = (Fa.uq<63:> << 63) | Fb.uq<62:0>; + }}); + 0x021: cpysn({{ // copy sign negated + Fc.uq = (~Fa.uq<63:> << 63) | Fb.uq<62:0>; + }}); + 0x022: cpyse({{ // copy sign and exponent + Fc.uq = (Fa.uq<63:52> << 52) | Fb.uq<51:0>; + }}); + + 0x02a: fcmoveq({{ Fc = (Fa == 0) ? Fb : Fc; }}); + 0x02b: fcmovne({{ Fc = (Fa != 0) ? Fb : Fc; }}); + 0x02c: fcmovlt({{ Fc = (Fa < 0) ? Fb : Fc; }}); + 0x02d: fcmovge({{ Fc = (Fa >= 0) ? Fb : Fc; }}); + 0x02e: fcmovle({{ Fc = (Fa <= 0) ? Fb : Fc; }}); + 0x02f: fcmovgt({{ Fc = (Fa > 0) ? Fb : Fc; }}); + + 0x024: mt_fpcr({{ FPCR = Fa.uq; }}, IsIprAccess); + 0x025: mf_fpcr({{ Fa.uq = FPCR; }}, IsIprAccess); + } + } + + // miscellaneous mem-format ops + 0x18: decode MEMFUNC { + format WarnUnimpl { + 0x8000: fetch(); + 0xa000: fetch_m(); + 0xe800: ecb(); + } + + format MiscPrefetch { + 0xf800: wh64({{ EA = Rb & ~ULL(63); }}, + {{ xc->writeHint(EA, 64, memAccessFlags); }}, + mem_flags = NO_FAULT, + inst_flags = [IsMemRef, IsDataPrefetch, + IsStore, MemWriteOp]); + } + + format BasicOperate { + 0xc000: rpcc({{ +#if FULL_SYSTEM + /* Rb is a fake dependency so here is a fun way to get + * the parser to understand that. + */ + Ra = xc->readMiscRegWithEffect(AlphaISA::IPR_CC, fault) + (Rb & 0); + +#else + Ra = curTick; +#endif + }}, IsUnverifiable); + + // All of the barrier instructions below do nothing in + // their execute() methods (hence the empty code blocks). + // All of their functionality is hard-coded in the + // pipeline based on the flags IsSerializing, + // IsMemBarrier, and IsWriteBarrier. In the current + // detailed CPU model, the execute() function only gets + // called at fetch, so there's no way to generate pipeline + // behavior at any other stage. Once we go to an + // exec-in-exec CPU model we should be able to get rid of + // these flags and implement this behavior via the + // execute() methods. + + // trapb is just a barrier on integer traps, where excb is + // a barrier on integer and FP traps. "EXCB is thus a + // superset of TRAPB." (Alpha ARM, Sec 4.11.4) We treat + // them the same though. + 0x0000: trapb({{ }}, IsSerializing, IsSerializeBefore, No_OpClass); + 0x0400: excb({{ }}, IsSerializing, IsSerializeBefore, No_OpClass); + 0x4000: mb({{ }}, IsMemBarrier, MemReadOp); + 0x4400: wmb({{ }}, IsWriteBarrier, MemWriteOp); + } + +#if FULL_SYSTEM + format BasicOperate { + 0xe000: rc({{ + Ra = xc->readIntrFlag(); + xc->setIntrFlag(0); + }}, IsNonSpeculative, IsUnverifiable); + 0xf000: rs({{ + Ra = xc->readIntrFlag(); + xc->setIntrFlag(1); + }}, IsNonSpeculative, IsUnverifiable); + } +#else + format FailUnimpl { + 0xe000: rc(); + 0xf000: rs(); + } +#endif + } + +#if FULL_SYSTEM + 0x00: CallPal::call_pal({{ + if (!palValid || + (palPriv + && xc->readMiscRegWithEffect(AlphaISA::IPR_ICM, fault) != AlphaISA::mode_kernel)) { + // invalid pal function code, or attempt to do privileged + // PAL call in non-kernel mode + fault = new UnimplementedOpcodeFault; + } + else { + // check to see if simulator wants to do something special + // on this PAL call (including maybe suppress it) + bool dopal = xc->simPalCheck(palFunc); + + if (dopal) { + xc->setMiscRegWithEffect(AlphaISA::IPR_EXC_ADDR, NPC); + NPC = xc->readMiscRegWithEffect(AlphaISA::IPR_PAL_BASE, fault) + palOffset; + } + } + }}, IsNonSpeculative); +#else + 0x00: decode PALFUNC { + format EmulatedCallPal { + 0x00: halt ({{ + exitSimLoop(curTick, "halt instruction encountered"); + }}, IsNonSpeculative); + 0x83: callsys({{ + xc->syscall(R0); + }}, IsSerializeAfter, IsNonSpeculative); + // Read uniq reg into ABI return value register (r0) + 0x9e: rduniq({{ R0 = Runiq; }}, IsIprAccess); + // Write uniq reg with value from ABI arg register (r16) + 0x9f: wruniq({{ Runiq = R16; }}, IsIprAccess); + } + } +#endif + +#if FULL_SYSTEM + 0x1b: decode PALMODE { + 0: OpcdecFault::hw_st_quad(); + 1: decode HW_LDST_QUAD { + format HwLoad { + 0: hw_ld({{ EA = (Rb + disp) & ~3; }}, {{ Ra = Mem.ul; }}, L); + 1: hw_ld({{ EA = (Rb + disp) & ~7; }}, {{ Ra = Mem.uq; }}, Q); + } + } + } + + 0x1f: decode PALMODE { + 0: OpcdecFault::hw_st_cond(); + format HwStore { + 1: decode HW_LDST_COND { + 0: decode HW_LDST_QUAD { + 0: hw_st({{ EA = (Rb + disp) & ~3; }}, + {{ Mem.ul = Ra<31:0>; }}, L); + 1: hw_st({{ EA = (Rb + disp) & ~7; }}, + {{ Mem.uq = Ra.uq; }}, Q); + } + + 1: FailUnimpl::hw_st_cond(); + } + } + } + + 0x19: decode PALMODE { + 0: OpcdecFault::hw_mfpr(); + format HwMoveIPR { + 1: hw_mfpr({{ + Ra = xc->readMiscRegWithEffect(ipr_index, fault); + }}, IsIprAccess); + } + } + + 0x1d: decode PALMODE { + 0: OpcdecFault::hw_mtpr(); + format HwMoveIPR { + 1: hw_mtpr({{ + xc->setMiscRegWithEffect(ipr_index, Ra); + if (traceData) { traceData->setData(Ra); } + }}, IsIprAccess); + } + } + + format BasicOperate { + 0x1e: decode PALMODE { + 0: OpcdecFault::hw_rei(); + 1:hw_rei({{ xc->hwrei(); }}, IsSerializing, IsSerializeBefore); + } + + // M5 special opcodes use the reserved 0x01 opcode space + 0x01: decode M5FUNC { + 0x00: arm({{ + AlphaPseudo::arm(xc->tcBase()); + }}, IsNonSpeculative); + 0x01: quiesce({{ + AlphaPseudo::quiesce(xc->tcBase()); + }}, IsNonSpeculative, IsQuiesce); + 0x02: quiesceNs({{ + AlphaPseudo::quiesceNs(xc->tcBase(), R16); + }}, IsNonSpeculative, IsQuiesce); + 0x03: quiesceCycles({{ + AlphaPseudo::quiesceCycles(xc->tcBase(), R16); + }}, IsNonSpeculative, IsQuiesce, IsUnverifiable); + 0x04: quiesceTime({{ + R0 = AlphaPseudo::quiesceTime(xc->tcBase()); + }}, IsNonSpeculative, IsUnverifiable); + 0x10: ivlb({{ + AlphaPseudo::ivlb(xc->tcBase()); + }}, No_OpClass, IsNonSpeculative); + 0x11: ivle({{ + AlphaPseudo::ivle(xc->tcBase()); + }}, No_OpClass, IsNonSpeculative); + 0x20: m5exit_old({{ + AlphaPseudo::m5exit_old(xc->tcBase()); + }}, No_OpClass, IsNonSpeculative); + 0x21: m5exit({{ + AlphaPseudo::m5exit(xc->tcBase(), R16); + }}, No_OpClass, IsNonSpeculative); + 0x31: loadsymbol({{ + AlphaPseudo::loadsymbol(xc->tcBase()); + }}, No_OpClass, IsNonSpeculative); + 0x30: initparam({{ Ra = xc->tcBase()->getCpuPtr()->system->init_param; }}); + 0x40: resetstats({{ + AlphaPseudo::resetstats(xc->tcBase(), R16, R17); + }}, IsNonSpeculative); + 0x41: dumpstats({{ + AlphaPseudo::dumpstats(xc->tcBase(), R16, R17); + }}, IsNonSpeculative); + 0x42: dumpresetstats({{ + AlphaPseudo::dumpresetstats(xc->tcBase(), R16, R17); + }}, IsNonSpeculative); + 0x43: m5checkpoint({{ + AlphaPseudo::m5checkpoint(xc->tcBase(), R16, R17); + }}, IsNonSpeculative); + 0x50: m5readfile({{ + R0 = AlphaPseudo::readfile(xc->tcBase(), R16, R17, R18); + }}, IsNonSpeculative); + 0x51: m5break({{ + AlphaPseudo::debugbreak(xc->tcBase()); + }}, IsNonSpeculative); + 0x52: m5switchcpu({{ + AlphaPseudo::switchcpu(xc->tcBase()); + }}, IsNonSpeculative); + 0x53: m5addsymbol({{ + AlphaPseudo::addsymbol(xc->tcBase(), R16, R17); + }}, IsNonSpeculative); + 0x54: m5panic({{ + panic("M5 panic instruction called at pc=%#x.", xc->readPC()); + }}, IsNonSpeculative); + 0x55: m5anBegin({{ + AlphaPseudo::anBegin(xc->tcBase(), R16); + }}, IsNonSpeculative); + 0x56: m5anWait({{ + AlphaPseudo::anWait(xc->tcBase(), R16, R17); + }}, IsNonSpeculative); + } + } +#endif +} diff --git a/src/arch/alpha/isa/fp.isa b/src/arch/alpha/isa/fp.isa new file mode 100644 index 000000000..b4339a1b7 --- /dev/null +++ b/src/arch/alpha/isa/fp.isa @@ -0,0 +1,312 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2005 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Floating-point instructions +// +// Note that many FP-type instructions which do not support all the +// various rounding & trapping modes use the simpler format +// BasicOperateWithNopCheck. +// + +output exec {{ + /// Check "FP enabled" machine status bit. Called when executing any FP + /// instruction in full-system mode. + /// @retval Full-system mode: NoFault if FP is enabled, FenFault + /// if not. Non-full-system mode: always returns NoFault. +#if FULL_SYSTEM + inline Fault checkFpEnableFault(%(CPU_exec_context)s *xc) + { + Fault fault = NoFault; // dummy... this ipr access should not fault + if (!EV5::ICSR_FPE(xc->readMiscRegWithEffect(AlphaISA::IPR_ICSR, fault))) { + fault = new FloatEnableFault; + } + return fault; + } +#else + inline Fault checkFpEnableFault(%(CPU_exec_context)s *xc) + { + return NoFault; + } +#endif +}}; + +output header {{ + /** + * Base class for general floating-point instructions. Includes + * support for various Alpha rounding and trapping modes. Only FP + * instructions that require this support are derived from this + * class; the rest derive directly from AlphaStaticInst. + */ + class AlphaFP : public AlphaStaticInst + { + public: + /// Alpha FP rounding modes. + enum RoundingMode { + Chopped = 0, ///< round toward zero + Minus_Infinity = 1, ///< round toward minus infinity + Normal = 2, ///< round to nearest (default) + Dynamic = 3, ///< use FPCR setting (in instruction) + Plus_Infinity = 3 ///< round to plus inifinity (in FPCR) + }; + + /// Alpha FP trapping modes. + /// For instructions that produce integer results, the + /// "Underflow Enable" modes really mean "Overflow Enable", and + /// the assembly modifier is V rather than U. + enum TrappingMode { + /// default: nothing enabled + Imprecise = 0, ///< no modifier + /// underflow/overflow traps enabled, inexact disabled + Underflow_Imprecise = 1, ///< /U or /V + Underflow_Precise = 5, ///< /SU or /SV + /// underflow/overflow and inexact traps enabled + Underflow_Inexact_Precise = 7 ///< /SUI or /SVI + }; + + protected: + /// Map Alpha rounding mode to C99 constants from <fenv.h>. + static const int alphaToC99RoundingMode[]; + + /// Map enum RoundingMode values to disassembly suffixes. + static const char *roundingModeSuffix[]; + /// Map enum TrappingMode values to FP disassembly suffixes. + static const char *fpTrappingModeSuffix[]; + /// Map enum TrappingMode values to integer disassembly suffixes. + static const char *intTrappingModeSuffix[]; + + /// This instruction's rounding mode. + RoundingMode roundingMode; + /// This instruction's trapping mode. + TrappingMode trappingMode; + + /// Have we warned about this instruction's unsupported + /// rounding mode (if applicable)? + mutable bool warnedOnRounding; + + /// Have we warned about this instruction's unsupported + /// trapping mode (if applicable)? + mutable bool warnedOnTrapping; + + /// Constructor + AlphaFP(const char *mnem, ExtMachInst _machInst, OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), + roundingMode((enum RoundingMode)FP_ROUNDMODE), + trappingMode((enum TrappingMode)FP_TRAPMODE), + warnedOnRounding(false), + warnedOnTrapping(false) + { + } + + int getC99RoundingMode(uint64_t fpcr_val) const; + + // This differs from the AlphaStaticInst version only in + // printing suffixes for non-default rounding & trapping modes. + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + +}}; + + +output decoder {{ + int + AlphaFP::getC99RoundingMode(uint64_t fpcr_val) const + { + if (roundingMode == Dynamic) { + return alphaToC99RoundingMode[bits(fpcr_val, 59, 58)]; + } + else { + return alphaToC99RoundingMode[roundingMode]; + } + } + + std::string + AlphaFP::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::string mnem_str(mnemonic); + +#ifndef SS_COMPATIBLE_DISASSEMBLY + std::string suffix(""); + suffix += ((_destRegIdx[0] >= FP_Base_DepTag) + ? fpTrappingModeSuffix[trappingMode] + : intTrappingModeSuffix[trappingMode]); + suffix += roundingModeSuffix[roundingMode]; + + if (suffix != "") { + mnem_str = csprintf("%s/%s", mnemonic, suffix); + } +#endif + + std::stringstream ss; + ccprintf(ss, "%-10s ", mnem_str.c_str()); + + // just print the first two source regs... if there's + // a third one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + if (_numSrcRegs > 1) { + ss << ","; + printReg(ss, _srcRegIdx[1]); + } + + // just print the first dest... if there's a second one, + // it's generally implicit + if (_numDestRegs > 0) { + if (_numSrcRegs > 0) + ss << ","; + printReg(ss, _destRegIdx[0]); + } + + return ss.str(); + } + + const int AlphaFP::alphaToC99RoundingMode[] = { + FE_TOWARDZERO, // Chopped + FE_DOWNWARD, // Minus_Infinity + FE_TONEAREST, // Normal + FE_UPWARD // Dynamic in inst, Plus_Infinity in FPCR + }; + + const char *AlphaFP::roundingModeSuffix[] = { "c", "m", "", "d" }; + // mark invalid trapping modes, but don't fail on them, because + // you could decode anything on a misspeculated path + const char *AlphaFP::fpTrappingModeSuffix[] = + { "", "u", "INVTM2", "INVTM3", "INVTM4", "su", "INVTM6", "sui" }; + const char *AlphaFP::intTrappingModeSuffix[] = + { "", "v", "INVTM2", "INVTM3", "INVTM4", "sv", "INVTM6", "svi" }; +}}; + +// FP instruction class execute method template. Handles non-standard +// rounding modes. +def template FloatingPointExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + if (trappingMode != Imprecise && !warnedOnTrapping) { + warn("%s: non-standard trapping mode not supported", + generateDisassembly(0, NULL)); + warnedOnTrapping = true; + } + + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; +#if USE_FENV + if (roundingMode == Normal) { + %(code)s; + } else { + fesetround(getC99RoundingMode( + xc->readMiscReg(AlphaISA::Fpcr_DepTag))); + %(code)s; + fesetround(FE_TONEAREST); + } +#else + if (roundingMode != Normal && !warnedOnRounding) { + warn("%s: non-standard rounding mode not supported", + generateDisassembly(0, NULL)); + warnedOnRounding = true; + } + %(code)s; +#endif + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + +// FP instruction class execute method template where no dynamic +// rounding mode control is needed. Like BasicExecute, but includes +// check & warning for non-standard trapping mode. +def template FPFixedRoundingExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + if (trappingMode != Imprecise && !warnedOnTrapping) { + warn("%s: non-standard trapping mode not supported", + generateDisassembly(0, NULL)); + warnedOnTrapping = true; + } + + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(code)s; + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + +def template FloatingPointDecode {{ + { + AlphaStaticInst *i = new %(class_name)s(machInst); + if (FC == 31) { + i = makeNop(i); + } + return i; + } +}}; + +// General format for floating-point operate instructions: +// - Checks trapping and rounding mode flags. Trapping modes +// currently unimplemented (will fail). +// - Generates NOP if FC == 31. +def format FloatingPointOperate(code, *opt_args) {{ + iop = InstObjParams(name, Name, 'AlphaFP', CodeBlock(code), opt_args) + decode_block = FloatingPointDecode.subst(iop) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + exec_output = FloatingPointExecute.subst(iop) +}}; + +// Special format for cvttq where rounding mode is pre-decoded +def format FPFixedRounding(code, class_suffix, *opt_args) {{ + Name += class_suffix + iop = InstObjParams(name, Name, 'AlphaFP', CodeBlock(code), opt_args) + decode_block = FloatingPointDecode.subst(iop) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + exec_output = FPFixedRoundingExecute.subst(iop) +}}; + diff --git a/src/arch/alpha/isa/int.isa b/src/arch/alpha/isa/int.isa new file mode 100644 index 000000000..45e096ebd --- /dev/null +++ b/src/arch/alpha/isa/int.isa @@ -0,0 +1,135 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2005 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Integer operate instructions +// + +output header {{ + /** + * Base class for integer immediate instructions. + */ + class IntegerImm : public AlphaStaticInst + { + protected: + /// Immediate operand value (unsigned 8-bit int). + uint8_t imm; + + /// Constructor + IntegerImm(const char *mnem, ExtMachInst _machInst, OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), imm(INTIMM) + { + } + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + std::string + IntegerImm::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + // just print the first source reg... if there's + // a second one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + ss << ","; + } + + ss << (int)imm; + + if (_numDestRegs > 0) { + ss << ","; + printReg(ss, _destRegIdx[0]); + } + + return ss.str(); + } +}}; + + +def template RegOrImmDecode {{ + { + AlphaStaticInst *i = + (IMM) ? (AlphaStaticInst *)new %(class_name)sImm(machInst) + : (AlphaStaticInst *)new %(class_name)s(machInst); + if (RC == 31) { + i = makeNop(i); + } + return i; + } +}}; + +// Primary format for integer operate instructions: +// - Generates both reg-reg and reg-imm versions if Rb_or_imm is used. +// - Generates NOP if RC == 31. +def format IntegerOperate(code, *opt_flags) {{ + # If the code block contains 'Rb_or_imm', we define two instructions, + # one using 'Rb' and one using 'imm', and have the decoder select + # the right one. + uses_imm = (code.find('Rb_or_imm') != -1) + if uses_imm: + orig_code = code + # base code is reg version: + # rewrite by substituting 'Rb' for 'Rb_or_imm' + code = re.sub(r'Rb_or_imm', 'Rb', orig_code) + # generate immediate version by substituting 'imm' + # note that imm takes no extenstion, so we extend + # the regexp to replace any extension as well + imm_code = re.sub(r'Rb_or_imm(\.\w+)?', 'imm', orig_code) + + # generate declaration for register version + cblk = CodeBlock(code) + iop = InstObjParams(name, Name, 'AlphaStaticInst', cblk, opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + exec_output = BasicExecute.subst(iop) + + if uses_imm: + # append declaration for imm version + imm_cblk = CodeBlock(imm_code) + imm_iop = InstObjParams(name, Name + 'Imm', 'IntegerImm', imm_cblk, + opt_flags) + header_output += BasicDeclare.subst(imm_iop) + decoder_output += BasicConstructor.subst(imm_iop) + exec_output += BasicExecute.subst(imm_iop) + # decode checks IMM bit to pick correct version + decode_block = RegOrImmDecode.subst(iop) + else: + # no imm version: just check for nop + decode_block = OperateNopCheckDecode.subst(iop) +}}; diff --git a/src/arch/alpha/isa/main.isa b/src/arch/alpha/isa/main.isa new file mode 100644 index 000000000..1270bf8d8 --- /dev/null +++ b/src/arch/alpha/isa/main.isa @@ -0,0 +1,463 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2005 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Alpha ISA description file. +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// +// Output include file directives. +// + +output header {{ +#include <sstream> +#include <iostream> +#include <iomanip> + +#include "config/ss_compatible_fp.hh" +#include "cpu/static_inst.hh" +#include "arch/alpha/faults.hh" +#include "mem/request.hh" // some constructors use MemReq flags +}}; + +output decoder {{ +#include "base/cprintf.hh" +#include "base/fenv.hh" +#include "base/loader/symtab.hh" +#include "config/ss_compatible_fp.hh" +#include "cpu/thread_context.hh" // for Jump::branchTarget() + +#include <math.h> + +using namespace AlphaISA; +}}; + +output exec {{ +#include <math.h> + +#if FULL_SYSTEM +#include "sim/pseudo_inst.hh" +#endif +#include "base/fenv.hh" +#include "config/ss_compatible_fp.hh" +#include "cpu/base.hh" +#include "cpu/exetrace.hh" +#include "sim/sim_exit.hh" +#include "mem/packet_impl.hh" + +using namespace AlphaISA; +}}; + +//////////////////////////////////////////////////////////////////// +// +// Namespace statement. Everything below this line will be in the +// AlphaISAInst namespace. +// + + +namespace AlphaISA; + +//////////////////////////////////////////////////////////////////// +// +// Bitfield definitions. +// + +// Universal (format-independent) fields +def bitfield PALMODE <32:32>; +def bitfield OPCODE <31:26>; +def bitfield RA <25:21>; +def bitfield RB <20:16>; + +// Memory format +def signed bitfield MEMDISP <15: 0>; // displacement +def bitfield MEMFUNC <15: 0>; // function code (same field, unsigned) + +// Memory-format jumps +def bitfield JMPFUNC <15:14>; // function code (disp<15:14>) +def bitfield JMPHINT <13: 0>; // tgt Icache idx hint (disp<13:0>) + +// Branch format +def signed bitfield BRDISP <20: 0>; // displacement + +// Integer operate format(s>; +def bitfield INTIMM <20:13>; // integer immediate (literal) +def bitfield IMM <12:12>; // immediate flag +def bitfield INTFUNC <11: 5>; // function code +def bitfield RC < 4: 0>; // dest reg + +// Floating-point operate format +def bitfield FA <25:21>; +def bitfield FB <20:16>; +def bitfield FP_FULLFUNC <15: 5>; // complete function code + def bitfield FP_TRAPMODE <15:13>; // trapping mode + def bitfield FP_ROUNDMODE <12:11>; // rounding mode + def bitfield FP_TYPEFUNC <10: 5>; // type+func: handiest for decoding + def bitfield FP_SRCTYPE <10: 9>; // source reg type + def bitfield FP_SHORTFUNC < 8: 5>; // short function code + def bitfield FP_SHORTFUNC_TOP2 <8:7>; // top 2 bits of short func code +def bitfield FC < 4: 0>; // dest reg + +// PALcode format +def bitfield PALFUNC <25: 0>; // function code + +// EV5 PAL instructions: +// HW_LD/HW_ST +def bitfield HW_LDST_PHYS <15>; // address is physical +def bitfield HW_LDST_ALT <14>; // use ALT_MODE IPR +def bitfield HW_LDST_WRTCK <13>; // HW_LD only: fault if no write acc +def bitfield HW_LDST_QUAD <12>; // size: 0=32b, 1=64b +def bitfield HW_LDST_VPTE <11>; // HW_LD only: is PTE fetch +def bitfield HW_LDST_LOCK <10>; // HW_LD only: is load locked +def bitfield HW_LDST_COND <10>; // HW_ST only: is store conditional +def signed bitfield HW_LDST_DISP <9:0>; // signed displacement + +// HW_REI +def bitfield HW_REI_TYP <15:14>; // type: stalling vs. non-stallingk +def bitfield HW_REI_MBZ <13: 0>; // must be zero + +// HW_MTPR/MW_MFPR +def bitfield HW_IPR_IDX <15:0>; // IPR index + +// M5 instructions +def bitfield M5FUNC <7:0>; + +def operand_types {{ + 'sb' : ('signed int', 8), + 'ub' : ('unsigned int', 8), + 'sw' : ('signed int', 16), + 'uw' : ('unsigned int', 16), + 'sl' : ('signed int', 32), + 'ul' : ('unsigned int', 32), + 'sq' : ('signed int', 64), + 'uq' : ('unsigned int', 64), + 'sf' : ('float', 32), + 'df' : ('float', 64) +}}; + +def operands {{ + # Int regs default to unsigned, but code should not count on this. + # For clarity, descriptions that depend on unsigned behavior should + # explicitly specify '.uq'. + 'Ra': ('IntReg', 'uq', 'PALMODE ? AlphaISA::reg_redir[RA] : RA', + 'IsInteger', 1), + 'Rb': ('IntReg', 'uq', 'PALMODE ? AlphaISA::reg_redir[RB] : RB', + 'IsInteger', 2), + 'Rc': ('IntReg', 'uq', 'PALMODE ? AlphaISA::reg_redir[RC] : RC', + 'IsInteger', 3), + 'Fa': ('FloatReg', 'df', 'FA', 'IsFloating', 1), + 'Fb': ('FloatReg', 'df', 'FB', 'IsFloating', 2), + 'Fc': ('FloatReg', 'df', 'FC', 'IsFloating', 3), + 'Mem': ('Mem', 'uq', None, ('IsMemRef', 'IsLoad', 'IsStore'), 4), + 'NPC': ('NPC', 'uq', None, ( None, None, 'IsControl' ), 4), + 'Runiq': ('ControlReg', 'uq', 'TheISA::Uniq_DepTag', None, 1), + 'FPCR': (' ControlReg', 'uq', 'TheISA::Fpcr_DepTag', None, 1), + # The next two are hacks for non-full-system call-pal emulation + 'R0': ('IntReg', 'uq', '0', None, 1), + 'R16': ('IntReg', 'uq', '16', None, 1), + 'R17': ('IntReg', 'uq', '17', None, 1), + 'R18': ('IntReg', 'uq', '18', None, 1) +}}; + +//////////////////////////////////////////////////////////////////// +// +// Basic instruction classes/templates/formats etc. +// + +output header {{ +// uncomment the following to get SimpleScalar-compatible disassembly +// (useful for diffing output traces). +// #define SS_COMPATIBLE_DISASSEMBLY + + /** + * Base class for all Alpha static instructions. + */ + class AlphaStaticInst : public StaticInst + { + protected: + + /// Make AlphaISA register dependence tags directly visible in + /// this class and derived classes. Maybe these should really + /// live here and not in the AlphaISA namespace. + enum DependenceTags { + FP_Base_DepTag = AlphaISA::FP_Base_DepTag, + Fpcr_DepTag = AlphaISA::Fpcr_DepTag, + Uniq_DepTag = AlphaISA::Uniq_DepTag, + Lock_Flag_DepTag = AlphaISA::Lock_Flag_DepTag, + Lock_Addr_DepTag = AlphaISA::Lock_Addr_DepTag, + IPR_Base_DepTag = AlphaISA::IPR_Base_DepTag + }; + + /// Constructor. + AlphaStaticInst(const char *mnem, ExtMachInst _machInst, + OpClass __opClass) + : StaticInst(mnem, _machInst, __opClass) + { + } + + /// Print a register name for disassembly given the unique + /// dependence tag number (FP or int). + void printReg(std::ostream &os, int reg) const; + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + void + AlphaStaticInst::printReg(std::ostream &os, int reg) const + { + if (reg < FP_Base_DepTag) { + ccprintf(os, "r%d", reg); + } + else { + ccprintf(os, "f%d", reg - FP_Base_DepTag); + } + } + + std::string + AlphaStaticInst::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + // just print the first two source regs... if there's + // a third one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + if (_numSrcRegs > 1) { + ss << ","; + printReg(ss, _srcRegIdx[1]); + } + + // just print the first dest... if there's a second one, + // it's generally implicit + if (_numDestRegs > 0) { + if (_numSrcRegs > 0) + ss << ","; + printReg(ss, _destRegIdx[0]); + } + + return ss.str(); + } +}}; + +// Declarations for execute() methods. +def template BasicExecDeclare {{ + Fault execute(%(CPU_exec_context)s *, Trace::InstRecord *) const; +}}; + +// Basic instruction class declaration template. +def template BasicDeclare {{ + /** + * Static instruction class for "%(mnemonic)s". + */ + class %(class_name)s : public %(base_class)s + { + public: + /// Constructor. + %(class_name)s(ExtMachInst machInst); + + %(BasicExecDeclare)s + }; +}}; + +// Basic instruction class constructor template. +def template BasicConstructor {{ + inline %(class_name)s::%(class_name)s(ExtMachInst machInst) + : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s) + { + %(constructor)s; + } +}}; + +// Basic instruction class execute method template. +def template BasicExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(code)s; + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + +// Basic decode template. +def template BasicDecode {{ + return new %(class_name)s(machInst); +}}; + +// Basic decode template, passing mnemonic in as string arg to constructor. +def template BasicDecodeWithMnemonic {{ + return new %(class_name)s("%(mnemonic)s", machInst); +}}; + +// The most basic instruction format... used only for a few misc. insts +def format BasicOperate(code, *flags) {{ + iop = InstObjParams(name, Name, 'AlphaStaticInst', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + + + +//////////////////////////////////////////////////////////////////// +// +// Nop +// + +output header {{ + /** + * Static instruction class for no-ops. This is a leaf class. + */ + class Nop : public AlphaStaticInst + { + /// Disassembly of original instruction. + const std::string originalDisassembly; + + public: + /// Constructor + Nop(const std::string _originalDisassembly, ExtMachInst _machInst) + : AlphaStaticInst("nop", _machInst, No_OpClass), + originalDisassembly(_originalDisassembly) + { + flags[IsNop] = true; + } + + ~Nop() { } + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + + %(BasicExecDeclare)s + }; + + /// Helper function for decoding nops. Substitute Nop object + /// for original inst passed in as arg (and delete latter). + static inline + AlphaStaticInst * + makeNop(AlphaStaticInst *inst) + { + AlphaStaticInst *nop = new Nop(inst->disassemble(0), inst->machInst); + delete inst; + return nop; + } +}}; + +output decoder {{ + std::string Nop::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { +#ifdef SS_COMPATIBLE_DISASSEMBLY + return originalDisassembly; +#else + return csprintf("%-10s (%s)", "nop", originalDisassembly); +#endif + } +}}; + +output exec {{ + Fault + Nop::execute(%(CPU_exec_context)s *, Trace::InstRecord *) const + { + return NoFault; + } +}}; + +// integer & FP operate instructions use Rc as dest, so check for +// Rc == 31 to detect nops +def template OperateNopCheckDecode {{ + { + AlphaStaticInst *i = new %(class_name)s(machInst); + if (RC == 31) { + i = makeNop(i); + } + return i; + } +}}; + +// Like BasicOperate format, but generates NOP if RC/FC == 31 +def format BasicOperateWithNopCheck(code, *opt_args) {{ + iop = InstObjParams(name, Name, 'AlphaStaticInst', CodeBlock(code), + opt_args) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = OperateNopCheckDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +// Integer instruction templates, formats, etc. +##include "int.isa" + +// Floating-point instruction templates, formats, etc. +##include "fp.isa" + +// Memory instruction templates, formats, etc. +##include "mem.isa" + +// Branch/jump instruction templates, formats, etc. +##include "branch.isa" + +// PAL instruction templates, formats, etc. +##include "pal.isa" + +// Opcdec fault instruction templates, formats, etc. +##include "opcdec.isa" + +// Unimplemented instruction templates, formats, etc. +##include "unimp.isa" + +// Unknown instruction templates, formats, etc. +##include "unknown.isa" + +// Execution utility functions +##include "util.isa" + +// The actual decoder +##include "decoder.isa" diff --git a/src/arch/alpha/isa/mem.isa b/src/arch/alpha/isa/mem.isa new file mode 100644 index 000000000..fe69c36a5 --- /dev/null +++ b/src/arch/alpha/isa/mem.isa @@ -0,0 +1,736 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2005 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt +// Kevin Lim + +//////////////////////////////////////////////////////////////////// +// +// Memory-format instructions: LoadAddress, Load, Store +// + +output header {{ + /** + * Base class for general Alpha memory-format instructions. + */ + class Memory : public AlphaStaticInst + { + protected: + + /// Memory request flags. See mem_req_base.hh. + unsigned memAccessFlags; + /// Pointer to EAComp object. + const StaticInstPtr eaCompPtr; + /// Pointer to MemAcc object. + const StaticInstPtr memAccPtr; + + /// Constructor + Memory(const char *mnem, ExtMachInst _machInst, OpClass __opClass, + StaticInstPtr _eaCompPtr = nullStaticInstPtr, + StaticInstPtr _memAccPtr = nullStaticInstPtr) + : AlphaStaticInst(mnem, _machInst, __opClass), + memAccessFlags(0), eaCompPtr(_eaCompPtr), memAccPtr(_memAccPtr) + { + } + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + + public: + + const StaticInstPtr &eaCompInst() const { return eaCompPtr; } + const StaticInstPtr &memAccInst() const { return memAccPtr; } + }; + + /** + * Base class for memory-format instructions using a 32-bit + * displacement (i.e. most of them). + */ + class MemoryDisp32 : public Memory + { + protected: + /// Displacement for EA calculation (signed). + int32_t disp; + + /// Constructor. + MemoryDisp32(const char *mnem, ExtMachInst _machInst, OpClass __opClass, + StaticInstPtr _eaCompPtr = nullStaticInstPtr, + StaticInstPtr _memAccPtr = nullStaticInstPtr) + : Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr), + disp(MEMDISP) + { + } + }; + + + /** + * Base class for a few miscellaneous memory-format insts + * that don't interpret the disp field: wh64, fetch, fetch_m, ecb. + * None of these instructions has a destination register either. + */ + class MemoryNoDisp : public Memory + { + protected: + /// Constructor + MemoryNoDisp(const char *mnem, ExtMachInst _machInst, OpClass __opClass, + StaticInstPtr _eaCompPtr = nullStaticInstPtr, + StaticInstPtr _memAccPtr = nullStaticInstPtr) + : Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr) + { + } + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + + +output decoder {{ + std::string + Memory::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return csprintf("%-10s %c%d,%d(r%d)", mnemonic, + flags[IsFloating] ? 'f' : 'r', RA, MEMDISP, RB); + } + + std::string + MemoryNoDisp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return csprintf("%-10s (r%d)", mnemonic, RB); + } +}}; + +def format LoadAddress(code) {{ + iop = InstObjParams(name, Name, 'MemoryDisp32', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + + +def template LoadStoreDeclare {{ + /** + * Static instruction class for "%(mnemonic)s". + */ + class %(class_name)s : public %(base_class)s + { + protected: + + /** + * "Fake" effective address computation class for "%(mnemonic)s". + */ + class EAComp : public %(base_class)s + { + public: + /// Constructor + EAComp(ExtMachInst machInst); + + %(BasicExecDeclare)s + }; + + /** + * "Fake" memory access instruction class for "%(mnemonic)s". + */ + class MemAcc : public %(base_class)s + { + public: + /// Constructor + MemAcc(ExtMachInst machInst); + + %(BasicExecDeclare)s + }; + + public: + + /// Constructor. + %(class_name)s(ExtMachInst machInst); + + %(BasicExecDeclare)s + + %(InitiateAccDeclare)s + + %(CompleteAccDeclare)s + }; +}}; + + +def template InitiateAccDeclare {{ + Fault initiateAcc(%(CPU_exec_context)s *, Trace::InstRecord *) const; +}}; + + +def template CompleteAccDeclare {{ + Fault completeAcc(Packet *, %(CPU_exec_context)s *, + Trace::InstRecord *) const; +}}; + + +def template LoadStoreConstructor {{ + /** TODO: change op_class to AddrGenOp or something (requires + * creating new member of OpClass enum in op_class.hh, updating + * config files, etc.). */ + inline %(class_name)s::EAComp::EAComp(ExtMachInst machInst) + : %(base_class)s("%(mnemonic)s (EAComp)", machInst, IntAluOp) + { + %(ea_constructor)s; + } + + inline %(class_name)s::MemAcc::MemAcc(ExtMachInst machInst) + : %(base_class)s("%(mnemonic)s (MemAcc)", machInst, %(op_class)s) + { + %(memacc_constructor)s; + } + + inline %(class_name)s::%(class_name)s(ExtMachInst machInst) + : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, + new EAComp(machInst), new MemAcc(machInst)) + { + %(constructor)s; + } +}}; + + +def template EACompExecute {{ + Fault + %(class_name)s::EAComp::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(code)s; + + if (fault == NoFault) { + %(op_wb)s; + xc->setEA(EA); + } + + return fault; + } +}}; + +def template LoadMemAccExecute {{ + Fault + %(class_name)s::MemAcc::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + EA = xc->getEA(); + + if (fault == NoFault) { + fault = xc->read(EA, (uint%(mem_acc_size)d_t&)Mem, memAccessFlags); + %(code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + + +def template LoadExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + + if (fault == NoFault) { + fault = xc->read(EA, (uint%(mem_acc_size)d_t&)Mem, memAccessFlags); + %(memacc_code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + + +def template LoadInitiateAcc {{ + Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_src_decl)s; + %(op_rd)s; + %(ea_code)s; + + if (fault == NoFault) { + fault = xc->read(EA, (uint%(mem_acc_size)d_t &)Mem, memAccessFlags); + } + + return fault; + } +}}; + + +def template LoadCompleteAcc {{ + Fault %(class_name)s::completeAcc(Packet *pkt, + %(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + + Mem = pkt->get<typeof(Mem)>(); + + if (fault == NoFault) { + %(memacc_code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + + +def template StoreMemAccExecute {{ + Fault + %(class_name)s::MemAcc::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + uint64_t write_result = 0; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + EA = xc->getEA(); + + if (fault == NoFault) { + %(code)s; + } + + if (fault == NoFault) { + fault = xc->write((uint%(mem_acc_size)d_t&)Mem, EA, + memAccessFlags, &write_result); + if (traceData) { traceData->setData(Mem); } + } + + if (fault == NoFault) { + %(postacc_code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + + +def template StoreExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + uint64_t write_result = 0; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + + if (fault == NoFault) { + %(memacc_code)s; + } + + if (fault == NoFault) { + fault = xc->write((uint%(mem_acc_size)d_t&)Mem, EA, + memAccessFlags, &write_result); + if (traceData) { traceData->setData(Mem); } + } + + if (fault == NoFault) { + %(postacc_code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + +def template StoreInitiateAcc {{ + Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + + if (fault == NoFault) { + %(memacc_code)s; + } + + if (fault == NoFault) { + fault = xc->write((uint%(mem_acc_size)d_t&)Mem, EA, + memAccessFlags, NULL); + if (traceData) { traceData->setData(Mem); } + } + + return fault; + } +}}; + + +def template StoreCompleteAcc {{ + Fault %(class_name)s::completeAcc(Packet *pkt, + %(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_dest_decl)s; + + if (fault == NoFault) { + %(postacc_code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + + +def template StoreCondCompleteAcc {{ + Fault %(class_name)s::completeAcc(Packet *pkt, + %(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_dest_decl)s; + + uint64_t write_result = pkt->req->getScResult(); + + if (fault == NoFault) { + %(postacc_code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + + +def template MiscMemAccExecute {{ + Fault %(class_name)s::MemAcc::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + EA = xc->getEA(); + + if (fault == NoFault) { + %(code)s; + } + + return NoFault; + } +}}; + +def template MiscExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + + if (fault == NoFault) { + %(memacc_code)s; + } + + return NoFault; + } +}}; + +def template MiscInitiateAcc {{ + Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + warn("Misc instruction does not support split access method!"); + return NoFault; + } +}}; + + +def template MiscCompleteAcc {{ + Fault %(class_name)s::completeAcc(Packet *pkt, + %(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + warn("Misc instruction does not support split access method!"); + + return NoFault; + } +}}; + +// load instructions use Ra as dest, so check for +// Ra == 31 to detect nops +def template LoadNopCheckDecode {{ + { + AlphaStaticInst *i = new %(class_name)s(machInst); + if (RA == 31) { + i = makeNop(i); + } + return i; + } +}}; + + +// for some load instructions, Ra == 31 indicates a prefetch (not a nop) +def template LoadPrefetchCheckDecode {{ + { + if (RA != 31) { + return new %(class_name)s(machInst); + } + else { + return new %(class_name)sPrefetch(machInst); + } + } +}}; + + +let {{ +def LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + postacc_code = '', base_class = 'MemoryDisp32', + decode_template = BasicDecode, exec_template_base = ''): + # Make sure flags are in lists (convert to lists if not). + mem_flags = makeList(mem_flags) + inst_flags = makeList(inst_flags) + + # add hook to get effective addresses into execution trace output. + ea_code += '\nif (traceData) { traceData->setAddr(EA); }\n' + + # generate code block objects + ea_cblk = CodeBlock(ea_code) + memacc_cblk = CodeBlock(memacc_code) + postacc_cblk = CodeBlock(postacc_code) + + # Some CPU models execute the memory operation as an atomic unit, + # while others want to separate them into an effective address + # computation and a memory access operation. As a result, we need + # to generate three StaticInst objects. Note that the latter two + # are nested inside the larger "atomic" one. + + # generate InstObjParams for EAComp object + ea_iop = InstObjParams(name, Name, base_class, ea_cblk, inst_flags) + + # generate InstObjParams for MemAcc object + memacc_iop = InstObjParams(name, Name, base_class, memacc_cblk, inst_flags) + # in the split execution model, the MemAcc portion is responsible + # for the post-access code. + memacc_iop.postacc_code = postacc_cblk.code + + # generate InstObjParams for InitiateAcc, CompleteAcc object + # The code used depends on the template being used + if (exec_template_base == 'Load'): + initiateacc_cblk = CodeBlock(ea_code + memacc_code) + completeacc_cblk = CodeBlock(memacc_code + postacc_code) + elif (exec_template_base.startswith('Store')): + initiateacc_cblk = CodeBlock(ea_code + memacc_code) + completeacc_cblk = CodeBlock(postacc_code) + else: + initiateacc_cblk = '' + completeacc_cblk = '' + + initiateacc_iop = InstObjParams(name, Name, base_class, initiateacc_cblk, + inst_flags) + + completeacc_iop = InstObjParams(name, Name, base_class, completeacc_cblk, + inst_flags) + + if (exec_template_base == 'Load'): + initiateacc_iop.ea_code = ea_cblk.code + initiateacc_iop.memacc_code = memacc_cblk.code + completeacc_iop.memacc_code = memacc_cblk.code + completeacc_iop.postacc_code = postacc_cblk.code + elif (exec_template_base.startswith('Store')): + initiateacc_iop.ea_code = ea_cblk.code + initiateacc_iop.memacc_code = memacc_cblk.code + completeacc_iop.postacc_code = postacc_cblk.code + + # generate InstObjParams for unified execution + cblk = CodeBlock(ea_code + memacc_code + postacc_code) + iop = InstObjParams(name, Name, base_class, cblk, inst_flags) + + iop.ea_constructor = ea_cblk.constructor + iop.ea_code = ea_cblk.code + iop.memacc_constructor = memacc_cblk.constructor + iop.memacc_code = memacc_cblk.code + iop.postacc_code = postacc_cblk.code + + if mem_flags: + s = '\n\tmemAccessFlags = ' + string.join(mem_flags, '|') + ';' + iop.constructor += s + memacc_iop.constructor += s + + # select templates + + # define aliases... most StoreCond templates are the same as the + # corresponding Store templates (only CompleteAcc is different). + StoreCondMemAccExecute = StoreMemAccExecute + StoreCondExecute = StoreExecute + StoreCondInitiateAcc = StoreInitiateAcc + + memAccExecTemplate = eval(exec_template_base + 'MemAccExecute') + fullExecTemplate = eval(exec_template_base + 'Execute') + initiateAccTemplate = eval(exec_template_base + 'InitiateAcc') + completeAccTemplate = eval(exec_template_base + 'CompleteAcc') + + # (header_output, decoder_output, decode_block, exec_output) + return (LoadStoreDeclare.subst(iop), LoadStoreConstructor.subst(iop), + decode_template.subst(iop), + EACompExecute.subst(ea_iop) + + memAccExecTemplate.subst(memacc_iop) + + fullExecTemplate.subst(iop) + + initiateAccTemplate.subst(initiateacc_iop) + + completeAccTemplate.subst(completeacc_iop)) +}}; + +def format LoadOrNop(memacc_code, ea_code = {{ EA = Rb + disp; }}, + mem_flags = [], inst_flags = []) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + decode_template = LoadNopCheckDecode, + exec_template_base = 'Load') +}}; + + +// Note that the flags passed in apply only to the prefetch version +def format LoadOrPrefetch(memacc_code, ea_code = {{ EA = Rb + disp; }}, + mem_flags = [], pf_flags = [], inst_flags = []) {{ + # declare the load instruction object and generate the decode block + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + decode_template = LoadPrefetchCheckDecode, + exec_template_base = 'Load') + + # Declare the prefetch instruction object. + + # Make sure flag args are lists so we can mess with them. + mem_flags = makeList(mem_flags) + pf_flags = makeList(pf_flags) + inst_flags = makeList(inst_flags) + + pf_mem_flags = mem_flags + pf_flags + ['NO_FAULT'] + pf_inst_flags = inst_flags + ['IsMemRef', 'IsLoad', + 'IsDataPrefetch', 'MemReadOp'] + + (pf_header_output, pf_decoder_output, _, pf_exec_output) = \ + LoadStoreBase(name, Name + 'Prefetch', ea_code, + 'xc->prefetch(EA, memAccessFlags);', + pf_mem_flags, pf_inst_flags, exec_template_base = 'Misc') + + header_output += pf_header_output + decoder_output += pf_decoder_output + exec_output += pf_exec_output +}}; + + +def format Store(memacc_code, ea_code = {{ EA = Rb + disp; }}, + mem_flags = [], inst_flags = []) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + exec_template_base = 'Store') +}}; + + +def format StoreCond(memacc_code, postacc_code, + ea_code = {{ EA = Rb + disp; }}, + mem_flags = [], inst_flags = []) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + postacc_code, exec_template_base = 'StoreCond') +}}; + + +// Use 'MemoryNoDisp' as base: for wh64, fetch, ecb +def format MiscPrefetch(ea_code, memacc_code, + mem_flags = [], inst_flags = []) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + base_class = 'MemoryNoDisp', exec_template_base = 'Misc') +}}; + + diff --git a/src/arch/alpha/isa/opcdec.isa b/src/arch/alpha/isa/opcdec.isa new file mode 100644 index 000000000..d279ae050 --- /dev/null +++ b/src/arch/alpha/isa/opcdec.isa @@ -0,0 +1,79 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2005 The Regents of The University of Michigan +// 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. +// +// Authors: Kevin Lim + +//////////////////////////////////////////////////////////////////// +// +// OPCDEC fault instructions +// + +output header {{ + /** + * Static instruction class for instructions that cause an OPCDEC fault + * when executed. This is currently only for PAL mode instructions + * executed in non-PAL mode. + */ + class OpcdecFault : public AlphaStaticInst + { + public: + /// Constructor + OpcdecFault(ExtMachInst _machInst) + : AlphaStaticInst("opcdec fault", _machInst, No_OpClass) + { + } + + %(BasicExecDeclare)s + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + std::string + OpcdecFault::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return csprintf("%-10s (inst 0x%x, opcode 0x%x)", + " OPCDEC fault", machInst, OPCODE); + } +}}; + +output exec {{ + Fault + OpcdecFault::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + return new UnimplementedOpcodeFault; + } +}}; + +def format OpcdecFault() {{ + decode_block = 'return new OpcdecFault(machInst);\n' +}}; + diff --git a/src/arch/alpha/isa/pal.isa b/src/arch/alpha/isa/pal.isa new file mode 100644 index 000000000..f4c10da1d --- /dev/null +++ b/src/arch/alpha/isa/pal.isa @@ -0,0 +1,280 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2005 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// PAL calls & PAL-specific instructions +// + +output header {{ + /** + * Base class for emulated call_pal calls (used only in + * non-full-system mode). + */ + class EmulatedCallPal : public AlphaStaticInst + { + protected: + + /// Constructor. + EmulatedCallPal(const char *mnem, ExtMachInst _machInst, + OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass) + { + } + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + std::string + EmulatedCallPal::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { +#ifdef SS_COMPATIBLE_DISASSEMBLY + return csprintf("%s %s", "call_pal", mnemonic); +#else + return csprintf("%-10s %s", "call_pal", mnemonic); +#endif + } +}}; + +def format EmulatedCallPal(code, *flags) {{ + iop = InstObjParams(name, Name, 'EmulatedCallPal', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +output header {{ + /** + * Base class for full-system-mode call_pal instructions. + * Probably could turn this into a leaf class and get rid of the + * parser template. + */ + class CallPalBase : public AlphaStaticInst + { + protected: + int palFunc; ///< Function code part of instruction + int palOffset; ///< Target PC, offset from IPR_PAL_BASE + bool palValid; ///< is the function code valid? + bool palPriv; ///< is this call privileged? + + /// Constructor. + CallPalBase(const char *mnem, ExtMachInst _machInst, + OpClass __opClass); + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + inline + CallPalBase::CallPalBase(const char *mnem, ExtMachInst _machInst, + OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), + palFunc(PALFUNC) + { + // From the 21164 HRM (paraphrased): + // Bit 7 of the function code (mask 0x80) indicates + // whether the call is privileged (bit 7 == 0) or + // unprivileged (bit 7 == 1). The privileged call table + // starts at 0x2000, the unprivielged call table starts at + // 0x3000. Bits 5-0 (mask 0x3f) are used to calculate the + // offset. + const int palPrivMask = 0x80; + const int palOffsetMask = 0x3f; + + // Pal call is invalid unless all other bits are 0 + palValid = ((machInst & ~(palPrivMask | palOffsetMask)) == 0); + palPriv = ((machInst & palPrivMask) == 0); + int shortPalFunc = (machInst & palOffsetMask); + // Add 1 to base to set pal-mode bit + palOffset = (palPriv ? 0x2001 : 0x3001) + (shortPalFunc << 6); + } + + std::string + CallPalBase::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return csprintf("%-10s %#x", "call_pal", palFunc); + } +}}; + +def format CallPal(code, *flags) {{ + iop = InstObjParams(name, Name, 'CallPalBase', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +//////////////////////////////////////////////////////////////////// +// +// hw_ld, hw_st +// + +output header {{ + /** + * Base class for hw_ld and hw_st. + */ + class HwLoadStore : public Memory + { + protected: + + /// Displacement for EA calculation (signed). + int16_t disp; + + /// Constructor + HwLoadStore(const char *mnem, ExtMachInst _machInst, OpClass __opClass, + StaticInstPtr _eaCompPtr = nullStaticInstPtr, + StaticInstPtr _memAccPtr = nullStaticInstPtr); + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + + +output decoder {{ + inline + HwLoadStore::HwLoadStore(const char *mnem, ExtMachInst _machInst, + OpClass __opClass, + StaticInstPtr _eaCompPtr, + StaticInstPtr _memAccPtr) + : Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr), + disp(HW_LDST_DISP) + { + memAccessFlags = 0; + if (HW_LDST_PHYS) memAccessFlags |= PHYSICAL; + if (HW_LDST_ALT) memAccessFlags |= ALTMODE; + if (HW_LDST_VPTE) memAccessFlags |= VPTE; + if (HW_LDST_LOCK) memAccessFlags |= LOCKED; + } + + std::string + HwLoadStore::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { +#ifdef SS_COMPATIBLE_DISASSEMBLY + return csprintf("%-10s r%d,%d(r%d)", mnemonic, RA, disp, RB); +#else + // HW_LDST_LOCK and HW_LDST_COND are the same bit. + const char *lock_str = + (HW_LDST_LOCK) ? (flags[IsLoad] ? ",LOCK" : ",COND") : ""; + + return csprintf("%-10s r%d,%d(r%d)%s%s%s%s%s", + mnemonic, RA, disp, RB, + HW_LDST_PHYS ? ",PHYS" : "", + HW_LDST_ALT ? ",ALT" : "", + HW_LDST_QUAD ? ",QUAD" : "", + HW_LDST_VPTE ? ",VPTE" : "", + lock_str); +#endif + } +}}; + +def format HwLoad(ea_code, memacc_code, class_ext, *flags) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name + class_ext, ea_code, memacc_code, + mem_flags = [], inst_flags = flags, + base_class = 'HwLoadStore', exec_template_base = 'Load') +}}; + + +def format HwStore(ea_code, memacc_code, class_ext, *flags) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name + class_ext, ea_code, memacc_code, + mem_flags = [], inst_flags = flags, + base_class = 'HwLoadStore', exec_template_base = 'Store') +}}; + + +def format HwStoreCond(ea_code, memacc_code, postacc_code, class_ext, + *flags) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name + class_ext, ea_code, memacc_code, + postacc_code, mem_flags = [], inst_flags = flags, + base_class = 'HwLoadStore') +}}; + + +output header {{ + /** + * Base class for hw_mfpr and hw_mtpr. + */ + class HwMoveIPR : public AlphaStaticInst + { + protected: + /// Index of internal processor register. + int ipr_index; + + /// Constructor + HwMoveIPR(const char *mnem, ExtMachInst _machInst, OpClass __opClass) + : AlphaStaticInst(mnem, _machInst, __opClass), + ipr_index(HW_IPR_IDX) + { + } + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + std::string + HwMoveIPR::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + if (_numSrcRegs > 0) { + // must be mtpr + return csprintf("%-10s r%d,IPR(%#x)", + mnemonic, RA, ipr_index); + } + else { + // must be mfpr + return csprintf("%-10s IPR(%#x),r%d", + mnemonic, ipr_index, RA); + } + } +}}; + +def format HwMoveIPR(code, *flags) {{ + all_flags = ['IprAccessOp'] + all_flags += flags + iop = InstObjParams(name, Name, 'HwMoveIPR', CodeBlock(code), + all_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + + diff --git a/src/arch/alpha/isa/unimp.isa b/src/arch/alpha/isa/unimp.isa new file mode 100644 index 000000000..6cfaa6991 --- /dev/null +++ b/src/arch/alpha/isa/unimp.isa @@ -0,0 +1,172 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2005 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Unimplemented instructions +// + +output header {{ + /** + * Static instruction class for unimplemented instructions that + * cause simulator termination. Note that these are recognized + * (legal) instructions that the simulator does not support; the + * 'Unknown' class is used for unrecognized/illegal instructions. + * This is a leaf class. + */ + class FailUnimplemented : public AlphaStaticInst + { + public: + /// Constructor + FailUnimplemented(const char *_mnemonic, ExtMachInst _machInst) + : AlphaStaticInst(_mnemonic, _machInst, No_OpClass) + { + // don't call execute() (which panics) if we're on a + // speculative path + flags[IsNonSpeculative] = true; + } + + %(BasicExecDeclare)s + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + /** + * Base class for unimplemented instructions that cause a warning + * to be printed (but do not terminate simulation). This + * implementation is a little screwy in that it will print a + * warning for each instance of a particular unimplemented machine + * instruction, not just for each unimplemented opcode. Should + * probably make the 'warned' flag a static member of the derived + * class. + */ + class WarnUnimplemented : public AlphaStaticInst + { + private: + /// Have we warned on this instruction yet? + mutable bool warned; + + public: + /// Constructor + WarnUnimplemented(const char *_mnemonic, ExtMachInst _machInst) + : AlphaStaticInst(_mnemonic, _machInst, No_OpClass), warned(false) + { + // don't call execute() (which panics) if we're on a + // speculative path + flags[IsNonSpeculative] = true; + } + + %(BasicExecDeclare)s + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + std::string + FailUnimplemented::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + return csprintf("%-10s (unimplemented)", mnemonic); + } + + std::string + WarnUnimplemented::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { +#ifdef SS_COMPATIBLE_DISASSEMBLY + return csprintf("%-10s", mnemonic); +#else + return csprintf("%-10s (unimplemented)", mnemonic); +#endif + } +}}; + +output exec {{ + Fault + FailUnimplemented::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + panic("attempt to execute unimplemented instruction '%s' " + "(inst 0x%08x, opcode 0x%x)", mnemonic, machInst, OPCODE); + return new UnimplementedOpcodeFault; + } + + Fault + WarnUnimplemented::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + if (!warned) { + warn("instruction '%s' unimplemented\n", mnemonic); + warned = true; + } + + return NoFault; + } +}}; + + +def format FailUnimpl() {{ + iop = InstObjParams(name, 'FailUnimplemented') + decode_block = BasicDecodeWithMnemonic.subst(iop) +}}; + +def format WarnUnimpl() {{ + iop = InstObjParams(name, 'WarnUnimplemented') + decode_block = BasicDecodeWithMnemonic.subst(iop) +}}; + +output header {{ + /** + * Static instruction class for unknown (illegal) instructions. + * These cause simulator termination if they are executed in a + * non-speculative mode. This is a leaf class. + */ + class Unknown : public AlphaStaticInst + { + public: + /// Constructor + Unknown(ExtMachInst _machInst) + : AlphaStaticInst("unknown", _machInst, No_OpClass) + { + // don't call execute() (which panics) if we're on a + // speculative path + flags[IsNonSpeculative] = true; + } + + %(BasicExecDeclare)s + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + diff --git a/src/arch/alpha/isa/unknown.isa b/src/arch/alpha/isa/unknown.isa new file mode 100644 index 000000000..1e95ccf68 --- /dev/null +++ b/src/arch/alpha/isa/unknown.isa @@ -0,0 +1,59 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2005 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Unknown instructions +// + +output decoder {{ + std::string + Unknown::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return csprintf("%-10s (inst 0x%x, opcode 0x%x)", + "unknown", machInst, OPCODE); + } +}}; + +output exec {{ + Fault + Unknown::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + panic("attempt to execute unknown instruction " + "(inst 0x%08x, opcode 0x%x)", machInst, OPCODE); + return new UnimplementedOpcodeFault; + } +}}; + +def format Unknown() {{ + decode_block = 'return new Unknown(machInst);\n' +}}; + diff --git a/src/arch/alpha/isa/util.isa b/src/arch/alpha/isa/util.isa new file mode 100644 index 000000000..8700d1e0b --- /dev/null +++ b/src/arch/alpha/isa/util.isa @@ -0,0 +1,119 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2005 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Utility functions for execute methods +// + +output exec {{ + + /// Return opa + opb, summing carry into third arg. + inline uint64_t + addc(uint64_t opa, uint64_t opb, int &carry) + { + uint64_t res = opa + opb; + if (res < opa || res < opb) + ++carry; + return res; + } + + /// Multiply two 64-bit values (opa * opb), returning the 128-bit + /// product in res_hi and res_lo. + inline void + mul128(uint64_t opa, uint64_t opb, uint64_t &res_hi, uint64_t &res_lo) + { + // do a 64x64 --> 128 multiply using four 32x32 --> 64 multiplies + uint64_t opa_hi = opa<63:32>; + uint64_t opa_lo = opa<31:0>; + uint64_t opb_hi = opb<63:32>; + uint64_t opb_lo = opb<31:0>; + + res_lo = opa_lo * opb_lo; + + // The middle partial products logically belong in bit + // positions 95 to 32. Thus the lower 32 bits of each product + // sum into the upper 32 bits of the low result, while the + // upper 32 sum into the low 32 bits of the upper result. + uint64_t partial1 = opa_hi * opb_lo; + uint64_t partial2 = opa_lo * opb_hi; + + uint64_t partial1_lo = partial1<31:0> << 32; + uint64_t partial1_hi = partial1<63:32>; + uint64_t partial2_lo = partial2<31:0> << 32; + uint64_t partial2_hi = partial2<63:32>; + + // Add partial1_lo and partial2_lo to res_lo, keeping track + // of any carries out + int carry_out = 0; + res_lo = addc(partial1_lo, res_lo, carry_out); + res_lo = addc(partial2_lo, res_lo, carry_out); + + // Now calculate the high 64 bits... + res_hi = (opa_hi * opb_hi) + partial1_hi + partial2_hi + carry_out; + } + + /// Map 8-bit S-floating exponent to 11-bit T-floating exponent. + /// See Table 2-2 of Alpha AHB. + inline int + map_s(int old_exp) + { + int hibit = old_exp<7:>; + int lobits = old_exp<6:0>; + + if (hibit == 1) { + return (lobits == 0x7f) ? 0x7ff : (0x400 | lobits); + } + else { + return (lobits == 0) ? 0 : (0x380 | lobits); + } + } + + /// Convert a 32-bit S-floating value to the equivalent 64-bit + /// representation to be stored in an FP reg. + inline uint64_t + s_to_t(uint32_t s_val) + { + uint64_t tmp = s_val; + return (tmp<31:> << 63 // sign bit + | (uint64_t)map_s(tmp<30:23>) << 52 // exponent + | tmp<22:0> << 29); // fraction + } + + /// Convert a 64-bit T-floating value to the equivalent 32-bit + /// S-floating representation to be stored in memory. + inline int32_t + t_to_s(uint64_t t_val) + { + return (t_val<63:62> << 30 // sign bit & hi exp bit + | t_val<58:29>); // rest of exp & fraction + } +}}; + diff --git a/src/arch/alpha/isa_traits.hh b/src/arch/alpha/isa_traits.hh new file mode 100644 index 000000000..c59d93238 --- /dev/null +++ b/src/arch/alpha/isa_traits.hh @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Gabe Black + */ + +#ifndef __ARCH_ALPHA_ISA_TRAITS_HH__ +#define __ARCH_ALPHA_ISA_TRAITS_HH__ + +namespace LittleEndianGuest {} + +#include "arch/alpha/types.hh" +#include "config/full_system.hh" +#include "sim/host.hh" + +class StaticInstPtr; + +namespace AlphaISA +{ + + typedef uint32_t MachInst; + typedef uint64_t ExtMachInst; + typedef uint8_t RegIndex; + + const int NumIntArchRegs = 32; + const int NumPALShadowRegs = 8; + const int NumFloatArchRegs = 32; + // @todo: Figure out what this number really should be. + const int NumMiscArchRegs = 32; + + // Static instruction parameters + const int MaxInstSrcRegs = 3; + const int MaxInstDestRegs = 2; + + // semantically meaningful register indices + const int ZeroReg = 31; // architecturally meaningful + // the rest of these depend on the ABI + const int StackPointerReg = 30; + const int GlobalPointerReg = 29; + const int ProcedureValueReg = 27; + const int ReturnAddressReg = 26; + const int ReturnValueReg = 0; + const int FramePointerReg = 15; + const int ArgumentReg0 = 16; + const int ArgumentReg1 = 17; + const int ArgumentReg2 = 18; + const int ArgumentReg3 = 19; + const int ArgumentReg4 = 20; + const int ArgumentReg5 = 21; + const int SyscallNumReg = ReturnValueReg; + const int SyscallPseudoReturnReg = ArgumentReg4; + const int SyscallSuccessReg = 19; + + + + const int LogVMPageSize = 13; // 8K bytes + const int VMPageSize = (1 << LogVMPageSize); + + const int BranchPredAddrShiftAmt = 2; // instructions are 4-byte aligned + + const int WordBytes = 4; + const int HalfwordBytes = 2; + const int ByteBytes = 1; + + + const int NumIntRegs = NumIntArchRegs + NumPALShadowRegs; + const int NumFloatRegs = NumFloatArchRegs; + const int NumMiscRegs = NumMiscArchRegs; + + // These enumerate all the registers for dependence tracking. + enum DependenceTags { + // 0..31 are the integer regs 0..31 + // 32..63 are the FP regs 0..31, i.e. use (reg + FP_Base_DepTag) + FP_Base_DepTag = 40, + Ctrl_Base_DepTag = 72, + Fpcr_DepTag = 72, // floating point control register + Uniq_DepTag = 73, + Lock_Flag_DepTag = 74, + Lock_Addr_DepTag = 75, + IPR_Base_DepTag = 76 + }; + + typedef uint64_t IntReg; + typedef IntReg IntRegFile[NumIntRegs]; + + // floating point register file entry type + typedef union { + uint64_t q; + double d; + } FloatReg; + + typedef union { + uint64_t q[NumFloatRegs]; // integer qword view + double d[NumFloatRegs]; // double-precision floating point view + + void clear() + { bzero(d, sizeof(d)); } + } FloatRegFile; + +extern const Addr PageShift; +extern const Addr PageBytes; +extern const Addr PageMask; +extern const Addr PageOffset; + +// redirected register map, really only used for the full system case. +extern const int reg_redir[NumIntRegs]; + +#if FULL_SYSTEM + + typedef uint64_t InternalProcReg; + +#include "arch/alpha/isa_fullsys_traits.hh" + +#else + const int NumInternalProcRegs = 0; +#endif + + // control register file contents + typedef uint64_t MiscReg; + class MiscRegFile { + protected: + uint64_t fpcr; // floating point condition codes + uint64_t uniq; // process-unique register + bool lock_flag; // lock flag for LL/SC + Addr lock_addr; // lock address for LL/SC + + public: + MiscReg readReg(int misc_reg); + + //These functions should be removed once the simplescalar cpu model + //has been replaced. + int getInstAsid(); + int getDataAsid(); + + MiscReg readRegWithEffect(int misc_reg, Fault &fault, ExecContext *xc); + + Fault setReg(int misc_reg, const MiscReg &val); + + Fault setRegWithEffect(int misc_reg, const MiscReg &val, + ExecContext *xc); + + void serialize(std::ostream &os); + + void unserialize(Checkpoint *cp, const std::string §ion); + + void clear() + { + fpcr = uniq = 0; + lock_flag = 0; + lock_addr = 0; + } + +#if FULL_SYSTEM + protected: + InternalProcReg ipr[NumInternalProcRegs]; // Internal processor regs + + private: + MiscReg readIpr(int idx, Fault &fault, ExecContext *xc); + + Fault setIpr(int idx, uint64_t val, ExecContext *xc); + + void copyIprs(ExecContext *xc); +#endif + friend class RegFile; + }; + + const int TotalNumRegs = NumIntRegs + NumFloatRegs + + NumMiscRegs + NumInternalProcRegs; + + const int TotalDataRegs = NumIntRegs + NumFloatRegs; + + typedef union { + IntReg intreg; + FloatReg fpreg; + MiscReg ctrlreg; + } AnyReg; + + struct RegFile { + IntRegFile intRegFile; // (signed) integer register file + FloatRegFile floatRegFile; // floating point register file + MiscRegFile miscRegs; // control register file + Addr pc; // program counter + Addr npc; // next-cycle program counter + Addr nnpc; + +#if FULL_SYSTEM + int intrflag; // interrupt flag + inline int instAsid() + { return EV5::ITB_ASN_ASN(miscRegs.ipr[IPR_ITB_ASN]); } + inline int dataAsid() + { return EV5::DTB_ASN_ASN(miscRegs.ipr[IPR_DTB_ASN]); } +#endif // FULL_SYSTEM + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + void clear() + { + bzero(intRegFile, sizeof(intRegFile)); + floatRegFile.clear(); + miscRegs.clear(); + } + }; + + static inline ExtMachInst makeExtMI(MachInst inst, const uint64_t &pc); + + StaticInstPtr decodeInst(ExtMachInst); + + // Alpha Does NOT have a delay slot + #define ISA_HAS_DELAY_SLOT 0 + + // return a no-op instruction... used for instruction fetch faults + extern const ExtMachInst NoopMachInst; + + enum annotes { + ANNOTE_NONE = 0, + // An impossible number for instruction annotations + ITOUCH_ANNOTE = 0xffffffff, + }; + + static inline bool isCallerSaveIntegerRegister(unsigned int reg) { + panic("register classification not implemented"); + return (reg >= 1 && reg <= 8 || reg >= 22 && reg <= 25 || reg == 27); + } + + static inline bool isCalleeSaveIntegerRegister(unsigned int reg) { + panic("register classification not implemented"); + return (reg >= 9 && reg <= 15); + } + + static inline bool isCallerSaveFloatRegister(unsigned int reg) { + panic("register classification not implemented"); + return false; + } + + static inline bool isCalleeSaveFloatRegister(unsigned int reg) { + panic("register classification not implemented"); + return false; + } + + static inline Addr alignAddress(const Addr &addr, + unsigned int nbytes) { + return (addr & ~(nbytes - 1)); + } + + // Instruction address compression hooks + static inline Addr realPCToFetchPC(const Addr &addr) { + return addr; + } + + static inline Addr fetchPCToRealPC(const Addr &addr) { + return addr; + } + + // the size of "fetched" instructions (not necessarily the size + // of real instructions for PISA) + static inline size_t fetchInstSize() { + return sizeof(MachInst); + } + + static inline MachInst makeRegisterCopy(int dest, int src) { + panic("makeRegisterCopy not implemented"); + return 0; + } + + // Machine operations + + void saveMachineReg(AnyReg &savereg, const RegFile ®_file, + int regnum); + + void restoreMachineReg(RegFile ®s, const AnyReg ®, + int regnum); + +#if 0 + static void serializeSpecialRegs(const Serializable::Proxy &proxy, + const RegFile ®s); + + static void unserializeSpecialRegs(const IniFile *db, + const std::string &category, + ConfigNode *node, + RegFile ®s); +#endif + + /** + * Function to insure ISA semantics about 0 registers. + * @param xc The execution context. + */ + template <class XC> + void zeroRegisters(XC *xc); + + const Addr MaxAddr = (Addr)-1; + +#if !FULL_SYSTEM + static inline void setSyscallReturn(SyscallReturn return_value, RegFile *regs) + { + // check for error condition. Alpha syscall convention is to + // indicate success/failure in reg a3 (r19) and put the + // return value itself in the standard return value reg (v0). + if (return_value.successful()) { + // no error + regs->intRegFile[SyscallSuccessReg] = 0; + regs->intRegFile[ReturnValueReg] = return_value.value(); + } else { + // got an error, return details + regs->intRegFile[SyscallSuccessReg] = (IntReg) -1; + regs->intRegFile[ReturnValueReg] = -return_value.value(); + } + } +#endif + + void copyRegs(ExecContext *src, ExecContext *dest); + + void copyMiscRegs(ExecContext *src, ExecContext *dest); + +#if FULL_SYSTEM + void copyIprs(ExecContext *src, ExecContext *dest); +#endif +}; + +#endif // __ARCH_ALPHA_ISA_TRAITS_HH__ diff --git a/src/arch/alpha/linux/aligned.hh b/src/arch/alpha/linux/aligned.hh new file mode 100644 index 000000000..c4687e348 --- /dev/null +++ b/src/arch/alpha/linux/aligned.hh @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2004 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Nathan Binkert + */ + +#ifndef __ARCH_ALPHA_LINUX_ALIGNED_HH__ +#define __ARCH_ALPHA_LINUX_ALIGNED_HH__ + + +/* GCC 3.3.X has a bug in which attributes+typedefs don't work. 3.2.X is fine + * as in 3.4.X, but the bug is marked will not fix in 3.3.X so here is + * the work around. + */ +#if (__GNUC__ == 3 && __GNUC_MINOR__ != 3) || __GNUC__ > 3 +typedef uint64_t uint64_ta __attribute__ ((aligned (8))) ; +typedef int64_t int64_ta __attribute__ ((aligned (8))) ; +typedef Addr Addr_a __attribute__ ((aligned (8))) ; +#else +#define uint64_ta uint64_t __attribute__ ((aligned (8))) +#define int64_ta int64_t __attribute__ ((aligned (8))) +#define Addr_a Addr __attribute__ ((aligned (8))) +#endif /* __GNUC__ __GNUC_MINOR__ */ + +#endif /* __ARCH_ALPHA_LINUX_ALIGNED_HH__ */ diff --git a/src/arch/alpha/linux/hwrpb.hh b/src/arch/alpha/linux/hwrpb.hh new file mode 100644 index 000000000..869ce026b --- /dev/null +++ b/src/arch/alpha/linux/hwrpb.hh @@ -0,0 +1,42 @@ +/* + * Copyright 1990 Hewlett-Packard Development Company, L.P. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __ARCH_ALPHA_LINUX_HWRPB_HH__ +#define __ARCH_ALPHA_LINUX_HWRPB_HH__ + +#include "arch/alpha/linux/aligned.hh" + +namespace Linux { + struct pcb_struct { + uint64_ta rpb_ksp; + uint64_ta rpb_usp; + uint64_ta rpb_ptbr; + uint32_t rpb_cc; + uint32_t rpb_psn; + uint64_ta rpb_unique; + uint64_ta rpb_fen; + uint64_ta res1, res2; + }; +} +#endif // __ARCH_ALPHA_LINUX_HWRPB_HH__ diff --git a/src/arch/alpha/linux/linux.cc b/src/arch/alpha/linux/linux.cc new file mode 100644 index 000000000..e6908a572 --- /dev/null +++ b/src/arch/alpha/linux/linux.cc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#include "arch/alpha/linux/linux.hh" + +#include <fcntl.h> + +// open(2) flags translation table +OpenFlagTransTable AlphaLinux::openFlagTable[] = { +#ifdef _MSC_VER + { AlphaLinux::TGT_O_RDONLY, _O_RDONLY }, + { AlphaLinux::TGT_O_WRONLY, _O_WRONLY }, + { AlphaLinux::TGT_O_RDWR, _O_RDWR }, + { AlphaLinux::TGT_O_APPEND, _O_APPEND }, + { AlphaLinux::TGT_O_CREAT, _O_CREAT }, + { AlphaLinux::TGT_O_TRUNC, _O_TRUNC }, + { AlphaLinux::TGT_O_EXCL, _O_EXCL }, +#ifdef _O_NONBLOCK + { AlphaLinux::TGT_O_NONBLOCK, _O_NONBLOCK }, +#endif +#ifdef _O_NOCTTY + { AlphaLinux::TGT_O_NOCTTY, _O_NOCTTY }, +#endif +#ifdef _O_SYNC + { AlphaLinux::TGT_O_SYNC, _O_SYNC }, +#endif +#else /* !_MSC_VER */ + { AlphaLinux::TGT_O_RDONLY, O_RDONLY }, + { AlphaLinux::TGT_O_WRONLY, O_WRONLY }, + { AlphaLinux::TGT_O_RDWR, O_RDWR }, + { AlphaLinux::TGT_O_APPEND, O_APPEND }, + { AlphaLinux::TGT_O_CREAT, O_CREAT }, + { AlphaLinux::TGT_O_TRUNC, O_TRUNC }, + { AlphaLinux::TGT_O_EXCL, O_EXCL }, + { AlphaLinux::TGT_O_NONBLOCK, O_NONBLOCK }, + { AlphaLinux::TGT_O_NOCTTY, O_NOCTTY }, +#ifdef O_SYNC + { AlphaLinux::TGT_O_SYNC, O_SYNC }, +#endif +#endif /* _MSC_VER */ +}; + +const int AlphaLinux::NUM_OPEN_FLAGS = + (sizeof(AlphaLinux::openFlagTable)/sizeof(AlphaLinux::openFlagTable[0])); + + + diff --git a/src/arch/alpha/linux/linux.hh b/src/arch/alpha/linux/linux.hh new file mode 100644 index 000000000..09988bab2 --- /dev/null +++ b/src/arch/alpha/linux/linux.hh @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#ifndef __ALPHA_ALPHA_LINUX_HH +#define __ALPHA_ALPHA_LINUX_HH + +#include "kern/linux/linux.hh" + +/* AlphaLinux class contains static constants/definitions/misc. + * structures which are specific to the Linux OS AND the Alpha + * architecture + */ +class AlphaLinux : public Linux +{ + public: + + /// This table maps the target open() flags to the corresponding + /// host open() flags. + static OpenFlagTransTable openFlagTable[]; + + /// Number of entries in openFlagTable[]. + static const int NUM_OPEN_FLAGS; + + //@{ + /// open(2) flag values. + static const int TGT_O_RDONLY = 00000000; //!< O_RDONLY + static const int TGT_O_WRONLY = 00000001; //!< O_WRONLY + static const int TGT_O_RDWR = 00000002; //!< O_RDWR + static const int TGT_O_NONBLOCK = 00000004; //!< O_NONBLOCK + static const int TGT_O_APPEND = 00000010; //!< O_APPEND + static const int TGT_O_CREAT = 00001000; //!< O_CREAT + static const int TGT_O_TRUNC = 00002000; //!< O_TRUNC + static const int TGT_O_EXCL = 00004000; //!< O_EXCL + static const int TGT_O_NOCTTY = 00010000; //!< O_NOCTTY + static const int TGT_O_SYNC = 00040000; //!< O_SYNC + static const int TGT_O_DRD = 00100000; //!< O_DRD + static const int TGT_O_DIRECTIO = 00200000; //!< O_DIRECTIO + static const int TGT_O_CACHE = 00400000; //!< O_CACHE + static const int TGT_O_DSYNC = 02000000; //!< O_DSYNC + static const int TGT_O_RSYNC = 04000000; //!< O_RSYNC + //@} + + /// For mmap(). + static const unsigned TGT_MAP_ANONYMOUS = 0x10; + + //@{ + /// For getsysinfo(). + static const unsigned GSI_PLATFORM_NAME = 103; //!< platform name as string + static const unsigned GSI_CPU_INFO = 59; //!< CPU information + static const unsigned GSI_PROC_TYPE = 60; //!< get proc_type + static const unsigned GSI_MAX_CPU = 30; //!< max # cpu's on this machine + static const unsigned GSI_CPUS_IN_BOX = 55; //!< number of CPUs in system + static const unsigned GSI_PHYSMEM = 19; //!< Physical memory in KB + static const unsigned GSI_CLK_TCK = 42; //!< clock freq in Hz + static const unsigned GSI_IEEE_FP_CONTROL = 45; + //@} + + //@{ + /// For getrusage(). + static const int TGT_RUSAGE_SELF = 0; + static const int TGT_RUSAGE_CHILDREN = -1; + static const int TGT_RUSAGE_BOTH = -2; + //@} + + //@{ + /// For setsysinfo(). + static const unsigned SSI_IEEE_FP_CONTROL = 14; //!< ieee_set_fp_control() + //@} + + //@{ + /// ioctl() command codes. + static const unsigned TIOCGETP = 0x40067408; + static const unsigned TIOCSETP = 0x80067409; + static const unsigned TIOCSETN = 0x8006740a; + static const unsigned TIOCSETC = 0x80067411; + static const unsigned TIOCGETC = 0x40067412; + static const unsigned FIONREAD = 0x4004667f; + static const unsigned TIOCISATTY = 0x2000745e; + static const unsigned TIOCGETS = 0x402c7413; + static const unsigned TIOCGETA = 0x40127417; + //@} + + /// For table(). + static const int TBL_SYSINFO = 12; + + /// Resource enumeration for getrlimit(). + enum rlimit_resources { + TGT_RLIMIT_CPU = 0, + TGT_RLIMIT_FSIZE = 1, + TGT_RLIMIT_DATA = 2, + TGT_RLIMIT_STACK = 3, + TGT_RLIMIT_CORE = 4, + TGT_RLIMIT_RSS = 5, + TGT_RLIMIT_NOFILE = 6, + TGT_RLIMIT_AS = 7, + TGT_RLIMIT_VMEM = 7, + TGT_RLIMIT_NPROC = 8, + TGT_RLIMIT_MEMLOCK = 9, + TGT_RLIMIT_LOCKS = 10 + }; +}; + +#endif diff --git a/src/arch/alpha/linux/process.cc b/src/arch/alpha/linux/process.cc new file mode 100644 index 000000000..56342dbc5 --- /dev/null +++ b/src/arch/alpha/linux/process.cc @@ -0,0 +1,600 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Ali Saidi + */ + +#include "arch/alpha/linux/linux.hh" +#include "arch/alpha/linux/process.hh" +#include "arch/alpha/isa_traits.hh" + +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "kern/linux/linux.hh" + +#include "sim/process.hh" +#include "sim/syscall_emul.hh" + +using namespace std; +using namespace AlphaISA; + + + +/// Target uname() handler. +static SyscallReturn +unameFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + TypedBufferArg<Linux::utsname> name(tc->getSyscallArg(0)); + + strcpy(name->sysname, "Linux"); + strcpy(name->nodename, "m5.eecs.umich.edu"); + strcpy(name->release, "2.4.20"); + strcpy(name->version, "#1 Mon Aug 18 11:32:15 EDT 2003"); + strcpy(name->machine, "alpha"); + + name.copyOut(tc->getMemPort()); + return 0; +} + +/// Target osf_getsysyinfo() handler. Even though this call is +/// borrowed from Tru64, the subcases that get used appear to be +/// different in practice from those used by Tru64 processes. +static SyscallReturn +osf_getsysinfoFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + unsigned op = tc->getSyscallArg(0); + // unsigned nbytes = tc->getSyscallArg(2); + + switch (op) { + + case 45: { // GSI_IEEE_FP_CONTROL + TypedBufferArg<uint64_t> fpcr(tc->getSyscallArg(1)); + // I don't think this exactly matches the HW FPCR + *fpcr = 0; + fpcr.copyOut(tc->getMemPort()); + return 0; + } + + default: + cerr << "osf_getsysinfo: unknown op " << op << endl; + abort(); + break; + } + + return 1; +} + +/// Target osf_setsysinfo() handler. +static SyscallReturn +osf_setsysinfoFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + unsigned op = tc->getSyscallArg(0); + // unsigned nbytes = tc->getSyscallArg(2); + + switch (op) { + + case 14: { // SSI_IEEE_FP_CONTROL + TypedBufferArg<uint64_t> fpcr(tc->getSyscallArg(1)); + // I don't think this exactly matches the HW FPCR + fpcr.copyIn(tc->getMemPort()); + DPRINTFR(SyscallVerbose, "osf_setsysinfo(SSI_IEEE_FP_CONTROL): " + " setting FPCR to 0x%x\n", gtoh(*(uint64_t*)fpcr)); + return 0; + } + + default: + cerr << "osf_setsysinfo: unknown op " << op << endl; + abort(); + break; + } + + return 1; +} + + +SyscallDesc AlphaLinuxProcess::syscallDescs[] = { + /* 0 */ SyscallDesc("osf_syscall", unimplementedFunc), + /* 1 */ SyscallDesc("exit", exitFunc), + /* 2 */ SyscallDesc("fork", unimplementedFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), + /* 5 */ SyscallDesc("osf_old_open", unimplementedFunc), + /* 6 */ SyscallDesc("close", closeFunc), + /* 7 */ SyscallDesc("osf_wait4", unimplementedFunc), + /* 8 */ SyscallDesc("osf_old_creat", unimplementedFunc), + /* 9 */ SyscallDesc("link", unimplementedFunc), + /* 10 */ SyscallDesc("unlink", unlinkFunc), + /* 11 */ SyscallDesc("osf_execve", unimplementedFunc), + /* 12 */ SyscallDesc("chdir", unimplementedFunc), + /* 13 */ SyscallDesc("fchdir", unimplementedFunc), + /* 14 */ SyscallDesc("mknod", unimplementedFunc), + /* 15 */ SyscallDesc("chmod", chmodFunc<AlphaLinux>), + /* 16 */ SyscallDesc("chown", chownFunc), + /* 17 */ SyscallDesc("brk", obreakFunc), + /* 18 */ SyscallDesc("osf_getfsstat", unimplementedFunc), + /* 19 */ SyscallDesc("lseek", lseekFunc), + /* 20 */ SyscallDesc("getxpid", getpidPseudoFunc), + /* 21 */ SyscallDesc("osf_mount", unimplementedFunc), + /* 22 */ SyscallDesc("umount", unimplementedFunc), + /* 23 */ SyscallDesc("setuid", setuidFunc), + /* 24 */ SyscallDesc("getxuid", getuidPseudoFunc), + /* 25 */ SyscallDesc("exec_with_loader", unimplementedFunc), + /* 26 */ SyscallDesc("osf_ptrace", unimplementedFunc), + /* 27 */ SyscallDesc("osf_nrecvmsg", unimplementedFunc), + /* 28 */ SyscallDesc("osf_nsendmsg", unimplementedFunc), + /* 29 */ SyscallDesc("osf_nrecvfrom", unimplementedFunc), + /* 30 */ SyscallDesc("osf_naccept", unimplementedFunc), + /* 31 */ SyscallDesc("osf_ngetpeername", unimplementedFunc), + /* 32 */ SyscallDesc("osf_ngetsockname", unimplementedFunc), + /* 33 */ SyscallDesc("access", unimplementedFunc), + /* 34 */ SyscallDesc("osf_chflags", unimplementedFunc), + /* 35 */ SyscallDesc("osf_fchflags", unimplementedFunc), + /* 36 */ SyscallDesc("sync", unimplementedFunc), + /* 37 */ SyscallDesc("kill", unimplementedFunc), + /* 38 */ SyscallDesc("osf_old_stat", unimplementedFunc), + /* 39 */ SyscallDesc("setpgid", unimplementedFunc), + /* 40 */ SyscallDesc("osf_old_lstat", unimplementedFunc), + /* 41 */ SyscallDesc("dup", dupFunc), + /* 42 */ SyscallDesc("pipe", pipePseudoFunc), + /* 43 */ SyscallDesc("osf_set_program_attributes", unimplementedFunc), + /* 44 */ SyscallDesc("osf_profil", unimplementedFunc), + /* 45 */ SyscallDesc("open", openFunc<AlphaLinux>), + /* 46 */ SyscallDesc("osf_old_sigaction", unimplementedFunc), + /* 47 */ SyscallDesc("getxgid", getgidPseudoFunc), + /* 48 */ SyscallDesc("osf_sigprocmask", ignoreFunc), + /* 49 */ SyscallDesc("osf_getlogin", unimplementedFunc), + /* 50 */ SyscallDesc("osf_setlogin", unimplementedFunc), + /* 51 */ SyscallDesc("acct", unimplementedFunc), + /* 52 */ SyscallDesc("sigpending", unimplementedFunc), + /* 53 */ SyscallDesc("osf_classcntl", unimplementedFunc), + /* 54 */ SyscallDesc("ioctl", ioctlFunc<AlphaLinux>), + /* 55 */ SyscallDesc("osf_reboot", unimplementedFunc), + /* 56 */ SyscallDesc("osf_revoke", unimplementedFunc), + /* 57 */ SyscallDesc("symlink", unimplementedFunc), + /* 58 */ SyscallDesc("readlink", unimplementedFunc), + /* 59 */ SyscallDesc("execve", unimplementedFunc), + /* 60 */ SyscallDesc("umask", unimplementedFunc), + /* 61 */ SyscallDesc("chroot", unimplementedFunc), + /* 62 */ SyscallDesc("osf_old_fstat", unimplementedFunc), + /* 63 */ SyscallDesc("getpgrp", unimplementedFunc), + /* 64 */ SyscallDesc("getpagesize", getpagesizeFunc), + /* 65 */ SyscallDesc("osf_mremap", unimplementedFunc), + /* 66 */ SyscallDesc("vfork", unimplementedFunc), + /* 67 */ SyscallDesc("stat", statFunc<AlphaLinux>), + /* 68 */ SyscallDesc("lstat", lstatFunc<AlphaLinux>), + /* 69 */ SyscallDesc("osf_sbrk", unimplementedFunc), + /* 70 */ SyscallDesc("osf_sstk", unimplementedFunc), + /* 71 */ SyscallDesc("mmap", mmapFunc<AlphaLinux>), + /* 72 */ SyscallDesc("osf_old_vadvise", unimplementedFunc), + /* 73 */ SyscallDesc("munmap", munmapFunc), + /* 74 */ SyscallDesc("mprotect", ignoreFunc), + /* 75 */ SyscallDesc("madvise", unimplementedFunc), + /* 76 */ SyscallDesc("vhangup", unimplementedFunc), + /* 77 */ SyscallDesc("osf_kmodcall", unimplementedFunc), + /* 78 */ SyscallDesc("osf_mincore", unimplementedFunc), + /* 79 */ SyscallDesc("getgroups", unimplementedFunc), + /* 80 */ SyscallDesc("setgroups", unimplementedFunc), + /* 81 */ SyscallDesc("osf_old_getpgrp", unimplementedFunc), + /* 82 */ SyscallDesc("setpgrp", unimplementedFunc), + /* 83 */ SyscallDesc("osf_setitimer", unimplementedFunc), + /* 84 */ SyscallDesc("osf_old_wait", unimplementedFunc), + /* 85 */ SyscallDesc("osf_table", unimplementedFunc), + /* 86 */ SyscallDesc("osf_getitimer", unimplementedFunc), + /* 87 */ SyscallDesc("gethostname", gethostnameFunc), + /* 88 */ SyscallDesc("sethostname", unimplementedFunc), + /* 89 */ SyscallDesc("getdtablesize", unimplementedFunc), + /* 90 */ SyscallDesc("dup2", unimplementedFunc), + /* 91 */ SyscallDesc("fstat", fstatFunc<AlphaLinux>), + /* 92 */ SyscallDesc("fcntl", fcntlFunc), + /* 93 */ SyscallDesc("osf_select", unimplementedFunc), + /* 94 */ SyscallDesc("poll", unimplementedFunc), + /* 95 */ SyscallDesc("fsync", unimplementedFunc), + /* 96 */ SyscallDesc("setpriority", unimplementedFunc), + /* 97 */ SyscallDesc("socket", unimplementedFunc), + /* 98 */ SyscallDesc("connect", unimplementedFunc), + /* 99 */ SyscallDesc("accept", unimplementedFunc), + /* 100 */ SyscallDesc("getpriority", unimplementedFunc), + /* 101 */ SyscallDesc("send", unimplementedFunc), + /* 102 */ SyscallDesc("recv", unimplementedFunc), + /* 103 */ SyscallDesc("sigreturn", unimplementedFunc), + /* 104 */ SyscallDesc("bind", unimplementedFunc), + /* 105 */ SyscallDesc("setsockopt", unimplementedFunc), + /* 106 */ SyscallDesc("listen", unimplementedFunc), + /* 107 */ SyscallDesc("osf_plock", unimplementedFunc), + /* 108 */ SyscallDesc("osf_old_sigvec", unimplementedFunc), + /* 109 */ SyscallDesc("osf_old_sigblock", unimplementedFunc), + /* 110 */ SyscallDesc("osf_old_sigsetmask", unimplementedFunc), + /* 111 */ SyscallDesc("sigsuspend", unimplementedFunc), + /* 112 */ SyscallDesc("osf_sigstack", ignoreFunc), + /* 113 */ SyscallDesc("recvmsg", unimplementedFunc), + /* 114 */ SyscallDesc("sendmsg", unimplementedFunc), + /* 115 */ SyscallDesc("osf_old_vtrace", unimplementedFunc), + /* 116 */ SyscallDesc("osf_gettimeofday", unimplementedFunc), + /* 117 */ SyscallDesc("osf_getrusage", unimplementedFunc), + /* 118 */ SyscallDesc("getsockopt", unimplementedFunc), + /* 119 */ SyscallDesc("numa_syscalls", unimplementedFunc), + /* 120 */ SyscallDesc("readv", unimplementedFunc), + /* 121 */ SyscallDesc("writev", writevFunc<AlphaLinux>), + /* 122 */ SyscallDesc("osf_settimeofday", unimplementedFunc), + /* 123 */ SyscallDesc("fchown", fchownFunc), + /* 124 */ SyscallDesc("fchmod", fchmodFunc<AlphaLinux>), + /* 125 */ SyscallDesc("recvfrom", unimplementedFunc), + /* 126 */ SyscallDesc("setreuid", unimplementedFunc), + /* 127 */ SyscallDesc("setregid", unimplementedFunc), + /* 128 */ SyscallDesc("rename", renameFunc), + /* 129 */ SyscallDesc("truncate", unimplementedFunc), + /* 130 */ SyscallDesc("ftruncate", unimplementedFunc), + /* 131 */ SyscallDesc("flock", unimplementedFunc), + /* 132 */ SyscallDesc("setgid", unimplementedFunc), + /* 133 */ SyscallDesc("sendto", unimplementedFunc), + /* 134 */ SyscallDesc("shutdown", unimplementedFunc), + /* 135 */ SyscallDesc("socketpair", unimplementedFunc), + /* 136 */ SyscallDesc("mkdir", unimplementedFunc), + /* 137 */ SyscallDesc("rmdir", unimplementedFunc), + /* 138 */ SyscallDesc("osf_utimes", unimplementedFunc), + /* 139 */ SyscallDesc("osf_old_sigreturn", unimplementedFunc), + /* 140 */ SyscallDesc("osf_adjtime", unimplementedFunc), + /* 141 */ SyscallDesc("getpeername", unimplementedFunc), + /* 142 */ SyscallDesc("osf_gethostid", unimplementedFunc), + /* 143 */ SyscallDesc("osf_sethostid", unimplementedFunc), + /* 144 */ SyscallDesc("getrlimit", getrlimitFunc<AlphaLinux>), + /* 145 */ SyscallDesc("setrlimit", ignoreFunc), + /* 146 */ SyscallDesc("osf_old_killpg", unimplementedFunc), + /* 147 */ SyscallDesc("setsid", unimplementedFunc), + /* 148 */ SyscallDesc("quotactl", unimplementedFunc), + /* 149 */ SyscallDesc("osf_oldquota", unimplementedFunc), + /* 150 */ SyscallDesc("getsockname", unimplementedFunc), + /* 151 */ SyscallDesc("osf_pread", unimplementedFunc), + /* 152 */ SyscallDesc("osf_pwrite", unimplementedFunc), + /* 153 */ SyscallDesc("osf_pid_block", unimplementedFunc), + /* 154 */ SyscallDesc("osf_pid_unblock", unimplementedFunc), + /* 155 */ SyscallDesc("osf_signal_urti", unimplementedFunc), + /* 156 */ SyscallDesc("sigaction", ignoreFunc), + /* 157 */ SyscallDesc("osf_sigwaitprim", unimplementedFunc), + /* 158 */ SyscallDesc("osf_nfssvc", unimplementedFunc), + /* 159 */ SyscallDesc("osf_getdirentries", unimplementedFunc), + /* 160 */ SyscallDesc("osf_statfs", unimplementedFunc), + /* 161 */ SyscallDesc("osf_fstatfs", unimplementedFunc), + /* 162 */ SyscallDesc("unknown #162", unimplementedFunc), + /* 163 */ SyscallDesc("osf_async_daemon", unimplementedFunc), + /* 164 */ SyscallDesc("osf_getfh", unimplementedFunc), + /* 165 */ SyscallDesc("osf_getdomainname", unimplementedFunc), + /* 166 */ SyscallDesc("setdomainname", unimplementedFunc), + /* 167 */ SyscallDesc("unknown #167", unimplementedFunc), + /* 168 */ SyscallDesc("unknown #168", unimplementedFunc), + /* 169 */ SyscallDesc("osf_exportfs", unimplementedFunc), + /* 170 */ SyscallDesc("unknown #170", unimplementedFunc), + /* 171 */ SyscallDesc("unknown #171", unimplementedFunc), + /* 172 */ SyscallDesc("unknown #172", unimplementedFunc), + /* 173 */ SyscallDesc("unknown #173", unimplementedFunc), + /* 174 */ SyscallDesc("unknown #174", unimplementedFunc), + /* 175 */ SyscallDesc("unknown #175", unimplementedFunc), + /* 176 */ SyscallDesc("unknown #176", unimplementedFunc), + /* 177 */ SyscallDesc("unknown #177", unimplementedFunc), + /* 178 */ SyscallDesc("unknown #178", unimplementedFunc), + /* 179 */ SyscallDesc("unknown #179", unimplementedFunc), + /* 180 */ SyscallDesc("unknown #180", unimplementedFunc), + /* 181 */ SyscallDesc("osf_alt_plock", unimplementedFunc), + /* 182 */ SyscallDesc("unknown #182", unimplementedFunc), + /* 183 */ SyscallDesc("unknown #183", unimplementedFunc), + /* 184 */ SyscallDesc("osf_getmnt", unimplementedFunc), + /* 185 */ SyscallDesc("unknown #185", unimplementedFunc), + /* 186 */ SyscallDesc("unknown #186", unimplementedFunc), + /* 187 */ SyscallDesc("osf_alt_sigpending", unimplementedFunc), + /* 188 */ SyscallDesc("osf_alt_setsid", unimplementedFunc), + /* 189 */ SyscallDesc("unknown #189", unimplementedFunc), + /* 190 */ SyscallDesc("unknown #190", unimplementedFunc), + /* 191 */ SyscallDesc("unknown #191", unimplementedFunc), + /* 192 */ SyscallDesc("unknown #192", unimplementedFunc), + /* 193 */ SyscallDesc("unknown #193", unimplementedFunc), + /* 194 */ SyscallDesc("unknown #194", unimplementedFunc), + /* 195 */ SyscallDesc("unknown #195", unimplementedFunc), + /* 196 */ SyscallDesc("unknown #196", unimplementedFunc), + /* 197 */ SyscallDesc("unknown #197", unimplementedFunc), + /* 198 */ SyscallDesc("unknown #198", unimplementedFunc), + /* 199 */ SyscallDesc("osf_swapon", unimplementedFunc), + /* 200 */ SyscallDesc("msgctl", unimplementedFunc), + /* 201 */ SyscallDesc("msgget", unimplementedFunc), + /* 202 */ SyscallDesc("msgrcv", unimplementedFunc), + /* 203 */ SyscallDesc("msgsnd", unimplementedFunc), + /* 204 */ SyscallDesc("semctl", unimplementedFunc), + /* 205 */ SyscallDesc("semget", unimplementedFunc), + /* 206 */ SyscallDesc("semop", unimplementedFunc), + /* 207 */ SyscallDesc("osf_utsname", unimplementedFunc), + /* 208 */ SyscallDesc("lchown", unimplementedFunc), + /* 209 */ SyscallDesc("osf_shmat", unimplementedFunc), + /* 210 */ SyscallDesc("shmctl", unimplementedFunc), + /* 211 */ SyscallDesc("shmdt", unimplementedFunc), + /* 212 */ SyscallDesc("shmget", unimplementedFunc), + /* 213 */ SyscallDesc("osf_mvalid", unimplementedFunc), + /* 214 */ SyscallDesc("osf_getaddressconf", unimplementedFunc), + /* 215 */ SyscallDesc("osf_msleep", unimplementedFunc), + /* 216 */ SyscallDesc("osf_mwakeup", unimplementedFunc), + /* 217 */ SyscallDesc("msync", unimplementedFunc), + /* 218 */ SyscallDesc("osf_signal", unimplementedFunc), + /* 219 */ SyscallDesc("osf_utc_gettime", unimplementedFunc), + /* 220 */ SyscallDesc("osf_utc_adjtime", unimplementedFunc), + /* 221 */ SyscallDesc("unknown #221", unimplementedFunc), + /* 222 */ SyscallDesc("osf_security", unimplementedFunc), + /* 223 */ SyscallDesc("osf_kloadcall", unimplementedFunc), + /* 224 */ SyscallDesc("unknown #224", unimplementedFunc), + /* 225 */ SyscallDesc("unknown #225", unimplementedFunc), + /* 226 */ SyscallDesc("unknown #226", unimplementedFunc), + /* 227 */ SyscallDesc("unknown #227", unimplementedFunc), + /* 228 */ SyscallDesc("unknown #228", unimplementedFunc), + /* 229 */ SyscallDesc("unknown #229", unimplementedFunc), + /* 230 */ SyscallDesc("unknown #230", unimplementedFunc), + /* 231 */ SyscallDesc("unknown #231", unimplementedFunc), + /* 232 */ SyscallDesc("unknown #232", unimplementedFunc), + /* 233 */ SyscallDesc("getpgid", unimplementedFunc), + /* 234 */ SyscallDesc("getsid", unimplementedFunc), + /* 235 */ SyscallDesc("sigaltstack", ignoreFunc), + /* 236 */ SyscallDesc("osf_waitid", unimplementedFunc), + /* 237 */ SyscallDesc("osf_priocntlset", unimplementedFunc), + /* 238 */ SyscallDesc("osf_sigsendset", unimplementedFunc), + /* 239 */ SyscallDesc("osf_set_speculative", unimplementedFunc), + /* 240 */ SyscallDesc("osf_msfs_syscall", unimplementedFunc), + /* 241 */ SyscallDesc("osf_sysinfo", unimplementedFunc), + /* 242 */ SyscallDesc("osf_uadmin", unimplementedFunc), + /* 243 */ SyscallDesc("osf_fuser", unimplementedFunc), + /* 244 */ SyscallDesc("osf_proplist_syscall", unimplementedFunc), + /* 245 */ SyscallDesc("osf_ntp_adjtime", unimplementedFunc), + /* 246 */ SyscallDesc("osf_ntp_gettime", unimplementedFunc), + /* 247 */ SyscallDesc("osf_pathconf", unimplementedFunc), + /* 248 */ SyscallDesc("osf_fpathconf", unimplementedFunc), + /* 249 */ SyscallDesc("unknown #249", unimplementedFunc), + /* 250 */ SyscallDesc("osf_uswitch", unimplementedFunc), + /* 251 */ SyscallDesc("osf_usleep_thread", unimplementedFunc), + /* 252 */ SyscallDesc("osf_audcntl", unimplementedFunc), + /* 253 */ SyscallDesc("osf_audgen", unimplementedFunc), + /* 254 */ SyscallDesc("sysfs", unimplementedFunc), + /* 255 */ SyscallDesc("osf_subsys_info", unimplementedFunc), + /* 256 */ SyscallDesc("osf_getsysinfo", osf_getsysinfoFunc), + /* 257 */ SyscallDesc("osf_setsysinfo", osf_setsysinfoFunc), + /* 258 */ SyscallDesc("osf_afs_syscall", unimplementedFunc), + /* 259 */ SyscallDesc("osf_swapctl", unimplementedFunc), + /* 260 */ SyscallDesc("osf_memcntl", unimplementedFunc), + /* 261 */ SyscallDesc("osf_fdatasync", unimplementedFunc), + /* 262 */ SyscallDesc("unknown #262", unimplementedFunc), + /* 263 */ SyscallDesc("unknown #263", unimplementedFunc), + /* 264 */ SyscallDesc("unknown #264", unimplementedFunc), + /* 265 */ SyscallDesc("unknown #265", unimplementedFunc), + /* 266 */ SyscallDesc("unknown #266", unimplementedFunc), + /* 267 */ SyscallDesc("unknown #267", unimplementedFunc), + /* 268 */ SyscallDesc("unknown #268", unimplementedFunc), + /* 269 */ SyscallDesc("unknown #269", unimplementedFunc), + /* 270 */ SyscallDesc("unknown #270", unimplementedFunc), + /* 271 */ SyscallDesc("unknown #271", unimplementedFunc), + /* 272 */ SyscallDesc("unknown #272", unimplementedFunc), + /* 273 */ SyscallDesc("unknown #273", unimplementedFunc), + /* 274 */ SyscallDesc("unknown #274", unimplementedFunc), + /* 275 */ SyscallDesc("unknown #275", unimplementedFunc), + /* 276 */ SyscallDesc("unknown #276", unimplementedFunc), + /* 277 */ SyscallDesc("unknown #277", unimplementedFunc), + /* 278 */ SyscallDesc("unknown #278", unimplementedFunc), + /* 279 */ SyscallDesc("unknown #279", unimplementedFunc), + /* 280 */ SyscallDesc("unknown #280", unimplementedFunc), + /* 281 */ SyscallDesc("unknown #281", unimplementedFunc), + /* 282 */ SyscallDesc("unknown #282", unimplementedFunc), + /* 283 */ SyscallDesc("unknown #283", unimplementedFunc), + /* 284 */ SyscallDesc("unknown #284", unimplementedFunc), + /* 285 */ SyscallDesc("unknown #285", unimplementedFunc), + /* 286 */ SyscallDesc("unknown #286", unimplementedFunc), + /* 287 */ SyscallDesc("unknown #287", unimplementedFunc), + /* 288 */ SyscallDesc("unknown #288", unimplementedFunc), + /* 289 */ SyscallDesc("unknown #289", unimplementedFunc), + /* 290 */ SyscallDesc("unknown #290", unimplementedFunc), + /* 291 */ SyscallDesc("unknown #291", unimplementedFunc), + /* 292 */ SyscallDesc("unknown #292", unimplementedFunc), + /* 293 */ SyscallDesc("unknown #293", unimplementedFunc), + /* 294 */ SyscallDesc("unknown #294", unimplementedFunc), + /* 295 */ SyscallDesc("unknown #295", unimplementedFunc), + /* 296 */ SyscallDesc("unknown #296", unimplementedFunc), + /* 297 */ SyscallDesc("unknown #297", unimplementedFunc), + /* 298 */ SyscallDesc("unknown #298", unimplementedFunc), + /* 299 */ SyscallDesc("unknown #299", unimplementedFunc), +/* + * Linux-specific system calls begin at 300 + */ + /* 300 */ SyscallDesc("bdflush", unimplementedFunc), + /* 301 */ SyscallDesc("sethae", unimplementedFunc), + /* 302 */ SyscallDesc("mount", unimplementedFunc), + /* 303 */ SyscallDesc("old_adjtimex", unimplementedFunc), + /* 304 */ SyscallDesc("swapoff", unimplementedFunc), + /* 305 */ SyscallDesc("getdents", unimplementedFunc), + /* 306 */ SyscallDesc("create_module", unimplementedFunc), + /* 307 */ SyscallDesc("init_module", unimplementedFunc), + /* 308 */ SyscallDesc("delete_module", unimplementedFunc), + /* 309 */ SyscallDesc("get_kernel_syms", unimplementedFunc), + /* 310 */ SyscallDesc("syslog", unimplementedFunc), + /* 311 */ SyscallDesc("reboot", unimplementedFunc), + /* 312 */ SyscallDesc("clone", unimplementedFunc), + /* 313 */ SyscallDesc("uselib", unimplementedFunc), + /* 314 */ SyscallDesc("mlock", unimplementedFunc), + /* 315 */ SyscallDesc("munlock", unimplementedFunc), + /* 316 */ SyscallDesc("mlockall", unimplementedFunc), + /* 317 */ SyscallDesc("munlockall", unimplementedFunc), + /* 318 */ SyscallDesc("sysinfo", unimplementedFunc), + /* 319 */ SyscallDesc("_sysctl", unimplementedFunc), + /* 320 */ SyscallDesc("was sys_idle", unimplementedFunc), + /* 321 */ SyscallDesc("oldumount", unimplementedFunc), + /* 322 */ SyscallDesc("swapon", unimplementedFunc), + /* 323 */ SyscallDesc("times", ignoreFunc), + /* 324 */ SyscallDesc("personality", unimplementedFunc), + /* 325 */ SyscallDesc("setfsuid", unimplementedFunc), + /* 326 */ SyscallDesc("setfsgid", unimplementedFunc), + /* 327 */ SyscallDesc("ustat", unimplementedFunc), + /* 328 */ SyscallDesc("statfs", unimplementedFunc), + /* 329 */ SyscallDesc("fstatfs", unimplementedFunc), + /* 330 */ SyscallDesc("sched_setparam", unimplementedFunc), + /* 331 */ SyscallDesc("sched_getparam", unimplementedFunc), + /* 332 */ SyscallDesc("sched_setscheduler", unimplementedFunc), + /* 333 */ SyscallDesc("sched_getscheduler", unimplementedFunc), + /* 334 */ SyscallDesc("sched_yield", unimplementedFunc), + /* 335 */ SyscallDesc("sched_get_priority_max", unimplementedFunc), + /* 336 */ SyscallDesc("sched_get_priority_min", unimplementedFunc), + /* 337 */ SyscallDesc("sched_rr_get_interval", unimplementedFunc), + /* 338 */ SyscallDesc("afs_syscall", unimplementedFunc), + /* 339 */ SyscallDesc("uname", unameFunc), + /* 340 */ SyscallDesc("nanosleep", unimplementedFunc), + /* 341 */ SyscallDesc("mremap", unimplementedFunc), + /* 342 */ SyscallDesc("nfsservctl", unimplementedFunc), + /* 343 */ SyscallDesc("setresuid", unimplementedFunc), + /* 344 */ SyscallDesc("getresuid", unimplementedFunc), + /* 345 */ SyscallDesc("pciconfig_read", unimplementedFunc), + /* 346 */ SyscallDesc("pciconfig_write", unimplementedFunc), + /* 347 */ SyscallDesc("query_module", unimplementedFunc), + /* 348 */ SyscallDesc("prctl", unimplementedFunc), + /* 349 */ SyscallDesc("pread", unimplementedFunc), + /* 350 */ SyscallDesc("pwrite", unimplementedFunc), + /* 351 */ SyscallDesc("rt_sigreturn", unimplementedFunc), + /* 352 */ SyscallDesc("rt_sigaction", ignoreFunc), + /* 353 */ SyscallDesc("rt_sigprocmask", unimplementedFunc), + /* 354 */ SyscallDesc("rt_sigpending", unimplementedFunc), + /* 355 */ SyscallDesc("rt_sigtimedwait", unimplementedFunc), + /* 356 */ SyscallDesc("rt_sigqueueinfo", unimplementedFunc), + /* 357 */ SyscallDesc("rt_sigsuspend", unimplementedFunc), + /* 358 */ SyscallDesc("select", unimplementedFunc), + /* 359 */ SyscallDesc("gettimeofday", gettimeofdayFunc<AlphaLinux>), + /* 360 */ SyscallDesc("settimeofday", unimplementedFunc), + /* 361 */ SyscallDesc("getitimer", unimplementedFunc), + /* 362 */ SyscallDesc("setitimer", unimplementedFunc), + /* 363 */ SyscallDesc("utimes", utimesFunc<AlphaLinux>), + /* 364 */ SyscallDesc("getrusage", getrusageFunc<AlphaLinux>), + /* 365 */ SyscallDesc("wait4", unimplementedFunc), + /* 366 */ SyscallDesc("adjtimex", unimplementedFunc), + /* 367 */ SyscallDesc("getcwd", unimplementedFunc), + /* 368 */ SyscallDesc("capget", unimplementedFunc), + /* 369 */ SyscallDesc("capset", unimplementedFunc), + /* 370 */ SyscallDesc("sendfile", unimplementedFunc), + /* 371 */ SyscallDesc("setresgid", unimplementedFunc), + /* 372 */ SyscallDesc("getresgid", unimplementedFunc), + /* 373 */ SyscallDesc("dipc", unimplementedFunc), + /* 374 */ SyscallDesc("pivot_root", unimplementedFunc), + /* 375 */ SyscallDesc("mincore", unimplementedFunc), + /* 376 */ SyscallDesc("pciconfig_iobase", unimplementedFunc), + /* 377 */ SyscallDesc("getdents64", unimplementedFunc), + /* 378 */ SyscallDesc("gettid", unimplementedFunc), + /* 379 */ SyscallDesc("readahead", unimplementedFunc), + /* 380 */ SyscallDesc("security", unimplementedFunc), + /* 381 */ SyscallDesc("tkill", unimplementedFunc), + /* 382 */ SyscallDesc("setxattr", unimplementedFunc), + /* 383 */ SyscallDesc("lsetxattr", unimplementedFunc), + /* 384 */ SyscallDesc("fsetxattr", unimplementedFunc), + /* 385 */ SyscallDesc("getxattr", unimplementedFunc), + /* 386 */ SyscallDesc("lgetxattr", unimplementedFunc), + /* 387 */ SyscallDesc("fgetxattr", unimplementedFunc), + /* 388 */ SyscallDesc("listxattr", unimplementedFunc), + /* 389 */ SyscallDesc("llistxattr", unimplementedFunc), + /* 390 */ SyscallDesc("flistxattr", unimplementedFunc), + /* 391 */ SyscallDesc("removexattr", unimplementedFunc), + /* 392 */ SyscallDesc("lremovexattr", unimplementedFunc), + /* 393 */ SyscallDesc("fremovexattr", unimplementedFunc), + /* 394 */ SyscallDesc("futex", unimplementedFunc), + /* 395 */ SyscallDesc("sched_setaffinity", unimplementedFunc), + /* 396 */ SyscallDesc("sched_getaffinity", unimplementedFunc), + /* 397 */ SyscallDesc("tuxcall", unimplementedFunc), + /* 398 */ SyscallDesc("io_setup", unimplementedFunc), + /* 399 */ SyscallDesc("io_destroy", unimplementedFunc), + /* 400 */ SyscallDesc("io_getevents", unimplementedFunc), + /* 401 */ SyscallDesc("io_submit", unimplementedFunc), + /* 402 */ SyscallDesc("io_cancel", unimplementedFunc), + /* 403 */ SyscallDesc("unknown #403", unimplementedFunc), + /* 404 */ SyscallDesc("unknown #404", unimplementedFunc), + /* 405 */ SyscallDesc("exit_group", exitFunc), // exit all threads... + /* 406 */ SyscallDesc("lookup_dcookie", unimplementedFunc), + /* 407 */ SyscallDesc("sys_epoll_create", unimplementedFunc), + /* 408 */ SyscallDesc("sys_epoll_ctl", unimplementedFunc), + /* 409 */ SyscallDesc("sys_epoll_wait", unimplementedFunc), + /* 410 */ SyscallDesc("remap_file_pages", unimplementedFunc), + /* 411 */ SyscallDesc("set_tid_address", unimplementedFunc), + /* 412 */ SyscallDesc("restart_syscall", unimplementedFunc), + /* 413 */ SyscallDesc("fadvise64", unimplementedFunc), + /* 414 */ SyscallDesc("timer_create", unimplementedFunc), + /* 415 */ SyscallDesc("timer_settime", unimplementedFunc), + /* 416 */ SyscallDesc("timer_gettime", unimplementedFunc), + /* 417 */ SyscallDesc("timer_getoverrun", unimplementedFunc), + /* 418 */ SyscallDesc("timer_delete", unimplementedFunc), + /* 419 */ SyscallDesc("clock_settime", unimplementedFunc), + /* 420 */ SyscallDesc("clock_gettime", unimplementedFunc), + /* 421 */ SyscallDesc("clock_getres", unimplementedFunc), + /* 422 */ SyscallDesc("clock_nanosleep", unimplementedFunc), + /* 423 */ SyscallDesc("semtimedop", unimplementedFunc), + /* 424 */ SyscallDesc("tgkill", unimplementedFunc), + /* 425 */ SyscallDesc("stat64", unimplementedFunc), + /* 426 */ SyscallDesc("lstat64", lstat64Func<AlphaLinux>), + /* 427 */ SyscallDesc("fstat64", fstat64Func<AlphaLinux>), + /* 428 */ SyscallDesc("vserver", unimplementedFunc), + /* 429 */ SyscallDesc("mbind", unimplementedFunc), + /* 430 */ SyscallDesc("get_mempolicy", unimplementedFunc), + /* 431 */ SyscallDesc("set_mempolicy", unimplementedFunc), + /* 432 */ SyscallDesc("mq_open", unimplementedFunc), + /* 433 */ SyscallDesc("mq_unlink", unimplementedFunc), + /* 434 */ SyscallDesc("mq_timedsend", unimplementedFunc), + /* 435 */ SyscallDesc("mq_timedreceive", unimplementedFunc), + /* 436 */ SyscallDesc("mq_notify", unimplementedFunc), + /* 437 */ SyscallDesc("mq_getsetattr", unimplementedFunc), + /* 438 */ SyscallDesc("waitid", unimplementedFunc), + /* 439 */ SyscallDesc("add_key", unimplementedFunc), + /* 440 */ SyscallDesc("request_key", unimplementedFunc), + /* 441 */ SyscallDesc("keyctl", unimplementedFunc) +}; + +AlphaLinuxProcess::AlphaLinuxProcess(const std::string &name, + ObjectFile *objFile, + System *system, + int stdin_fd, + int stdout_fd, + int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, + uint64_t _euid, + uint64_t _gid, + uint64_t _egid, + uint64_t _pid, + uint64_t _ppid) + : AlphaLiveProcess(name, objFile, system, stdin_fd, stdout_fd, + stderr_fd, argv, envp, _uid, _euid, _gid, _egid, _pid, _ppid), + Num_Syscall_Descs(sizeof(syscallDescs) / sizeof(SyscallDesc)) +{ + //init_regs->intRegFile[0] = 0; +} + + + +SyscallDesc* +AlphaLinuxProcess::getDesc(int callnum) +{ + if (callnum < 0 || callnum > Num_Syscall_Descs) + return NULL; + return &syscallDescs[callnum]; +} diff --git a/src/arch/alpha/linux/process.hh b/src/arch/alpha/linux/process.hh new file mode 100644 index 000000000..b23904844 --- /dev/null +++ b/src/arch/alpha/linux/process.hh @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2003-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __ALPHA_LINUX_PROCESS_HH__ +#define __ALPHA_LINUX_PROCESS_HH__ + +#include "arch/alpha/process.hh" + +namespace AlphaISA { + +/// A process with emulated Alpha/Linux syscalls. +class AlphaLinuxProcess : public AlphaLiveProcess +{ + public: + /// Constructor. + AlphaLinuxProcess(const std::string &name, + ObjectFile *objFile, + System *system, + int stdin_fd, int stdout_fd, int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid); + + virtual SyscallDesc* getDesc(int callnum); + + /// The target system's hostname. + static const char *hostname; + + /// Array of syscall descriptors, indexed by call number. + static SyscallDesc syscallDescs[]; + + const int Num_Syscall_Descs; +}; + +} // namespace AlphaISA +#endif // __ALPHA_LINUX_PROCESS_HH__ diff --git a/src/arch/alpha/linux/system.cc b/src/arch/alpha/linux/system.cc new file mode 100644 index 000000000..7cf234eeb --- /dev/null +++ b/src/arch/alpha/linux/system.cc @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Lisa Hsu + * Nathan Binkert + * Steve Reinhardt + */ + +/** + * @file + * This code loads the linux kernel, console, pal and patches certain + * functions. The symbol tables are loaded so that traces can show + * the executing function and we can skip functions. Various delay + * loops are skipped and their final values manually computed to speed + * up boot time. + */ + +#include "arch/arguments.hh" +#include "arch/vtophys.hh" +#include "arch/alpha/linux/system.hh" +#include "arch/alpha/linux/threadinfo.hh" +#include "arch/alpha/system.hh" +#include "base/loader/symtab.hh" +#include "cpu/thread_context.hh" +#include "cpu/base.hh" +#include "dev/platform.hh" +#include "kern/linux/printk.hh" +#include "kern/linux/events.hh" +#include "mem/physical.hh" +#include "mem/port.hh" +#include "sim/builder.hh" +#include "sim/byteswap.hh" + +using namespace std; +using namespace AlphaISA; +using namespace Linux; + +LinuxAlphaSystem::LinuxAlphaSystem(Params *p) + : AlphaSystem(p) +{ + Addr addr = 0; + + /** + * The symbol swapper_pg_dir marks the beginning of the kernel and + * the location of bootloader passed arguments + */ + if (!kernelSymtab->findAddress("swapper_pg_dir", KernelStart)) { + panic("Could not determine start location of kernel"); + } + + /** + * Since we aren't using a bootloader, we have to copy the + * kernel arguments directly into the kernel's memory. + */ + virtPort.writeBlob(CommandLine(), (uint8_t*)params()->boot_osflags.c_str(), + params()->boot_osflags.length()+1); + + /** + * find the address of the est_cycle_freq variable and insert it + * so we don't through the lengthly process of trying to + * calculated it by using the PIT, RTC, etc. + */ + if (kernelSymtab->findAddress("est_cycle_freq", addr)) + virtPort.write(addr, (uint64_t)(Clock::Frequency / + p->boot_cpu_frequency)); + + + /** + * EV5 only supports 127 ASNs so we are going to tell the kernel that the + * paritiuclar EV6 we have only supports 127 asns. + * @todo At some point we should change ev5.hh and the palcode to support + * 255 ASNs. + */ + if (kernelSymtab->findAddress("dp264_mv", addr)) + virtPort.write(addr + 0x18, LittleEndianGuest::htog((uint32_t)127)); + else + panic("could not find dp264_mv\n"); + +#ifndef NDEBUG + kernelPanicEvent = addKernelFuncEvent<BreakPCEvent>("panic"); + if (!kernelPanicEvent) + panic("could not find kernel symbol \'panic\'"); + +#if 0 + kernelDieEvent = addKernelFuncEvent<BreakPCEvent>("die_if_kernel"); + if (!kernelDieEvent) + panic("could not find kernel symbol \'die_if_kernel\'"); +#endif + +#endif + + /** + * Any time ide_delay_50ms, calibarte_delay or + * determine_cpu_caches is called just skip the + * function. Currently determine_cpu_caches only is used put + * information in proc, however if that changes in the future we + * will have to fill in the cache size variables appropriately. + */ + + skipIdeDelay50msEvent = + addKernelFuncEvent<SkipFuncEvent>("ide_delay_50ms"); + skipDelayLoopEvent = + addKernelFuncEvent<SkipDelayLoopEvent>("calibrate_delay"); + skipCacheProbeEvent = + addKernelFuncEvent<SkipFuncEvent>("determine_cpu_caches"); + debugPrintkEvent = addKernelFuncEvent<DebugPrintkEvent>("dprintk"); + idleStartEvent = addKernelFuncEvent<IdleStartEvent>("cpu_idle"); + + if (kernelSymtab->findAddress("alpha_switch_to", addr) && DTRACE(Thread)) { + printThreadEvent = new PrintThreadInfo(&pcEventQueue, "threadinfo", + addr + sizeof(MachInst) * 6); + } else { + printThreadEvent = NULL; + } +} + +LinuxAlphaSystem::~LinuxAlphaSystem() +{ +#ifndef NDEBUG + delete kernelPanicEvent; +#endif + delete skipIdeDelay50msEvent; + delete skipDelayLoopEvent; + delete skipCacheProbeEvent; + delete debugPrintkEvent; + delete idleStartEvent; + delete printThreadEvent; +} + + +void +LinuxAlphaSystem::setDelayLoop(ThreadContext *tc) +{ + Addr addr = 0; + if (kernelSymtab->findAddress("loops_per_jiffy", addr)) { + Tick cpuFreq = tc->getCpuPtr()->frequency(); + Tick intrFreq = platform->intrFrequency(); + VirtualPort *vp; + + vp = tc->getVirtPort(); + vp->writeHtoG(addr, (uint32_t)((cpuFreq / intrFreq) * 0.9988)); + tc->delVirtPort(vp); + } +} + + +void +LinuxAlphaSystem::SkipDelayLoopEvent::process(ThreadContext *tc) +{ + SkipFuncEvent::process(tc); + // calculate and set loops_per_jiffy + ((LinuxAlphaSystem *)tc->getSystemPtr())->setDelayLoop(tc); +} + +void +LinuxAlphaSystem::PrintThreadInfo::process(ThreadContext *tc) +{ + Linux::ThreadInfo ti(tc); + + DPRINTF(Thread, "Currently Executing Thread %s, pid %d, started at: %d\n", + ti.curTaskName(), ti.curTaskPID(), ti.curTaskStart()); +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(LinuxAlphaSystem) + + Param<Tick> boot_cpu_frequency; + SimObjectParam<PhysicalMemory *> physmem; + SimpleEnumParam<System::MemoryMode> mem_mode; + + Param<string> kernel; + Param<string> console; + Param<string> pal; + + Param<string> boot_osflags; + Param<string> readfile; + Param<string> symbolfile; + Param<unsigned int> init_param; + + Param<uint64_t> system_type; + Param<uint64_t> system_rev; + +END_DECLARE_SIM_OBJECT_PARAMS(LinuxAlphaSystem) + +BEGIN_INIT_SIM_OBJECT_PARAMS(LinuxAlphaSystem) + + INIT_PARAM(boot_cpu_frequency, "Frequency of the boot CPU"), + INIT_PARAM(physmem, "phsyical memory"), + INIT_ENUM_PARAM(mem_mode, "Memory Mode, (1=atomic, 2=timing)", + System::MemoryModeStrings), + INIT_PARAM(kernel, "file that contains the kernel code"), + INIT_PARAM(console, "file that contains the console code"), + INIT_PARAM(pal, "file that contains palcode"), + INIT_PARAM_DFLT(boot_osflags, "flags to pass to the kernel during boot", + "a"), + INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), + INIT_PARAM_DFLT(symbolfile, "file to read symbols from", ""), + INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), + INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 34), + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10) + +END_INIT_SIM_OBJECT_PARAMS(LinuxAlphaSystem) + +CREATE_SIM_OBJECT(LinuxAlphaSystem) +{ + AlphaSystem::Params *p = new AlphaSystem::Params; + p->name = getInstanceName(); + p->boot_cpu_frequency = boot_cpu_frequency; + p->physmem = physmem; + p->mem_mode = mem_mode; + p->kernel_path = kernel; + p->console_path = console; + p->palcode = pal; + p->boot_osflags = boot_osflags; + p->init_param = init_param; + p->readfile = readfile; + p->symbolfile = symbolfile; + p->system_type = system_type; + p->system_rev = system_rev; + return new LinuxAlphaSystem(p); +} + +REGISTER_SIM_OBJECT("LinuxAlphaSystem", LinuxAlphaSystem) + diff --git a/src/arch/alpha/linux/system.hh b/src/arch/alpha/linux/system.hh new file mode 100644 index 000000000..6921ba820 --- /dev/null +++ b/src/arch/alpha/linux/system.hh @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Lisa Hsu + * Nathan Binkert + */ + +#ifndef __ARCH_ALPHA_LINUX_SYSTEM_HH__ +#define __ARCH_ALPHA_LINUX_SYSTEM_HH__ + +class ThreadContext; + +class BreakPCEvent; +class IdleStartEvent; + +#include "arch/alpha/system.hh" +#include "kern/linux/events.hh" + +using namespace AlphaISA; +using namespace Linux; + +/** + * This class contains linux specific system code (Loading, Events). + * It points to objects that are the system binaries to load and patches them + * appropriately to work in simulator. + */ +class LinuxAlphaSystem : public AlphaSystem +{ + private: + class SkipDelayLoopEvent : public SkipFuncEvent + { + public: + SkipDelayLoopEvent(PCEventQueue *q, const std::string &desc, Addr addr) + : SkipFuncEvent(q, desc, addr) {} + virtual void process(ThreadContext *tc); + }; + + class PrintThreadInfo : public PCEvent + { + public: + PrintThreadInfo(PCEventQueue *q, const std::string &desc, Addr addr) + : PCEvent(q, desc, addr) {} + virtual void process(ThreadContext *tc); + }; + + + /** + * Addresses defining where the kernel bootloader places various + * elements. Details found in include/asm-alpha/system.h + */ + Addr KernelStart; // Lookup the symbol swapper_pg_dir + + public: + Addr InitStack() const { return KernelStart + 0x02000; } + Addr EmptyPGT() const { return KernelStart + 0x04000; } + Addr EmptyPGE() const { return KernelStart + 0x08000; } + Addr ZeroPGE() const { return KernelStart + 0x0A000; } + Addr StartAddr() const { return KernelStart + 0x10000; } + + Addr Param() const { return ZeroPGE() + 0x0; } + Addr CommandLine() const { return Param() + 0x0; } + Addr InitrdStart() const { return Param() + 0x100; } + Addr InitrdSize() const { return Param() + 0x108; } + static const int CommandLineSize = 256; + + private: +#ifndef NDEBUG + /** Event to halt the simulator if the kernel calls panic() */ + BreakPCEvent *kernelPanicEvent; + + /** Event to halt the simulator if the kernel calls die_if_kernel */ + BreakPCEvent *kernelDieEvent; +#endif + + /** + * Event to skip determine_cpu_caches() because we don't support + * the IPRs that the code can access to figure out cache sizes + */ + SkipFuncEvent *skipCacheProbeEvent; + + /** PC based event to skip the ide_delay_50ms() call */ + SkipFuncEvent *skipIdeDelay50msEvent; + + /** + * PC based event to skip the dprink() call and emulate its + * functionality + */ + DebugPrintkEvent *debugPrintkEvent; + + /** + * Skip calculate_delay_loop() rather than waiting for this to be + * calculated + */ + SkipDelayLoopEvent *skipDelayLoopEvent; + + /** + * Event to print information about thread switches if the trace flag + * Thread is set + */ + PrintThreadInfo *printThreadEvent; + + /** Grab the PCBB of the idle process when it starts */ + IdleStartEvent *idleStartEvent; + + public: + LinuxAlphaSystem(Params *p); + ~LinuxAlphaSystem(); + + void setDelayLoop(ThreadContext *tc); +}; + +#endif // __ARCH_ALPHA_LINUX_SYSTEM_HH__ diff --git a/src/arch/alpha/linux/thread_info.hh b/src/arch/alpha/linux/thread_info.hh new file mode 100644 index 000000000..78257da56 --- /dev/null +++ b/src/arch/alpha/linux/thread_info.hh @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Nathan Binkert + */ + +#ifndef __ARCH_ALPHA_LINUX_THREAD_INFO_H__ +#define __ARCH_ALPHA_LINUX_THREAD_INFO_H__ + +#include "arch/alpha/linux/hwrpb.hh" + +namespace Linux { + struct thread_info { + struct pcb_struct pcb; + Addr_a task; + }; +} + +#endif // __ARCH_ALPHA_LINUX_THREAD_INFO_H__ diff --git a/src/arch/alpha/linux/threadinfo.hh b/src/arch/alpha/linux/threadinfo.hh new file mode 100644 index 000000000..caeb69f15 --- /dev/null +++ b/src/arch/alpha/linux/threadinfo.hh @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2004 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Nathan Binkert + */ + +#ifndef __ARCH_ALPHA_LINUX_LINUX_TREADNIFO_HH__ +#define __ARCH_ALPHA_LINUX_LINUX_TREADNIFO_HH__ + +#include "arch/alpha/linux/thread_info.hh" +#include "cpu/thread_context.hh" +#include "kern/linux/sched.hh" +#include "sim/vptr.hh" + +namespace Linux { + +class ThreadInfo +{ + private: + ThreadContext *tc; + + public: + ThreadInfo(ThreadContext *_tc) : tc(_tc) {} + ~ThreadInfo() {} + + inline VPtr<thread_info> + curThreadInfo() + { + Addr current; + + /* Each kernel stack is only 2 pages, the start of which is the + * thread_info struct. So we can get the address by masking off + * the lower 14 bits. + */ + current = tc->readIntReg(TheISA::StackPointerReg) & ~0x3fff; + return VPtr<thread_info>(tc, current); + } + + inline VPtr<task_struct> + curTaskInfo() + { + Addr task = curThreadInfo()->task; + return VPtr<task_struct>(tc, task); + } + + std::string + curTaskName() + { + return curTaskInfo()->name; + } + + int32_t + curTaskPID() + { + return curTaskInfo()->pid; + } + + uint64_t + curTaskStart() + { + return curTaskInfo()->start; + } +}; + +/* namespace Linux */ } + +#endif // __ARCH_ALPHA_LINUX_LINUX_THREADINFO_HH__ diff --git a/src/arch/alpha/osfpal.cc b/src/arch/alpha/osfpal.cc new file mode 100644 index 000000000..ed1d255a6 --- /dev/null +++ b/src/arch/alpha/osfpal.cc @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include "arch/alpha/osfpal.hh" + +namespace { + const char *strings[PAL::NumCodes] = { + // Priviledged PAL instructions + "halt", // 0x00 + "cflush", // 0x01 + "draina", // 0x02 + 0, // 0x03 + 0, // 0x04 + 0, // 0x05 + 0, // 0x06 + 0, // 0x07 + 0, // 0x08 + "cserve", // 0x09 + "swppal", // 0x0a + 0, // 0x0b + 0, // 0x0c + "wripir", // 0x0d + 0, // 0x0e + 0, // 0x0f + "rdmces", // 0x10 + "wrmces", // 0x11 + 0, // 0x12 + 0, // 0x13 + 0, // 0x14 + 0, // 0x15 + 0, // 0x16 + 0, // 0x17 + 0, // 0x18 + 0, // 0x19 + 0, // 0x1a + 0, // 0x1b + 0, // 0x1c + 0, // 0x1d + 0, // 0x1e + 0, // 0x1f + 0, // 0x20 + 0, // 0x21 + 0, // 0x22 + 0, // 0x23 + 0, // 0x24 + 0, // 0x25 + 0, // 0x26 + 0, // 0x27 + 0, // 0x28 + 0, // 0x29 + 0, // 0x2a + "wrfen", // 0x2b + 0, // 0x2c + "wrvptptr", // 0x2d + 0, // 0x2e + 0, // 0x2f + "swpctx", // 0x30 + "wrval", // 0x31 + "rdval", // 0x32 + "tbi", // 0x33 + "wrent", // 0x34 + "swpipl", // 0x35 + "rdps", // 0x36 + "wrkgp", // 0x37 + "wrusp", // 0x38 + "wrperfmon", // 0x39 + "rdusp", // 0x3a + 0, // 0x3b + "whami", // 0x3c + "retsys", // 0x3d + "wtint", // 0x3e + "rti", // 0x3f + 0, // 0x40 + 0, // 0x41 + 0, // 0x42 + 0, // 0x43 + 0, // 0x44 + 0, // 0x45 + 0, // 0x46 + 0, // 0x47 + 0, // 0x48 + 0, // 0x49 + 0, // 0x4a + 0, // 0x4b + 0, // 0x4c + 0, // 0x4d + 0, // 0x4e + 0, // 0x4f + 0, // 0x50 + 0, // 0x51 + 0, // 0x52 + 0, // 0x53 + 0, // 0x54 + 0, // 0x55 + 0, // 0x56 + 0, // 0x57 + 0, // 0x58 + 0, // 0x59 + 0, // 0x5a + 0, // 0x5b + 0, // 0x5c + 0, // 0x5d + 0, // 0x5e + 0, // 0x5f + 0, // 0x60 + 0, // 0x61 + 0, // 0x62 + 0, // 0x63 + 0, // 0x64 + 0, // 0x65 + 0, // 0x66 + 0, // 0x67 + 0, // 0x68 + 0, // 0x69 + 0, // 0x6a + 0, // 0x6b + 0, // 0x6c + 0, // 0x6d + 0, // 0x6e + 0, // 0x6f + 0, // 0x70 + 0, // 0x71 + 0, // 0x72 + 0, // 0x73 + 0, // 0x74 + 0, // 0x75 + 0, // 0x76 + 0, // 0x77 + 0, // 0x78 + 0, // 0x79 + 0, // 0x7a + 0, // 0x7b + 0, // 0x7c + 0, // 0x7d + 0, // 0x7e + 0, // 0x7f + + // Unpriviledged PAL instructions + "bpt", // 0x80 + "bugchk", // 0x81 + 0, // 0x82 + "callsys", // 0x83 + 0, // 0x84 + 0, // 0x85 + "imb", // 0x86 + 0, // 0x87 + 0, // 0x88 + 0, // 0x89 + 0, // 0x8a + 0, // 0x8b + 0, // 0x8c + 0, // 0x8d + 0, // 0x8e + 0, // 0x8f + 0, // 0x90 + 0, // 0x91 + "urti", // 0x92 + 0, // 0x93 + 0, // 0x94 + 0, // 0x95 + 0, // 0x96 + 0, // 0x97 + 0, // 0x98 + 0, // 0x99 + 0, // 0x9a + 0, // 0x9b + 0, // 0x9c + 0, // 0x9d + "rdunique", // 0x9e + "wrunique", // 0x9f + 0, // 0xa0 + 0, // 0xa1 + 0, // 0xa2 + 0, // 0xa3 + 0, // 0xa4 + 0, // 0xa5 + 0, // 0xa6 + 0, // 0xa7 + 0, // 0xa8 + 0, // 0xa9 + "gentrap", // 0xaa + 0, // 0xab + 0, // 0xac + 0, // 0xad + "clrfen", // 0xae + 0, // 0xaf + 0, // 0xb0 + 0, // 0xb1 + 0, // 0xb2 + 0, // 0xb3 + 0, // 0xb4 + 0, // 0xb5 + 0, // 0xb6 + 0, // 0xb7 + 0, // 0xb8 + 0, // 0xb9 + 0, // 0xba + 0, // 0xbb + 0, // 0xbc + 0, // 0xbd + "nphalt", // 0xbe + "copypal", // 0xbf +#if 0 + 0, // 0xc0 + 0, // 0xc1 + 0, // 0xc2 + 0, // 0xc3 + 0, // 0xc4 + 0, // 0xc5 + 0, // 0xc6 + 0, // 0xc7 + 0, // 0xc8 + 0, // 0xc9 + 0, // 0xca + 0, // 0xcb + 0, // 0xcc + 0, // 0xcd + 0, // 0xce + 0, // 0xcf + 0, // 0xd0 + 0, // 0xd1 + 0, // 0xd2 + 0, // 0xd3 + 0, // 0xd4 + 0, // 0xd5 + 0, // 0xd6 + 0, // 0xd7 + 0, // 0xd8 + 0, // 0xd9 + 0, // 0xda + 0, // 0xdb + 0, // 0xdc + 0, // 0xdd + 0, // 0xde + 0, // 0xdf + 0, // 0xe0 + 0, // 0xe1 + 0, // 0xe2 + 0, // 0xe3 + 0, // 0xe4 + 0, // 0xe5 + 0, // 0xe6 + 0, // 0xe7 + 0, // 0xe8 + 0, // 0xe9 + 0, // 0xea + 0, // 0xeb + 0, // 0xec + 0, // 0xed + 0, // 0xee + 0, // 0xef + 0, // 0xf0 + 0, // 0xf1 + 0, // 0xf2 + 0, // 0xf3 + 0, // 0xf4 + 0, // 0xf5 + 0, // 0xf6 + 0, // 0xf7 + 0, // 0xf8 + 0, // 0xf9 + 0, // 0xfa + 0, // 0xfb + 0, // 0xfc + 0, // 0xfd + 0, // 0xfe + 0 // 0xff +#endif + }; +} + +const char * +PAL::name(int index) +{ + if (index > NumCodes || index < 0) + return 0; + + return strings[index]; +} diff --git a/src/arch/alpha/osfpal.hh b/src/arch/alpha/osfpal.hh new file mode 100644 index 000000000..cf3940b85 --- /dev/null +++ b/src/arch/alpha/osfpal.hh @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __OSFPAL_HH__ +#define __OSFPAL_HH__ + +struct PAL +{ + enum { + // Privileged PAL functions + halt = 0x00, + cflush = 0x01, + draina = 0x02, + cserve = 0x09, + swppal = 0x0a, + wripir = 0x0d, + rdmces = 0x10, + wrmces = 0x11, + wrfen = 0x2b, + wrvptptr = 0x2d, + swpctx = 0x30, + wrval = 0x31, + rdval = 0x32, + tbi = 0x33, + wrent = 0x34, + swpipl = 0x35, + rdps = 0x36, + wrkgp = 0x37, + wrusp = 0x38, + wrperfmon = 0x39, + rdusp = 0x3a, + whami = 0x3c, + retsys = 0x3d, + wtint = 0x3e, + rti = 0x3f, + + // unprivileged pal functions + bpt = 0x80, + bugchk = 0x81, + callsys = 0x83, + imb = 0x86, + urti = 0x92, + rdunique = 0x9e, + wrunique = 0x9f, + gentrap = 0xaa, + clrfen = 0xae, + nphalt = 0xbe, + copypal = 0xbf, + NumCodes + }; + + static const char *name(int index); +}; + +#endif // __OSFPAL_HH__ diff --git a/src/arch/alpha/pagetable.hh b/src/arch/alpha/pagetable.hh new file mode 100644 index 000000000..3108c0a3e --- /dev/null +++ b/src/arch/alpha/pagetable.hh @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __ARCH_ALPHA_PAGETABLE_H__ +#define __ARCH_ALPHA_PAGETABLE_H__ + +#include "arch/alpha/isa_traits.hh" +#include "arch/alpha/utility.hh" +#include "config/full_system.hh" + +namespace AlphaISA { + +#if FULL_SYSTEM + struct VAddr + { + static const int ImplBits = 43; + static const Addr ImplMask = (ULL(1) << ImplBits) - 1; + static const Addr UnImplMask = ~ImplMask; + + VAddr(Addr a) : addr(a) {} + Addr addr; + operator Addr() const { return addr; } + const VAddr &operator=(Addr a) { addr = a; return *this; } + + Addr vpn() const { return (addr & ImplMask) >> PageShift; } + Addr page() const { return addr & PageMask; } + Addr offset() const { return addr & PageOffset; } + + Addr level3() const + { return AlphaISA::PteAddr(addr >> PageShift); } + Addr level2() const + { return AlphaISA::PteAddr(addr >> NPtePageShift + PageShift); } + Addr level1() const + { return AlphaISA::PteAddr(addr >> 2 * NPtePageShift + PageShift); } + }; + + struct PageTableEntry + { + PageTableEntry(uint64_t e) : entry(e) {} + uint64_t entry; + operator uint64_t() const { return entry; } + const PageTableEntry &operator=(uint64_t e) { entry = e; return *this; } + const PageTableEntry &operator=(const PageTableEntry &e) + { entry = e.entry; return *this; } + + Addr _pfn() const { return (entry >> 32) & 0xffffffff; } + Addr _sw() const { return (entry >> 16) & 0xffff; } + int _rsv0() const { return (entry >> 14) & 0x3; } + bool _uwe() const { return (entry >> 13) & 0x1; } + bool _kwe() const { return (entry >> 12) & 0x1; } + int _rsv1() const { return (entry >> 10) & 0x3; } + bool _ure() const { return (entry >> 9) & 0x1; } + bool _kre() const { return (entry >> 8) & 0x1; } + bool _nomb() const { return (entry >> 7) & 0x1; } + int _gh() const { return (entry >> 5) & 0x3; } + bool _asm() const { return (entry >> 4) & 0x1; } + bool _foe() const { return (entry >> 3) & 0x1; } + bool _fow() const { return (entry >> 2) & 0x1; } + bool _for() const { return (entry >> 1) & 0x1; } + bool valid() const { return (entry >> 0) & 0x1; } + + Addr paddr() const { return _pfn() << PageShift; } + }; + + // ITB/DTB page table entry + struct PTE + { + Addr tag; // virtual page number tag + Addr ppn; // physical page number + uint8_t xre; // read permissions - VMEM_PERM_* mask + uint8_t xwe; // write permissions - VMEM_PERM_* mask + uint8_t asn; // address space number + bool asma; // address space match + bool fonr; // fault on read + bool fonw; // fault on write + bool valid; // valid page table entry + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + }; +#endif +}; +#endif // __ARCH_ALPHA_PAGETABLE_H__ + diff --git a/src/arch/alpha/process.cc b/src/arch/alpha/process.cc new file mode 100644 index 000000000..9e360e80f --- /dev/null +++ b/src/arch/alpha/process.cc @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2003-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + */ + +#include "arch/alpha/isa_traits.hh" +#include "arch/alpha/process.hh" +#include "base/loader/object_file.hh" +#include "base/misc.hh" +#include "cpu/thread_context.hh" +#include "sim/system.hh" + + +using namespace AlphaISA; +using namespace std; + +AlphaLiveProcess::AlphaLiveProcess(const std::string &nm, ObjectFile *objFile, + System *_system, int stdin_fd, int stdout_fd, int stderr_fd, + std::vector<std::string> &argv, std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid) + : LiveProcess(nm, objFile, _system, stdin_fd, stdout_fd, stderr_fd, + argv, envp, _uid, _euid, _gid, _egid, _pid, _ppid) +{ + brk_point = objFile->dataBase() + objFile->dataSize() + objFile->bssSize(); + brk_point = roundUp(brk_point, VMPageSize); + + // Set up stack. On Alpha, stack goes below text section. This + // code should get moved to some architecture-specific spot. + stack_base = objFile->textBase() - (409600+4096); + + // Set up region for mmaps. Tru64 seems to start just above 0 and + // grow up from there. + mmap_start = mmap_end = 0x10000; + + // Set pointer for next thread stack. Reserve 8M for main stack. + next_thread_stack_base = stack_base - (8 * 1024 * 1024); + +} + +void +AlphaLiveProcess::startup() +{ + argsInit(MachineBytes, VMPageSize); + + threadContexts[0]->setIntReg(GlobalPointerReg, objFile->globalPointer()); +} + + diff --git a/src/arch/alpha/process.hh b/src/arch/alpha/process.hh new file mode 100644 index 000000000..c4aeb1885 --- /dev/null +++ b/src/arch/alpha/process.hh @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2003-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + */ + +#ifndef __ALPHA_PROCESS_HH__ +#define __ALPHA_PROCESS_HH__ + +#include <string> +#include <vector> +#include "sim/process.hh" + +class ObjectFile; +class System; + + +class AlphaLiveProcess : public LiveProcess +{ + protected: + AlphaLiveProcess(const std::string &nm, ObjectFile *objFile, + System *_system, int stdin_fd, int stdout_fd, int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid); + + void startup(); +}; + + +#endif // __ALPHA_PROCESS_HH__ diff --git a/src/arch/alpha/regfile.hh b/src/arch/alpha/regfile.hh new file mode 100644 index 000000000..43b48a0ab --- /dev/null +++ b/src/arch/alpha/regfile.hh @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + */ + +#ifndef __ARCH_ALPHA_REGFILE_HH__ +#define __ARCH_ALPHA_REGFILE_HH__ + +#include "arch/alpha/types.hh" +#include "arch/alpha/isa_traits.hh" +#include "sim/faults.hh" + +#include <string> + +//XXX These should be implemented by someone who knows the alpha stuff better + +class Checkpoint; +class ThreadContext; + +namespace AlphaISA +{ + + static inline std::string getIntRegName(RegIndex) + { + return ""; + } + + static inline std::string getFloatRegName(RegIndex) + { + return ""; + } + + static inline std::string getMiscRegName(RegIndex) + { + return ""; + } + + class IntRegFile + { + protected: + IntReg regs[NumIntRegs]; + + public: + + IntReg readReg(int intReg) + { + return regs[intReg]; + } + + Fault setReg(int intReg, const IntReg &val) + { + regs[intReg] = val; + return NoFault; + } + + void serialize(std::ostream &os); + + void unserialize(Checkpoint *cp, const std::string §ion); + + void clear() + { bzero(regs, sizeof(regs)); } + }; + + class FloatRegFile + { + public: + + union { + uint64_t q[NumFloatRegs]; // integer qword view + double d[NumFloatRegs]; // double-precision floating point view + }; + + void serialize(std::ostream &os); + + void unserialize(Checkpoint *cp, const std::string §ion); + + void clear() + { bzero(d, sizeof(d)); } + }; + + class MiscRegFile { + protected: + uint64_t fpcr; // floating point condition codes + uint64_t uniq; // process-unique register + bool lock_flag; // lock flag for LL/SC + Addr lock_addr; // lock address for LL/SC + + public: + MiscReg readReg(int misc_reg); + + MiscReg readRegWithEffect(int misc_reg, Fault &fault, + ThreadContext *tc); + + //These functions should be removed once the simplescalar cpu model + //has been replaced. + int getInstAsid(); + int getDataAsid(); + + Fault setReg(int misc_reg, const MiscReg &val); + + Fault setRegWithEffect(int misc_reg, const MiscReg &val, + ThreadContext *tc); + + void clear() + { + fpcr = uniq = 0; + lock_flag = 0; + lock_addr = 0; + } + + void serialize(std::ostream &os); + + void unserialize(Checkpoint *cp, const std::string §ion); +#if FULL_SYSTEM + protected: + typedef uint64_t InternalProcReg; + + InternalProcReg ipr[NumInternalProcRegs]; // Internal processor regs + + private: + InternalProcReg readIpr(int idx, Fault &fault, ThreadContext *tc); + + Fault setIpr(int idx, InternalProcReg val, ThreadContext *tc); +#endif + friend class RegFile; + }; + + class RegFile { + + protected: + Addr pc; // program counter + Addr npc; // next-cycle program counter + Addr nnpc; + + public: + Addr readPC() + { + return pc; + } + + void setPC(Addr val) + { + pc = val; + } + + Addr readNextPC() + { + return npc; + } + + void setNextPC(Addr val) + { + npc = val; + } + + Addr readNextNPC() + { + return nnpc; + } + + void setNextNPC(Addr val) + { + nnpc = val; + } + + protected: + IntRegFile intRegFile; // (signed) integer register file + FloatRegFile floatRegFile; // floating point register file + MiscRegFile miscRegFile; // control register file + + public: + +#if FULL_SYSTEM + int intrflag; // interrupt flag + inline int instAsid() + { return miscRegFile.getInstAsid(); } + inline int dataAsid() + { return miscRegFile.getDataAsid(); } +#endif // FULL_SYSTEM + + void clear() + { + intRegFile.clear(); + floatRegFile.clear(); + miscRegFile.clear(); + } + + MiscReg readMiscReg(int miscReg) + { + return miscRegFile.readReg(miscReg); + } + + MiscReg readMiscRegWithEffect(int miscReg, + Fault &fault, ThreadContext *tc) + { + fault = NoFault; + return miscRegFile.readRegWithEffect(miscReg, fault, tc); + } + + Fault setMiscReg(int miscReg, const MiscReg &val) + { + return miscRegFile.setReg(miscReg, val); + } + + Fault setMiscRegWithEffect(int miscReg, const MiscReg &val, + ThreadContext * tc) + { + return miscRegFile.setRegWithEffect(miscReg, val, tc); + } + + FloatReg readFloatReg(int floatReg) + { + return floatRegFile.d[floatReg]; + } + + FloatReg readFloatReg(int floatReg, int width) + { + return readFloatReg(floatReg); + } + + FloatRegBits readFloatRegBits(int floatReg) + { + return floatRegFile.q[floatReg]; + } + + FloatRegBits readFloatRegBits(int floatReg, int width) + { + return readFloatRegBits(floatReg); + } + + Fault setFloatReg(int floatReg, const FloatReg &val) + { + floatRegFile.d[floatReg] = val; + return NoFault; + } + + Fault setFloatReg(int floatReg, const FloatReg &val, int width) + { + return setFloatReg(floatReg, val); + } + + Fault setFloatRegBits(int floatReg, const FloatRegBits &val) + { + floatRegFile.q[floatReg] = val; + return NoFault; + } + + Fault setFloatRegBits(int floatReg, const FloatRegBits &val, int width) + { + return setFloatRegBits(floatReg, val); + } + + IntReg readIntReg(int intReg) + { + return intRegFile.readReg(intReg); + } + + Fault setIntReg(int intReg, const IntReg &val) + { + return intRegFile.setReg(intReg, val); + } + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + void changeContext(RegContextParam param, RegContextVal val) + { + //This would be an alternative place to call/implement + //the swapPALShadow function + } + }; + + void copyRegs(ThreadContext *src, ThreadContext *dest); + + void copyMiscRegs(ThreadContext *src, ThreadContext *dest); + +#if FULL_SYSTEM + void copyIprs(ThreadContext *src, ThreadContext *dest); +#endif +} // namespace AlphaISA + +#endif diff --git a/src/arch/alpha/stacktrace.cc b/src/arch/alpha/stacktrace.cc new file mode 100644 index 000000000..d70a4d6dd --- /dev/null +++ b/src/arch/alpha/stacktrace.cc @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <string> + +#include "arch/alpha/isa_traits.hh" +#include "arch/alpha/stacktrace.hh" +#include "arch/alpha/vtophys.hh" +#include "base/bitfield.hh" +#include "base/trace.hh" +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "sim/system.hh" + +using namespace std; +using namespace AlphaISA; + +ProcessInfo::ProcessInfo(ThreadContext *_tc) + : tc(_tc) +{ + Addr addr = 0; + + VirtualPort *vp; + + vp = tc->getVirtPort(); + + if (!tc->getSystemPtr()->kernelSymtab->findAddress("thread_info_size", addr)) + panic("thread info not compiled into kernel\n"); + thread_info_size = vp->readGtoH<int32_t>(addr); + + if (!tc->getSystemPtr()->kernelSymtab->findAddress("task_struct_size", addr)) + panic("thread info not compiled into kernel\n"); + task_struct_size = vp->readGtoH<int32_t>(addr); + + if (!tc->getSystemPtr()->kernelSymtab->findAddress("thread_info_task", addr)) + panic("thread info not compiled into kernel\n"); + task_off = vp->readGtoH<int32_t>(addr); + + if (!tc->getSystemPtr()->kernelSymtab->findAddress("task_struct_pid", addr)) + panic("thread info not compiled into kernel\n"); + pid_off = vp->readGtoH<int32_t>(addr); + + if (!tc->getSystemPtr()->kernelSymtab->findAddress("task_struct_comm", addr)) + panic("thread info not compiled into kernel\n"); + name_off = vp->readGtoH<int32_t>(addr); + + tc->delVirtPort(vp); +} + +Addr +ProcessInfo::task(Addr ksp) const +{ + Addr base = ksp & ~0x3fff; + if (base == ULL(0xfffffc0000000000)) + return 0; + + Addr tsk; + + VirtualPort *vp; + + vp = tc->getVirtPort(); + tsk = vp->readGtoH<Addr>(base + task_off); + tc->delVirtPort(vp); + + return tsk; +} + +int +ProcessInfo::pid(Addr ksp) const +{ + Addr task = this->task(ksp); + if (!task) + return -1; + + uint16_t pd; + + VirtualPort *vp; + + vp = tc->getVirtPort(); + pd = vp->readGtoH<uint16_t>(task + pid_off); + tc->delVirtPort(vp); + + return pd; +} + +string +ProcessInfo::name(Addr ksp) const +{ + Addr task = this->task(ksp); + if (!task) + return "console"; + + char comm[256]; + CopyStringOut(tc, comm, task + name_off, sizeof(comm)); + if (!comm[0]) + return "startup"; + + return comm; +} + +StackTrace::StackTrace() + : tc(0), stack(64) +{ +} + +StackTrace::StackTrace(ThreadContext *_tc, StaticInstPtr inst) + : tc(0), stack(64) +{ + trace(_tc, inst); +} + +StackTrace::~StackTrace() +{ +} + +void +StackTrace::trace(ThreadContext *_tc, bool is_call) +{ + tc = _tc; + + bool usermode = (tc->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0; + + Addr pc = tc->readNextPC(); + bool kernel = tc->getSystemPtr()->kernelStart <= pc && + pc <= tc->getSystemPtr()->kernelEnd; + + if (usermode) { + stack.push_back(user); + return; + } + + if (!kernel) { + stack.push_back(console); + return; + } + + SymbolTable *symtab = tc->getSystemPtr()->kernelSymtab; + Addr ksp = tc->readIntReg(TheISA::StackPointerReg); + Addr bottom = ksp & ~0x3fff; + Addr addr; + + if (is_call) { + if (!symtab->findNearestAddr(pc, addr)) + panic("could not find address %#x", pc); + + stack.push_back(addr); + pc = tc->readPC(); + } + + Addr ra; + int size; + + while (ksp > bottom) { + if (!symtab->findNearestAddr(pc, addr)) + panic("could not find symbol for pc=%#x", pc); + assert(pc >= addr && "symbol botch: callpc < func"); + + stack.push_back(addr); + + if (isEntry(addr)) + return; + + if (decodePrologue(ksp, pc, addr, size, ra)) { + if (!ra) + return; + + if (size <= 0) { + stack.push_back(unknown); + return; + } + + pc = ra; + ksp += size; + } else { + stack.push_back(unknown); + return; + } + + bool kernel = tc->getSystemPtr()->kernelStart <= pc && + pc <= tc->getSystemPtr()->kernelEnd; + if (!kernel) + return; + + if (stack.size() >= 1000) + panic("unwinding too far"); + } + + panic("unwinding too far"); +} + +bool +StackTrace::isEntry(Addr addr) +{ + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp12)) + return true; + + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp7)) + return true; + + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp11)) + return true; + + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp21)) + return true; + + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp9)) + return true; + + if (addr == tc->readMiscReg(AlphaISA::IPR_PALtemp2)) + return true; + + return false; +} + +bool +StackTrace::decodeStack(MachInst inst, int &disp) +{ + // lda $sp, -disp($sp) + // + // Opcode<31:26> == 0x08 + // RA<25:21> == 30 + // RB<20:16> == 30 + // Disp<15:0> + const MachInst mem_mask = 0xffff0000; + const MachInst lda_pattern = 0x23de0000; + const MachInst lda_disp_mask = 0x0000ffff; + + // subq $sp, disp, $sp + // addq $sp, disp, $sp + // + // Opcode<31:26> == 0x10 + // RA<25:21> == 30 + // Lit<20:13> + // One<12> = 1 + // Func<11:5> == 0x20 (addq) + // Func<11:5> == 0x29 (subq) + // RC<4:0> == 30 + const MachInst intop_mask = 0xffe01fff; + const MachInst addq_pattern = 0x43c0141e; + const MachInst subq_pattern = 0x43c0153e; + const MachInst intop_disp_mask = 0x001fe000; + const int intop_disp_shift = 13; + + if ((inst & mem_mask) == lda_pattern) + disp = -sext<16>(inst & lda_disp_mask); + else if ((inst & intop_mask) == addq_pattern) + disp = -int((inst & intop_disp_mask) >> intop_disp_shift); + else if ((inst & intop_mask) == subq_pattern) + disp = int((inst & intop_disp_mask) >> intop_disp_shift); + else + return false; + + return true; +} + +bool +StackTrace::decodeSave(MachInst inst, int ®, int &disp) +{ + // lda $stq, disp($sp) + // + // Opcode<31:26> == 0x08 + // RA<25:21> == ? + // RB<20:16> == 30 + // Disp<15:0> + const MachInst stq_mask = 0xfc1f0000; + const MachInst stq_pattern = 0xb41e0000; + const MachInst stq_disp_mask = 0x0000ffff; + const MachInst reg_mask = 0x03e00000; + const int reg_shift = 21; + + if ((inst & stq_mask) == stq_pattern) { + reg = (inst & reg_mask) >> reg_shift; + disp = sext<16>(inst & stq_disp_mask); + } else { + return false; + } + + return true; +} + +/* + * Decode the function prologue for the function we're in, and note + * which registers are stored where, and how large the stack frame is. + */ +bool +StackTrace::decodePrologue(Addr sp, Addr callpc, Addr func, + int &size, Addr &ra) +{ + size = 0; + ra = 0; + + for (Addr pc = func; pc < callpc; pc += sizeof(MachInst)) { + MachInst inst; + CopyOut(tc, (uint8_t *)&inst, pc, sizeof(MachInst)); + + int reg, disp; + if (decodeStack(inst, disp)) { + if (size) { + // panic("decoding frame size again"); + return true; + } + size += disp; + } else if (decodeSave(inst, reg, disp)) { + if (!ra && reg == ReturnAddressReg) { + CopyOut(tc, (uint8_t *)&ra, sp + disp, sizeof(Addr)); + if (!ra) { + // panic("no return address value pc=%#x\n", pc); + return false; + } + } + } + } + + return true; +} + +#if TRACING_ON +void +StackTrace::dump() +{ + StringWrap name(tc->getCpuPtr()->name()); + SymbolTable *symtab = tc->getSystemPtr()->kernelSymtab; + + DPRINTFN("------ Stack ------\n"); + + string symbol; + for (int i = 0, size = stack.size(); i < size; ++i) { + Addr addr = stack[size - i - 1]; + if (addr == user) + symbol = "user"; + else if (addr == console) + symbol = "console"; + else if (addr == unknown) + symbol = "unknown"; + else + symtab->findSymbol(addr, symbol); + + DPRINTFN("%#x: %s\n", addr, symbol); + } +} +#endif diff --git a/src/arch/alpha/stacktrace.hh b/src/arch/alpha/stacktrace.hh new file mode 100644 index 000000000..d12aee211 --- /dev/null +++ b/src/arch/alpha/stacktrace.hh @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __ARCH_ALPHA_STACKTRACE_HH__ +#define __ARCH_ALPHA_STACKTRACE_HH__ + +#include "base/trace.hh" +#include "cpu/static_inst.hh" + +class ThreadContext; +class StackTrace; + +class ProcessInfo +{ + private: + ThreadContext *tc; + + int thread_info_size; + int task_struct_size; + int task_off; + int pid_off; + int name_off; + + public: + ProcessInfo(ThreadContext *_tc); + + Addr task(Addr ksp) const; + int pid(Addr ksp) const; + std::string name(Addr ksp) const; +}; + +class StackTrace +{ + protected: + typedef TheISA::MachInst MachInst; + private: + ThreadContext *tc; + std::vector<Addr> stack; + + private: + bool isEntry(Addr addr); + bool decodePrologue(Addr sp, Addr callpc, Addr func, int &size, Addr &ra); + bool decodeSave(MachInst inst, int ®, int &disp); + bool decodeStack(MachInst inst, int &disp); + + void trace(ThreadContext *tc, bool is_call); + + public: + StackTrace(); + StackTrace(ThreadContext *tc, StaticInstPtr inst); + ~StackTrace(); + + void clear() + { + tc = 0; + stack.clear(); + } + + bool valid() const { return tc != NULL; } + bool trace(ThreadContext *tc, StaticInstPtr inst); + + public: + const std::vector<Addr> &getstack() const { return stack; } + + static const int user = 1; + static const int console = 2; + static const int unknown = 3; + +#if TRACING_ON + private: + void dump(); + + public: + void dprintf() { if (DTRACE(Stack)) dump(); } +#else + public: + void dprintf() {} +#endif +}; + +inline bool +StackTrace::trace(ThreadContext *tc, StaticInstPtr inst) +{ + if (!inst->isCall() && !inst->isReturn()) + return false; + + if (valid()) + clear(); + + trace(tc, !inst->isReturn()); + return true; +} + +#endif // __ARCH_ALPHA_STACKTRACE_HH__ diff --git a/src/arch/alpha/syscallreturn.hh b/src/arch/alpha/syscallreturn.hh new file mode 100644 index 000000000..803c3b7da --- /dev/null +++ b/src/arch/alpha/syscallreturn.hh @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Gabe Black + */ + +#ifndef __ARCH_ALPHA_SYSCALLRETURN_HH__ +#define __ARCH_ALPHA_SYSCALLRETURN_HH__ + +class SyscallReturn { + public: + template <class T> + SyscallReturn(T v, bool s) + { + retval = (uint64_t)v; + success = s; + } + + template <class T> + SyscallReturn(T v) + { + success = (v >= 0); + retval = (uint64_t)v; + } + + ~SyscallReturn() {} + + SyscallReturn& operator=(const SyscallReturn& s) { + retval = s.retval; + success = s.success; + return *this; + } + + bool successful() { return success; } + uint64_t value() { return retval; } + + + private: + uint64_t retval; + bool success; +}; + +namespace AlphaISA +{ + static inline void setSyscallReturn(SyscallReturn return_value, RegFile *regs) + { + // check for error condition. Alpha syscall convention is to + // indicate success/failure in reg a3 (r19) and put the + // return value itself in the standard return value reg (v0). + if (return_value.successful()) { + // no error + regs->setIntReg(SyscallSuccessReg, 0); + regs->setIntReg(ReturnValueReg, return_value.value()); + } else { + // got an error, return details + regs->setIntReg(SyscallSuccessReg, (IntReg)-1); + regs->setIntReg(ReturnValueReg, -return_value.value()); + } + } +} + +#endif diff --git a/src/arch/alpha/system.cc b/src/arch/alpha/system.cc new file mode 100644 index 000000000..5597eaedc --- /dev/null +++ b/src/arch/alpha/system.cc @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Nathan Binkert + */ + +#include "arch/alpha/ev5.hh" +#include "arch/alpha/system.hh" +#include "arch/vtophys.hh" +#include "base/remote_gdb.hh" +#include "base/loader/object_file.hh" +#include "base/loader/symtab.hh" +#include "base/trace.hh" +#include "mem/physical.hh" +#include "sim/byteswap.hh" +#include "sim/builder.hh" + + +using namespace LittleEndianGuest; + +AlphaSystem::AlphaSystem(Params *p) + : System(p) +{ + consoleSymtab = new SymbolTable; + palSymtab = new SymbolTable; + + + /** + * Load the pal, and console code into memory + */ + // Load Console Code + console = createObjectFile(params()->console_path); + if (console == NULL) + fatal("Could not load console file %s", params()->console_path); + + // Load pal file + pal = createObjectFile(params()->palcode); + if (pal == NULL) + fatal("Could not load PALcode file %s", params()->palcode); + + + // Load program sections into memory + pal->loadSections(&functionalPort, AlphaISA::LoadAddrMask); + console->loadSections(&functionalPort, AlphaISA::LoadAddrMask); + + // load symbols + if (!console->loadGlobalSymbols(consoleSymtab)) + panic("could not load console symbols\n"); + + if (!pal->loadGlobalSymbols(palSymtab)) + panic("could not load pal symbols\n"); + + if (!pal->loadLocalSymbols(palSymtab)) + panic("could not load pal symbols\n"); + + if (!console->loadGlobalSymbols(debugSymbolTable)) + panic("could not load console symbols\n"); + + if (!pal->loadGlobalSymbols(debugSymbolTable)) + panic("could not load pal symbols\n"); + + if (!pal->loadLocalSymbols(debugSymbolTable)) + panic("could not load pal symbols\n"); + + Addr addr = 0; +#ifndef NDEBUG + consolePanicEvent = addConsoleFuncEvent<BreakPCEvent>("panic"); +#endif + + /** + * Copy the osflags (kernel arguments) into the consoles + * memory. (Presently Linux does not use the console service + * routine to get these command line arguments, but Tru64 and + * others do.) + */ + if (consoleSymtab->findAddress("env_booted_osflags", addr)) { + virtPort.writeBlob(addr, (uint8_t*)params()->boot_osflags.c_str(), + strlen(params()->boot_osflags.c_str())); + } + + /** + * Set the hardware reset parameter block system type and revision + * information to Tsunami. + */ + if (consoleSymtab->findAddress("m5_rpb", addr)) { + uint64_t data; + data = htog(params()->system_type); + virtPort.write(addr+0x50, data); + data = htog(params()->system_rev); + virtPort.write(addr+0x58, data); + } else + panic("could not find hwrpb\n"); + +} + +AlphaSystem::~AlphaSystem() +{ + delete consoleSymtab; + delete console; + delete pal; +#ifdef DEBUG + delete consolePanicEvent; +#endif +} + +/** + * This function fixes up addresses that are used to match PCs for + * hooking simulator events on to target function executions. + * + * Alpha binaries may have multiple global offset table (GOT) + * sections. A function that uses the GOT starts with a + * two-instruction prolog which sets the global pointer (gp == r29) to + * the appropriate GOT section. The proper gp value is calculated + * based on the function address, which must be passed by the caller + * in the procedure value register (pv aka t12 == r27). This sequence + * looks like the following: + * + * opcode Ra Rb offset + * ldah gp,X(pv) 09 29 27 X + * lda gp,Y(gp) 08 29 29 Y + * + * for some constant offsets X and Y. The catch is that the linker + * (or maybe even the compiler, I'm not sure) may recognize that the + * caller and callee are using the same GOT section, making this + * prolog redundant, and modify the call target to skip these + * instructions. If we check for execution of the first instruction + * of a function (the one the symbol points to) to detect when to skip + * it, we'll miss all these modified calls. It might work to + * unconditionally check for the third instruction, but not all + * functions have this prolog, and there's some chance that those + * first two instructions could have undesired consequences. So we do + * the Right Thing and pattern-match the first two instructions of the + * function to decide where to patch. + * + * Eventually this code should be moved into an ISA-specific file. + */ +Addr +AlphaSystem::fixFuncEventAddr(Addr addr) +{ + // mask for just the opcode, Ra, and Rb fields (not the offset) + const uint32_t inst_mask = 0xffff0000; + // ldah gp,X(pv): opcode 9, Ra = 29, Rb = 27 + const uint32_t gp_ldah_pattern = (9 << 26) | (29 << 21) | (27 << 16); + // lda gp,Y(gp): opcode 8, Ra = 29, rb = 29 + const uint32_t gp_lda_pattern = (8 << 26) | (29 << 21) | (29 << 16); + + uint32_t i1 = virtPort.read<uint32_t>(addr); + uint32_t i2 = virtPort.read<uint32_t>(addr + sizeof(AlphaISA::MachInst)); + + if ((i1 & inst_mask) == gp_ldah_pattern && + (i2 & inst_mask) == gp_lda_pattern) { + Addr new_addr = addr + 2* sizeof(AlphaISA::MachInst); + DPRINTF(Loader, "fixFuncEventAddr: %p -> %p", addr, new_addr); + return new_addr; + } else { + return addr; + } +} + + +void +AlphaSystem::setAlphaAccess(Addr access) +{ + Addr addr = 0; + if (consoleSymtab->findAddress("m5AlphaAccess", addr)) { + virtPort.write(addr, htog(EV5::Phys2K0Seg(access))); + } else + panic("could not find m5AlphaAccess\n"); +} + +bool +AlphaSystem::breakpoint() +{ + return remoteGDB[0]->trap(ALPHA_KENTRY_INT); +} + +void +AlphaSystem::serialize(std::ostream &os) +{ + System::serialize(os); + consoleSymtab->serialize("console_symtab", os); + palSymtab->serialize("pal_symtab", os); +} + + +void +AlphaSystem::unserialize(Checkpoint *cp, const std::string §ion) +{ + System::unserialize(cp,section); + consoleSymtab->unserialize("console_symtab", cp, section); + palSymtab->unserialize("pal_symtab", cp, section); +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaSystem) + + Param<Tick> boot_cpu_frequency; + SimObjectParam<PhysicalMemory *> physmem; + SimpleEnumParam<System::MemoryMode> mem_mode; + + Param<std::string> kernel; + Param<std::string> console; + Param<std::string> pal; + + Param<std::string> boot_osflags; + Param<std::string> readfile; + Param<std::string> symbolfile; + Param<unsigned int> init_param; + + Param<uint64_t> system_type; + Param<uint64_t> system_rev; + +END_DECLARE_SIM_OBJECT_PARAMS(AlphaSystem) + +BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaSystem) + + INIT_PARAM(boot_cpu_frequency, "Frequency of the boot CPU"), + INIT_PARAM(physmem, "phsyical memory"), + INIT_ENUM_PARAM(mem_mode, "Memory Mode, (1=atomic, 2=timing)", + System::MemoryModeStrings), + INIT_PARAM(kernel, "file that contains the kernel code"), + INIT_PARAM(console, "file that contains the console code"), + INIT_PARAM(pal, "file that contains palcode"), + INIT_PARAM_DFLT(boot_osflags, "flags to pass to the kernel during boot", + "a"), + INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), + INIT_PARAM_DFLT(symbolfile, "file to read symbols from", ""), + INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), + INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 34), + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10) + +END_INIT_SIM_OBJECT_PARAMS(AlphaSystem) + +CREATE_SIM_OBJECT(AlphaSystem) +{ + AlphaSystem::Params *p = new AlphaSystem::Params; + p->name = getInstanceName(); + p->boot_cpu_frequency = boot_cpu_frequency; + p->physmem = physmem; + p->mem_mode = mem_mode; + p->kernel_path = kernel; + p->console_path = console; + p->palcode = pal; + p->boot_osflags = boot_osflags; + p->init_param = init_param; + p->readfile = readfile; + p->symbolfile = symbolfile; + p->system_type = system_type; + p->system_rev = system_rev; + return new AlphaSystem(p); +} + +REGISTER_SIM_OBJECT("AlphaSystem", AlphaSystem) + + diff --git a/src/arch/alpha/system.hh b/src/arch/alpha/system.hh new file mode 100644 index 000000000..0c073a68c --- /dev/null +++ b/src/arch/alpha/system.hh @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Nathan Binkert + */ + +#ifndef __ARCH_ALPHA_SYSTEM_HH__ +#define __ARCH_ALPHA_SYSTEM_HH__ + +#include <string> +#include <vector> + +#include "sim/system.hh" +#include "base/loader/symtab.hh" +#include "cpu/pc_event.hh" +#include "kern/system_events.hh" +#include "sim/sim_object.hh" + +class AlphaSystem : public System +{ + public: + struct Params : public System::Params + { + std::string console_path; + std::string palcode; + uint64_t system_type; + uint64_t system_rev; + }; + + AlphaSystem(Params *p); + + ~AlphaSystem(); + + virtual bool breakpoint(); + +/** + * Serialization stuff + */ + public: + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + /** + * Set the m5AlphaAccess pointer in the console + */ + void setAlphaAccess(Addr access); + + /** console symbol table */ + SymbolTable *consoleSymtab; + + /** pal symbol table */ + SymbolTable *palSymtab; + + /** Object pointer for the console code */ + ObjectFile *console; + + /** Object pointer for the PAL code */ + ObjectFile *pal; + +#ifndef NDEBUG + /** Event to halt the simulator if the console calls panic() */ + BreakPCEvent *consolePanicEvent; +#endif + protected: + const Params *params() const { return (const Params *)_params; } + + /** Add a function-based event to PALcode. */ + template <class T> + T *addPalFuncEvent(const char *lbl) + { + return addFuncEvent<T>(palSymtab, lbl); + } + + /** Add a function-based event to the console code. */ + template <class T> + T *addConsoleFuncEvent(const char *lbl) + { + return addFuncEvent<T>(consoleSymtab, lbl); + } + + virtual Addr fixFuncEventAddr(Addr addr); + +}; + +#endif + diff --git a/src/arch/alpha/tlb.cc b/src/arch/alpha/tlb.cc new file mode 100644 index 000000000..bab44c434 --- /dev/null +++ b/src/arch/alpha/tlb.cc @@ -0,0 +1,634 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + * Andrew Schultz + */ + +#include <string> +#include <vector> + +#include "arch/alpha/pagetable.hh" +#include "arch/alpha/tlb.hh" +#include "arch/alpha/faults.hh" +#include "base/inifile.hh" +#include "base/str.hh" +#include "base/trace.hh" +#include "config/alpha_tlaser.hh" +#include "cpu/thread_context.hh" +#include "sim/builder.hh" + +using namespace std; +using namespace EV5; + +/////////////////////////////////////////////////////////////////////// +// +// Alpha TLB +// +#ifdef DEBUG +bool uncacheBit39 = false; +bool uncacheBit40 = false; +#endif + +#define MODE2MASK(X) (1 << (X)) + +AlphaTLB::AlphaTLB(const string &name, int s) + : SimObject(name), size(s), nlu(0) +{ + table = new AlphaISA::PTE[size]; + memset(table, 0, sizeof(AlphaISA::PTE[size])); +} + +AlphaTLB::~AlphaTLB() +{ + if (table) + delete [] table; +} + +// look up an entry in the TLB +AlphaISA::PTE * +AlphaTLB::lookup(Addr vpn, uint8_t asn) const +{ + // assume not found... + AlphaISA::PTE *retval = NULL; + + PageTable::const_iterator i = lookupTable.find(vpn); + if (i != lookupTable.end()) { + while (i->first == vpn) { + int index = i->second; + AlphaISA::PTE *pte = &table[index]; + assert(pte->valid); + if (vpn == pte->tag && (pte->asma || pte->asn == asn)) { + retval = pte; + break; + } + + ++i; + } + } + + DPRINTF(TLB, "lookup %#x, asn %#x -> %s ppn %#x\n", vpn, (int)asn, + retval ? "hit" : "miss", retval ? retval->ppn : 0); + return retval; +} + + +Fault +AlphaTLB::checkCacheability(RequestPtr &req) +{ + // in Alpha, cacheability is controlled by upper-level bits of the + // physical address + + /* + * We support having the uncacheable bit in either bit 39 or bit 40. + * The Turbolaser platform (and EV5) support having the bit in 39, but + * Tsunami (which Linux assumes uses an EV6) generates accesses with + * the bit in 40. So we must check for both, but we have debug flags + * to catch a weird case where both are used, which shouldn't happen. + */ + + +#if ALPHA_TLASER + if (req->getPaddr() & PAddrUncachedBit39) { +#else + if (req->getPaddr() & PAddrUncachedBit43) { +#endif + // IPR memory space not implemented + if (PAddrIprSpace(req->getPaddr())) { + return new UnimpFault("IPR memory space not implemented!"); + } else { + // mark request as uncacheable + req->setFlags(req->getFlags() | UNCACHEABLE); + +#if !ALPHA_TLASER + // Clear bits 42:35 of the physical address (10-2 in Tsunami manual) + req->setPaddr(req->getPaddr() & PAddrUncachedMask); +#endif + } + } + return NoFault; +} + + +// insert a new TLB entry +void +AlphaTLB::insert(Addr addr, AlphaISA::PTE &pte) +{ + AlphaISA::VAddr vaddr = addr; + if (table[nlu].valid) { + Addr oldvpn = table[nlu].tag; + PageTable::iterator i = lookupTable.find(oldvpn); + + if (i == lookupTable.end()) + panic("TLB entry not found in lookupTable"); + + int index; + while ((index = i->second) != nlu) { + if (table[index].tag != oldvpn) + panic("TLB entry not found in lookupTable"); + + ++i; + } + + DPRINTF(TLB, "remove @%d: %#x -> %#x\n", nlu, oldvpn, table[nlu].ppn); + + lookupTable.erase(i); + } + + DPRINTF(TLB, "insert @%d: %#x -> %#x\n", nlu, vaddr.vpn(), pte.ppn); + + table[nlu] = pte; + table[nlu].tag = vaddr.vpn(); + table[nlu].valid = true; + + lookupTable.insert(make_pair(vaddr.vpn(), nlu)); + nextnlu(); +} + +void +AlphaTLB::flushAll() +{ + DPRINTF(TLB, "flushAll\n"); + memset(table, 0, sizeof(AlphaISA::PTE[size])); + lookupTable.clear(); + nlu = 0; +} + +void +AlphaTLB::flushProcesses() +{ + PageTable::iterator i = lookupTable.begin(); + PageTable::iterator end = lookupTable.end(); + while (i != end) { + int index = i->second; + AlphaISA::PTE *pte = &table[index]; + assert(pte->valid); + + // we can't increment i after we erase it, so save a copy and + // increment it to get the next entry now + PageTable::iterator cur = i; + ++i; + + if (!pte->asma) { + DPRINTF(TLB, "flush @%d: %#x -> %#x\n", index, pte->tag, pte->ppn); + pte->valid = false; + lookupTable.erase(cur); + } + } +} + +void +AlphaTLB::flushAddr(Addr addr, uint8_t asn) +{ + AlphaISA::VAddr vaddr = addr; + + PageTable::iterator i = lookupTable.find(vaddr.vpn()); + if (i == lookupTable.end()) + return; + + while (i->first == vaddr.vpn()) { + int index = i->second; + AlphaISA::PTE *pte = &table[index]; + assert(pte->valid); + + if (vaddr.vpn() == pte->tag && (pte->asma || pte->asn == asn)) { + DPRINTF(TLB, "flushaddr @%d: %#x -> %#x\n", index, vaddr.vpn(), + pte->ppn); + + // invalidate this entry + pte->valid = false; + + lookupTable.erase(i); + } + + ++i; + } +} + + +void +AlphaTLB::serialize(ostream &os) +{ + SERIALIZE_SCALAR(size); + SERIALIZE_SCALAR(nlu); + + for (int i = 0; i < size; i++) { + nameOut(os, csprintf("%s.PTE%d", name(), i)); + table[i].serialize(os); + } +} + +void +AlphaTLB::unserialize(Checkpoint *cp, const string §ion) +{ + UNSERIALIZE_SCALAR(size); + UNSERIALIZE_SCALAR(nlu); + + for (int i = 0; i < size; i++) { + table[i].unserialize(cp, csprintf("%s.PTE%d", section, i)); + if (table[i].valid) { + lookupTable.insert(make_pair(table[i].tag, i)); + } + } +} + + +/////////////////////////////////////////////////////////////////////// +// +// Alpha ITB +// +AlphaITB::AlphaITB(const std::string &name, int size) + : AlphaTLB(name, size) +{} + + +void +AlphaITB::regStats() +{ + hits + .name(name() + ".hits") + .desc("ITB hits"); + misses + .name(name() + ".misses") + .desc("ITB misses"); + acv + .name(name() + ".acv") + .desc("ITB acv"); + accesses + .name(name() + ".accesses") + .desc("ITB accesses"); + + accesses = hits + misses; +} + + +Fault +AlphaITB::translate(RequestPtr &req, ThreadContext *tc) const +{ + if (AlphaISA::PcPAL(req->getVaddr())) { + // strip off PAL PC marker (lsb is 1) + req->setPaddr((req->getVaddr() & ~3) & PAddrImplMask); + hits++; + return NoFault; + } + + if (req->getFlags() & PHYSICAL) { + req->setPaddr(req->getVaddr()); + } else { + // verify that this is a good virtual address + if (!validVirtualAddress(req->getVaddr())) { + acv++; + return new ItbAcvFault(req->getVaddr()); + } + + + // VA<42:41> == 2, VA<39:13> maps directly to PA<39:13> for EV5 + // VA<47:41> == 0x7e, VA<40:13> maps directly to PA<40:13> for EV6 +#if ALPHA_TLASER + if ((MCSR_SP(tc->readMiscReg(AlphaISA::IPR_MCSR)) & 2) && + VAddrSpaceEV5(req->getVaddr()) == 2) { +#else + if (VAddrSpaceEV6(req->getVaddr()) == 0x7e) { +#endif + // only valid in kernel mode + if (ICM_CM(tc->readMiscReg(AlphaISA::IPR_ICM)) != + AlphaISA::mode_kernel) { + acv++; + return new ItbAcvFault(req->getVaddr()); + } + + req->setPaddr(req->getVaddr() & PAddrImplMask); + +#if !ALPHA_TLASER + // sign extend the physical address properly + if (req->getPaddr() & PAddrUncachedBit40) + req->setPaddr(req->getPaddr() | ULL(0xf0000000000)); + else + req->setPaddr(req->getPaddr() & ULL(0xffffffffff)); +#endif + + } else { + // not a physical address: need to look up pte + int asn = DTB_ASN_ASN(tc->readMiscReg(AlphaISA::IPR_DTB_ASN)); + AlphaISA::PTE *pte = lookup(AlphaISA::VAddr(req->getVaddr()).vpn(), + asn); + + if (!pte) { + misses++; + return new ItbPageFault(req->getVaddr()); + } + + req->setPaddr((pte->ppn << AlphaISA::PageShift) + + (AlphaISA::VAddr(req->getVaddr()).offset() + & ~3)); + + // check permissions for this access + if (!(pte->xre & + (1 << ICM_CM(tc->readMiscReg(AlphaISA::IPR_ICM))))) { + // instruction access fault + acv++; + return new ItbAcvFault(req->getVaddr()); + } + + hits++; + } + } + + // check that the physical address is ok (catch bad physical addresses) + if (req->getPaddr() & ~PAddrImplMask) + return genMachineCheckFault(); + + return checkCacheability(req); + +} + +/////////////////////////////////////////////////////////////////////// +// +// Alpha DTB +// +AlphaDTB::AlphaDTB(const std::string &name, int size) + : AlphaTLB(name, size) +{} + +void +AlphaDTB::regStats() +{ + read_hits + .name(name() + ".read_hits") + .desc("DTB read hits") + ; + + read_misses + .name(name() + ".read_misses") + .desc("DTB read misses") + ; + + read_acv + .name(name() + ".read_acv") + .desc("DTB read access violations") + ; + + read_accesses + .name(name() + ".read_accesses") + .desc("DTB read accesses") + ; + + write_hits + .name(name() + ".write_hits") + .desc("DTB write hits") + ; + + write_misses + .name(name() + ".write_misses") + .desc("DTB write misses") + ; + + write_acv + .name(name() + ".write_acv") + .desc("DTB write access violations") + ; + + write_accesses + .name(name() + ".write_accesses") + .desc("DTB write accesses") + ; + + hits + .name(name() + ".hits") + .desc("DTB hits") + ; + + misses + .name(name() + ".misses") + .desc("DTB misses") + ; + + acv + .name(name() + ".acv") + .desc("DTB access violations") + ; + + accesses + .name(name() + ".accesses") + .desc("DTB accesses") + ; + + hits = read_hits + write_hits; + misses = read_misses + write_misses; + acv = read_acv + write_acv; + accesses = read_accesses + write_accesses; +} + +Fault +AlphaDTB::translate(RequestPtr &req, ThreadContext *tc, bool write) const +{ + Addr pc = tc->readPC(); + + AlphaISA::mode_type mode = + (AlphaISA::mode_type)DTB_CM_CM(tc->readMiscReg(AlphaISA::IPR_DTB_CM)); + + + /** + * Check for alignment faults + */ + if (req->getVaddr() & (req->getSize() - 1)) { + DPRINTF(TLB, "Alignment Fault on %#x, size = %d", req->getVaddr(), + req->getSize()); + uint64_t flags = write ? MM_STAT_WR_MASK : 0; + return new DtbAlignmentFault(req->getVaddr(), req->getFlags(), flags); + } + + if (pc & 0x1) { + mode = (req->getFlags() & ALTMODE) ? + (AlphaISA::mode_type)ALT_MODE_AM( + tc->readMiscReg(AlphaISA::IPR_ALT_MODE)) + : AlphaISA::mode_kernel; + } + + if (req->getFlags() & PHYSICAL) { + req->setPaddr(req->getVaddr()); + } else { + // verify that this is a good virtual address + if (!validVirtualAddress(req->getVaddr())) { + if (write) { write_acv++; } else { read_acv++; } + uint64_t flags = (write ? MM_STAT_WR_MASK : 0) | + MM_STAT_BAD_VA_MASK | + MM_STAT_ACV_MASK; + return new DtbPageFault(req->getVaddr(), req->getFlags(), flags); + } + + // Check for "superpage" mapping +#if ALPHA_TLASER + if ((MCSR_SP(tc->readMiscReg(AlphaISA::IPR_MCSR)) & 2) && + VAddrSpaceEV5(req->getVaddr()) == 2) { +#else + if (VAddrSpaceEV6(req->getVaddr()) == 0x7e) { +#endif + + // only valid in kernel mode + if (DTB_CM_CM(tc->readMiscReg(AlphaISA::IPR_DTB_CM)) != + AlphaISA::mode_kernel) { + if (write) { write_acv++; } else { read_acv++; } + uint64_t flags = ((write ? MM_STAT_WR_MASK : 0) | + MM_STAT_ACV_MASK); + return new DtbAcvFault(req->getVaddr(), req->getFlags(), flags); + } + + req->setPaddr(req->getVaddr() & PAddrImplMask); + +#if !ALPHA_TLASER + // sign extend the physical address properly + if (req->getPaddr() & PAddrUncachedBit40) + req->setPaddr(req->getPaddr() | ULL(0xf0000000000)); + else + req->setPaddr(req->getPaddr() & ULL(0xffffffffff)); +#endif + + } else { + if (write) + write_accesses++; + else + read_accesses++; + + int asn = DTB_ASN_ASN(tc->readMiscReg(AlphaISA::IPR_DTB_ASN)); + + // not a physical address: need to look up pte + AlphaISA::PTE *pte = lookup(AlphaISA::VAddr(req->getVaddr()).vpn(), + asn); + + if (!pte) { + // page fault + if (write) { write_misses++; } else { read_misses++; } + uint64_t flags = (write ? MM_STAT_WR_MASK : 0) | + MM_STAT_DTB_MISS_MASK; + return (req->getFlags() & VPTE) ? + (Fault)(new PDtbMissFault(req->getVaddr(), req->getFlags(), + flags)) : + (Fault)(new NDtbMissFault(req->getVaddr(), req->getFlags(), + flags)); + } + + req->setPaddr((pte->ppn << AlphaISA::PageShift) + + AlphaISA::VAddr(req->getVaddr()).offset()); + + if (write) { + if (!(pte->xwe & MODE2MASK(mode))) { + // declare the instruction access fault + write_acv++; + uint64_t flags = MM_STAT_WR_MASK | + MM_STAT_ACV_MASK | + (pte->fonw ? MM_STAT_FONW_MASK : 0); + return new DtbPageFault(req->getVaddr(), req->getFlags(), flags); + } + if (pte->fonw) { + write_acv++; + uint64_t flags = MM_STAT_WR_MASK | + MM_STAT_FONW_MASK; + return new DtbPageFault(req->getVaddr(), req->getFlags(), flags); + } + } else { + if (!(pte->xre & MODE2MASK(mode))) { + read_acv++; + uint64_t flags = MM_STAT_ACV_MASK | + (pte->fonr ? MM_STAT_FONR_MASK : 0); + return new DtbAcvFault(req->getVaddr(), req->getFlags(), flags); + } + if (pte->fonr) { + read_acv++; + uint64_t flags = MM_STAT_FONR_MASK; + return new DtbPageFault(req->getVaddr(), req->getFlags(), flags); + } + } + } + + if (write) + write_hits++; + else + read_hits++; + } + + // check that the physical address is ok (catch bad physical addresses) + if (req->getPaddr() & ~PAddrImplMask) + return genMachineCheckFault(); + + return checkCacheability(req); +} + +AlphaISA::PTE & +AlphaTLB::index(bool advance) +{ + AlphaISA::PTE *pte = &table[nlu]; + + if (advance) + nextnlu(); + + return *pte; +} + +DEFINE_SIM_OBJECT_CLASS_NAME("AlphaTLB", AlphaTLB) + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaITB) + + Param<int> size; + +END_DECLARE_SIM_OBJECT_PARAMS(AlphaITB) + +BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaITB) + + INIT_PARAM_DFLT(size, "TLB size", 48) + +END_INIT_SIM_OBJECT_PARAMS(AlphaITB) + + +CREATE_SIM_OBJECT(AlphaITB) +{ + return new AlphaITB(getInstanceName(), size); +} + +REGISTER_SIM_OBJECT("AlphaITB", AlphaITB) + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaDTB) + + Param<int> size; + +END_DECLARE_SIM_OBJECT_PARAMS(AlphaDTB) + +BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaDTB) + + INIT_PARAM_DFLT(size, "TLB size", 64) + +END_INIT_SIM_OBJECT_PARAMS(AlphaDTB) + + +CREATE_SIM_OBJECT(AlphaDTB) +{ + return new AlphaDTB(getInstanceName(), size); +} + +REGISTER_SIM_OBJECT("AlphaDTB", AlphaDTB) + diff --git a/src/arch/alpha/tlb.hh b/src/arch/alpha/tlb.hh new file mode 100644 index 000000000..955460649 --- /dev/null +++ b/src/arch/alpha/tlb.hh @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __ALPHA_MEMORY_HH__ +#define __ALPHA_MEMORY_HH__ + +#include <map> + +#include "arch/alpha/ev5.hh" +#include "arch/alpha/isa_traits.hh" +#include "arch/alpha/utility.hh" +#include "arch/alpha/vtophys.hh" +#include "base/statistics.hh" +#include "mem/request.hh" +#include "sim/faults.hh" +#include "sim/sim_object.hh" + +class ThreadContext; + +class AlphaTLB : public SimObject +{ + protected: + typedef std::multimap<Addr, int> PageTable; + PageTable lookupTable; // Quick lookup into page table + + AlphaISA::PTE *table; // the Page Table + int size; // TLB Size + int nlu; // not last used entry (for replacement) + + void nextnlu() { if (++nlu >= size) nlu = 0; } + AlphaISA::PTE *lookup(Addr vpn, uint8_t asn) const; + + public: + AlphaTLB(const std::string &name, int size); + virtual ~AlphaTLB(); + + int getsize() const { return size; } + + AlphaISA::PTE &index(bool advance = true); + void insert(Addr vaddr, AlphaISA::PTE &pte); + + void flushAll(); + void flushProcesses(); + void flushAddr(Addr addr, uint8_t asn); + + // static helper functions... really EV5 VM traits + static bool validVirtualAddress(Addr vaddr) { + // unimplemented bits must be all 0 or all 1 + Addr unimplBits = vaddr & EV5::VAddrUnImplMask; + return (unimplBits == 0) || (unimplBits == EV5::VAddrUnImplMask); + } + + static Fault checkCacheability(RequestPtr &req); + + // Checkpointing + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +class AlphaITB : public AlphaTLB +{ + protected: + mutable Stats::Scalar<> hits; + mutable Stats::Scalar<> misses; + mutable Stats::Scalar<> acv; + mutable Stats::Formula accesses; + + public: + AlphaITB(const std::string &name, int size); + virtual void regStats(); + + Fault translate(RequestPtr &req, ThreadContext *tc) const; +}; + +class AlphaDTB : public AlphaTLB +{ + protected: + mutable Stats::Scalar<> read_hits; + mutable Stats::Scalar<> read_misses; + mutable Stats::Scalar<> read_acv; + mutable Stats::Scalar<> read_accesses; + mutable Stats::Scalar<> write_hits; + mutable Stats::Scalar<> write_misses; + mutable Stats::Scalar<> write_acv; + mutable Stats::Scalar<> write_accesses; + Stats::Formula hits; + Stats::Formula misses; + Stats::Formula acv; + Stats::Formula accesses; + + public: + AlphaDTB(const std::string &name, int size); + virtual void regStats(); + + Fault translate(RequestPtr &req, ThreadContext *tc, bool write) const; +}; + +#endif // __ALPHA_MEMORY_HH__ diff --git a/src/arch/alpha/tru64/process.cc b/src/arch/alpha/tru64/process.cc new file mode 100644 index 000000000..55b8ebf8b --- /dev/null +++ b/src/arch/alpha/tru64/process.cc @@ -0,0 +1,592 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Ali Saidi + */ + +#include "arch/alpha/tru64/tru64.hh" +#include "arch/alpha/isa_traits.hh" +#include "arch/alpha/tru64/process.hh" + +#include "cpu/thread_context.hh" +#include "kern/tru64/tru64.hh" + +#include "sim/process.hh" +#include "sim/syscall_emul.hh" + +using namespace std; +using namespace AlphaISA; + +/// Target uname() handler. +static SyscallReturn +unameFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + TypedBufferArg<AlphaTru64::utsname> name(tc->getSyscallArg(0)); + + strcpy(name->sysname, "OSF1"); + strcpy(name->nodename, "m5.eecs.umich.edu"); + strcpy(name->release, "V5.1"); + strcpy(name->version, "732"); + strcpy(name->machine, "alpha"); + + name.copyOut(tc->getMemPort()); + return 0; +} + +/// Target getsysyinfo() handler. +static SyscallReturn +getsysinfoFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + unsigned op = tc->getSyscallArg(0); + unsigned nbytes = tc->getSyscallArg(2); + + switch (op) { + + case AlphaTru64::GSI_MAX_CPU: { + TypedBufferArg<uint32_t> max_cpu(tc->getSyscallArg(1)); + *max_cpu = htog((uint32_t)process->numCpus()); + max_cpu.copyOut(tc->getMemPort()); + return 1; + } + + case AlphaTru64::GSI_CPUS_IN_BOX: { + TypedBufferArg<uint32_t> cpus_in_box(tc->getSyscallArg(1)); + *cpus_in_box = htog((uint32_t)process->numCpus()); + cpus_in_box.copyOut(tc->getMemPort()); + return 1; + } + + case AlphaTru64::GSI_PHYSMEM: { + TypedBufferArg<uint64_t> physmem(tc->getSyscallArg(1)); + *physmem = htog((uint64_t)1024 * 1024); // physical memory in KB + physmem.copyOut(tc->getMemPort()); + return 1; + } + + case AlphaTru64::GSI_CPU_INFO: { + TypedBufferArg<AlphaTru64::cpu_info> infop(tc->getSyscallArg(1)); + + infop->current_cpu = htog(0); + infop->cpus_in_box = htog(process->numCpus()); + infop->cpu_type = htog(57); + infop->ncpus = htog(process->numCpus()); + uint64_t cpumask = (1 << process->numCpus()) - 1; + infop->cpus_present = infop->cpus_running = htog(cpumask); + infop->cpu_binding = htog(0); + infop->cpu_ex_binding = htog(0); + infop->mhz = htog(667); + + infop.copyOut(tc->getMemPort()); + return 1; + } + + case AlphaTru64::GSI_PROC_TYPE: { + TypedBufferArg<uint64_t> proc_type(tc->getSyscallArg(1)); + *proc_type = htog((uint64_t)11); + proc_type.copyOut(tc->getMemPort()); + return 1; + } + + case AlphaTru64::GSI_PLATFORM_NAME: { + BufferArg bufArg(tc->getSyscallArg(1), nbytes); + strncpy((char *)bufArg.bufferPtr(), + "COMPAQ Professional Workstation XP1000", + nbytes); + bufArg.copyOut(tc->getMemPort()); + return 1; + } + + case AlphaTru64::GSI_CLK_TCK: { + TypedBufferArg<uint64_t> clk_hz(tc->getSyscallArg(1)); + *clk_hz = htog((uint64_t)1024); + clk_hz.copyOut(tc->getMemPort()); + return 1; + } + + default: + warn("getsysinfo: unknown op %d\n", op); + break; + } + + return 0; +} + +/// Target setsysyinfo() handler. +static SyscallReturn +setsysinfoFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + unsigned op = tc->getSyscallArg(0); + + switch (op) { + case AlphaTru64::SSI_IEEE_FP_CONTROL: + warn("setsysinfo: ignoring ieee_set_fp_control() arg 0x%x\n", + tc->getSyscallArg(1)); + break; + + default: + warn("setsysinfo: unknown op %d\n", op); + break; + } + + return 0; +} + + +/// Target table() handler. +static +SyscallReturn tableFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + using namespace std; + using namespace TheISA; + + int id = tc->getSyscallArg(0); // table ID + int index = tc->getSyscallArg(1); // index into table + // arg 2 is buffer pointer; type depends on table ID + int nel = tc->getSyscallArg(3); // number of elements + int lel = tc->getSyscallArg(4); // expected element size + + switch (id) { + case AlphaTru64::TBL_SYSINFO: { + if (index != 0 || nel != 1 || lel != sizeof(Tru64::tbl_sysinfo)) + return -EINVAL; + TypedBufferArg<Tru64::tbl_sysinfo> elp(tc->getSyscallArg(2)); + + const int clk_hz = one_million; + elp->si_user = htog(curTick / (Clock::Frequency / clk_hz)); + elp->si_nice = htog(0); + elp->si_sys = htog(0); + elp->si_idle = htog(0); + elp->wait = htog(0); + elp->si_hz = htog(clk_hz); + elp->si_phz = htog(clk_hz); + elp->si_boottime = htog(seconds_since_epoch); // seconds since epoch? + elp->si_max_procs = htog(process->numCpus()); + elp.copyOut(tc->getMemPort()); + return 0; + } + + default: + cerr << "table(): id " << id << " unknown." << endl; + return -EINVAL; + } +} + +SyscallDesc AlphaTru64Process::syscallDescs[] = { + /* 0 */ SyscallDesc("syscall (#0)", AlphaTru64::indirectSyscallFunc, + SyscallDesc::SuppressReturnValue), + /* 1 */ SyscallDesc("exit", exitFunc), + /* 2 */ SyscallDesc("fork", unimplementedFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), + /* 5 */ SyscallDesc("old_open", unimplementedFunc), + /* 6 */ SyscallDesc("close", closeFunc), + /* 7 */ SyscallDesc("wait4", unimplementedFunc), + /* 8 */ SyscallDesc("old_creat", unimplementedFunc), + /* 9 */ SyscallDesc("link", unimplementedFunc), + /* 10 */ SyscallDesc("unlink", unlinkFunc), + /* 11 */ SyscallDesc("execv", unimplementedFunc), + /* 12 */ SyscallDesc("chdir", unimplementedFunc), + /* 13 */ SyscallDesc("fchdir", unimplementedFunc), + /* 14 */ SyscallDesc("mknod", unimplementedFunc), + /* 15 */ SyscallDesc("chmod", unimplementedFunc), + /* 16 */ SyscallDesc("chown", unimplementedFunc), + /* 17 */ SyscallDesc("obreak", obreakFunc), + /* 18 */ SyscallDesc("pre_F64_getfsstat", unimplementedFunc), + /* 19 */ SyscallDesc("lseek", lseekFunc), + /* 20 */ SyscallDesc("getpid", getpidPseudoFunc), + /* 21 */ SyscallDesc("mount", unimplementedFunc), + /* 22 */ SyscallDesc("unmount", unimplementedFunc), + /* 23 */ SyscallDesc("setuid", setuidFunc), + /* 24 */ SyscallDesc("getuid", getuidPseudoFunc), + /* 25 */ SyscallDesc("exec_with_loader", unimplementedFunc), + /* 26 */ SyscallDesc("ptrace", unimplementedFunc), + /* 27 */ SyscallDesc("recvmsg", unimplementedFunc), + /* 28 */ SyscallDesc("sendmsg", unimplementedFunc), + /* 29 */ SyscallDesc("recvfrom", unimplementedFunc), + /* 30 */ SyscallDesc("accept", unimplementedFunc), + /* 31 */ SyscallDesc("getpeername", unimplementedFunc), + /* 32 */ SyscallDesc("getsockname", unimplementedFunc), + /* 33 */ SyscallDesc("access", unimplementedFunc), + /* 34 */ SyscallDesc("chflags", unimplementedFunc), + /* 35 */ SyscallDesc("fchflags", unimplementedFunc), + /* 36 */ SyscallDesc("sync", unimplementedFunc), + /* 37 */ SyscallDesc("kill", unimplementedFunc), + /* 38 */ SyscallDesc("old_stat", unimplementedFunc), + /* 39 */ SyscallDesc("setpgid", unimplementedFunc), + /* 40 */ SyscallDesc("old_lstat", unimplementedFunc), + /* 41 */ SyscallDesc("dup", unimplementedFunc), + /* 42 */ SyscallDesc("pipe", unimplementedFunc), + /* 43 */ SyscallDesc("set_program_attributes", unimplementedFunc), + /* 44 */ SyscallDesc("profil", unimplementedFunc), + /* 45 */ SyscallDesc("open", openFunc<AlphaTru64>), + /* 46 */ SyscallDesc("obsolete osigaction", unimplementedFunc), + /* 47 */ SyscallDesc("getgid", getgidPseudoFunc), + /* 48 */ SyscallDesc("sigprocmask", ignoreFunc), + /* 49 */ SyscallDesc("getlogin", unimplementedFunc), + /* 50 */ SyscallDesc("setlogin", unimplementedFunc), + /* 51 */ SyscallDesc("acct", unimplementedFunc), + /* 52 */ SyscallDesc("sigpending", unimplementedFunc), + /* 53 */ SyscallDesc("classcntl", unimplementedFunc), + /* 54 */ SyscallDesc("ioctl", ioctlFunc<AlphaTru64>), + /* 55 */ SyscallDesc("reboot", unimplementedFunc), + /* 56 */ SyscallDesc("revoke", unimplementedFunc), + /* 57 */ SyscallDesc("symlink", unimplementedFunc), + /* 58 */ SyscallDesc("readlink", unimplementedFunc), + /* 59 */ SyscallDesc("execve", unimplementedFunc), + /* 60 */ SyscallDesc("umask", unimplementedFunc), + /* 61 */ SyscallDesc("chroot", unimplementedFunc), + /* 62 */ SyscallDesc("old_fstat", unimplementedFunc), + /* 63 */ SyscallDesc("getpgrp", unimplementedFunc), + /* 64 */ SyscallDesc("getpagesize", getpagesizeFunc), + /* 65 */ SyscallDesc("mremap", unimplementedFunc), + /* 66 */ SyscallDesc("vfork", unimplementedFunc), + /* 67 */ SyscallDesc("pre_F64_stat", statFunc<Tru64_PreF64>), + /* 68 */ SyscallDesc("pre_F64_lstat", lstatFunc<Tru64_PreF64>), + /* 69 */ SyscallDesc("sbrk", unimplementedFunc), + /* 70 */ SyscallDesc("sstk", unimplementedFunc), + /* 71 */ SyscallDesc("mmap", mmapFunc<AlphaTru64>), + /* 72 */ SyscallDesc("ovadvise", unimplementedFunc), + /* 73 */ SyscallDesc("munmap", munmapFunc), + /* 74 */ SyscallDesc("mprotect", ignoreFunc), + /* 75 */ SyscallDesc("madvise", unimplementedFunc), + /* 76 */ SyscallDesc("old_vhangup", unimplementedFunc), + /* 77 */ SyscallDesc("kmodcall", unimplementedFunc), + /* 78 */ SyscallDesc("mincore", unimplementedFunc), + /* 79 */ SyscallDesc("getgroups", unimplementedFunc), + /* 80 */ SyscallDesc("setgroups", unimplementedFunc), + /* 81 */ SyscallDesc("old_getpgrp", unimplementedFunc), + /* 82 */ SyscallDesc("setpgrp", unimplementedFunc), + /* 83 */ SyscallDesc("setitimer", unimplementedFunc), + /* 84 */ SyscallDesc("old_wait", unimplementedFunc), + /* 85 */ SyscallDesc("table", tableFunc), + /* 86 */ SyscallDesc("getitimer", unimplementedFunc), + /* 87 */ SyscallDesc("gethostname", gethostnameFunc), + /* 88 */ SyscallDesc("sethostname", unimplementedFunc), + /* 89 */ SyscallDesc("getdtablesize", unimplementedFunc), + /* 90 */ SyscallDesc("dup2", unimplementedFunc), + /* 91 */ SyscallDesc("pre_F64_fstat", fstatFunc<Tru64_PreF64>), + /* 92 */ SyscallDesc("fcntl", fcntlFunc), + /* 93 */ SyscallDesc("select", unimplementedFunc), + /* 94 */ SyscallDesc("poll", unimplementedFunc), + /* 95 */ SyscallDesc("fsync", unimplementedFunc), + /* 96 */ SyscallDesc("setpriority", unimplementedFunc), + /* 97 */ SyscallDesc("socket", unimplementedFunc), + /* 98 */ SyscallDesc("connect", unimplementedFunc), + /* 99 */ SyscallDesc("old_accept", unimplementedFunc), + /* 100 */ SyscallDesc("getpriority", unimplementedFunc), + /* 101 */ SyscallDesc("old_send", unimplementedFunc), + /* 102 */ SyscallDesc("old_recv", unimplementedFunc), + /* 103 */ SyscallDesc("sigreturn", AlphaTru64::sigreturnFunc, + SyscallDesc::SuppressReturnValue), + /* 104 */ SyscallDesc("bind", unimplementedFunc), + /* 105 */ SyscallDesc("setsockopt", unimplementedFunc), + /* 106 */ SyscallDesc("listen", unimplementedFunc), + /* 107 */ SyscallDesc("plock", unimplementedFunc), + /* 108 */ SyscallDesc("old_sigvec", unimplementedFunc), + /* 109 */ SyscallDesc("old_sigblock", unimplementedFunc), + /* 110 */ SyscallDesc("old_sigsetmask", unimplementedFunc), + /* 111 */ SyscallDesc("sigsuspend", unimplementedFunc), + /* 112 */ SyscallDesc("sigstack", ignoreFunc), + /* 113 */ SyscallDesc("old_recvmsg", unimplementedFunc), + /* 114 */ SyscallDesc("old_sendmsg", unimplementedFunc), + /* 115 */ SyscallDesc("obsolete vtrace", unimplementedFunc), + /* 116 */ SyscallDesc("gettimeofday", gettimeofdayFunc<AlphaTru64>), + /* 117 */ SyscallDesc("getrusage", getrusageFunc<AlphaTru64>), + /* 118 */ SyscallDesc("getsockopt", unimplementedFunc), + /* 119 */ SyscallDesc("numa_syscalls", unimplementedFunc), + /* 120 */ SyscallDesc("readv", unimplementedFunc), + /* 121 */ SyscallDesc("writev", unimplementedFunc), + /* 122 */ SyscallDesc("settimeofday", unimplementedFunc), + /* 123 */ SyscallDesc("fchown", unimplementedFunc), + /* 124 */ SyscallDesc("fchmod", unimplementedFunc), + /* 125 */ SyscallDesc("old_recvfrom", unimplementedFunc), + /* 126 */ SyscallDesc("setreuid", unimplementedFunc), + /* 127 */ SyscallDesc("setregid", unimplementedFunc), + /* 128 */ SyscallDesc("rename", renameFunc), + /* 129 */ SyscallDesc("truncate", truncateFunc), + /* 130 */ SyscallDesc("ftruncate", ftruncateFunc), + /* 131 */ SyscallDesc("flock", unimplementedFunc), + /* 132 */ SyscallDesc("setgid", unimplementedFunc), + /* 133 */ SyscallDesc("sendto", unimplementedFunc), + /* 134 */ SyscallDesc("shutdown", unimplementedFunc), + /* 135 */ SyscallDesc("socketpair", unimplementedFunc), + /* 136 */ SyscallDesc("mkdir", unimplementedFunc), + /* 137 */ SyscallDesc("rmdir", unimplementedFunc), + /* 138 */ SyscallDesc("utimes", unimplementedFunc), + /* 139 */ SyscallDesc("obsolete 4.2 sigreturn", unimplementedFunc), + /* 140 */ SyscallDesc("adjtime", unimplementedFunc), + /* 141 */ SyscallDesc("old_getpeername", unimplementedFunc), + /* 142 */ SyscallDesc("gethostid", unimplementedFunc), + /* 143 */ SyscallDesc("sethostid", unimplementedFunc), + /* 144 */ SyscallDesc("getrlimit", getrlimitFunc<AlphaTru64>), + /* 145 */ SyscallDesc("setrlimit", ignoreFunc), + /* 146 */ SyscallDesc("old_killpg", unimplementedFunc), + /* 147 */ SyscallDesc("setsid", unimplementedFunc), + /* 148 */ SyscallDesc("quotactl", unimplementedFunc), + /* 149 */ SyscallDesc("oldquota", unimplementedFunc), + /* 150 */ SyscallDesc("old_getsockname", unimplementedFunc), + /* 151 */ SyscallDesc("pread", unimplementedFunc), + /* 152 */ SyscallDesc("pwrite", unimplementedFunc), + /* 153 */ SyscallDesc("pid_block", unimplementedFunc), + /* 154 */ SyscallDesc("pid_unblock", unimplementedFunc), + /* 155 */ SyscallDesc("signal_urti", unimplementedFunc), + /* 156 */ SyscallDesc("sigaction", ignoreFunc), + /* 157 */ SyscallDesc("sigwaitprim", unimplementedFunc), + /* 158 */ SyscallDesc("nfssvc", unimplementedFunc), + /* 159 */ SyscallDesc("getdirentries", AlphaTru64::getdirentriesFunc), + /* 160 */ SyscallDesc("pre_F64_statfs", statfsFunc<Tru64_PreF64>), + /* 161 */ SyscallDesc("pre_F64_fstatfs", fstatfsFunc<Tru64_PreF64>), + /* 162 */ SyscallDesc("unknown #162", unimplementedFunc), + /* 163 */ SyscallDesc("async_daemon", unimplementedFunc), + /* 164 */ SyscallDesc("getfh", unimplementedFunc), + /* 165 */ SyscallDesc("getdomainname", unimplementedFunc), + /* 166 */ SyscallDesc("setdomainname", unimplementedFunc), + /* 167 */ SyscallDesc("unknown #167", unimplementedFunc), + /* 168 */ SyscallDesc("unknown #168", unimplementedFunc), + /* 169 */ SyscallDesc("exportfs", unimplementedFunc), + /* 170 */ SyscallDesc("unknown #170", unimplementedFunc), + /* 171 */ SyscallDesc("unknown #171", unimplementedFunc), + /* 172 */ SyscallDesc("unknown #172", unimplementedFunc), + /* 173 */ SyscallDesc("unknown #173", unimplementedFunc), + /* 174 */ SyscallDesc("unknown #174", unimplementedFunc), + /* 175 */ SyscallDesc("unknown #175", unimplementedFunc), + /* 176 */ SyscallDesc("unknown #176", unimplementedFunc), + /* 177 */ SyscallDesc("unknown #177", unimplementedFunc), + /* 178 */ SyscallDesc("unknown #178", unimplementedFunc), + /* 179 */ SyscallDesc("unknown #179", unimplementedFunc), + /* 180 */ SyscallDesc("unknown #180", unimplementedFunc), + /* 181 */ SyscallDesc("alt_plock", unimplementedFunc), + /* 182 */ SyscallDesc("unknown #182", unimplementedFunc), + /* 183 */ SyscallDesc("unknown #183", unimplementedFunc), + /* 184 */ SyscallDesc("getmnt", unimplementedFunc), + /* 185 */ SyscallDesc("unknown #185", unimplementedFunc), + /* 186 */ SyscallDesc("unknown #186", unimplementedFunc), + /* 187 */ SyscallDesc("alt_sigpending", unimplementedFunc), + /* 188 */ SyscallDesc("alt_setsid", unimplementedFunc), + /* 189 */ SyscallDesc("unknown #189", unimplementedFunc), + /* 190 */ SyscallDesc("unknown #190", unimplementedFunc), + /* 191 */ SyscallDesc("unknown #191", unimplementedFunc), + /* 192 */ SyscallDesc("unknown #192", unimplementedFunc), + /* 193 */ SyscallDesc("unknown #193", unimplementedFunc), + /* 194 */ SyscallDesc("unknown #194", unimplementedFunc), + /* 195 */ SyscallDesc("unknown #195", unimplementedFunc), + /* 196 */ SyscallDesc("unknown #196", unimplementedFunc), + /* 197 */ SyscallDesc("unknown #197", unimplementedFunc), + /* 198 */ SyscallDesc("unknown #198", unimplementedFunc), + /* 199 */ SyscallDesc("swapon", unimplementedFunc), + /* 200 */ SyscallDesc("msgctl", unimplementedFunc), + /* 201 */ SyscallDesc("msgget", unimplementedFunc), + /* 202 */ SyscallDesc("msgrcv", unimplementedFunc), + /* 203 */ SyscallDesc("msgsnd", unimplementedFunc), + /* 204 */ SyscallDesc("semctl", unimplementedFunc), + /* 205 */ SyscallDesc("semget", unimplementedFunc), + /* 206 */ SyscallDesc("semop", unimplementedFunc), + /* 207 */ SyscallDesc("uname", unameFunc), + /* 208 */ SyscallDesc("lchown", unimplementedFunc), + /* 209 */ SyscallDesc("shmat", unimplementedFunc), + /* 210 */ SyscallDesc("shmctl", unimplementedFunc), + /* 211 */ SyscallDesc("shmdt", unimplementedFunc), + /* 212 */ SyscallDesc("shmget", unimplementedFunc), + /* 213 */ SyscallDesc("mvalid", unimplementedFunc), + /* 214 */ SyscallDesc("getaddressconf", unimplementedFunc), + /* 215 */ SyscallDesc("msleep", unimplementedFunc), + /* 216 */ SyscallDesc("mwakeup", unimplementedFunc), + /* 217 */ SyscallDesc("msync", unimplementedFunc), + /* 218 */ SyscallDesc("signal", unimplementedFunc), + /* 219 */ SyscallDesc("utc_gettime", unimplementedFunc), + /* 220 */ SyscallDesc("utc_adjtime", unimplementedFunc), + /* 221 */ SyscallDesc("unknown #221", unimplementedFunc), + /* 222 */ SyscallDesc("security", unimplementedFunc), + /* 223 */ SyscallDesc("kloadcall", unimplementedFunc), + /* 224 */ SyscallDesc("stat", statFunc<Tru64_F64>), + /* 225 */ SyscallDesc("lstat", lstatFunc<Tru64_F64>), + /* 226 */ SyscallDesc("fstat", fstatFunc<Tru64_F64>), + /* 227 */ SyscallDesc("statfs", statfsFunc<Tru64_F64>), + /* 228 */ SyscallDesc("fstatfs", fstatfsFunc<Tru64_F64>), + /* 229 */ SyscallDesc("getfsstat", unimplementedFunc), + /* 230 */ SyscallDesc("gettimeofday64", unimplementedFunc), + /* 231 */ SyscallDesc("settimeofday64", unimplementedFunc), + /* 232 */ SyscallDesc("unknown #232", unimplementedFunc), + /* 233 */ SyscallDesc("getpgid", unimplementedFunc), + /* 234 */ SyscallDesc("getsid", unimplementedFunc), + /* 235 */ SyscallDesc("sigaltstack", ignoreFunc), + /* 236 */ SyscallDesc("waitid", unimplementedFunc), + /* 237 */ SyscallDesc("priocntlset", unimplementedFunc), + /* 238 */ SyscallDesc("sigsendset", unimplementedFunc), + /* 239 */ SyscallDesc("set_speculative", unimplementedFunc), + /* 240 */ SyscallDesc("msfs_syscall", unimplementedFunc), + /* 241 */ SyscallDesc("sysinfo", unimplementedFunc), + /* 242 */ SyscallDesc("uadmin", unimplementedFunc), + /* 243 */ SyscallDesc("fuser", unimplementedFunc), + /* 244 */ SyscallDesc("proplist_syscall", unimplementedFunc), + /* 245 */ SyscallDesc("ntp_adjtime", unimplementedFunc), + /* 246 */ SyscallDesc("ntp_gettime", unimplementedFunc), + /* 247 */ SyscallDesc("pathconf", unimplementedFunc), + /* 248 */ SyscallDesc("fpathconf", unimplementedFunc), + /* 249 */ SyscallDesc("sync2", unimplementedFunc), + /* 250 */ SyscallDesc("uswitch", unimplementedFunc), + /* 251 */ SyscallDesc("usleep_thread", unimplementedFunc), + /* 252 */ SyscallDesc("audcntl", unimplementedFunc), + /* 253 */ SyscallDesc("audgen", unimplementedFunc), + /* 254 */ SyscallDesc("sysfs", unimplementedFunc), + /* 255 */ SyscallDesc("subsys_info", unimplementedFunc), + /* 256 */ SyscallDesc("getsysinfo", getsysinfoFunc), + /* 257 */ SyscallDesc("setsysinfo", setsysinfoFunc), + /* 258 */ SyscallDesc("afs_syscall", unimplementedFunc), + /* 259 */ SyscallDesc("swapctl", unimplementedFunc), + /* 260 */ SyscallDesc("memcntl", unimplementedFunc), + /* 261 */ SyscallDesc("fdatasync", unimplementedFunc), + /* 262 */ SyscallDesc("oflock", unimplementedFunc), + /* 263 */ SyscallDesc("F64_readv", unimplementedFunc), + /* 264 */ SyscallDesc("F64_writev", unimplementedFunc), + /* 265 */ SyscallDesc("cdslxlate", unimplementedFunc), + /* 266 */ SyscallDesc("sendfile", unimplementedFunc), +}; + + + +SyscallDesc AlphaTru64Process::machSyscallDescs[] = { + /* 0 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 1 */ SyscallDesc("m5_mutex_lock", AlphaTru64::m5_mutex_lockFunc), + /* 2 */ SyscallDesc("m5_mutex_trylock", AlphaTru64::m5_mutex_trylockFunc), + /* 3 */ SyscallDesc("m5_mutex_unlock", AlphaTru64::m5_mutex_unlockFunc), + /* 4 */ SyscallDesc("m5_cond_signal", AlphaTru64::m5_cond_signalFunc), + /* 5 */ SyscallDesc("m5_cond_broadcast", AlphaTru64::m5_cond_broadcastFunc), + /* 6 */ SyscallDesc("m5_cond_wait", AlphaTru64::m5_cond_waitFunc), + /* 7 */ SyscallDesc("m5_thread_exit", AlphaTru64::m5_thread_exitFunc), + /* 8 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 9 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 10 */ SyscallDesc("task_self", unimplementedFunc), + /* 11 */ SyscallDesc("thread_reply", unimplementedFunc), + /* 12 */ SyscallDesc("task_notify", unimplementedFunc), + /* 13 */ SyscallDesc("thread_self", unimplementedFunc), + /* 14 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 15 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 16 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 17 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 18 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 19 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 20 */ SyscallDesc("msg_send_trap", unimplementedFunc), + /* 21 */ SyscallDesc("msg_receive_trap", unimplementedFunc), + /* 22 */ SyscallDesc("msg_rpc_trap", unimplementedFunc), + /* 23 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 24 */ SyscallDesc("nxm_block", AlphaTru64::nxm_blockFunc), + /* 25 */ SyscallDesc("nxm_unblock", AlphaTru64::nxm_unblockFunc), + /* 26 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 27 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 28 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 29 */ SyscallDesc("nxm_thread_destroy", unimplementedFunc), + /* 30 */ SyscallDesc("lw_wire", unimplementedFunc), + /* 31 */ SyscallDesc("lw_unwire", unimplementedFunc), + /* 32 */ SyscallDesc("nxm_thread_create", AlphaTru64::nxm_thread_createFunc), + /* 33 */ SyscallDesc("nxm_task_init", AlphaTru64::nxm_task_initFunc), + /* 34 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 35 */ SyscallDesc("nxm_idle", AlphaTru64::nxm_idleFunc), + /* 36 */ SyscallDesc("nxm_wakeup_idle", unimplementedFunc), + /* 37 */ SyscallDesc("nxm_set_pthid", unimplementedFunc), + /* 38 */ SyscallDesc("nxm_thread_kill", unimplementedFunc), + /* 39 */ SyscallDesc("nxm_thread_block", AlphaTru64::nxm_thread_blockFunc), + /* 40 */ SyscallDesc("nxm_thread_wakeup", unimplementedFunc), + /* 41 */ SyscallDesc("init_process", unimplementedFunc), + /* 42 */ SyscallDesc("nxm_get_binding", unimplementedFunc), + /* 43 */ SyscallDesc("map_fd", unimplementedFunc), + /* 44 */ SyscallDesc("nxm_resched", unimplementedFunc), + /* 45 */ SyscallDesc("nxm_set_cancel", unimplementedFunc), + /* 46 */ SyscallDesc("nxm_set_binding", unimplementedFunc), + /* 47 */ SyscallDesc("stack_create", AlphaTru64::stack_createFunc), + /* 48 */ SyscallDesc("nxm_get_state", unimplementedFunc), + /* 49 */ SyscallDesc("nxm_thread_suspend", unimplementedFunc), + /* 50 */ SyscallDesc("nxm_thread_resume", unimplementedFunc), + /* 51 */ SyscallDesc("nxm_signal_check", unimplementedFunc), + /* 52 */ SyscallDesc("htg_unix_syscall", unimplementedFunc), + /* 53 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 54 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 55 */ SyscallDesc("host_self", unimplementedFunc), + /* 56 */ SyscallDesc("host_priv_self", unimplementedFunc), + /* 57 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 58 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 59 */ SyscallDesc("swtch_pri", AlphaTru64::swtch_priFunc), + /* 60 */ SyscallDesc("swtch", unimplementedFunc), + /* 61 */ SyscallDesc("thread_switch", unimplementedFunc), + /* 62 */ SyscallDesc("semop_fast", unimplementedFunc), + /* 63 */ SyscallDesc("nxm_pshared_init", unimplementedFunc), + /* 64 */ SyscallDesc("nxm_pshared_block", unimplementedFunc), + /* 65 */ SyscallDesc("nxm_pshared_unblock", unimplementedFunc), + /* 66 */ SyscallDesc("nxm_pshared_destroy", unimplementedFunc), + /* 67 */ SyscallDesc("nxm_swtch_pri", AlphaTru64::swtch_priFunc), + /* 68 */ SyscallDesc("lw_syscall", unimplementedFunc), + /* 69 */ SyscallDesc("kern_invalid", unimplementedFunc), + /* 70 */ SyscallDesc("mach_sctimes_0", unimplementedFunc), + /* 71 */ SyscallDesc("mach_sctimes_1", unimplementedFunc), + /* 72 */ SyscallDesc("mach_sctimes_2", unimplementedFunc), + /* 73 */ SyscallDesc("mach_sctimes_3", unimplementedFunc), + /* 74 */ SyscallDesc("mach_sctimes_4", unimplementedFunc), + /* 75 */ SyscallDesc("mach_sctimes_5", unimplementedFunc), + /* 76 */ SyscallDesc("mach_sctimes_6", unimplementedFunc), + /* 77 */ SyscallDesc("mach_sctimes_7", unimplementedFunc), + /* 78 */ SyscallDesc("mach_sctimes_8", unimplementedFunc), + /* 79 */ SyscallDesc("mach_sctimes_9", unimplementedFunc), + /* 80 */ SyscallDesc("mach_sctimes_10", unimplementedFunc), + /* 81 */ SyscallDesc("mach_sctimes_11", unimplementedFunc), + /* 82 */ SyscallDesc("mach_sctimes_port_alloc_dealloc", unimplementedFunc) +}; + +SyscallDesc* +AlphaTru64Process::getDesc(int callnum) +{ + if (callnum < -Num_Mach_Syscall_Descs || callnum > Num_Syscall_Descs) + return NULL; + + if (callnum < 0) + return &machSyscallDescs[-callnum]; + else + return &syscallDescs[callnum]; +} + + +AlphaTru64Process::AlphaTru64Process(const std::string &name, + ObjectFile *objFile, + System *system, + int stdin_fd, + int stdout_fd, + int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid) + : AlphaLiveProcess(name, objFile, system, stdin_fd, stdout_fd, + stderr_fd, argv, envp, _uid, _euid, _gid, _egid, _pid, _ppid), + Num_Syscall_Descs(sizeof(syscallDescs) / sizeof(SyscallDesc)), + Num_Mach_Syscall_Descs(sizeof(machSyscallDescs) / sizeof(SyscallDesc)) +{ +} diff --git a/src/arch/alpha/tru64/process.hh b/src/arch/alpha/tru64/process.hh new file mode 100644 index 000000000..23429dad3 --- /dev/null +++ b/src/arch/alpha/tru64/process.hh @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2003-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __ALPHA_TRU64_PROCESS_HH__ +#define __ALPHA_TRU64_PROCESS_HH__ + +#include "arch/alpha/process.hh" + +namespace AlphaISA { +/// A process with emulated Alpha Tru64 syscalls. +class AlphaTru64Process : public AlphaLiveProcess +{ + public: + /// Constructor. + AlphaTru64Process(const std::string &name, + ObjectFile *objFile, + System *system, + int stdin_fd, int stdout_fd, int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid); + + /// Array of syscall descriptors, indexed by call number. + static SyscallDesc syscallDescs[]; + + /// Array of mach syscall descriptors, indexed by call number. + static SyscallDesc machSyscallDescs[]; + + const int Num_Syscall_Descs; + const int Num_Mach_Syscall_Descs; + + virtual SyscallDesc* getDesc(int callnum); +}; + +} // namespace AlphaISA + +#endif // __ALPHA_TRU64_PROCESS_HH__ diff --git a/src/arch/alpha/tru64/system.cc b/src/arch/alpha/tru64/system.cc new file mode 100644 index 000000000..00918bda4 --- /dev/null +++ b/src/arch/alpha/tru64/system.cc @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu + */ + +#include "arch/alpha/tru64/system.hh" +#include "arch/isa_traits.hh" +#include "arch/vtophys.hh" +#include "base/loader/symtab.hh" +#include "base/trace.hh" +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "kern/tru64/tru64_events.hh" +#include "kern/system_events.hh" +#include "mem/physical.hh" +#include "mem/port.hh" +#include "sim/builder.hh" + +using namespace std; + +Tru64AlphaSystem::Tru64AlphaSystem(Tru64AlphaSystem::Params *p) + : AlphaSystem(p) +{ + Addr addr = 0; + if (kernelSymtab->findAddress("enable_async_printf", addr)) { + virtPort.write(addr, (uint32_t)0); + } + +#ifdef DEBUG + kernelPanicEvent = addKernelFuncEvent<BreakPCEvent>("panic"); + if (!kernelPanicEvent) + panic("could not find kernel symbol \'panic\'"); +#endif + + badaddrEvent = addKernelFuncEvent<BadAddrEvent>("badaddr"); + if (!badaddrEvent) + panic("could not find kernel symbol \'badaddr\'"); + + skipPowerStateEvent = + addKernelFuncEvent<SkipFuncEvent>("tl_v48_capture_power_state"); + skipScavengeBootEvent = + addKernelFuncEvent<SkipFuncEvent>("pmap_scavenge_boot"); + +#if TRACING_ON + printfEvent = addKernelFuncEvent<PrintfEvent>("printf"); + debugPrintfEvent = addKernelFuncEvent<DebugPrintfEvent>("m5printf"); + debugPrintfrEvent = addKernelFuncEvent<DebugPrintfrEvent>("m5printfr"); + dumpMbufEvent = addKernelFuncEvent<DumpMbufEvent>("m5_dump_mbuf"); +#endif +} + +Tru64AlphaSystem::~Tru64AlphaSystem() +{ +#ifdef DEBUG + delete kernelPanicEvent; +#endif + delete badaddrEvent; + delete skipPowerStateEvent; + delete skipScavengeBootEvent; +#if TRACING_ON + delete printfEvent; + delete debugPrintfEvent; + delete debugPrintfrEvent; + delete dumpMbufEvent; +#endif +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(Tru64AlphaSystem) + + Param<Tick> boot_cpu_frequency; + SimObjectParam<PhysicalMemory *> physmem; + SimpleEnumParam<System::MemoryMode> mem_mode; + + Param<string> kernel; + Param<string> console; + Param<string> pal; + + Param<string> boot_osflags; + Param<string> readfile; + Param<string> symbolfile; + Param<unsigned int> init_param; + + Param<uint64_t> system_type; + Param<uint64_t> system_rev; + +END_DECLARE_SIM_OBJECT_PARAMS(Tru64AlphaSystem) + +BEGIN_INIT_SIM_OBJECT_PARAMS(Tru64AlphaSystem) + + INIT_PARAM(boot_cpu_frequency, "frequency of the boot cpu"), + INIT_PARAM(physmem, "phsyical memory"), + INIT_ENUM_PARAM(mem_mode, "Memory Mode, (1=atomic, 2=timing)", + System::MemoryModeStrings), + INIT_PARAM(kernel, "file that contains the kernel code"), + INIT_PARAM(console, "file that contains the console code"), + INIT_PARAM(pal, "file that contains palcode"), + INIT_PARAM_DFLT(boot_osflags, "flags to pass to the kernel during boot", + "a"), + INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), + INIT_PARAM_DFLT(symbolfile, "file to read symbols from", ""), + INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), + INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 12), + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 2<<1) + +END_INIT_SIM_OBJECT_PARAMS(Tru64AlphaSystem) + +CREATE_SIM_OBJECT(Tru64AlphaSystem) +{ + AlphaSystem::Params *p = new AlphaSystem::Params; + p->name = getInstanceName(); + p->boot_cpu_frequency = boot_cpu_frequency; + p->physmem = physmem; + p->mem_mode = mem_mode; + p->kernel_path = kernel; + p->console_path = console; + p->palcode = pal; + p->boot_osflags = boot_osflags; + p->init_param = init_param; + p->readfile = readfile; + p->symbolfile = symbolfile; + p->system_type = system_type; + p->system_rev = system_rev; + + return new Tru64AlphaSystem(p); +} + +REGISTER_SIM_OBJECT("Tru64AlphaSystem", Tru64AlphaSystem) diff --git a/src/arch/alpha/tru64/system.hh b/src/arch/alpha/tru64/system.hh new file mode 100644 index 000000000..947e92f50 --- /dev/null +++ b/src/arch/alpha/tru64/system.hh @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu + */ + +#ifndef __ARCH_ALPHA_TRU64_SYSTEM_HH__ +#define __ARCH_ALPHA_TRU64_SYSTEM_HH__ + +#include "arch/alpha/system.hh" +#include "arch/isa_traits.hh" +#include "sim/system.hh" + +class ThreadContext; + +class BreakPCEvent; +class BadAddrEvent; +class SkipFuncEvent; +class PrintfEvent; +class DebugPrintfEvent; +class DebugPrintfrEvent; +class DumpMbufEvent; +class AlphaArguments; + +class Tru64AlphaSystem : public AlphaSystem +{ + private: +#ifdef DEBUG + /** Event to halt the simulator if the kernel calls panic() */ + BreakPCEvent *kernelPanicEvent; +#endif + + BadAddrEvent *badaddrEvent; + SkipFuncEvent *skipPowerStateEvent; + SkipFuncEvent *skipScavengeBootEvent; + PrintfEvent *printfEvent; + DebugPrintfEvent *debugPrintfEvent; + DebugPrintfrEvent *debugPrintfrEvent; + DumpMbufEvent *dumpMbufEvent; + + public: + Tru64AlphaSystem(Params *p); + ~Tru64AlphaSystem(); + + static void Printf(AlphaArguments args); + static void DumpMbuf(AlphaArguments args); +}; + +#endif // __ARCH_ALPHA_TRU64_SYSTEM_HH__ diff --git a/src/arch/alpha/tru64/tru64.cc b/src/arch/alpha/tru64/tru64.cc new file mode 100644 index 000000000..56b04846f --- /dev/null +++ b/src/arch/alpha/tru64/tru64.cc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#include "arch/alpha/tru64/tru64.hh" + +// open(2) flags translation table +OpenFlagTransTable AlphaTru64::openFlagTable[] = { +#ifdef _MSC_VER + { AlphaTru64::TGT_O_RDONLY, _O_RDONLY }, + { AlphaTru64::TGT_O_WRONLY, _O_WRONLY }, + { AlphaTru64::TGT_O_RDWR, _O_RDWR }, + { AlphaTru64::TGT_O_APPEND, _O_APPEND }, + { AlphaTru64::TGT_O_CREAT, _O_CREAT }, + { AlphaTru64::TGT_O_TRUNC, _O_TRUNC }, + { AlphaTru64::TGT_O_EXCL, _O_EXCL }, +#ifdef _O_NONBLOCK + { AlphaTru64::TGT_O_NONBLOCK, _O_NONBLOCK }, +#endif +#ifdef _O_NOCTTY + { AlphaTru64::TGT_O_NOCTTY, _O_NOCTTY }, +#endif +#ifdef _O_SYNC + { AlphaTru64::TGT_O_SYNC, _O_SYNC }, +#endif +#else /* !_MSC_VER */ + { AlphaTru64::TGT_O_RDONLY, O_RDONLY }, + { AlphaTru64::TGT_O_WRONLY, O_WRONLY }, + { AlphaTru64::TGT_O_RDWR, O_RDWR }, + { AlphaTru64::TGT_O_APPEND, O_APPEND }, + { AlphaTru64::TGT_O_CREAT, O_CREAT }, + { AlphaTru64::TGT_O_TRUNC, O_TRUNC }, + { AlphaTru64::TGT_O_EXCL, O_EXCL }, + { AlphaTru64::TGT_O_NONBLOCK, O_NONBLOCK }, + { AlphaTru64::TGT_O_NOCTTY, O_NOCTTY }, +#ifdef O_SYNC + { AlphaTru64::TGT_O_SYNC, O_SYNC }, +#endif +#endif /* _MSC_VER */ +}; + +const int AlphaTru64::NUM_OPEN_FLAGS = + (sizeof(AlphaTru64::openFlagTable)/sizeof(AlphaTru64::openFlagTable[0])); + + + diff --git a/src/arch/alpha/tru64/tru64.hh b/src/arch/alpha/tru64/tru64.hh new file mode 100644 index 000000000..f0711b995 --- /dev/null +++ b/src/arch/alpha/tru64/tru64.hh @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#ifndef __ALPHA_ALPHA_TRU64_HH +#define __ALPHA_ALPHA_TRU64_HH + +#include "kern/tru64/tru64.hh" + +class AlphaTru64 : public Tru64 +{ + + public: + /// This table maps the target open() flags to the corresponding + /// host open() flags. + static OpenFlagTransTable openFlagTable[]; + + /// Number of entries in openFlagTable[]. + static const int NUM_OPEN_FLAGS; + + //@{ + /// open(2) flag values. + static const int TGT_O_RDONLY = 00000000; //!< O_RDONLY + static const int TGT_O_WRONLY = 00000001; //!< O_WRONLY + static const int TGT_O_RDWR = 00000002; //!< O_RDWR + static const int TGT_O_NONBLOCK = 00000004; //!< O_NONBLOCK + static const int TGT_O_APPEND = 00000010; //!< O_APPEND + static const int TGT_O_CREAT = 00001000; //!< O_CREAT + static const int TGT_O_TRUNC = 00002000; //!< O_TRUNC + static const int TGT_O_EXCL = 00004000; //!< O_EXCL + static const int TGT_O_NOCTTY = 00010000; //!< O_NOCTTY + static const int TGT_O_SYNC = 00040000; //!< O_SYNC + static const int TGT_O_DRD = 00100000; //!< O_DRD + static const int TGT_O_DIRECTIO = 00200000; //!< O_DIRECTIO + static const int TGT_O_CACHE = 00400000; //!< O_CACHE + static const int TGT_O_DSYNC = 02000000; //!< O_DSYNC + static const int TGT_O_RSYNC = 04000000; //!< O_RSYNC + //@} + + /// For mmap(). + static const unsigned TGT_MAP_ANONYMOUS = 0x10; + + //@{ + /// For getsysinfo(). + static const unsigned GSI_PLATFORM_NAME = 103; //!< platform name as string + static const unsigned GSI_CPU_INFO = 59; //!< CPU information + static const unsigned GSI_PROC_TYPE = 60; //!< get proc_type + static const unsigned GSI_MAX_CPU = 30; //!< max # cpu's on this machine + static const unsigned GSI_CPUS_IN_BOX = 55; //!< number of CPUs in system + static const unsigned GSI_PHYSMEM = 19; //!< Physical memory in KB + static const unsigned GSI_CLK_TCK = 42; //!< clock freq in Hz + //@} + + //@{ + /// For getrusage(). + static const int TGT_RUSAGE_THREAD = 1; + static const int TGT_RUSAGE_SELF = 0; + static const int TGT_RUSAGE_CHILDREN = -1; + //@} + + //@{ + /// For setsysinfo(). + static const unsigned SSI_IEEE_FP_CONTROL = 14; //!< ieee_set_fp_control() + //@} + + //@{ + /// ioctl() command codes. + static const unsigned TIOCGETP = 0x40067408; + static const unsigned TIOCSETP = 0x80067409; + static const unsigned TIOCSETN = 0x8006740a; + static const unsigned TIOCSETC = 0x80067411; + static const unsigned TIOCGETC = 0x40067412; + static const unsigned FIONREAD = 0x4004667f; + static const unsigned TIOCISATTY = 0x2000745e; + static const unsigned TIOCGETS = 0x402c7413; + static const unsigned TIOCGETA = 0x40127417; + //@} + + //@{ + /// For table(). + static const int TBL_SYSINFO = 12; + //@} + + /// Resource enumeration for getrlimit(). + enum rlimit_resources { + TGT_RLIMIT_CPU = 0, + TGT_RLIMIT_FSIZE = 1, + TGT_RLIMIT_DATA = 2, + TGT_RLIMIT_STACK = 3, + TGT_RLIMIT_CORE = 4, + TGT_RLIMIT_RSS = 5, + TGT_RLIMIT_NOFILE = 6, + TGT_RLIMIT_AS = 7, + TGT_RLIMIT_VMEM = 7, + TGT_RLIMIT_NPROC = 8, + TGT_RLIMIT_MEMLOCK = 9, + TGT_RLIMIT_LOCKS = 10 + }; +}; + + + +#endif diff --git a/src/arch/alpha/types.hh b/src/arch/alpha/types.hh new file mode 100644 index 000000000..ae42552d8 --- /dev/null +++ b/src/arch/alpha/types.hh @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __ARCH_ALPHA_TYPES_HH__ +#define __ARCH_ALPHA_TYPES_HH__ + +#include <inttypes.h> + +namespace AlphaISA +{ + + typedef uint32_t MachInst; + typedef uint64_t ExtMachInst; + typedef uint8_t RegIndex; + + typedef uint64_t IntReg; + + // floating point register file entry type + typedef double FloatReg; + typedef uint64_t FloatRegBits; + + // control register file contents + typedef uint64_t MiscReg; + + typedef union { + IntReg intreg; + FloatReg fpreg; + MiscReg ctrlreg; + } AnyReg; + + enum RegContextParam + { + CONTEXT_PALMODE + }; + + typedef bool RegContextVal; + + enum annotes { + ANNOTE_NONE = 0, + // An impossible number for instruction annotations + ITOUCH_ANNOTE = 0xffffffff, + }; + +} // namespace AlphaISA + +#endif diff --git a/src/arch/alpha/utility.hh b/src/arch/alpha/utility.hh new file mode 100644 index 000000000..d3ccc0444 --- /dev/null +++ b/src/arch/alpha/utility.hh @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __ARCH_ALPHA_UTILITY_HH__ +#define __ARCH_ALPHA_UTILITY_HH__ + +#include "config/full_system.hh" +#include "arch/alpha/types.hh" +#include "arch/alpha/isa_traits.hh" +#include "arch/alpha/regfile.hh" +#include "base/misc.hh" + +namespace AlphaISA +{ + + static inline ExtMachInst + makeExtMI(MachInst inst, const uint64_t &pc) { +#if FULL_SYSTEM + ExtMachInst ext_inst = inst; + if (pc && 0x1) + return ext_inst|=(static_cast<ExtMachInst>(pc & 0x1) << 32); + else + return ext_inst; +#else + return ExtMachInst(inst); +#endif + } + + inline bool isCallerSaveIntegerRegister(unsigned int reg) { + panic("register classification not implemented"); + return (reg >= 1 && reg <= 8 || reg >= 22 && reg <= 25 || reg == 27); + } + + inline bool isCalleeSaveIntegerRegister(unsigned int reg) { + panic("register classification not implemented"); + return (reg >= 9 && reg <= 15); + } + + inline bool isCallerSaveFloatRegister(unsigned int reg) { + panic("register classification not implemented"); + return false; + } + + inline bool isCalleeSaveFloatRegister(unsigned int reg) { + panic("register classification not implemented"); + return false; + } + + inline Addr alignAddress(const Addr &addr, + unsigned int nbytes) { + return (addr & ~(nbytes - 1)); + } + + // Instruction address compression hooks + inline Addr realPCToFetchPC(const Addr &addr) { + return addr; + } + + inline Addr fetchPCToRealPC(const Addr &addr) { + return addr; + } + + // the size of "fetched" instructions (not necessarily the size + // of real instructions for PISA) + inline size_t fetchInstSize() { + return sizeof(MachInst); + } + + inline MachInst makeRegisterCopy(int dest, int src) { + panic("makeRegisterCopy not implemented"); + return 0; + } + + // Machine operations + + void saveMachineReg(AnyReg &savereg, const RegFile ®_file, + int regnum); + + void restoreMachineReg(RegFile ®s, const AnyReg ®, + int regnum); + + /** + * Function to insure ISA semantics about 0 registers. + * @param tc The thread context. + */ + template <class TC> + void zeroRegisters(TC *tc); + +#if FULL_SYSTEM + // Alpha IPR register accessors + inline bool PcPAL(Addr addr) { return addr & 0x1; } + + //////////////////////////////////////////////////////////////////////// + // + // Translation stuff + // + + inline Addr PteAddr(Addr a) { return (a & PteMask) << PteShift; } + + // User Virtual + inline bool IsUSeg(Addr a) { return USegBase <= a && a <= USegEnd; } + + // Kernel Direct Mapped + inline bool IsK0Seg(Addr a) { return K0SegBase <= a && a <= K0SegEnd; } + inline Addr K0Seg2Phys(Addr addr) { return addr & ~K0SegBase; } + + // Kernel Virtual + inline bool IsK1Seg(Addr a) { return K1SegBase <= a && a <= K1SegEnd; } + + inline Addr + TruncPage(Addr addr) + { return addr & ~(PageBytes - 1); } + + inline Addr + RoundPage(Addr addr) + { return (addr + PageBytes - 1) & ~(PageBytes - 1); } + + void initCPU(ThreadContext *tc, int cpuId); + void initIPRs(ThreadContext *tc, int cpuId); + + /** + * Function to check for and process any interrupts. + * @param tc The thread context. + */ + template <class TC> + void processInterrupts(TC *tc); +#endif + +} // namespace AlphaISA + +#endif diff --git a/src/arch/alpha/vtophys.cc b/src/arch/alpha/vtophys.cc new file mode 100644 index 000000000..f7fd92c15 --- /dev/null +++ b/src/arch/alpha/vtophys.cc @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + * Ali Saidi + */ + +#include <string> + +#include "arch/alpha/ev5.hh" +#include "arch/alpha/vtophys.hh" +#include "base/chunk_generator.hh" +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "mem/vport.hh" + +using namespace std; +using namespace AlphaISA; + +AlphaISA::PageTableEntry +AlphaISA::kernel_pte_lookup(FunctionalPort *mem, Addr ptbr, AlphaISA::VAddr vaddr) +{ + Addr level1_pte = ptbr + vaddr.level1(); + AlphaISA::PageTableEntry level1 = mem->read<uint64_t>(level1_pte); + if (!level1.valid()) { + DPRINTF(VtoPhys, "level 1 PTE not valid, va = %#\n", vaddr); + return 0; + } + + Addr level2_pte = level1.paddr() + vaddr.level2(); + AlphaISA::PageTableEntry level2 = mem->read<uint64_t>(level2_pte); + if (!level2.valid()) { + DPRINTF(VtoPhys, "level 2 PTE not valid, va = %#x\n", vaddr); + return 0; + } + + Addr level3_pte = level2.paddr() + vaddr.level3(); + AlphaISA::PageTableEntry level3 = mem->read<uint64_t>(level3_pte); + if (!level3.valid()) { + DPRINTF(VtoPhys, "level 3 PTE not valid, va = %#x\n", vaddr); + return 0; + } + return level3; +} + +Addr +AlphaISA::vtophys(Addr vaddr) +{ + Addr paddr = 0; + if (AlphaISA::IsUSeg(vaddr)) + DPRINTF(VtoPhys, "vtophys: invalid vaddr %#x", vaddr); + else if (AlphaISA::IsK0Seg(vaddr)) + paddr = AlphaISA::K0Seg2Phys(vaddr); + else + panic("vtophys: ptbr is not set on virtual lookup"); + + DPRINTF(VtoPhys, "vtophys(%#x) -> %#x\n", vaddr, paddr); + + return paddr; +} + +Addr +AlphaISA::vtophys(ThreadContext *tc, Addr addr) +{ + AlphaISA::VAddr vaddr = addr; + Addr ptbr = tc->readMiscReg(AlphaISA::IPR_PALtemp20); + Addr paddr = 0; + //@todo Andrew couldn't remember why he commented some of this code + //so I put it back in. Perhaps something to do with gdb debugging? + if (AlphaISA::PcPAL(vaddr) && (vaddr < EV5::PalMax)) { + paddr = vaddr & ~ULL(1); + } else { + if (AlphaISA::IsK0Seg(vaddr)) { + paddr = AlphaISA::K0Seg2Phys(vaddr); + } else if (!ptbr) { + paddr = vaddr; + } else { + AlphaISA::PageTableEntry pte = + kernel_pte_lookup(tc->getPhysPort(), ptbr, vaddr); + if (pte.valid()) + paddr = pte.paddr() | vaddr.offset(); + } + } + + + DPRINTF(VtoPhys, "vtophys(%#x) -> %#x\n", vaddr, paddr); + + return paddr; +} + + +void +AlphaISA::CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen) +{ + uint8_t *dst = (uint8_t *)dest; + VirtualPort *vp = tc->getVirtPort(tc); + + vp->readBlob(src, dst, cplen); + + tc->delVirtPort(vp); + +} + +void +AlphaISA::CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen) +{ + uint8_t *src = (uint8_t *)source; + VirtualPort *vp = tc->getVirtPort(tc); + + vp->writeBlob(dest, src, cplen); + + tc->delVirtPort(vp); +} + +void +AlphaISA::CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen) +{ + int len = 0; + VirtualPort *vp = tc->getVirtPort(tc); + + do { + vp->readBlob(vaddr++, (uint8_t*)dst++, 1); + len++; + } while (len < maxlen && dst[len] != 0 ); + + tc->delVirtPort(vp); + dst[len] = 0; +} + +void +AlphaISA::CopyStringIn(ThreadContext *tc, char *src, Addr vaddr) +{ + VirtualPort *vp = tc->getVirtPort(tc); + for (ChunkGenerator gen(vaddr, strlen(src), AlphaISA::PageBytes); !gen.done(); + gen.next()) + { + vp->writeBlob(gen.addr(), (uint8_t*)src, gen.size()); + src += gen.size(); + } + tc->delVirtPort(vp); +} diff --git a/src/arch/alpha/vtophys.hh b/src/arch/alpha/vtophys.hh new file mode 100644 index 000000000..32b999c37 --- /dev/null +++ b/src/arch/alpha/vtophys.hh @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __ARCH_ALPHA_VTOPHYS_H__ +#define __ARCH_ALPHA_VTOPHYS_H__ + +#include "arch/alpha/isa_traits.hh" +#include "arch/alpha/pagetable.hh" +#include "arch/alpha/utility.hh" + +class ThreadContext; +class FunctionalPort; + +namespace AlphaISA { + + PageTableEntry + kernel_pte_lookup(FunctionalPort *mem, Addr ptbr, AlphaISA::VAddr vaddr); + + Addr vtophys(Addr vaddr); + Addr vtophys(ThreadContext *tc, Addr vaddr); + + void CopyOut(ThreadContext *tc, void *dst, Addr src, size_t len); + void CopyIn(ThreadContext *tc, Addr dst, void *src, size_t len); + void CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen); + void CopyStringIn(ThreadContext *tc, char *src, Addr vaddr); + +}; +#endif // __ARCH_ALPHA_VTOPHYS_H__ + diff --git a/src/arch/isa_parser.py b/src/arch/isa_parser.py new file mode 100755 index 000000000..4d522e18a --- /dev/null +++ b/src/arch/isa_parser.py @@ -0,0 +1,1818 @@ +# Copyright (c) 2003-2005 The Regents of The University of Michigan +# 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. +# +# Authors: Steve Reinhardt +# Korey Sewell + +import os +import sys +import re +import string +import traceback +# get type names +from types import * + +# Prepend the directory where the PLY lex & yacc modules are found +# to the search path. Assumes we're compiling in a subdirectory +# of 'build' in the current tree. +sys.path[0:0] = [os.environ['M5_PLY']] + +import lex +import yacc + +##################################################################### +# +# Lexer +# +# The PLY lexer module takes two things as input: +# - A list of token names (the string list 'tokens') +# - A regular expression describing a match for each token. The +# regexp for token FOO can be provided in two ways: +# - as a string variable named t_FOO +# - as the doc string for a function named t_FOO. In this case, +# the function is also executed, allowing an action to be +# associated with each token match. +# +##################################################################### + +# Reserved words. These are listed separately as they are matched +# using the same regexp as generic IDs, but distinguished in the +# t_ID() function. The PLY documentation suggests this approach. +reserved = ( + 'BITFIELD', 'DECODE', 'DECODER', 'DEFAULT', 'DEF', 'EXEC', 'FORMAT', + 'HEADER', 'LET', 'NAMESPACE', 'OPERAND_TYPES', 'OPERANDS', + 'OUTPUT', 'SIGNED', 'TEMPLATE' + ) + +# List of tokens. The lex module requires this. +tokens = reserved + ( + # identifier + 'ID', + + # integer literal + 'INTLIT', + + # string literal + 'STRLIT', + + # code literal + 'CODELIT', + + # ( ) [ ] { } < > , ; : :: * + 'LPAREN', 'RPAREN', + 'LBRACKET', 'RBRACKET', + 'LBRACE', 'RBRACE', + 'LESS', 'GREATER', 'EQUALS', + 'COMMA', 'SEMI', 'COLON', 'DBLCOLON', + 'ASTERISK', + + # C preprocessor directives + 'CPPDIRECTIVE' + +# The following are matched but never returned. commented out to +# suppress PLY warning + # newfile directive +# 'NEWFILE', + + # endfile directive +# 'ENDFILE' +) + +# Regular expressions for token matching +t_LPAREN = r'\(' +t_RPAREN = r'\)' +t_LBRACKET = r'\[' +t_RBRACKET = r'\]' +t_LBRACE = r'\{' +t_RBRACE = r'\}' +t_LESS = r'\<' +t_GREATER = r'\>' +t_EQUALS = r'=' +t_COMMA = r',' +t_SEMI = r';' +t_COLON = r':' +t_DBLCOLON = r'::' +t_ASTERISK = r'\*' + +# Identifiers and reserved words +reserved_map = { } +for r in reserved: + reserved_map[r.lower()] = r + +def t_ID(t): + r'[A-Za-z_]\w*' + t.type = reserved_map.get(t.value,'ID') + return t + +# Integer literal +def t_INTLIT(t): + r'(0x[\da-fA-F]+)|\d+' + try: + t.value = int(t.value,0) + except ValueError: + error(t.lineno, 'Integer value "%s" too large' % t.value) + t.value = 0 + return t + +# String literal. Note that these use only single quotes, and +# can span multiple lines. +def t_STRLIT(t): + r"(?m)'([^'])+'" + # strip off quotes + t.value = t.value[1:-1] + t.lineno += t.value.count('\n') + return t + + +# "Code literal"... like a string literal, but delimiters are +# '{{' and '}}' so they get formatted nicely under emacs c-mode +def t_CODELIT(t): + r"(?m)\{\{([^\}]|}(?!\}))+\}\}" + # strip off {{ & }} + t.value = t.value[2:-2] + t.lineno += t.value.count('\n') + return t + +def t_CPPDIRECTIVE(t): + r'^\#[^\#].*\n' + t.lineno += t.value.count('\n') + return t + +def t_NEWFILE(t): + r'^\#\#newfile\s+"[\w/.-]*"' + fileNameStack.push((t.value[11:-1], t.lineno)) + t.lineno = 0 + +def t_ENDFILE(t): + r'^\#\#endfile' + (old_filename, t.lineno) = fileNameStack.pop() + +# +# The functions t_NEWLINE, t_ignore, and t_error are +# special for the lex module. +# + +# Newlines +def t_NEWLINE(t): + r'\n+' + t.lineno += t.value.count('\n') + +# Comments +def t_comment(t): + r'//.*' + +# Completely ignored characters +t_ignore = ' \t\x0c' + +# Error handler +def t_error(t): + error(t.lineno, "illegal character '%s'" % t.value[0]) + t.skip(1) + +# Build the lexer +lex.lex() + +##################################################################### +# +# Parser +# +# Every function whose name starts with 'p_' defines a grammar rule. +# The rule is encoded in the function's doc string, while the +# function body provides the action taken when the rule is matched. +# The argument to each function is a list of the values of the +# rule's symbols: t[0] for the LHS, and t[1..n] for the symbols +# on the RHS. For tokens, the value is copied from the t.value +# attribute provided by the lexer. For non-terminals, the value +# is assigned by the producing rule; i.e., the job of the grammar +# rule function is to set the value for the non-terminal on the LHS +# (by assigning to t[0]). +##################################################################### + +# The LHS of the first grammar rule is used as the start symbol +# (in this case, 'specification'). Note that this rule enforces +# that there will be exactly one namespace declaration, with 0 or more +# global defs/decls before and after it. The defs & decls before +# the namespace decl will be outside the namespace; those after +# will be inside. The decoder function is always inside the namespace. +def p_specification(t): + 'specification : opt_defs_and_outputs name_decl opt_defs_and_outputs decode_block' + global_code = t[1] + isa_name = t[2] + namespace = isa_name + "Inst" + # wrap the decode block as a function definition + t[4].wrap_decode_block(''' +StaticInstPtr +%(isa_name)s::decodeInst(%(isa_name)s::ExtMachInst machInst) +{ + using namespace %(namespace)s; +''' % vars(), '}') + # both the latter output blocks and the decode block are in the namespace + namespace_code = t[3] + t[4] + # pass it all back to the caller of yacc.parse() + t[0] = (isa_name, namespace, global_code, namespace_code) + +# ISA name declaration looks like "namespace <foo>;" +def p_name_decl(t): + 'name_decl : NAMESPACE ID SEMI' + t[0] = t[2] + +# 'opt_defs_and_outputs' is a possibly empty sequence of +# def and/or output statements. +def p_opt_defs_and_outputs_0(t): + 'opt_defs_and_outputs : empty' + t[0] = GenCode() + +def p_opt_defs_and_outputs_1(t): + 'opt_defs_and_outputs : defs_and_outputs' + t[0] = t[1] + +def p_defs_and_outputs_0(t): + 'defs_and_outputs : def_or_output' + t[0] = t[1] + +def p_defs_and_outputs_1(t): + 'defs_and_outputs : defs_and_outputs def_or_output' + t[0] = t[1] + t[2] + +# The list of possible definition/output statements. +def p_def_or_output(t): + '''def_or_output : def_format + | def_bitfield + | def_template + | def_operand_types + | def_operands + | output_header + | output_decoder + | output_exec + | global_let''' + t[0] = t[1] + +# Output blocks 'output <foo> {{...}}' (C++ code blocks) are copied +# directly to the appropriate output section. + + +# Protect any non-dict-substitution '%'s in a format string +# (i.e. those not followed by '(') +def protect_non_subst_percents(s): + return re.sub(r'%(?!\()', '%%', s) + +# Massage output block by substituting in template definitions and bit +# operators. We handle '%'s embedded in the string that don't +# indicate template substitutions (or CPU-specific symbols, which get +# handled in GenCode) by doubling them first so that the format +# operation will reduce them back to single '%'s. +def process_output(s): + s = protect_non_subst_percents(s) + # protects cpu-specific symbols too + s = protect_cpu_symbols(s) + return substBitOps(s % templateMap) + +def p_output_header(t): + 'output_header : OUTPUT HEADER CODELIT SEMI' + t[0] = GenCode(header_output = process_output(t[3])) + +def p_output_decoder(t): + 'output_decoder : OUTPUT DECODER CODELIT SEMI' + t[0] = GenCode(decoder_output = process_output(t[3])) + +def p_output_exec(t): + 'output_exec : OUTPUT EXEC CODELIT SEMI' + t[0] = GenCode(exec_output = process_output(t[3])) + +# global let blocks 'let {{...}}' (Python code blocks) are executed +# directly when seen. Note that these execute in a special variable +# context 'exportContext' to prevent the code from polluting this +# script's namespace. +def p_global_let(t): + 'global_let : LET CODELIT SEMI' + updateExportContext() + try: + exec fixPythonIndentation(t[2]) in exportContext + except Exception, exc: + error(t.lineno(1), + 'error: %s in global let block "%s".' % (exc, t[2])) + t[0] = GenCode() # contributes nothing to the output C++ file + +# Define the mapping from operand type extensions to C++ types and bit +# widths (stored in operandTypeMap). +def p_def_operand_types(t): + 'def_operand_types : DEF OPERAND_TYPES CODELIT SEMI' + try: + userDict = eval('{' + t[3] + '}') + except Exception, exc: + error(t.lineno(1), + 'error: %s in def operand_types block "%s".' % (exc, t[3])) + buildOperandTypeMap(userDict, t.lineno(1)) + t[0] = GenCode() # contributes nothing to the output C++ file + +# Define the mapping from operand names to operand classes and other +# traits. Stored in operandNameMap. +def p_def_operands(t): + 'def_operands : DEF OPERANDS CODELIT SEMI' + if not globals().has_key('operandTypeMap'): + error(t.lineno(1), + 'error: operand types must be defined before operands') + try: + userDict = eval('{' + t[3] + '}') + except Exception, exc: + error(t.lineno(1), + 'error: %s in def operands block "%s".' % (exc, t[3])) + buildOperandNameMap(userDict, t.lineno(1)) + t[0] = GenCode() # contributes nothing to the output C++ file + +# A bitfield definition looks like: +# 'def [signed] bitfield <ID> [<first>:<last>]' +# This generates a preprocessor macro in the output file. +def p_def_bitfield_0(t): + 'def_bitfield : DEF opt_signed BITFIELD ID LESS INTLIT COLON INTLIT GREATER SEMI' + expr = 'bits(machInst, %2d, %2d)' % (t[6], t[8]) + if (t[2] == 'signed'): + expr = 'sext<%d>(%s)' % (t[6] - t[8] + 1, expr) + hash_define = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr) + t[0] = GenCode(header_output = hash_define) + +# alternate form for single bit: 'def [signed] bitfield <ID> [<bit>]' +def p_def_bitfield_1(t): + 'def_bitfield : DEF opt_signed BITFIELD ID LESS INTLIT GREATER SEMI' + expr = 'bits(machInst, %2d, %2d)' % (t[6], t[6]) + if (t[2] == 'signed'): + expr = 'sext<%d>(%s)' % (1, expr) + hash_define = '#undef %s\n#define %s\t%s\n' % (t[4], t[4], expr) + t[0] = GenCode(header_output = hash_define) + +def p_opt_signed_0(t): + 'opt_signed : SIGNED' + t[0] = t[1] + +def p_opt_signed_1(t): + 'opt_signed : empty' + t[0] = '' + +# Global map variable to hold templates +templateMap = {} + +def p_def_template(t): + 'def_template : DEF TEMPLATE ID CODELIT SEMI' + templateMap[t[3]] = Template(t[4]) + t[0] = GenCode() + +# An instruction format definition looks like +# "def format <fmt>(<params>) {{...}};" +def p_def_format(t): + 'def_format : DEF FORMAT ID LPAREN param_list RPAREN CODELIT SEMI' + (id, params, code) = (t[3], t[5], t[7]) + defFormat(id, params, code, t.lineno(1)) + t[0] = GenCode() + +# The formal parameter list for an instruction format is a possibly +# empty list of comma-separated parameters. Positional (standard, +# non-keyword) parameters must come first, followed by keyword +# parameters, followed by a '*foo' parameter that gets excess +# positional arguments (as in Python). Each of these three parameter +# categories is optional. +# +# Note that we do not support the '**foo' parameter for collecting +# otherwise undefined keyword args. Otherwise the parameter list is +# (I believe) identical to what is supported in Python. +# +# The param list generates a tuple, where the first element is a list of +# the positional params and the second element is a dict containing the +# keyword params. +def p_param_list_0(t): + 'param_list : positional_param_list COMMA nonpositional_param_list' + t[0] = t[1] + t[3] + +def p_param_list_1(t): + '''param_list : positional_param_list + | nonpositional_param_list''' + t[0] = t[1] + +def p_positional_param_list_0(t): + 'positional_param_list : empty' + t[0] = [] + +def p_positional_param_list_1(t): + 'positional_param_list : ID' + t[0] = [t[1]] + +def p_positional_param_list_2(t): + 'positional_param_list : positional_param_list COMMA ID' + t[0] = t[1] + [t[3]] + +def p_nonpositional_param_list_0(t): + 'nonpositional_param_list : keyword_param_list COMMA excess_args_param' + t[0] = t[1] + t[3] + +def p_nonpositional_param_list_1(t): + '''nonpositional_param_list : keyword_param_list + | excess_args_param''' + t[0] = t[1] + +def p_keyword_param_list_0(t): + 'keyword_param_list : keyword_param' + t[0] = [t[1]] + +def p_keyword_param_list_1(t): + 'keyword_param_list : keyword_param_list COMMA keyword_param' + t[0] = t[1] + [t[3]] + +def p_keyword_param(t): + 'keyword_param : ID EQUALS expr' + t[0] = t[1] + ' = ' + t[3].__repr__() + +def p_excess_args_param(t): + 'excess_args_param : ASTERISK ID' + # Just concatenate them: '*ID'. Wrap in list to be consistent + # with positional_param_list and keyword_param_list. + t[0] = [t[1] + t[2]] + +# End of format definition-related rules. +############## + +# +# A decode block looks like: +# decode <field1> [, <field2>]* [default <inst>] { ... } +# +def p_decode_block(t): + 'decode_block : DECODE ID opt_default LBRACE decode_stmt_list RBRACE' + default_defaults = defaultStack.pop() + codeObj = t[5] + # use the "default defaults" only if there was no explicit + # default statement in decode_stmt_list + if not codeObj.has_decode_default: + codeObj += default_defaults + codeObj.wrap_decode_block('switch (%s) {\n' % t[2], '}\n') + t[0] = codeObj + +# The opt_default statement serves only to push the "default defaults" +# onto defaultStack. This value will be used by nested decode blocks, +# and used and popped off when the current decode_block is processed +# (in p_decode_block() above). +def p_opt_default_0(t): + 'opt_default : empty' + # no default specified: reuse the one currently at the top of the stack + defaultStack.push(defaultStack.top()) + # no meaningful value returned + t[0] = None + +def p_opt_default_1(t): + 'opt_default : DEFAULT inst' + # push the new default + codeObj = t[2] + codeObj.wrap_decode_block('\ndefault:\n', 'break;\n') + defaultStack.push(codeObj) + # no meaningful value returned + t[0] = None + +def p_decode_stmt_list_0(t): + 'decode_stmt_list : decode_stmt' + t[0] = t[1] + +def p_decode_stmt_list_1(t): + 'decode_stmt_list : decode_stmt decode_stmt_list' + if (t[1].has_decode_default and t[2].has_decode_default): + error(t.lineno(1), 'Two default cases in decode block') + t[0] = t[1] + t[2] + +# +# Decode statement rules +# +# There are four types of statements allowed in a decode block: +# 1. Format blocks 'format <foo> { ... }' +# 2. Nested decode blocks +# 3. Instruction definitions. +# 4. C preprocessor directives. + + +# Preprocessor directives found in a decode statement list are passed +# through to the output, replicated to all of the output code +# streams. This works well for ifdefs, so we can ifdef out both the +# declarations and the decode cases generated by an instruction +# definition. Handling them as part of the grammar makes it easy to +# keep them in the right place with respect to the code generated by +# the other statements. +def p_decode_stmt_cpp(t): + 'decode_stmt : CPPDIRECTIVE' + t[0] = GenCode(t[1], t[1], t[1], t[1]) + +# A format block 'format <foo> { ... }' sets the default instruction +# format used to handle instruction definitions inside the block. +# This format can be overridden by using an explicit format on the +# instruction definition or with a nested format block. +def p_decode_stmt_format(t): + 'decode_stmt : FORMAT push_format_id LBRACE decode_stmt_list RBRACE' + # The format will be pushed on the stack when 'push_format_id' is + # processed (see below). Once the parser has recognized the full + # production (though the right brace), we're done with the format, + # so now we can pop it. + formatStack.pop() + t[0] = t[4] + +# This rule exists so we can set the current format (& push the stack) +# when we recognize the format name part of the format block. +def p_push_format_id(t): + 'push_format_id : ID' + try: + formatStack.push(formatMap[t[1]]) + t[0] = ('', '// format %s' % t[1]) + except KeyError: + error(t.lineno(1), 'instruction format "%s" not defined.' % t[1]) + +# Nested decode block: if the value of the current field matches the +# specified constant, do a nested decode on some other field. +def p_decode_stmt_decode(t): + 'decode_stmt : case_label COLON decode_block' + label = t[1] + codeObj = t[3] + # just wrap the decoding code from the block as a case in the + # outer switch statement. + codeObj.wrap_decode_block('\n%s:\n' % label) + codeObj.has_decode_default = (label == 'default') + t[0] = codeObj + +# Instruction definition (finally!). +def p_decode_stmt_inst(t): + 'decode_stmt : case_label COLON inst SEMI' + label = t[1] + codeObj = t[3] + codeObj.wrap_decode_block('\n%s:' % label, 'break;\n') + codeObj.has_decode_default = (label == 'default') + t[0] = codeObj + +# The case label is either a list of one or more constants or 'default' +def p_case_label_0(t): + 'case_label : intlit_list' + t[0] = ': '.join(map(lambda a: 'case %#x' % a, t[1])) + +def p_case_label_1(t): + 'case_label : DEFAULT' + t[0] = 'default' + +# +# The constant list for a decode case label must be non-empty, but may have +# one or more comma-separated integer literals in it. +# +def p_intlit_list_0(t): + 'intlit_list : INTLIT' + t[0] = [t[1]] + +def p_intlit_list_1(t): + 'intlit_list : intlit_list COMMA INTLIT' + t[0] = t[1] + t[0].append(t[3]) + +# Define an instruction using the current instruction format (specified +# by an enclosing format block). +# "<mnemonic>(<args>)" +def p_inst_0(t): + 'inst : ID LPAREN arg_list RPAREN' + # Pass the ID and arg list to the current format class to deal with. + currentFormat = formatStack.top() + codeObj = currentFormat.defineInst(t[1], t[3], t.lineno(1)) + args = ','.join(map(str, t[3])) + args = re.sub('(?m)^', '//', args) + args = re.sub('^//', '', args) + comment = '\n// %s::%s(%s)\n' % (currentFormat.id, t[1], args) + codeObj.prepend_all(comment) + t[0] = codeObj + +# Define an instruction using an explicitly specified format: +# "<fmt>::<mnemonic>(<args>)" +def p_inst_1(t): + 'inst : ID DBLCOLON ID LPAREN arg_list RPAREN' + try: + format = formatMap[t[1]] + except KeyError: + error(t.lineno(1), 'instruction format "%s" not defined.' % t[1]) + codeObj = format.defineInst(t[3], t[5], t.lineno(1)) + comment = '\n// %s::%s(%s)\n' % (t[1], t[3], t[5]) + codeObj.prepend_all(comment) + t[0] = codeObj + +# The arg list generates a tuple, where the first element is a list of +# the positional args and the second element is a dict containing the +# keyword args. +def p_arg_list_0(t): + 'arg_list : positional_arg_list COMMA keyword_arg_list' + t[0] = ( t[1], t[3] ) + +def p_arg_list_1(t): + 'arg_list : positional_arg_list' + t[0] = ( t[1], {} ) + +def p_arg_list_2(t): + 'arg_list : keyword_arg_list' + t[0] = ( [], t[1] ) + +def p_positional_arg_list_0(t): + 'positional_arg_list : empty' + t[0] = [] + +def p_positional_arg_list_1(t): + 'positional_arg_list : expr' + t[0] = [t[1]] + +def p_positional_arg_list_2(t): + 'positional_arg_list : positional_arg_list COMMA expr' + t[0] = t[1] + [t[3]] + +def p_keyword_arg_list_0(t): + 'keyword_arg_list : keyword_arg' + t[0] = t[1] + +def p_keyword_arg_list_1(t): + 'keyword_arg_list : keyword_arg_list COMMA keyword_arg' + t[0] = t[1] + t[0].update(t[3]) + +def p_keyword_arg(t): + 'keyword_arg : ID EQUALS expr' + t[0] = { t[1] : t[3] } + +# +# Basic expressions. These constitute the argument values of +# "function calls" (i.e. instruction definitions in the decode block) +# and default values for formal parameters of format functions. +# +# Right now, these are either strings, integers, or (recursively) +# lists of exprs (using Python square-bracket list syntax). Note that +# bare identifiers are trated as string constants here (since there +# isn't really a variable namespace to refer to). +# +def p_expr_0(t): + '''expr : ID + | INTLIT + | STRLIT + | CODELIT''' + t[0] = t[1] + +def p_expr_1(t): + '''expr : LBRACKET list_expr RBRACKET''' + t[0] = t[2] + +def p_list_expr_0(t): + 'list_expr : expr' + t[0] = [t[1]] + +def p_list_expr_1(t): + 'list_expr : list_expr COMMA expr' + t[0] = t[1] + [t[3]] + +def p_list_expr_2(t): + 'list_expr : empty' + t[0] = [] + +# +# Empty production... use in other rules for readability. +# +def p_empty(t): + 'empty :' + pass + +# Parse error handler. Note that the argument here is the offending +# *token*, not a grammar symbol (hence the need to use t.value) +def p_error(t): + if t: + error(t.lineno, "syntax error at '%s'" % t.value) + else: + error(0, "unknown syntax error", True) + +# END OF GRAMMAR RULES +# +# Now build the parser. +yacc.yacc() + + +##################################################################### +# +# Support Classes +# +##################################################################### + +# Expand template with CPU-specific references into a dictionary with +# an entry for each CPU model name. The entry key is the model name +# and the corresponding value is the template with the CPU-specific +# refs substituted for that model. +def expand_cpu_symbols_to_dict(template): + # Protect '%'s that don't go with CPU-specific terms + t = re.sub(r'%(?!\(CPU_)', '%%', template) + result = {} + for cpu in cpu_models: + result[cpu.name] = t % cpu.strings + return result + +# *If* the template has CPU-specific references, return a single +# string containing a copy of the template for each CPU model with the +# corresponding values substituted in. If the template has no +# CPU-specific references, it is returned unmodified. +def expand_cpu_symbols_to_string(template): + if template.find('%(CPU_') != -1: + return reduce(lambda x,y: x+y, + expand_cpu_symbols_to_dict(template).values()) + else: + return template + +# Protect CPU-specific references by doubling the corresponding '%'s +# (in preparation for substituting a different set of references into +# the template). +def protect_cpu_symbols(template): + return re.sub(r'%(?=\(CPU_)', '%%', template) + +############### +# GenCode class +# +# The GenCode class encapsulates generated code destined for various +# output files. The header_output and decoder_output attributes are +# strings containing code destined for decoder.hh and decoder.cc +# respectively. The decode_block attribute contains code to be +# incorporated in the decode function itself (that will also end up in +# decoder.cc). The exec_output attribute is a dictionary with a key +# for each CPU model name; the value associated with a particular key +# is the string of code for that CPU model's exec.cc file. The +# has_decode_default attribute is used in the decode block to allow +# explicit default clauses to override default default clauses. + +class GenCode: + # Constructor. At this point we substitute out all CPU-specific + # symbols. For the exec output, these go into the per-model + # dictionary. For all other output types they get collapsed into + # a single string. + def __init__(self, + header_output = '', decoder_output = '', exec_output = '', + decode_block = '', has_decode_default = False): + self.header_output = expand_cpu_symbols_to_string(header_output) + self.decoder_output = expand_cpu_symbols_to_string(decoder_output) + if isinstance(exec_output, dict): + self.exec_output = exec_output + elif isinstance(exec_output, str): + # If the exec_output arg is a single string, we replicate + # it for each of the CPU models, substituting and + # %(CPU_foo)s params appropriately. + self.exec_output = expand_cpu_symbols_to_dict(exec_output) + self.decode_block = expand_cpu_symbols_to_string(decode_block) + self.has_decode_default = has_decode_default + + # Override '+' operator: generate a new GenCode object that + # concatenates all the individual strings in the operands. + def __add__(self, other): + exec_output = {} + for cpu in cpu_models: + n = cpu.name + exec_output[n] = self.exec_output[n] + other.exec_output[n] + return GenCode(self.header_output + other.header_output, + self.decoder_output + other.decoder_output, + exec_output, + self.decode_block + other.decode_block, + self.has_decode_default or other.has_decode_default) + + # Prepend a string (typically a comment) to all the strings. + def prepend_all(self, pre): + self.header_output = pre + self.header_output + self.decoder_output = pre + self.decoder_output + self.decode_block = pre + self.decode_block + for cpu in cpu_models: + self.exec_output[cpu.name] = pre + self.exec_output[cpu.name] + + # Wrap the decode block in a pair of strings (e.g., 'case foo:' + # and 'break;'). Used to build the big nested switch statement. + def wrap_decode_block(self, pre, post = ''): + self.decode_block = pre + indent(self.decode_block) + post + +################ +# Format object. +# +# A format object encapsulates an instruction format. It must provide +# a defineInst() method that generates the code for an instruction +# definition. + +exportContextSymbols = ('InstObjParams', 'CodeBlock', + 'makeList', 're', 'string') + +exportContext = {} + +def updateExportContext(): + exportContext.update(exportDict(*exportContextSymbols)) + exportContext.update(templateMap) + +def exportDict(*symNames): + return dict([(s, eval(s)) for s in symNames]) + + +class Format: + def __init__(self, id, params, code): + # constructor: just save away arguments + self.id = id + self.params = params + label = 'def format ' + id + self.user_code = compile(fixPythonIndentation(code), label, 'exec') + param_list = string.join(params, ", ") + f = '''def defInst(_code, _context, %s): + my_locals = vars().copy() + exec _code in _context, my_locals + return my_locals\n''' % param_list + c = compile(f, label + ' wrapper', 'exec') + exec c + self.func = defInst + + def defineInst(self, name, args, lineno): + context = {} + updateExportContext() + context.update(exportContext) + context.update({ 'name': name, 'Name': string.capitalize(name) }) + try: + vars = self.func(self.user_code, context, *args[0], **args[1]) + except Exception, exc: + error(lineno, 'error defining "%s": %s.' % (name, exc)) + for k in vars.keys(): + if k not in ('header_output', 'decoder_output', + 'exec_output', 'decode_block'): + del vars[k] + return GenCode(**vars) + +# Special null format to catch an implicit-format instruction +# definition outside of any format block. +class NoFormat: + def __init__(self): + self.defaultInst = '' + + def defineInst(self, name, args, lineno): + error(lineno, + 'instruction definition "%s" with no active format!' % name) + +# This dictionary maps format name strings to Format objects. +formatMap = {} + +# Define a new format +def defFormat(id, params, code, lineno): + # make sure we haven't already defined this one + if formatMap.get(id, None) != None: + error(lineno, 'format %s redefined.' % id) + # create new object and store in global map + formatMap[id] = Format(id, params, code) + + +############## +# Stack: a simple stack object. Used for both formats (formatStack) +# and default cases (defaultStack). Simply wraps a list to give more +# stack-like syntax and enable initialization with an argument list +# (as opposed to an argument that's a list). + +class Stack(list): + def __init__(self, *items): + list.__init__(self, items) + + def push(self, item): + self.append(item); + + def top(self): + return self[-1] + +# The global format stack. +formatStack = Stack(NoFormat()) + +# The global default case stack. +defaultStack = Stack( None ) + +# Global stack that tracks current file and line number. +# Each element is a tuple (filename, lineno) that records the +# *current* filename and the line number in the *previous* file where +# it was included. +fileNameStack = Stack() + +################### +# Utility functions + +# +# Indent every line in string 's' by two spaces +# (except preprocessor directives). +# Used to make nested code blocks look pretty. +# +def indent(s): + return re.sub(r'(?m)^(?!#)', ' ', s) + +# +# Munge a somewhat arbitrarily formatted piece of Python code +# (e.g. from a format 'let' block) into something whose indentation +# will get by the Python parser. +# +# The two keys here are that Python will give a syntax error if +# there's any whitespace at the beginning of the first line, and that +# all lines at the same lexical nesting level must have identical +# indentation. Unfortunately the way code literals work, an entire +# let block tends to have some initial indentation. Rather than +# trying to figure out what that is and strip it off, we prepend 'if +# 1:' to make the let code the nested block inside the if (and have +# the parser automatically deal with the indentation for us). +# +# We don't want to do this if (1) the code block is empty or (2) the +# first line of the block doesn't have any whitespace at the front. + +def fixPythonIndentation(s): + # get rid of blank lines first + s = re.sub(r'(?m)^\s*\n', '', s); + if (s != '' and re.match(r'[ \t]', s[0])): + s = 'if 1:\n' + s + return s + +# Error handler. Just call exit. Output formatted to work under +# Emacs compile-mode. Optional 'print_traceback' arg, if set to True, +# prints a Python stack backtrace too (can be handy when trying to +# debug the parser itself). +def error(lineno, string, print_traceback = False): + spaces = "" + for (filename, line) in fileNameStack[0:-1]: + print spaces + "In file included from " + filename + ":" + spaces += " " + # Print a Python stack backtrace if requested. + if (print_traceback): + traceback.print_exc() + if lineno != 0: + line_str = "%d:" % lineno + else: + line_str = "" + sys.exit(spaces + "%s:%s %s" % (fileNameStack[-1][0], line_str, string)) + + +##################################################################### +# +# Bitfield Operator Support +# +##################################################################### + +bitOp1ArgRE = re.compile(r'<\s*(\w+)\s*:\s*>') + +bitOpWordRE = re.compile(r'(?<![\w\.])([\w\.]+)<\s*(\w+)\s*:\s*(\w+)\s*>') +bitOpExprRE = re.compile(r'\)<\s*(\w+)\s*:\s*(\w+)\s*>') + +def substBitOps(code): + # first convert single-bit selectors to two-index form + # i.e., <n> --> <n:n> + code = bitOp1ArgRE.sub(r'<\1:\1>', code) + # simple case: selector applied to ID (name) + # i.e., foo<a:b> --> bits(foo, a, b) + code = bitOpWordRE.sub(r'bits(\1, \2, \3)', code) + # if selector is applied to expression (ending in ')'), + # we need to search backward for matching '(' + match = bitOpExprRE.search(code) + while match: + exprEnd = match.start() + here = exprEnd - 1 + nestLevel = 1 + while nestLevel > 0: + if code[here] == '(': + nestLevel -= 1 + elif code[here] == ')': + nestLevel += 1 + here -= 1 + if here < 0: + sys.exit("Didn't find '('!") + exprStart = here+1 + newExpr = r'bits(%s, %s, %s)' % (code[exprStart:exprEnd+1], + match.group(1), match.group(2)) + code = code[:exprStart] + newExpr + code[match.end():] + match = bitOpExprRE.search(code) + return code + + +#################### +# Template objects. +# +# Template objects are format strings that allow substitution from +# the attribute spaces of other objects (e.g. InstObjParams instances). + +class Template: + def __init__(self, t): + self.template = t + + def subst(self, d): + # Start with the template namespace. Make a copy since we're + # going to modify it. + myDict = templateMap.copy() + # if the argument is a dictionary, we just use it. + if isinstance(d, dict): + myDict.update(d) + # if the argument is an object, we use its attribute map. + elif hasattr(d, '__dict__'): + myDict.update(d.__dict__) + else: + raise TypeError, "Template.subst() arg must be or have dictionary" + # Protect non-Python-dict substitutions (e.g. if there's a printf + # in the templated C++ code) + template = protect_non_subst_percents(self.template) + # CPU-model-specific substitutions are handled later (in GenCode). + template = protect_cpu_symbols(template) + return template % myDict + + # Convert to string. This handles the case when a template with a + # CPU-specific term gets interpolated into another template or into + # an output block. + def __str__(self): + return expand_cpu_symbols_to_string(self.template) + +##################################################################### +# +# Code Parser +# +# The remaining code is the support for automatically extracting +# instruction characteristics from pseudocode. +# +##################################################################### + +# Force the argument to be a list. Useful for flags, where a caller +# can specify a singleton flag or a list of flags. Also usful for +# converting tuples to lists so they can be modified. +def makeList(arg): + if isinstance(arg, list): + return arg + elif isinstance(arg, tuple): + return list(arg) + elif not arg: + return [] + else: + return [ arg ] + +# Generate operandTypeMap from the user's 'def operand_types' +# statement. +def buildOperandTypeMap(userDict, lineno): + global operandTypeMap + operandTypeMap = {} + for (ext, (desc, size)) in userDict.iteritems(): + if desc == 'signed int': + ctype = 'int%d_t' % size + is_signed = 1 + elif desc == 'unsigned int': + ctype = 'uint%d_t' % size + is_signed = 0 + elif desc == 'float': + is_signed = 1 # shouldn't really matter + if size == 32: + ctype = 'float' + elif size == 64: + ctype = 'double' + if ctype == '': + error(lineno, 'Unrecognized type description "%s" in userDict') + operandTypeMap[ext] = (size, ctype, is_signed) + +# +# +# +# Base class for operand descriptors. An instance of this class (or +# actually a class derived from this one) represents a specific +# operand for a code block (e.g, "Rc.sq" as a dest). Intermediate +# derived classes encapsulates the traits of a particular operand type +# (e.g., "32-bit integer register"). +# +class Operand(object): + def __init__(self, full_name, ext, is_src, is_dest): + self.full_name = full_name + self.ext = ext + self.is_src = is_src + self.is_dest = is_dest + # The 'effective extension' (eff_ext) is either the actual + # extension, if one was explicitly provided, or the default. + if ext: + self.eff_ext = ext + else: + self.eff_ext = self.dflt_ext + + (self.size, self.ctype, self.is_signed) = operandTypeMap[self.eff_ext] + + # note that mem_acc_size is undefined for non-mem operands... + # template must be careful not to use it if it doesn't apply. + if self.isMem(): + self.mem_acc_size = self.makeAccSize() + self.mem_acc_type = self.ctype + + # Finalize additional fields (primarily code fields). This step + # is done separately since some of these fields may depend on the + # register index enumeration that hasn't been performed yet at the + # time of __init__(). + def finalize(self): + self.flags = self.getFlags() + self.constructor = self.makeConstructor() + self.op_decl = self.makeDecl() + + if self.is_src: + self.op_rd = self.makeRead() + self.op_src_decl = self.makeDecl() + else: + self.op_rd = '' + self.op_src_decl = '' + + if self.is_dest: + self.op_wb = self.makeWrite() + self.op_dest_decl = self.makeDecl() + else: + self.op_wb = '' + self.op_dest_decl = '' + + def isMem(self): + return 0 + + def isReg(self): + return 0 + + def isFloatReg(self): + return 0 + + def isIntReg(self): + return 0 + + def isControlReg(self): + return 0 + + def getFlags(self): + # note the empty slice '[:]' gives us a copy of self.flags[0] + # instead of a reference to it + my_flags = self.flags[0][:] + if self.is_src: + my_flags += self.flags[1] + if self.is_dest: + my_flags += self.flags[2] + return my_flags + + def makeDecl(self): + # Note that initializations in the declarations are solely + # to avoid 'uninitialized variable' errors from the compiler. + return self.ctype + ' ' + self.base_name + ' = 0;\n'; + +class IntRegOperand(Operand): + def isReg(self): + return 1 + + def isIntReg(self): + return 1 + + def makeConstructor(self): + c = '' + if self.is_src: + c += '\n\t_srcRegIdx[%d] = %s;' % \ + (self.src_reg_idx, self.reg_spec) + if self.is_dest: + c += '\n\t_destRegIdx[%d] = %s;' % \ + (self.dest_reg_idx, self.reg_spec) + return c + + def makeRead(self): + if (self.ctype == 'float' or self.ctype == 'double'): + error(0, 'Attempt to read integer register as FP') + if (self.size == self.dflt_size): + return '%s = xc->readIntReg(this, %d);\n' % \ + (self.base_name, self.src_reg_idx) + elif (self.size > self.dflt_size): + int_reg_val = 'xc->readIntReg(this, %d)' % (self.src_reg_idx) + if (self.is_signed): + int_reg_val = 'sext<%d>(%s)' % (self.dflt_size, int_reg_val) + return '%s = %s;\n' % (self.base_name, int_reg_val) + else: + return '%s = bits(xc->readIntReg(this, %d), %d, 0);\n' % \ + (self.base_name, self.src_reg_idx, self.size-1) + + def makeWrite(self): + if (self.ctype == 'float' or self.ctype == 'double'): + error(0, 'Attempt to write integer register as FP') + if (self.size != self.dflt_size and self.is_signed): + final_val = 'sext<%d>(%s)' % (self.size, self.base_name) + else: + final_val = self.base_name + wb = ''' + { + %s final_val = %s; + xc->setIntReg(this, %d, final_val);\n + if (traceData) { traceData->setData(final_val); } + }''' % (self.dflt_ctype, final_val, self.dest_reg_idx) + return wb + +class FloatRegOperand(Operand): + def isReg(self): + return 1 + + def isFloatReg(self): + return 1 + + def makeConstructor(self): + c = '' + if self.is_src: + c += '\n\t_srcRegIdx[%d] = %s + FP_Base_DepTag;' % \ + (self.src_reg_idx, self.reg_spec) + if self.is_dest: + c += '\n\t_destRegIdx[%d] = %s + FP_Base_DepTag;' % \ + (self.dest_reg_idx, self.reg_spec) + return c + + def makeRead(self): + bit_select = 0 + width = 0; + if (self.ctype == 'float'): + func = 'readFloatReg' + width = 32; + elif (self.ctype == 'double'): + func = 'readFloatReg' + width = 64; + else: + func = 'readFloatRegBits' + if (self.ctype == 'uint32_t'): + width = 32; + elif (self.ctype == 'uint64_t'): + width = 64; + if (self.size != self.dflt_size): + bit_select = 1 + if width: + base = 'xc->%s(this, %d, %d)' % \ + (func, self.src_reg_idx, width) + else: + base = 'xc->%s(this, %d)' % \ + (func, self.src_reg_idx) + if bit_select: + return '%s = bits(%s, %d, 0);\n' % \ + (self.base_name, base, self.size-1) + else: + return '%s = %s;\n' % (self.base_name, base) + + def makeWrite(self): + final_val = self.base_name + final_ctype = self.ctype + widthSpecifier = '' + width = 0 + if (self.ctype == 'float'): + width = 32 + func = 'setFloatReg' + elif (self.ctype == 'double'): + width = 64 + func = 'setFloatReg' + elif (self.ctype == 'uint32_t'): + func = 'setFloatRegBits' + width = 32 + elif (self.ctype == 'uint64_t'): + func = 'setFloatRegBits' + width = 64 + else: + func = 'setFloatRegBits' + final_ctype = 'uint%d_t' % self.dflt_size + if (self.size != self.dflt_size and self.is_signed): + final_val = 'sext<%d>(%s)' % (self.size, self.base_name) + if width: + widthSpecifier = ', %d' % width + wb = ''' + { + %s final_val = %s; + xc->%s(this, %d, final_val%s);\n + if (traceData) { traceData->setData(final_val); } + }''' % (final_ctype, final_val, func, self.dest_reg_idx, + widthSpecifier) + return wb + +class ControlRegOperand(Operand): + def isReg(self): + return 1 + + def isControlReg(self): + return 1 + + def makeConstructor(self): + c = '' + if self.is_src: + c += '\n\t_srcRegIdx[%d] = %s;' % \ + (self.src_reg_idx, self.reg_spec) + if self.is_dest: + c += '\n\t_destRegIdx[%d] = %s;' % \ + (self.dest_reg_idx, self.reg_spec) + return c + + def makeRead(self): + bit_select = 0 + if (self.ctype == 'float' or self.ctype == 'double'): + error(0, 'Attempt to read control register as FP') + base = 'xc->readMiscReg(%s)' % self.reg_spec + if self.size == self.dflt_size: + return '%s = %s;\n' % (self.base_name, base) + else: + return '%s = bits(%s, %d, 0);\n' % \ + (self.base_name, base, self.size-1) + + def makeWrite(self): + if (self.ctype == 'float' or self.ctype == 'double'): + error(0, 'Attempt to write control register as FP') + wb = 'xc->setMiscReg(%s, %s);\n' % (self.reg_spec, self.base_name) + wb += 'if (traceData) { traceData->setData(%s); }' % \ + self.base_name + return wb + +class MemOperand(Operand): + def isMem(self): + return 1 + + def makeConstructor(self): + return '' + + def makeDecl(self): + # Note that initializations in the declarations are solely + # to avoid 'uninitialized variable' errors from the compiler. + # Declare memory data variable. + c = '%s %s = 0;\n' % (self.ctype, self.base_name) + return c + + def makeRead(self): + return '' + + def makeWrite(self): + return '' + + # Return the memory access size *in bits*, suitable for + # forming a type via "uint%d_t". Divide by 8 if you want bytes. + def makeAccSize(self): + return self.size + + +class NPCOperand(Operand): + def makeConstructor(self): + return '' + + def makeRead(self): + return '%s = xc->readNextPC();\n' % self.base_name + + def makeWrite(self): + return 'xc->setNextPC(%s);\n' % self.base_name + +class NNPCOperand(Operand): + def makeConstructor(self): + return '' + + def makeRead(self): + return '%s = xc->readNextNPC();\n' % self.base_name + + def makeWrite(self): + return 'xc->setNextNPC(%s);\n' % self.base_name + +def buildOperandNameMap(userDict, lineno): + global operandNameMap + operandNameMap = {} + for (op_name, val) in userDict.iteritems(): + (base_cls_name, dflt_ext, reg_spec, flags, sort_pri) = val + (dflt_size, dflt_ctype, dflt_is_signed) = operandTypeMap[dflt_ext] + # Canonical flag structure is a triple of lists, where each list + # indicates the set of flags implied by this operand always, when + # used as a source, and when used as a dest, respectively. + # For simplicity this can be initialized using a variety of fairly + # obvious shortcuts; we convert these to canonical form here. + if not flags: + # no flags specified (e.g., 'None') + flags = ( [], [], [] ) + elif isinstance(flags, str): + # a single flag: assumed to be unconditional + flags = ( [ flags ], [], [] ) + elif isinstance(flags, list): + # a list of flags: also assumed to be unconditional + flags = ( flags, [], [] ) + elif isinstance(flags, tuple): + # it's a tuple: it should be a triple, + # but each item could be a single string or a list + (uncond_flags, src_flags, dest_flags) = flags + flags = (makeList(uncond_flags), + makeList(src_flags), makeList(dest_flags)) + # Accumulate attributes of new operand class in tmp_dict + tmp_dict = {} + for attr in ('dflt_ext', 'reg_spec', 'flags', 'sort_pri', + 'dflt_size', 'dflt_ctype', 'dflt_is_signed'): + tmp_dict[attr] = eval(attr) + tmp_dict['base_name'] = op_name + # New class name will be e.g. "IntReg_Ra" + cls_name = base_cls_name + '_' + op_name + # Evaluate string arg to get class object. Note that the + # actual base class for "IntReg" is "IntRegOperand", i.e. we + # have to append "Operand". + try: + base_cls = eval(base_cls_name + 'Operand') + except NameError: + error(lineno, + 'error: unknown operand base class "%s"' % base_cls_name) + # The following statement creates a new class called + # <cls_name> as a subclass of <base_cls> with the attributes + # in tmp_dict, just as if we evaluated a class declaration. + operandNameMap[op_name] = type(cls_name, (base_cls,), tmp_dict) + + # Define operand variables. + operands = userDict.keys() + + operandsREString = (r''' + (?<![\w\.]) # neg. lookbehind assertion: prevent partial matches + ((%s)(?:\.(\w+))?) # match: operand with optional '.' then suffix + (?![\w\.]) # neg. lookahead assertion: prevent partial matches + ''' + % string.join(operands, '|')) + + global operandsRE + operandsRE = re.compile(operandsREString, re.MULTILINE|re.VERBOSE) + + # Same as operandsREString, but extension is mandatory, and only two + # groups are returned (base and ext, not full name as above). + # Used for subtituting '_' for '.' to make C++ identifiers. + operandsWithExtREString = (r'(?<![\w\.])(%s)\.(\w+)(?![\w\.])' + % string.join(operands, '|')) + + global operandsWithExtRE + operandsWithExtRE = re.compile(operandsWithExtREString, re.MULTILINE) + + +class OperandList: + + # Find all the operands in the given code block. Returns an operand + # descriptor list (instance of class OperandList). + def __init__(self, code): + self.items = [] + self.bases = {} + # delete comments so we don't match on reg specifiers inside + code = commentRE.sub('', code) + # search for operands + next_pos = 0 + while 1: + match = operandsRE.search(code, next_pos) + if not match: + # no more matches: we're done + break + op = match.groups() + # regexp groups are operand full name, base, and extension + (op_full, op_base, op_ext) = op + # if the token following the operand is an assignment, this is + # a destination (LHS), else it's a source (RHS) + is_dest = (assignRE.match(code, match.end()) != None) + is_src = not is_dest + # see if we've already seen this one + op_desc = self.find_base(op_base) + if op_desc: + if op_desc.ext != op_ext: + error(0, 'Inconsistent extensions for operand %s' % \ + op_base) + op_desc.is_src = op_desc.is_src or is_src + op_desc.is_dest = op_desc.is_dest or is_dest + else: + # new operand: create new descriptor + op_desc = operandNameMap[op_base](op_full, op_ext, + is_src, is_dest) + self.append(op_desc) + # start next search after end of current match + next_pos = match.end() + self.sort() + # enumerate source & dest register operands... used in building + # constructor later + self.numSrcRegs = 0 + self.numDestRegs = 0 + self.numFPDestRegs = 0 + self.numIntDestRegs = 0 + self.memOperand = None + for op_desc in self.items: + if op_desc.isReg(): + if op_desc.is_src: + op_desc.src_reg_idx = self.numSrcRegs + self.numSrcRegs += 1 + if op_desc.is_dest: + op_desc.dest_reg_idx = self.numDestRegs + self.numDestRegs += 1 + if op_desc.isFloatReg(): + self.numFPDestRegs += 1 + elif op_desc.isIntReg(): + self.numIntDestRegs += 1 + elif op_desc.isMem(): + if self.memOperand: + error(0, "Code block has more than one memory operand.") + self.memOperand = op_desc + # now make a final pass to finalize op_desc fields that may depend + # on the register enumeration + for op_desc in self.items: + op_desc.finalize() + + def __len__(self): + return len(self.items) + + def __getitem__(self, index): + return self.items[index] + + def append(self, op_desc): + self.items.append(op_desc) + self.bases[op_desc.base_name] = op_desc + + def find_base(self, base_name): + # like self.bases[base_name], but returns None if not found + # (rather than raising exception) + return self.bases.get(base_name) + + # internal helper function for concat[Some]Attr{Strings|Lists} + def __internalConcatAttrs(self, attr_name, filter, result): + for op_desc in self.items: + if filter(op_desc): + result += getattr(op_desc, attr_name) + return result + + # return a single string that is the concatenation of the (string) + # values of the specified attribute for all operands + def concatAttrStrings(self, attr_name): + return self.__internalConcatAttrs(attr_name, lambda x: 1, '') + + # like concatAttrStrings, but only include the values for the operands + # for which the provided filter function returns true + def concatSomeAttrStrings(self, filter, attr_name): + return self.__internalConcatAttrs(attr_name, filter, '') + + # return a single list that is the concatenation of the (list) + # values of the specified attribute for all operands + def concatAttrLists(self, attr_name): + return self.__internalConcatAttrs(attr_name, lambda x: 1, []) + + # like concatAttrLists, but only include the values for the operands + # for which the provided filter function returns true + def concatSomeAttrLists(self, filter, attr_name): + return self.__internalConcatAttrs(attr_name, filter, []) + + def sort(self): + self.items.sort(lambda a, b: a.sort_pri - b.sort_pri) + +# Regular expression object to match C++ comments +# (used in findOperands()) +commentRE = re.compile(r'//.*\n') + +# Regular expression object to match assignment statements +# (used in findOperands()) +assignRE = re.compile(r'\s*=(?!=)', re.MULTILINE) + +# Munge operand names in code string to make legal C++ variable names. +# This means getting rid of the type extension if any. +# (Will match base_name attribute of Operand object.) +def substMungedOpNames(code): + return operandsWithExtRE.sub(r'\1', code) + +def joinLists(t): + return map(string.join, t) + +def makeFlagConstructor(flag_list): + if len(flag_list) == 0: + return '' + # filter out repeated flags + flag_list.sort() + i = 1 + while i < len(flag_list): + if flag_list[i] == flag_list[i-1]: + del flag_list[i] + else: + i += 1 + pre = '\n\tflags[' + post = '] = true;' + code = pre + string.join(flag_list, post + pre) + post + return code + +class CodeBlock: + def __init__(self, code): + self.orig_code = code + self.operands = OperandList(code) + self.code = substMungedOpNames(substBitOps(code)) + self.constructor = self.operands.concatAttrStrings('constructor') + self.constructor += \ + '\n\t_numSrcRegs = %d;' % self.operands.numSrcRegs + self.constructor += \ + '\n\t_numDestRegs = %d;' % self.operands.numDestRegs + self.constructor += \ + '\n\t_numFPDestRegs = %d;' % self.operands.numFPDestRegs + self.constructor += \ + '\n\t_numIntDestRegs = %d;' % self.operands.numIntDestRegs + + self.op_decl = self.operands.concatAttrStrings('op_decl') + + is_src = lambda op: op.is_src + is_dest = lambda op: op.is_dest + + self.op_src_decl = \ + self.operands.concatSomeAttrStrings(is_src, 'op_src_decl') + self.op_dest_decl = \ + self.operands.concatSomeAttrStrings(is_dest, 'op_dest_decl') + + self.op_rd = self.operands.concatAttrStrings('op_rd') + self.op_wb = self.operands.concatAttrStrings('op_wb') + + self.flags = self.operands.concatAttrLists('flags') + + if self.operands.memOperand: + self.mem_acc_size = self.operands.memOperand.mem_acc_size + self.mem_acc_type = self.operands.memOperand.mem_acc_type + + # Make a basic guess on the operand class (function unit type). + # These are good enough for most cases, and will be overridden + # later otherwise. + if 'IsStore' in self.flags: + self.op_class = 'MemWriteOp' + elif 'IsLoad' in self.flags or 'IsPrefetch' in self.flags: + self.op_class = 'MemReadOp' + elif 'IsFloating' in self.flags: + self.op_class = 'FloatAddOp' + else: + self.op_class = 'IntAluOp' + +# Assume all instruction flags are of the form 'IsFoo' +instFlagRE = re.compile(r'Is.*') + +# OpClass constants end in 'Op' except No_OpClass +opClassRE = re.compile(r'.*Op|No_OpClass') + +class InstObjParams: + def __init__(self, mnem, class_name, base_class = '', + code = None, opt_args = [], *extras): + self.mnemonic = mnem + self.class_name = class_name + self.base_class = base_class + if code: + #If the user already made a CodeBlock, pick the parts from it + if isinstance(code, CodeBlock): + origCode = code.orig_code + codeBlock = code + else: + origCode = code + codeBlock = CodeBlock(code) + compositeCode = '\n'.join([origCode] + + [pair[1] for pair in extras]) + compositeBlock = CodeBlock(compositeCode) + for code_attr in compositeBlock.__dict__.keys(): + setattr(self, code_attr, getattr(compositeBlock, code_attr)) + for (key, snippet) in extras: + setattr(self, key, CodeBlock(snippet).code) + self.code = codeBlock.code + self.orig_code = origCode + else: + self.constructor = '' + self.flags = [] + # Optional arguments are assumed to be either StaticInst flags + # or an OpClass value. To avoid having to import a complete + # list of these values to match against, we do it ad-hoc + # with regexps. + for oa in opt_args: + if instFlagRE.match(oa): + self.flags.append(oa) + elif opClassRE.match(oa): + self.op_class = oa + else: + error(0, 'InstObjParams: optional arg "%s" not recognized ' + 'as StaticInst::Flag or OpClass.' % oa) + + # add flag initialization to contructor here to include + # any flags added via opt_args + self.constructor += makeFlagConstructor(self.flags) + + # if 'IsFloating' is set, add call to the FP enable check + # function (which should be provided by isa_desc via a declare) + if 'IsFloating' in self.flags: + self.fp_enable_check = 'fault = checkFpEnableFault(xc);' + else: + self.fp_enable_check = '' + +####################### +# +# Output file template +# + +file_template = ''' +/* + * DO NOT EDIT THIS FILE!!! + * + * It was automatically generated from the ISA description in %(filename)s + */ + +%(includes)s + +%(global_output)s + +namespace %(namespace)s { + +%(namespace_output)s + +} // namespace %(namespace)s + +%(decode_function)s +''' + + +# Update the output file only if the new contents are different from +# the current contents. Minimizes the files that need to be rebuilt +# after minor changes. +def update_if_needed(file, contents): + update = False + if os.access(file, os.R_OK): + f = open(file, 'r') + old_contents = f.read() + f.close() + if contents != old_contents: + print 'Updating', file + os.remove(file) # in case it's write-protected + update = True + else: + print 'File', file, 'is unchanged' + else: + print 'Generating', file + update = True + if update: + f = open(file, 'w') + f.write(contents) + f.close() + +# This regular expression matches '##include' directives +includeRE = re.compile(r'^\s*##include\s+"(?P<filename>[\w/.-]*)".*$', + re.MULTILINE) + +# Function to replace a matched '##include' directive with the +# contents of the specified file (with nested ##includes replaced +# recursively). 'matchobj' is an re match object (from a match of +# includeRE) and 'dirname' is the directory relative to which the file +# path should be resolved. +def replace_include(matchobj, dirname): + fname = matchobj.group('filename') + full_fname = os.path.normpath(os.path.join(dirname, fname)) + contents = '##newfile "%s"\n%s\n##endfile\n' % \ + (full_fname, read_and_flatten(full_fname)) + return contents + +# Read a file and recursively flatten nested '##include' files. +def read_and_flatten(filename): + current_dir = os.path.dirname(filename) + try: + contents = open(filename).read() + except IOError: + error(0, 'Error including file "%s"' % filename) + fileNameStack.push((filename, 0)) + # Find any includes and include them + contents = includeRE.sub(lambda m: replace_include(m, current_dir), + contents) + fileNameStack.pop() + return contents + +# +# Read in and parse the ISA description. +# +def parse_isa_desc(isa_desc_file, output_dir): + # Read file and (recursively) all included files into a string. + # PLY requires that the input be in a single string so we have to + # do this up front. + isa_desc = read_and_flatten(isa_desc_file) + + # Initialize filename stack with outer file. + fileNameStack.push((isa_desc_file, 0)) + + # Parse it. + (isa_name, namespace, global_code, namespace_code) = yacc.parse(isa_desc) + + # grab the last three path components of isa_desc_file to put in + # the output + filename = '/'.join(isa_desc_file.split('/')[-3:]) + + # generate decoder.hh + includes = '#include "base/bitfield.hh" // for bitfield support' + global_output = global_code.header_output + namespace_output = namespace_code.header_output + decode_function = '' + update_if_needed(output_dir + '/decoder.hh', file_template % vars()) + + # generate decoder.cc + includes = '#include "decoder.hh"' + global_output = global_code.decoder_output + namespace_output = namespace_code.decoder_output + # namespace_output += namespace_code.decode_block + decode_function = namespace_code.decode_block + update_if_needed(output_dir + '/decoder.cc', file_template % vars()) + + # generate per-cpu exec files + for cpu in cpu_models: + includes = '#include "decoder.hh"\n' + includes += cpu.includes + global_output = global_code.exec_output[cpu.name] + namespace_output = namespace_code.exec_output[cpu.name] + decode_function = '' + update_if_needed(output_dir + '/' + cpu.filename, + file_template % vars()) + +# global list of CpuModel objects (see cpu_models.py) +cpu_models = [] + +# Called as script: get args from command line. +# Args are: <path to cpu_models.py> <isa desc file> <output dir> <cpu models> +if __name__ == '__main__': + execfile(sys.argv[1]) # read in CpuModel definitions + cpu_models = [CpuModel.dict[cpu] for cpu in sys.argv[4:]] + parse_isa_desc(sys.argv[2], sys.argv[3]) diff --git a/src/arch/isa_specific.hh b/src/arch/isa_specific.hh new file mode 100644 index 000000000..181e81302 --- /dev/null +++ b/src/arch/isa_specific.hh @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + */ + +#ifndef __ARCH_ISA_SPECIFIC_HH__ +#define __ARCH_ISA_SPECIFIC_HH__ + +//This file provides a mechanism for other source code to bring in +//files from the ISA being compiled with + +//These are constants so you can selective compile code based on the isa +//To use them, do something like +// +//#if THE_ISA == YOUR_FAVORITE_ISA +// conditional_code +//#endif +// +//Note that this is how this file sets up the other isa "hooks" + +//These macros have numerical values because otherwise the preprocessor +//would treat them as 0 in comparisons. +#define ALPHA_ISA 21064 +#define SPARC_ISA 42 +#define MIPS_ISA 34000 + +//These tell the preprocessor where to find the files of a particular +//ISA, and set the "TheISA" macro for use elsewhere. +#if THE_ISA == ALPHA_ISA + #define TheISA AlphaISA +#elif THE_ISA == SPARC_ISA + #define TheISA SparcISA +#elif THE_ISA == MIPS_ISA + #define TheISA MipsISA +#else + #error "THE_ISA not set" +#endif + +#endif diff --git a/src/arch/mips/SConscript b/src/arch/mips/SConscript new file mode 100644 index 000000000..8353bcde7 --- /dev/null +++ b/src/arch/mips/SConscript @@ -0,0 +1,83 @@ +# -*- mode:python -*- + +# Copyright (c) 2004-2005 The Regents of The University of Michigan +# 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. +# +# Authors: Gabe Black +# Steve Reinhardt +# Korey Sewell + +import os +import sys +from os.path import isdir + +# Import build environment variable from SConstruct. +Import('env') + +################################################### +# +# Define needed sources. +# +################################################### + +# Base sources used by all configurations. +base_sources = Split(''' + faults.cc + isa_traits.cc + utility.cc + ''') + +# Full-system sources +full_system_sources = Split(''' + #Insert Full-System Files Here + ''') + +# Syscall emulation (non-full-system) sources +syscall_emulation_sources = Split(''' + linux/linux.cc + linux/process.cc + process.cc + ''') + +# Set up complete list of sources based on configuration. +sources = base_sources + +if env['FULL_SYSTEM']: + sources += full_system_sources +else: + sources += syscall_emulation_sources + +# Convert file names to SCons File objects. This takes care of the +# path relative to the top of the directory tree. +sources = [File(s) for s in sources] + +# Add in files generated by the ISA description. +isa_desc_files = env.ISADesc('isa/main.isa') +# Only non-header files need to be compiled. +isa_desc_sources = [f for f in isa_desc_files if not f.path.endswith('.hh')] +sources += isa_desc_sources + +Return('sources') diff --git a/src/arch/mips/faults.cc b/src/arch/mips/faults.cc new file mode 100644 index 000000000..2a8ab1df5 --- /dev/null +++ b/src/arch/mips/faults.cc @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Korey Sewell + */ + +#include "arch/mips/faults.hh" +#include "cpu/thread_context.hh" +#include "cpu/base.hh" +#include "base/trace.hh" + +#if !FULL_SYSTEM +#include "sim/process.hh" +#include "mem/page_table.hh" +#endif + +namespace MipsISA +{ + +FaultName MachineCheckFault::_name = "Machine Check"; +FaultVect MachineCheckFault::_vect = 0x0401; +FaultStat MachineCheckFault::_count; + +FaultName AlignmentFault::_name = "Alignment"; +FaultVect AlignmentFault::_vect = 0x0301; +FaultStat AlignmentFault::_count; + +FaultName ResetFault::_name = "reset"; +FaultVect ResetFault::_vect = 0x0001; +FaultStat ResetFault::_count; + +FaultName ArithmeticFault::_name = "arith"; +FaultVect ArithmeticFault::_vect = 0x0501; +FaultStat ArithmeticFault::_count; + +#if !FULL_SYSTEM +FaultName PageTableFault::_name = "page_table_fault"; +FaultVect PageTableFault::_vect = 0x0000; +FaultStat PageTableFault::_count; +#endif + +FaultName InterruptFault::_name = "interrupt"; +FaultVect InterruptFault::_vect = 0x0101; +FaultStat InterruptFault::_count; + +FaultName NDtbMissFault::_name = "dtb_miss_single"; +FaultVect NDtbMissFault::_vect = 0x0201; +FaultStat NDtbMissFault::_count; + +FaultName PDtbMissFault::_name = "dtb_miss_double"; +FaultVect PDtbMissFault::_vect = 0x0281; +FaultStat PDtbMissFault::_count; + +FaultName DtbPageFault::_name = "dfault"; +FaultVect DtbPageFault::_vect = 0x0381; +FaultStat DtbPageFault::_count; + +FaultName DtbAcvFault::_name = "dfault"; +FaultVect DtbAcvFault::_vect = 0x0381; +FaultStat DtbAcvFault::_count; + +FaultName ItbMissFault::_name = "itbmiss"; +FaultVect ItbMissFault::_vect = 0x0181; +FaultStat ItbMissFault::_count; + +FaultName ItbPageFault::_name = "itbmiss"; +FaultVect ItbPageFault::_vect = 0x0181; +FaultStat ItbPageFault::_count; + +FaultName ItbAcvFault::_name = "iaccvio"; +FaultVect ItbAcvFault::_vect = 0x0081; +FaultStat ItbAcvFault::_count; + +FaultName UnimplementedOpcodeFault::_name = "opdec"; +FaultVect UnimplementedOpcodeFault::_vect = 0x0481; +FaultStat UnimplementedOpcodeFault::_count; + +FaultName FloatEnableFault::_name = "fen"; +FaultVect FloatEnableFault::_vect = 0x0581; +FaultStat FloatEnableFault::_count; + +FaultName PalFault::_name = "pal"; +FaultVect PalFault::_vect = 0x2001; +FaultStat PalFault::_count; + +FaultName IntegerOverflowFault::_name = "intover"; +FaultVect IntegerOverflowFault::_vect = 0x0501; +FaultStat IntegerOverflowFault::_count; + +void PageTableFault::invoke(ThreadContext *tc) +{ + Process *p = tc->getProcessPtr(); + + // address is higher than the stack region or in the current stack region + if (vaddr > p->stack_base || vaddr > p->stack_min) + FaultBase::invoke(tc); + + // We've accessed the next page + if (vaddr > p->stack_min - PageBytes) { + p->stack_min -= PageBytes; + if (p->stack_base - p->stack_min > 8*1024*1024) + fatal("Over max stack size for one thread\n"); + p->pTable->allocate(p->stack_min, PageBytes); + warn("Increasing stack size by one page."); + } else { + FaultBase::invoke(tc); + } +} + +} // namespace MipsISA + diff --git a/src/arch/mips/faults.hh b/src/arch/mips/faults.hh new file mode 100644 index 000000000..9d2c5df32 --- /dev/null +++ b/src/arch/mips/faults.hh @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Korey Sewell + */ + +#ifndef __MIPS_FAULTS_HH__ +#define __MIPS_FAULTS_HH__ + +#include "sim/faults.hh" + +// The design of the "name" and "vect" functions is in sim/faults.hh + +namespace MipsISA +{ + +typedef const Addr FaultVect; + +class MipsFault : public FaultBase +{ + protected: + virtual bool skipFaultingInstruction() {return false;} + virtual bool setRestartAddress() {return true;} + public: +#if FULL_SYSTEM + void invoke(ThreadContext * tc); +#endif + virtual FaultVect vect() = 0; + virtual FaultStat & countStat() = 0; +}; + +class MachineCheckFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} + bool isMachineCheckFault() {return true;} +}; + +class AlignmentFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} + bool isAlignmentFault() {return true;} +}; + +#if !FULL_SYSTEM +class PageTableFault : public MipsFault +{ + private: + Addr vaddr; + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + PageTableFault(Addr va) + : vaddr(va) {} + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} + void invoke(ThreadContext * tc); +}; + +static inline Fault genPageTableFault(Addr va) +{ + return new PageTableFault(va); +} +#endif + + +static inline Fault genMachineCheckFault() +{ + return new MachineCheckFault; +} + +static inline Fault genAlignmentFault() +{ + return new AlignmentFault; +} + +class ResetFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class ArithmeticFault : public MipsFault +{ + protected: + bool skipFaultingInstruction() {return true;} + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +#if FULL_SYSTEM + void invoke(ThreadContext * tc); +#endif +}; + +class InterruptFault : public MipsFault +{ + protected: + bool setRestartAddress() {return false;} + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class NDtbMissFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class PDtbMissFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class DtbPageFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class DtbAcvFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class ItbMissFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class ItbPageFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class ItbAcvFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class UnimplementedOpcodeFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class FloatEnableFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class PalFault : public MipsFault +{ + protected: + bool skipFaultingInstruction() {return true;} + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +class IntegerOverflowFault : public MipsFault +{ + private: + static FaultName _name; + static FaultVect _vect; + static FaultStat _count; + public: + FaultName name() {return _name;} + FaultVect vect() {return _vect;} + FaultStat & countStat() {return _count;} +}; + +} // MipsISA namespace + +#endif // __FAULTS_HH__ diff --git a/src/arch/mips/isa/base.isa b/src/arch/mips/isa/base.isa new file mode 100644 index 000000000..7c042f16f --- /dev/null +++ b/src/arch/mips/isa/base.isa @@ -0,0 +1,111 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Base class for MIPS instructions, and some support functions +// + +//Outputs to decoder.hh +output header {{ + + using namespace MipsISA; + + /** + * Base class for all MIPS static instructions. + */ + class MipsStaticInst : public StaticInst + { + protected: + + // Constructor + MipsStaticInst(const char *mnem, MachInst _machInst, OpClass __opClass) + : StaticInst(mnem, _machInst, __opClass) + { + } + + /// Print a register name for disassembly given the unique + /// dependence tag number (FP or int). + void printReg(std::ostream &os, int reg) const; + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + +}}; + +//Ouputs to decoder.cc +output decoder {{ + + void MipsStaticInst::printReg(std::ostream &os, int reg) const + { + if (reg < FP_Base_DepTag) { + ccprintf(os, "r%d", reg); + } + else { + ccprintf(os, "f%d", reg - FP_Base_DepTag); + } + } + + std::string MipsStaticInst::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + // Need to find standard way to not print + // this info. Maybe add bool variable to + // class? + if (mnemonic != "syscall") { + if(_numDestRegs > 0){ + printReg(ss, _destRegIdx[0]); + } + + if(_numSrcRegs > 0) { + ss << ", "; + printReg(ss, _srcRegIdx[0]); + } + + if(_numSrcRegs > 1) { + ss << ", "; + printReg(ss, _srcRegIdx[1]); + } + } + + // Should we define a separate inst. class + // just for two insts? + if(mnemonic == "sll" || mnemonic == "sra"){ + ccprintf(ss,", %d",SA); + } + + return ss.str(); + } + +}}; + diff --git a/src/arch/mips/isa/bitfields.isa b/src/arch/mips/isa/bitfields.isa new file mode 100644 index 000000000..35815bf1f --- /dev/null +++ b/src/arch/mips/isa/bitfields.isa @@ -0,0 +1,102 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Bitfield definitions. +// + +def bitfield OPCODE <31:26>; +def bitfield OPCODE_HI <31:29>; +def bitfield OPCODE_LO <28:26>; + +def bitfield REGIMM <20:16>; +def bitfield REGIMM_HI <20:19>; +def bitfield REGIMM_LO <18:16>; + +def bitfield FUNCTION < 5: 0>; +def bitfield FUNCTION_HI < 5: 3>; +def bitfield FUNCTION_LO < 2: 0>; + +def bitfield RS <25:21>; +def bitfield RS_MSB <25:25>; +def bitfield RS_HI <25:24>; +def bitfield RS_LO <23:21>; +def bitfield RS_SRL <25:22>; +def bitfield RS_RT <25:16>; +def bitfield RT <20:16>; +def bitfield RT_HI <20:19>; +def bitfield RT_LO <18:16>; +def bitfield RT_RD <20:11>; +def bitfield RD <15:11>; + +def bitfield INTIMM <15: 0>; + +// Floating-point operate format +def bitfield FMT <25:21>; +def bitfield FR <25:21>; +def bitfield FT <20:16>; +def bitfield FS <15:11>; +def bitfield FD <10:6>; + +def bitfield ND <17:17>; +def bitfield TF <16:16>; +def bitfield MOVCI <16:16>; +def bitfield MOVCF <16:16>; +def bitfield SRL <21:21>; +def bitfield SRLV < 6: 6>; +def bitfield SA <10: 6>; + +// Floating Point Condition Codes +def bitfield CC <10:8>; +def bitfield BRANCH_CC <20:18>; + +// CP0 Register Select +def bitfield SEL < 2: 0>; + +// Interrupts +def bitfield SC < 5: 5>; + +// Branch format +def bitfield OFFSET <15: 0>; // displacement + +// Jmp format +def bitfield JMPTARG <25: 0>; +def bitfield HINT <10: 6>; + +def bitfield SYSCALLCODE <25: 6>; +def bitfield TRAPCODE <15:13>; + +// EXT/INS instructions +def bitfield MSB <15:11>; +def bitfield LSB <10: 6>; + +// M5 instructions +def bitfield M5FUNC <7:0>; diff --git a/src/arch/mips/isa/decoder.isa b/src/arch/mips/isa/decoder.isa new file mode 100644 index 000000000..d65e3eb94 --- /dev/null +++ b/src/arch/mips/isa/decoder.isa @@ -0,0 +1,1102 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// The actual MIPS32 ISA decoder +// ----------------------------- +// The following instructions are specified in the MIPS32 ISA +// Specification. Decoding closely follows the style specified +// in the MIPS32 ISA specification document starting with Table +// A-2 (document available @ www.mips.com) +// +decode OPCODE_HI default Unknown::unknown() { + //Table A-2 + 0x0: decode OPCODE_LO { + 0x0: decode FUNCTION_HI { + 0x0: decode FUNCTION_LO { + 0x1: decode MOVCI { + format BasicOp { + 0: movf({{ Rd = (getCondCode(FCSR, CC) == 0) ? Rd : Rs; }}); + 1: movt({{ Rd = (getCondCode(FCSR, CC) == 1) ? Rd : Rs; }}); + } + } + + format BasicOp { + //Table A-3 Note: "Specific encodings of the rd, rs, and + //rt fields are used to distinguish SLL, SSNOP, and EHB + //functions + 0x0: decode RS { + 0x0: decode RT_RD { + 0x0: decode SA default Nop::nop(){ + 0x1: WarnUnimpl::ssnop(); + 0x3: WarnUnimpl::ehb(); + } + default: sll({{ Rd = Rt.uw << SA; }}); + } + } + + 0x2: decode RS_SRL { + 0x0:decode SRL { + 0: srl({{ Rd = Rt.uw >> SA; }}); + + //Hardcoded assuming 32-bit ISA, probably need parameter here + 1: rotr({{ Rd = (Rt.uw << (32 - SA)) | (Rt.uw >> SA);}}); + } + } + + 0x3: decode RS { + 0x0: sra({{ + uint32_t temp = Rt >> SA; + if ( (Rt & 0x80000000) > 0 ) { + uint32_t mask = 0x80000000; + for(int i=0; i < SA; i++) { + temp |= mask; + mask = mask >> 1; + } + } + Rd = temp; + }}); + } + + 0x4: sllv({{ Rd = Rt.uw << Rs<4:0>; }}); + + 0x6: decode SRLV { + 0: srlv({{ Rd = Rt.uw >> Rs<4:0>; }}); + + //Hardcoded assuming 32-bit ISA, probably need parameter here + 1: rotrv({{ Rd = (Rt.uw << (32 - Rs<4:0>)) | (Rt.uw >> Rs<4:0>);}}); + } + + 0x7: srav({{ + int shift_amt = Rs<4:0>; + + uint32_t temp = Rt >> shift_amt; + + if ( (Rt & 0x80000000) > 0 ) { + uint32_t mask = 0x80000000; + for(int i=0; i < shift_amt; i++) { + temp |= mask; + mask = mask >> 1; + } + } + + Rd = temp; + }}); + } + } + + 0x1: decode FUNCTION_LO { + //Table A-3 Note: "Specific encodings of the hint field are + //used to distinguish JR from JR.HB and JALR from JALR.HB" + format Jump { + 0x0: decode HINT { + 0x1: jr_hb({{ NNPC = Rs & ~1; }}, IsReturn, ClearHazards); + default: jr({{ NNPC = Rs & ~1; }}, IsReturn); + } + + 0x1: decode HINT { + 0x1: jalr_hb({{ Rd = NNPC; NNPC = Rs; }}, IsCall, Link + , ClearHazards); + default: jalr({{ Rd = NNPC; NNPC = Rs; }}, IsCall, + Link); + } + } + + format BasicOp { + 0x2: movz({{ Rd = (Rt == 0) ? Rs : Rd; }}); + 0x3: movn({{ Rd = (Rt != 0) ? Rs : Rd; }}); + 0x4: syscall({{ xc->syscall(R2); }}, + IsSerializeAfter, IsNonSpeculative); + 0x7: sync({{ ; }}, IsMemBarrier); + } + + format FailUnimpl { + 0x5: break(); + } + } + + 0x2: decode FUNCTION_LO { + format HiLoMiscOp { + 0x0: mfhi({{ Rd = HI; }}); + 0x1: mthi({{ HI = Rs; }}); + 0x2: mflo({{ Rd = LO; }}); + 0x3: mtlo({{ LO = Rs; }}); + } + } + + 0x3: decode FUNCTION_LO { + format HiLoOp { + 0x0: mult({{ val = Rs.sd * Rt.sd; }}); + 0x1: multu({{ val = Rs.ud * Rt.ud; }}); + } + + format HiLoMiscOp { + 0x2: div({{ if (Rt.sd != 0) { + HI = Rs.sd % Rt.sd; + LO = Rs.sd / Rt.sd; + } + }}); + 0x3: divu({{ if (Rt.ud != 0) { + HI = Rs.ud % Rt.ud; + LO = Rs.ud / Rt.ud; + } + }}); + } + } + + 0x4: decode HINT { + 0x0: decode FUNCTION_LO { + format IntOp { + 0x0: add({{ Rd.sw = Rs.sw + Rt.sw; /*Trap on Overflow*/}}); + 0x1: addu({{ Rd.sw = Rs.sw + Rt.sw;}}); + 0x2: sub({{ Rd.sw = Rs.sw - Rt.sw; /*Trap on Overflow*/}}); + 0x3: subu({{ Rd.sw = Rs.sw - Rt.sw;}}); + 0x4: and({{ Rd = Rs & Rt;}}); + 0x5: or({{ Rd = Rs | Rt;}}); + 0x6: xor({{ Rd = Rs ^ Rt;}}); + 0x7: nor({{ Rd = ~(Rs | Rt);}}); + } + } + } + + 0x5: decode HINT { + 0x0: decode FUNCTION_LO { + format IntOp{ + 0x2: slt({{ Rd.sw = ( Rs.sw < Rt.sw ) ? 1 : 0}}); + 0x3: sltu({{ Rd.uw = ( Rs.uw < Rt.uw ) ? 1 : 0}}); + } + } + } + + 0x6: decode FUNCTION_LO { + format Trap { + 0x0: tge({{ cond = (Rs.sw >= Rt.sw); }}); + 0x1: tgeu({{ cond = (Rs.uw >= Rt.uw); }}); + 0x2: tlt({{ cond = (Rs.sw < Rt.sw); }}); + 0x3: tltu({{ cond = (Rs.uw >= Rt.uw); }}); + 0x4: teq({{ cond = (Rs.sw == Rt.sw); }}); + 0x6: tne({{ cond = (Rs.sw != Rt.sw); }}); + } + } + } + + 0x1: decode REGIMM_HI { + 0x0: decode REGIMM_LO { + format Branch { + 0x0: bltz({{ cond = (Rs.sw < 0); }}); + 0x1: bgez({{ cond = (Rs.sw >= 0); }}); + 0x2: bltzl({{ cond = (Rs.sw < 0); }}, Likely); + 0x3: bgezl({{ cond = (Rs.sw >= 0); }}, Likely); + } + } + + 0x1: decode REGIMM_LO { + format Trap { + 0x0: tgei( {{ cond = (Rs.sw >= INTIMM); }}); + 0x1: tgeiu({{ cond = (Rs.uw >= INTIMM); }}); + 0x2: tlti( {{ cond = (Rs.sw < INTIMM); }}); + 0x3: tltiu({{ cond = (Rs.uw < INTIMM); }}); + 0x4: teqi( {{ cond = (Rs.sw == INTIMM);}}); + 0x6: tnei( {{ cond = (Rs.sw != INTIMM);}}); + } + } + + 0x2: decode REGIMM_LO { + format Branch { + 0x0: bltzal({{ cond = (Rs.sw < 0); }}, Link); + 0x1: decode RS { + 0x0: bal ({{ cond = 1; }}, IsCall, Link); + default: bgezal({{ cond = (Rs.sw >= 0); }}, Link); + } + 0x2: bltzall({{ cond = (Rs.sw < 0); }}, Link, Likely); + 0x3: bgezall({{ cond = (Rs.sw >= 0); }}, Link, Likely); + } + } + + 0x3: decode REGIMM_LO { + format WarnUnimpl { + 0x7: synci(); + } + } + } + + format Jump { + 0x2: j({{ NNPC = (NPC & 0xF0000000) | (JMPTARG << 2);}}); + 0x3: jal({{ NNPC = (NPC & 0xF0000000) | (JMPTARG << 2); }}, IsCall, + Link); + } + + format Branch { + 0x4: decode RS_RT { + 0x0: b({{ cond = 1; }}); + default: beq({{ cond = (Rs.sw == Rt.sw); }}); + } + 0x5: bne({{ cond = (Rs.sw != Rt.sw); }}); + 0x6: blez({{ cond = (Rs.sw <= 0); }}); + 0x7: bgtz({{ cond = (Rs.sw > 0); }}); + } + } + + 0x1: decode OPCODE_LO { + format IntImmOp { + 0x0: addi({{ Rt.sw = Rs.sw + imm; /*Trap If Overflow*/}}); + 0x1: addiu({{ Rt.sw = Rs.sw + imm;}}); + 0x2: slti({{ Rt.sw = ( Rs.sw < imm) ? 1 : 0 }}); + 0x3: sltiu({{ Rt.uw = ( Rs.uw < (uint32_t)sextImm ) ? 1 : 0 }}); + 0x4: andi({{ Rt.sw = Rs.sw & zextImm;}}); + 0x5: ori({{ Rt.sw = Rs.sw | zextImm;}}); + 0x6: xori({{ Rt.sw = Rs.sw ^ zextImm;}}); + + 0x7: decode RS { + 0x0: lui({{ Rt = imm << 16}}); + } + } + } + + 0x2: decode OPCODE_LO { + //Table A-11 MIPS32 COP0 Encoding of rs Field + 0x0: decode RS_MSB { + 0x0: decode RS { + format CP0Control { + 0x0: mfc0({{ Rt = xc->readMiscReg(RD << 5 | SEL); }}); + 0x4: mtc0({{ xc->setMiscReg(RD << 5 | SEL, Rt); }}); + } + + format MipsMT { + 0x8: mftr(); + 0xC: mttr(); + 0xB: decode RD { + 0x0: decode SC { + 0x0: dvpe(); + 0x1: evpe(); + } + 0x1: decode SC { + 0x0: dmt(); + 0x1: emt(); + 0xC: decode SC { + 0x0: di(); + 0x1: ei(); + } + } + } + } + + format FailUnimpl { + 0xA: rdpgpr(); + 0xE: wrpgpr(); + } + } + + //Table A-12 MIPS32 COP0 Encoding of Function Field When rs=CO + 0x1: decode FUNCTION { + format FailUnimpl { + 0x01: tlbr(); + 0x02: tlbwi(); + 0x06: tlbwr(); + 0x08: tlbp(); + + 0x18: eret(); + 0x1F: deret(); + 0x20: wait(); + } + } + } + + //Table A-13 MIPS32 COP1 Encoding of rs Field + 0x1: decode RS_MSB { + + 0x0: decode RS_HI { + 0x0: decode RS_LO { + format CP1Control { + 0x0: mfc1 ({{ Rt.uw = Fs.uw; }}); + + 0x2: cfc1({{ + switch (FS) + { + case 0: + Rt = FIR; + break; + case 25: + Rt = 0 | (FCSR & 0xFE000000) >> 24 | (FCSR & 0x00800000) >> 23; + break; + case 26: + Rt = 0 | (FCSR & 0x0003F07C); + break; + case 28: + Rt = 0 | (FCSR & 0x00000F80) | (FCSR & 0x01000000) >> 21 | (FCSR & 0x00000003); + break; + case 31: + Rt = FCSR; + break; + default: + panic("FP Control Value (%d) Not Valid"); + } + }}); + + 0x3: mfhc1({{ Rt.uw = Fs.ud<63:32>;}}); + + 0x4: mtc1 ({{ Fs.uw = Rt.uw; }}); + + 0x6: ctc1({{ + switch (FS) + { + case 25: + FCSR = 0 | (Rt.uw<7:1> << 25) // move 31...25 + | (FCSR & 0x01000000) // bit 24 + | (FCSR & 0x004FFFFF);// bit 22...0 + break; + + case 26: + FCSR = 0 | (FCSR & 0xFFFC0000) // move 31...18 + | Rt.uw<17:12> << 12 // bit 17...12 + | (FCSR & 0x00000F80) << 7// bit 11...7 + | Rt.uw<6:2> << 2 // bit 6...2 + | (FCSR & 0x00000002); // bit 1...0 + break; + + case 28: + FCSR = 0 | (FCSR & 0xFE000000) // move 31...25 + | Rt.uw<2:2> << 24 // bit 24 + | (FCSR & 0x00FFF000) << 23// bit 23...12 + | Rt.uw<11:7> << 7 // bit 24 + | (FCSR & 0x000007E) + | Rt.uw<1:0>;// bit 22...0 + break; + + case 31: + FCSR = Rt.uw; + break; + + default: + panic("FP Control Value (%d) Not Available. Ignoring Access to" + "Floating Control Status Register", FS); + } + }}); + + 0x7: mthc1({{ + uint64_t fs_hi = Rt.uw; + uint64_t fs_lo = Fs.ud & 0x0FFFFFFFF; + Fs.ud = (fs_hi << 32) | fs_lo; + }}); + + } + } + + 0x1: decode ND { + format Branch { + 0x0: decode TF { + 0x0: bc1f({{ cond = getCondCode(FCSR, BRANCH_CC) == 0; + }}); + 0x1: bc1t({{ cond = getCondCode(FCSR, BRANCH_CC) == 1; + }}); + } + 0x1: decode TF { + 0x0: bc1fl({{ cond = getCondCode(FCSR, BRANCH_CC) == 0; + }}, Likely); + 0x1: bc1tl({{ cond = getCondCode(FCSR, BRANCH_CC) == 1; + }}, Likely); + } + } + } + } + + 0x1: decode RS_HI { + 0x2: decode RS_LO { + //Table A-14 MIPS32 COP1 Encoding of Function Field When rs=S + //(( single-precision floating point)) + 0x0: decode FUNCTION_HI { + 0x0: decode FUNCTION_LO { + format FloatOp { + 0x0: add_s({{ Fd.sf = Fs.sf + Ft.sf;}}); + 0x1: sub_s({{ Fd.sf = Fs.sf - Ft.sf;}}); + 0x2: mul_s({{ Fd.sf = Fs.sf * Ft.sf;}}); + 0x3: div_s({{ Fd.sf = Fs.sf / Ft.sf;}}); + 0x4: sqrt_s({{ Fd.sf = sqrt(Fs.sf);}}); + 0x5: abs_s({{ Fd.sf = fabs(Fs.sf);}}); + 0x7: neg_s({{ Fd.sf = -Fs.sf;}}); + } + + 0x6: BasicOp::mov_s({{ Fd.sf = Fs.sf;}}); + } + + 0x1: decode FUNCTION_LO { + format FloatConvertOp { + 0x0: round_l_s({{ val = Fs.sf; }}, ToLong, + Round); + 0x1: trunc_l_s({{ val = Fs.sf; }}, ToLong, + Trunc); + 0x2: ceil_l_s({{ val = Fs.sf; }}, ToLong, + Ceil); + 0x3: floor_l_s({{ val = Fs.sf; }}, ToLong, + Floor); + 0x4: round_w_s({{ val = Fs.sf; }}, ToWord, + Round); + 0x5: trunc_w_s({{ val = Fs.sf; }}, ToWord, + Trunc); + 0x6: ceil_w_s({{ val = Fs.sf; }}, ToWord, + Ceil); + 0x7: floor_w_s({{ val = Fs.sf; }}, ToWord, + Floor); + } + } + + 0x2: decode FUNCTION_LO { + 0x1: decode MOVCF { + format BasicOp { + 0x0: movf_s({{ Fd = (getCondCode(FCSR,CC) == 0) ? Fs : Fd; }}); + 0x1: movt_s({{ Fd = (getCondCode(FCSR,CC) == 1) ? Fs : Fd; }}); + } + } + + format BasicOp { + 0x2: movz_s({{ Fd = (Rt == 0) ? Fs : Fd; }}); + 0x3: movn_s({{ Fd = (Rt != 0) ? Fs : Fd; }}); + } + + format FloatOp { + 0x5: recip_s({{ Fd = 1 / Fs; }}); + 0x6: rsqrt_s({{ Fd = 1 / sqrt(Fs);}}); + } + } + + 0x4: decode FUNCTION_LO { + format FloatConvertOp { + 0x1: cvt_d_s({{ val = Fs.sf; }}, ToDouble); + 0x4: cvt_w_s({{ val = Fs.sf; }}, ToWord); + 0x5: cvt_l_s({{ val = Fs.sf; }}, ToLong); + } + + 0x6: FloatOp::cvt_ps_s({{ + Fd.ud = (uint64_t) Fs.uw << 32 | + (uint64_t) Ft.uw; + }}); + } + + 0x6: decode FUNCTION_LO { + format FloatCompareOp { + 0x0: c_f_s({{ cond = 0; }}, SinglePrecision, + UnorderedFalse); + 0x1: c_un_s({{ cond = 0; }}, SinglePrecision, + UnorderedTrue); + 0x2: c_eq_s({{ cond = (Fs.sf == Ft.sf); }}, + UnorderedFalse); + 0x3: c_ueq_s({{ cond = (Fs.sf == Ft.sf); }}, + UnorderedTrue); + 0x4: c_olt_s({{ cond = (Fs.sf < Ft.sf); }}, + UnorderedFalse); + 0x5: c_ult_s({{ cond = (Fs.sf < Ft.sf); }}, + UnorderedTrue); + 0x6: c_ole_s({{ cond = (Fs.sf <= Ft.sf); }}, + UnorderedFalse); + 0x7: c_ule_s({{ cond = (Fs.sf <= Ft.sf); }}, + UnorderedTrue); + } + } + + 0x7: decode FUNCTION_LO { + format FloatCompareOp { + 0x0: c_sf_s({{ cond = 0; }}, SinglePrecision, + UnorderedFalse, QnanException); + 0x1: c_ngle_s({{ cond = 0; }}, SinglePrecision, + UnorderedTrue, QnanException); + 0x2: c_seq_s({{ cond = (Fs.sf == Ft.sf);}}, + UnorderedFalse, QnanException); + 0x3: c_ngl_s({{ cond = (Fs.sf == Ft.sf); }}, + UnorderedTrue, QnanException); + 0x4: c_lt_s({{ cond = (Fs.sf < Ft.sf); }}, + UnorderedFalse, QnanException); + 0x5: c_nge_s({{ cond = (Fs.sf < Ft.sf); }}, + UnorderedTrue, QnanException); + 0x6: c_le_s({{ cond = (Fs.sf <= Ft.sf); }}, + UnorderedFalse, QnanException); + 0x7: c_ngt_s({{ cond = (Fs.sf <= Ft.sf); }}, + UnorderedTrue, QnanException); + } + } + } + + //Table A-15 MIPS32 COP1 Encoding of Function Field When rs=D + 0x1: decode FUNCTION_HI { + 0x0: decode FUNCTION_LO { + format FloatOp { + 0x0: add_d({{ Fd.df = Fs.df + Ft.df; }}); + 0x1: sub_d({{ Fd.df = Fs.df - Ft.df; }}); + 0x2: mul_d({{ Fd.df = Fs.df * Ft.df; }}); + 0x3: div_d({{ Fd.df = Fs.df / Ft.df; }}); + 0x4: sqrt_d({{ Fd.df = sqrt(Fs.df); }}); + 0x5: abs_d({{ Fd.df = fabs(Fs.df); }}); + 0x7: neg_d({{ Fd.df = -1 * Fs.df; }}); + } + + 0x6: BasicOp::mov_d({{ Fd.df = Fs.df; }}); + } + + 0x1: decode FUNCTION_LO { + format FloatConvertOp { + 0x0: round_l_d({{ val = Fs.df; }}, ToLong, + Round); + 0x1: trunc_l_d({{ val = Fs.df; }}, ToLong, + Trunc); + 0x2: ceil_l_d({{ val = Fs.df; }}, ToLong, + Ceil); + 0x3: floor_l_d({{ val = Fs.df; }}, ToLong, + Floor); + 0x4: round_w_d({{ val = Fs.df; }}, ToWord, + Round); + 0x5: trunc_w_d({{ val = Fs.df; }}, ToWord, + Trunc); + 0x6: ceil_w_d({{ val = Fs.df; }}, ToWord, + Ceil); + 0x7: floor_w_d({{ val = Fs.df; }}, ToWord, + Floor); + } + } + + 0x2: decode FUNCTION_LO { + 0x1: decode MOVCF { + format BasicOp { + 0x0: movf_d({{ Fd.df = (getCondCode(FCSR,CC) == 0) ? + Fs.df : Fd.df; + }}); + 0x1: movt_d({{ Fd.df = (getCondCode(FCSR,CC) == 1) ? + Fs.df : Fd.df; + }}); + } + } + + format BasicOp { + 0x2: movz_d({{ Fd.df = (Rt == 0) ? Fs.df : Fd.df; }}); + 0x3: movn_d({{ Fd.df = (Rt != 0) ? Fs.df : Fd.df; }}); + } + + format FloatOp { + 0x5: recip_d({{ Fd.df = 1 / Fs.df }}); + 0x6: rsqrt_d({{ Fd.df = 1 / sqrt(Fs.df) }}); + } + } + + 0x4: decode FUNCTION_LO { + format FloatConvertOp { + 0x0: cvt_s_d({{ val = Fs.df; }}, ToSingle); + 0x4: cvt_w_d({{ val = Fs.df; }}, ToWord); + 0x5: cvt_l_d({{ val = Fs.df; }}, ToLong); + } + } + + 0x6: decode FUNCTION_LO { + format FloatCompareOp { + 0x0: c_f_d({{ cond = 0; }}, DoublePrecision, + UnorderedFalse); + 0x1: c_un_d({{ cond = 0; }}, DoublePrecision, + UnorderedTrue); + 0x2: c_eq_d({{ cond = (Fs.df == Ft.df); }}, + UnorderedFalse); + 0x3: c_ueq_d({{ cond = (Fs.df == Ft.df); }}, + UnorderedTrue); + 0x4: c_olt_d({{ cond = (Fs.df < Ft.df); }}, + UnorderedFalse); + 0x5: c_ult_d({{ cond = (Fs.df < Ft.df); }}, + UnorderedTrue); + 0x6: c_ole_d({{ cond = (Fs.df <= Ft.df); }}, + UnorderedFalse); + 0x7: c_ule_d({{ cond = (Fs.df <= Ft.df); }}, + UnorderedTrue); + } + } + + 0x7: decode FUNCTION_LO { + format FloatCompareOp { + 0x0: c_sf_d({{ cond = 0; }}, DoublePrecision, + UnorderedFalse, QnanException); + 0x1: c_ngle_d({{ cond = 0; }}, DoublePrecision, + UnorderedTrue, QnanException); + 0x2: c_seq_d({{ cond = (Fs.df == Ft.df); }}, + UnorderedFalse, QnanException); + 0x3: c_ngl_d({{ cond = (Fs.df == Ft.df); }}, + UnorderedTrue, QnanException); + 0x4: c_lt_d({{ cond = (Fs.df < Ft.df); }}, + UnorderedFalse, QnanException); + 0x5: c_nge_d({{ cond = (Fs.df < Ft.df); }}, + UnorderedTrue, QnanException); + 0x6: c_le_d({{ cond = (Fs.df <= Ft.df); }}, + UnorderedFalse, QnanException); + 0x7: c_ngt_d({{ cond = (Fs.df <= Ft.df); }}, + UnorderedTrue, QnanException); + } + } + } + + //Table A-16 MIPS32 COP1 Encoding of Function Field When rs=W + 0x4: decode FUNCTION { + format FloatConvertOp { + 0x20: cvt_s_w({{ val = Fs.uw; }}, ToSingle); + 0x21: cvt_d_w({{ val = Fs.uw; }}, ToDouble); + 0x26: FailUnimpl::cvt_ps_w(); + } + } + + //Table A-16 MIPS32 COP1 Encoding of Function Field When rs=L1 + //Note: "1. Format type L is legal only if 64-bit floating point operations + //are enabled." + 0x5: decode FUNCTION_HI { + format FloatConvertOp { + 0x20: cvt_s_l({{ val = Fs.ud; }}, ToSingle); + 0x21: cvt_d_l({{ val = Fs.ud; }}, ToDouble); + 0x26: FailUnimpl::cvt_ps_l(); + } + } + + //Table A-17 MIPS64 COP1 Encoding of Function Field When rs=PS1 + //Note: "1. Format type PS is legal only if 64-bit floating point operations + //are enabled. " + 0x6: decode FUNCTION_HI { + 0x0: decode FUNCTION_LO { + format Float64Op { + 0x0: add_ps({{ + Fd1.sf = Fs1.sf + Ft2.sf; + Fd2.sf = Fs2.sf + Ft2.sf; + }}); + 0x1: sub_ps({{ + Fd1.sf = Fs1.sf - Ft2.sf; + Fd2.sf = Fs2.sf - Ft2.sf; + }}); + 0x2: mul_ps({{ + Fd1.sf = Fs1.sf * Ft2.sf; + Fd2.sf = Fs2.sf * Ft2.sf; + }}); + 0x5: abs_ps({{ + Fd1.sf = fabs(Fs1.sf); + Fd2.sf = fabs(Fs2.sf); + }}); + 0x6: mov_ps({{ + Fd1.sf = Fs1.sf; + Fd2.sf = Fs2.sf; + }}); + 0x7: neg_ps({{ + Fd1.sf = -(Fs1.sf); + Fd2.sf = -(Fs2.sf); + }}); + } + } + + 0x2: decode FUNCTION_LO { + 0x1: decode MOVCF { + format Float64Op { + 0x0: movf_ps({{ + Fd1 = (getCondCode(FCSR, CC) == 0) ? + Fs1 : Fd1; + Fd2 = (getCondCode(FCSR, CC+1) == 0) ? + Fs2 : Fd2; + }}); + 0x1: movt_ps({{ + Fd2 = (getCondCode(FCSR, CC) == 1) ? + Fs1 : Fd1; + Fd2 = (getCondCode(FCSR, CC+1) == 1) ? + Fs2 : Fd2; + }}); + } + } + + format Float64Op { + 0x2: movz_ps({{ + Fd1 = (getCondCode(FCSR, CC) == 0) ? + Fs1 : Fd1; + Fd2 = (getCondCode(FCSR, CC) == 0) ? + Fs2 : Fd2; + }}); + 0x3: movn_ps({{ + Fd1 = (getCondCode(FCSR, CC) == 1) ? + Fs1 : Fd1; + Fd2 = (getCondCode(FCSR, CC) == 1) ? + Fs2 : Fd2; + }}); + } + + } + + 0x4: decode FUNCTION_LO { + 0x0: FloatOp::cvt_s_pu({{ Fd.sf = Fs2.sf; }}); + } + + 0x5: decode FUNCTION_LO { + 0x0: FloatOp::cvt_s_pl({{ Fd.sf = Fs1.sf; }}); + + format Float64Op { + 0x4: pll({{ Fd.ud = (uint64_t) Fs1.uw << 32 | + Ft1.uw; + }}); + 0x5: plu({{ Fd.ud = (uint64_t) Fs1.uw << 32 | + Ft2.uw; + }}); + 0x6: pul({{ Fd.ud = (uint64_t) Fs2.uw << 32 | + Ft1.uw; + }}); + 0x7: puu({{ Fd.ud = (uint64_t) Fs2.uw << 32 | + Ft2.uw; + }}); + } + } + + 0x6: decode FUNCTION_LO { + format FloatPSCompareOp { + 0x0: c_f_ps({{ cond1 = 0; }}, {{ cond2 = 0; }}, + UnorderedFalse); + 0x1: c_un_ps({{ cond1 = 0; }}, {{ cond2 = 0; }}, + UnorderedTrue); + 0x2: c_eq_ps({{ cond1 = (Fs1.sf == Ft1.sf); }}, + {{ cond2 = (Fs2.sf == Ft2.sf); }}, + UnorderedFalse); + 0x3: c_ueq_ps({{ cond1 = (Fs1.sf == Ft1.sf); }}, + {{ cond2 = (Fs2.sf == Ft2.sf); }}, + UnorderedTrue); + 0x4: c_olt_ps({{ cond1 = (Fs1.sf < Ft1.sf); }}, + {{ cond2 = (Fs2.sf < Ft2.sf); }}, + UnorderedFalse); + 0x5: c_ult_ps({{ cond1 = (Fs.sf < Ft.sf); }}, + {{ cond2 = (Fs2.sf < Ft2.sf); }}, + UnorderedTrue); + 0x6: c_ole_ps({{ cond1 = (Fs.sf <= Ft.sf); }}, + {{ cond2 = (Fs2.sf <= Ft2.sf); }}, + UnorderedFalse); + 0x7: c_ule_ps({{ cond1 = (Fs1.sf <= Ft1.sf); }}, + {{ cond2 = (Fs2.sf <= Ft2.sf); }}, + UnorderedTrue); + } + } + + 0x7: decode FUNCTION_LO { + format FloatPSCompareOp { + 0x0: c_sf_ps({{ cond1 = 0; }}, {{ cond2 = 0; }}, + UnorderedFalse, QnanException); + 0x1: c_ngle_ps({{ cond1 = 0; }}, + {{ cond2 = 0; }}, + UnorderedTrue, QnanException); + 0x2: c_seq_ps({{ cond1 = (Fs1.sf == Ft1.sf); }}, + {{ cond2 = (Fs2.sf == Ft2.sf); }}, + UnorderedFalse, QnanException); + 0x3: c_ngl_ps({{ cond1 = (Fs1.sf == Ft1.sf); }}, + {{ cond2 = (Fs2.sf == Ft2.sf); }}, + UnorderedTrue, QnanException); + 0x4: c_lt_ps({{ cond1 = (Fs1.sf < Ft1.sf); }}, + {{ cond2 = (Fs2.sf < Ft2.sf); }}, + UnorderedFalse, QnanException); + 0x5: c_nge_ps({{ cond1 = (Fs1.sf < Ft1.sf); }}, + {{ cond2 = (Fs2.sf < Ft2.sf); }}, + UnorderedTrue, QnanException); + 0x6: c_le_ps({{ cond1 = (Fs1.sf <= Ft1.sf); }}, + {{ cond2 = (Fs2.sf <= Ft2.sf); }}, + UnorderedFalse, QnanException); + 0x7: c_ngt_ps({{ cond1 = (Fs1.sf <= Ft1.sf); }}, + {{ cond2 = (Fs2.sf <= Ft2.sf); }}, + UnorderedTrue, QnanException); + } + } + } + } + } + } + + //Table A-19 MIPS32 COP2 Encoding of rs Field + 0x2: decode RS_MSB { + format FailUnimpl { + 0x0: decode RS_HI { + 0x0: decode RS_LO { + 0x0: mfc2(); + 0x2: cfc2(); + 0x3: mfhc2(); + 0x4: mtc2(); + 0x6: ctc2(); + 0x7: mftc2(); + } + + 0x1: decode ND { + 0x0: decode TF { + 0x0: bc2f(); + 0x1: bc2t(); + } + + 0x1: decode TF { + 0x0: bc2fl(); + 0x1: bc2tl(); + } + } + } + } + } + + //Table A-20 MIPS64 COP1X Encoding of Function Field 1 + //Note: "COP1X instructions are legal only if 64-bit floating point + //operations are enabled." + 0x3: decode FUNCTION_HI { + 0x0: decode FUNCTION_LO { + format LoadIndexedMemory { + 0x0: lwxc1({{ Fd.uw = Mem.uw;}}); + 0x1: ldxc1({{ Fd.ud = Mem.ud;}}); + 0x5: luxc1({{ Fd.ud = Mem.ud;}}, + {{ EA = (Rs + Rt) & ~7; }}); + } + } + + 0x1: decode FUNCTION_LO { + format StoreIndexedMemory { + 0x0: swxc1({{ Mem.uw = Fs.uw;}}); + 0x1: sdxc1({{ Mem.ud = Fs.ud;}}); + 0x5: suxc1({{ Mem.ud = Fs.ud;}}, + {{ EA = (Rs + Rt) & ~7; }}); + } + + 0x7: Prefetch::prefx({{ EA = Rs + Rt; }}); + } + + 0x3: decode FUNCTION_LO { + 0x6: Float64Op::alnv_ps({{ if (Rs<2:0> == 0) { + Fd.ud = Fs.ud; + } else if (Rs<2:0> == 4) { + #if BYTE_ORDER == BIG_ENDIAN + Fd.ud = Fs.ud<31:0> << 32 | + Ft.ud<63:32>; + #elif BYTE_ORDER == LITTLE_ENDIAN + Fd.ud = Ft.ud<31:0> << 32 | + Fs.ud<63:32>; + #endif + } else { + Fd.ud = Fd.ud; + } + }}); + } + + format FloatAccOp { + 0x4: decode FUNCTION_LO { + 0x0: madd_s({{ Fd.sf = (Fs.sf * Ft.sf) + Fr.sf; }}); + 0x1: madd_d({{ Fd.df = (Fs.df * Ft.df) + Fr.df; }}); + 0x6: madd_ps({{ + Fd1.sf = (Fs1.df * Ft1.df) + Fr1.df; + Fd2.sf = (Fs2.df * Ft2.df) + Fr2.df; + }}); + } + + 0x5: decode FUNCTION_LO { + 0x0: msub_s({{ Fd.sf = (Fs.sf * Ft.sf) - Fr.sf; }}); + 0x1: msub_d({{ Fd.df = (Fs.df * Ft.df) - Fr.df; }}); + 0x6: msub_ps({{ + Fd1.sf = (Fs1.df * Ft1.df) - Fr1.df; + Fd2.sf = (Fs2.df * Ft2.df) - Fr2.df; + }}); + } + + 0x6: decode FUNCTION_LO { + 0x0: nmadd_s({{ Fd.sf = (-1 * Fs.sf * Ft.sf) - Fr.sf; }}); + 0x1: nmadd_d({{ Fd.df = (-1 * Fs.df * Ft.df) + Fr.df; }}); + 0x6: nmadd_ps({{ + Fd1.sf = -((Fs1.df * Ft1.df) + Fr1.df); + Fd2.sf = -((Fs2.df * Ft2.df) + Fr2.df); + }}); + } + + 0x7: decode FUNCTION_LO { + 0x0: nmsub_s({{ Fd.sf = (-1 * Fs.sf * Ft.sf) - Fr.sf; }}); + 0x1: nmsub_d({{ Fd.df = (-1 * Fs.df * Ft.df) - Fr.df; }}); + 0x6: nmsub_ps({{ + Fd1.sf = -((Fs1.df * Ft1.df) - Fr1.df); + Fd2.sf = -((Fs2.df * Ft2.df) - Fr2.df); + }}); + } + + } + } + + format Branch { + 0x4: beql({{ cond = (Rs.sw == Rt.sw); }}, Likely); + 0x5: bnel({{ cond = (Rs.sw != Rt.sw); }}, Likely); + 0x6: blezl({{ cond = (Rs.sw <= 0); }}, Likely); + 0x7: bgtzl({{ cond = (Rs.sw > 0); }}, Likely); + } + } + + 0x3: decode OPCODE_LO { + //Table A-5 MIPS32 SPECIAL2 Encoding of Function Field + 0x4: decode FUNCTION_HI { + 0x0: decode FUNCTION_LO { + 0x2: IntOp::mul({{ int64_t temp1 = Rs.sd * Rt.sd; + Rd.sw = temp1<31:0> + }}); + + format HiLoOp { + 0x0: madd({{ val = ((int64_t) HI << 32 | LO) + + (Rs.sd * Rt.sd); + }}); + 0x1: maddu({{ val = ((uint64_t) HI << 32 | LO) + + (Rs.ud * Rt.ud); + }}); + 0x4: msub({{ val = ((int64_t) HI << 32 | LO) - + (Rs.sd * Rt.sd); + }}); + 0x5: msubu({{ val = ((uint64_t) HI << 32 | LO) - + (Rs.ud * Rt.ud); + }}); + } + } + + 0x4: decode FUNCTION_LO { + format BasicOp { + 0x0: clz({{ int cnt = 32; + for (int idx = 31; idx >= 0; idx--) { + if( Rs<idx:idx> == 1) { + cnt = 31 - idx; + break; + } + } + Rd.uw = cnt; + }}); + 0x1: clo({{ int cnt = 32; + for (int idx = 31; idx >= 0; idx--) { + if( Rs<idx:idx> == 0) { + cnt = 31 - idx; + break; + } + } + Rd.uw = cnt; + }}); + } + } + + 0x7: decode FUNCTION_LO { + 0x7: FailUnimpl::sdbbp(); + } + } + + //Table A-6 MIPS32 SPECIAL3 Encoding of Function Field for Release 2 + //of the Architecture + 0x7: decode FUNCTION_HI { + 0x0: decode FUNCTION_LO { + format BasicOp { + 0x0: ext({{ Rt.uw = bits(Rs.uw, MSB+LSB, LSB); }}); + 0x4: ins({{ Rt.uw = bits(Rt.uw, 31, MSB+1) << (MSB+1) | + bits(Rs.uw, MSB-LSB, 0) << LSB | + bits(Rt.uw, LSB-1, 0); + }}); + } + } + + 0x1: decode FUNCTION_LO { + format MipsMT { + 0x0: fork(); + 0x1: yield(); + } + } + + //Table A-10 MIPS32 BSHFL Encoding of sa Field + 0x4: decode SA { + format BasicOp { + 0x02: wsbh({{ Rd.uw = Rt.uw<23:16> << 24 | + Rt.uw<31:24> << 16 | + Rt.uw<7:0> << 8 | + Rt.uw<15:8>; + }}); + 0x10: seb({{ Rd.sw = Rt.sb; }}); + 0x18: seh({{ Rd.sw = Rt.sh; }}); + } + } + + 0x6: decode FUNCTION_LO { + 0x7: FailUnimpl::rdhwr(); + } + } + } + + 0x4: decode OPCODE_LO { + format LoadMemory { + 0x0: lb({{ Rt.sw = Mem.sb; }}); + 0x1: lh({{ Rt.sw = Mem.sh; }}); + 0x3: lw({{ Rt.sw = Mem.sw; }}); + 0x4: lbu({{ Rt.uw = Mem.ub; }}); + 0x5: lhu({{ Rt.uw = Mem.uh; }}); + } + + format LoadUnalignedMemory { + 0x2: lwl({{ uint32_t mem_shift = 24 - (8 * byte_offset); + Rt.uw = mem_word << mem_shift | + Rt.uw & mask(mem_shift); + }}); + 0x6: lwr({{ uint32_t mem_shift = 8 * byte_offset; + Rt.uw = Rt.uw & (mask(mem_shift) << (32 - mem_shift)) | + mem_word >> mem_shift; + }}); + } + } + + 0x5: decode OPCODE_LO { + format StoreMemory { + 0x0: sb({{ Mem.ub = Rt<7:0>; }}); + 0x1: sh({{ Mem.uh = Rt<15:0>; }}); + 0x3: sw({{ Mem.uw = Rt<31:0>; }}); + } + + format StoreUnalignedMemory { + 0x2: swl({{ uint32_t reg_shift = 24 - (8 * byte_offset); + uint32_t mem_shift = 32 - reg_shift; + mem_word = mem_word & (mask(reg_shift) << mem_shift) | + Rt.uw >> reg_shift; + }}); + 0x6: swr({{ uint32_t reg_shift = 8 * byte_offset; + mem_word = Rt.uw << reg_shift | + mem_word & (mask(reg_shift)); + }}); + } + + 0x7: FailUnimpl::cache(); + } + + 0x6: decode OPCODE_LO { + format LoadMemory { + 0x0: ll({{ Rt.uw = Mem.uw; }}, mem_flags=LOCKED); + 0x1: lwc1({{ Ft.uw = Mem.uw; }}); + 0x5: ldc1({{ Ft.ud = Mem.ud; }}); + } + + 0x3: Prefetch::pref(); + } + + + 0x7: decode OPCODE_LO { + 0x0: StoreCond::sc({{ Mem.uw = Rt.uw;}}, + {{ uint64_t tmp = write_result; + Rt.uw = (tmp == 0 || tmp == 1) ? tmp : Rt.uw; + }}, mem_flags=LOCKED, inst_flags = IsStoreConditional); + + format StoreMemory { + 0x1: swc1({{ Mem.uw = Ft.uw; }}); + 0x5: sdc1({{ Mem.ud = Ft.ud; }}); + } + } +} + + diff --git a/src/arch/mips/isa/formats/basic.isa b/src/arch/mips/isa/formats/basic.isa new file mode 100644 index 000000000..29a445b2c --- /dev/null +++ b/src/arch/mips/isa/formats/basic.isa @@ -0,0 +1,96 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt +// Korey Sewell + +// Declarations for execute() methods. +def template BasicExecDeclare {{ + Fault execute(%(CPU_exec_context)s *, Trace::InstRecord *) const; +}}; + +// Basic instruction class declaration template. +def template BasicDeclare {{ + /** + * Static instruction class for "%(mnemonic)s". + */ + class %(class_name)s : public %(base_class)s + { + public: + /// Constructor. + %(class_name)s(MachInst machInst); + %(BasicExecDeclare)s + }; +}}; + +// Basic instruction class constructor template. +def template BasicConstructor {{ + inline %(class_name)s::%(class_name)s(MachInst machInst) : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s) + { + %(constructor)s; + } +}}; + + +// Basic instruction class execute method template. +def template BasicExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(code)s; + + if(fault == NoFault) + { + %(op_wb)s; + } + return fault; + } +}}; + +// Basic decode template. +def template BasicDecode {{ + return new %(class_name)s(machInst); +}}; + +// Basic decode template, passing mnemonic in as string arg to constructor. +def template BasicDecodeWithMnemonic {{ + return new %(class_name)s("%(mnemonic)s", machInst); +}}; + +// The most basic instruction format... +def format BasicOp(code, *flags) {{ + iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; diff --git a/src/arch/mips/isa/formats/branch.isa b/src/arch/mips/isa/formats/branch.isa new file mode 100644 index 000000000..8c89fbfa2 --- /dev/null +++ b/src/arch/mips/isa/formats/branch.isa @@ -0,0 +1,278 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Control transfer instructions +// + +output header {{ + +#include <iostream> + + /** + * Base class for instructions whose disassembly is not purely a + * function of the machine instruction (i.e., it depends on the + * PC). This class overrides the disassemble() method to check + * the PC and symbol table values before re-using a cached + * disassembly string. This is necessary for branches and jumps, + * where the disassembly string includes the target address (which + * may depend on the PC and/or symbol table). + */ + class PCDependentDisassembly : public MipsStaticInst + { + protected: + /// Cached program counter from last disassembly + mutable Addr cachedPC; + + /// Cached symbol table pointer from last disassembly + mutable const SymbolTable *cachedSymtab; + + /// Constructor + PCDependentDisassembly(const char *mnem, MachInst _machInst, + OpClass __opClass) + : MipsStaticInst(mnem, _machInst, __opClass), + cachedPC(0), cachedSymtab(0) + { + } + + const std::string & + disassemble(Addr pc, const SymbolTable *symtab) const; + }; + + /** + * Base class for branches (PC-relative control transfers), + * conditional or unconditional. + */ + class Branch : public PCDependentDisassembly + { + protected: + /// target address (signed) Displacement . + int32_t disp; + + /// Constructor. + Branch(const char *mnem, MachInst _machInst, OpClass __opClass) + : PCDependentDisassembly(mnem, _machInst, __opClass), + disp(OFFSET << 2) + { + //If Bit 17 is 1 then Sign Extend + if ( (disp & 0x00020000) > 0 ) { + disp |= 0xFFFE0000; + } + } + + Addr branchTarget(Addr branchPC) const; + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + /** + * Base class for jumps (register-indirect control transfers). In + * the Mips ISA, these are always unconditional. + */ + class Jump : public PCDependentDisassembly + { + protected: + + /// Displacement to target address (signed). + int32_t disp; + + uint32_t target; + + public: + /// Constructor + Jump(const char *mnem, MachInst _machInst, OpClass __opClass) + : PCDependentDisassembly(mnem, _machInst, __opClass), + disp(JMPTARG << 2) + { + } + + Addr branchTarget(ThreadContext *tc) const; + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + Addr + Branch::branchTarget(Addr branchPC) const + { + return branchPC + 4 + disp; + } + + Addr + Jump::branchTarget(ThreadContext *tc) const + { + Addr NPC = tc->readPC() + 4; + uint64_t Rb = tc->readIntReg(_srcRegIdx[0]); + return (Rb & ~3) | (NPC & 1); + } + + const std::string & + PCDependentDisassembly::disassemble(Addr pc, + const SymbolTable *symtab) const + { + if (!cachedDisassembly || + pc != cachedPC || symtab != cachedSymtab) + { + if (cachedDisassembly) + delete cachedDisassembly; + + cachedDisassembly = + new std::string(generateDisassembly(pc, symtab)); + cachedPC = pc; + cachedSymtab = symtab; + } + + return *cachedDisassembly; + } + + std::string + Branch::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + // There's only one register arg (RA), but it could be + // either a source (the condition for conditional + // branches) or a destination (the link reg for + // unconditional branches) + if (_numSrcRegs == 1) { + printReg(ss, _srcRegIdx[0]); + ss << ", "; + } else if(_numSrcRegs == 2) { + printReg(ss, _srcRegIdx[0]); + ss << ", "; + printReg(ss, _srcRegIdx[1]); + ss << ", "; + } + + Addr target = pc + 4 + disp; + + std::string str; + if (symtab && symtab->findSymbol(target, str)) + ss << str; + else + ccprintf(ss, "0x%x", target); + + return ss.str(); + } + + std::string + Jump::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + if ( mnemonic == "jal" ) { + Addr npc = pc + 4; + ccprintf(ss,"0x%x",(npc & 0xF0000000) | disp); + } else if (_numSrcRegs == 0) { + std::string str; + if (symtab && symtab->findSymbol(disp, str)) + ss << str; + else + ccprintf(ss, "0x%x", disp); + } else if (_numSrcRegs == 1) { + printReg(ss, _srcRegIdx[0]); + } else if(_numSrcRegs == 2) { + printReg(ss, _srcRegIdx[0]); + ss << ", "; + printReg(ss, _srcRegIdx[1]); + } + + return ss.str(); + } +}}; + +def format Branch(code,*opt_flags) {{ + not_taken_code = ' NNPC = NNPC;\n' + not_taken_code += '} \n' + + #Build Instruction Flags + #Use Link & Likely Flags to Add Link/Condition Code + inst_flags = ('IsDirectControl', ) + for x in opt_flags: + if x == 'Link': + code += 'R31 = NNPC;\n' + elif x == 'Likely': + not_taken_code = ' NPC = NNPC;\n' + not_taken_code += ' NNPC = NNPC + 4;\n' + not_taken_code += '} \n' + inst_flags = ('IsCondDelaySlot', ) + else: + inst_flags += (x, ) + + #Take into account uncond. branch instruction + if 'cond == 1' in code: + inst_flags += ('IsUnCondControl', ) + else: + inst_flags += ('IsCondControl', ) + + #Condition code + code = 'bool cond;\n' + code + code += 'if (cond) {\n' + code += ' NNPC = NPC + disp;\n' + code += '} else {\n' + code += not_taken_code + + iop = InstObjParams(name, Name, 'Branch', CodeBlock(code), inst_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format Jump(code, *opt_flags) {{ + #Build Instruction Flags + #Use Link Flag to Add Link Code + inst_flags = ('IsIndirectControl', 'IsUncondControl') + for x in opt_flags: + if x == 'Link': + code = 'R31 = NNPC;\n' + code + elif x == 'ClearHazards': + code += '/* Code Needed to Clear Execute & Inst Hazards */\n' + else: + inst_flags += (x, ) + + iop = InstObjParams(name, Name, 'Jump', CodeBlock(code), inst_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + + + + diff --git a/src/arch/mips/isa/formats/control.isa b/src/arch/mips/isa/formats/control.isa new file mode 100644 index 000000000..6c7d396f3 --- /dev/null +++ b/src/arch/mips/isa/formats/control.isa @@ -0,0 +1,156 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Integer operate instructions +// + +//Outputs to decoder.hh +output header {{ + + class Control : public MipsStaticInst + { + protected: + + /// Constructor + Control(const char *mnem, MachInst _machInst, OpClass __opClass) : + MipsStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + class CP0Control : public Control + { + protected: + + /// Constructor + CP0Control(const char *mnem, MachInst _machInst, OpClass __opClass) : + Control(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + class CP1Control : public Control + { + protected: + + /// Constructor + CP1Control(const char *mnem, MachInst _machInst, OpClass __opClass) : + Control(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + +}}; + +//Outputs to decoder.cc +output decoder {{ + std::string Control::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + if (mnemonic == "mfc0" || mnemonic == "mtc0") { + ccprintf(ss, "%-10s %d,%d,%d", mnemonic,RT,RD,SEL); + } else { + + // just print the first dest... if there's a second one, + // it's generally implicit + if (_numDestRegs > 0) { + printReg(ss, _destRegIdx[0]); + } + + ss << ", "; + + // just print the first two source regs... if there's + // a third one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + + if (_numSrcRegs > 1) { + ss << ", "; + printReg(ss, _srcRegIdx[1]); + } + } + + return ss.str(); + } + + std::string CP0Control::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + ccprintf(ss, "%-10s r%d, r%d, %d", mnemonic, RT, RD, SEL); + return ss.str(); + } + + std::string CP1Control::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + ccprintf(ss, "%-10s r%d, f%d", mnemonic, RT, FS); + return ss.str(); + } + +}}; + +def format System(code, *flags) {{ + iop = InstObjParams(name, Name, 'Control', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format CP0Control(code, *flags) {{ + iop = InstObjParams(name, Name, 'CP0Control', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format CP1Control(code, *flags) {{ + iop = InstObjParams(name, Name, 'CP1Control', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + + diff --git a/src/arch/mips/isa/formats/formats.isa b/src/arch/mips/isa/formats/formats.isa new file mode 100644 index 000000000..4c3eec132 --- /dev/null +++ b/src/arch/mips/isa/formats/formats.isa @@ -0,0 +1,66 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//Templates from this format are used later +//Include the basic format +##include "basic.isa" + +//Include the basic format +##include "noop.isa" + +//Include utility functions +##include "util.isa" + +//Include the control/cp0/cp1 formats +##include "control.isa" + +//Include the integer formats +##include "int.isa" + +//Include the floatOp format +##include "fp.isa" + +//Include the mem format +##include "mem.isa" + +//Include the mem format +##include "mt.isa" + +//Include the trap format +##include "trap.isa" + +//Include the branch format +##include "branch.isa" + +//Include the noop format +##include "unimp.isa" + +//Include the noop format +##include "unknown.isa" diff --git a/src/arch/mips/isa/formats/fp.isa b/src/arch/mips/isa/formats/fp.isa new file mode 100644 index 000000000..cdb892b3f --- /dev/null +++ b/src/arch/mips/isa/formats/fp.isa @@ -0,0 +1,373 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Floating Point operate instructions +// + +output header {{ + /** + * Base class for FP operations. + */ + class FPOp : public MipsStaticInst + { + protected: + + /// Constructor + FPOp(const char *mnem, MachInst _machInst, OpClass __opClass) : MipsStaticInst(mnem, _machInst, __opClass) + { + } + + //std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + + //needs function to check for fpEnable or not + }; + + class FPCompareOp : public FPOp + { + protected: + FPCompareOp(const char *mnem, MachInst _machInst, OpClass __opClass) : FPOp(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + + }; +}}; + +output decoder {{ + std::string FPCompareOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + ccprintf(ss,"%d",CC); + + if(_numSrcRegs > 0) { + ss << ", "; + printReg(ss, _srcRegIdx[0]); + } + + if(_numSrcRegs > 1) { + ss << ", "; + printReg(ss, _srcRegIdx[1]); + } + + return ss.str(); + } +}}; + +output exec {{ + + //If any operand is Nan return the appropriate QNaN + template <class T> + bool + fpNanOperands(FPOp *inst, %(CPU_exec_context)s *xc, const T &src_type, + Trace::InstRecord *traceData) + { + uint64_t mips_nan = 0; + T src_op = 0; + int size = sizeof(src_op) * 8; + + for (int i = 0; i < inst->numSrcRegs(); i++) { + uint64_t src_bits = xc->readFloatRegBits(inst, 0, size); + + if (isNan(&src_bits, size) ) { + if (isSnan(&src_bits, size)) { + switch (size) + { + case 32: mips_nan = MIPS32_QNAN; break; + case 64: mips_nan = MIPS64_QNAN; break; + default: panic("Unsupported Floating Point Size (%d)", size); + } + } else { + mips_nan = src_bits; + } + + xc->setFloatRegBits(inst, 0, mips_nan, size); + if (traceData) { traceData->setData(mips_nan); } + return true; + } + } + return false; + } + + template <class T> + bool + fpInvalidOp(FPOp *inst, %(CPU_exec_context)s *cpu, const T dest_val, + Trace::InstRecord *traceData) + { + uint64_t mips_nan = 0; + T src_op = dest_val; + int size = sizeof(src_op) * 8; + + if (isNan(&src_op, size)) { + switch (size) + { + case 32: mips_nan = MIPS32_QNAN; break; + case 64: mips_nan = MIPS64_QNAN; break; + default: panic("Unsupported Floating Point Size (%d)", size); + } + + //Set value to QNAN + cpu->setFloatRegBits(inst, 0, mips_nan, size); + + //Read FCSR from FloatRegFile + uint32_t fcsr_bits = cpu->tcBase()->readFloatRegBits(FCSR); + + //Write FCSR from FloatRegFile + cpu->tcBase()->setFloatRegBits(FCSR, genInvalidVector(fcsr_bits)); + + if (traceData) { traceData->setData(mips_nan); } + return true; + } + + return false; + } + + void + fpResetCauseBits(%(CPU_exec_context)s *cpu) + { + //Read FCSR from FloatRegFile + uint32_t fcsr = cpu->tcBase()->readFloatRegBits(FCSR); + + fcsr = bits(fcsr, 31, 18) << 18 | bits(fcsr, 11, 0); + + //Write FCSR from FloatRegFile + cpu->tcBase()->setFloatRegBits(FCSR, fcsr); + } +}}; + +def template FloatingPointExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + + //When is the right time to reset cause bits? + //start of every instruction or every cycle? +#if FULL_SYSTEM + fpResetCauseBits(xc); +#endif + %(op_decl)s; + %(op_rd)s; + + //Check if any FP operand is a NaN value + if (!fpNanOperands((FPOp*)this, xc, Fd, traceData)) { + %(code)s; + + //Change this code for Full-System/Sycall Emulation + //separation + //---- + //Should Full System-Mode throw a fault here? + //---- + //Check for IEEE 754 FP Exceptions + //fault = fpNanOperands((FPOp*)this, xc, Fd, traceData); + if ( +#if FULL_SYSTEM + !fpInvalidOp((FPOp*)this, xc, Fd, traceData) && +#endif + fault == NoFault) + { + %(op_wb)s; + } + } + + return fault; + } +}}; + +// Primary format for float point operate instructions: +def format FloatOp(code, *flags) {{ + iop = InstObjParams(name, Name, 'FPOp', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = FloatingPointExecute.subst(iop) +}}; + +def format FloatCompareOp(cond_code, *flags) {{ + import sys + + code = 'bool cond;\n' + if '.sf' in cond_code or 'SinglePrecision' in flags: + if 'QnanException' in flags: + code += 'if (isQnan(&Fs.sf, 32) || isQnan(&Ft.sf, 32)) {\n' + code += '\tFCSR = genInvalidVector(FCSR);\n' + code += '\treturn NoFault;' + code += '}\n else ' + code += 'if (isNan(&Fs.sf, 32) || isNan(&Ft.sf, 32)) {\n' + elif '.df' in cond_code or 'DoublePrecision' in flags: + if 'QnanException' in flags: + code += 'if (isQnan(&Fs.df, 64) || isQnan(&Ft.df, 64)) {\n' + code += '\tFCSR = genInvalidVector(FCSR);\n' + code += '\treturn NoFault;' + code += '}\n else ' + code += 'if (isNan(&Fs.df, 64) || isNan(&Ft.df, 64)) {\n' + else: + sys.exit('Decoder Failed: Can\'t Determine Operand Type\n') + + if 'UnorderedTrue' in flags: + code += 'cond = 1;\n' + elif 'UnorderedFalse' in flags: + code += 'cond = 0;\n' + else: + sys.exit('Decoder Failed: Float Compare Instruction Needs A Unordered Flag\n') + + code += '} else {\n' + code += cond_code + '}' + code += 'FCSR = genCCVector(FCSR, CC, cond);\n' + + iop = InstObjParams(name, Name, 'FPCompareOp', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format FloatConvertOp(code, *flags) {{ + import sys + + #Determine Source Type + convert = 'fpConvert(' + if '.sf' in code: + code = 'float ' + code + '\n' + convert += 'SINGLE_TO_' + elif '.df' in code: + code = 'double ' + code + '\n' + convert += 'DOUBLE_TO_' + elif '.uw' in code: + code = 'uint32_t ' + code + '\n' + convert += 'WORD_TO_' + elif '.ud' in code: + code = 'uint64_t ' + code + '\n' + convert += 'LONG_TO_' + else: + sys.exit("Error Determining Source Type for Conversion") + + #Determine Destination Type + if 'ToSingle' in flags: + code += 'Fd.uw = ' + convert + 'SINGLE, ' + elif 'ToDouble' in flags: + code += 'Fd.ud = ' + convert + 'DOUBLE, ' + elif 'ToWord' in flags: + code += 'Fd.uw = ' + convert + 'WORD, ' + elif 'ToLong' in flags: + code += 'Fd.ud = ' + convert + 'LONG, ' + else: + sys.exit("Error Determining Destination Type for Conversion") + + #Figure out how to round value + if 'Ceil' in flags: + code += 'ceil(val)); ' + elif 'Floor' in flags: + code += 'floor(val)); ' + elif 'Round' in flags: + code += 'roundFP(val, 0)); ' + elif 'Trunc' in flags: + code += 'truncFP(val));' + else: + code += 'val); ' + + iop = InstObjParams(name, Name, 'FPOp', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format FloatAccOp(code, *flags) {{ + iop = InstObjParams(name, Name, 'FPOp', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +// Primary format for float64 operate instructions: +def format Float64Op(code, *flags) {{ + iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format FloatPSCompareOp(cond_code1, cond_code2, *flags) {{ + import sys + + code = 'bool cond1, cond2;\n' + code += 'bool code_block1, code_block2;\n' + code += 'code_block1 = code_block2 = true;\n' + + if 'QnanException' in flags: + code += 'if (isQnan(&Fs1.sf, 32) || isQnan(&Ft1.sf, 32)) {\n' + code += '\tFCSR = genInvalidVector(FCSR);\n' + code += 'code_block1 = false;' + code += '}\n' + code += 'if (isQnan(&Fs2.sf, 32) || isQnan(&Ft2.sf, 32)) {\n' + code += '\tFCSR = genInvalidVector(FCSR);\n' + code += 'code_block2 = false;' + code += '}\n' + + code += 'if (code_block1) {' + code += '\tif (isNan(&Fs1.sf, 32) || isNan(&Ft1.sf, 32)) {\n' + if 'UnorderedTrue' in flags: + code += 'cond1 = 1;\n' + elif 'UnorderedFalse' in flags: + code += 'cond1 = 0;\n' + else: + sys.exit('Decoder Failed: Float Compare Instruction Needs A Unordered Flag\n') + code += '} else {\n' + code += cond_code1 + code += 'FCSR = genCCVector(FCSR, CC, cond1);}\n}\n' + + code += 'if (code_block2) {' + code += '\tif (isNan(&Fs2.sf, 32) || isNan(&Ft2.sf, 32)) {\n' + if 'UnorderedTrue' in flags: + code += 'cond2 = 1;\n' + elif 'UnorderedFalse' in flags: + code += 'cond2 = 0;\n' + else: + sys.exit('Decoder Failed: Float Compare Instruction Needs A Unordered Flag\n') + code += '} else {\n' + code += cond_code2 + code += 'FCSR = genCCVector(FCSR, CC, cond2);}\n}' + + iop = InstObjParams(name, Name, 'FPCompareOp', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + diff --git a/src/arch/mips/isa/formats/int.isa b/src/arch/mips/isa/formats/int.isa new file mode 100644 index 000000000..654dd8921 --- /dev/null +++ b/src/arch/mips/isa/formats/int.isa @@ -0,0 +1,269 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Integer operate instructions +// +output header {{ +#include <iostream> + /** + * Base class for integer operations. + */ + class IntOp : public MipsStaticInst + { + protected: + + /// Constructor + IntOp(const char *mnem, MachInst _machInst, OpClass __opClass) : + MipsStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + + class HiLoOp: public IntOp + { + protected: + + /// Constructor + HiLoOp(const char *mnem, MachInst _machInst, OpClass __opClass) : + IntOp(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + class HiLoMiscOp: public HiLoOp + { + protected: + + /// Constructor + HiLoMiscOp(const char *mnem, MachInst _machInst, OpClass __opClass) : + HiLoOp(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + + class IntImmOp : public MipsStaticInst + { + protected: + + int16_t imm; + int32_t sextImm; + uint32_t zextImm; + + /// Constructor + IntImmOp(const char *mnem, MachInst _machInst, OpClass __opClass) : + MipsStaticInst(mnem, _machInst, __opClass),imm(INTIMM), + sextImm(INTIMM),zextImm(0x0000FFFF & INTIMM) + { + //If Bit 15 is 1 then Sign Extend + int32_t temp = sextImm & 0x00008000; + if (temp > 0 && mnemonic != "lui") { + sextImm |= 0xFFFF0000; + } + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + + + }; + +}}; + +// HiLo<Misc> instruction class execute method template. +// Mainly to get instruction trace data to print out +// correctly +def template HiLoExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(code)s; + + if(fault == NoFault) + { + %(op_wb)s; + //If there are 2 Destination Registers then + //concatenate the values for the traceData + if(traceData && _numDestRegs == 2) { + uint64_t hilo_final_val = (uint64_t)HI << 32 | LO; + traceData->setData(hilo_final_val); + } + } + return fault; + } +}}; + +//Outputs to decoder.cc +output decoder {{ + std::string IntOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + // just print the first dest... if there's a second one, + // it's generally implicit + if (_numDestRegs > 0) { + printReg(ss, _destRegIdx[0]); + ss << ", "; + } + + // just print the first two source regs... if there's + // a third one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + + if (_numSrcRegs > 1) { + ss << ", "; + printReg(ss, _srcRegIdx[1]); + } + + return ss.str(); + } + + std::string HiLoOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + //Destination Registers are implicit for HI/LO ops + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + } + + if (_numSrcRegs > 1) { + ss << ", "; + printReg(ss, _srcRegIdx[1]); + } + + return ss.str(); + } + + std::string HiLoMiscOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + if (_numDestRegs > 0 && _destRegIdx[0] < 32) { + printReg(ss, _destRegIdx[0]); + } else if (_numSrcRegs > 0 && _srcRegIdx[0] < 32) { + printReg(ss, _srcRegIdx[0]); + } + + return ss.str(); + } + + std::string IntImmOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + std::stringstream ss; + + ccprintf(ss, "%-10s ", mnemonic); + + if (_numDestRegs > 0) { + printReg(ss, _destRegIdx[0]); + } + + ss << ", "; + + if (_numSrcRegs > 0) { + printReg(ss, _srcRegIdx[0]); + ss << ", "; + } + + if( mnemonic == "lui") + ccprintf(ss, "0x%x ", sextImm); + else + ss << (int) sextImm; + + return ss.str(); + } + +}}; + +def format IntOp(code, *opt_flags) {{ + iop = InstObjParams(name, Name, 'IntOp', CodeBlock(code), opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = RegNopCheckDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format IntImmOp(code, *opt_flags) {{ + iop = InstObjParams(name, Name, 'IntImmOp', CodeBlock(code), opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = ImmNopCheckDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format HiLoOp(code, *opt_flags) {{ + if '.sd' in code: + code = 'int64_t ' + code + elif '.ud' in code: + code = 'uint64_t ' + code + + code += 'HI = val<63:32>;\n' + code += 'LO = val<31:0>;\n' + + iop = InstObjParams(name, Name, 'HiLoOp', CodeBlock(code), opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = HiLoExecute.subst(iop) +}}; + +def format HiLoMiscOp(code, *opt_flags) {{ + iop = InstObjParams(name, Name, 'HiLoMiscOp', CodeBlock(code), opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = HiLoExecute.subst(iop) +}}; + + + + + diff --git a/src/arch/mips/isa/formats/mem.isa b/src/arch/mips/isa/formats/mem.isa new file mode 100644 index 000000000..d6b0c2938 --- /dev/null +++ b/src/arch/mips/isa/formats/mem.isa @@ -0,0 +1,604 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt +// Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Memory-format instructions +// + +output header {{ + /** + * Base class for general Mips memory-format instructions. + */ + class Memory : public MipsStaticInst + { + protected: + + /// Memory request flags. See mem_req_base.hh. + unsigned memAccessFlags; + /// Pointer to EAComp object. + const StaticInstPtr eaCompPtr; + /// Pointer to MemAcc object. + const StaticInstPtr memAccPtr; + + /// Displacement for EA calculation (signed). + int32_t disp; + + /// Constructor + Memory(const char *mnem, MachInst _machInst, OpClass __opClass, + StaticInstPtr _eaCompPtr = nullStaticInstPtr, + StaticInstPtr _memAccPtr = nullStaticInstPtr) + : MipsStaticInst(mnem, _machInst, __opClass), + memAccessFlags(0), eaCompPtr(_eaCompPtr), memAccPtr(_memAccPtr), + disp(sext<16>(OFFSET)) + { + } + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + + public: + + const StaticInstPtr &eaCompInst() const { return eaCompPtr; } + const StaticInstPtr &memAccInst() const { return memAccPtr; } + }; + + /** + * Base class for a few miscellaneous memory-format insts + * that don't interpret the disp field + */ + class MemoryNoDisp : public Memory + { + protected: + /// Constructor + MemoryNoDisp(const char *mnem, ExtMachInst _machInst, OpClass __opClass, + StaticInstPtr _eaCompPtr = nullStaticInstPtr, + StaticInstPtr _memAccPtr = nullStaticInstPtr) + : Memory(mnem, _machInst, __opClass, _eaCompPtr, _memAccPtr) + { + } + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + + +output decoder {{ + std::string + Memory::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return csprintf("%-10s %c%d, %d(r%d)", mnemonic, + flags[IsFloating] ? 'f' : 'r', RT, disp, RS); + } + + std::string + MemoryNoDisp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return csprintf("%-10s %c%d, r%d(r%d)", mnemonic, + flags[IsFloating] ? 'f' : 'r', + flags[IsFloating] ? FD : RD, + RS, RT); + } +}}; + +def template LoadStoreDeclare {{ + /** + * Static instruction class for "%(mnemonic)s". + */ + class %(class_name)s : public %(base_class)s + { + protected: + + /** + * "Fake" effective address computation class for "%(mnemonic)s". + */ + class EAComp : public %(base_class)s + { + public: + /// Constructor + EAComp(MachInst machInst); + + %(BasicExecDeclare)s + }; + + /** + * "Fake" memory access instruction class for "%(mnemonic)s". + */ + class MemAcc : public %(base_class)s + { + public: + /// Constructor + MemAcc(MachInst machInst); + + %(BasicExecDeclare)s + }; + + public: + + /// Constructor. + %(class_name)s(MachInst machInst); + + %(BasicExecDeclare)s + + %(InitiateAccDeclare)s + + %(CompleteAccDeclare)s + }; +}}; + + +def template InitiateAccDeclare {{ + Fault initiateAcc(%(CPU_exec_context)s *, Trace::InstRecord *) const; +}}; + + +def template CompleteAccDeclare {{ + Fault completeAcc(Packet *, %(CPU_exec_context)s *, Trace::InstRecord *) const; +}}; + + +def template LoadStoreConstructor {{ + /** TODO: change op_class to AddrGenOp or something (requires + * creating new member of OpClass enum in op_class.hh, updating + * config files, etc.). */ + inline %(class_name)s::EAComp::EAComp(MachInst machInst) + : %(base_class)s("%(mnemonic)s (EAComp)", machInst, IntAluOp) + { + %(ea_constructor)s; + } + + inline %(class_name)s::MemAcc::MemAcc(MachInst machInst) + : %(base_class)s("%(mnemonic)s (MemAcc)", machInst, %(op_class)s) + { + %(memacc_constructor)s; + } + + inline %(class_name)s::%(class_name)s(MachInst machInst) + : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s, + new EAComp(machInst), new MemAcc(machInst)) + { + %(constructor)s; + } +}}; + + +def template EACompExecute {{ + Fault + %(class_name)s::EAComp::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(code)s; + + if (fault == NoFault) { + %(op_wb)s; + xc->setEA(EA); + } + + return fault; + } +}}; + +def template LoadMemAccExecute {{ + Fault + %(class_name)s::MemAcc::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + EA = xc->getEA(); + + if (fault == NoFault) { + fault = xc->read(EA, (uint%(mem_acc_size)d_t&)Mem, memAccessFlags); + %(code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + + +def template LoadExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + + if (fault == NoFault) { + fault = xc->read(EA, (uint%(mem_acc_size)d_t&)Mem, memAccessFlags); + %(memacc_code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + + +def template LoadInitiateAcc {{ + Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_src_decl)s; + %(op_rd)s; + %(ea_code)s; + + if (fault == NoFault) { + fault = xc->read(EA, (uint%(mem_acc_size)d_t &)Mem, memAccessFlags); + } + + return fault; + } +}}; + + +def template LoadCompleteAcc {{ + Fault %(class_name)s::completeAcc(Packet *pkt, + %(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + + Mem = pkt->get<typeof(Mem)>(); + + if (fault == NoFault) { + %(memacc_code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + + +def template StoreMemAccExecute {{ + Fault + %(class_name)s::MemAcc::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + uint64_t write_result = 0; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + EA = xc->getEA(); + + if (fault == NoFault) { + %(code)s; + } + + if (fault == NoFault) { + fault = xc->write((uint%(mem_acc_size)d_t&)Mem, EA, + memAccessFlags, &write_result); + if (traceData) { traceData->setData(Mem); } + } + + if (fault == NoFault) { + %(postacc_code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + + +def template StoreExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + uint64_t write_result = 0; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + + if (fault == NoFault) { + %(memacc_code)s; + } + + if (fault == NoFault) { + fault = xc->write((uint%(mem_acc_size)d_t&)Mem, EA, + memAccessFlags, &write_result); + if (traceData) { traceData->setData(Mem); } + } + + if (fault == NoFault) { + %(postacc_code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + +def template StoreInitiateAcc {{ + Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + + if (fault == NoFault) { + %(memacc_code)s; + } + + if (fault == NoFault) { + fault = xc->write((uint%(mem_acc_size)d_t&)Mem, EA, + memAccessFlags, NULL); + if (traceData) { traceData->setData(Mem); } + } + + return fault; + } +}}; + + +def template StoreCompleteAcc {{ + Fault %(class_name)s::completeAcc(Packet *pkt, + %(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_dest_decl)s; + + if (fault == NoFault) { + %(postacc_code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + +def template StoreCondCompleteAcc {{ + Fault %(class_name)s::completeAcc(Packet *pkt, + %(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_dest_decl)s; + + uint64_t write_result = pkt->req->getScResult(); + + if (fault == NoFault) { + %(postacc_code)s; + } + + if (fault == NoFault) { + %(op_wb)s; + } + + return fault; + } +}}; + + +def template MiscMemAccExecute {{ + Fault %(class_name)s::MemAcc::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + EA = xc->getEA(); + + if (fault == NoFault) { + %(code)s; + } + + return NoFault; + } +}}; + +def template MiscExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Addr EA; + Fault fault = NoFault; + + %(fp_enable_check)s; + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + + if (fault == NoFault) { + %(memacc_code)s; + } + + return NoFault; + } +}}; + +def template MiscInitiateAcc {{ + Fault %(class_name)s::initiateAcc(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + panic("Misc instruction does not support split access method!"); + return NoFault; + } +}}; + + +def template MiscCompleteAcc {{ + Fault %(class_name)s::completeAcc(Packet *pkt, + %(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + panic("Misc instruction does not support split access method!"); + + return NoFault; + } +}}; + +def format LoadMemory(memacc_code, ea_code = {{ EA = Rs + disp; }}, + mem_flags = [], inst_flags = []) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + decode_template = ImmNopCheckDecode, + exec_template_base = 'Load') +}}; + +def format StoreMemory(memacc_code, ea_code = {{ EA = Rs + disp; }}, + mem_flags = [], inst_flags = []) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + exec_template_base = 'Store') +}}; + +def format LoadIndexedMemory(memacc_code, ea_code = {{ EA = Rs + Rt; }}, + mem_flags = [], inst_flags = []) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + decode_template = ImmNopCheckDecode, + exec_template_base = 'Load') +}}; + +def format StoreIndexedMemory(memacc_code, ea_code = {{ EA = Rs + Rt; }}, + mem_flags = [], inst_flags = []) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + exec_template_base = 'Store') +}}; + +def format LoadUnalignedMemory(memacc_code, ea_code = {{ EA = (Rs + disp) & ~3; }}, + mem_flags = [], inst_flags = []) {{ + decl_code = 'uint32_t mem_word = Mem.uw;\n' + decl_code += 'uint32_t unalign_addr = Rs + disp;\n' + decl_code += 'uint32_t byte_offset = unalign_addr & 3;\n' + decl_code += '#if BYTE_ORDER == BIG_ENDIAN\n' + decl_code += '\tbyte_offset ^= 3;\n' + decl_code += '#endif\n' + + memacc_code = decl_code + memacc_code + + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + decode_template = ImmNopCheckDecode, + exec_template_base = 'Load') +}}; + +def format StoreUnalignedMemory(memacc_code, ea_code = {{ EA = (Rs + disp) & ~3; }}, + mem_flags = [], inst_flags = []) {{ + decl_code = 'uint32_t mem_word = 0;\n' + decl_code += 'uint32_t unaligned_addr = Rs + disp;\n' + decl_code += 'uint32_t byte_offset = unaligned_addr & 3;\n' + decl_code += '#if BYTE_ORDER == BIG_ENDIAN\n' + decl_code += '\tbyte_offset ^= 3;\n' + decl_code += '#endif\n' + decl_code += 'fault = xc->read(EA, (uint32_t&)mem_word, memAccessFlags);\n' + memacc_code = decl_code + memacc_code + '\nMem = mem_word;\n' + + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + exec_template_base = 'Store') +}}; + +def format Prefetch(ea_code = {{ EA = Rs + disp; }}, + mem_flags = [], pf_flags = [], inst_flags = []) {{ + pf_mem_flags = mem_flags + pf_flags + ['NO_FAULT'] + pf_inst_flags = inst_flags + ['IsMemRef', 'IsLoad', + 'IsDataPrefetch', 'MemReadOp'] + + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, + 'xc->prefetch(EA, memAccessFlags);', + pf_mem_flags, pf_inst_flags, exec_template_base = 'Misc') + +}}; + +def format StoreCond(memacc_code, postacc_code, + ea_code = {{ EA = Rs + disp; }}, + mem_flags = [], inst_flags = []) {{ + (header_output, decoder_output, decode_block, exec_output) = \ + LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + postacc_code, exec_template_base = 'StoreCond') +}}; diff --git a/src/arch/mips/isa/formats/mt.isa b/src/arch/mips/isa/formats/mt.isa new file mode 100644 index 000000000..96435f8c9 --- /dev/null +++ b/src/arch/mips/isa/formats/mt.isa @@ -0,0 +1,82 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// MT instructions +// + +output header {{ + /** + * Base class for MIPS MT ASE operations. + */ + class MT : public MipsStaticInst + { + protected: + + /// Constructor + MT(const char *mnem, MachInst _machInst, OpClass __opClass) : + MipsStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + //Edit This Template When MT is Implemented + std::string MT::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return "Disassembly of MT instruction\n"; + } +}}; + +def template MTExecute {{ + //Edit This Template When MT is Implemented + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + //Write the resulting state to the execution context + %(op_wb)s; + + //Call into the trap handler with the appropriate fault + return No_Fault; + } +}}; + +// Primary format for integer operate instructions: +def format MipsMT() {{ + code = 'panic(\"Mips MT Is Currently Unimplemented.\");\n' + iop = InstObjParams(name, Name, 'MT', CodeBlock(code)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; diff --git a/src/arch/mips/isa/formats/noop.isa b/src/arch/mips/isa/formats/noop.isa new file mode 100644 index 000000000..7f3d313ad --- /dev/null +++ b/src/arch/mips/isa/formats/noop.isa @@ -0,0 +1,139 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Nop +// + +output header {{ + /** + * Static instruction class for no-ops. This is a leaf class. + */ + class Nop : public MipsStaticInst + { + /// Disassembly of original instruction. + const std::string originalDisassembly; + + public: + /// Constructor + Nop(const std::string _originalDisassembly, MachInst _machInst) + : MipsStaticInst("nop", _machInst, No_OpClass), + originalDisassembly(_originalDisassembly) + { + flags[IsNop] = true; + } + + ~Nop() { } + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + + %(BasicExecDeclare)s + }; +}}; + +output decoder {{ + std::string Nop::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + return csprintf("%-10s %s", "nop", originalDisassembly); + } + + /// Helper function for decoding nops. Substitute Nop object + /// for original inst passed in as arg (and delete latter). + inline + MipsStaticInst * + makeNop(MipsStaticInst *inst) + { + std::string nop_str = "(" + inst->disassemble(0) + ")"; + MipsStaticInst *nop = new Nop(nop_str, inst->machInst); + delete inst; + return nop; + } +}}; + +output exec {{ + Fault + Nop::execute(%(CPU_exec_context)s *, Trace::InstRecord *) const + { + return NoFault; + } +}}; + +// Int & FP operate instructions use RD as dest, so check for +// RD == 0 to detect nops +def template RegNopCheckDecode {{ + { + MipsStaticInst *i = new %(class_name)s(machInst); + //if (RD == 0) { + //i = makeNop(i); + //} + return i; + } +}}; + +def template OperateNopCheckDecode {{ + { + MipsStaticInst *i = new %(class_name)s(machInst); + //if (RD == 0) { + // i = makeNop(i); + //} + return i; + } +}}; + +// IntImm & Memory instructions use Rt as dest, so check for +// Rt == 0 to detect nops +def template ImmNopCheckDecode {{ + { + MipsStaticInst *i = new %(class_name)s(machInst); + //if (RT == 0) { + // i = makeNop(i); + // } + return i; + } +}}; + + +// Like BasicOperate format, but generates NOP if RC/FC == 31 +def format BasicOperateWithNopCheck(code, *opt_args) {{ + iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), + opt_args) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = OperateNopCheckDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; + +def format Nop() {{ + decode_block = 'return new Nop(\"\",machInst);\n' +}}; + diff --git a/src/arch/mips/isa/formats/tlbop.isa b/src/arch/mips/isa/formats/tlbop.isa new file mode 100644 index 000000000..b974ccbed --- /dev/null +++ b/src/arch/mips/isa/formats/tlbop.isa @@ -0,0 +1,80 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// TlbOp instructions +// + +output header {{ + /** + * Base class for integer operations. + */ + class TlbOp : public MipsStaticInst + { + protected: + + /// Constructor + TlbOp(const char *mnem, MachInst _machInst, OpClass __opClass) : MipsStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + std::string TlbOp::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return "Disassembly of integer instruction\n"; + } +}}; + +def template TlbOpExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + //Write the resulting state to the execution context + %(op_wb)s; + + //Call into the trap handler with the appropriate fault + return No_Fault; + } +}}; + +// Primary format for integer operate instructions: +def format TlbOp(code, *opt_flags) {{ + orig_code = code + cblk = CodeBlock(code) + iop = InstObjParams(name, Name, 'MipsStaticInst', cblk, opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecodeWithMnemonic.subst(iop) + exec_output = TlbOpExecute.subst(iop) +}}; diff --git a/src/arch/mips/isa/formats/trap.isa b/src/arch/mips/isa/formats/trap.isa new file mode 100644 index 000000000..b9066f374 --- /dev/null +++ b/src/arch/mips/isa/formats/trap.isa @@ -0,0 +1,81 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Trap instructions +// + +output header {{ + /** + * Base class for integer operations. + */ + class Trap : public MipsStaticInst + { + protected: + + /// Constructor + Trap(const char *mnem, MachInst _machInst, OpClass __opClass) : MipsStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + std::string Trap::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return "Disassembly of trap instruction\n"; + } +}}; + +def template TrapExecute {{ + //Edit This Template When Traps Are Implemented + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + //Write the resulting state to the execution context + %(op_wb)s; + + //Call into the trap handler with the appropriate fault + return No_Fault; + } +}}; + +def format Trap(code, *flags) {{ + code = 'warn(\"' + code += 'Trap Exception Handler Is Currently Not Implemented.' + code += '\");' + iop = InstObjParams(name, Name, 'MipsStaticInst', CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; diff --git a/src/arch/mips/isa/formats/unimp.isa b/src/arch/mips/isa/formats/unimp.isa new file mode 100644 index 000000000..03068fa74 --- /dev/null +++ b/src/arch/mips/isa/formats/unimp.isa @@ -0,0 +1,145 @@ +// -*- mode:c++ -*- + + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Unimplemented instructions +// + +output header {{ + /** + * Static instruction class for unimplemented instructions that + * cause simulator termination. Note that these are recognized + * (legal) instructions that the simulator does not support; the + * 'Unknown' class is used for unrecognized/illegal instructions. + * This is a leaf class. + */ + class FailUnimplemented : public MipsStaticInst + { + public: + /// Constructor + FailUnimplemented(const char *_mnemonic, MachInst _machInst) + : MipsStaticInst(_mnemonic, _machInst, No_OpClass) + { + // don't call execute() (which panics) if we're on a + // speculative path + flags[IsNonSpeculative] = true; + } + + %(BasicExecDeclare)s + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; + + /** + * Base class for unimplemented instructions that cause a warning + * to be printed (but do not terminate simulation). This + * implementation is a little screwy in that it will print a + * warning for each instance of a particular unimplemented machine + * instruction, not just for each unimplemented opcode. Should + * probably make the 'warned' flag a static member of the derived + * class. + */ + class WarnUnimplemented : public MipsStaticInst + { + private: + /// Have we warned on this instruction yet? + mutable bool warned; + + public: + /// Constructor + WarnUnimplemented(const char *_mnemonic, MachInst _machInst) + : MipsStaticInst(_mnemonic, _machInst, No_OpClass), warned(false) + { + // don't call execute() (which panics) if we're on a + // speculative path + flags[IsNonSpeculative] = true; + } + + %(BasicExecDeclare)s + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + std::string + FailUnimplemented::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + return csprintf("%-10s (unimplemented)", mnemonic); + } + + std::string + WarnUnimplemented::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + return csprintf("%-10s (unimplemented)", mnemonic); + } +}}; + +output exec {{ + Fault + FailUnimplemented::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + panic("attempt to execute unimplemented instruction '%s' " + "(inst 0x%08x, opcode 0x%x, binary:%s)", mnemonic, machInst, OPCODE, + inst2string(machInst)); + return new UnimplementedOpcodeFault; + } + + Fault + WarnUnimplemented::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + if (!warned) { + warn("\tinstruction '%s' unimplemented\n", mnemonic); + warned = true; + } + + return NoFault; + } +}}; + + +def format FailUnimpl() {{ + iop = InstObjParams(name, 'FailUnimplemented') + decode_block = BasicDecodeWithMnemonic.subst(iop) +}}; + +def format WarnUnimpl() {{ + iop = InstObjParams(name, 'WarnUnimplemented') + decode_block = BasicDecodeWithMnemonic.subst(iop) +}}; + diff --git a/src/arch/mips/isa/formats/unknown.isa b/src/arch/mips/isa/formats/unknown.isa new file mode 100644 index 000000000..70b3901e9 --- /dev/null +++ b/src/arch/mips/isa/formats/unknown.isa @@ -0,0 +1,84 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Unknown instructions +// + +output header {{ + /** + * Static instruction class for unknown (illegal) instructions. + * These cause simulator termination if they are executed in a + * non-speculative mode. This is a leaf class. + */ + class Unknown : public MipsStaticInst + { + public: + /// Constructor + Unknown(MachInst _machInst) + : MipsStaticInst("unknown", _machInst, No_OpClass) + { + // don't call execute() (which panics) if we're on a + // speculative path + flags[IsNonSpeculative] = true; + } + + %(BasicExecDeclare)s + + std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + std::string + Unknown::generateDisassembly(Addr pc, const SymbolTable *symtab) const + { + return csprintf("%-10s (inst 0x%x, opcode 0x%x, binary:%s)", + "unknown", machInst, OPCODE, inst2string(machInst)); + } +}}; + +output exec {{ + Fault + Unknown::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + panic("attempt to execute unknown instruction " + "(inst 0x%08x, opcode 0x%x, binary: %s)", machInst, OPCODE, inst2string(machInst)); + return new UnimplementedOpcodeFault; + } +}}; + +def format Unknown() {{ + decode_block = 'return new Unknown(machInst);\n' +}}; + diff --git a/src/arch/mips/isa/formats/util.isa b/src/arch/mips/isa/formats/util.isa new file mode 100644 index 000000000..73164bc0d --- /dev/null +++ b/src/arch/mips/isa/formats/util.isa @@ -0,0 +1,189 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Steve Reinhardt +// Korey Sewell + +let {{ +def LoadStoreBase(name, Name, ea_code, memacc_code, mem_flags, inst_flags, + postacc_code = '', base_class = 'Memory', + decode_template = BasicDecode, exec_template_base = ''): + # Make sure flags are in lists (convert to lists if not). + mem_flags = makeList(mem_flags) + inst_flags = makeList(inst_flags) + + # add hook to get effective addresses into execution trace output. + ea_code += '\nif (traceData) { traceData->setAddr(EA); }\n' + + # generate code block objects + ea_cblk = CodeBlock(ea_code) + memacc_cblk = CodeBlock(memacc_code) + postacc_cblk = CodeBlock(postacc_code) + + # Some CPU models execute the memory operation as an atomic unit, + # while others want to separate them into an effective address + # computation and a memory access operation. As a result, we need + # to generate three StaticInst objects. Note that the latter two + # are nested inside the larger "atomic" one. + + # generate InstObjParams for EAComp object + ea_iop = InstObjParams(name, Name, base_class, ea_cblk, inst_flags) + + # generate InstObjParams for MemAcc object + memacc_iop = InstObjParams(name, Name, base_class, memacc_cblk, inst_flags) + # in the split execution model, the MemAcc portion is responsible + # for the post-access code. + memacc_iop.postacc_code = postacc_cblk.code + + # generate InstObjParams for InitiateAcc, CompleteAcc object + # The code used depends on the template being used + if (exec_template_base == 'Load'): + initiateacc_cblk = CodeBlock(ea_code + memacc_code) + completeacc_cblk = CodeBlock(memacc_code + postacc_code) + elif (exec_template_base.startswith('Store')): + initiateacc_cblk = CodeBlock(ea_code + memacc_code) + completeacc_cblk = CodeBlock(postacc_code) + else: + initiateacc_cblk = '' + completeacc_cblk = '' + + initiateacc_iop = InstObjParams(name, Name, base_class, initiateacc_cblk, + inst_flags) + + completeacc_iop = InstObjParams(name, Name, base_class, completeacc_cblk, + inst_flags) + + if (exec_template_base == 'Load'): + initiateacc_iop.ea_code = ea_cblk.code + initiateacc_iop.memacc_code = memacc_cblk.code + completeacc_iop.memacc_code = memacc_cblk.code + completeacc_iop.postacc_code = postacc_cblk.code + elif (exec_template_base.startswith('Store')): + initiateacc_iop.ea_code = ea_cblk.code + initiateacc_iop.memacc_code = memacc_cblk.code + completeacc_iop.postacc_code = postacc_cblk.code + + # generate InstObjParams for unified execution + cblk = CodeBlock(ea_code + memacc_code + postacc_code) + iop = InstObjParams(name, Name, base_class, cblk, inst_flags) + + iop.ea_constructor = ea_cblk.constructor + iop.ea_code = ea_cblk.code + iop.memacc_constructor = memacc_cblk.constructor + iop.memacc_code = memacc_cblk.code + iop.postacc_code = postacc_cblk.code + + if mem_flags: + s = '\n\tmemAccessFlags = ' + string.join(mem_flags, '|') + ';' + iop.constructor += s + memacc_iop.constructor += s + + # select templates + + # define aliases... most StoreCond templates are the same as the + # corresponding Store templates (only CompleteAcc is different). + StoreCondMemAccExecute = StoreMemAccExecute + StoreCondExecute = StoreExecute + StoreCondInitiateAcc = StoreInitiateAcc + + memAccExecTemplate = eval(exec_template_base + 'MemAccExecute') + fullExecTemplate = eval(exec_template_base + 'Execute') + initiateAccTemplate = eval(exec_template_base + 'InitiateAcc') + completeAccTemplate = eval(exec_template_base + 'CompleteAcc') + + # (header_output, decoder_output, decode_block, exec_output) + return (LoadStoreDeclare.subst(iop), LoadStoreConstructor.subst(iop), + decode_template.subst(iop), + EACompExecute.subst(ea_iop) + + memAccExecTemplate.subst(memacc_iop) + + fullExecTemplate.subst(iop) + + initiateAccTemplate.subst(initiateacc_iop) + + completeAccTemplate.subst(completeacc_iop)) +}}; +output header {{ + std::string inst2string(MachInst machInst); +}}; + +output decoder {{ + +std::string inst2string(MachInst machInst) +{ + std::string str = ""; + uint32_t mask = 0x80000000; + + for(int i=0; i < 32; i++) { + if ((machInst & mask) == 0) { + str += "0"; + } else { + str += "1"; + } + + mask = mask >> 1; + } + + return str; +} + +}}; +output exec {{ + + using namespace MipsISA; + + /// CLEAR ALL CPU INST/EXE HAZARDS + inline void + clear_exe_inst_hazards() + { + //CODE HERE + } + + + /// Check "FP enabled" machine status bit. Called when executing any FP + /// instruction in full-system mode. + /// @retval Full-system mode: NoFault if FP is enabled, FenFault + /// if not. Non-full-system mode: always returns NoFault. +#if FULL_SYSTEM + inline Fault checkFpEnableFault(%(CPU_exec_context)s *xc) + { + Fault fault = NoFault; // dummy... this ipr access should not fault + if (!Mips34k::ICSR_FPE(xc->readIpr(MipsISA::IPR_ICSR, fault))) { + fault = FloatEnableFault; + } + return fault; + } +#else + inline Fault checkFpEnableFault(%(CPU_exec_context)s *xc) + { + return NoFault; + } +#endif + + + +}}; + + diff --git a/src/arch/mips/isa/includes.isa b/src/arch/mips/isa/includes.isa new file mode 100644 index 000000000..f58c8adaa --- /dev/null +++ b/src/arch/mips/isa/includes.isa @@ -0,0 +1,82 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// Output include file directives. +// + +output header {{ +#include <sstream> +#include <iostream> +#include <iomanip> + +#include "cpu/static_inst.hh" +#include "arch/mips/isa_traits.hh" +}}; + +output decoder {{ +#include "arch/mips/isa_traits.hh" +#include "base/cprintf.hh" +#include "base/loader/symtab.hh" +#include "cpu/thread_context.hh" +#include "arch/mips/faults.hh" +#include "arch/mips/isa_traits.hh" +#include "arch/mips/utility.hh" + +#include <math.h> +#if defined(linux) +#include <fenv.h> +#endif + +using namespace MipsISA; +}}; + +output exec {{ +#include "arch/mips/faults.hh" +#include "arch/mips/isa_traits.hh" +#include "arch/mips/utility.hh" + +#include <math.h> +#if defined(linux) +#include <fenv.h> +#endif + +#ifdef FULL_SYSTEM +//#include "arch/alpha/pseudo_inst.hh" +#endif +#include "cpu/base.hh" +#include "cpu/exetrace.hh" +#include "sim/sim_exit.hh" +#include "mem/packet_impl.hh" + +using namespace MipsISA; +}}; + diff --git a/src/arch/mips/isa/main.isa b/src/arch/mips/isa/main.isa new file mode 100644 index 000000000..2d7c63cd5 --- /dev/null +++ b/src/arch/mips/isa/main.isa @@ -0,0 +1,61 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// MIPS ISA description file. +// +//////////////////////////////////////////////////////////////////// + +//Include the C++ include directives +##include "includes.isa" + +//////////////////////////////////////////////////////////////////// +// +// Namespace statement. Everything below this line will be in the +// MipsISAInst namespace. +// + +namespace MipsISA; + +//Include the bitfield definitions +##include "bitfields.isa" + +//Include the operand_types and operand definitions +##include "operands.isa" + +//Include the base class for mips instructions, and some support code +##include "base.isa" + +//Include the definitions for the instruction formats +##include "formats/formats.isa" + +//Include the decoder definition +##include "decoder.isa" diff --git a/src/arch/mips/isa/operands.isa b/src/arch/mips/isa/operands.isa new file mode 100644 index 000000000..3843dc053 --- /dev/null +++ b/src/arch/mips/isa/operands.isa @@ -0,0 +1,91 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +def operand_types {{ + 'sb' : ('signed int', 8), + 'ub' : ('unsigned int', 8), + 'sh' : ('signed int', 16), + 'uh' : ('unsigned int', 16), + 'sw' : ('signed int', 32), + 'uw' : ('unsigned int', 32), + 'sd' : ('signed int', 64), + 'ud' : ('unsigned int', 64), + 'sf' : ('float', 32), + 'df' : ('float', 64), + 'qf' : ('float', 128) +}}; + +def operands {{ + #General Purpose Integer Reg Operands + 'Rd': ('IntReg', 'uw', 'RD', 'IsInteger', 1), + 'Rs': ('IntReg', 'uw', 'RS', 'IsInteger', 2), + 'Rt': ('IntReg', 'uw', 'RT', 'IsInteger', 3), + + #Operands used for Link or Syscall Insts + 'R31': ('IntReg', 'uw','31','IsInteger', 4), + 'R2': ('IntReg', 'uw','2', 'IsInteger', 5), + + #Special Integer Reg operands + 'HI': ('IntReg', 'uw','MipsISA::HI', 'IsInteger', 6), + 'LO': ('IntReg', 'uw','MipsISA::LO', 'IsInteger', 7), + + #Immediate Value operand + 'IntImm': ('IntReg', 'uw', 'INTIMM', 'IsInteger', 3), + + #Floating Point Reg Operands + 'Fd': ('FloatReg', 'sf', 'FD', 'IsFloating', 1), + 'Fs': ('FloatReg', 'sf', 'FS', 'IsFloating', 2), + 'Ft': ('FloatReg', 'sf', 'FT', 'IsFloating', 3), + 'Fr': ('FloatReg', 'sf', 'FR', 'IsFloating', 3), + + #Special Floating Point Control Reg Operands + 'FIR': ('FloatReg', 'uw', 'MipsISA::FIR', 'IsFloating', 1), + 'FCCR': ('FloatReg', 'uw', 'MipsISA::FCCR', 'IsFloating', 2), + 'FEXR': ('FloatReg', 'uw', 'MipsISA::FEXR', 'IsFloating', 3), + 'FENR': ('FloatReg', 'uw', 'MipsISA::FENR', 'IsFloating', 3), + 'FCSR': ('FloatReg', 'uw', 'MipsISA::FCSR', 'IsFloating', 3), + + #Operands For Paired Singles FP Operations + 'Fd1': ('FloatReg', 'sf', 'FD', 'IsFloating', 4), + 'Fd2': ('FloatReg', 'sf', 'FD+1', 'IsFloating', 4), + 'Fs1': ('FloatReg', 'sf', 'FS', 'IsFloating', 5), + 'Fs2': ('FloatReg', 'sf', 'FS+1', 'IsFloating', 5), + 'Ft1': ('FloatReg', 'sf', 'FT', 'IsFloating', 6), + 'Ft2': ('FloatReg', 'sf', 'FT+1', 'IsFloating', 6), + 'Fr1': ('FloatReg', 'sf', 'FR', 'IsFloating', 7), + 'Fr2': ('FloatReg', 'sf', 'FR+1', 'IsFloating', 7), + + #Memory Operand + 'Mem': ('Mem', 'uw', None, ('IsMemRef', 'IsLoad', 'IsStore'), 4), + + #Program Counter Operands + 'NPC': ('NPC', 'uw', None, ( None, None, 'IsControl' ), 4), + 'NNPC':('NNPC', 'uw', None, ( None, None, 'IsControl' ), 4) +}}; diff --git a/src/arch/mips/isa_traits.cc b/src/arch/mips/isa_traits.cc new file mode 100644 index 000000000..3a8cb46a5 --- /dev/null +++ b/src/arch/mips/isa_traits.cc @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Korey Sewell + */ + +#include "arch/mips/isa_traits.hh" +#include "arch/mips/regfile/regfile.hh" +#include "sim/serialize.hh" +#include "base/bitfield.hh" + +using namespace MipsISA; +using namespace std; + +void +MipsISA::copyRegs(ThreadContext *src, ThreadContext *dest) +{ + panic("Copy Regs Not Implemented Yet\n"); +} + +void +MipsISA::copyMiscRegs(ThreadContext *src, ThreadContext *dest) +{ + panic("Copy Misc. Regs Not Implemented Yet\n"); +} + +void +MipsISA::MiscRegFile::copyMiscRegs(ThreadContext *tc) +{ + panic("Copy Misc. Regs Not Implemented Yet\n"); +} + +void +IntRegFile::serialize(std::ostream &os) +{ + SERIALIZE_ARRAY(regs, NumIntRegs); +} + +void +IntRegFile::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_ARRAY(regs, NumIntRegs); +} + +void +RegFile::serialize(std::ostream &os) +{ + intRegFile.serialize(os); + //SERIALIZE_ARRAY(floatRegFile, NumFloatRegs); + //SERIALZE_ARRAY(miscRegFile); + //SERIALIZE_SCALAR(miscRegs.fpcr); + //SERIALIZE_SCALAR(miscRegs.lock_flag); + //SERIALIZE_SCALAR(miscRegs.lock_addr); + SERIALIZE_SCALAR(pc); + SERIALIZE_SCALAR(npc); + SERIALIZE_SCALAR(nnpc); +} + + +void +RegFile::unserialize(Checkpoint *cp, const std::string §ion) +{ + intRegFile.unserialize(cp, section); + //UNSERIALIZE_ARRAY(floatRegFile); + //UNSERIALZE_ARRAY(miscRegFile); + //UNSERIALIZE_SCALAR(miscRegs.fpcr); + //UNSERIALIZE_SCALAR(miscRegs.lock_flag); + //UNSERIALIZE_SCALAR(miscRegs.lock_addr); + UNSERIALIZE_SCALAR(pc); + UNSERIALIZE_SCALAR(npc); + UNSERIALIZE_SCALAR(nnpc); + +} diff --git a/src/arch/mips/isa_traits.hh b/src/arch/mips/isa_traits.hh new file mode 100644 index 000000000..f85fc5bea --- /dev/null +++ b/src/arch/mips/isa_traits.hh @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Korey Sewell + */ + +#ifndef __ARCH_MIPS_ISA_TRAITS_HH__ +#define __ARCH_MIPS_ISA_TRAITS_HH__ + +#include "arch/mips/types.hh" +#include "sim/host.hh" + +namespace LittleEndianGuest {}; + +#define TARGET_MIPS + +class StaticInstPtr; + +namespace MipsISA +{ + using namespace LittleEndianGuest; + + StaticInstPtr decodeInst(ExtMachInst); + + // MIPS DOES a delay slot + #define ISA_HAS_DELAY_SLOT 1 + + const Addr PageShift = 13; + const Addr PageBytes = ULL(1) << PageShift; + const Addr PageMask = ~(PageBytes - 1); + const Addr PageOffset = PageBytes - 1; + + // return a no-op instruction... used for instruction fetch faults + const ExtMachInst NoopMachInst = 0x00000000; + + // Constants Related to the number of registers + const int NumIntArchRegs = 32; + const int NumIntSpecialRegs = 2; + const int NumFloatArchRegs = 32; + const int NumFloatSpecialRegs = 5; + const int NumControlRegs = 265; + const int NumInternalProcRegs = 0; + + const int NumIntRegs = NumIntArchRegs + NumIntSpecialRegs; //HI & LO Regs + const int NumFloatRegs = NumFloatArchRegs + NumFloatSpecialRegs;// + const int NumMiscRegs = NumControlRegs; + + const int TotalNumRegs = NumIntRegs + NumFloatRegs + + NumMiscRegs + 0/*NumInternalProcRegs*/; + + const int TotalDataRegs = NumIntRegs + NumFloatRegs; + + // Static instruction parameters + const int MaxInstSrcRegs = 3; + const int MaxInstDestRegs = 2; + + // semantically meaningful register indices + const int ZeroReg = 0; + const int AssemblerReg = 1; + const int ReturnValueReg = 2; + const int ReturnValueReg1 = 2; + const int ReturnValueReg2 = 3; + const int ArgumentReg0 = 4; + const int ArgumentReg1 = 5; + const int ArgumentReg2 = 6; + const int ArgumentReg3 = 7; + const int KernelReg0 = 26; + const int KernelReg1 = 27; + const int GlobalPointerReg = 28; + const int StackPointerReg = 29; + const int FramePointerReg = 30; + const int ReturnAddressReg = 31; + + const int SyscallNumReg = ReturnValueReg1; + const int SyscallPseudoReturnReg = ReturnValueReg1; + const int SyscallSuccessReg = ArgumentReg3; + + const int LogVMPageSize = 13; // 8K bytes + const int VMPageSize = (1 << LogVMPageSize); + + const int BranchPredAddrShiftAmt = 2; // instructions are 4-byte aligned + + const int MachineBytes = 4; + const int WordBytes = 4; + const int HalfwordBytes = 2; + const int ByteBytes = 1; + + // These help enumerate all the registers for dependence tracking. + const int FP_Base_DepTag = 34; + const int Ctrl_Base_DepTag = 257; + + const int ANNOTE_NONE = 0; + const uint32_t ITOUCH_ANNOTE = 0xffffffff; + +}; + +using namespace MipsISA; + +#endif // __ARCH_MIPS_ISA_TRAITS_HH__ diff --git a/src/arch/mips/linux/linux.cc b/src/arch/mips/linux/linux.cc new file mode 100644 index 000000000..90404af53 --- /dev/null +++ b/src/arch/mips/linux/linux.cc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#include "arch/mips/linux/linux.hh" + +#include <fcntl.h> + +// open(2) flags translation table +OpenFlagTransTable MipsLinux::openFlagTable[] = { +#ifdef _MSC_VER + { MipsLinux::TGT_O_RDONLY, _O_RDONLY }, + { MipsLinux::TGT_O_WRONLY, _O_WRONLY }, + { MipsLinux::TGT_O_RDWR, _O_RDWR }, + { MipsLinux::TGT_O_APPEND, _O_APPEND }, + { MipsLinux::TGT_O_CREAT, _O_CREAT }, + { MipsLinux::TGT_O_TRUNC, _O_TRUNC }, + { MipsLinux::TGT_O_EXCL, _O_EXCL }, +#ifdef _O_NONBLOCK + { MipsLinux::TGT_O_NONBLOCK, _O_NONBLOCK }, +#endif +#ifdef _O_NOCTTY + { MipsLinux::TGT_O_NOCTTY, _O_NOCTTY }, +#endif +#ifdef _O_SYNC + { MipsLinux::TGT_O_SYNC, _O_SYNC }, +#endif +#else /* !_MSC_VER */ + { MipsLinux::TGT_O_RDONLY, O_RDONLY }, + { MipsLinux::TGT_O_WRONLY, O_WRONLY }, + { MipsLinux::TGT_O_RDWR, O_RDWR }, + { MipsLinux::TGT_O_APPEND, O_APPEND }, + { MipsLinux::TGT_O_CREAT, O_CREAT }, + { MipsLinux::TGT_O_TRUNC, O_TRUNC }, + { MipsLinux::TGT_O_EXCL, O_EXCL }, + { MipsLinux::TGT_O_NONBLOCK, O_NONBLOCK }, + { MipsLinux::TGT_O_NOCTTY, O_NOCTTY }, +#ifdef O_SYNC + { MipsLinux::TGT_O_SYNC, O_SYNC }, +#endif +#endif /* _MSC_VER */ +}; + +const int MipsLinux::NUM_OPEN_FLAGS = + (sizeof(MipsLinux::openFlagTable)/sizeof(MipsLinux::openFlagTable[0])); + + + diff --git a/src/arch/mips/linux/linux.hh b/src/arch/mips/linux/linux.hh new file mode 100644 index 000000000..f85935bb9 --- /dev/null +++ b/src/arch/mips/linux/linux.hh @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#ifndef __ARCH_MIPS_LINUX_LINUX_HH__ +#define __ARCH_MIPS_LINUX_LINUX_HH__ + +#include "kern/linux/linux.hh" + +class MipsLinux : public Linux +{ + public: + + /// This table maps the target open() flags to the corresponding + /// host open() flags. + static OpenFlagTransTable openFlagTable[]; + + /// Number of entries in openFlagTable[]. + static const int NUM_OPEN_FLAGS; + + //@{ + /// open(2) flag values. + static const int TGT_O_RDONLY = 0x00000000; //!< O_RDONLY + static const int TGT_O_WRONLY = 0x00000001; //!< O_WRONLY + static const int TGT_O_RDWR = 0x00000002; //!< O_RDWR + static const int TGT_O_NONBLOCK = 0x00000080; //!< O_NONBLOCK + static const int TGT_O_APPEND = 0x00000008; //!< O_APPEND + static const int TGT_O_CREAT = 0x00000100; //!< O_CREAT + static const int TGT_O_TRUNC = 0x00000200; //!< O_TRUNC + static const int TGT_O_EXCL = 0x00000400; //!< O_EXCL + static const int TGT_O_NOCTTY = 0x00000800; //!< O_NOCTTY + static const int TGT_O_SYNC = 0x00000010; //!< O_SYNC + static const int TGT_O_DRD = 0x00010000; //!< O_DRD + static const int TGT_O_DIRECTIO = 0x00020000; //!< O_DIRECTIO + static const int TGT_O_CACHE = 0x00002000; //!< O_CACHE + static const int TGT_O_DSYNC = 0x00008000; //!< O_DSYNC + static const int TGT_O_RSYNC = 0x00040000; //!< O_RSYNC + //@} + + /// For mmap(). + static const unsigned TGT_MAP_ANONYMOUS = 0x800; + + //@{ + /// For getsysinfo(). + static const unsigned GSI_PLATFORM_NAME = 103; //!< platform name as string + static const unsigned GSI_CPU_INFO = 59; //!< CPU information + static const unsigned GSI_PROC_TYPE = 60; //!< get proc_type + static const unsigned GSI_MAX_CPU = 30; //!< max # cpu's on this machine + static const unsigned GSI_CPUS_IN_BOX = 55; //!< number of CPUs in system + static const unsigned GSI_PHYSMEM = 19; //!< Physical memory in KB + static const unsigned GSI_CLK_TCK = 42; //!< clock freq in Hz + //@} + + //@{ + /// For getrusage(). + static const int TGT_RUSAGE_SELF = 0; + static const int TGT_RUSAGE_CHILDREN = -1; + static const int TGT_RUSAGE_BOTH = -2; + //@} + + //@{ + /// For setsysinfo(). + static const unsigned SSI_IEEE_FP_CONTROL = 14; //!< ieee_set_fp_control() + //@} + + //@{ + /// ioctl() command codes. + static const unsigned TIOCGETP = 0x7408; + static const unsigned TIOCSETP = 0x7409; + static const unsigned TIOCSETN = 0x740a; + static const unsigned TIOCSETC = 0x7411; + static const unsigned TIOCGETC = 0x7412; + static const unsigned FIONREAD = 0x467f; + static const unsigned TIOCISATTY = 0x5480; + static const unsigned TIOCGETS = 0x7413; + static const unsigned TIOCGETA = 0x7417; + //@} + + /// For table(). + static const int TBL_SYSINFO = 12; + + /// Resource enumeration for getrlimit(). + enum rlimit_resources { + TGT_RLIMIT_CPU = 0, + TGT_RLIMIT_FSIZE = 1, + TGT_RLIMIT_DATA = 2, + TGT_RLIMIT_STACK = 3, + TGT_RLIMIT_CORE = 4, + TGT_RLIMIT_NOFILE = 5, + TGT_RLIMIT_AS = 6, + TGT_RLIMIT_RSS = 7, + TGT_RLIMIT_VMEM = 7, + TGT_RLIMIT_NPROC = 8, + TGT_RLIMIT_MEMLOCK = 9, + TGT_RLIMIT_LOCKS = 10 + }; + +}; + +#endif diff --git a/src/arch/mips/linux/process.cc b/src/arch/mips/linux/process.cc new file mode 100644 index 000000000..d182cfa30 --- /dev/null +++ b/src/arch/mips/linux/process.cc @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#include "arch/mips/linux/linux.hh" +#include "arch/mips/linux/process.hh" +#include "arch/mips/isa_traits.hh" + +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "kern/linux/linux.hh" + +#include "sim/process.hh" +#include "sim/syscall_emul.hh" + +using namespace std; +using namespace MipsISA; + +/// Target uname() handler. +static SyscallReturn +unameFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + TypedBufferArg<Linux::utsname> name(tc->getSyscallArg(0)); + + strcpy(name->sysname, "Linux"); + strcpy(name->nodename, "m5.eecs.umich.edu"); + strcpy(name->release, "2.4.20"); + strcpy(name->version, "#1 Mon Aug 18 11:32:15 EDT 2003"); + strcpy(name->machine, "mips"); + + name.copyOut(tc->getMemPort()); + return 0; +} + +/// Target sys_getsysyinfo() handler. Even though this call is +/// borrowed from Tru64, the subcases that get used appear to be +/// different in practice from those used by Tru64 processes. +static SyscallReturn +sys_getsysinfoFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + unsigned op = tc->getSyscallArg(0); + // unsigned nbytes = tc->getSyscallArg(2); + + switch (op) { + + case 45: { // GSI_IEEE_FP_CONTROL + TypedBufferArg<uint64_t> fpcr(tc->getSyscallArg(1)); + // I don't think this exactly matches the HW FPCR + *fpcr = 0; + fpcr.copyOut(tc->getMemPort()); + return 0; + } + + default: + cerr << "sys_getsysinfo: unknown op " << op << endl; + abort(); + break; + } + + return 1; +} + +/// Target sys_setsysinfo() handler. +static SyscallReturn +sys_setsysinfoFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + unsigned op = tc->getSyscallArg(0); + // unsigned nbytes = tc->getSyscallArg(2); + + switch (op) { + + case 14: { // SSI_IEEE_FP_CONTROL + TypedBufferArg<uint64_t> fpcr(tc->getSyscallArg(1)); + // I don't think this exactly matches the HW FPCR + fpcr.copyIn(tc->getMemPort()); + DPRINTFR(SyscallVerbose, "sys_setsysinfo(SSI_IEEE_FP_CONTROL): " + " setting FPCR to 0x%x\n", gtoh(*(uint64_t*)fpcr)); + return 0; + } + + default: + cerr << "sys_setsysinfo: unknown op " << op << endl; + abort(); + break; + } + + return 1; +} + + +SyscallDesc MipsLinuxProcess::syscallDescs[] = { + /* 0 */ SyscallDesc("syscall", unimplementedFunc), + /* 1 */ SyscallDesc("exit", exitFunc), + /* 2 */ SyscallDesc("fork", unimplementedFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), + /* 5 */ SyscallDesc("open", openFunc<MipsLinux>), + /* 6 */ SyscallDesc("close", closeFunc), + /* 7 */ SyscallDesc("waitpid", unimplementedFunc), + /* 8 */ SyscallDesc("creat", unimplementedFunc), + /* 9 */ SyscallDesc("link", unimplementedFunc), + /* 10 */ SyscallDesc("unlink", unlinkFunc), + /* 11 */ SyscallDesc("execve", unimplementedFunc), + /* 12 */ SyscallDesc("chdir", unimplementedFunc), + /* 13 */ SyscallDesc("time", unimplementedFunc), + /* 14 */ SyscallDesc("mknod", unimplementedFunc), + /* 15 */ SyscallDesc("chmod", chmodFunc<MipsLinux>), + /* 16 */ SyscallDesc("lchown", chownFunc), + /* 17 */ SyscallDesc("break", obreakFunc), + /* 18 */ SyscallDesc("unused#18", unimplementedFunc), + /* 19 */ SyscallDesc("lseek", lseekFunc), + /* 20 */ SyscallDesc("getpid", getpidFunc), + /* 21 */ SyscallDesc("mount", unimplementedFunc), + /* 22 */ SyscallDesc("umount", unimplementedFunc), + /* 23 */ SyscallDesc("setuid", setuidFunc), + /* 24 */ SyscallDesc("getuid", getuidFunc), + /* 25 */ SyscallDesc("stime", unimplementedFunc), + /* 26 */ SyscallDesc("ptrace", unimplementedFunc), + /* 27 */ SyscallDesc("alarm", unimplementedFunc), + /* 28 */ SyscallDesc("unused#28", unimplementedFunc), + /* 29 */ SyscallDesc("pause", unimplementedFunc), + /* 30 */ SyscallDesc("utime", unimplementedFunc), + /* 31 */ SyscallDesc("stty", unimplementedFunc), + /* 32 */ SyscallDesc("gtty", unimplementedFunc), + /* 33 */ SyscallDesc("access", unimplementedFunc), + /* 34 */ SyscallDesc("nice", unimplementedFunc), + /* 35 */ SyscallDesc("ftime", unimplementedFunc), + /* 36 */ SyscallDesc("sync", unimplementedFunc), + /* 37 */ SyscallDesc("kill", ignoreFunc), + /* 38 */ SyscallDesc("rename", unimplementedFunc), + /* 39 */ SyscallDesc("mkdir", unimplementedFunc), + /* 40 */ SyscallDesc("rmdir", unimplementedFunc), + /* 41 */ SyscallDesc("dup", unimplementedFunc), + /* 42 */ SyscallDesc("pipe", unimplementedFunc), + /* 43 */ SyscallDesc("times", unimplementedFunc), + /* 44 */ SyscallDesc("prof", unimplementedFunc), + /* 45 */ SyscallDesc("brk", obreakFunc), + /* 46 */ SyscallDesc("setgid", unimplementedFunc), + /* 47 */ SyscallDesc("getgid", getgidFunc), + /* 48 */ SyscallDesc("signal", ignoreFunc), + /* 49 */ SyscallDesc("geteuid", geteuidFunc), + /* 50 */ SyscallDesc("getegid", getegidFunc), + /* 51 */ SyscallDesc("acct", unimplementedFunc), + /* 52 */ SyscallDesc("umount2", unimplementedFunc), + /* 53 */ SyscallDesc("lock", unimplementedFunc), + /* 54 */ SyscallDesc("ioctl", ioctlFunc<MipsLinux>), + /* 55 */ SyscallDesc("fcntl", fcntlFunc), + /* 56 */ SyscallDesc("mpx", unimplementedFunc), + /* 57 */ SyscallDesc("setpgid", unimplementedFunc), + /* 58 */ SyscallDesc("ulimit", unimplementedFunc), + /* 59 */ SyscallDesc("unused#59", unimplementedFunc), + /* 60 */ SyscallDesc("umask", unimplementedFunc), + /* 61 */ SyscallDesc("chroot", unimplementedFunc), + /* 62 */ SyscallDesc("ustat", unimplementedFunc), + /* 63 */ SyscallDesc("dup2", unimplementedFunc), + /* 64 */ SyscallDesc("getppid", getpagesizeFunc), + /* 65 */ SyscallDesc("getpgrp", unimplementedFunc), + /* 66 */ SyscallDesc("setsid", unimplementedFunc), + /* 67 */ SyscallDesc("sigaction",unimplementedFunc), + /* 68 */ SyscallDesc("sgetmask", unimplementedFunc), + /* 69 */ SyscallDesc("ssetmask", unimplementedFunc), + /* 70 */ SyscallDesc("setreuid", unimplementedFunc), + /* 71 */ SyscallDesc("setregid", unimplementedFunc), + /* 72 */ SyscallDesc("sigsuspend", unimplementedFunc), + /* 73 */ SyscallDesc("sigpending", unimplementedFunc), + /* 74 */ SyscallDesc("sethostname", ignoreFunc), + /* 75 */ SyscallDesc("setrlimit", unimplementedFunc), + /* 76 */ SyscallDesc("getrlimit", unimplementedFunc), + /* 77 */ SyscallDesc("getrusage", unimplementedFunc), + /* 78 */ SyscallDesc("gettimeofday", unimplementedFunc), + /* 79 */ SyscallDesc("settimeofday", unimplementedFunc), + /* 80 */ SyscallDesc("getgroups", unimplementedFunc), + /* 81 */ SyscallDesc("setgroups", unimplementedFunc), + /* 82 */ SyscallDesc("reserved#82", unimplementedFunc), + /* 83 */ SyscallDesc("symlink", unimplementedFunc), + /* 84 */ SyscallDesc("unused#84", unimplementedFunc), + /* 85 */ SyscallDesc("readlink", unimplementedFunc), + /* 86 */ SyscallDesc("uselib", unimplementedFunc), + /* 87 */ SyscallDesc("swapon", gethostnameFunc), + /* 88 */ SyscallDesc("reboot", unimplementedFunc), + /* 89 */ SyscallDesc("readdir", unimplementedFunc), + /* 90 */ SyscallDesc("mmap", mmapFunc<MipsLinux>), + /* 91 */ SyscallDesc("munmap",munmapFunc), + /* 92 */ SyscallDesc("truncate", truncateFunc), + /* 93 */ SyscallDesc("ftruncate", ftruncateFunc), + /* 94 */ SyscallDesc("fchmod", unimplementedFunc), + /* 95 */ SyscallDesc("fchown", unimplementedFunc), + /* 96 */ SyscallDesc("getpriority", unimplementedFunc), + /* 97 */ SyscallDesc("setpriority", unimplementedFunc), + /* 98 */ SyscallDesc("profil", unimplementedFunc), + /* 99 */ SyscallDesc("statfs", unimplementedFunc), + /* 100 */ SyscallDesc("fstatfs", unimplementedFunc), + /* 101 */ SyscallDesc("ioperm", unimplementedFunc), + /* 102 */ SyscallDesc("socketcall", unimplementedFunc), + /* 103 */ SyscallDesc("syslog", unimplementedFunc), + /* 104 */ SyscallDesc("setitimer", unimplementedFunc), + /* 105 */ SyscallDesc("getitimer", unimplementedFunc), + /* 106 */ SyscallDesc("stat", statFunc<MipsLinux>), + /* 107 */ SyscallDesc("lstat", unimplementedFunc), + /* 108 */ SyscallDesc("fstat", fstatFunc<MipsLinux>), + /* 109 */ SyscallDesc("unused#109", unimplementedFunc), + /* 110 */ SyscallDesc("iopl", unimplementedFunc), + /* 111 */ SyscallDesc("vhangup", unimplementedFunc), + /* 112 */ SyscallDesc("idle", ignoreFunc), + /* 113 */ SyscallDesc("vm86", unimplementedFunc), + /* 114 */ SyscallDesc("wait4", unimplementedFunc), + /* 115 */ SyscallDesc("swapoff", unimplementedFunc), + /* 116 */ SyscallDesc("sysinfo", unimplementedFunc), + /* 117 */ SyscallDesc("ipc", unimplementedFunc), + /* 118 */ SyscallDesc("fsync", unimplementedFunc), + /* 119 */ SyscallDesc("sigreturn", unimplementedFunc), + /* 120 */ SyscallDesc("clone", unimplementedFunc), + /* 121 */ SyscallDesc("setdomainname", unimplementedFunc), + /* 122 */ SyscallDesc("uname", unameFunc), + /* 123 */ SyscallDesc("modify_ldt", unimplementedFunc), + /* 124 */ SyscallDesc("adjtimex", unimplementedFunc), + /* 125 */ SyscallDesc("mprotect", ignoreFunc), + /* 126 */ SyscallDesc("sigprocmask", unimplementedFunc), + /* 127 */ SyscallDesc("create_module", unimplementedFunc), + /* 128 */ SyscallDesc("init_module", unimplementedFunc), + /* 129 */ SyscallDesc("delete_module", unimplementedFunc), + /* 130 */ SyscallDesc("get_kernel_syms", unimplementedFunc), + /* 131 */ SyscallDesc("quotactl", unimplementedFunc), + /* 132 */ SyscallDesc("getpgid", unimplementedFunc), + /* 133 */ SyscallDesc("fchdir", unimplementedFunc), + /* 134 */ SyscallDesc("bdflush", unimplementedFunc), + /* 135 */ SyscallDesc("sysfs", unimplementedFunc), + /* 136 */ SyscallDesc("personality", unimplementedFunc), + /* 137 */ SyscallDesc("afs_syscall", unimplementedFunc), + /* 138 */ SyscallDesc("setfsuid", unimplementedFunc), + /* 139 */ SyscallDesc("setfsgid", unimplementedFunc), + /* 140 */ SyscallDesc("llseek", unimplementedFunc), + /* 141 */ SyscallDesc("getdents", unimplementedFunc), + /* 142 */ SyscallDesc("newselect", unimplementedFunc), + /* 143 */ SyscallDesc("flock", unimplementedFunc), + /* 144 */ SyscallDesc("msync", unimplementedFunc), + /* 145 */ SyscallDesc("readv", unimplementedFunc), + /* 146 */ SyscallDesc("writev", writevFunc<MipsLinux>), + /* 147 */ SyscallDesc("cacheflush", unimplementedFunc), + /* 148 */ SyscallDesc("cachectl", unimplementedFunc), + /* 149 */ SyscallDesc("sysmips", unimplementedFunc), + /* 150 */ SyscallDesc("unused#150", unimplementedFunc), + /* 151 */ SyscallDesc("getsid", unimplementedFunc), + /* 152 */ SyscallDesc("fdatasync", unimplementedFunc), + /* 153 */ SyscallDesc("sysctl", unimplementedFunc), + /* 154 */ SyscallDesc("mlock", unimplementedFunc), + /* 155 */ SyscallDesc("munlock", unimplementedFunc), + /* 156 */ SyscallDesc("mlockall", unimplementedFunc), + /* 157 */ SyscallDesc("munlockall", unimplementedFunc), + /* 158 */ SyscallDesc("sched_setparam", unimplementedFunc), + /* 159 */ SyscallDesc("sched_getparam", unimplementedFunc), + /* 160 */ SyscallDesc("sched_setscheduler", unimplementedFunc), + /* 161 */ SyscallDesc("sched_getscheduler", unimplementedFunc), + /* 162 */ SyscallDesc("sched_yield", unimplementedFunc), + /* 163 */ SyscallDesc("sched_get_prioritymax", unimplementedFunc), + /* 164 */ SyscallDesc("sched_get_priority_min", unimplementedFunc), + /* 165 */ SyscallDesc("sched_rr_get_interval", unimplementedFunc), + /* 166 */ SyscallDesc("nanosleep", unimplementedFunc), + /* 167 */ SyscallDesc("mremap", unimplementedFunc), + /* 168 */ SyscallDesc("accept", unimplementedFunc), + /* 169 */ SyscallDesc("bind", unimplementedFunc), + /* 170 */ SyscallDesc("connect", unimplementedFunc), + /* 171 */ SyscallDesc("getpeername", unimplementedFunc), + /* 172 */ SyscallDesc("getsockname", unimplementedFunc), + /* 173 */ SyscallDesc("getsockopt", unimplementedFunc), + /* 174 */ SyscallDesc("listen", unimplementedFunc), + /* 175 */ SyscallDesc("recv", unimplementedFunc), + /* 176 */ SyscallDesc("recvmsg", unimplementedFunc), + /* 177 */ SyscallDesc("send", unimplementedFunc), + /* 178 */ SyscallDesc("sendmsg", ignoreFunc), + /* 179 */ SyscallDesc("sendto", unimplementedFunc), + /* 180 */ SyscallDesc("setsockopt", unimplementedFunc), + /* 181 */ SyscallDesc("shutdown", unimplementedFunc), + /* 182 */ SyscallDesc("unknown #182", unimplementedFunc), + /* 183 */ SyscallDesc("socket", ignoreFunc), + /* 184 */ SyscallDesc("socketpair", unimplementedFunc), + /* 185 */ SyscallDesc("setresuid", unimplementedFunc), + /* 186 */ SyscallDesc("getresuid", unimplementedFunc), + /* 187 */ SyscallDesc("query_module", unimplementedFunc), + /* 188 */ SyscallDesc("poll", unimplementedFunc), + /* 189 */ SyscallDesc("nfsservctl", unimplementedFunc), + /* 190 */ SyscallDesc("setresgid", unimplementedFunc), + /* 191 */ SyscallDesc("getresgid", unimplementedFunc), + /* 192 */ SyscallDesc("prctl", unimplementedFunc), + /* 193 */ SyscallDesc("rt_sigreturn", unimplementedFunc), + /* 194 */ SyscallDesc("rt_sigaction", ignoreFunc), + /* 195 */ SyscallDesc("rt_sigprocmask", ignoreFunc), + /* 196 */ SyscallDesc("rt_sigpending", unimplementedFunc), + /* 197 */ SyscallDesc("rt_sigtimedwait", unimplementedFunc), + /* 198 */ SyscallDesc("rt_sigqueueinfo", ignoreFunc), + /* 199 */ SyscallDesc("rt_sigsuspend", unimplementedFunc), + /* 200 */ SyscallDesc("pread64", unimplementedFunc), + /* 201 */ SyscallDesc("pwrite64", unimplementedFunc), + /* 202 */ SyscallDesc("chown", unimplementedFunc), + /* 203 */ SyscallDesc("getcwd", unimplementedFunc), + /* 204 */ SyscallDesc("capget", unimplementedFunc), + /* 205 */ SyscallDesc("capset", unimplementedFunc), + /* 206 */ SyscallDesc("sigalstack", unimplementedFunc), + /* 207 */ SyscallDesc("sendfile", unimplementedFunc), + /* 208 */ SyscallDesc("getpmsg", unimplementedFunc), + /* 209 */ SyscallDesc("putpmsg", unimplementedFunc), + /* 210 */ SyscallDesc("mmap2", unimplementedFunc), + /* 211 */ SyscallDesc("truncate64", unimplementedFunc), + /* 212 */ SyscallDesc("ftruncate64", unimplementedFunc), + /* 213 */ SyscallDesc("stat64", unimplementedFunc), + /* 214 */ SyscallDesc("lstat64", lstat64Func<MipsLinux>), + /* 215 */ SyscallDesc("fstat64", fstat64Func<MipsLinux>), + /* 216 */ SyscallDesc("pivot_root", unimplementedFunc), + /* 217 */ SyscallDesc("mincore", unimplementedFunc), + /* 218 */ SyscallDesc("madvise", unimplementedFunc), + /* 219 */ SyscallDesc("getdents64", unimplementedFunc), + /* 220 */ SyscallDesc("fcntl64", fcntl64Func), + /* 221 */ SyscallDesc("reserved#221", unimplementedFunc), + /* 222 */ SyscallDesc("gettid", unimplementedFunc), + /* 223 */ SyscallDesc("readahead", unimplementedFunc), + /* 224 */ SyscallDesc("setxattr", unimplementedFunc), + /* 225 */ SyscallDesc("lsetxattr", unimplementedFunc), + /* 226 */ SyscallDesc("fsetxattr", unimplementedFunc), + /* 227 */ SyscallDesc("getxattr", unimplementedFunc), + /* 228 */ SyscallDesc("lgetxattr", unimplementedFunc), + /* 229 */ SyscallDesc("fgetxattr", unimplementedFunc), + /* 230 */ SyscallDesc("listxattr", unimplementedFunc), + /* 231 */ SyscallDesc("llistxattr", unimplementedFunc), + /* 232 */ SyscallDesc("flistxattr", unimplementedFunc), + /* 233 */ SyscallDesc("removexattr", unimplementedFunc), + /* 234 */ SyscallDesc("lremovexattr", unimplementedFunc), + /* 235 */ SyscallDesc("fremovexattr", ignoreFunc), + /* 236 */ SyscallDesc("tkill", unimplementedFunc), + /* 237 */ SyscallDesc("sendfile64", unimplementedFunc), + /* 238 */ SyscallDesc("futex", unimplementedFunc), + /* 239 */ SyscallDesc("sched_setaffinity", unimplementedFunc), + /* 240 */ SyscallDesc("sched_getaffinity", unimplementedFunc), + /* 241 */ SyscallDesc("io_setup", unimplementedFunc), + /* 242 */ SyscallDesc("io_destroy", unimplementedFunc), + /* 243 */ SyscallDesc("io_getevents", unimplementedFunc), + /* 244 */ SyscallDesc("io_submit", unimplementedFunc), + /* 245 */ SyscallDesc("io_cancel", unimplementedFunc), + /* 246 */ SyscallDesc("exit_group", exitFunc), + /* 247 */ SyscallDesc("lookup_dcookie", unimplementedFunc), + /* 248 */ SyscallDesc("epoll_create", unimplementedFunc), + /* 249 */ SyscallDesc("epoll_ctl", unimplementedFunc), + /* 250 */ SyscallDesc("epoll_wait", unimplementedFunc), + /* 251 */ SyscallDesc("remap_file_pages", unimplementedFunc), + /* 252 */ SyscallDesc("set_tid_address", unimplementedFunc), + /* 253 */ SyscallDesc("restart_syscall", unimplementedFunc), + /* 254 */ SyscallDesc("fadvise64", unimplementedFunc), + /* 255 */ SyscallDesc("statfs64", unimplementedFunc), + /* 256 */ SyscallDesc("fstafs64", unimplementedFunc), + /* 257 */ SyscallDesc("timer_create", sys_getsysinfoFunc), + /* 258 */ SyscallDesc("timer_settime", sys_setsysinfoFunc), + /* 259 */ SyscallDesc("timer_gettime", unimplementedFunc), + /* 260 */ SyscallDesc("timer_getoverrun", unimplementedFunc), + /* 261 */ SyscallDesc("timer_delete", unimplementedFunc), + /* 262 */ SyscallDesc("clock_settime", unimplementedFunc), + /* 263 */ SyscallDesc("clock_gettime", unimplementedFunc), + /* 264 */ SyscallDesc("clock_getres", unimplementedFunc), + /* 265 */ SyscallDesc("clock_nanosleep", unimplementedFunc), + /* 266 */ SyscallDesc("tgkill", unimplementedFunc), + /* 267 */ SyscallDesc("utimes", unimplementedFunc), + /* 268 */ SyscallDesc("mbind", unimplementedFunc), + /* 269 */ SyscallDesc("get_mempolicy", unimplementedFunc), + /* 270 */ SyscallDesc("set_mempolicy", unimplementedFunc), + /* 271 */ SyscallDesc("mq_open", unimplementedFunc), + /* 272 */ SyscallDesc("mq_unlink", unimplementedFunc), + /* 273 */ SyscallDesc("mq_timedsend", unimplementedFunc), + /* 274 */ SyscallDesc("mq_timedreceive", unimplementedFunc), + /* 275 */ SyscallDesc("mq_notify", unimplementedFunc), + /* 276 */ SyscallDesc("mq_getsetattr", unimplementedFunc), + /* 277 */ SyscallDesc("vserver", unimplementedFunc), + /* 278 */ SyscallDesc("waitid", unimplementedFunc), + /* 279 */ SyscallDesc("unknown #279", unimplementedFunc), + /* 280 */ SyscallDesc("add_key", unimplementedFunc), + /* 281 */ SyscallDesc("request_key", unimplementedFunc), + /* 282 */ SyscallDesc("keyctl", unimplementedFunc), +}; + +MipsLinuxProcess::MipsLinuxProcess(const std::string &name, + ObjectFile *objFile, + System *system, + int stdin_fd, + int stdout_fd, + int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, + uint64_t _euid, + uint64_t _gid, + uint64_t _egid, + uint64_t _pid, + uint64_t _ppid) + : MipsLiveProcess(name, objFile, system, stdin_fd, stdout_fd, stderr_fd, + argv, envp, _uid, _euid, _gid, _egid, _pid, _ppid), + Num_Syscall_Descs(sizeof(syscallDescs) / sizeof(SyscallDesc)) +{ } + +SyscallDesc* +MipsLinuxProcess::getDesc(int callnum) +{ + //MIPS32 syscalls are in the range of 4000 - 4999 + int m5_sys_idx = callnum - 4000; + + if (m5_sys_idx < 0 || m5_sys_idx > Num_Syscall_Descs) + return NULL; + + return &syscallDescs[m5_sys_idx]; +} diff --git a/src/arch/mips/linux/process.hh b/src/arch/mips/linux/process.hh new file mode 100644 index 000000000..06f86379c --- /dev/null +++ b/src/arch/mips/linux/process.hh @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2003-2004 The Regents of The University of Michigan + * 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 __MIPS_LINUX_PROCESS_HH__ +#define __MIPS_LINUX_PROCESS_HH__ + +#include "arch/mips/process.hh" + + +/// A process with emulated Mips/Linux syscalls. +class MipsLinuxProcess : public MipsLiveProcess +{ + public: + /// Constructor. + MipsLinuxProcess(const std::string &name, + ObjectFile *objFile, + System *system, + int stdin_fd, int stdout_fd, int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid); + + virtual SyscallDesc* getDesc(int callnum); + + /// The target system's hostname. + static const char *hostname; + + /// Array of syscall descriptors, indexed by call number. + static SyscallDesc syscallDescs[]; + + const int Num_Syscall_Descs; +}; + + +#endif // __MIPS_LINUX_PROCESS_HH__ diff --git a/src/arch/mips/process.cc b/src/arch/mips/process.cc new file mode 100644 index 000000000..4bc91ca24 --- /dev/null +++ b/src/arch/mips/process.cc @@ -0,0 +1,71 @@ + +/* + * Copyright (c) 2003-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + * Korey Sewell + */ + +#include "arch/mips/isa_traits.hh" +#include "arch/mips/process.hh" +#include "base/loader/object_file.hh" +#include "base/misc.hh" +#include "cpu/thread_context.hh" +#include "sim/system.hh" + +using namespace std; +using namespace MipsISA; + +MipsLiveProcess::MipsLiveProcess(const std::string &nm, ObjectFile *objFile, + System *_system, int stdin_fd, int stdout_fd, int stderr_fd, + std::vector<std::string> &argv, std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid) + : LiveProcess(nm, objFile, _system, stdin_fd, stdout_fd, stderr_fd, + argv, envp, _uid, _euid, _gid, _egid, _pid, _ppid) +{ + // Set up stack. On MIPS, stack starts at the top of kuseg + // user address space. MIPS stack grows down from here + stack_base = 0x7FFFFFFF; + + // Set pointer for next thread stack. Reserve 8M for main stack. + next_thread_stack_base = stack_base - (8 * 1024 * 1024); + + // Set up break point (Top of Heap) + brk_point = objFile->dataBase() + objFile->dataSize() + objFile->bssSize(); + brk_point = roundUp(brk_point, VMPageSize); + + // Set up region for mmaps. For now, start at bottom of kuseg space. + mmap_start = mmap_end = 0x10000; +} + +void +MipsLiveProcess::startup() +{ + argsInit(MachineBytes, VMPageSize); +} diff --git a/src/arch/mips/process.hh b/src/arch/mips/process.hh new file mode 100644 index 000000000..fb004375d --- /dev/null +++ b/src/arch/mips/process.hh @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2003-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + * Korey Sewell + */ + +#ifndef __MIPS_PROCESS_HH__ +#define __MIPS_PROCESS_HH__ + +#include <string> +#include <vector> +#include "sim/process.hh" + +class LiveProcess; +class ObjectFile; +class System; + +class MipsLiveProcess : public LiveProcess +{ + protected: + MipsLiveProcess(const std::string &nm, ObjectFile *objFile, + System *_system, int stdin_fd, int stdout_fd, int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid); + + void startup(); + +}; + + +#endif // __MIPS_PROCESS_HH__ diff --git a/src/arch/mips/regfile.hh b/src/arch/mips/regfile.hh new file mode 100644 index 000000000..4b2f1ac35 --- /dev/null +++ b/src/arch/mips/regfile.hh @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#ifndef __ARCH_MIPS_REGFILE_HH__ +#define __ARCH_MIPS_REGFILE_HH__ + +#include "arch/mips/regfile/regfile.hh" + +#endif diff --git a/src/arch/mips/regfile/float_regfile.hh b/src/arch/mips/regfile/float_regfile.hh new file mode 100644 index 000000000..ce5f1fdde --- /dev/null +++ b/src/arch/mips/regfile/float_regfile.hh @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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 __ARCH_MIPS_REGFILE_FLOAT_REGFILE_HH__ +#define __ARCH_MIPS_REGFILE_FLOAT_REGFILE_HH__ + +#include "arch/mips/types.hh" +#include "arch/mips/isa_traits.hh" +#include "base/misc.hh" +#include "base/bitfield.hh" +#include "sim/faults.hh" + +#include <string> + +class Checkpoint; + +namespace MipsISA +{ + static inline std::string getFloatRegName(RegIndex) + { + return ""; + } + + const uint32_t MIPS32_QNAN = 0x7fbfffff; + const uint64_t MIPS64_QNAN = ULL(0x7fbfffffffffffff); + + enum FPControlRegNums { + FIR = NumFloatArchRegs, + FCCR, + FEXR, + FENR, + FCSR + }; + + enum FCSRBits { + Inexact = 1, + Underflow, + Overflow, + DivideByZero, + Invalid, + Unimplemented + }; + + enum FCSRFields { + Flag_Field = 1, + Enable_Field = 6, + Cause_Field = 11 + }; + + const int SingleWidth = 32; + const int SingleBytes = SingleWidth / 4; + + const int DoubleWidth = 64; + const int DoubleBytes = DoubleWidth / 4; + + const int QuadWidth = 128; + const int QuadBytes = QuadWidth / 4; + + class FloatRegFile + { + protected: + FloatReg32 regs[NumFloatRegs]; + + public: + + void clear() + { + bzero(regs, sizeof(regs)); + } + + double readReg(int floatReg, int width) + { + switch(width) + { + case SingleWidth: + { + void *float_ptr = ®s[floatReg]; + return *(float *) float_ptr; + } + + case DoubleWidth: + { + uint64_t double_val = (FloatReg64)regs[floatReg + 1] << 32 | regs[floatReg]; + void *double_ptr = &double_val; + return *(double *) double_ptr; + } + + default: + panic("Attempted to read a %d bit floating point register!", width); + } + } + + FloatRegBits readRegBits(int floatReg, int width) + { + if (floatReg < NumFloatArchRegs - 1) { + switch(width) + { + case SingleWidth: + return regs[floatReg]; + + case DoubleWidth: + return (FloatReg64)regs[floatReg + 1] << 32 | regs[floatReg]; + + default: + panic("Attempted to read a %d bit floating point register!", width); + } + } else { + if (width > SingleWidth) + assert("Control Regs are only 32 bits wide"); + + return regs[floatReg]; + } + } + + Fault setReg(int floatReg, const FloatRegVal &val, int width) + { + switch(width) + { + case SingleWidth: + { + float temp = val; + void *float_ptr = &temp; + regs[floatReg] = *(FloatReg32 *) float_ptr; + break; + } + + case DoubleWidth: + { + const void *double_ptr = &val; + FloatReg64 temp_double = *(FloatReg64 *) double_ptr; + regs[floatReg + 1] = bits(temp_double, 63, 32); + regs[floatReg] = bits(temp_double, 31, 0); + break; + } + + default: + panic("Attempted to read a %d bit floating point register!", width); + } + + return NoFault; + } + + Fault setRegBits(int floatReg, const FloatRegBits &val, int width) + { + + switch(width) + { + case SingleWidth: + regs[floatReg] = val; + break; + + case DoubleWidth: + regs[floatReg + 1] = bits(val, 63, 32); + regs[floatReg] = bits(val, 31, 0); + break; + + default: + panic("Attempted to read a %d bit floating point register!", width); + } + return NoFault; + } + + void serialize(std::ostream &os); + + void unserialize(Checkpoint *cp, const std::string §ion); + }; + +} // namespace MipsISA + +#endif diff --git a/src/arch/mips/regfile/int_regfile.hh b/src/arch/mips/regfile/int_regfile.hh new file mode 100644 index 000000000..a45a17a85 --- /dev/null +++ b/src/arch/mips/regfile/int_regfile.hh @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#ifndef __ARCH_MIPS_REGFILE_INT_REGFILE_HH__ +#define __ARCH_MIPS_REGFILE_INT_REGFILE_HH__ + +#include "arch/mips/types.hh" +#include "arch/mips/isa_traits.hh" +#include "base/misc.hh" +#include "sim/faults.hh" + +class Checkpoint; +class ThreadContext; + +namespace MipsISA +{ + static inline std::string getIntRegName(RegIndex) + { + return ""; + } + + enum MiscIntRegNums { + HI = NumIntArchRegs, + LO + }; + + class IntRegFile + { + protected: + IntReg regs[NumIntRegs]; + + public: + IntReg readReg(int intReg) + { + return regs[intReg]; + } + + Fault setReg(int intReg, const IntReg &val) + { + regs[intReg] = val; + return NoFault; + } + + void serialize(std::ostream &os); + + void unserialize(Checkpoint *cp, const std::string §ion); + + }; + +} // namespace MipsISA + +#endif diff --git a/src/arch/mips/regfile/misc_regfile.hh b/src/arch/mips/regfile/misc_regfile.hh new file mode 100644 index 000000000..a4527a203 --- /dev/null +++ b/src/arch/mips/regfile/misc_regfile.hh @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#ifndef __ARCH_MIPS_REGFILE_MISC_REGFILE_HH__ +#define __ARCH_MIPS_REGFILE_MISC_REGFILE_HH__ + +#include "arch/mips/types.hh" +#include "sim/faults.hh" + +class ThreadContext; + +namespace MipsISA +{ + static inline std::string getMiscRegName(RegIndex) + { + return ""; + } + + //Coprocessor 0 Register Names + enum MiscRegTags { + //Reference MIPS32 Arch. for Programmers, Vol. III, Ch.8 + //(Register Number-Register Select) Summary of Register + //------------------------------------------------------ + Index = 0, //Bank 0: 0 - 3 + MVPControl, + MVPConf0, + MVPConf1, + + Random = 8, //Bank 1: 8 - 15 + VPEControl, + VPEConf0, + VPEConf1, + YQMask, + VPESchedule, + VPEScheFBack, + VPEOpt, + + EntryLo0 = 16, //Bank 2: 16 - 23 + TCStatus, + TCBind, + TCRestart, + TCHalt, + TCContext, + TCSchedule, + TCScheFBack, + + EntryLo1 = 24, // Bank 3: 24 + + Context = 32, // Bank 4: 32 - 33 + ContextConfig, + + //PageMask = 40, //Bank 5: 40 - 41 + PageGrain = 41, + + Wired = 48, //Bank 6: 48 - 55 + SRSConf0, + SRSConf1, + SRSConf2, + SRSConf3, + SRSConf4, + + HWRena = 56, //Bank 7: 56 + + BadVAddr = 63, //Bank 8: 63 + + Count = 64, //Bank 9: 64 + + EntryHi = 72, //Bank 10:72 - 79 + + Compare = 80, //Bank 10:80 - 87 + + Status = 88, //Bank 12:88 - 96 + IntCtl = 89, + SRSCtl = 90, + SRSMap = 91, + + Cause = 97, //97-104 + + EPC = 105, //105-112 + + PRId = 113, //113-120, + EBase = 114, + + Config = 121, //Bank 16: 121-128 + Config1 = 122, + Config2 = 123, + Config3 = 124, + Config6 = 127, + Config7 = 128, + + + LLAddr = 129, //Bank 17: 129-136 + + WatchLo0 = 137, //Bank 18: 137-144 + WatchLo1 = 138, + WatchLo2 = 139, + WatchLo3 = 140, + WatchLo4 = 141, + WatchLo5 = 142, + WatchLo6 = 143, + WatchLo7 = 144, + + WatchHi0 = 145,//Bank 19: 145-152 + WatchHi1 = 146, + WatchHi2 = 147, + WatchHi3 = 148, + WatchHi4 = 149, + WatchHi5 = 150, + WatchHi6 = 151, + WatchHi7 = 152, + + XCContext64 = 153, //Bank 20: 153-160 + + //Bank 21: 161-168 + + //Bank 22: 169-176 + + Debug = 177, //Bank 23: 177-184 + TraceControl1 = 178, + TraceControl2 = 179, + UserTraceData = 180, + TraceBPC = 181, + + DEPC = 185,//Bank 24: 185-192 + + PerfCnt0 = 193,//Bank 25: 193 - 200 + PerfCnt1 = 194, + PerfCnt2 = 195, + PerfCnt3 = 196, + PerfCnt4 = 197, + PerfCnt5 = 198, + PerfCnt6 = 199, + PerfCnt7 = 200, + + ErrCtl = 201, //Bank 26: 201 - 208 + + CacheErr0 = 209, //Bank 27: 209 - 216 + CacheErr1 = 210, + CacheErr2 = 211, + CacheErr3 = 212, + + TagLo0 = 217,//Bank 28: 217 - 224 + DataLo1 = 218, + TagLo2 = 219, + DataLo3 = 220, + TagLo4 = 221, + DataLo5 = 222, + TagLo6 = 223, + DataLo7 = 234, + + TagHi0 = 233,//Bank 29: 233 - 240 + DataHi1 = 234, + TagHi2 = 235, + DataHi3 = 236, + TagHi4 = 237, + DataHi5 = 238, + TagHi6 = 239, + DataHi7 = 240, + + + ErrorEPC = 249,//Bank 30: 241 - 248 + + DESAVE = 257//Bank 31: 249-256 + }; + + class MiscRegFile { + + protected: + uint64_t fpcr; // floating point condition codes + // FPCR is not used in MIPS. Condition + // codes are kept as part of the FloatRegFile + + bool lock_flag; // lock flag for LL/SC + // use LL reg. in the future + + Addr lock_addr; // lock address for LL/SC + // use LLAddr reg. in the future + + MiscReg miscRegFile[NumMiscRegs]; + + public: + void clear() + { + fpcr = 0; + lock_flag = 0; + lock_addr = 0; + } + + void copyMiscRegs(ThreadContext *tc); + + MiscReg readReg(int misc_reg) + { + return miscRegFile[misc_reg]; + } + + MiscReg readRegWithEffect(int misc_reg, Fault &fault, ThreadContext *tc) + { + return miscRegFile[misc_reg]; + } + + Fault setReg(int misc_reg, const MiscReg &val) + { + miscRegFile[misc_reg] = val; return NoFault; + } + + Fault setRegWithEffect(int misc_reg, const MiscReg &val, + ThreadContext *tc) + { + miscRegFile[misc_reg] = val; return NoFault; + } + + friend class RegFile; + }; +} // namespace MipsISA + +#endif diff --git a/src/arch/mips/regfile/regfile.hh b/src/arch/mips/regfile/regfile.hh new file mode 100644 index 000000000..3a18c681b --- /dev/null +++ b/src/arch/mips/regfile/regfile.hh @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#ifndef __ARCH_MIPS_REGFILE_REGFILE_HH__ +#define __ARCH_MIPS_REGFILE_REGFILE_HH__ + +#include "arch/mips/types.hh" +#include "arch/mips/regfile/int_regfile.hh" +#include "arch/mips/regfile/float_regfile.hh" +#include "arch/mips/regfile/misc_regfile.hh" +#include "sim/faults.hh" + +class Checkpoint; +class ThreadContext; + +namespace MipsISA +{ + class RegFile { + protected: + IntRegFile intRegFile; // (signed) integer register file + FloatRegFile floatRegFile; // floating point register file + MiscRegFile miscRegFile; // control register file + + public: + + void clear() + { + bzero(&intRegFile, sizeof(intRegFile)); + bzero(&floatRegFile, sizeof(floatRegFile)); + bzero(&miscRegFile, sizeof(miscRegFile)); + } + + MiscReg readMiscReg(int miscReg) + { + return miscRegFile.readReg(miscReg); + } + + MiscReg readMiscRegWithEffect(int miscReg, + Fault &fault, ThreadContext *tc) + { + fault = NoFault; + return miscRegFile.readRegWithEffect(miscReg, fault, tc); + } + + Fault setMiscReg(int miscReg, const MiscReg &val) + { + return miscRegFile.setReg(miscReg, val); + } + + Fault setMiscRegWithEffect(int miscReg, const MiscReg &val, + ThreadContext * tc) + { + return miscRegFile.setRegWithEffect(miscReg, val, tc); + } + + FloatRegVal readFloatReg(int floatReg) + { + return floatRegFile.readReg(floatReg,SingleWidth); + } + + FloatRegVal readFloatReg(int floatReg, int width) + { + return floatRegFile.readReg(floatReg,width); + } + + FloatRegBits readFloatRegBits(int floatReg) + { + return floatRegFile.readRegBits(floatReg,SingleWidth); + } + + FloatRegBits readFloatRegBits(int floatReg, int width) + { + return floatRegFile.readRegBits(floatReg,width); + } + + Fault setFloatReg(int floatReg, const FloatRegVal &val) + { + return floatRegFile.setReg(floatReg, val, SingleWidth); + } + + Fault setFloatReg(int floatReg, const FloatRegVal &val, int width) + { + return floatRegFile.setReg(floatReg, val, width); + } + + Fault setFloatRegBits(int floatReg, const FloatRegBits &val) + { + return floatRegFile.setRegBits(floatReg, val, SingleWidth); + } + + Fault setFloatRegBits(int floatReg, const FloatRegBits &val, int width) + { + return floatRegFile.setRegBits(floatReg, val, width); + } + + IntReg readIntReg(int intReg) + { + return intRegFile.readReg(intReg); + } + + Fault setIntReg(int intReg, const IntReg &val) + { + return intRegFile.setReg(intReg, val); + } + protected: + + Addr pc; // program counter + Addr npc; // next-cycle program counter + Addr nnpc; // next-next-cycle program counter + // used to implement branch delay slot + // not real register + public: + Addr readPC() + { + return pc; + } + + void setPC(Addr val) + { + pc = val; + } + + Addr readNextPC() + { + return npc; + } + + void setNextPC(Addr val) + { + npc = val; + } + + Addr readNextNPC() + { + return nnpc; + } + + void setNextNPC(Addr val) + { + nnpc = val; + } + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + void changeContext(RegContextParam param, RegContextVal val) + { + } + }; + + void copyRegs(ThreadContext *src, ThreadContext *dest); + + void copyMiscRegs(ThreadContext *src, ThreadContext *dest); + +} // namespace MipsISA + +#endif diff --git a/src/arch/mips/stacktrace.hh b/src/arch/mips/stacktrace.hh new file mode 100644 index 000000000..f9e092dbd --- /dev/null +++ b/src/arch/mips/stacktrace.hh @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#ifndef __ARCH_MIPS_STACKTRACE_HH__ +#define __ARCH_MIPS_STACKTRACE_HH__ + +#include "base/trace.hh" +#include "cpu/static_inst.hh" + +class ThreadContext; +class StackTrace; + +class ProcessInfo +{ + private: + ThreadContext *tc; + + int thread_info_size; + int task_struct_size; + int task_off; + int pid_off; + int name_off; + + public: + ProcessInfo(ThreadContext *_tc); + + Addr task(Addr ksp) const; + int pid(Addr ksp) const; + std::string name(Addr ksp) const; +}; + +class StackTrace +{ + protected: + typedef TheISA::MachInst MachInst; + private: + ThreadContext *tc; + std::vector<Addr> stack; + + private: + bool isEntry(Addr addr); + bool decodePrologue(Addr sp, Addr callpc, Addr func, int &size, Addr &ra); + bool decodeSave(MachInst inst, int ®, int &disp); + bool decodeStack(MachInst inst, int &disp); + + void trace(ThreadContext *tc, bool is_call); + + public: + StackTrace(); + StackTrace(ThreadContext *tc, StaticInstPtr inst); + ~StackTrace(); + + void clear() + { + tc = 0; + stack.clear(); + } + + bool valid() const { return tc != NULL; } + bool trace(ThreadContext *tc, StaticInstPtr inst); + + public: + const std::vector<Addr> &getstack() const { return stack; } + + static const int user = 1; + static const int console = 2; + static const int unknown = 3; + +#if TRACING_ON + private: + void dump(); + + public: + void dprintf() { if (DTRACE(Stack)) dump(); } +#else + public: + void dprintf() {} +#endif +}; + +inline bool +StackTrace::trace(ThreadContext *tc, StaticInstPtr inst) +{ + if (!inst->isCall() && !inst->isReturn()) + return false; + + if (valid()) + clear(); + + trace(tc, !inst->isReturn()); + return true; +} + +#endif // __ARCH_MIPS_STACKTRACE_HH__ diff --git a/src/arch/mips/syscallreturn.hh b/src/arch/mips/syscallreturn.hh new file mode 100644 index 000000000..ef1093caf --- /dev/null +++ b/src/arch/mips/syscallreturn.hh @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Korey Sewell + */ + +#ifndef __ARCH_MIPS_SYSCALLRETURN_HH__ +#define __ARCH_MIPS_SYSCALLRETURN_HH__ + +class SyscallReturn { + public: + template <class T> + SyscallReturn(T v, bool s) + { + retval = (uint32_t)v; + success = s; + } + + template <class T> + SyscallReturn(T v) + { + success = (v >= 0); + retval = (uint32_t)v; + } + + ~SyscallReturn() {} + + SyscallReturn& operator=(const SyscallReturn& s) { + retval = s.retval; + success = s.success; + return *this; + } + + bool successful() { return success; } + uint64_t value() { return retval; } + + + private: + uint64_t retval; + bool success; +}; + +namespace MipsISA +{ + static inline void setSyscallReturn(SyscallReturn return_value, RegFile *regs) + { + if (return_value.successful()) { + // no error + regs->setIntReg(SyscallSuccessReg, 0); + regs->setIntReg(ReturnValueReg1, return_value.value()); + } else { + // got an error, return details + regs->setIntReg(SyscallSuccessReg, (IntReg) -1); + regs->setIntReg(ReturnValueReg1, -return_value.value()); + } + } +} + +#endif diff --git a/src/arch/mips/types.hh b/src/arch/mips/types.hh new file mode 100644 index 000000000..d4fa296fd --- /dev/null +++ b/src/arch/mips/types.hh @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#ifndef __ARCH_MIPS_TYPES_HH__ +#define __ARCH_MIPS_TYPES_HH__ + +#include "sim/host.hh" + +namespace MipsISA +{ + typedef uint32_t MachInst; + typedef uint64_t ExtMachInst; + typedef uint8_t RegIndex; + + typedef uint32_t IntReg; + + // floating point register file entry type + typedef uint32_t FloatReg32; + typedef uint64_t FloatReg64; + typedef uint64_t FloatRegBits; + + typedef double FloatRegVal; + typedef double FloatReg; + + // cop-0/cop-1 system control register + typedef uint64_t MiscReg; + + typedef union { + IntReg intreg; + FloatReg fpreg; + MiscReg ctrlreg; + } AnyReg; + + typedef int RegContextParam; + typedef int RegContextVal; + + //used in FP convert & round function + enum ConvertType{ + SINGLE_TO_DOUBLE, + SINGLE_TO_WORD, + SINGLE_TO_LONG, + + DOUBLE_TO_SINGLE, + DOUBLE_TO_WORD, + DOUBLE_TO_LONG, + + LONG_TO_SINGLE, + LONG_TO_DOUBLE, + LONG_TO_WORD, + LONG_TO_PS, + + WORD_TO_SINGLE, + WORD_TO_DOUBLE, + WORD_TO_LONG, + WORD_TO_PS, + + PL_TO_SINGLE, + PU_TO_SINGLE + }; + + //used in FP convert & round function + enum RoundMode{ + RND_ZERO, + RND_DOWN, + RND_UP, + RND_NEAREST + }; + +} // namespace MipsISA + +#endif diff --git a/src/arch/mips/utility.cc b/src/arch/mips/utility.cc new file mode 100644 index 000000000..9ac4bb6d8 --- /dev/null +++ b/src/arch/mips/utility.cc @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2003-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#include "arch/mips/regfile.hh" +#include "arch/mips/utility.hh" +#include "base/misc.hh" +#include "base/bitfield.hh" + +using namespace MipsISA; + +uint64_t +MipsISA::fpConvert(ConvertType cvt_type, double fp_val) +{ + + switch (cvt_type) + { + case SINGLE_TO_DOUBLE: + { + double sdouble_val = fp_val; + void *sdouble_ptr = &sdouble_val; + uint64_t sdp_bits = *(uint64_t *) sdouble_ptr; + return sdp_bits; + } + + case SINGLE_TO_WORD: + { + int32_t sword_val = (int32_t) fp_val; + void *sword_ptr = &sword_val; + uint64_t sword_bits= *(uint32_t *) sword_ptr; + return sword_bits; + } + + case WORD_TO_SINGLE: + { + float wfloat_val = fp_val; + void *wfloat_ptr = &wfloat_val; + uint64_t wfloat_bits = *(uint32_t *) wfloat_ptr; + return wfloat_bits; + } + + case WORD_TO_DOUBLE: + { + double wdouble_val = fp_val; + void *wdouble_ptr = &wdouble_val; + uint64_t wdp_bits = *(uint64_t *) wdouble_ptr; + return wdp_bits; + } + + default: + panic("Invalid Floating Point Conversion Type (%d). See \"types.hh\" for List of Conversions\n",cvt_type); + return 0; + } +} + +double +MipsISA::roundFP(double val, int digits) +{ + double digit_offset = pow(10.0,digits); + val = val * digit_offset; + val = val + 0.5; + val = floor(val); + val = val / digit_offset; + return val; +} + +double +MipsISA::truncFP(double val) +{ + int trunc_val = (int) val; + return (double) trunc_val; +} + +bool +MipsISA::getCondCode(uint32_t fcsr, int cc_idx) +{ + int shift = (cc_idx == 0) ? 23 : cc_idx + 24; + bool cc_val = (fcsr >> shift) & 0x00000001; + return cc_val; +} + +uint32_t +MipsISA::genCCVector(uint32_t fcsr, int cc_num, uint32_t cc_val) +{ + int cc_idx = (cc_num == 0) ? 23 : cc_num + 24; + + fcsr = bits(fcsr, 31, cc_idx + 1) << cc_idx + 1 | + cc_val << cc_idx | + bits(fcsr, cc_idx - 1, 0); + + return fcsr; +} + +uint32_t +MipsISA::genInvalidVector(uint32_t fcsr_bits) +{ + //Set FCSR invalid in "flag" field + int invalid_offset = Invalid + Flag_Field; + fcsr_bits = fcsr_bits | (1 << invalid_offset); + + //Set FCSR invalid in "cause" flag + int cause_offset = Invalid + Cause_Field; + fcsr_bits = fcsr_bits | (1 << cause_offset); + + return fcsr_bits; +} + +bool +MipsISA::isNan(void *val_ptr, int size) +{ + switch (size) + { + case 32: + { + uint32_t val_bits = *(uint32_t *) val_ptr; + return (bits(val_bits, 30, 23) == 0xFF); + } + + case 64: + { + uint64_t val_bits = *(uint64_t *) val_ptr; + return (bits(val_bits, 62, 52) == 0x7FF); + } + + default: + panic("Type unsupported. Size mismatch\n"); + } +} + + +bool +MipsISA::isQnan(void *val_ptr, int size) +{ + switch (size) + { + case 32: + { + uint32_t val_bits = *(uint32_t *) val_ptr; + return (bits(val_bits, 30, 22) == 0x1FE); + } + + case 64: + { + uint64_t val_bits = *(uint64_t *) val_ptr; + return (bits(val_bits, 62, 51) == 0xFFE); + } + + default: + panic("Type unsupported. Size mismatch\n"); + } +} + +bool +MipsISA::isSnan(void *val_ptr, int size) +{ + switch (size) + { + case 32: + { + uint32_t val_bits = *(uint32_t *) val_ptr; + return (bits(val_bits, 30, 22) == 0x1FF); + } + + case 64: + { + uint64_t val_bits = *(uint64_t *) val_ptr; + return (bits(val_bits, 62, 51) == 0xFFF); + } + + default: + panic("Type unsupported. Size mismatch\n"); + } +} diff --git a/src/arch/mips/utility.hh b/src/arch/mips/utility.hh new file mode 100644 index 000000000..ae2fe1aea --- /dev/null +++ b/src/arch/mips/utility.hh @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __ARCH_MIPS_UTILITY_HH__ +#define __ARCH_MIPS_UTILITY_HH__ + +#include "arch/mips/types.hh" +#include "base/misc.hh" +#include "config/full_system.hh" +//XXX This is needed for size_t. We should use something other than size_t +//#include "kern/linux/linux.hh" +#include "sim/host.hh" + +class ThreadContext; + +namespace MipsISA { + + //Floating Point Utility Functions + uint64_t fpConvert(ConvertType cvt_type, double fp_val); + double roundFP(double val, int digits); + double truncFP(double val); + + bool getCondCode(uint32_t fcsr, int cc); + uint32_t genCCVector(uint32_t fcsr, int num, uint32_t cc_val); + uint32_t genInvalidVector(uint32_t fcsr); + + bool isNan(void *val_ptr, int size); + bool isQnan(void *val_ptr, int size); + bool isSnan(void *val_ptr, int size); + + /** + * Function to insure ISA semantics about 0 registers. + * @param tc The thread context. + */ + template <class TC> + void zeroRegisters(TC *tc); + + void copyRegs(ThreadContext *src, ThreadContext *dest); + + // Instruction address compression hooks + static inline Addr realPCToFetchPC(const Addr &addr) { + return addr; + } + + static inline Addr fetchPCToRealPC(const Addr &addr) { + return addr; + } + + // the size of "fetched" instructions (not necessarily the size + // of real instructions for PISA) + static inline size_t fetchInstSize() { + return sizeof(MachInst); + } + + static inline MachInst makeRegisterCopy(int dest, int src) { + panic("makeRegisterCopy not implemented"); + return 0; + } + + static inline ExtMachInst + makeExtMI(MachInst inst, const uint64_t &pc) { +#if FULL_SYSTEM + ExtMachInst ext_inst = inst; + if (pc && 0x1) + return ext_inst|=(static_cast<ExtMachInst>(pc & 0x1) << 32); + else + return ext_inst; +#else + return ExtMachInst(inst); +#endif + } +}; + + +#endif diff --git a/src/arch/sparc/SConscript b/src/arch/sparc/SConscript new file mode 100644 index 000000000..66f2b57e0 --- /dev/null +++ b/src/arch/sparc/SConscript @@ -0,0 +1,86 @@ +# -*- mode:python -*- + +# Copyright (c) 2004-2005 The Regents of The University of Michigan +# 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. +# +# Authors: Gabe Black +# Steve Reinhardt + +import os +import sys +from os.path import isdir + +# Import build environment variable from SConstruct. +Import('env') + +################################################### +# +# Define needed sources. +# +################################################### + +# Base sources used by all configurations. +base_sources = Split(''' + faults.cc + floatregfile.cc + intregfile.cc + miscregfile.cc + regfile.cc + ''') + +# Full-system sources +full_system_sources = Split(''' + ua2005.cc + vtophys.cc + ''') + +# Syscall emulation (non-full-system) sources +syscall_emulation_sources = Split(''' + linux/linux.cc + linux/process.cc + process.cc + solaris/process.cc + solaris/solaris.cc + ''') + +sources = base_sources + +if env['FULL_SYSTEM']: + sources += full_system_sources +else: + sources += syscall_emulation_sources + +# Convert file names to SCons File objects. This takes care of the +# path relative to the top of the directory tree. +sources = [File(s) for s in sources] + +# Add in files generated by the ISA description. +isa_desc_files = env.ISADesc('isa/main.isa') +# Only non-header files need to be compiled. +isa_desc_sources = [f for f in isa_desc_files if not f.path.endswith('.hh')] +sources += isa_desc_sources + +Return('sources') diff --git a/src/arch/sparc/asi.hh b/src/arch/sparc/asi.hh new file mode 100644 index 000000000..482e077e0 --- /dev/null +++ b/src/arch/sparc/asi.hh @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + */ + +#ifndef __ARCH_SPARC_ASI_HH__ +#define __ARCH_SPARC_ASI_HH__ + +namespace SparcISA +{ + enum ASI { + /* Priveleged ASIs */ + //0x00-0x03 implementation dependent + ASI_NUCLEUS = 0x4, + ASI_N = 0x4, + //0x05-0x0B implementation dependent + ASI_NL = 0xC, + ASI_NUCLEUS_LITTLE = ASI_NL, + //0x0D-0x0F implementation dependent + ASI_AIUP = 0x10, + ASI_AS_IF_USER_PRIMARY = ASI_AIUP, + ASI_AIUS = 0x11, + ASI_AS_IF_USER_SECONDARY = ASI_AIUS, + //0x12-0x13 implementation dependent + ASI_REAL = 0x14, + ASI_REAL_IO = 0x15, + ASI_BLK_AIUP = 0x16, + ASI_BLOCK_AS_IF_USER_PRIMARY = ASI_BLK_AIUP, + ASI_BLK_AIUS = 0x17, + ASI_BLOCK_AS_IF_USER_SECONDARY = ASI_BLK_AIUS, + ASI_AIUPL = 0x18, + ASI_AS_IF_USER_PRIMARY_LITTLE = ASI_AIUPL, + ASI_AIUSL = 0x19, + ASI_AS_IF_USER_SECONDARY_LITTLE = ASI_AIUSL, + //0x1A-0x1B implementation dependent + ASI_REAL_L = 0x1C, + ASI_REAL_LITTLE = ASI_REAL_L, + ASI_REAL_IO_L = 0x1D, + ASI_REAL_IO_LITTLE = ASI_REAL_IO_L, + ASI_BLK_AIUPL = 0x1E, + ASI_BLOCK_AS_IF_USER_PRIMARY_LITTLE = ASI_BLK_AIUPL, + ASI_BLK_AIUSL = 0x1F, + ASI_BLOCK_AS_IF_USER_SECONDARY_LITTLE = ASI_BLK_AIUSL, + ASI_SCRATCHPAD = 0x20, + ASI_MMU_CONTEXTID = 0x21, + ASI_LDTX_AIUP = 0x22, + ASI_LD_TWINX_AS_IF_USER_PRIMARY = ASI_LDTX_AIUP, + ASI_LDTX_AIUS = 0x23, + ASI_LD_TWINX_AS_IF_USER_SECONDARY = ASI_LDTX_AIUS, + //0x24 implementation dependent + ASI_QUEUE = 0x25, + ASI_LDTX_REAL = 0x26, + ASI_LD_TWINX_REAL = ASI_LDTX_REAL, + ASI_LDTX_N = 0x27, + ASI_LD_TWINX_NUCLEUS = ASI_LDTX_N, + //0x28-0x29 implementation dependent + ASI_LDTX_AIUPL = 0x2A, + ASI_LD_TWINX_AS_IF_USER_PRIMARY_LITTLE = ASI_LDTX_AIUPL, + ASI_LDTX_AIUSL = 0x2B, + ASI_LD_TWINX_AS_IF_USER_SECONDARY_LITTLE = ASI_LDTX_AIUSL, + //0x2C-0x2D implementation dependent + ASI_LDTX_REAL_L = 0x2E, + ASI_LD_TWINX_REAL_LITTLE = ASI_LDTX_REAL_L, + ASI_LDTX_NL = 0x2F, + ASI_LD_TWINX_NUCLEUS_LITTLE = ASI_LDTX_NL, + //0x30-0x40 implementation dependent + ASI_CMT_SHARED = 0x41, + //0x42-0x4F implementation dependent + ASI_HYP_SCRATCHPAD = 0x4F, + ASI_IMMU = 0x50, + ASI_MMU_REAL = 0x52, + //0x53 implementation dependent + ASI_MMU = 0x54, + ASI_ITLB_DATA_ACCESS_REG = 0x55, + ASI_ITLB_TAG_READ_REG = 0x56, + ASI_IMMU_DEMAP = 0x57, + ASI_DMMU = 0x58, + ASI_UMMU = 0x58, + //0x59-0x5B reserved + ASI_DTLB_DATA_IN_REG = 0x5C, + ASI_DTLB_DATA_ACCESS_REG = 0x5D, + ASI_DTLB_TAG_READ_REG = 0x5E, + ASI_DMMU_DEMAP = 0x5F, + //0x60-62 implementation dependent + ASI_CMT_PER_STRAND = 0x63, + //0x64-0x67 implementation dependent + //0x68-0x7F reserved + + /* Unpriveleged ASIs */ + ASI_P = 0x80, + ASI_PRIMARY = ASI_P, + ASI_S = 0x81, + ASI_SECONDARY = ASI_S, + ASI_PNF = 0x82, + ASI_PRIMARY_NO_FAULT = ASI_PNF, + ASI_SNF = 0x83, + ASI_SECONDARY_NO_FAULT = ASI_SNF, + //0x84-0x87 reserved + ASI_PL = 0x88, + ASI_PRIMARY_LITTLE = ASI_PL, + ASI_SL = 0x89, + ASI_SECONDARY_LITTLE = ASI_SL, + ASI_PNFL = 0x8A, + ASI_PRIMARY_NO_FAULT_LITTLE = ASI_PNFL, + ASI_SNFL = 0x8B, + ASI_SECONDARY_NO_FAULT_LITTLE = ASI_SNFL, + //0x8C-0xBF reserved + ASI_PST8_P = 0xC0, + ASI_PST8_PRIMARY = ASI_PST8_P, + ASI_PST8_S = 0xC1, + ASI_PST8_SECONDARY = ASI_PST8_S, + ASI_PST16_P = 0xC2, + ASI_PST16_PRIMARY = ASI_PST16_P, + ASI_PST16_S = 0xC3, + ASI_PST16_SECONDARY = ASI_PST16_S, + ASI_PST32_P = 0xC4, + ASI_PST32_PRIMARY = ASI_PST32_P, + ASI_PST32_S = 0xC5, + ASI_PST32_SECONDARY = ASI_PST32_S, + //0xC6-0xC7 implementation dependent + ASI_PST8_PL = 0xC8, + ASI_PST8_PRIMARY_LITTLE = ASI_PST8_PL, + ASI_PST8_SL = 0xC9, + ASI_PST8_SECONDARY_LITTLE = ASI_PST8_SL, + ASI_PST16_PL = 0xCA, + ASI_PST16_PRIMARY_LITTLE = ASI_PST16_PL, + ASI_PST16_SL = 0xCB, + ASI_PST16_SECONDARY_LITTLE = ASI_PST16_SL, + ASI_PST32_PL = 0xCC, + ASI_PST32_PRIMARY_LITTLE = ASI_PST32_PL, + ASI_PST32_SL = 0xCD, + ASI_PST32_SECONDARY_LITTLE = ASI_PST32_SL, + //0xCE-0xCF implementation dependent + ASI_PL8_P = 0xD0, + ASI_PL8_PRIMARY = ASI_PL8_P, + ASI_PL8_S = 0xD1, + ASI_PL8_SECONDARY = ASI_PL8_S, + ASI_PL16_P = 0xD2, + ASI_PL16_PRIMARY = ASI_PL16_P, + ASI_PL16_S = 0xD3, + ASI_PL16_SECONDARY = ASI_PL16_S, + //0xD4-0xD7 implementation dependent + ASI_PL8_PL = 0xD8, + ASI_PL8_PRIMARY_LITTLE = ASI_PL8_PL, + ASI_PL8_SL = 0xD9, + ASI_PL8_SECONDARY_LITTLE = ASI_PL8_SL, + ASI_PL16_PL = 0xDA, + ASI_PL16_PRIMARY_LITTLE = ASI_PL16_PL, + ASI_PL16_SL = 0xDB, + ASI_PL16_SECONDARY_LITTLE = ASI_PL16_SL, + //0xDC-0xDF implementation dependent + //0xE0-0xE1 reserved + ASI_LDTX_P = 0xE2, + ASI_LD_TWINX_PRIMARY = ASI_LDTX_P, + ASI_LDTX_S = 0xE3, + ASI_LD_TWINX_SECONDARY = ASI_LDTX_S, + //0xE4-0xE9 implementation dependent + ASI_LDTX_PL = 0xEA, + ASI_LD_TWINX_PRIMARY_LITTLE = ASI_LDTX_PL, + ASI_LDTX_SL = 0xEB, + ASI_LD_TWINX_SECONDARY_LITTLE = ASI_LDTX_SL, + //0xEC-0xEF implementation dependent + ASI_BLK_P = 0xF0, + ASI_BLOCK_PRIMARY = ASI_BLK_P, + ASI_BLK_S = 0xF1, + ASI_BLOCK_SECONDARY = ASI_BLK_S, + //0xF2-0xF7 implementation dependent + ASI_BLK_PL = 0xF8, + ASI_BLOCK_PRIMARY_LITTLE = ASI_BLK_PL, + ASI_BLK_SL = 0xF9, + ASI_BLOCK_SECONDARY_LITTLE = ASI_BLK_SL + //0xFA-0xFF implementation dependent + }; +}; + +#endif // __ARCH_SPARC_TLB_HH__ diff --git a/src/arch/sparc/faults.cc b/src/arch/sparc/faults.cc new file mode 100644 index 000000000..7b7765935 --- /dev/null +++ b/src/arch/sparc/faults.cc @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Kevin Lim + */ + +#include "arch/sparc/faults.hh" +#include "cpu/thread_context.hh" +#include "cpu/base.hh" +#include "base/trace.hh" +#if !FULL_SYSTEM +#include "sim/process.hh" +#include "mem/page_table.hh" +#endif + +namespace SparcISA +{ + +FaultName InternalProcessorError::_name = "intprocerr"; +TrapType InternalProcessorError::_trapType = 0x029; +FaultPriority InternalProcessorError::_priority = 4; +FaultStat InternalProcessorError::_count; + +FaultName MemAddressNotAligned::_name = "unalign"; +TrapType MemAddressNotAligned::_trapType = 0x034; +FaultPriority MemAddressNotAligned::_priority = 10; +FaultStat MemAddressNotAligned::_count; + +FaultName PowerOnReset::_name = "pow_reset"; +TrapType PowerOnReset::_trapType = 0x001; +FaultPriority PowerOnReset::_priority = 0; +FaultStat PowerOnReset::_count; + +FaultName WatchDogReset::_name = "watch_dog_reset"; +TrapType WatchDogReset::_trapType = 0x002; +FaultPriority WatchDogReset::_priority = 1; +FaultStat WatchDogReset::_count; + +FaultName ExternallyInitiatedReset::_name = "extern_reset"; +TrapType ExternallyInitiatedReset::_trapType = 0x003; +FaultPriority ExternallyInitiatedReset::_priority = 1; +FaultStat ExternallyInitiatedReset::_count; + +FaultName SoftwareInitiatedReset::_name = "software_reset"; +TrapType SoftwareInitiatedReset::_trapType = 0x004; +FaultPriority SoftwareInitiatedReset::_priority = 1; +FaultStat SoftwareInitiatedReset::_count; + +FaultName REDStateException::_name = "red_counte"; +TrapType REDStateException::_trapType = 0x005; +FaultPriority REDStateException::_priority = 1; +FaultStat REDStateException::_count; + +FaultName InstructionAccessException::_name = "inst_access"; +TrapType InstructionAccessException::_trapType = 0x008; +FaultPriority InstructionAccessException::_priority = 5; +FaultStat InstructionAccessException::_count; + +FaultName InstructionAccessMMUMiss::_name = "inst_mmu"; +TrapType InstructionAccessMMUMiss::_trapType = 0x009; +FaultPriority InstructionAccessMMUMiss::_priority = 2; +FaultStat InstructionAccessMMUMiss::_count; + +FaultName InstructionAccessError::_name = "inst_error"; +TrapType InstructionAccessError::_trapType = 0x00A; +FaultPriority InstructionAccessError::_priority = 3; +FaultStat InstructionAccessError::_count; + +FaultName IllegalInstruction::_name = "illegal_inst"; +TrapType IllegalInstruction::_trapType = 0x010; +FaultPriority IllegalInstruction::_priority = 7; +FaultStat IllegalInstruction::_count; + +FaultName PrivilegedOpcode::_name = "priv_opcode"; +TrapType PrivilegedOpcode::_trapType = 0x011; +FaultPriority PrivilegedOpcode::_priority = 6; +FaultStat PrivilegedOpcode::_count; + +FaultName UnimplementedLDD::_name = "unimp_ldd"; +TrapType UnimplementedLDD::_trapType = 0x012; +FaultPriority UnimplementedLDD::_priority = 6; +FaultStat UnimplementedLDD::_count; + +FaultName UnimplementedSTD::_name = "unimp_std"; +TrapType UnimplementedSTD::_trapType = 0x013; +FaultPriority UnimplementedSTD::_priority = 6; +FaultStat UnimplementedSTD::_count; + +FaultName FpDisabled::_name = "fp_disabled"; +TrapType FpDisabled::_trapType = 0x020; +FaultPriority FpDisabled::_priority = 8; +FaultStat FpDisabled::_count; + +FaultName FpExceptionIEEE754::_name = "fp_754"; +TrapType FpExceptionIEEE754::_trapType = 0x021; +FaultPriority FpExceptionIEEE754::_priority = 11; +FaultStat FpExceptionIEEE754::_count; + +FaultName FpExceptionOther::_name = "fp_other"; +TrapType FpExceptionOther::_trapType = 0x022; +FaultPriority FpExceptionOther::_priority = 11; +FaultStat FpExceptionOther::_count; + +FaultName TagOverflow::_name = "tag_overflow"; +TrapType TagOverflow::_trapType = 0x023; +FaultPriority TagOverflow::_priority = 14; +FaultStat TagOverflow::_count; + +FaultName DivisionByZero::_name = "div_by_zero"; +TrapType DivisionByZero::_trapType = 0x028; +FaultPriority DivisionByZero::_priority = 15; +FaultStat DivisionByZero::_count; + +FaultName DataAccessException::_name = "data_access"; +TrapType DataAccessException::_trapType = 0x030; +FaultPriority DataAccessException::_priority = 12; +FaultStat DataAccessException::_count; + +FaultName DataAccessMMUMiss::_name = "data_mmu"; +TrapType DataAccessMMUMiss::_trapType = 0x031; +FaultPriority DataAccessMMUMiss::_priority = 12; +FaultStat DataAccessMMUMiss::_count; + +FaultName DataAccessError::_name = "data_error"; +TrapType DataAccessError::_trapType = 0x032; +FaultPriority DataAccessError::_priority = 12; +FaultStat DataAccessError::_count; + +FaultName DataAccessProtection::_name = "data_protection"; +TrapType DataAccessProtection::_trapType = 0x033; +FaultPriority DataAccessProtection::_priority = 12; +FaultStat DataAccessProtection::_count; + +FaultName LDDFMemAddressNotAligned::_name = "unalign_lddf"; +TrapType LDDFMemAddressNotAligned::_trapType = 0x035; +FaultPriority LDDFMemAddressNotAligned::_priority = 10; +FaultStat LDDFMemAddressNotAligned::_count; + +FaultName STDFMemAddressNotAligned::_name = "unalign_stdf"; +TrapType STDFMemAddressNotAligned::_trapType = 0x036; +FaultPriority STDFMemAddressNotAligned::_priority = 10; +FaultStat STDFMemAddressNotAligned::_count; + +FaultName PrivilegedAction::_name = "priv_action"; +TrapType PrivilegedAction::_trapType = 0x037; +FaultPriority PrivilegedAction::_priority = 11; +FaultStat PrivilegedAction::_count; + +FaultName LDQFMemAddressNotAligned::_name = "unalign_ldqf"; +TrapType LDQFMemAddressNotAligned::_trapType = 0x038; +FaultPriority LDQFMemAddressNotAligned::_priority = 10; +FaultStat LDQFMemAddressNotAligned::_count; + +FaultName STQFMemAddressNotAligned::_name = "unalign_stqf"; +TrapType STQFMemAddressNotAligned::_trapType = 0x039; +FaultPriority STQFMemAddressNotAligned::_priority = 10; +FaultStat STQFMemAddressNotAligned::_count; + +FaultName AsyncDataError::_name = "async_data"; +TrapType AsyncDataError::_trapType = 0x040; +FaultPriority AsyncDataError::_priority = 2; +FaultStat AsyncDataError::_count; + +FaultName CleanWindow::_name = "clean_win"; +TrapType CleanWindow::_trapType = 0x024; +FaultPriority CleanWindow::_priority = 10; +FaultStat CleanWindow::_count; + +//The enumerated faults + +FaultName InterruptLevelN::_name = "interrupt_n"; +TrapType InterruptLevelN::_baseTrapType = 0x041; +FaultStat InterruptLevelN::_count; + +FaultName SpillNNormal::_name = "spill_n_normal"; +TrapType SpillNNormal::_baseTrapType = 0x080; +FaultPriority SpillNNormal::_priority = 9; +FaultStat SpillNNormal::_count; + +FaultName SpillNOther::_name = "spill_n_other"; +TrapType SpillNOther::_baseTrapType = 0x0A0; +FaultPriority SpillNOther::_priority = 9; +FaultStat SpillNOther::_count; + +FaultName FillNNormal::_name = "fill_n_normal"; +TrapType FillNNormal::_baseTrapType = 0x0C0; +FaultPriority FillNNormal::_priority = 9; +FaultStat FillNNormal::_count; + +FaultName FillNOther::_name = "fill_n_other"; +TrapType FillNOther::_baseTrapType = 0x0E0; +FaultPriority FillNOther::_priority = 9; +FaultStat FillNOther::_count; + +FaultName TrapInstruction::_name = "trap_inst_n"; +TrapType TrapInstruction::_baseTrapType = 0x100; +FaultPriority TrapInstruction::_priority = 16; +FaultStat TrapInstruction::_count; + +#if !FULL_SYSTEM +FaultName PageTableFault::_name = "page_table_fault"; +TrapType PageTableFault::_trapType = 0x0000; +FaultPriority PageTableFault::_priority = 0; +FaultStat PageTableFault::_count; +#endif + +#if FULL_SYSTEM + +void SparcFault::invoke(ThreadContext * tc) +{ + FaultBase::invoke(tc); + countStat()++; + + //Use the SPARC trap state machine + /*// exception restart address + if (setRestartAddress() || !tc->inPalMode()) + tc->setMiscReg(AlphaISA::IPR_EXC_ADDR, tc->regs.pc); + + if (skipFaultingInstruction()) { + // traps... skip faulting instruction. + tc->setMiscReg(AlphaISA::IPR_EXC_ADDR, + tc->readMiscReg(AlphaISA::IPR_EXC_ADDR) + 4); + } + + if (!tc->inPalMode()) + AlphaISA::swap_palshadow(&(tc->regs), true); + + tc->regs.pc = tc->readMiscReg(AlphaISA::IPR_PAL_BASE) + vect(); + tc->regs.npc = tc->regs.pc + sizeof(MachInst);*/ +} + +#endif + +#if !FULL_SYSTEM + +void TrapInstruction::invoke(ThreadContext * tc) +{ + // Should be handled in ISA. +} + +void PageTableFault::invoke(ThreadContext *tc) +{ + Process *p = tc->getProcessPtr(); + + // address is higher than the stack region or in the current stack region + if (vaddr > p->stack_base || vaddr > p->stack_min) + FaultBase::invoke(tc); + + // We've accessed the next page + if (vaddr > p->stack_min - PageBytes) { + p->stack_min -= PageBytes; + if (p->stack_base - p->stack_min > 8*1024*1024) + fatal("Over max stack size for one thread\n"); + p->pTable->allocate(p->stack_min, PageBytes); + warn("Increasing stack size by one page."); + } else { + FaultBase::invoke(tc); + } +} +#endif + +} // namespace SparcISA + diff --git a/src/arch/sparc/faults.hh b/src/arch/sparc/faults.hh new file mode 100644 index 000000000..b279f4911 --- /dev/null +++ b/src/arch/sparc/faults.hh @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Kevin Lim + */ + +#ifndef __ALPHA_FAULTS_HH__ +#define __ALPHA_FAULTS_HH__ + +#include "sim/faults.hh" + +// The design of the "name" and "vect" functions is in sim/faults.hh + +namespace SparcISA +{ + +typedef const uint32_t TrapType; +typedef const uint32_t FaultPriority; + +class SparcFault : public FaultBase +{ + public: +#if FULL_SYSTEM + void invoke(ThreadContext * tc); +#endif + virtual TrapType trapType() = 0; + virtual FaultPriority priority() = 0; + virtual FaultStat & countStat() = 0; +}; + +class InternalProcessorError : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} + bool isMachineCheckFault() {return true;} +}; + +class MemAddressNotAligned : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} + bool isAlignmentFault() {return true;} +}; + +#if !FULL_SYSTEM +class PageTableFault : public SparcFault +{ + private: + Addr vaddr; + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + PageTableFault(Addr va) + : vaddr(va) {} + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} + void invoke(ThreadContext * tc); +}; + +static inline Fault genPageTableFault(Addr va) +{ + return new PageTableFault(va); +} +#endif + +static inline Fault genMachineCheckFault() +{ + return new InternalProcessorError; +} + +static inline Fault genAlignmentFault() +{ + return new MemAddressNotAligned; +} + +class PowerOnReset : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class WatchDogReset : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class ExternallyInitiatedReset : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class SoftwareInitiatedReset : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class REDStateException : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class InstructionAccessException : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class InstructionAccessMMUMiss : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class InstructionAccessError : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class IllegalInstruction : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class PrivilegedOpcode : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class UnimplementedLDD : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class UnimplementedSTD : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class FpDisabled : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class FpExceptionIEEE754 : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class FpExceptionOther : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class TagOverflow : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class DivisionByZero : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class DataAccessException : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class DataAccessMMUMiss : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class DataAccessError : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class DataAccessProtection : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class LDDFMemAddressNotAligned : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class STDFMemAddressNotAligned : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class PrivilegedAction : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class LDQFMemAddressNotAligned : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class STQFMemAddressNotAligned : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class AsyncDataError : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class CleanWindow : public SparcFault +{ + private: + static FaultName _name; + static TrapType _trapType; + static FaultPriority _priority; + static FaultStat _count; + public: + FaultName name() {return _name;} + TrapType trapType() {return _trapType;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class EnumeratedFault : public SparcFault +{ + protected: + uint32_t _n; + virtual TrapType baseTrapType() = 0; + public: + EnumeratedFault(uint32_t n) : SparcFault() {_n = n;} + TrapType trapType() {return baseTrapType() + _n;} +}; + +class InterruptLevelN : public EnumeratedFault +{ + private: + static FaultName _name; + static TrapType _baseTrapType; + static FaultStat _count; + TrapType baseTrapType() {return _baseTrapType;} + public: + InterruptLevelN(uint32_t n) : EnumeratedFault(n) {;} + FaultName name() {return _name;} + FaultPriority priority() {return 32 - _n;} + FaultStat & countStat() {return _count;} +}; + +class SpillNNormal : public EnumeratedFault +{ + private: + static FaultName _name; + static TrapType _baseTrapType; + static FaultPriority _priority; + static FaultStat _count; + TrapType baseTrapType() {return _baseTrapType;} + public: + SpillNNormal(uint32_t n) : EnumeratedFault(n) {;} + FaultName name() {return _name;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class SpillNOther : public EnumeratedFault +{ + private: + static FaultName _name; + static TrapType _baseTrapType; + static FaultPriority _priority; + static FaultStat _count; + TrapType baseTrapType() {return _baseTrapType;} + public: + SpillNOther(uint32_t n) : EnumeratedFault(n) {;} + FaultName name() {return _name;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class FillNNormal : public EnumeratedFault +{ + private: + static FaultName _name; + static TrapType _baseTrapType; + static FaultPriority _priority; + static FaultStat _count; + TrapType baseTrapType() {return _baseTrapType;} + public: + FillNNormal(uint32_t n) : EnumeratedFault(n) {;} + FaultName name() {return _name;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class FillNOther : public EnumeratedFault +{ + private: + static FaultName _name; + static TrapType _baseTrapType; + static FaultPriority _priority; + static FaultStat _count; + TrapType baseTrapType() {return _baseTrapType;} + public: + FillNOther(uint32_t n) : EnumeratedFault(n) {;} + FaultName name() {return _name;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +}; + +class TrapInstruction : public EnumeratedFault +{ + private: + static FaultName _name; + static TrapType _baseTrapType; + static FaultPriority _priority; + static FaultStat _count; + uint64_t syscall_num; + TrapType baseTrapType() {return _baseTrapType;} + public: + TrapInstruction(uint32_t n, uint64_t syscall) : + EnumeratedFault(n), syscall_num(syscall) {;} + FaultName name() {return _name;} + FaultPriority priority() {return _priority;} + FaultStat & countStat() {return _count;} +#if !FULL_SYSTEM + void invoke(ThreadContext * tc); +#endif +}; + + +} // SparcISA namespace + +#endif // __FAULTS_HH__ diff --git a/src/arch/sparc/floatregfile.cc b/src/arch/sparc/floatregfile.cc new file mode 100644 index 000000000..3afe6ef54 --- /dev/null +++ b/src/arch/sparc/floatregfile.cc @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + */ + +#include "arch/sparc/floatregfile.hh" +#include "base/trace.hh" +#include "sim/byteswap.hh" +#include "sim/serialize.hh" + +using namespace SparcISA; +using namespace std; + +class Checkpoint; + +string SparcISA::getFloatRegName(RegIndex index) +{ + static std::string floatRegName[NumFloatRegs] = + {"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", + "f32", "f33", "f34", "f35", "f36", "f37", "f38", "f39", + "f40", "f41", "f42", "f43", "f44", "f45", "f46", "f47", + "f48", "f49", "f50", "f51", "f52", "f53", "f54", "f55", + "f56", "f57", "f58", "f59", "f60", "f61", "f62", "f63"}; + return floatRegName[index]; +} + +void FloatRegFile::clear() +{ + bzero(regSpace, sizeof(regSpace)); +} + +FloatReg FloatRegFile::readReg(int floatReg, int width) +{ + //In each of these cases, we have to copy the value into a temporary + //variable. This is because we may otherwise try to access an + //unaligned portion of memory. + FloatReg result; + switch(width) + { + case SingleWidth: + float32_t result32; + memcpy(&result32, regSpace + 4 * floatReg, sizeof(result32)); + result = htog(result32); + break; + case DoubleWidth: + float64_t result64; + memcpy(&result64, regSpace + 4 * floatReg, sizeof(result64)); + result = htog(result64); + break; + case QuadWidth: + float128_t result128; + memcpy(&result128, regSpace + 4 * floatReg, sizeof(result128)); + result = htog(result128); + break; + default: + panic("Attempted to read a %d bit floating point register!", width); + } + return result; +} + +FloatRegBits FloatRegFile::readRegBits(int floatReg, int width) +{ + //In each of these cases, we have to copy the value into a temporary + //variable. This is because we may otherwise try to access an + //unaligned portion of memory. + FloatRegBits result; + switch(width) + { + case SingleWidth: + uint32_t result32; + memcpy(&result32, regSpace + 4 * floatReg, sizeof(result32)); + result = htog(result32); + break; + case DoubleWidth: + uint64_t result64; + memcpy(&result64, regSpace + 4 * floatReg, sizeof(result64)); + result = htog(result64); + break; + case QuadWidth: + uint64_t result128; + memcpy(&result128, regSpace + 4 * floatReg, sizeof(result128)); + result = htog(result128); + break; + default: + panic("Attempted to read a %d bit floating point register!", width); + } + return result; +} + +Fault FloatRegFile::setReg(int floatReg, const FloatReg &val, int width) +{ + //In each of these cases, we have to copy the value into a temporary + //variable. This is because we may otherwise try to access an + //unaligned portion of memory. + + uint32_t result32; + uint64_t result64; + switch(width) + { + case SingleWidth: + result32 = gtoh((uint32_t)val); + memcpy(regSpace + 4 * floatReg, &result32, sizeof(result32)); + break; + case DoubleWidth: + result64 = gtoh((uint64_t)val); + memcpy(regSpace + 4 * floatReg, &result64, sizeof(result64)); + break; + case QuadWidth: + panic("Quad width FP not implemented."); + break; + default: + panic("Attempted to read a %d bit floating point register!", width); + } + return NoFault; +} + +Fault FloatRegFile::setRegBits(int floatReg, const FloatRegBits &val, int width) +{ + //In each of these cases, we have to copy the value into a temporary + //variable. This is because we may otherwise try to access an + //unaligned portion of memory. + uint32_t result32; + uint64_t result64; + switch(width) + { + case SingleWidth: + result32 = gtoh((uint32_t)val); + memcpy(regSpace + 4 * floatReg, &result32, sizeof(result32)); + break; + case DoubleWidth: + result64 = gtoh((uint64_t)val); + memcpy(regSpace + 4 * floatReg, &result64, sizeof(result64)); + break; + case QuadWidth: + panic("Quad width FP not implemented."); + break; + default: + panic("Attempted to read a %d bit floating point register!", width); + } + return NoFault; +} + +void FloatRegFile::serialize(std::ostream &os) +{ + SERIALIZE_ARRAY((unsigned char *)regSpace, + SingleWidth / 8 * NumFloatRegs); +} + +void FloatRegFile::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_ARRAY((unsigned char *)regSpace, + SingleWidth / 8 * NumFloatRegs); +} + diff --git a/src/arch/sparc/floatregfile.hh b/src/arch/sparc/floatregfile.hh new file mode 100644 index 000000000..9d760c9ff --- /dev/null +++ b/src/arch/sparc/floatregfile.hh @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + */ + +#ifndef __ARCH_SPARC_FLOATREGFILE_HH__ +#define __ARCH_SPARC_FLOATREGFILE_HH__ + +#include "arch/sparc/faults.hh" +#include "arch/sparc/isa_traits.hh" +#include "arch/sparc/types.hh" + +#include <string> + +namespace SparcISA +{ + std::string getFloatRegName(RegIndex); + + typedef float float32_t; + typedef double float64_t; + //FIXME long double refers to a 10 byte float, rather than a + //16 byte float as required. This data type may have to be emulated. + typedef double float128_t; + + class FloatRegFile + { + public: + static const int SingleWidth = 32; + static const int DoubleWidth = 64; + static const int QuadWidth = 128; + + protected: + + //Since the floating point registers overlap each other, + //A generic storage space is used. The float to be returned is + //pulled from the appropriate section of this region. + char regSpace[(SingleWidth / 8) * NumFloatRegs]; + + public: + + void clear(); + + FloatReg readReg(int floatReg, int width); + + FloatRegBits readRegBits(int floatReg, int width); + + Fault setReg(int floatReg, const FloatReg &val, int width); + + Fault setRegBits(int floatReg, const FloatRegBits &val, int width); + + void serialize(std::ostream &os); + + void unserialize(Checkpoint *cp, const std::string §ion); + }; +} + +#endif diff --git a/src/arch/sparc/intregfile.cc b/src/arch/sparc/intregfile.cc new file mode 100644 index 000000000..bef62f6ae --- /dev/null +++ b/src/arch/sparc/intregfile.cc @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + */ + +#include "arch/sparc/intregfile.hh" +#include "base/trace.hh" +#include "sim/serialize.hh" + +using namespace SparcISA; +using namespace std; + +class Checkpoint; + +string SparcISA::getIntRegName(RegIndex index) +{ + static std::string intRegName[NumIntRegs] = + {"g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", + "o0", "o1", "o2", "o3", "o4", "o5", "o6", "o7", + "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", + "i0", "i1", "i2", "i3", "i4", "i5", "i6", "i7"}; + return intRegName[index]; +} + +int IntRegFile::flattenIndex(int reg) +{ + int flatIndex = offset[reg >> FrameOffsetBits] + | (reg & FrameOffsetMask); + DPRINTF(Sparc, "Flattened index %d into %d.\n", reg, flatIndex); + return flatIndex; +} + +void IntRegFile::clear() +{ + int x; + for (x = 0; x < MaxGL; x++) + memset(regGlobals[x], 0, sizeof(IntReg) * RegsPerFrame); + for(int x = 0; x < 2 * NWindows; x++) + bzero(regSegments[x], sizeof(IntReg) * RegsPerFrame); +} + +IntRegFile::IntRegFile() +{ + offset[Globals] = 0; + regView[Globals] = regGlobals[0]; + setCWP(0); + clear(); +} + +IntReg IntRegFile::readReg(int intReg) +{ + IntReg val = + regView[intReg >> FrameOffsetBits][intReg & FrameOffsetMask]; + DPRINTF(Sparc, "Read register %d = 0x%x\n", intReg, val); + return val; +} + +Fault IntRegFile::setReg(int intReg, const IntReg &val) +{ + if(intReg) + { + DPRINTF(Sparc, "Wrote register %d = 0x%x\n", intReg, val); + regView[intReg >> FrameOffsetBits][intReg & FrameOffsetMask] = val; + } + return NoFault; +} + +//This doesn't effect the actual CWP register. +//It's purpose is to adjust the view of the register file +//to what it would be if CWP = cwp. +void IntRegFile::setCWP(int cwp) +{ + int index = ((NWindows - cwp) % NWindows) * 2; + offset[Outputs] = FrameOffset + (index * RegsPerFrame); + offset[Locals] = FrameOffset + ((index+1) * RegsPerFrame); + offset[Inputs] = FrameOffset + + (((index+2) % (NWindows * 2)) * RegsPerFrame); + regView[Outputs] = regSegments[index]; + regView[Locals] = regSegments[index+1]; + regView[Inputs] = regSegments[(index+2) % (NWindows * 2)]; + + DPRINTF(Sparc, "Changed the CWP value to %d\n", cwp); +} + +void IntRegFile::setGlobals(int gl) +{ + DPRINTF(Sparc, "Now using %d globals", gl); + + regView[Globals] = regGlobals[gl]; + offset[Globals] = RegGlobalOffset + gl * RegsPerFrame; +} + +void IntRegFile::serialize(std::ostream &os) +{ + unsigned int x; + for(x = 0; x < MaxGL; x++) + SERIALIZE_ARRAY(regGlobals[x], RegsPerFrame); + for(x = 0; x < 2 * NWindows; x++) + SERIALIZE_ARRAY(regSegments[x], RegsPerFrame); +} + +void IntRegFile::unserialize(Checkpoint *cp, const std::string §ion) +{ + unsigned int x; + for(x = 0; x < MaxGL; x++) + UNSERIALIZE_ARRAY(regGlobals[x], RegsPerFrame); + for(unsigned int x = 0; x < 2 * NWindows; x++) + UNSERIALIZE_ARRAY(regSegments[x], RegsPerFrame); +} diff --git a/src/arch/sparc/intregfile.hh b/src/arch/sparc/intregfile.hh new file mode 100644 index 000000000..d305c753b --- /dev/null +++ b/src/arch/sparc/intregfile.hh @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + */ + +#ifndef __ARCH_SPARC_INTREGFILE_HH__ +#define __ARCH_SPARC_INTREGFILE_HH__ + +#include "arch/sparc/faults.hh" +#include "arch/sparc/isa_traits.hh" +#include "arch/sparc/types.hh" + +#include <string> + +namespace SparcISA +{ + class RegFile; + + //This function translates integer register file indices into names + std::string getIntRegName(RegIndex); + + class IntRegFile + { + private: + friend class RegFile; + protected: + static const int FrameOffsetBits = 3; + static const int FrameNumBits = 2; + + static const int RegsPerFrame = 1 << FrameOffsetBits; + static const int FrameNumMask = + (FrameNumBits == sizeof(int)) ? + (unsigned int)(-1) : + (1 << FrameNumBits) - 1; + static const int FrameOffsetMask = + (FrameOffsetBits == sizeof(int)) ? + (unsigned int)(-1) : + (1 << FrameOffsetBits) - 1; + + IntReg regGlobals[MaxGL][RegsPerFrame]; + IntReg regSegments[2 * NWindows][RegsPerFrame]; + + enum regFrame {Globals, Outputs, Locals, Inputs, NumFrames}; + + IntReg * regView[NumFrames]; + + static const int RegGlobalOffset = 0; + static const int FrameOffset = MaxGL * RegsPerFrame; + int offset[NumFrames]; + + public: + + int flattenIndex(int reg); + + void clear(); + + IntRegFile(); + + IntReg readReg(int intReg); + + Fault setReg(int intReg, const IntReg &val); + + void serialize(std::ostream &os); + + void unserialize(Checkpoint *cp, const std::string §ion); + + protected: + //This doesn't effect the actual CWP register. + //It's purpose is to adjust the view of the register file + //to what it would be if CWP = cwp. + void setCWP(int cwp); + + void setGlobals(int gl); + }; +} + +#endif diff --git a/src/arch/sparc/isa/base.isa b/src/arch/sparc/isa/base.isa new file mode 100644 index 000000000..b518265aa --- /dev/null +++ b/src/arch/sparc/isa/base.isa @@ -0,0 +1,284 @@ +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Ali Saidi +// Gabe Black +// Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Base class for sparc instructions, and some support functions +// + +output header {{ + + union CondCodes + { + struct + { + uint8_t c:1; + uint8_t v:1; + uint8_t z:1; + uint8_t n:1; + }; + uint32_t bits; + }; + + enum CondTest + { + Always=0x8, + Never=0x0, + NotEqual=0x9, + Equal=0x1, + Greater=0xA, + LessOrEqual=0x2, + GreaterOrEqual=0xB, + Less=0x3, + GreaterUnsigned=0xC, + LessOrEqualUnsigned=0x4, + CarryClear=0xD, + CarrySet=0x5, + Positive=0xE, + Negative=0x6, + OverflowClear=0xF, + OverflowSet=0x7 + }; + + extern char * CondTestAbbrev[]; + + /** + * Base class for all SPARC static instructions. + */ + class SparcStaticInst : public StaticInst + { + protected: + // Constructor. + SparcStaticInst(const char *mnem, + MachInst _machInst, OpClass __opClass) + : StaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + + void printReg(std::ostream &os, int reg) const; + void printSrcReg(std::ostream &os, int reg) const; + void printDestReg(std::ostream &os, int reg) const; + + void printRegArray(std::ostream &os, + const RegIndex indexArray[], int num) const; + }; + + bool passesCondition(uint32_t codes, uint32_t condition); + + inline int64_t sign_ext(uint64_t data, int origWidth) + { + int shiftAmount = 64 - origWidth; + return (((int64_t)data) << shiftAmount) >> shiftAmount; + } +}}; + +output decoder {{ + + char * CondTestAbbrev[] = + { + "nev", //Never + "e", //Equal + "le", //Less or Equal + "l", //Less + "leu", //Less or Equal Unsigned + "c", //Carry set + "n", //Negative + "o", //Overflow set + "a", //Always + "ne", //Not Equal + "g", //Greater + "ge", //Greater or Equal + "gu", //Greater Unsigned + "cc", //Carry clear + "p", //Positive + "oc" //Overflow Clear + }; +}}; + +def template ROrImmDecode {{ + { + return (I ? (SparcStaticInst *)(new %(class_name)sImm(machInst)) + : (SparcStaticInst *)(new %(class_name)s(machInst))); + } +}}; + +let {{ + def splitOutImm(code): + matcher = re.compile(r'Rs(?P<rNum>\d)_or_imm(?P<iNum>\d+)(?P<typeQual>\.\w+)?') + rOrImmMatch = matcher.search(code) + if (rOrImmMatch == None): + return (False, code, '', '', '') + rString = rOrImmMatch.group("rNum") + if (rOrImmMatch.group("typeQual") != None): + rString += rOrImmMatch.group("typeQual") + iString = rOrImmMatch.group("iNum") + orig_code = code + code = matcher.sub('Rs' + rString, orig_code) + imm_code = matcher.sub('imm', orig_code) + return (True, code, imm_code, rString, iString) +}}; + +output decoder {{ + + inline void printMnemonic(std::ostream &os, const char * mnemonic) + { + ccprintf(os, "\t%s ", mnemonic); + } + + void SparcStaticInst::printRegArray(std::ostream &os, + const RegIndex indexArray[], int num) const + { + if(num <= 0) + return; + printReg(os, indexArray[0]); + for(int x = 1; x < num; x++) + { + os << ", "; + printReg(os, indexArray[x]); + } + } + + void + SparcStaticInst::printSrcReg(std::ostream &os, int reg) const + { + if(_numSrcRegs > reg) + printReg(os, _srcRegIdx[reg]); + } + + void + SparcStaticInst::printDestReg(std::ostream &os, int reg) const + { + if(_numDestRegs > reg) + printReg(os, _destRegIdx[reg]); + } + + void + SparcStaticInst::printReg(std::ostream &os, int reg) const + { + const int MaxGlobal = 8; + const int MaxOutput = 16; + const int MaxLocal = 24; + const int MaxInput = 32; + if (reg == FramePointerReg) + ccprintf(os, "%%fp"); + else if (reg == StackPointerReg) + ccprintf(os, "%%sp"); + else if(reg < MaxGlobal) + ccprintf(os, "%%g%d", reg); + else if(reg < MaxOutput) + ccprintf(os, "%%o%d", reg - MaxGlobal); + else if(reg < MaxLocal) + ccprintf(os, "%%l%d", reg - MaxOutput); + else if(reg < MaxInput) + ccprintf(os, "%%i%d", reg - MaxLocal); + else { + ccprintf(os, "%%f%d", reg - FP_Base_DepTag); + } + } + + std::string SparcStaticInst::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream ss; + + printMnemonic(ss, mnemonic); + + // just print the first two source regs... if there's + // a third one, it's a read-modify-write dest (Rc), + // e.g. for CMOVxx + if(_numSrcRegs > 0) + { + printReg(ss, _srcRegIdx[0]); + } + if(_numSrcRegs > 1) + { + ss << ","; + printReg(ss, _srcRegIdx[1]); + } + + // just print the first dest... if there's a second one, + // it's generally implicit + if(_numDestRegs > 0) + { + if(_numSrcRegs > 0) + ss << ","; + printReg(ss, _destRegIdx[0]); + } + + return ss.str(); + } + + bool passesCondition(uint32_t codes, uint32_t condition) + { + CondCodes condCodes; + condCodes.bits = codes; + switch(condition) + { + case Always: + return true; + case Never: + return false; + case NotEqual: + return !condCodes.z; + case Equal: + return condCodes.z; + case Greater: + return !(condCodes.z | (condCodes.n ^ condCodes.v)); + case LessOrEqual: + return condCodes.z | (condCodes.n ^ condCodes.v); + case GreaterOrEqual: + return !(condCodes.n ^ condCodes.v); + case Less: + return (condCodes.n ^ condCodes.v); + case GreaterUnsigned: + return !(condCodes.c | condCodes.z); + case LessOrEqualUnsigned: + return (condCodes.c | condCodes.z); + case CarryClear: + return !condCodes.c; + case CarrySet: + return condCodes.c; + case Positive: + return !condCodes.n; + case Negative: + return condCodes.n; + case OverflowClear: + return !condCodes.v; + case OverflowSet: + return condCodes.v; + } + panic("Tried testing condition nonexistant " + "condition code %d", condition); + } +}}; + diff --git a/src/arch/sparc/isa/bitfields.isa b/src/arch/sparc/isa/bitfields.isa new file mode 100644 index 000000000..27f52fa29 --- /dev/null +++ b/src/arch/sparc/isa/bitfields.isa @@ -0,0 +1,78 @@ +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Ali Saidi +// Gabe Black +// Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Bitfield definitions. +// + +// Bitfields are shared liberally between instruction formats, so they are +// simply defined alphabetically + +def bitfield A <29>; +def bitfield BPCC <21:20>; // for BPcc & FBPcc +def bitfield FCMPCC <26:56>; // for FCMP & FCMPEa +def bitfield FMOVCC <13:11>; // for FMOVcc +def bitfield CC <12:11>; // for MOVcc & Tcc +def bitfield MOVCC3 <18>; // also for MOVcc +def bitfield CMASK <6:4>; +def bitfield COND2 <28:25>; +def bitfield COND4 <17:14>; +def bitfield D16HI <21:20>; +def bitfield D16LO <13:0>; +def bitfield DISP19 <18:0>; +def bitfield DISP22 <21:0>; +def bitfield DISP30 <29:0>; +def bitfield FCN <29:26>; +def bitfield I <13>; +def bitfield IMM_ASI <12:5>; +def bitfield IMM22 <21:0>; +def bitfield MMASK <3:0>; +def bitfield OP <31:30>; +def bitfield OP2 <24:22>; +def bitfield OP3 <24:19>; +def bitfield OPF <13:5>; +def bitfield OPF_CC <13:11>; +def bitfield OPF_LOW5 <9:5>; +def bitfield OPF_LOW6 <10:5>; +def bitfield P <19>; +def bitfield RCOND2 <27:25>; +def bitfield RCOND3 <12:10>; +def bitfield RCOND4 <12:10>; +def bitfield RD <29:25>; +def bitfield RS1 <18:14>; +def bitfield RS2 <4:0>; +def bitfield SHCNT32 <4:0>; +def bitfield SHCNT64 <5:0>; +def bitfield SIMM10 <9:0>; +def bitfield SIMM11 <10:0>; +def bitfield SIMM13 <12:0>; +def bitfield SW_TRAP <7:0>; +def bitfield X <12>; diff --git a/src/arch/sparc/isa/decoder.isa b/src/arch/sparc/isa/decoder.isa new file mode 100644 index 000000000..1384b21a0 --- /dev/null +++ b/src/arch/sparc/isa/decoder.isa @@ -0,0 +1,924 @@ +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Ali Saidi +// Gabe Black +// Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// The actual decoder specification +// + +decode OP default Unknown::unknown() +{ + 0x0: decode OP2 + { + //Throw an illegal instruction acception + 0x0: Trap::illtrap({{fault = new IllegalInstruction;}}); + format BranchN + { + 0x1: decode COND2 + { + //Branch Always + 0x8: decode A + { + 0x0: b(19, {{ + NNPC = xc->readPC() + disp; + }}); + 0x1: b(19, {{ + NPC = xc->readPC() + disp; + NNPC = NPC + 4; + }}, ',a'); + } + //Branch Never + 0x0: decode A + { + 0x0: bn(19, {{ + NNPC = NNPC;//Don't do anything + }}); + 0x1: bn(19, {{ + NPC = xc->readNextPC() + 4; + NNPC = NPC + 4; + }}, ',a'); + } + default: decode BPCC + { + 0x0: bpcci(19, {{ + if(passesCondition(Ccr<3:0>, COND2)) + NNPC = xc->readPC() + disp; + else + handle_annul + }}); + 0x2: bpccx(19, {{ + if(passesCondition(Ccr<7:4>, COND2)) + NNPC = xc->readPC() + disp; + else + handle_annul + }}); + } + } + 0x2: bicc(22, {{ + if(passesCondition(Ccr<3:0>, COND2)) + NNPC = xc->readPC() + disp; + else + handle_annul + }}); + } + 0x3: decode RCOND2 + { + format BranchSplit + { + 0x1: bpreq({{ + if(Rs1.sdw == 0) + NNPC = xc->readPC() + disp; + else + handle_annul + }}); + 0x2: bprle({{ + if(Rs1.sdw <= 0) + NNPC = xc->readPC() + disp; + else + handle_annul + }}); + 0x3: bprl({{ + if(Rs1.sdw < 0) + NNPC = xc->readPC() + disp; + else + handle_annul + }}); + 0x5: bprne({{ + if(Rs1.sdw != 0) + NNPC = xc->readPC() + disp; + else + handle_annul + }}); + 0x6: bprg({{ + if(Rs1.sdw > 0) + NNPC = xc->readPC() + disp; + else + handle_annul + }}); + 0x7: bprge({{ + if(Rs1.sdw >= 0) + NNPC = xc->readPC() + disp; + else + handle_annul + }}); + } + } + //SETHI (or NOP if rd == 0 and imm == 0) + 0x4: SetHi::sethi({{Rd.udw = imm;}}); + 0x5: Trap::fbpfcc({{fault = new FpDisabled;}}); + 0x6: Trap::fbfcc({{fault = new FpDisabled;}}); + } + 0x1: BranchN::call(30, {{ + R15 = xc->readPC(); + NNPC = R15 + disp; + }}); + 0x2: decode OP3 { + format IntOp { + 0x00: add({{Rd = Rs1.sdw + Rs2_or_imm13;}}); + 0x01: and({{Rd = Rs1.sdw & Rs2_or_imm13;}}); + 0x02: or({{Rd = Rs1.sdw | Rs2_or_imm13;}}); + 0x03: xor({{Rd = Rs1.sdw ^ Rs2_or_imm13;}}); + 0x04: sub({{Rd = Rs1.sdw - Rs2_or_imm13;}}); + 0x05: andn({{Rd = Rs1.sdw & ~Rs2_or_imm13;}}); + 0x06: orn({{Rd = Rs1.sdw | ~Rs2_or_imm13;}}); + 0x07: xnor({{Rd = ~(Rs1.sdw ^ Rs2_or_imm13);}}); + 0x08: addc({{Rd = Rs1.sdw + Rs2_or_imm13 + Ccr<0:0>;}}); + 0x09: mulx({{Rd = Rs1.sdw * Rs2_or_imm13;}}); + 0x0A: umul({{ + Rd = Rs1.udw<31:0> * Rs2_or_imm13<31:0>; + Y = Rd<63:32>; + }}); + 0x0B: smul({{ + Rd.sdw = Rs1.sdw<31:0> * Rs2_or_imm13<31:0>; + Y = Rd.sdw; + }}); + 0x0C: subc({{Rd.sdw = Rs1.sdw + (~Rs2_or_imm13) + 1 - Ccr<0:0>}}); + 0x0D: udivx({{ + if(Rs2_or_imm13 == 0) fault = new DivisionByZero; + else Rd.udw = Rs1.udw / Rs2_or_imm13; + }}); + 0x0E: udiv({{ + if(Rs2_or_imm13 == 0) fault = new DivisionByZero; + else + { + Rd.udw = ((Y << 32) | Rs1.udw<31:0>) / Rs2_or_imm13; + if(Rd.udw >> 32 != 0) + Rd.udw = 0xFFFFFFFF; + } + }}); + 0x0F: sdiv({{ + if(Rs2_or_imm13.sdw == 0) + fault = new DivisionByZero; + else + { + Rd.udw = ((int64_t)((Y << 32) | Rs1.sdw<31:0>)) / Rs2_or_imm13.sdw; + if(Rd.udw<63:31> != 0) + Rd.udw = 0x7FFFFFFF; + else if(Rd.udw<63:> && Rd.udw<62:31> != 0xFFFFFFFF) + Rd.udw = 0xFFFFFFFF80000000ULL; + } + }}); + } + format IntOpCc { + 0x10: addcc({{ + int64_t resTemp, val2 = Rs2_or_imm13; + Rd = resTemp = Rs1 + val2;}}, + {{(Rs1<31:0> + val2<31:0>)<32:>}}, + {{Rs1<31:> == val2<31:> && val2<31:> != resTemp<31:>}}, + {{(Rs1<63:1> + val2<63:1> + (Rs1 & val2)<0:>)<63:>}}, + {{Rs1<63:> == val2<63:> && val2<63:> != resTemp<63:>}} + ); + 0x11: IntOpCcRes::andcc({{Rd = Rs1 & Rs2_or_imm13;}}); + 0x12: IntOpCcRes::orcc({{Rd = Rs1 | Rs2_or_imm13;}}); + 0x13: IntOpCcRes::xorcc({{Rd = Rs1 ^ Rs2_or_imm13;}}); + 0x14: subcc({{ + int64_t val2 = Rs2_or_imm13; + Rd = Rs1 - val2;}}, + {{(~(Rs1<31:0> + (~val2)<31:0> + 1))<32:>}}, + {{(Rs1<31:> != val2<31:>) && (Rs1<31:> != Rd<31:>)}}, + {{(~(Rs1<63:1> + (~val2)<63:1> + + (Rs1 | ~val2)<0:>))<63:>}}, + {{Rs1<63:> != val2<63:> && Rs1<63:> != Rd<63:>}} + ); + 0x15: IntOpCcRes::andncc({{Rd = Rs1 & ~Rs2_or_imm13;}}); + 0x16: IntOpCcRes::orncc({{Rd = Rs1 | ~Rs2_or_imm13;}}); + 0x17: IntOpCcRes::xnorcc({{Rd = ~(Rs1 ^ Rs2_or_imm13);}}); + 0x18: addccc({{ + int64_t resTemp, val2 = Rs2_or_imm13; + int64_t carryin = Ccr<0:0>; + Rd = resTemp = Rs1 + val2 + carryin;}}, + {{(Rs1<31:0> + val2<31:0> + carryin)<32:>}}, + {{Rs1<31:> == val2<31:> && val2<31:> != resTemp<31:>}}, + {{(Rs1<63:1> + val2<63:1> + + ((Rs1 & val2) | (carryin & (Rs1 | val2)))<0:>)<63:>}}, + {{Rs1<63:> == val2<63:> && val2<63:> != resTemp<63:>}} + ); + 0x1A: umulcc({{ + uint64_t resTemp; + Rd = resTemp = Rs1.udw<31:0> * Rs2_or_imm13.udw<31:0>; + Y = resTemp<63:32>;}}, + {{0}},{{0}},{{0}},{{0}}); + 0x1B: smulcc({{ + int64_t resTemp; + Rd = resTemp = Rs1.sdw<31:0> * Rs2_or_imm13.sdw<31:0>; + Y = resTemp<63:32>;}}, + {{0}},{{0}},{{0}},{{0}}); + 0x1C: subccc({{ + int64_t resTemp, val2 = Rs2_or_imm13; + int64_t carryin = Ccr<0:0>; + Rd = resTemp = Rs1 + ~val2 + 1 - carryin;}}, + {{(~((Rs1<31:0> + (~(val2 + carryin))<31:0> + 1))<32:>)}}, + {{Rs1<31:> != val2<31:> && Rs1<31:> != resTemp<31:>}}, + {{(~((Rs1<63:1> + (~(val2 + carryin))<63:1>) + (Rs1<0:> + (~(val2+carryin))<0:> + 1)<63:1>))<63:>}}, + {{Rs1<63:> != val2<63:> && Rs1<63:> != resTemp<63:>}} + ); + 0x1D: udivxcc({{ + if(Rs2_or_imm13.udw == 0) fault = new DivisionByZero; + else Rd = Rs1.udw / Rs2_or_imm13.udw;}} + ,{{0}},{{0}},{{0}},{{0}}); + 0x1E: udivcc({{ + uint32_t resTemp, val2 = Rs2_or_imm13.udw; + int32_t overflow = 0; + if(val2 == 0) fault = new DivisionByZero; + else + { + resTemp = (uint64_t)((Y << 32) | Rs1.udw<31:0>) / val2; + overflow = (resTemp<63:32> != 0); + if(overflow) Rd = resTemp = 0xFFFFFFFF; + else Rd = resTemp; + } }}, + {{0}}, + {{overflow}}, + {{0}}, + {{0}} + ); + 0x1F: sdivcc({{ + int64_t val2 = Rs2_or_imm13.sdw<31:0>; + bool overflow = false, underflow = false; + if(val2 == 0) fault = new DivisionByZero; + else + { + Rd = (int64_t)((Y << 32) | Rs1.sdw<31:0>) / val2; + overflow = (Rd<63:31> != 0); + underflow = (Rd<63:> && Rd<62:31> != 0xFFFFFFFF); + if(overflow) Rd = 0x7FFFFFFF; + else if(underflow) Rd = 0xFFFFFFFF80000000ULL; + } }}, + {{0}}, + {{overflow || underflow}}, + {{0}}, + {{0}} + ); + 0x20: taddcc({{ + int64_t resTemp, val2 = Rs2_or_imm13; + Rd = resTemp = Rs1 + val2; + int32_t overflow = Rs1<1:0> || val2<1:0> || (Rs1<31:> == val2<31:> && val2<31:> != resTemp<31:>);}}, + {{((Rs1 & 0xFFFFFFFF + val2 & 0xFFFFFFFF) >> 31)}}, + {{overflow}}, + {{((Rs1 >> 1) + (val2 >> 1) + (Rs1 & val2 & 0x1))<63:>}}, + {{Rs1<63:> == val2<63:> && val2<63:> != resTemp<63:>}} + ); + 0x21: tsubcc({{ + int64_t resTemp, val2 = Rs2_or_imm13; + Rd = resTemp = Rs1 + val2; + int32_t overflow = Rs1<1:0> || val2<1:0> || (Rs1<31:> == val2<31:> && val2<31:> != resTemp<31:>);}}, + {{(Rs1 & 0xFFFFFFFF + val2 & 0xFFFFFFFF) >> 31}}, + {{overflow}}, + {{((Rs1 >> 1) + (val2 >> 1) + (Rs1 & val2 & 0x1))<63:>}}, + {{Rs1<63:> == val2<63:> && val2<63:> != resTemp<63:>}} + ); + 0x22: taddcctv({{ + int64_t val2 = Rs2_or_imm13; + Rd = Rs1 + val2; + int32_t overflow = Rs1<1:0> || val2<1:0> || + (Rs1<31:> == val2<31:> && val2<31:> != Rd<31:>); + if(overflow) fault = new TagOverflow;}}, + {{((Rs1 & 0xFFFFFFFF + val2 & 0xFFFFFFFF) >> 31)}}, + {{overflow}}, + {{((Rs1 >> 1) + (val2 >> 1) + (Rs1 & val2 & 0x1))<63:>}}, + {{Rs1<63:> == val2<63:> && val2<63:> != Rd<63:>}} + ); + 0x23: tsubcctv({{ + int64_t resTemp, val2 = Rs2_or_imm13; + Rd = resTemp = Rs1 + val2; + int32_t overflow = Rs1<1:0> || val2<1:0> || (Rs1<31:> == val2<31:> && val2<31:> != resTemp<31:>); + if(overflow) fault = new TagOverflow;}}, + {{((Rs1 & 0xFFFFFFFF + val2 & 0xFFFFFFFF) >> 31)}}, + {{overflow}}, + {{((Rs1 >> 1) + (val2 >> 1) + (Rs1 & val2 & 0x1))<63:>}}, + {{Rs1<63:> == val2<63:> && val2<63:> != resTemp<63:>}} + ); + 0x24: mulscc({{ + int64_t resTemp, multiplicand = Rs2_or_imm13; + int32_t multiplier = Rs1<31:0>; + int32_t savedLSB = Rs1<0:>; + multiplier = multiplier<31:1> | + ((Ccr<3:3> + ^ Ccr<1:1>) << 32); + if(!Y<0:>) + multiplicand = 0; + Rd = resTemp = multiplicand + multiplier; + Y = Y<31:1> | (savedLSB << 31);}}, + {{((multiplicand & 0xFFFFFFFF + multiplier & 0xFFFFFFFF) >> 31)}}, + {{multiplicand<31:> == multiplier<31:> && multiplier<31:> != resTemp<31:>}}, + {{((multiplicand >> 1) + (multiplier >> 1) + (multiplicand & multiplier & 0x1))<63:>}}, + {{multiplicand<63:> == multiplier<63:> && multiplier<63:> != resTemp<63:>}} + ); + } + format IntOp + { + 0x25: decode X { + 0x0: sll({{Rd = Rs1 << (I ? SHCNT32 : Rs2<4:0>);}}); + 0x1: sllx({{Rd = Rs1 << (I ? SHCNT64 : Rs2<5:0>);}}); + } + 0x26: decode X { + 0x0: srl({{Rd = Rs1.uw >> (I ? SHCNT32 : Rs2<4:0>);}}); + 0x1: srlx({{Rd = Rs1.udw >> (I ? SHCNT64 : Rs2<5:0>);}}); + } + 0x27: decode X { + 0x0: sra({{Rd = Rs1.sw >> (I ? SHCNT32 : Rs2<4:0>);}}); + 0x1: srax({{Rd = Rs1.sdw >> (I ? SHCNT64 : Rs2<5:0>);}}); + } + // XXX might want a format rdipr thing here + 0x28: decode RS1 { + 0xF: decode I { + 0x0: Nop::stbar({{/*stuff*/}}); + 0x1: Nop::membar({{/*stuff*/}}); + } + default: rdasr({{ + Rd = xc->readMiscRegWithEffect(RS1 + AsrStart, fault); + }}); + } + 0x29: HPriv::rdhpr({{ + // XXX Need to protect with format that traps non-priv/priv + // access + Rd = xc->readMiscRegWithEffect(RS1 + HprStart, fault); + }}); + 0x2A: Priv::rdpr({{ + // XXX Need to protect with format that traps non-priv + // access + Rd = xc->readMiscRegWithEffect(RS1 + PrStart, fault); + }}); + 0x2B: BasicOperate::flushw({{ + if(NWindows - 2 - Cansave == 0) + { + if(Otherwin) + fault = new SpillNOther(Wstate<5:3>); + else + fault = new SpillNNormal(Wstate<2:0>); + } + }}); + 0x2C: decode MOVCC3 + { + 0x0: Trap::movccfcc({{fault = new FpDisabled;}}); + 0x1: decode CC + { + 0x0: movcci({{ + if(passesCondition(Ccr<3:0>, COND4)) + Rd = Rs2_or_imm11; + else + Rd = Rd; + }}); + 0x2: movccx({{ + if(passesCondition(Ccr<7:4>, COND4)) + Rd = Rs2_or_imm11; + else + Rd = Rd; + }}); + } + } + 0x2D: sdivx({{ + if(Rs2_or_imm13.sdw == 0) fault = new DivisionByZero; + else Rd.sdw = Rs1.sdw / Rs2_or_imm13.sdw; + }}); + 0x2E: decode RS1 { + 0x0: IntOp::popc({{ + int64_t count = 0; + uint64_t temp = Rs2_or_imm13; + //Count the 1s in the front 4bits until none are left + uint8_t oneBits[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4}; + while(temp) + { + count += oneBits[temp & 0xF]; + temp = temp >> 4; + } + Rd = count; + }}); + } + 0x2F: decode RCOND3 + { + 0x1: movreq({{Rd = (Rs1.sdw == 0) ? Rs2_or_imm10 : Rd;}}); + 0x2: movrle({{Rd = (Rs1.sdw <= 0) ? Rs2_or_imm10 : Rd;}}); + 0x3: movrl({{Rd = (Rs1.sdw < 0) ? Rs2_or_imm10 : Rd;}}); + 0x5: movrne({{Rd = (Rs1.sdw != 0) ? Rs2_or_imm10 : Rd;}}); + 0x6: movrg({{Rd = (Rs1.sdw > 0) ? Rs2_or_imm10 : Rd;}}); + 0x7: movrge({{Rd = (Rs1.sdw >= 0) ? Rs2_or_imm10 : Rd;}}); + } + 0x30: wrasr({{ + xc->setMiscRegWithEffect(RD + AsrStart, Rs1 ^ Rs2_or_imm13); + }}); + 0x31: decode FCN { + 0x0: BasicOperate::saved({{/*Boogy Boogy*/}}); + 0x1: BasicOperate::restored({{/*Boogy Boogy*/}}); + } + 0x32: Priv::wrpr({{ + // XXX Need to protect with format that traps non-priv + // access + fault = xc->setMiscRegWithEffect(RD + PrStart, Rs1 ^ Rs2_or_imm13); + }}); + 0x33: HPriv::wrhpr({{ + // XXX Need to protect with format that traps non-priv/priv + // access + fault = xc->setMiscRegWithEffect(RD + HprStart, Rs1 ^ Rs2_or_imm13); + }}); + 0x34: decode OPF{ + format BasicOperate{ + 0x01: fmovs({{ + Frd.uw = Frs2.uw; + //fsr.ftt = fsr.cexc = 0 + Fsr &= ~(7 << 14); + Fsr &= ~(0x1F); + }}); + 0x02: fmovd({{ + Frd.udw = Frs2.udw; + //fsr.ftt = fsr.cexc = 0 + Fsr &= ~(7 << 14); + Fsr &= ~(0x1F); + }}); + 0x03: Trap::fmovq({{fault = new FpDisabled;}}); + 0x05: fnegs({{ + Frd.uw = Frs2.uw ^ (1UL << 31); + //fsr.ftt = fsr.cexc = 0 + Fsr &= ~(7 << 14); + Fsr &= ~(0x1F); + }}); + 0x06: fnegd({{ + Frd.udw = Frs2.udw ^ (1ULL << 63); + //fsr.ftt = fsr.cexc = 0 + Fsr &= ~(7 << 14); + Fsr &= ~(0x1F); + }}); + 0x07: Trap::fnegq({{fault = new FpDisabled;}}); + 0x09: fabss({{ + Frd.uw = ((1UL << 31) - 1) & Frs2.uw; + //fsr.ftt = fsr.cexc = 0 + Fsr &= ~(7 << 14); + Fsr &= ~(0x1F); + }}); + 0x0A: fabsd({{ + Frd.udw = ((1ULL << 63) - 1) & Frs2.udw; + //fsr.ftt = fsr.cexc = 0 + Fsr &= ~(7 << 14); + Fsr &= ~(0x1F); + }}); + 0x0B: Trap::fabsq({{fault = new FpDisabled;}}); + 0x29: fsqrts({{Frd.sf = sqrt(Frs2.sf);}}); + 0x2A: fsqrtd({{Frd.df = sqrt(Frs2.df);}}); + 0x2B: Trap::fsqrtq({{fault = new FpDisabled;}}); + 0x41: fadds({{Frd.sf = Frs1.sf + Frs2.sf;}}); + 0x42: faddd({{Frd.df = Frs1.df + Frs2.df;}}); + 0x43: Trap::faddq({{fault = new FpDisabled;}}); + 0x45: fsubs({{Frd.sf = Frs1.sf - Frs2.sf;}}); + 0x46: fsubd({{Frd.df = Frs1.df - Frs2.df;}}); + 0x47: Trap::fsubq({{fault = new FpDisabled;}}); + 0x49: fmuls({{Frd.sf = Frs1.sf * Frs2.sf;}}); + 0x4A: fmuld({{Frd.df = Frs1.df * Frs2.df;}}); + 0x4B: Trap::fmulq({{fault = new FpDisabled;}}); + 0x4D: fdivs({{Frd.sf = Frs1.sf / Frs2.sf;}}); + 0x4E: fdivd({{Frd.df = Frs1.df / Frs2.df;}}); + 0x4F: Trap::fdivq({{fault = new FpDisabled;}}); + 0x69: fsmuld({{Frd.df = Frs1.sf * Frs2.sf;}}); + 0x6E: Trap::fdmulq({{fault = new FpDisabled;}}); + 0x81: fstox({{ + Frd.df = (double)static_cast<int64_t>(Frs2.sf); + }}); + 0x82: fdtox({{ + Frd.df = (double)static_cast<int64_t>(Frs2.df); + }}); + 0x83: Trap::fqtox({{fault = new FpDisabled;}}); + 0x84: fxtos({{ + Frd.sf = static_cast<float>((int64_t)Frs2.df); + }}); + 0x88: fxtod({{ + Frd.df = static_cast<double>((int64_t)Frs2.df); + }}); + 0x8C: Trap::fxtoq({{fault = new FpDisabled;}}); + 0xC4: fitos({{ + Frd.sf = static_cast<float>((int32_t)Frs2.sf); + }}); + 0xC6: fdtos({{Frd.sf = Frs2.df;}}); + 0xC7: Trap::fqtos({{fault = new FpDisabled;}}); + 0xC8: fitod({{ + Frd.df = static_cast<double>((int32_t)Frs2.sf); + }}); + 0xC9: fstod({{Frd.df = Frs2.sf;}}); + 0xCB: Trap::fqtod({{fault = new FpDisabled;}}); + 0xCC: Trap::fitoq({{fault = new FpDisabled;}}); + 0xCD: Trap::fstoq({{fault = new FpDisabled;}}); + 0xCE: Trap::fdtoq({{fault = new FpDisabled;}}); + 0xD1: fstoi({{ + Frd.sf = (float)static_cast<int32_t>(Frs2.sf); + }}); + 0xD2: fdtoi({{ + Frd.sf = (float)static_cast<int32_t>(Frs2.df); + }}); + 0xD3: Trap::fqtoi({{fault = new FpDisabled;}}); + default: Trap::fpop1({{fault = new FpDisabled;}}); + } + } + 0x35: Trap::fpop2({{fault = new FpDisabled;}}); + //This used to be just impdep1, but now it's a whole bunch + //of instructions + 0x36: decode OPF{ + 0x00: Trap::edge8({{fault = new IllegalInstruction;}}); + 0x01: Trap::edge8n({{fault = new IllegalInstruction;}}); + 0x02: Trap::edge8l({{fault = new IllegalInstruction;}}); + 0x03: Trap::edge8ln({{fault = new IllegalInstruction;}}); + 0x04: Trap::edge16({{fault = new IllegalInstruction;}}); + 0x05: Trap::edge16n({{fault = new IllegalInstruction;}}); + 0x06: Trap::edge16l({{fault = new IllegalInstruction;}}); + 0x07: Trap::edge16ln({{fault = new IllegalInstruction;}}); + 0x08: Trap::edge32({{fault = new IllegalInstruction;}}); + 0x09: Trap::edge32n({{fault = new IllegalInstruction;}}); + 0x0A: Trap::edge32l({{fault = new IllegalInstruction;}}); + 0x0B: Trap::edge32ln({{fault = new IllegalInstruction;}}); + 0x10: Trap::array8({{fault = new IllegalInstruction;}}); + 0x12: Trap::array16({{fault = new IllegalInstruction;}}); + 0x14: Trap::array32({{fault = new IllegalInstruction;}}); + 0x18: BasicOperate::alignaddr({{ + uint64_t sum = Rs1 + Rs2; + Rd = sum & ~7; + Gsr = (Gsr & ~7) | (sum & 7); + }}); + 0x19: Trap::bmask({{fault = new IllegalInstruction;}}); + 0x1A: BasicOperate::alignaddresslittle({{ + uint64_t sum = Rs1 + Rs2; + Rd = sum & ~7; + Gsr = (Gsr & ~7) | ((~sum + 1) & 7); + }}); + 0x20: Trap::fcmple16({{fault = new IllegalInstruction;}}); + 0x22: Trap::fcmpne16({{fault = new IllegalInstruction;}}); + 0x24: Trap::fcmple32({{fault = new IllegalInstruction;}}); + 0x26: Trap::fcmpne32({{fault = new IllegalInstruction;}}); + 0x28: Trap::fcmpgt16({{fault = new IllegalInstruction;}}); + 0x2A: Trap::fcmpeq16({{fault = new IllegalInstruction;}}); + 0x2C: Trap::fcmpgt32({{fault = new IllegalInstruction;}}); + 0x2E: Trap::fcmpeq32({{fault = new IllegalInstruction;}}); + 0x31: Trap::fmul8x16({{fault = new IllegalInstruction;}}); + 0x33: Trap::fmul8x16au({{fault = new IllegalInstruction;}}); + 0x35: Trap::fmul8x16al({{fault = new IllegalInstruction;}}); + 0x36: Trap::fmul8sux16({{fault = new IllegalInstruction;}}); + 0x37: Trap::fmul8ulx16({{fault = new IllegalInstruction;}}); + 0x38: Trap::fmuld8sux16({{fault = new IllegalInstruction;}}); + 0x39: Trap::fmuld8ulx16({{fault = new IllegalInstruction;}}); + 0x3A: Trap::fpack32({{fault = new IllegalInstruction;}}); + 0x3B: Trap::fpack16({{fault = new IllegalInstruction;}}); + 0x3D: Trap::fpackfix({{fault = new IllegalInstruction;}}); + 0x3E: Trap::pdist({{fault = new IllegalInstruction;}}); + 0x48: BasicOperate::faligndata({{ + uint64_t msbX = Frs1.udw; + uint64_t lsbX = Frs2.udw; + //Some special cases need to be split out, first + //because they're the most likely to be used, and + //second because otherwise, we end up shifting by + //greater than the width of the type being shifted, + //namely 64, which produces undefined results according + //to the C standard. + switch(Gsr<2:0>) + { + case 0: + Frd.udw = msbX; + break; + case 8: + Frd.udw = lsbX; + break; + default: + uint64_t msbShift = Gsr<2:0> * 8; + uint64_t lsbShift = (8 - Gsr<2:0>) * 8; + uint64_t msbMask = ((uint64_t)(-1)) >> msbShift; + uint64_t lsbMask = ((uint64_t)(-1)) << lsbShift; + Frd.udw = ((msbX & msbMask) << msbShift) | + ((lsbX & lsbMask) >> lsbShift); + } + }}); + 0x4B: Trap::fpmerge({{fault = new IllegalInstruction;}}); + 0x4C: Trap::bshuffle({{fault = new IllegalInstruction;}}); + 0x4D: Trap::fexpand({{fault = new IllegalInstruction;}}); + 0x50: Trap::fpadd16({{fault = new IllegalInstruction;}}); + 0x51: Trap::fpadd16s({{fault = new IllegalInstruction;}}); + 0x52: Trap::fpadd32({{fault = new IllegalInstruction;}}); + 0x53: Trap::fpadd32s({{fault = new IllegalInstruction;}}); + 0x54: Trap::fpsub16({{fault = new IllegalInstruction;}}); + 0x55: Trap::fpsub16s({{fault = new IllegalInstruction;}}); + 0x56: Trap::fpsub32({{fault = new IllegalInstruction;}}); + 0x57: Trap::fpsub32s({{fault = new IllegalInstruction;}}); + 0x60: BasicOperate::fzero({{Frd.df = 0;}}); + 0x61: BasicOperate::fzeros({{Frd.sf = 0;}}); + 0x62: Trap::fnor({{fault = new IllegalInstruction;}}); + 0x63: Trap::fnors({{fault = new IllegalInstruction;}}); + 0x64: Trap::fandnot2({{fault = new IllegalInstruction;}}); + 0x65: Trap::fandnot2s({{fault = new IllegalInstruction;}}); + 0x66: BasicOperate::fnot2({{ + Frd.df = (double)(~((uint64_t)Frs2.df)); + }}); + 0x67: BasicOperate::fnot2s({{ + Frd.sf = (float)(~((uint32_t)Frs2.sf)); + }}); + 0x68: Trap::fandnot1({{fault = new IllegalInstruction;}}); + 0x69: Trap::fandnot1s({{fault = new IllegalInstruction;}}); + 0x6A: BasicOperate::fnot1({{ + Frd.df = (double)(~((uint64_t)Frs1.df)); + }}); + 0x6B: BasicOperate::fnot1s({{ + Frd.sf = (float)(~((uint32_t)Frs1.sf)); + }}); + 0x6C: Trap::fxor({{fault = new IllegalInstruction;}}); + 0x6D: Trap::fxors({{fault = new IllegalInstruction;}}); + 0x6E: Trap::fnand({{fault = new IllegalInstruction;}}); + 0x6F: Trap::fnands({{fault = new IllegalInstruction;}}); + 0x70: Trap::fand({{fault = new IllegalInstruction;}}); + 0x71: Trap::fands({{fault = new IllegalInstruction;}}); + 0x72: Trap::fxnor({{fault = new IllegalInstruction;}}); + 0x73: Trap::fxnors({{fault = new IllegalInstruction;}}); + 0x74: BasicOperate::fsrc1({{Frd.udw = Frs1.udw;}}); + 0x75: BasicOperate::fsrc1s({{Frd.uw = Frs1.uw;}}); + 0x76: Trap::fornot2({{fault = new IllegalInstruction;}}); + 0x77: Trap::fornot2s({{fault = new IllegalInstruction;}}); + 0x78: BasicOperate::fsrc2({{Frd.udw = Frs2.udw;}}); + 0x79: BasicOperate::fsrc2s({{Frd.uw = Frs2.uw;}}); + 0x7A: Trap::fornot1({{fault = new IllegalInstruction;}}); + 0x7B: Trap::fornot1s({{fault = new IllegalInstruction;}}); + 0x7C: Trap::for({{fault = new IllegalInstruction;}}); + 0x7D: Trap::fors({{fault = new IllegalInstruction;}}); + 0x7E: Trap::fone({{fault = new IllegalInstruction;}}); + 0x7F: Trap::fones({{fault = new IllegalInstruction;}}); + 0x80: Trap::shutdown({{fault = new IllegalInstruction;}}); + 0x81: Trap::siam({{fault = new IllegalInstruction;}}); + } + 0x37: Trap::impdep2({{fault = new IllegalInstruction;}}); + 0x38: Branch::jmpl({{ + Addr target = Rs1 + Rs2_or_imm13; + if(target & 0x3) + fault = new MemAddressNotAligned; + else + { + Rd = xc->readPC(); + NNPC = target; + } + }}); + 0x39: Branch::return({{ + //If both MemAddressNotAligned and + //a fill trap happen, it's not clear + //which one should be returned. + Addr target = Rs1 + Rs2_or_imm13; + if(target & 0x3) + fault = new MemAddressNotAligned; + else + NNPC = target; + if(fault == NoFault) + { + //CWP should be set directly so that it always happens + //Also, this will allow writing to the new window and + //reading from the old one + Cwp = (Cwp - 1 + NWindows) % NWindows; + if(Canrestore == 0) + { + if(Otherwin) + fault = new FillNOther(Wstate<5:3>); + else + fault = new FillNNormal(Wstate<2:0>); + } + else + { + Rd = Rs1 + Rs2_or_imm13; + Cansave = Cansave + 1; + Canrestore = Canrestore - 1; + } + //This is here to make sure the CWP is written + //no matter what. This ensures that the results + //are written in the new window as well. + xc->setMiscRegWithEffect(MISCREG_CWP, Cwp); + } + }}); + 0x3A: decode CC + { + 0x0: Trap::tcci({{ + if(passesCondition(Ccr<3:0>, COND2)) + { +#if FULL_SYSTEM + int lTrapNum = I ? (Rs1 + SW_TRAP) : (Rs1 + Rs2); + DPRINTF(Sparc, "The trap number is %d\n", lTrapNum); + fault = new TrapInstruction(lTrapNum); +#else + DPRINTF(Sparc, "The syscall number is %d\n", R1); + xc->syscall(R1); +#endif + } + }}); + 0x2: Trap::tccx({{ + if(passesCondition(Ccr<7:4>, COND2)) + { +#if FULL_SYSTEM + int lTrapNum = I ? (Rs1 + SW_TRAP) : (Rs1 + Rs2); + DPRINTF(Sparc, "The trap number is %d\n", lTrapNum); + fault = new TrapInstruction(lTrapNum); +#else + DPRINTF(Sparc, "The syscall number is %d\n", R1); + xc->syscall(R1); +#endif + } + }}); + } + 0x3B: Nop::flush({{/*Instruction memory flush*/}}); + 0x3C: save({{ + //CWP should be set directly so that it always happens + //Also, this will allow writing to the new window and + //reading from the old one + if(Cansave == 0) + { + if(Otherwin) + fault = new SpillNOther(Wstate<5:3>); + else + fault = new SpillNNormal(Wstate<2:0>); + Cwp = (Cwp + 2) % NWindows; + } + else if(Cleanwin - Canrestore == 0) + { + Cwp = (Cwp + 1) % NWindows; + fault = new CleanWindow; + } + else + { + Cwp = (Cwp + 1) % NWindows; + Rd = Rs1 + Rs2_or_imm13; + Cansave = Cansave - 1; + Canrestore = Canrestore + 1; + } + //This is here to make sure the CWP is written + //no matter what. This ensures that the results + //are written in the new window as well. + xc->setMiscRegWithEffect(MISCREG_CWP, Cwp); + }}); + 0x3D: restore({{ + //CWP should be set directly so that it always happens + //Also, this will allow writing to the new window and + //reading from the old one + Cwp = (Cwp - 1 + NWindows) % NWindows; + if(Canrestore == 0) + { + if(Otherwin) + fault = new FillNOther(Wstate<5:3>); + else + fault = new FillNNormal(Wstate<2:0>); + } + else + { + Rd = Rs1 + Rs2_or_imm13; + Cansave = Cansave + 1; + Canrestore = Canrestore - 1; + } + //This is here to make sure the CWP is written + //no matter what. This ensures that the results + //are written in the new window as well. + xc->setMiscRegWithEffect(MISCREG_CWP, Cwp); + }}); + 0x3E: decode FCN { + 0x0: Priv::done({{ + if(Tl == 0) + return new IllegalInstruction; + + Cwp = Tstate<4:0>; + Pstate = Tstate<20:8>; + Asi = Tstate<31:24>; + Ccr = Tstate<39:32>; + Gl = Tstate<42:40>; + NPC = Tnpc; + NNPC = Tnpc + 4; + Tl = Tl - 1; + }}); + 0x1: Priv::retry({{ + if(Tl == 0) + return new IllegalInstruction; + Cwp = Tstate<4:0>; + Pstate = Tstate<20:8>; + Asi = Tstate<31:24>; + Ccr = Tstate<39:32>; + Gl = Tstate<42:40>; + NPC = Tpc; + NNPC = Tnpc + 4; + Tl = Tl - 1; + }}); + } + } + } + 0x3: decode OP3 { + format Load { + 0x00: lduw({{Rd = Mem;}}, {{32}}); + 0x01: ldub({{Rd = Mem;}}, {{8}}); + 0x02: lduh({{Rd = Mem;}}, {{16}}); + 0x03: ldd({{ + uint64_t val = Mem; + RdLow = val<31:0>; + RdHigh = val<63:32>; + }}, {{64}}); + } + format Store { + 0x04: stw({{Mem = Rd.sw;}}, {{32}}); + 0x05: stb({{Mem = Rd.sb;}}, {{8}}); + 0x06: sth({{Mem = Rd.shw;}}, {{16}}); + 0x07: std({{Mem = RdLow<31:0> | (RdHigh<31:0> << 32);}}, {{64}}); + } + format Load { + 0x08: ldsw({{Rd = (int32_t)Mem;}}, {{32}}); + 0x09: ldsb({{Rd = (int8_t)Mem;}}, {{8}}); + 0x0A: ldsh({{Rd = (int16_t)Mem;}}, {{16}}); + 0x0B: ldx({{Rd = (int64_t)Mem;}}, {{64}}); + 0x0D: ldstub({{ + Rd = Mem; + Mem = 0xFF; + }}, {{8}}); + } + 0x0E: Store::stx({{Mem = Rd}}, {{64}}); + 0x0F: LoadStore::swap({{ + uint32_t temp = Rd; + Rd = Mem; + Mem = temp; + }}, {{32}}); + format Load { + 0x10: lduwa({{Rd = Mem;}}, {{32}}); + 0x11: lduba({{Rd = Mem;}}, {{8}}); + 0x12: lduha({{Rd = Mem;}}, {{16}}); + 0x13: ldda({{ + uint64_t val = Mem; + RdLow = val<31:0>; + RdHigh = val<63:32>; + }}, {{64}}); + } + format Store { + 0x14: stwa({{Mem = Rd;}}, {{32}}); + 0x15: stba({{Mem = Rd;}}, {{8}}); + 0x16: stha({{Mem = Rd;}}, {{16}}); + 0x17: stda({{Mem = RdLow<31:0> | RdHigh<31:0> << 32;}}, {{64}}); + } + format Load { + 0x18: ldswa({{Rd = (int32_t)Mem;}}, {{32}}); + 0x19: ldsba({{Rd = (int8_t)Mem;}}, {{8}}); + 0x1A: ldsha({{Rd = (int16_t)Mem;}}, {{16}}); + 0x1B: ldxa({{Rd = (int64_t)Mem;}}, {{64}}); + } + 0x1D: LoadStore::ldstuba({{ + Rd = Mem; + Mem = 0xFF; + }}, {{8}}); + 0x1E: Store::stxa({{Mem = Rd}}, {{64}}); + 0x1F: LoadStore::swapa({{ + uint32_t temp = Rd; + Rd = Mem; + Mem = temp; + }}, {{32}}); + format Trap { + 0x20: Load::ldf({{Frd.uw = Mem;}}, {{32}}); + 0x21: decode X { + 0x0: Load::ldfsr({{Fsr = Mem<31:0> | Fsr<63:32>;}}, {{32}}); + 0x1: Load::ldxfsr({{Fsr = Mem;}}, {{64}}); + } + 0x22: ldqf({{fault = new FpDisabled;}}); + 0x23: Load::lddf({{Frd.udw = Mem;}}, {{64}}); + 0x24: Store::stf({{Mem = Frd.uw;}}, {{32}}); + 0x25: decode X { + 0x0: Store::stfsr({{Mem = Fsr<31:0>;}}, {{32}}); + 0x1: Store::stxfsr({{Mem = Fsr;}}, {{64}}); + } + 0x26: stqf({{fault = new FpDisabled;}}); + 0x27: Store::stdf({{Mem = Frd.udw;}}, {{64}}); + 0x2D: Nop::prefetch({{ }}); + 0x30: Load::ldfa({{Frd.uw = Mem;}}, {{32}}); + 0x32: ldqfa({{fault = new FpDisabled;}}); + 0x33: Load::lddfa({{Frd.udw = Mem;}}, {{64}}); + 0x34: Store::stfa({{Mem = Frd.uw;}}, {{32}}); + 0x36: stqfa({{fault = new FpDisabled;}}); + //XXX need to work in the ASI thing + 0x37: Store::stdfa({{Mem = Frd.udw;}}, {{64}}); + 0x3C: Cas::casa({{ + uint64_t val = Mem.uw; + if(Rs2.uw == val) + Mem.uw = Rd.uw; + Rd.uw = val; + }}); + 0x3D: Nop::prefetcha({{ }}); + 0x3E: Cas::casxa({{ + uint64_t val = Mem.udw; + if(Rs2 == val) + Mem.udw = Rd; + Rd = val; + }}); + } + } +} diff --git a/src/arch/sparc/isa/formats.isa b/src/arch/sparc/isa/formats.isa new file mode 100644 index 000000000..17d68061b --- /dev/null +++ b/src/arch/sparc/isa/formats.isa @@ -0,0 +1,28 @@ +//Include the basic format +//Templates from this format are used later +##include "formats/basic.isa" + +//Include the noop format +##include "formats/nop.isa" + +//Include the integerOp and integerOpCc format +##include "formats/integerop.isa" + +//Include the memory format +##include "formats/mem.isa" + +//Include the compare and swap format +##include "formats/cas.isa" + +//Include the trap format +##include "formats/trap.isa" + +//Include the "unknown" format +##include "formats/unknown.isa" + +//Include the priveleged mode format +##include "formats/priv.isa" + +//Include the branch format +##include "formats/branch.isa" + diff --git a/src/arch/sparc/isa/formats/basic.isa b/src/arch/sparc/isa/formats/basic.isa new file mode 100644 index 000000000..0a47a7ffe --- /dev/null +++ b/src/arch/sparc/isa/formats/basic.isa @@ -0,0 +1,91 @@ +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Ali Saidi +// Gabe Black +// Steve Reinhardt + +// Declarations for execute() methods. +def template BasicExecDeclare {{ + Fault execute(%(CPU_exec_context)s *, Trace::InstRecord *) const; +}}; + +// Basic instruction class declaration template. +def template BasicDeclare {{ + /** + * Static instruction class for "%(mnemonic)s". + */ + class %(class_name)s : public %(base_class)s + { + public: + // Constructor. + %(class_name)s(MachInst machInst); + %(BasicExecDeclare)s + }; +}}; + +// Basic instruction class constructor template. +def template BasicConstructor {{ + inline %(class_name)s::%(class_name)s(MachInst machInst) + : %(base_class)s("%(mnemonic)s", machInst, %(op_class)s) + { + %(constructor)s; + } +}}; + +// Basic instruction class execute method template. +def template BasicExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(op_decl)s; + %(op_rd)s; + %(code)s; + + if(fault == NoFault) + { + %(op_wb)s; + } + return fault; + } +}}; + +// Basic decode template. +def template BasicDecode {{ + return new %(class_name)s(machInst); +}}; + +// The most basic instruction format... used only for a few misc. insts +def format BasicOperate(code, *flags) {{ + iop = InstObjParams(name, Name, 'SparcStaticInst', + CodeBlock(code), flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = BasicExecute.subst(iop) +}}; diff --git a/src/arch/sparc/isa/formats/branch.isa b/src/arch/sparc/isa/formats/branch.isa new file mode 100644 index 000000000..2c206354b --- /dev/null +++ b/src/arch/sparc/isa/formats/branch.isa @@ -0,0 +1,272 @@ +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Gabe Black +// Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Branch instructions +// + +output header {{ + /** + * Base class for branch operations. + */ + class Branch : public SparcStaticInst + { + protected: + // Constructor + Branch(const char *mnem, MachInst _machInst, OpClass __opClass) : + SparcStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + }; + + /** + * Base class for branch operations with an immediate displacement. + */ + class BranchDisp : public Branch + { + protected: + // Constructor + BranchDisp(const char *mnem, MachInst _machInst, + OpClass __opClass) : + Branch(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + + int32_t disp; + }; + + /** + * Base class for branches with n bit displacements. + */ + template<int bits> + class BranchNBits : public BranchDisp + { + protected: + // Constructor + BranchNBits(const char *mnem, MachInst _machInst, + OpClass __opClass) : + BranchDisp(mnem, _machInst, __opClass) + { + disp = sign_ext(_machInst << 2, bits + 2); + } + }; + + /** + * Base class for 16bit split displacements. + */ + class BranchSplit : public BranchDisp + { + protected: + // Constructor + BranchSplit(const char *mnem, MachInst _machInst, + OpClass __opClass) : + BranchDisp(mnem, _machInst, __opClass) + { + disp = sign_ext((D16HI << 16) | (D16LO << 2), 18); + } + }; + + /** + * Base class for branches that use an immediate and a register to + * compute their displacements. + */ + class BranchImm13 : public Branch + { + protected: + // Constructor + BranchImm13(const char *mnem, MachInst _machInst, OpClass __opClass) : + Branch(mnem, _machInst, __opClass), imm(sign_ext(SIMM13, 13)) + { + } + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + + int32_t imm; + }; +}}; + +output decoder {{ + + template class BranchNBits<19>; + + template class BranchNBits<22>; + + template class BranchNBits<30>; + + std::string Branch::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream response; + + printMnemonic(response, mnemonic); + printRegArray(response, _srcRegIdx, _numSrcRegs); + if(_numDestRegs && _numSrcRegs) + response << ", "; + printDestReg(response, 0); + + return response.str(); + } + + std::string BranchImm13::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream response; + + printMnemonic(response, mnemonic); + printRegArray(response, _srcRegIdx, _numSrcRegs); + if(_numSrcRegs > 0) + response << ", "; + ccprintf(response, "0x%x", imm); + if (_numDestRegs > 0) + response << ", "; + printDestReg(response, 0); + + return response.str(); + } + + std::string BranchDisp::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream response; + std::string symbol; + Addr symbolAddr; + + Addr target = disp + pc; + + printMnemonic(response, mnemonic); + ccprintf(response, "0x%x", target); + + if(symtab->findNearestSymbol(target, symbol, symbolAddr)) + { + ccprintf(response, " <%s", symbol); + if(symbolAddr != target) + ccprintf(response, "+%d>", target - symbolAddr); + else + ccprintf(response, ">"); + } + + return response.str(); + } +}}; + +def template BranchExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + //Attempt to execute the instruction + Fault fault = NoFault; + + %(op_decl)s; + %(op_rd)s; + + NNPC = xc->readNextNPC(); + %(code)s; + + if(fault == NoFault) + { + //Write the resulting state to the execution context + %(op_wb)s; + } + + return fault; + } +}}; + +let {{ + handle_annul = ''' + { + if(A) + { + NPC = xc->readNextNPC(); + NNPC = NPC + 4; + } + else + { + NPC = xc->readNextPC(); + NNPC = xc->readNextNPC(); + } + }''' +}}; + +// Primary format for branch instructions: +def format Branch(code, *opt_flags) {{ + (usesImm, code, immCode, + rString, iString) = splitOutImm(code) + iop = InstObjParams(name, Name, 'Branch', code, opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + exec_output = BranchExecute.subst(iop) + if usesImm: + imm_iop = InstObjParams(name, Name + 'Imm', 'BranchImm' + iString, + immCode, opt_flags) + header_output += BasicDeclare.subst(imm_iop) + decoder_output += BasicConstructor.subst(imm_iop) + exec_output += BranchExecute.subst(imm_iop) + decode_block = ROrImmDecode.subst(iop) + else: + decode_block = BasicDecode.subst(iop) +}}; + +// Primary format for branch instructions: +def format BranchN(bits, code, *opt_flags) {{ + code = re.sub(r'handle_annul', handle_annul, code) + codeBlk = CodeBlock(code) + new_opt_flags = [] + for flag in opt_flags: + if flag == ',a': + name += ',a' + Name += 'Annul' + else: + new_opt_flags += flag + iop = InstObjParams(name, Name, "BranchNBits<%d>" % bits, codeBlk, new_opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + exec_output = BranchExecute.subst(iop) + decode_block = BasicDecode.subst(iop) +}}; + +// Primary format for branch instructions: +def format BranchSplit(code, *opt_flags) {{ + code = re.sub(r'handle_annul', handle_annul, code) + codeBlk = CodeBlock(code) + iop = InstObjParams(name, Name, 'BranchSplit', codeBlk, opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + exec_output = BranchExecute.subst(iop) + decode_block = BasicDecode.subst(iop) +}}; + diff --git a/src/arch/sparc/isa/formats/integerop.isa b/src/arch/sparc/isa/formats/integerop.isa new file mode 100644 index 000000000..83c7e6958 --- /dev/null +++ b/src/arch/sparc/isa/formats/integerop.isa @@ -0,0 +1,350 @@ +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Ali Saidi +// Gabe Black +// Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Integer operate instructions +// + +output header {{ + /** + * Base class for integer operations. + */ + class IntOp : public SparcStaticInst + { + protected: + // Constructor + IntOp(const char *mnem, ExtMachInst _machInst, + OpClass __opClass) : + SparcStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + + virtual bool printPseudoOps(std::ostream &os, Addr pc, + const SymbolTable *symtab) const; + }; + + /** + * Base class for immediate integer operations. + */ + class IntOpImm : public IntOp + { + protected: + // Constructor + IntOpImm(const char *mnem, ExtMachInst _machInst, + OpClass __opClass) : + IntOp(mnem, _machInst, __opClass) + { + } + + int64_t imm; + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + + virtual bool printPseudoOps(std::ostream &os, Addr pc, + const SymbolTable *symtab) const; + }; + + /** + * Base class for 10 bit immediate integer operations. + */ + class IntOpImm10 : public IntOpImm + { + protected: + // Constructor + IntOpImm10(const char *mnem, ExtMachInst _machInst, + OpClass __opClass) : + IntOpImm(mnem, _machInst, __opClass) + { + imm = sign_ext(SIMM10, 10); + } + }; + + /** + * Base class for 11 bit immediate integer operations. + */ + class IntOpImm11 : public IntOpImm + { + protected: + // Constructor + IntOpImm11(const char *mnem, ExtMachInst _machInst, + OpClass __opClass) : + IntOpImm(mnem, _machInst, __opClass) + { + imm = sign_ext(SIMM11, 11); + } + }; + + /** + * Base class for 13 bit immediate integer operations. + */ + class IntOpImm13 : public IntOpImm + { + protected: + // Constructor + IntOpImm13(const char *mnem, ExtMachInst _machInst, + OpClass __opClass) : + IntOpImm(mnem, _machInst, __opClass) + { + imm = sign_ext(SIMM13, 13); + } + }; + + /** + * Base class for sethi. + */ + class SetHi : public IntOpImm + { + protected: + // Constructor + SetHi(const char *mnem, ExtMachInst _machInst, + OpClass __opClass) : + IntOpImm(mnem, _machInst, __opClass) + { + imm = (IMM22 & 0x3FFFFF) << 10; + } + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + }; +}}; + +def template SetHiDecode {{ + { + if(RD == 0 && IMM22 == 0) + return (SparcStaticInst *)(new Nop("nop", machInst, No_OpClass)); + else + return (SparcStaticInst *)(new %(class_name)s(machInst)); + } +}}; + +output decoder {{ + + bool IntOp::printPseudoOps(std::ostream &os, Addr pc, + const SymbolTable *symbab) const + { + if(!strcmp(mnemonic, "or") && _srcRegIdx[0] == 0) + { + printMnemonic(os, "mov"); + printSrcReg(os, 1); + ccprintf(os, ", "); + printDestReg(os, 0); + return true; + } + return false; + } + + bool IntOpImm::printPseudoOps(std::ostream &os, Addr pc, + const SymbolTable *symbab) const + { + if(!strcmp(mnemonic, "or")) + { + if(_numSrcRegs > 0 && _srcRegIdx[0] == 0) + { + if(imm == 0) + printMnemonic(os, "clr"); + else + { + printMnemonic(os, "mov"); + ccprintf(os, " 0x%x, ", imm); + } + printDestReg(os, 0); + return true; + } + else if(imm == 0) + { + printMnemonic(os, "mov"); + printSrcReg(os, 0); + ccprintf(os, ", "); + printDestReg(os, 0); + return true; + } + } + return false; + } + + std::string IntOp::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream response; + + if(printPseudoOps(response, pc, symtab)) + return response.str(); + printMnemonic(response, mnemonic); + printRegArray(response, _srcRegIdx, _numSrcRegs); + if(_numDestRegs && _numSrcRegs) + response << ", "; + printDestReg(response, 0); + return response.str(); + } + + std::string IntOpImm::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream response; + + if(printPseudoOps(response, pc, symtab)) + return response.str(); + printMnemonic(response, mnemonic); + printRegArray(response, _srcRegIdx, _numSrcRegs); + if(_numSrcRegs > 0) + response << ", "; + ccprintf(response, "0x%x", imm); + if(_numDestRegs > 0) + response << ", "; + printDestReg(response, 0); + return response.str(); + } + + std::string SetHi::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream response; + + printMnemonic(response, mnemonic); + ccprintf(response, "%%hi(0x%x), ", imm); + printDestReg(response, 0); + return response.str(); + } +}}; + +def template IntOpExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + + %(op_decl)s; + %(op_rd)s; + %(code)s; + + //Write the resulting state to the execution context + if(fault == NoFault) + { + %(cc_code)s; + %(op_wb)s; + } + return fault; + } +}}; + +let {{ + def doIntFormat(code, ccCode, name, Name, opt_flags): + (usesImm, code, immCode, + rString, iString) = splitOutImm(code) + iop = InstObjParams(name, Name, 'IntOp', code, + opt_flags, ("cc_code", ccCode)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + exec_output = IntOpExecute.subst(iop) + if usesImm: + imm_iop = InstObjParams(name, Name + 'Imm', 'IntOpImm' + iString, + immCode, opt_flags, ("cc_code", ccCode)) + header_output += BasicDeclare.subst(imm_iop) + decoder_output += BasicConstructor.subst(imm_iop) + exec_output += IntOpExecute.subst(imm_iop) + decode_block = ROrImmDecode.subst(iop) + else: + decode_block = BasicDecode.subst(iop) + return (header_output, decoder_output, exec_output, decode_block) + + calcCcCode = ''' + uint16_t _ic, _iv, _iz, _in, _xc, _xv, _xz, _xn; + + _in = (Rd >> 31) & 1; + _iz = ((Rd & 0xFFFFFFFF) == 0); + _xn = (Rd >> 63) & 1; + _xz = (Rd == 0); + _iv = %(ivValue)s & 1; + _ic = %(icValue)s & 1; + _xv = %(xvValue)s & 1; + _xc = %(xcValue)s & 1; + + Ccr = _ic << 0 | _iv << 1 | _iz << 2 | _in << 3 | + _xc << 4 | _xv << 5 | _xz << 6 | _xn << 7; + + + DPRINTF(Sparc, "in = %%d\\n", _in); + DPRINTF(Sparc, "iz = %%d\\n", _iz); + DPRINTF(Sparc, "xn = %%d\\n", _xn); + DPRINTF(Sparc, "xz = %%d\\n", _xz); + DPRINTF(Sparc, "iv = %%d\\n", _iv); + DPRINTF(Sparc, "ic = %%d\\n", _ic); + DPRINTF(Sparc, "xv = %%d\\n", _xv); + DPRINTF(Sparc, "xc = %%d\\n", _xc); + ''' +}}; + +// Primary format for integer operate instructions: +def format IntOp(code, *opt_flags) {{ + ccCode = '' + (header_output, + decoder_output, + exec_output, + decode_block) = doIntFormat(code, ccCode, + name, Name, opt_flags) +}}; + +// Primary format for integer operate instructions: +def format IntOpCc(code, icValue, ivValue, xcValue, xvValue, *opt_flags) {{ + ccCode = calcCcCode % vars() + (header_output, + decoder_output, + exec_output, + decode_block) = doIntFormat(code, ccCode, + name, Name, opt_flags) +}}; + +// Primary format for integer operate instructions: +def format IntOpCcRes(code, *opt_flags) {{ + ccCode = calcCcCode % {"icValue":"0", + "ivValue":"0", + "xcValue":"0", + "xvValue":"0"} + (header_output, + decoder_output, + exec_output, + decode_block) = doIntFormat(code, ccCode, + name, Name, opt_flags) +}}; + +def format SetHi(code, *opt_flags) {{ + iop = InstObjParams(name, Name, 'SetHi', + code, opt_flags, ("cc_code", '')) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + exec_output = IntOpExecute.subst(iop) + decode_block = SetHiDecode.subst(iop) +}}; + diff --git a/src/arch/sparc/isa/formats/mem.isa b/src/arch/sparc/isa/formats/mem.isa new file mode 100644 index 000000000..9011c1fc6 --- /dev/null +++ b/src/arch/sparc/isa/formats/mem.isa @@ -0,0 +1,171 @@ +//////////////////////////////////////////////////////////////////// +// +// Mem instructions +// + +output header {{ + /** + * Base class for memory operations. + */ + class Mem : public SparcStaticInst + { + protected: + + // Constructor + Mem(const char *mnem, ExtMachInst _machInst, OpClass __opClass) : + SparcStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + }; + + /** + * Class for memory operations which use an immediate offset. + */ + class MemImm : public Mem + { + protected: + + // Constructor + MemImm(const char *mnem, ExtMachInst _machInst, OpClass __opClass) : + Mem(mnem, _machInst, __opClass) + { + imm = sign_ext(SIMM13, 13); + } + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + + int32_t imm; + }; +}}; + +output decoder {{ + std::string Mem::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream response; + bool load = flags[IsLoad]; + bool save = flags[IsStore]; + + printMnemonic(response, mnemonic); + if(save) + { + printReg(response, _srcRegIdx[0]); + ccprintf(response, ", "); + } + ccprintf(response, "[ "); + printReg(response, _srcRegIdx[!save ? 0 : 1]); + ccprintf(response, " + "); + printReg(response, _srcRegIdx[!save ? 1 : 2]); + ccprintf(response, " ]"); + if(load) + { + ccprintf(response, ", "); + printReg(response, _destRegIdx[0]); + } + + return response.str(); + } + + std::string MemImm::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream response; + bool load = flags[IsLoad]; + bool save = flags[IsStore]; + + printMnemonic(response, mnemonic); + if(save) + { + printReg(response, _srcRegIdx[0]); + ccprintf(response, ", "); + } + ccprintf(response, "[ "); + printReg(response, _srcRegIdx[!save ? 0 : 1]); + if(imm >= 0) + ccprintf(response, " + 0x%x ]", imm); + else + ccprintf(response, " + -0x%x ]", -imm); + if(load) + { + ccprintf(response, ", "); + printReg(response, _destRegIdx[0]); + } + + return response.str(); + } +}}; + +def template MemExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + Addr EA; + %(op_decl)s; + %(op_rd)s; + %(ea_code)s; + DPRINTF(Sparc, "The address is 0x%x\n", EA); + %(load)s; + %(code)s; + + if(fault == NoFault) + { + %(store)s; + //Write the resulting state to the execution context + %(op_wb)s; + } + + return fault; + } +}}; + +let {{ + # Leave memAccessFlags at 0 for now + loadString = "xc->read(EA, (uint%(width)s_t&)Mem, 0);" + storeString = "uint64_t write_result = 0; \ + xc->write((uint%(width)s_t)Mem, EA, 0, &write_result);" + + def doMemFormat(code, load, store, name, Name, opt_flags): + addrCalcReg = 'EA = Rs1 + Rs2;' + addrCalcImm = 'EA = Rs1 + imm;' + iop = InstObjParams(name, Name, 'Mem', code, + opt_flags, ("ea_code", addrCalcReg), + ("load", load), ("store", store)) + iop_imm = InstObjParams(name, Name + 'Imm', 'MemImm', code, + opt_flags, ("ea_code", addrCalcImm), + ("load", load), ("store", store)) + header_output = BasicDeclare.subst(iop) + BasicDeclare.subst(iop_imm) + decoder_output = BasicConstructor.subst(iop) + BasicConstructor.subst(iop_imm) + decode_block = ROrImmDecode.subst(iop) + exec_output = MemExecute.subst(iop) + MemExecute.subst(iop_imm) + return (header_output, decoder_output, exec_output, decode_block) +}}; + +def format Load(code, width, *opt_flags) {{ + (header_output, + decoder_output, + exec_output, + decode_block) = doMemFormat(code, + loadString % {"width":width}, '', name, Name, opt_flags) +}}; + +def format Store(code, width, *opt_flags) {{ + (header_output, + decoder_output, + exec_output, + decode_block) = doMemFormat(code, '', + storeString % {"width":width}, name, Name, opt_flags) +}}; + +def format LoadStore(code, width, *opt_flags) {{ + (header_output, + decoder_output, + exec_output, + decode_block) = doMemFormat(code, + loadString % {"width":width}, storeString % {"width":width}, + name, Name, opt_flags) +}}; diff --git a/src/arch/sparc/isa/formats/nop.isa b/src/arch/sparc/isa/formats/nop.isa new file mode 100644 index 000000000..37ef2e8d0 --- /dev/null +++ b/src/arch/sparc/isa/formats/nop.isa @@ -0,0 +1,98 @@ +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Gabe Black +// Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Nop instruction +// + +// Per-cpu-model nop execute method. +def template NopExec {{ + + Fault execute(%(CPU_exec_context)s *xc, Trace::InstRecord *traceData) const + { + // Nothing to see here, move along + return NoFault; + } +}}; + +output header {{ + /** + * Nop class. + */ + class Nop : public SparcStaticInst + { + public: + // Constructor + Nop(const char *mnem, ExtMachInst _machInst, OpClass __opClass) : + SparcStaticInst(mnem, _machInst, __opClass) + { + } + + // All Nop instructions do the same thing, so this can be + // defined here. Nops can be defined directly, so there + // needs to be a default implementation. Interpolate via + // template so i gets expanded to a set of + // cpu-model-specific functions. + %(NopExec)s + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + }; +}}; + +output decoder {{ + std::string Nop::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream response; + printMnemonic(response, mnemonic); + return response.str(); + } +}}; + +def template NopExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + //Nothing to see here, move along + return NoFault; + } +}}; + +// Primary format for integer operate instructions: +def format Nop(code, *opt_flags) {{ + orig_code = code + cblk = CodeBlock(code) + iop = InstObjParams(name, Name, 'Nop', cblk, opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = NopExecute.subst(iop) +}}; diff --git a/src/arch/sparc/isa/formats/priv.isa b/src/arch/sparc/isa/formats/priv.isa new file mode 100644 index 000000000..d7ee01519 --- /dev/null +++ b/src/arch/sparc/isa/formats/priv.isa @@ -0,0 +1,137 @@ +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Ali Saidi +// Gabe Black +// Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Privilege mode instructions +// + +output header {{ + /** + * Base class for privelege mode operations. + */ + class Priv : public SparcStaticInst + { + protected: + // Constructor + Priv(const char *mnem, ExtMachInst _machInst, OpClass __opClass) : + SparcStaticInst(mnem, _machInst, __opClass) + { + } + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + }; + + /** + * Base class for privelege mode operations with immediates. + */ + class PrivImm : public Priv + { + protected: + // Constructor + PrivImm(const char *mnem, ExtMachInst _machInst, + OpClass __opClass) : + Priv(mnem, _machInst, __opClass), imm(SIMM13) + { + } + + int32_t imm; + }; + +}}; + +output decoder {{ + std::string Priv::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream response; + + printMnemonic(response, mnemonic); + + return response.str(); + } +}}; + +def template PrivExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + %(op_decl)s; + %(op_rd)s; + + //If the processor isn't in privileged mode, fault out right away + if(%(check)s) + return new PrivilegedAction; + + Fault fault = NoFault; + %(code)s; + %(op_wb)s; + return fault; + } +}}; + +let {{ + def doPrivFormat(code, checkCode, name, Name, opt_flags): + (usesImm, code, immCode, + rString, iString) = splitOutImm(code) + iop = InstObjParams(name, Name, 'Priv', code, + opt_flags, ("check", checkCode)) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + exec_output = PrivExecute.subst(iop) + if usesImm: + imm_iop = InstObjParams(name, Name + 'Imm', 'PrivImm', + immCode, opt_flags, ("check", checkCode)) + header_output += BasicDeclare.subst(imm_iop) + decoder_output += BasicConstructor.subst(imm_iop) + exec_output += PrivExecute.subst(imm_iop) + decode_block = ROrImmDecode.subst(iop) + else: + decode_block = BasicDecode.subst(iop) + return (header_output, decoder_output, exec_output, decode_block) +}}; + +// Primary format for integer operate instructions: +def format Priv(code, *opt_flags) {{ + checkCode = '''((xc->readMiscReg(PrStart + MISCREG_PSTATE))<2:2>) || + ((xc->readMiscReg(HprStart + MISCREG_HPSTATE))<2:2>)''' + (header_output, decoder_output, + exec_output, decode_block) = doPrivFormat(code, + checkCode, name, Name, opt_flags + ('IprAccessOp',)) +}}; + +def format HPriv(code, *opt_flags) {{ + checkCode = "((xc->readMiscReg(HprStart + MISCREG_HPSTATE))<2:2>)" + (header_output, decoder_output, + exec_output, decode_block) = doPrivFormat(code, + checkCode, name, Name, opt_flags + ('IprAccessOp',)) +}}; + diff --git a/src/arch/sparc/isa/formats/trap.isa b/src/arch/sparc/isa/formats/trap.isa new file mode 100644 index 000000000..04d467cfe --- /dev/null +++ b/src/arch/sparc/isa/formats/trap.isa @@ -0,0 +1,93 @@ +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Gabe Black +// Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Trap instructions +// + +output header {{ + /** + * Base class for trap instructions, + * or instructions that always fault. + */ + class Trap : public SparcStaticInst + { + protected: + + // Constructor + Trap(const char *mnem, ExtMachInst _machInst, OpClass __opClass) : + SparcStaticInst(mnem, _machInst, __opClass), trapNum(SW_TRAP) + { + } + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + + int trapNum; + }; +}}; + +output decoder {{ + std::string Trap::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + std::stringstream response; + + printMnemonic(response, mnemonic); + ccprintf(response, " "); + printReg(response, _srcRegIdx[0]); + ccprintf(response, ", 0x%x", trapNum); + ccprintf(response, ", or "); + printReg(response, _srcRegIdx[1]); + return response.str(); + } +}}; + +def template TrapExecute {{ + Fault %(class_name)s::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + Fault fault = NoFault; + %(op_decl)s; + %(op_rd)s; + %(code)s + return fault; + } +}}; + +def format Trap(code, *opt_flags) {{ + orig_code = code + cblk = CodeBlock(code) + iop = InstObjParams(name, Name, 'Trap', cblk, opt_flags) + header_output = BasicDeclare.subst(iop) + decoder_output = BasicConstructor.subst(iop) + decode_block = BasicDecode.subst(iop) + exec_output = TrapExecute.subst(iop) +}}; diff --git a/src/arch/sparc/isa/formats/unknown.isa b/src/arch/sparc/isa/formats/unknown.isa new file mode 100644 index 000000000..8541d6a62 --- /dev/null +++ b/src/arch/sparc/isa/formats/unknown.isa @@ -0,0 +1,75 @@ +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Gabe Black +// Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Unknown instructions +// + +output header {{ + /** + * Class for Unknown/Illegal instructions + */ + class Unknown : public SparcStaticInst + { + public: + + // Constructor + Unknown(ExtMachInst _machInst) : + SparcStaticInst("unknown", _machInst, No_OpClass) + { + } + + %(BasicExecDeclare)s + + std::string generateDisassembly(Addr pc, + const SymbolTable *symtab) const; + + }; +}}; + +output decoder {{ + std::string Unknown::generateDisassembly(Addr pc, + const SymbolTable *symtab) const + { + return "Unknown instruction"; + } +}}; + +output exec {{ + Fault Unknown::execute(%(CPU_exec_context)s *xc, + Trace::InstRecord *traceData) const + { + return new IllegalInstruction; + } +}}; + +def format Unknown() {{ + decode_block = 'return new Unknown(machInst);\n' +}}; diff --git a/src/arch/sparc/isa/includes.isa b/src/arch/sparc/isa/includes.isa new file mode 100644 index 000000000..f1c2bee96 --- /dev/null +++ b/src/arch/sparc/isa/includes.isa @@ -0,0 +1,71 @@ +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Ali Saidi +// Gabe Black +// Steve Reinhardt + +//////////////////////////////////////////////////////////////////// +// +// Output include file directives. +// + +output header {{ +#include <sstream> +#include <iostream> + +#include "cpu/static_inst.hh" +#include "arch/sparc/faults.hh" +#include "mem/request.hh" // some constructors use MemReq flags +#include "arch/sparc/isa_traits.hh" +#include "arch/sparc/regfile.hh" +}}; + +output decoder {{ +#include "base/cprintf.hh" +#include "base/loader/symtab.hh" +#include "cpu/thread_context.hh" // for Jump::branchTarget() + +#if defined(linux) +#include <fenv.h> +#endif + +using namespace SparcISA; +}}; + +output exec {{ +#if defined(linux) +#include <fenv.h> +#endif + +#include "arch/sparc/asi.hh" +#include "cpu/base.hh" +#include "cpu/exetrace.hh" +#include "sim/sim_exit.hh" + +using namespace SparcISA; +}}; + diff --git a/src/arch/sparc/isa/main.isa b/src/arch/sparc/isa/main.isa new file mode 100644 index 000000000..14acf54fa --- /dev/null +++ b/src/arch/sparc/isa/main.isa @@ -0,0 +1,61 @@ +// -*- mode:c++ -*- + +// Copyright (c) 2003-2005 The Regents of The University of Michigan +// 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. +// +// Authors: Korey Sewell + +//////////////////////////////////////////////////////////////////// +// +// SPARC ISA description file. +// +//////////////////////////////////////////////////////////////////// + +//Include the C++ include directives +##include "includes.isa" + +//////////////////////////////////////////////////////////////////// +// +// Namespace statement. Everything below this line will be in the +// SparcISAInst namespace. +// + +namespace SparcISA; + +//Include the bitfield definitions +##include "bitfields.isa" + +//Include the operand_types and operand definitions +##include "operands.isa" + +//Include the base class for sparc instructions, and some support code +##include "base.isa" + +//Include the definitions for the instruction formats +##include "formats.isa" + +//Include the decoder definition +##include "decoder.isa" diff --git a/src/arch/sparc/isa/operands.isa b/src/arch/sparc/isa/operands.isa new file mode 100644 index 000000000..605816083 --- /dev/null +++ b/src/arch/sparc/isa/operands.isa @@ -0,0 +1,89 @@ +// Copyright (c) 2006 The Regents of The University of Michigan +// 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. +// +// Authors: Ali Saidi +// Gabe Black +// Steve Reinhardt + +def operand_types {{ + 'sb' : ('signed int', 8), + 'ub' : ('unsigned int', 8), + 'shw' : ('signed int', 16), + 'uhw' : ('unsigned int', 16), + 'sw' : ('signed int', 32), + 'uw' : ('unsigned int', 32), + 'sdw' : ('signed int', 64), + 'udw' : ('unsigned int', 64), + 'sf' : ('float', 32), + 'df' : ('float', 64), + 'qf' : ('float', 128) +}}; + +def operands {{ + # Int regs default to unsigned, but code should not count on this. + # For clarity, descriptions that depend on unsigned behavior should + # explicitly specify '.uq'. + 'Rd': ('IntReg', 'udw', 'RD', 'IsInteger', 1), + 'RdLow': ('IntReg', 'udw', 'RD & (~1)', 'IsInteger', 2), + 'RdHigh': ('IntReg', 'udw', 'RD | 1', 'IsInteger', 3), + 'Rs1': ('IntReg', 'udw', 'RS1', 'IsInteger', 4), + 'Rs2': ('IntReg', 'udw', 'RS2', 'IsInteger', 5), + 'Frd': ('FloatReg', 'df', 'RD', 'IsFloating', 10), + 'Frs1': ('FloatReg', 'df', 'RS1', 'IsFloating', 11), + 'Frs2': ('FloatReg', 'df', 'RS2', 'IsFloating', 12), + 'Mem': ('Mem', 'udw', None, ('IsMemRef', 'IsLoad', 'IsStore'), 20), + 'NPC': ('NPC', 'udw', None, ( None, None, 'IsControl' ), 31), + 'NNPC': ('NNPC', 'udw', None, (None, None, 'IsControl' ), 32), + #'Runiq': ('ControlReg', 'uq', 'Uniq', None, 1), + #'FPCR': ('ControlReg', 'uq', 'Fpcr', None, 1), + 'R0': ('IntReg', 'udw', '0', None, 6), + 'R1': ('IntReg', 'udw', '1', None, 7), + 'R15': ('IntReg', 'udw', '15', 'IsInteger', 8), + 'R16': ('IntReg', 'udw', '16', None, 9), + + # Control registers + 'Y': ('ControlReg', 'udw', 'MISCREG_Y', None, 40), + 'Ccr': ('ControlReg', 'udw', 'MISCREG_CCR', None, 41), + 'Asi': ('ControlReg', 'udw', 'MISCREG_ASI', None, 42), + + 'Tpc': ('ControlReg', 'udw', 'MISCREG_TPC', None, 43), + 'Tnpc': ('ControlReg', 'udw', 'MISCREG_TNPC', None, 44), + 'Tstate': ('ControlReg', 'udw', 'MISCREG_TSTATE', None, 45), + 'Pstate': ('ControlReg', 'udw', 'MISCREG_PSTATE', None, 46), + 'Tl': ('ControlReg', 'udw', 'MISCREG_TL', None, 47), + + 'Cwp': ('ControlReg', 'udw', 'MISCREG_CWP', None, 48), + 'Cansave': ('ControlReg', 'udw', 'MISCREG_CANSAVE', None, 49), + 'Canrestore': ('ControlReg', 'udw', 'MISCREG_CANRESTORE', None, 50), + 'Cleanwin': ('ControlReg', 'udw', 'MISCREG_CLEANWIN', None, 51), + 'Otherwin': ('ControlReg', 'udw', 'MISCREG_OTHERWIN', None, 52), + 'Wstate': ('ControlReg', 'udw', 'MISCREG_WSTATE', None, 53), + 'Gl': ('ControlReg', 'udw', 'MISCREG_GL', None, 54), + + 'Fsr': ('ControlReg', 'udw', 'MISCREG_FSR', None, 55), + 'Gsr': ('ControlReg', 'udw', 'MISCREG_GSR', None, 56) + +}}; diff --git a/src/arch/sparc/isa_traits.hh b/src/arch/sparc/isa_traits.hh new file mode 100644 index 000000000..6d5aa4251 --- /dev/null +++ b/src/arch/sparc/isa_traits.hh @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + */ + +#ifndef __ARCH_SPARC_ISA_TRAITS_HH__ +#define __ARCH_SPARC_ISA_TRAITS_HH__ + +#include "arch/sparc/types.hh" +#include "base/misc.hh" +#include "config/full_system.hh" +#include "sim/host.hh" + +class ThreadContext; +class FastCPU; +//class FullCPU; +class Checkpoint; + +class StaticInst; +class StaticInstPtr; + +namespace BigEndianGuest {} + +#if FULL_SYSTEM +#include "arch/sparc/isa_fullsys_traits.hh" +#endif + +namespace SparcISA +{ + class RegFile; + + //This makes sure the big endian versions of certain functions are used. + using namespace BigEndianGuest; + + // Alpha Does NOT have a delay slot + #define ISA_HAS_DELAY_SLOT 1 + + //TODO this needs to be a SPARC Noop + // Alpha UNOP (ldq_u r31,0(r0)) + const MachInst NoopMachInst = 0x2ffe0000; + + const int NumIntRegs = 32; + const int NumFloatRegs = 64; + const int NumMiscRegs = 40; + + // These enumerate all the registers for dependence tracking. + enum DependenceTags { + // 0..31 are the integer regs 0..31 + // 32..95 are the FP regs 0..31, i.e. use (reg + FP_Base_DepTag) + FP_Base_DepTag = NumIntRegs, + Ctrl_Base_DepTag = NumIntRegs + NumFloatRegs, + //XXX These are here solely to get compilation and won't work + Fpcr_DepTag = 0, + Uniq_DepTag = 0 + }; + + + // MAXTL - maximum trap level + const int MaxPTL = 2; + const int MaxTL = 6; + const int MaxGL = 3; + const int MaxPGL = 2; + + // NWINDOWS - number of register windows, can be 3 to 32 + const int NWindows = 32; + + // semantically meaningful register indices + const int ZeroReg = 0; // architecturally meaningful + // the rest of these depend on the ABI + const int StackPointerReg = 14; + const int ReturnAddressReg = 31; // post call, precall is 15 + const int ReturnValueReg = 8; // Post return, 24 is pre-return. + const int FramePointerReg = 30; + const int ArgumentReg0 = 8; + const int ArgumentReg1 = 9; + const int ArgumentReg2 = 10; + const int ArgumentReg3 = 11; + const int ArgumentReg4 = 12; + const int ArgumentReg5 = 13; + // Some OS syscall use a second register (o1) to return a second value + const int SyscallPseudoReturnReg = ArgumentReg1; + + //XXX These numbers are bogus + const int MaxInstSrcRegs = 8; + const int MaxInstDestRegs = 9; + + //8K. This value is implmentation specific; and should probably + //be somewhere else. + const int LogVMPageSize = 13; + const int VMPageSize = (1 << LogVMPageSize); + + //Why does both the previous set of constants and this one exist? + const int PageShift = 13; + const int PageBytes = ULL(1) << PageShift; + + const int BranchPredAddrShiftAmt = 2; + + const int MachineBytes = 8; + const int WordBytes = 4; + const int HalfwordBytes = 2; + const int ByteBytes = 1; + + void serialize(std::ostream & os); + + void unserialize(Checkpoint *cp, const std::string §ion); + + StaticInstPtr decodeInst(ExtMachInst); + + // return a no-op instruction... used for instruction fetch faults + extern const MachInst NoopMachInst; +} + +#endif // __ARCH_SPARC_ISA_TRAITS_HH__ diff --git a/src/arch/sparc/linux/linux.cc b/src/arch/sparc/linux/linux.cc new file mode 100644 index 000000000..1211d5f65 --- /dev/null +++ b/src/arch/sparc/linux/linux.cc @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + */ + +#include "arch/sparc/linux/linux.hh" +#include <fcntl.h> + +// open(2) flags translation table +OpenFlagTransTable SparcLinux::openFlagTable[] = { +#ifdef _MSC_VER + { SparcLinux::TGT_O_RDONLY, _O_RDONLY }, + { SparcLinux::TGT_O_WRONLY, _O_WRONLY }, + { SparcLinux::TGT_O_RDWR, _O_RDWR }, + { SparcLinux::TGT_O_APPEND, _O_APPEND }, + { SparcLinux::TGT_O_CREAT, _O_CREAT }, + { SparcLinux::TGT_O_TRUNC, _O_TRUNC }, + { SparcLinux::TGT_O_EXCL, _O_EXCL }, +#ifdef _O_NONBLOCK + { SparcLinux::TGT_O_NONBLOCK, _O_NONBLOCK }, +#endif +#ifdef _O_NOCTTY + { SparcLinux::TGT_O_NOCTTY, _O_NOCTTY }, +#endif +#ifdef _O_SYNC + { SparcLinux::TGT_O_SYNC, _O_SYNC }, +#endif +#else /* !_MSC_VER */ + { SparcLinux::TGT_O_RDONLY, O_RDONLY }, + { SparcLinux::TGT_O_WRONLY, O_WRONLY }, + { SparcLinux::TGT_O_RDWR, O_RDWR }, + { SparcLinux::TGT_O_APPEND, O_APPEND }, + { SparcLinux::TGT_O_CREAT, O_CREAT }, + { SparcLinux::TGT_O_TRUNC, O_TRUNC }, + { SparcLinux::TGT_O_EXCL, O_EXCL }, + { SparcLinux::TGT_O_NONBLOCK, O_NONBLOCK }, + { SparcLinux::TGT_O_NOCTTY, O_NOCTTY }, +#ifdef O_SYNC + { SparcLinux::TGT_O_SYNC, O_SYNC }, +#endif +#endif /* _MSC_VER */ +}; + +const int SparcLinux::NUM_OPEN_FLAGS = + (sizeof(SparcLinux::openFlagTable)/sizeof(SparcLinux::openFlagTable[0])); + diff --git a/src/arch/sparc/linux/linux.hh b/src/arch/sparc/linux/linux.hh new file mode 100644 index 000000000..fefd27197 --- /dev/null +++ b/src/arch/sparc/linux/linux.hh @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + */ + +#ifndef __ARCH_SPARC_LINUX_LINUX_HH__ +#define __ARCH_SPARC_LINUX_LINUX_HH__ + +#include "kern/linux/linux.hh" + +class SparcLinux : public Linux +{ + public: + + typedef struct { + uint32_t st_dev; + char __pad1[4]; + uint64_t st_ino; + uint32_t st_mode; + uint16_t st_nlink; + uint32_t st_uid; + uint32_t st_gid; + uint32_t st_rdev; + char __pad2[4]; + int64_t st_size; + int64_t st_atimeX; + int64_t st_mtimeX; + int64_t st_ctimeX; + int64_t st_blksize; + int64_t st_blocks; + uint64_t __unused4[2]; + } tgt_stat; + + static OpenFlagTransTable openFlagTable[]; + + static const int TGT_O_RDONLY = 0x00000000; //!< O_RDONLY + static const int TGT_O_WRONLY = 0x00000001; //!< O_WRONLY + static const int TGT_O_RDWR = 0x00000002; //!< O_RDWR + static const int TGT_O_NONBLOCK = 0x00004000; //!< O_NONBLOCK + static const int TGT_O_APPEND = 0x00000008; //!< O_APPEND + static const int TGT_O_CREAT = 0x00000200; //!< O_CREAT + static const int TGT_O_TRUNC = 0x00000400; //!< O_TRUNC + static const int TGT_O_EXCL = 0x00000800; //!< O_EXCL + static const int TGT_O_NOCTTY = 0x00008000; //!< O_NOCTTY + static const int TGT_O_SYNC = 0x00002000; //!< O_SYNC +// static const int TGT_O_DRD = 0x00010000; //!< O_DRD +// static const int TGT_O_DIRECTIO = 0x00020000; //!< O_DIRECTIO +// static const int TGT_O_CACHE = 0x00002000; //!< O_CACHE +// static const int TGT_O_DSYNC = 0x00008000; //!< O_DSYNC +// static const int TGT_O_RSYNC = 0x00040000; //!< O_RSYNC + + static const int NUM_OPEN_FLAGS; + + static const unsigned TGT_MAP_ANONYMOUS = 0x20; +}; + +#endif diff --git a/src/arch/sparc/linux/process.cc b/src/arch/sparc/linux/process.cc new file mode 100644 index 000000000..44c893f2b --- /dev/null +++ b/src/arch/sparc/linux/process.cc @@ -0,0 +1,415 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Gabe Black + * Ali Saidi + */ + +#include "arch/sparc/isa_traits.hh" +#include "arch/sparc/linux/process.hh" +#include "arch/sparc/regfile.hh" + +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "kern/linux/linux.hh" + +#include "sim/process.hh" +#include "sim/syscall_emul.hh" + +using namespace std; +using namespace SparcISA; + + +/// Target uname() handler. +static SyscallReturn +unameFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + TypedBufferArg<Linux::utsname> name(tc->getSyscallArg(0)); + + strcpy(name->sysname, "Linux"); + strcpy(name->nodename, "m5.eecs.umich.edu"); + strcpy(name->release, "2.4.20"); + strcpy(name->version, "#1 Mon Aug 18 11:32:15 EDT 2003"); + strcpy(name->machine, "sparc"); + + name.copyOut(tc->getMemPort()); + + return 0; +} + + +SyscallReturn SparcISA::getresuidFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc) +{ + const IntReg id = htog(100); + Addr ruid = tc->getSyscallArg(0); + Addr euid = tc->getSyscallArg(1); + Addr suid = tc->getSyscallArg(2); + //Handle the EFAULT case + //Set the ruid + if(ruid) + { + BufferArg ruidBuff(ruid, sizeof(IntReg)); + memcpy(ruidBuff.bufferPtr(), &id, sizeof(IntReg)); + ruidBuff.copyOut(tc->getMemPort()); + } + //Set the euid + if(euid) + { + BufferArg euidBuff(euid, sizeof(IntReg)); + memcpy(euidBuff.bufferPtr(), &id, sizeof(IntReg)); + euidBuff.copyOut(tc->getMemPort()); + } + //Set the suid + if(suid) + { + BufferArg suidBuff(suid, sizeof(IntReg)); + memcpy(suidBuff.bufferPtr(), &id, sizeof(IntReg)); + suidBuff.copyOut(tc->getMemPort()); + } + return 0; +} + +SyscallDesc SparcLinuxProcess::syscallDescs[] = { + /* 0 */ SyscallDesc("restart_syscall", unimplementedFunc), + /* 1 */ SyscallDesc("exit", exitFunc), + /* 2 */ SyscallDesc("fork", unimplementedFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), + /* 5 */ SyscallDesc("open", openFunc<SparcLinux>), + /* 6 */ SyscallDesc("close", closeFunc), + /* 7 */ SyscallDesc("wait4", unimplementedFunc), + /* 8 */ SyscallDesc("creat", unimplementedFunc), + /* 9 */ SyscallDesc("link", unimplementedFunc), + /* 10 */ SyscallDesc("unlink", unlinkFunc), + /* 11 */ SyscallDesc("execv", unimplementedFunc), + /* 12 */ SyscallDesc("chdir", unimplementedFunc), + /* 13 */ SyscallDesc("chown", chownFunc), + /* 14 */ SyscallDesc("mknod", unimplementedFunc), + /* 15 */ SyscallDesc("chmod", chmodFunc<Linux>), + /* 16 */ SyscallDesc("lchown", unimplementedFunc), + /* 17 */ SyscallDesc("brk", obreakFunc), + /* 18 */ SyscallDesc("perfctr", unimplementedFunc), + /* 19 */ SyscallDesc("lseek", lseekFunc), + /* 20 */ SyscallDesc("getpid", getpidFunc), + /* 21 */ SyscallDesc("capget", unimplementedFunc), + /* 22 */ SyscallDesc("capset", unimplementedFunc), + /* 23 */ SyscallDesc("setuid", setuidFunc), + /* 24 */ SyscallDesc("getuid", getuidFunc), + /* 25 */ SyscallDesc("time", unimplementedFunc), + /* 26 */ SyscallDesc("ptrace", unimplementedFunc), + /* 27 */ SyscallDesc("alarm", unimplementedFunc), + /* 28 */ SyscallDesc("sigaltstack", unimplementedFunc), + /* 29 */ SyscallDesc("pause", unimplementedFunc), + /* 30 */ SyscallDesc("utime", unimplementedFunc), + /* 31 */ SyscallDesc("lchown32", unimplementedFunc), + /* 32 */ SyscallDesc("fchown32", unimplementedFunc), + /* 33 */ SyscallDesc("access", unimplementedFunc), + /* 34 */ SyscallDesc("nice", unimplementedFunc), + /* 35 */ SyscallDesc("chown32", unimplementedFunc), + /* 36 */ SyscallDesc("sync", unimplementedFunc), + /* 37 */ SyscallDesc("kill", unimplementedFunc), + /* 38 */ SyscallDesc("stat", unimplementedFunc), + /* 39 */ SyscallDesc("sendfile", unimplementedFunc), + /* 40 */ SyscallDesc("lstat", unimplementedFunc), + /* 41 */ SyscallDesc("dup", unimplementedFunc), + /* 42 */ SyscallDesc("pipe", pipePseudoFunc), + /* 43 */ SyscallDesc("times", unimplementedFunc), + /* 44 */ SyscallDesc("getuid32", unimplementedFunc), + /* 45 */ SyscallDesc("umount2", unimplementedFunc), + /* 46 */ SyscallDesc("setgid", unimplementedFunc), + /* 47 */ SyscallDesc("getgid", getgidFunc), + /* 48 */ SyscallDesc("signal", unimplementedFunc), + /* 49 */ SyscallDesc("geteuid", geteuidFunc), + /* 50 */ SyscallDesc("getegid", getegidFunc), + /* 51 */ SyscallDesc("acct", unimplementedFunc), + /* 52 */ SyscallDesc("memory_ordering", unimplementedFunc), + /* 53 */ SyscallDesc("getgid32", unimplementedFunc), + /* 54 */ SyscallDesc("ioctl", unimplementedFunc), + /* 55 */ SyscallDesc("reboot", unimplementedFunc), + /* 56 */ SyscallDesc("mmap2", unimplementedFunc), + /* 57 */ SyscallDesc("symlink", unimplementedFunc), + /* 58 */ SyscallDesc("readlink", unimplementedFunc), + /* 59 */ SyscallDesc("execve", unimplementedFunc), + /* 60 */ SyscallDesc("umask", unimplementedFunc), + /* 61 */ SyscallDesc("chroot", unimplementedFunc), + /* 62 */ SyscallDesc("fstat", fstatFunc<SparcLinux>), + /* 63 */ SyscallDesc("fstat64", unimplementedFunc), + /* 64 */ SyscallDesc("getpagesize", unimplementedFunc), + /* 65 */ SyscallDesc("msync", unimplementedFunc), + /* 66 */ SyscallDesc("vfork", unimplementedFunc), + /* 67 */ SyscallDesc("pread64", unimplementedFunc), + /* 68 */ SyscallDesc("pwrite64", unimplementedFunc), + /* 69 */ SyscallDesc("geteuid32", unimplementedFunc), + /* 70 */ SyscallDesc("getdgid32", unimplementedFunc), + /* 71 */ SyscallDesc("mmap", mmapFunc<SparcLinux>), + /* 72 */ SyscallDesc("setreuid32", unimplementedFunc), + /* 73 */ SyscallDesc("munmap", munmapFunc), + /* 74 */ SyscallDesc("mprotect", unimplementedFunc), + /* 75 */ SyscallDesc("madvise", unimplementedFunc), + /* 76 */ SyscallDesc("vhangup", unimplementedFunc), + /* 77 */ SyscallDesc("truncate64", unimplementedFunc), + /* 78 */ SyscallDesc("mincore", unimplementedFunc), + /* 79 */ SyscallDesc("getgroups", unimplementedFunc), + /* 80 */ SyscallDesc("setgroups", unimplementedFunc), + /* 81 */ SyscallDesc("getpgrp", unimplementedFunc), + /* 82 */ SyscallDesc("setgroups32", unimplementedFunc), + /* 83 */ SyscallDesc("setitimer", unimplementedFunc), + /* 84 */ SyscallDesc("ftruncate64", unimplementedFunc), + /* 85 */ SyscallDesc("swapon", unimplementedFunc), + /* 86 */ SyscallDesc("getitimer", unimplementedFunc), + /* 87 */ SyscallDesc("setuid32", unimplementedFunc), + /* 88 */ SyscallDesc("sethostname", unimplementedFunc), + /* 89 */ SyscallDesc("setgid32", unimplementedFunc), + /* 90 */ SyscallDesc("dup2", unimplementedFunc), + /* 91 */ SyscallDesc("setfsuid32", unimplementedFunc), + /* 92 */ SyscallDesc("fcntl", unimplementedFunc), + /* 93 */ SyscallDesc("select", unimplementedFunc), + /* 94 */ SyscallDesc("setfsgid32", unimplementedFunc), + /* 95 */ SyscallDesc("fsync", unimplementedFunc), + /* 96 */ SyscallDesc("setpriority", unimplementedFunc), + /* 97 */ SyscallDesc("socket", unimplementedFunc), + /* 98 */ SyscallDesc("connect", unimplementedFunc), + /* 99 */ SyscallDesc("accept", unimplementedFunc), + /* 100 */ SyscallDesc("getpriority", unimplementedFunc), + /* 101 */ SyscallDesc("rt_sigreturn", unimplementedFunc), + /* 102 */ SyscallDesc("rt_sigaction", ignoreFunc), + /* 103 */ SyscallDesc("rt_sigprocmask", unimplementedFunc), + /* 104 */ SyscallDesc("rt_sigpending", unimplementedFunc), + /* 105 */ SyscallDesc("rt_sigtimedwait", unimplementedFunc), + /* 106 */ SyscallDesc("rt_sigqueueinfo", unimplementedFunc), + /* 107 */ SyscallDesc("rt_sigsuspend", unimplementedFunc), + /* 108 */ SyscallDesc("setresuid", unimplementedFunc), + /* 109 */ SyscallDesc("getresuid", getresuidFunc), + /* 110 */ SyscallDesc("setresgid", unimplementedFunc), + /* 111 */ SyscallDesc("getresgid", unimplementedFunc), + /* 112 */ SyscallDesc("setregid32", unimplementedFunc), + /* 113 */ SyscallDesc("recvmsg", unimplementedFunc), + /* 114 */ SyscallDesc("sendmsg", unimplementedFunc), + /* 115 */ SyscallDesc("getgroups32", unimplementedFunc), + /* 116 */ SyscallDesc("gettimeofday", unimplementedFunc), + /* 117 */ SyscallDesc("getrusage", unimplementedFunc), + /* 118 */ SyscallDesc("getsockopt", unimplementedFunc), + /* 119 */ SyscallDesc("getcwd", unimplementedFunc), + /* 120 */ SyscallDesc("readv", unimplementedFunc), + /* 121 */ SyscallDesc("writev", unimplementedFunc), + /* 122 */ SyscallDesc("settimeofday", unimplementedFunc), + /* 123 */ SyscallDesc("fchown", unimplementedFunc), + /* 124 */ SyscallDesc("fchmod", unimplementedFunc), + /* 125 */ SyscallDesc("recvfrom", unimplementedFunc), + /* 126 */ SyscallDesc("setreuid", unimplementedFunc), + /* 127 */ SyscallDesc("setregid", unimplementedFunc), + /* 128 */ SyscallDesc("rename", unimplementedFunc), + /* 129 */ SyscallDesc("truncate", unimplementedFunc), + /* 130 */ SyscallDesc("ftruncate", unimplementedFunc), + /* 131 */ SyscallDesc("flock", unimplementedFunc), + /* 132 */ SyscallDesc("lstat64", unimplementedFunc), + /* 133 */ SyscallDesc("sendto", unimplementedFunc), + /* 134 */ SyscallDesc("shutdown", unimplementedFunc), + /* 135 */ SyscallDesc("socketpair", unimplementedFunc), + /* 136 */ SyscallDesc("mkdir", unimplementedFunc), + /* 137 */ SyscallDesc("rmdir", unimplementedFunc), + /* 138 */ SyscallDesc("utimes", unimplementedFunc), + /* 139 */ SyscallDesc("stat64", unimplementedFunc), + /* 140 */ SyscallDesc("sendfile64", unimplementedFunc), + /* 141 */ SyscallDesc("getpeername", unimplementedFunc), + /* 142 */ SyscallDesc("futex", unimplementedFunc), + /* 143 */ SyscallDesc("gettid", unimplementedFunc), + /* 144 */ SyscallDesc("getrlimit", unimplementedFunc), + /* 145 */ SyscallDesc("setrlimit", unimplementedFunc), + /* 146 */ SyscallDesc("pivot_root", unimplementedFunc), + /* 147 */ SyscallDesc("prctl", unimplementedFunc), + /* 148 */ SyscallDesc("pciconfig_read", unimplementedFunc), + /* 149 */ SyscallDesc("pciconfig_write", unimplementedFunc), + /* 150 */ SyscallDesc("getsockname", unimplementedFunc), + /* 151 */ SyscallDesc("inotify_init", unimplementedFunc), + /* 152 */ SyscallDesc("inotify_add_watch", unimplementedFunc), + /* 153 */ SyscallDesc("poll", unimplementedFunc), + /* 154 */ SyscallDesc("getdents64", unimplementedFunc), + /* 155 */ SyscallDesc("fcntl64", unimplementedFunc), + /* 156 */ SyscallDesc("inotify_rm_watch", unimplementedFunc), + /* 157 */ SyscallDesc("statfs", unimplementedFunc), + /* 158 */ SyscallDesc("fstatfs", unimplementedFunc), + /* 159 */ SyscallDesc("umount", unimplementedFunc), + /* 160 */ SyscallDesc("sched_set_affinity", unimplementedFunc), + /* 161 */ SyscallDesc("sched_get_affinity", unimplementedFunc), + /* 162 */ SyscallDesc("getdomainname", unimplementedFunc), + /* 163 */ SyscallDesc("setdomainname", unimplementedFunc), + /* 164 */ SyscallDesc("utrap_install", unimplementedFunc), + /* 165 */ SyscallDesc("quotactl", unimplementedFunc), + /* 166 */ SyscallDesc("set_tid_address", unimplementedFunc), + /* 167 */ SyscallDesc("mount", unimplementedFunc), + /* 168 */ SyscallDesc("ustat", unimplementedFunc), + /* 169 */ SyscallDesc("setxattr", unimplementedFunc), + /* 170 */ SyscallDesc("lsetxattr", unimplementedFunc), + /* 171 */ SyscallDesc("fsetxattr", unimplementedFunc), + /* 172 */ SyscallDesc("getxattr", unimplementedFunc), + /* 173 */ SyscallDesc("lgetxattr", unimplementedFunc), + /* 174 */ SyscallDesc("getdents", unimplementedFunc), + /* 175 */ SyscallDesc("setsid", unimplementedFunc), + /* 176 */ SyscallDesc("fchdir", unimplementedFunc), + /* 177 */ SyscallDesc("fgetxattr", unimplementedFunc), + /* 178 */ SyscallDesc("listxattr", unimplementedFunc), + /* 179 */ SyscallDesc("llistxattr", unimplementedFunc), + /* 180 */ SyscallDesc("flistxattr", unimplementedFunc), + /* 181 */ SyscallDesc("removexattr", unimplementedFunc), + /* 182 */ SyscallDesc("lremovexattr", unimplementedFunc), + /* 183 */ SyscallDesc("sigpending", unimplementedFunc), + /* 184 */ SyscallDesc("query_module", unimplementedFunc), + /* 185 */ SyscallDesc("setpgid", unimplementedFunc), + /* 186 */ SyscallDesc("fremovexattr", unimplementedFunc), + /* 187 */ SyscallDesc("tkill", unimplementedFunc), + /* 188 */ SyscallDesc("exit_group", exitFunc), + /* 189 */ SyscallDesc("uname", unameFunc), + /* 190 */ SyscallDesc("init_module", unimplementedFunc), + /* 191 */ SyscallDesc("personality", unimplementedFunc), + /* 192 */ SyscallDesc("remap_file_pages", unimplementedFunc), + /* 193 */ SyscallDesc("epoll_create", unimplementedFunc), + /* 194 */ SyscallDesc("epoll_ctl", unimplementedFunc), + /* 195 */ SyscallDesc("epoll_wait", unimplementedFunc), + /* 196 */ SyscallDesc("ioprio_set", unimplementedFunc), + /* 197 */ SyscallDesc("getppid", getppidFunc), + /* 198 */ SyscallDesc("sigaction", ignoreFunc), + /* 199 */ SyscallDesc("sgetmask", unimplementedFunc), + /* 200 */ SyscallDesc("ssetmask", unimplementedFunc), + /* 201 */ SyscallDesc("sigsuspend", unimplementedFunc), + /* 202 */ SyscallDesc("oldlstat", unimplementedFunc), + /* 203 */ SyscallDesc("uselib", unimplementedFunc), + /* 204 */ SyscallDesc("readdir", unimplementedFunc), + /* 205 */ SyscallDesc("readahead", unimplementedFunc), + /* 206 */ SyscallDesc("socketcall", unimplementedFunc), + /* 207 */ SyscallDesc("syslog", unimplementedFunc), + /* 208 */ SyscallDesc("lookup_dcookie", unimplementedFunc), + /* 209 */ SyscallDesc("fadvise64", unimplementedFunc), + /* 210 */ SyscallDesc("fadvise64_64", unimplementedFunc), + /* 211 */ SyscallDesc("tgkill", unimplementedFunc), + /* 212 */ SyscallDesc("waitpid", unimplementedFunc), + /* 213 */ SyscallDesc("swapoff", unimplementedFunc), + /* 214 */ SyscallDesc("sysinfo", unimplementedFunc), + /* 215 */ SyscallDesc("ipc", unimplementedFunc), + /* 216 */ SyscallDesc("sigreturn", unimplementedFunc), + /* 217 */ SyscallDesc("clone", unimplementedFunc), + /* 218 */ SyscallDesc("ioprio_get", unimplementedFunc), + /* 219 */ SyscallDesc("adjtimex", unimplementedFunc), + /* 220 */ SyscallDesc("sigprocmask", unimplementedFunc), + /* 221 */ SyscallDesc("create_module", unimplementedFunc), + /* 222 */ SyscallDesc("delete_module", unimplementedFunc), + /* 223 */ SyscallDesc("get_kernel_syms", unimplementedFunc), + /* 224 */ SyscallDesc("getpgid", unimplementedFunc), + /* 225 */ SyscallDesc("bdflush", unimplementedFunc), + /* 226 */ SyscallDesc("sysfs", unimplementedFunc), + /* 227 */ SyscallDesc("afs_syscall", unimplementedFunc), + /* 228 */ SyscallDesc("setfsuid", unimplementedFunc), + /* 229 */ SyscallDesc("setfsgid", unimplementedFunc), + /* 230 */ SyscallDesc("_newselect", unimplementedFunc), + /* 231 */ SyscallDesc("time", unimplementedFunc), + /* 232 */ SyscallDesc("oldstat", unimplementedFunc), + /* 233 */ SyscallDesc("stime", unimplementedFunc), + /* 234 */ SyscallDesc("statfs64", unimplementedFunc), + /* 235 */ SyscallDesc("fstatfs64", unimplementedFunc), + /* 236 */ SyscallDesc("_llseek", unimplementedFunc), + /* 237 */ SyscallDesc("mlock", unimplementedFunc), + /* 238 */ SyscallDesc("munlock", unimplementedFunc), + /* 239 */ SyscallDesc("mlockall", unimplementedFunc), + /* 240 */ SyscallDesc("munlockall", unimplementedFunc), + /* 241 */ SyscallDesc("sched_setparam", unimplementedFunc), + /* 242 */ SyscallDesc("sched_getparam", unimplementedFunc), + /* 243 */ SyscallDesc("sched_setscheduler", unimplementedFunc), + /* 244 */ SyscallDesc("sched_getscheduler", unimplementedFunc), + /* 245 */ SyscallDesc("sched_yield", unimplementedFunc), + /* 246 */ SyscallDesc("sched_get_priority_max", unimplementedFunc), + /* 247 */ SyscallDesc("sched_get_priority_min", unimplementedFunc), + /* 248 */ SyscallDesc("sched_rr_get_interval", unimplementedFunc), + /* 249 */ SyscallDesc("nanosleep", unimplementedFunc), + /* 250 */ SyscallDesc("mremap", unimplementedFunc), + /* 251 */ SyscallDesc("_sysctl", unimplementedFunc), + /* 252 */ SyscallDesc("getsid", unimplementedFunc), + /* 253 */ SyscallDesc("fdatasync", unimplementedFunc), + /* 254 */ SyscallDesc("nfsservctl", unimplementedFunc), + /* 255 */ SyscallDesc("aplib", unimplementedFunc), + /* 256 */ SyscallDesc("clock_settime", unimplementedFunc), + /* 257 */ SyscallDesc("clock_gettime", unimplementedFunc), + /* 258 */ SyscallDesc("clock_getres", unimplementedFunc), + /* 259 */ SyscallDesc("clock_nanosleep", unimplementedFunc), + /* 260 */ SyscallDesc("sched_getaffinity", unimplementedFunc), + /* 261 */ SyscallDesc("sched_setaffinity", unimplementedFunc), + /* 262 */ SyscallDesc("timer_settime", unimplementedFunc), + /* 263 */ SyscallDesc("timer_gettime", unimplementedFunc), + /* 264 */ SyscallDesc("timer_getoverrun", unimplementedFunc), + /* 265 */ SyscallDesc("timer_delete", unimplementedFunc), + /* 266 */ SyscallDesc("timer_create", unimplementedFunc), + /* 267 */ SyscallDesc("vserver", unimplementedFunc), + /* 268 */ SyscallDesc("io_setup", unimplementedFunc), + /* 269 */ SyscallDesc("io_destroy", unimplementedFunc), + /* 270 */ SyscallDesc("io_submit", unimplementedFunc), + /* 271 */ SyscallDesc("io_cancel", unimplementedFunc), + /* 272 */ SyscallDesc("io_getevents", unimplementedFunc), + /* 273 */ SyscallDesc("mq_open", unimplementedFunc), + /* 274 */ SyscallDesc("mq_unlink", unimplementedFunc), + /* 275 */ SyscallDesc("mq_timedsend", unimplementedFunc), + /* 276 */ SyscallDesc("mq_timedreceive", unimplementedFunc), + /* 277 */ SyscallDesc("mq_notify", unimplementedFunc), + /* 278 */ SyscallDesc("mq_getsetattr", unimplementedFunc), + /* 279 */ SyscallDesc("waitid", unimplementedFunc), + /* 280 */ SyscallDesc("sys_setaltroot", unimplementedFunc), + /* 281 */ SyscallDesc("add_key", unimplementedFunc), + /* 282 */ SyscallDesc("request_key", unimplementedFunc), + /* 283 */ SyscallDesc("keyctl", unimplementedFunc) +}; + +SparcLinuxProcess::SparcLinuxProcess(const std::string &name, + ObjectFile *objFile, + System * system, + int stdin_fd, + int stdout_fd, + int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid) + : SparcLiveProcess(name, objFile, system, + stdin_fd, stdout_fd, stderr_fd, argv, envp, + _uid, _euid, _gid, _egid, _pid, _ppid), + Num_Syscall_Descs(sizeof(syscallDescs) / sizeof(SyscallDesc)) +{ + // The sparc syscall table must be <= 284 entries because that is all there + // is space for. + assert(Num_Syscall_Descs <= 284); +} + + + +SyscallDesc* +SparcLinuxProcess::getDesc(int callnum) +{ + if (callnum < 0 || callnum > Num_Syscall_Descs) + return NULL; + return &syscallDescs[callnum]; +} diff --git a/src/arch/sparc/linux/process.hh b/src/arch/sparc/linux/process.hh new file mode 100644 index 000000000..c758d5433 --- /dev/null +++ b/src/arch/sparc/linux/process.hh @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2003-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __SPARC_LINUX_PROCESS_HH__ +#define __SPARC_LINUX_PROCESS_HH__ + +#include "arch/sparc/linux/linux.hh" +#include "arch/sparc/syscallreturn.hh" +#include "arch/sparc/process.hh" +#include "sim/process.hh" + +namespace SparcISA { + +/// A process with emulated SPARC/Linux syscalls. +class SparcLinuxProcess : public SparcLiveProcess +{ + public: + /// Constructor. + SparcLinuxProcess(const std::string &name, + ObjectFile *objFile, + System * system, + int stdin_fd, int stdout_fd, int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid); + + virtual SyscallDesc* getDesc(int callnum); + + /// The target system's hostname. + static const char *hostname; + + /// Array of syscall descriptors, indexed by call number. + static SyscallDesc syscallDescs[]; + + const int Num_Syscall_Descs; +}; + +SyscallReturn getresuidFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +} // namespace SparcISA +#endif // __ALPHA_LINUX_PROCESS_HH__ diff --git a/src/arch/sparc/miscregfile.cc b/src/arch/sparc/miscregfile.cc new file mode 100644 index 000000000..8041e45c0 --- /dev/null +++ b/src/arch/sparc/miscregfile.cc @@ -0,0 +1,540 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + */ + +#include "arch/sparc/miscregfile.hh" +#include "base/trace.hh" +#include "cpu/base.hh" +#include "cpu/thread_context.hh" + +using namespace SparcISA; +using namespace std; + +class Checkpoint; + +//These functions map register indices to names +string SparcISA::getMiscRegName(RegIndex index) +{ + static::string miscRegName[NumMiscRegs] = + {"y", "ccr", "asi", "tick", "pc", "fprs", "pcr", "pic", + "gsr", "softint_set", "softint_clr", "softint", "tick_cmpr", + "stick", "stick_cmpr", + "tpc", "tnpc", "tstate", "tt", "privtick", "tba", "pstate", "tl", + "pil", "cwp", "cansave", "canrestore", "cleanwin", "otherwin", + "wstate", "gl", + "hpstate", "htstate", "hintp", "htba", "hver", "strand_sts_reg", + "hstick_cmpr", + "fsr"}; + return miscRegName[index]; +} + +#if FULL_SYSTEM + +//XXX These need an implementation someplace +/** Fullsystem only register version of ReadRegWithEffect() */ +MiscReg MiscRegFile::readFSRegWithEffect(int miscReg, Fault &fault, ThreadContext *tc); +/** Fullsystem only register version of SetRegWithEffect() */ +Fault MiscRegFile::setFSRegWithEffect(int miscReg, const MiscReg &val, + ThreadContext * tc); +#endif + +void MiscRegFile::reset() +{ + pstateFields.pef = 0; //No FPU + //pstateFields.pef = 1; //FPU +#if FULL_SYSTEM + //For SPARC, when a system is first started, there is a power + //on reset Trap which sets the processor into the following state. + //Bits that aren't set aren't defined on startup. + tl = MaxTL; + gl = MaxGL; + + tickFields.counter = 0; //The TICK register is unreadable bya + tickFields.npt = 1; //The TICK register is unreadable by by !priv + + softint = 0; // Clear all the soft interrupt bits + tick_cmprFields.int_dis = 1; // disable timer compare interrupts + tick_cmprFields.tick_cmpr = 0; // Reset to 0 for pretty printing + stickFields.npt = 1; //The TICK register is unreadable by by !priv + stick_cmprFields.int_dis = 1; // disable timer compare interrupts + stick_cmprFields.tick_cmpr = 0; // Reset to 0 for pretty printing + + + tt[tl] = power_on_reset; + pstate = 0; // fields 0 but pef + pstateFields.pef = 1; + + hpstate = 0; + hpstateFields.red = 1; + hpstateFields.hpriv = 1; + hpstateFields.tlz = 0; // this is a guess + hintp = 0; // no interrupts pending + hstick_cmprFields.int_dis = 1; // disable timer compare interrupts + hstick_cmprFields.tick_cmpr = 0; // Reset to 0 for pretty printing +#else +/* //This sets up the initial state of the processor for usermode processes + pstateFields.priv = 0; //Process runs in user mode + pstateFields.ie = 1; //Interrupts are enabled + fsrFields.rd = 0; //Round to nearest + fsrFields.tem = 0; //Floating point traps not enabled + fsrFields.ns = 0; //Non standard mode off + fsrFields.qne = 0; //Floating point queue is empty + fsrFields.aexc = 0; //No accrued exceptions + fsrFields.cexc = 0; //No current exceptions + + //Register window management registers + otherwin = 0; //No windows contain info from other programs + canrestore = 0; //There are no windows to pop + cansave = MaxTL - 2; //All windows are available to save into + cleanwin = MaxTL;*/ +#endif +} + +MiscReg MiscRegFile::readReg(int miscReg) +{ + switch (miscReg) { + case MISCREG_Y: + return y; + case MISCREG_CCR: + return ccr; + case MISCREG_ASI: + return asi; + case MISCREG_FPRS: + return fprs; + case MISCREG_TICK: + return tick; + case MISCREG_PCR: + case MISCREG_PIC: + panic("ASR number %d not implemented\n", miscReg - AsrStart); + case MISCREG_GSR: + return gsr; + case MISCREG_SOFTINT: + return softint; + case MISCREG_TICK_CMPR: + return tick_cmpr; + case MISCREG_STICK: + return stick; + case MISCREG_STICK_CMPR: + return stick_cmpr; + + /** Privilged Registers */ + case MISCREG_TPC: + return tpc[tl-1]; + case MISCREG_TNPC: + return tnpc[tl-1]; + case MISCREG_TSTATE: + return tstate[tl-1]; + case MISCREG_TT: + return tt[tl-1]; + case MISCREG_PRIVTICK: + panic("Priviliged access to tick registers not implemented\n"); + case MISCREG_TBA: + return tba; + case MISCREG_PSTATE: + return pstate; + case MISCREG_TL: + return tl; + case MISCREG_PIL: + return pil; + case MISCREG_CWP: + return cwp; + case MISCREG_CANSAVE: + return cansave; + case MISCREG_CANRESTORE: + return canrestore; + case MISCREG_CLEANWIN: + return cleanwin; + case MISCREG_OTHERWIN: + return otherwin; + case MISCREG_WSTATE: + return wstate; + case MISCREG_GL: + return gl; + + /** Hyper privileged registers */ + case MISCREG_HPSTATE: + return hpstate; + case MISCREG_HTSTATE: + return htstate[tl-1]; + case MISCREG_HINTP: + panic("HINTP not implemented\n"); + case MISCREG_HTBA: + return htba; + case MISCREG_HVER: + return NWindows | MaxTL << 8 | MaxGL << 16; + case MISCREG_STRAND_STS_REG: + return strandStatusReg; + case MISCREG_HSTICK_CMPR: + return hstick_cmpr; + + /** Floating Point Status Register */ + case MISCREG_FSR: + return fsr; + default: + panic("Miscellaneous register %d not implemented\n", miscReg); + } +} + +MiscReg MiscRegFile::readRegWithEffect(int miscReg, + Fault &fault, ThreadContext * tc) +{ + fault = NoFault; + switch (miscReg) { + case MISCREG_Y: + case MISCREG_CCR: + case MISCREG_ASI: + return readReg(miscReg); + + case MISCREG_TICK: + case MISCREG_PRIVTICK: + // Check for reading privilege + if (tickFields.npt && !isNonPriv()) { + fault = new PrivilegedAction; + return 0; + } + return tc->getCpuPtr()->curCycle() - tickFields.counter | + tickFields.npt << 63; + case MISCREG_PC: + return tc->readPC(); + case MISCREG_FPRS: + fault = new UnimpFault("FPU not implemented\n"); + return 0; + case MISCREG_PCR: + fault = new UnimpFault("Performance Instrumentation not impl\n"); + return 0; + case MISCREG_PIC: + fault = new UnimpFault("Performance Instrumentation not impl\n"); + return 0; + case MISCREG_GSR: + return readReg(miscReg); + + /** Privilged Registers */ + case MISCREG_TPC: + case MISCREG_TNPC: + case MISCREG_TSTATE: + case MISCREG_TT: + if (tl == 0) { + fault = new IllegalInstruction; + return 0; + } // NOTE THE FALL THROUGH! + case MISCREG_PSTATE: + case MISCREG_TL: + return readReg(miscReg); + + case MISCREG_TBA: + return readReg(miscReg) & ULL(~0x7FFF); + + case MISCREG_PIL: + + case MISCREG_CWP: + case MISCREG_CANSAVE: + case MISCREG_CANRESTORE: + case MISCREG_CLEANWIN: + case MISCREG_OTHERWIN: + case MISCREG_WSTATE: + case MISCREG_GL: + return readReg(miscReg); + + /** Floating Point Status Register */ + case MISCREG_FSR: + panic("Floating Point not implemented\n"); + default: +#if FULL_SYSTEM + return readFSRegWithEffect(miscReg, fault, tc); +#else + fault = new IllegalInstruction; + return 0; +#endif + } +} + +Fault MiscRegFile::setReg(int miscReg, const MiscReg &val) +{ + switch (miscReg) { + case MISCREG_Y: + y = val; + return NoFault; + case MISCREG_CCR: + ccr = val; + return NoFault; + case MISCREG_ASI: + asi = val; + return NoFault; + case MISCREG_FPRS: + fprs = val; + return NoFault; + case MISCREG_TICK: + tick = val; + return NoFault; + case MISCREG_PCR: + case MISCREG_PIC: + panic("ASR number %d not implemented\n", miscReg - AsrStart); + case MISCREG_GSR: + gsr = val; + case MISCREG_SOFTINT: + softint = val; + return NoFault; + case MISCREG_TICK_CMPR: + tick_cmpr = val; + return NoFault; + case MISCREG_STICK: + stick = val; + return NoFault; + case MISCREG_STICK_CMPR: + stick_cmpr = val; + return NoFault; + + /** Privilged Registers */ + case MISCREG_TPC: + tpc[tl-1] = val; + return NoFault; + case MISCREG_TNPC: + tnpc[tl-1] = val; + return NoFault; + case MISCREG_TSTATE: + tstate[tl-1] = val; + return NoFault; + case MISCREG_TT: + tt[tl-1] = val; + return NoFault; + case MISCREG_PRIVTICK: + panic("Priviliged access to tick regesiters not implemented\n"); + case MISCREG_TBA: + tba = val; + return NoFault; + case MISCREG_PSTATE: + pstate = val; + return NoFault; + case MISCREG_TL: + tl = val; + return NoFault; + case MISCREG_PIL: + pil = val; + return NoFault; + case MISCREG_CWP: + cwp = val; + return NoFault; + case MISCREG_CANSAVE: + cansave = val; + return NoFault; + case MISCREG_CANRESTORE: + canrestore = val; + return NoFault; + case MISCREG_CLEANWIN: + cleanwin = val; + return NoFault; + case MISCREG_OTHERWIN: + otherwin = val; + return NoFault; + case MISCREG_WSTATE: + wstate = val; + return NoFault; + case MISCREG_GL: + gl = val; + return NoFault; + + /** Hyper privileged registers */ + case MISCREG_HPSTATE: + hpstate = val; + return NoFault; + case MISCREG_HTSTATE: + htstate[tl-1] = val; + return NoFault; + case MISCREG_HINTP: + panic("HINTP not implemented\n"); + case MISCREG_HTBA: + htba = val; + return NoFault; + case MISCREG_STRAND_STS_REG: + strandStatusReg = val; + return NoFault; + case MISCREG_HSTICK_CMPR: + hstick_cmpr = val; + return NoFault; + + /** Floating Point Status Register */ + case MISCREG_FSR: + fsr = val; + return NoFault; + default: + panic("Miscellaneous register %d not implemented\n", miscReg); + } +} + +Fault MiscRegFile::setRegWithEffect(int miscReg, + const MiscReg &val, ThreadContext * tc) +{ + const uint64_t Bit64 = (1ULL << 63); + switch (miscReg) { + case MISCREG_Y: + case MISCREG_CCR: + case MISCREG_ASI: + setReg(miscReg, val); + return NoFault; + case MISCREG_PRIVTICK: + case MISCREG_TICK: + if (isNonPriv()) + return new PrivilegedOpcode; + if (isPriv()) + return new PrivilegedAction; + tickFields.counter = tc->getCpuPtr()->curCycle() - val & ~Bit64; + tickFields.npt = val & Bit64 ? 1 : 0; + return NoFault; + case MISCREG_PC: + return new IllegalInstruction; + case MISCREG_FPRS: + return new UnimpFault("FPU not implemented\n"); + case MISCREG_PCR: + return new UnimpFault("Performance Instrumentation not impl\n"); + case MISCREG_PIC: + return new UnimpFault("Performance Instrumentation not impl\n"); + case MISCREG_GSR: + return setReg(miscReg, val); + + /** Privilged Registers */ + case MISCREG_TPC: + case MISCREG_TNPC: + case MISCREG_TSTATE: + case MISCREG_TT: + if (tl == 0) + return new IllegalInstruction; + setReg(miscReg, val); + return NoFault; + + case MISCREG_TBA: + // clear lower 7 bits on writes. + setReg(miscReg, val & ULL(~0x7FFF)); + return NoFault; + + case MISCREG_PSTATE: + setReg(miscReg, val); + return NoFault; + + case MISCREG_TL: + if (isHyperPriv() && val > MaxTL) + setReg(miscReg, MaxTL); + else if (isPriv() && !isHyperPriv() && val > MaxPTL) + setReg(miscReg, MaxPTL); + else + setReg(miscReg, val); + return NoFault; + + case MISCREG_CWP: + tc->changeRegFileContext(CONTEXT_CWP, val); + case MISCREG_CANSAVE: + case MISCREG_CANRESTORE: + case MISCREG_CLEANWIN: + case MISCREG_OTHERWIN: + case MISCREG_WSTATE: + setReg(miscReg, val); + return NoFault; + + case MISCREG_GL: + int newval; + if (isHyperPriv() && val > MaxGL) + newval = MaxGL; + else if (isPriv() && !isHyperPriv() && val > MaxPGL) + newval = MaxPGL; + else + newval = val; + tc->changeRegFileContext(CONTEXT_GLOBALS, newval); + setReg(miscReg, newval); + return NoFault; + + /** Floating Point Status Register */ + case MISCREG_FSR: + panic("Floating Point not implemented\n"); + default: +#if FULL_SYSTEM + setFSRegWithEffect(miscReg, val, tc); +#else + return new IllegalInstruction; +#endif + } +} + +void MiscRegFile::serialize(std::ostream & os) +{ + SERIALIZE_SCALAR(pstate); + SERIALIZE_SCALAR(tba); + SERIALIZE_SCALAR(y); + SERIALIZE_SCALAR(pil); + SERIALIZE_SCALAR(gl); + SERIALIZE_SCALAR(cwp); + SERIALIZE_ARRAY(tt, MaxTL); + SERIALIZE_SCALAR(ccr); + SERIALIZE_SCALAR(asi); + SERIALIZE_SCALAR(tl); + SERIALIZE_ARRAY(tpc, MaxTL); + SERIALIZE_ARRAY(tnpc, MaxTL); + SERIALIZE_ARRAY(tstate, MaxTL); + SERIALIZE_SCALAR(tick); + SERIALIZE_SCALAR(cansave); + SERIALIZE_SCALAR(canrestore); + SERIALIZE_SCALAR(otherwin); + SERIALIZE_SCALAR(cleanwin); + SERIALIZE_SCALAR(wstate); + SERIALIZE_SCALAR(fsr); + SERIALIZE_SCALAR(fprs); + SERIALIZE_SCALAR(hpstate); + SERIALIZE_ARRAY(htstate, MaxTL); + SERIALIZE_SCALAR(htba); + SERIALIZE_SCALAR(hstick_cmpr); +} + +void MiscRegFile::unserialize(Checkpoint * cp, const std::string & section) +{ + UNSERIALIZE_SCALAR(pstate); + UNSERIALIZE_SCALAR(tba); + UNSERIALIZE_SCALAR(y); + UNSERIALIZE_SCALAR(pil); + UNSERIALIZE_SCALAR(gl); + UNSERIALIZE_SCALAR(cwp); + UNSERIALIZE_ARRAY(tt, MaxTL); + UNSERIALIZE_SCALAR(ccr); + UNSERIALIZE_SCALAR(asi); + UNSERIALIZE_SCALAR(tl); + UNSERIALIZE_ARRAY(tpc, MaxTL); + UNSERIALIZE_ARRAY(tnpc, MaxTL); + UNSERIALIZE_ARRAY(tstate, MaxTL); + UNSERIALIZE_SCALAR(tick); + UNSERIALIZE_SCALAR(cansave); + UNSERIALIZE_SCALAR(canrestore); + UNSERIALIZE_SCALAR(otherwin); + UNSERIALIZE_SCALAR(cleanwin); + UNSERIALIZE_SCALAR(wstate); + UNSERIALIZE_SCALAR(fsr); + UNSERIALIZE_SCALAR(fprs); + UNSERIALIZE_SCALAR(hpstate); + UNSERIALIZE_ARRAY(htstate, MaxTL); + UNSERIALIZE_SCALAR(htba); + UNSERIALIZE_SCALAR(hstick_cmpr); +} + diff --git a/src/arch/sparc/miscregfile.hh b/src/arch/sparc/miscregfile.hh new file mode 100644 index 000000000..be143311f --- /dev/null +++ b/src/arch/sparc/miscregfile.hh @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + */ + +#ifndef __ARCH_SPARC_MISCREGFILE_HH__ +#define __ARCH_SPARC_MISCREGFILE_HH__ + +#include "arch/sparc/faults.hh" +#include "arch/sparc/isa_traits.hh" +#include "arch/sparc/types.hh" + +#include <string> + +namespace SparcISA +{ + + //These functions map register indices to names + std::string getMiscRegName(RegIndex); + + const int AsrStart = 0; + const int PrStart = 32; + const int HprStart = 64; + const int MiscStart = 96; + + enum MiscRegIndex + { + /** Ancillary State Registers */ + MISCREG_Y = AsrStart + 0, + MISCREG_CCR = AsrStart + 2, + MISCREG_ASI = AsrStart + 3, + MISCREG_TICK = AsrStart + 4, + MISCREG_PC = AsrStart + 5, + MISCREG_FPRS = AsrStart + 6, + MISCREG_PCR = AsrStart + 16, + MISCREG_PIC = AsrStart + 17, + MISCREG_GSR = AsrStart + 19, + MISCREG_SOFTINT_SET = AsrStart + 20, + MISCREG_SOFTINT_CLR = AsrStart + 21, + MISCREG_SOFTINT = AsrStart + 22, + MISCREG_TICK_CMPR = AsrStart + 23, + MISCREG_STICK = AsrStart + 24, + MISCREG_STICK_CMPR = AsrStart + 25, + + /** Privilged Registers */ + MISCREG_TPC = PrStart + 0, + MISCREG_TNPC = PrStart + 1, + MISCREG_TSTATE = PrStart + 2, + MISCREG_TT = PrStart + 3, + MISCREG_PRIVTICK = PrStart + 4, + MISCREG_TBA = PrStart + 5, + MISCREG_PSTATE = PrStart + 6, + MISCREG_TL = PrStart + 7, + MISCREG_PIL = PrStart + 8, + MISCREG_CWP = PrStart + 9, + MISCREG_CANSAVE = PrStart + 10, + MISCREG_CANRESTORE = PrStart + 11, + MISCREG_CLEANWIN = PrStart + 12, + MISCREG_OTHERWIN = PrStart + 13, + MISCREG_WSTATE = PrStart + 14, + MISCREG_GL = PrStart + 16, + + /** Hyper privileged registers */ + MISCREG_HPSTATE = HprStart + 0, + MISCREG_HTSTATE = HprStart + 1, + MISCREG_HINTP = HprStart + 3, + MISCREG_HTBA = HprStart + 5, + MISCREG_HVER = HprStart + 6, + MISCREG_STRAND_STS_REG = HprStart + 16, + MISCREG_HSTICK_CMPR = HprStart + 31, + + /** Floating Point Status Register */ + MISCREG_FSR = MiscStart + 0 + + }; + + // The control registers, broken out into fields + class MiscRegFile + { + private: + + /* ASR Registers */ + union { + uint64_t y; // Y (used in obsolete multiplication) + struct { + uint64_t value:32; // The actual value stored in y + uint64_t :32; // reserved bits + } yFields; + }; + union { + uint8_t ccr; // Condition Code Register + struct { + union { + uint8_t icc:4; // 32-bit condition codes + struct { + uint8_t c:1; // Carry + uint8_t v:1; // Overflow + uint8_t z:1; // Zero + uint8_t n:1; // Negative + } iccFields; + }; + union { + uint8_t xcc:4; // 64-bit condition codes + struct { + uint8_t c:1; // Carry + uint8_t v:1; // Overflow + uint8_t z:1; // Zero + uint8_t n:1; // Negative + } xccFields; + }; + } ccrFields; + }; + uint8_t asi; // Address Space Identifier + union { + uint64_t tick; // Hardware clock-tick counter + struct { + int64_t counter:63; // Clock-tick count + uint64_t npt:1; // Non-priveleged trap + } tickFields; + }; + union { + uint8_t fprs; // Floating-Point Register State + struct { + uint8_t dl:1; // Dirty lower + uint8_t du:1; // Dirty upper + uint8_t fef:1; // FPRS enable floating-Point + } fprsFields; + }; + union { + uint64_t gsr; //General Status Register + struct { + uint64_t mask:32; + uint64_t :4; + uint64_t im:1; + uint64_t irnd:2; + uint64_t :17; + uint64_t scale:5; + uint64_t align:3; + } gsrFields; + }; + union { + uint64_t softint; + struct { + uint64_t tm:1; + uint64_t int_level:14; + uint64_t sm:1; + } softintFields; + }; + union { + uint64_t tick_cmpr; // Hardware tick compare registers + struct { + uint64_t tick_cmpr:63; // Clock-tick count + uint64_t int_dis:1; // Non-priveleged trap + } tick_cmprFields; + }; + union { + uint64_t stick; // Hardware clock-tick counter + struct { + int64_t :63; // Not used, storage in SparcSystem + uint64_t npt:1; // Non-priveleged trap + } stickFields; + }; + union { + uint64_t stick_cmpr; // Hardware tick compare registers + struct { + uint64_t tick_cmpr:63; // Clock-tick count + uint64_t int_dis:1; // Non-priveleged trap + } stick_cmprFields; + }; + + + /* Privileged Registers */ + uint64_t tpc[MaxTL]; // Trap Program Counter (value from + // previous trap level) + uint64_t tnpc[MaxTL]; // Trap Next Program Counter (value from + // previous trap level) + union { + uint64_t tstate[MaxTL]; // Trap State + struct { + //Values are from previous trap level + uint64_t cwp:5; // Current Window Pointer + uint64_t :3; // Reserved bits + uint64_t pstate:13; // Process State + uint64_t :3; // Reserved bits + uint64_t asi:8; // Address Space Identifier + uint64_t ccr:8; // Condition Code Register + uint64_t gl:8; // Global level + } tstateFields[MaxTL]; + }; + uint16_t tt[MaxTL]; // Trap Type (Type of trap which occured + // on the previous level) + uint64_t tba; // Trap Base Address + + union { + uint16_t pstate; // Process State Register + struct { + uint16_t :1; // reserved + uint16_t ie:1; // Interrupt enable + uint16_t priv:1; // Privelege mode + uint16_t am:1; // Address mask + uint16_t pef:1; // PSTATE enable floating-point + uint16_t :1; // reserved2 + uint16_t mm:2; // Memory Model + uint16_t tle:1; // Trap little-endian + uint16_t cle:1; // Current little-endian + } pstateFields; + }; + uint8_t tl; // Trap Level + uint8_t pil; // Process Interrupt Register + uint8_t cwp; // Current Window Pointer + uint8_t cansave; // Savable windows + uint8_t canrestore; // Restorable windows + uint8_t cleanwin; // Clean windows + uint8_t otherwin; // Other windows + union { + uint8_t wstate; // Window State + struct { + uint8_t normal:3; // Bits TT<4:2> are set to on a normal + // register window trap + uint8_t other:3; // Bits TT<4:2> are set to on an "otherwin" + // register window trap + } wstateFields; + }; + uint8_t gl; // Global level register + + + /** Hyperprivileged Registers */ + union { + uint64_t hpstate; // Hyperprivileged State Register + struct { + uint8_t tlz: 1; + uint8_t :1; + uint8_t hpriv:1; + uint8_t :2; + uint8_t red:1; + uint8_t :4; + uint8_t ibe:1; + uint8_t id:1; + } hpstateFields; + }; + + uint64_t htstate[MaxTL]; // Hyperprivileged Trap State Register + uint64_t hintp; + uint64_t htba; // Hyperprivileged Trap Base Address register + union { + uint64_t hstick_cmpr; // Hardware tick compare registers + struct { + uint64_t tick_cmpr:63; // Clock-tick count + uint64_t int_dis:1; // Non-priveleged trap + } hstick_cmprFields; + }; + + uint64_t strandStatusReg; // Per strand status register + + + /** Floating point misc registers. */ + union { + uint64_t fsr; // Floating-Point State Register + struct { + union { + uint64_t cexc:5; // Current excpetion + struct { + uint64_t nxc:1; // Inexact + uint64_t dzc:1; // Divide by zero + uint64_t ufc:1; // Underflow + uint64_t ofc:1; // Overflow + uint64_t nvc:1; // Invalid operand + } cexcFields; + }; + union { + uint64_t aexc:5; // Accrued exception + struct { + uint64_t nxc:1; // Inexact + uint64_t dzc:1; // Divide by zero + uint64_t ufc:1; // Underflow + uint64_t ofc:1; // Overflow + uint64_t nvc:1; // Invalid operand + } aexcFields; + }; + uint64_t fcc0:2; // Floating-Point condtion codes + uint64_t :1; // Reserved bits + uint64_t qne:1; // Deferred trap queue not empty + // with no queue, it should read 0 + uint64_t ftt:3; // Floating-Point trap type + uint64_t ver:3; // Version (of the FPU) + uint64_t :2; // Reserved bits + uint64_t ns:1; // Nonstandard floating point + union { + uint64_t tem:5; // Trap Enable Mask + struct { + uint64_t nxm:1; // Inexact + uint64_t dzm:1; // Divide by zero + uint64_t ufm:1; // Underflow + uint64_t ofm:1; // Overflow + uint64_t nvm:1; // Invalid operand + } temFields; + }; + uint64_t :2; // Reserved bits + uint64_t rd:2; // Rounding direction + uint64_t fcc1:2; // Floating-Point condition codes + uint64_t fcc2:2; // Floating-Point condition codes + uint64_t fcc3:2; // Floating-Point condition codes + uint64_t :26; // Reserved bits + } fsrFields; + }; + + // These need to check the int_dis field and if 0 then + // set appropriate bit in softint and checkinterrutps on the cpu +#if FULL_SYSTEM + /** Process a tick compare event and generate an interrupt on the cpu if + * appropriate. */ + void processTickCompare(ThreadContext *tc); + void processSTickCompare(ThreadContext *tc); + void processHSTickCompare(ThreadContext *tc); + + typedef CpuEventWrapper<MiscRegFile, + &MiscRegFile::processTickCompare> TickCompareEvent; + TickCompareEvent *tickCompare; + + typedef CpuEventWrapper<MiscRegFile, + &MiscRegFile::processSTickCompare> STickCompareEvent; + STickCompareEvent *sTickCompare; + + typedef CpuEventWrapper<MiscRegFile, + &MiscRegFile::processHSTickCompare> HSTickCompareEvent; + HSTickCompareEvent *hSTickCompare; + + /** Fullsystem only register version of ReadRegWithEffect() */ + MiscReg readFSRegWithEffect(int miscReg, Fault &fault, ThreadContext *tc); + /** Fullsystem only register version of SetRegWithEffect() */ + Fault setFSRegWithEffect(int miscReg, const MiscReg &val, + ThreadContext * tc); +#endif + public: + + void reset(); + + MiscRegFile() + { + reset(); + } + + /** read a value out of an either an SE or FS IPR. No checking is done + * about SE vs. FS as this is mostly used to copy the regfile. Thus more + * register are copied that are necessary for FS. However this prevents + * a bunch of ifdefs and is rarely called so is not performance + * criticial. */ + MiscReg readReg(int miscReg); + + /** Read a value from an IPR. Only the SE iprs are here and the rest + * are are readFSRegWithEffect (which is called by readRegWithEffect()). + * Checking is done for permission based on state bits in the miscreg + * file. */ + MiscReg readRegWithEffect(int miscReg, Fault &fault, ThreadContext *tc); + + /** write a value into an either an SE or FS IPR. No checking is done + * about SE vs. FS as this is mostly used to copy the regfile. Thus more + * register are copied that are necessary for FS. However this prevents + * a bunch of ifdefs and is rarely called so is not performance + * criticial.*/ + Fault setReg(int miscReg, const MiscReg &val); + + /** Write a value into an IPR. Only the SE iprs are here and the rest + * are are setFSRegWithEffect (which is called by setRegWithEffect()). + * Checking is done for permission based on state bits in the miscreg + * file. */ + Fault setRegWithEffect(int miscReg, + const MiscReg &val, ThreadContext * tc); + + void serialize(std::ostream & os); + + void unserialize(Checkpoint * cp, const std::string & section); + + void copyMiscRegs(ThreadContext * tc); + + protected: + + bool isHyperPriv() { return hpstateFields.hpriv; } + bool isPriv() { return hpstateFields.hpriv || pstateFields.priv; } + bool isNonPriv() { return !isPriv(); } + }; +} + +#endif diff --git a/src/arch/sparc/process.cc b/src/arch/sparc/process.cc new file mode 100644 index 000000000..3323ba7a0 --- /dev/null +++ b/src/arch/sparc/process.cc @@ -0,0 +1,330 @@ +/* + * Copyright (c) 2003-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + */ + +#include "arch/sparc/isa_traits.hh" +#include "arch/sparc/process.hh" +#include "base/loader/object_file.hh" +#include "base/loader/elf_object.hh" +#include "base/misc.hh" +#include "cpu/thread_context.hh" +#include "mem/page_table.hh" +#include "mem/translating_port.hh" +#include "sim/system.hh" + +using namespace std; +using namespace SparcISA; + + +SparcLiveProcess::SparcLiveProcess(const std::string &nm, ObjectFile *objFile, + System *_system, int stdin_fd, int stdout_fd, int stderr_fd, + std::vector<std::string> &argv, std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid) + : LiveProcess(nm, objFile, _system, stdin_fd, stdout_fd, stderr_fd, + argv, envp, _uid, _euid, _gid, _egid, _pid, _ppid) +{ + + // XXX all the below need to be updated for SPARC - Ali + brk_point = objFile->dataBase() + objFile->dataSize() + objFile->bssSize(); + brk_point = roundUp(brk_point, VMPageSize); + + // Set up stack. On SPARC Linux, stack goes from the top of memory + // downward, less the hole for the kernel address space. + stack_base = (Addr)0x80000000000ULL; + + // Set up region for mmaps. Tru64 seems to start just above 0 and + // grow up from there. + mmap_start = mmap_end = 0xfffff80000000000ULL; + + // Set pointer for next thread stack. Reserve 8M for main stack. + next_thread_stack_base = stack_base - (8 * 1024 * 1024); +} + +void +SparcLiveProcess::startup() +{ + argsInit(MachineBytes, VMPageSize); + + //From the SPARC ABI + + //The process runs in user mode + threadContexts[0]->setMiscRegWithEffect(MISCREG_PSTATE, 0x02); + + //Setup default FP state + threadContexts[0]->setMiscReg(MISCREG_FSR, 0); + + threadContexts[0]->setMiscReg(MISCREG_TICK, 0); + // + /* + * Register window management registers + */ + + //No windows contain info from other programs + threadContexts[0]->setMiscRegWithEffect(MISCREG_OTHERWIN, 0); + //There are no windows to pop + threadContexts[0]->setMiscRegWithEffect(MISCREG_CANRESTORE, 0); + //All windows are available to save into + threadContexts[0]->setMiscRegWithEffect(MISCREG_CANSAVE, NWindows - 2); + //All windows are "clean" + threadContexts[0]->setMiscRegWithEffect(MISCREG_CLEANWIN, NWindows); + //Start with register window 0 + threadContexts[0]->setMiscRegWithEffect(MISCREG_CWP, 0); +} + +m5_auxv_t buildAuxVect(int64_t type, int64_t val) +{ + m5_auxv_t result; + result.a_type = TheISA::htog(type); + result.a_val = TheISA::htog(val); + return result; +} + +void +SparcLiveProcess::argsInit(int intSize, int pageSize) +{ + Process::startup(); + + string filename; + if(argv.size() < 1) + filename = ""; + else + filename = argv[0]; + + Addr alignmentMask = ~(intSize - 1); + + // load object file into target memory + objFile->loadSections(initVirtMem); + + //These are the auxilliary vector types + enum auxTypes + { + SPARC_AT_HWCAP = 16, + SPARC_AT_PAGESZ = 6, + SPARC_AT_CLKTCK = 17, + SPARC_AT_PHDR = 3, + SPARC_AT_PHENT = 4, + SPARC_AT_PHNUM = 5, + SPARC_AT_BASE = 7, + SPARC_AT_FLAGS = 8, + SPARC_AT_ENTRY = 9, + SPARC_AT_UID = 11, + SPARC_AT_EUID = 12, + SPARC_AT_GID = 13, + SPARC_AT_EGID = 14, + SPARC_AT_SECURE = 23 + }; + + enum hardwareCaps + { + M5_HWCAP_SPARC_FLUSH = 1, + M5_HWCAP_SPARC_STBAR = 2, + M5_HWCAP_SPARC_SWAP = 4, + M5_HWCAP_SPARC_MULDIV = 8, + M5_HWCAP_SPARC_V9 = 16, + //This one should technically only be set + //if there is a cheetah or cheetah_plus tlb, + //but we'll use it all the time + M5_HWCAP_SPARC_ULTRA3 = 32 + }; + + const int64_t hwcap = + M5_HWCAP_SPARC_FLUSH | + M5_HWCAP_SPARC_STBAR | + M5_HWCAP_SPARC_SWAP | + M5_HWCAP_SPARC_MULDIV | + M5_HWCAP_SPARC_V9 | + M5_HWCAP_SPARC_ULTRA3; + + + //Setup the auxilliary vectors. These will already have endian conversion. + //Auxilliary vectors are loaded only for elf formatted executables. + ElfObject * elfObject = dynamic_cast<ElfObject *>(objFile); + if(elfObject) + { + //Bits which describe the system hardware capabilities + auxv.push_back(buildAuxVect(SPARC_AT_HWCAP, hwcap)); + //The system page size + auxv.push_back(buildAuxVect(SPARC_AT_PAGESZ, SparcISA::VMPageSize)); + //Defined to be 100 in the kernel source. + //Frequency at which times() increments + auxv.push_back(buildAuxVect(SPARC_AT_CLKTCK, 100)); + // For statically linked executables, this is the virtual address of the + // program header tables if they appear in the executable image + auxv.push_back(buildAuxVect(SPARC_AT_PHDR, elfObject->programHeaderTable())); + // This is the size of a program header entry from the elf file. + auxv.push_back(buildAuxVect(SPARC_AT_PHENT, elfObject->programHeaderSize())); + // This is the number of program headers from the original elf file. + auxv.push_back(buildAuxVect(SPARC_AT_PHNUM, elfObject->programHeaderCount())); + //This is the address of the elf "interpreter", It should be set + //to 0 for regular executables. It should be something else + //(not sure what) for dynamic libraries. + auxv.push_back(buildAuxVect(SPARC_AT_BASE, 0)); + //This is hardwired to 0 in the elf loading code in the kernel + auxv.push_back(buildAuxVect(SPARC_AT_FLAGS, 0)); + //The entry point to the program + auxv.push_back(buildAuxVect(SPARC_AT_ENTRY, objFile->entryPoint())); + //Different user and group IDs + auxv.push_back(buildAuxVect(SPARC_AT_UID, uid())); + auxv.push_back(buildAuxVect(SPARC_AT_EUID, euid())); + auxv.push_back(buildAuxVect(SPARC_AT_GID, gid())); + auxv.push_back(buildAuxVect(SPARC_AT_EGID, egid())); + //Whether to enable "secure mode" in the executable + auxv.push_back(buildAuxVect(SPARC_AT_SECURE, 0)); + } + + //Figure out how big the initial stack needs to be + + // The unaccounted for 0 at the top of the stack + int mysterious_size = intSize; + + //This is the name of the file which is present on the initial stack + //It's purpose is to let the user space linker examine the original file. + int file_name_size = filename.size() + 1; + + int env_data_size = 0; + for (int i = 0; i < envp.size(); ++i) { + env_data_size += envp[i].size() + 1; + } + int arg_data_size = 0; + for (int i = 0; i < argv.size(); ++i) { + arg_data_size += argv[i].size() + 1; + } + + //The info_block needs to be padded so it's size is a multiple of the + //alignment mask. Also, it appears that there needs to be at least some + //padding, so if the size is already a multiple, we need to increase it + //anyway. + int info_block_size = + (file_name_size + + env_data_size + + arg_data_size + + intSize) & alignmentMask; + + int info_block_padding = + info_block_size - + file_name_size - + env_data_size - + arg_data_size; + + //Each auxilliary vector is two 8 byte words + int aux_array_size = intSize * 2 * (auxv.size() + 1); + + int envp_array_size = intSize * (envp.size() + 1); + int argv_array_size = intSize * (argv.size() + 1); + + int argc_size = intSize; + int window_save_size = intSize * 16; + + int space_needed = + mysterious_size + + info_block_size + + aux_array_size + + envp_array_size + + argv_array_size + + argc_size + + window_save_size; + + stack_min = stack_base - space_needed; + stack_min &= alignmentMask; + stack_size = stack_base - stack_min; + + // map memory + pTable->allocate(roundDown(stack_min, pageSize), + roundUp(stack_size, pageSize)); + + // map out initial stack contents + Addr mysterious_base = stack_base - mysterious_size; + Addr file_name_base = mysterious_base - file_name_size; + Addr env_data_base = file_name_base - env_data_size; + Addr arg_data_base = env_data_base - arg_data_size; + Addr auxv_array_base = arg_data_base - aux_array_size - info_block_padding; + Addr envp_array_base = auxv_array_base - envp_array_size; + Addr argv_array_base = envp_array_base - argv_array_size; + Addr argc_base = argv_array_base - argc_size; +#ifndef NDEBUG + // only used in DPRINTF + Addr window_save_base = argc_base - window_save_size; +#endif + + DPRINTF(Sparc, "The addresses of items on the initial stack:\n"); + DPRINTF(Sparc, "0x%x - file name\n", file_name_base); + DPRINTF(Sparc, "0x%x - env data\n", env_data_base); + DPRINTF(Sparc, "0x%x - arg data\n", arg_data_base); + DPRINTF(Sparc, "0x%x - auxv array\n", auxv_array_base); + DPRINTF(Sparc, "0x%x - envp array\n", envp_array_base); + DPRINTF(Sparc, "0x%x - argv array\n", argv_array_base); + DPRINTF(Sparc, "0x%x - argc \n", argc_base); + DPRINTF(Sparc, "0x%x - window save\n", window_save_base); + DPRINTF(Sparc, "0x%x - stack min\n", stack_min); + + // write contents to stack + + // figure out argc + uint64_t argc = argv.size(); + uint64_t guestArgc = TheISA::htog(argc); + + //Write out the mysterious 0 + uint64_t mysterious_zero = 0; + initVirtMem->writeBlob(mysterious_base, + (uint8_t*)&mysterious_zero, mysterious_size); + + //Write the file name + initVirtMem->writeString(file_name_base, filename.c_str()); + + //Copy the aux stuff + for(int x = 0; x < auxv.size(); x++) + { + initVirtMem->writeBlob(auxv_array_base + x * 2 * intSize, + (uint8_t*)&(auxv[x].a_type), intSize); + initVirtMem->writeBlob(auxv_array_base + (x * 2 + 1) * intSize, + (uint8_t*)&(auxv[x].a_val), intSize); + } + //Write out the terminating zeroed auxilliary vector + const uint64_t zero = 0; + initVirtMem->writeBlob(auxv_array_base + 2 * intSize * auxv.size(), + (uint8_t*)&zero, 2 * intSize); + + copyStringArray(envp, envp_array_base, env_data_base, initVirtMem); + copyStringArray(argv, argv_array_base, arg_data_base, initVirtMem); + + initVirtMem->writeBlob(argc_base, (uint8_t*)&guestArgc, intSize); + + threadContexts[0]->setIntReg(ArgumentReg0, argc); + threadContexts[0]->setIntReg(ArgumentReg1, argv_array_base); + threadContexts[0]->setIntReg(StackPointerReg, stack_min - StackBias); + + Addr prog_entry = objFile->entryPoint(); + threadContexts[0]->setPC(prog_entry); + threadContexts[0]->setNextPC(prog_entry + sizeof(MachInst)); + threadContexts[0]->setNextNPC(prog_entry + (2 * sizeof(MachInst))); + +// num_processes++; +} diff --git a/src/arch/sparc/process.hh b/src/arch/sparc/process.hh new file mode 100644 index 000000000..7cc52e241 --- /dev/null +++ b/src/arch/sparc/process.hh @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2003-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + */ + +#ifndef __SPARC_PROCESS_HH__ +#define __SPARC_PROCESS_HH__ + +#include <string> +#include <vector> +#include "sim/process.hh" + +class ObjectFile; +class System; + +typedef struct +{ + int64_t a_type; + union { + int64_t a_val; + Addr a_ptr; + Addr a_fcn; + }; +} m5_auxv_t; + +class SparcLiveProcess : public LiveProcess +{ + protected: + + static const Addr StackBias = 2047; + + std::vector<m5_auxv_t> auxv; + + SparcLiveProcess(const std::string &nm, ObjectFile *objFile, + System *_system, int stdin_fd, int stdout_fd, int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid); + + void startup(); + + public: + + void argsInit(int intSize, int pageSize); + +}; + +#endif // __SPARC_PROCESS_HH__ diff --git a/src/arch/sparc/regfile.cc b/src/arch/sparc/regfile.cc new file mode 100644 index 000000000..747426781 --- /dev/null +++ b/src/arch/sparc/regfile.cc @@ -0,0 +1,279 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + */ + +#include "arch/sparc/regfile.hh" +#include "cpu/thread_context.hh" + +class Checkpoint; + +using namespace SparcISA; +using namespace std; + +//RegFile class methods +Addr RegFile::readPC() +{ + return pc; +} + +void RegFile::setPC(Addr val) +{ + pc = val; +} + +Addr RegFile::readNextPC() +{ + return npc; +} + +void RegFile::setNextPC(Addr val) +{ + npc = val; +} + +Addr RegFile::readNextNPC() +{ + return nnpc; +} + +void RegFile::setNextNPC(Addr val) +{ + nnpc = val; +} + +void RegFile::clear() +{ + intRegFile.clear(); + floatRegFile.clear(); +} + +MiscReg RegFile::readMiscReg(int miscReg) +{ + return miscRegFile.readReg(miscReg); +} + +MiscReg RegFile::readMiscRegWithEffect(int miscReg, + Fault &fault, ThreadContext *tc) +{ + return miscRegFile.readRegWithEffect(miscReg, fault, tc); +} + +Fault RegFile::setMiscReg(int miscReg, const MiscReg &val) +{ + return miscRegFile.setReg(miscReg, val); +} + +Fault RegFile::setMiscRegWithEffect(int miscReg, const MiscReg &val, + ThreadContext * tc) +{ + return miscRegFile.setRegWithEffect(miscReg, val, tc); +} + +FloatReg RegFile::readFloatReg(int floatReg, int width) +{ + return floatRegFile.readReg(floatReg, width); +} + +FloatReg RegFile::readFloatReg(int floatReg) +{ + //Use the "natural" width of a single float + return floatRegFile.readReg(floatReg, FloatRegFile::SingleWidth); +} + +FloatRegBits RegFile::readFloatRegBits(int floatReg, int width) +{ + return floatRegFile.readRegBits(floatReg, width); +} + +FloatRegBits RegFile::readFloatRegBits(int floatReg) +{ + //Use the "natural" width of a single float + return floatRegFile.readRegBits(floatReg, + FloatRegFile::SingleWidth); +} + +Fault RegFile::setFloatReg(int floatReg, const FloatReg &val, int width) +{ + return floatRegFile.setReg(floatReg, val, width); +} + +Fault RegFile::setFloatReg(int floatReg, const FloatReg &val) +{ + //Use the "natural" width of a single float + return setFloatReg(floatReg, val, FloatRegFile::SingleWidth); +} + +Fault RegFile::setFloatRegBits(int floatReg, const FloatRegBits &val, int width) +{ + return floatRegFile.setRegBits(floatReg, val, width); +} + +Fault RegFile::setFloatRegBits(int floatReg, const FloatRegBits &val) +{ + //Use the "natural" width of a single float + return floatRegFile.setRegBits(floatReg, val, + FloatRegFile::SingleWidth); +} + +IntReg RegFile::readIntReg(int intReg) +{ + return intRegFile.readReg(intReg); +} + +Fault RegFile::setIntReg(int intReg, const IntReg &val) +{ + return intRegFile.setReg(intReg, val); +} + +void RegFile::serialize(std::ostream &os) +{ + intRegFile.serialize(os); + floatRegFile.serialize(os); + miscRegFile.serialize(os); + SERIALIZE_SCALAR(pc); + SERIALIZE_SCALAR(npc); +} + +void RegFile::unserialize(Checkpoint *cp, const std::string §ion) +{ + intRegFile.unserialize(cp, section); + floatRegFile.unserialize(cp, section); + miscRegFile.unserialize(cp, section); + UNSERIALIZE_SCALAR(pc); + UNSERIALIZE_SCALAR(npc); +} + +void RegFile::changeContext(RegContextParam param, RegContextVal val) +{ + switch(param) + { + case CONTEXT_CWP: + intRegFile.setCWP(val); + break; + case CONTEXT_GLOBALS: + intRegFile.setGlobals(val); + break; + default: + panic("Tried to set illegal context parameter in the SPARC regfile.\n"); + } +} + +int SparcISA::InterruptLevel(uint64_t softint) +{ + if (softint & 0x10000 || softint & 0x1) + return 14; + + int level = 14; + while (level >= 0 && !(1 << (level + 1) & softint)) + level--; + if (1 << (level + 1) & softint) + return level; + return 0; +} + +void SparcISA::copyMiscRegs(ThreadContext *src, ThreadContext *dest) +{ + + uint8_t tl = src->readMiscReg(MISCREG_TL); + + // Read all the trap level dependent registers and save them off + for(int i = 1; i <= MaxTL; i++) + { + src->setMiscReg(MISCREG_TL, i); + dest->setMiscReg(MISCREG_TL, i); + + dest->setMiscReg(MISCREG_TT, src->readMiscReg(MISCREG_TT)); + dest->setMiscReg(MISCREG_TPC, src->readMiscReg(MISCREG_TPC)); + dest->setMiscReg(MISCREG_TNPC, src->readMiscReg(MISCREG_TNPC)); + dest->setMiscReg(MISCREG_TSTATE, src->readMiscReg(MISCREG_TSTATE)); + } + + // Save off the traplevel + dest->setMiscReg(MISCREG_TL, tl); + src->setMiscReg(MISCREG_TL, tl); + + + // ASRs + dest->setMiscReg(MISCREG_Y, src->readMiscReg(MISCREG_Y)); + dest->setMiscReg(MISCREG_CCR, src->readMiscReg(MISCREG_CCR)); + dest->setMiscReg(MISCREG_ASI, src->readMiscReg(MISCREG_ASI)); + dest->setMiscReg(MISCREG_TICK, src->readMiscReg(MISCREG_TICK)); + dest->setMiscReg(MISCREG_FPRS, src->readMiscReg(MISCREG_FPRS)); + dest->setMiscReg(MISCREG_SOFTINT, src->readMiscReg(MISCREG_SOFTINT)); + dest->setMiscReg(MISCREG_TICK_CMPR, src->readMiscReg(MISCREG_TICK_CMPR)); + dest->setMiscReg(MISCREG_STICK, src->readMiscReg(MISCREG_STICK)); + dest->setMiscReg(MISCREG_STICK_CMPR, src->readMiscReg(MISCREG_STICK_CMPR)); + + // Priv Registers + dest->setMiscReg(MISCREG_TICK, src->readMiscReg(MISCREG_TICK)); + dest->setMiscReg(MISCREG_TBA, src->readMiscReg(MISCREG_TBA)); + dest->setMiscReg(MISCREG_PSTATE, src->readMiscReg(MISCREG_PSTATE)); + dest->setMiscReg(MISCREG_PIL, src->readMiscReg(MISCREG_PIL)); + dest->setMiscReg(MISCREG_CWP, src->readMiscReg(MISCREG_CWP)); + dest->setMiscReg(MISCREG_CANSAVE, src->readMiscReg(MISCREG_CANSAVE)); + dest->setMiscReg(MISCREG_CANRESTORE, src->readMiscReg(MISCREG_CANRESTORE)); + dest->setMiscReg(MISCREG_OTHERWIN, src->readMiscReg(MISCREG_OTHERWIN)); + dest->setMiscReg(MISCREG_CLEANWIN, src->readMiscReg(MISCREG_CLEANWIN)); + dest->setMiscReg(MISCREG_WSTATE, src->readMiscReg(MISCREG_WSTATE)); + dest->setMiscReg(MISCREG_GL, src->readMiscReg(MISCREG_GL)); + + // Hyperprivilged registers + dest->setMiscReg(MISCREG_HPSTATE, src->readMiscReg(MISCREG_HPSTATE)); + dest->setMiscReg(MISCREG_HINTP, src->readMiscReg(MISCREG_HINTP)); + dest->setMiscReg(MISCREG_HTBA, src->readMiscReg(MISCREG_HTBA)); + dest->setMiscReg(MISCREG_STRAND_STS_REG, + src->readMiscReg(MISCREG_STRAND_STS_REG)); + dest->setMiscReg(MISCREG_HSTICK_CMPR, + src->readMiscReg(MISCREG_HSTICK_CMPR)); + + // FSR + dest->setMiscReg(MISCREG_FSR, src->readMiscReg(MISCREG_FSR)); +} + +void SparcISA::copyRegs(ThreadContext *src, ThreadContext *dest) +{ + // First loop through the integer registers. + for (int i = 0; i < TheISA::NumIntRegs; ++i) { + dest->setIntReg(i, src->readIntReg(i)); + } + + // Then loop through the floating point registers. + for (int i = 0; i < TheISA::NumFloatRegs; ++i) { + dest->setFloatRegBits(i, src->readFloatRegBits(i)); + } + + // Copy misc. registers + copyMiscRegs(src, dest); + + // Lastly copy PC/NPC + dest->setPC(src->readPC()); + dest->setNextPC(src->readNextPC()); + dest->setNextNPC(src->readNextNPC()); +} diff --git a/src/arch/sparc/regfile.hh b/src/arch/sparc/regfile.hh new file mode 100644 index 000000000..500fbbba4 --- /dev/null +++ b/src/arch/sparc/regfile.hh @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + */ + +#ifndef __ARCH_SPARC_REGFILE_HH__ +#define __ARCH_SPARC_REGFILE_HH__ + +#include "arch/sparc/faults.hh" +#include "arch/sparc/floatregfile.hh" +#include "arch/sparc/intregfile.hh" +#include "arch/sparc/isa_traits.hh" +#include "arch/sparc/miscregfile.hh" +#include "arch/sparc/types.hh" +#include "sim/host.hh" + +#include <string> + +class Checkpoint; + +namespace SparcISA +{ + class RegFile + { + protected: + Addr pc; // Program Counter + Addr npc; // Next Program Counter + Addr nnpc; + + public: + Addr readPC(); + void setPC(Addr val); + + Addr readNextPC(); + void setNextPC(Addr val); + + Addr readNextNPC(); + void setNextNPC(Addr val); + + protected: + IntRegFile intRegFile; // integer register file + FloatRegFile floatRegFile; // floating point register file + MiscRegFile miscRegFile; // control register file + + public: + + void clear(); + + int FlattenIntIndex(int reg); + + MiscReg readMiscReg(int miscReg); + + MiscReg readMiscRegWithEffect(int miscReg, + Fault &fault, ThreadContext *tc); + + Fault setMiscReg(int miscReg, const MiscReg &val); + + Fault setMiscRegWithEffect(int miscReg, const MiscReg &val, + ThreadContext * tc); + + FloatReg readFloatReg(int floatReg, int width); + + FloatReg readFloatReg(int floatReg); + + FloatRegBits readFloatRegBits(int floatReg, int width); + + FloatRegBits readFloatRegBits(int floatReg); + + Fault setFloatReg(int floatReg, const FloatReg &val, int width); + + Fault setFloatReg(int floatReg, const FloatReg &val); + + Fault setFloatRegBits(int floatReg, const FloatRegBits &val, int width); + + Fault setFloatRegBits(int floatReg, const FloatRegBits &val); + + IntReg readIntReg(int intReg); + + Fault setIntReg(int intReg, const IntReg &val); + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + public: + + void changeContext(RegContextParam param, RegContextVal val); + }; + + void copyRegs(ThreadContext *src, ThreadContext *dest); + + void copyMiscRegs(ThreadContext *src, ThreadContext *dest); + + int InterruptLevel(uint64_t softint); + +} // namespace SparcISA + +#endif diff --git a/src/arch/sparc/solaris/process.cc b/src/arch/sparc/solaris/process.cc new file mode 100644 index 000000000..ff466c8e6 --- /dev/null +++ b/src/arch/sparc/solaris/process.cc @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#include "arch/sparc/isa_traits.hh" +#include "arch/sparc/solaris/process.hh" +#include "arch/sparc/regfile.hh" + +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "kern/solaris/solaris.hh" + +#include "sim/process.hh" +#include "sim/syscall_emul.hh" + +using namespace std; +using namespace SparcISA; + + +/// Target uname() handler. +static SyscallReturn +unameFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + TypedBufferArg<Solaris::utsname> name(tc->getSyscallArg(0)); + + strcpy(name->sysname, "SunOS"); + strcpy(name->nodename, "m5.eecs.umich.edu"); + strcpy(name->release, "5.9"); //?? do we want this or something newer? + strcpy(name->version, "Generic_118558-21"); + strcpy(name->machine, "sun4u"); + + name.copyOut(tc->getMemPort()); + + return 0; +} + + +SyscallDesc SparcSolarisProcess::syscallDescs[] = { + /* 0 */ SyscallDesc("syscall", unimplementedFunc), + /* 1 */ SyscallDesc("exit", exitFunc), + /* 2 */ SyscallDesc("fork", unimplementedFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), + /* 5 */ SyscallDesc("open", openFunc<SparcSolaris>), + /* 6 */ SyscallDesc("close", closeFunc), + /* 7 */ SyscallDesc("wait", unimplementedFunc), + /* 8 */ SyscallDesc("creat", unimplementedFunc), + /* 9 */ SyscallDesc("link", unimplementedFunc), + /* 10 */ SyscallDesc("unlink", unlinkFunc), + /* 11 */ SyscallDesc("exec", unimplementedFunc), + /* 12 */ SyscallDesc("chdir", unimplementedFunc), + /* 13 */ SyscallDesc("time", unimplementedFunc), + /* 14 */ SyscallDesc("mknod", unimplementedFunc), + /* 15 */ SyscallDesc("chmod", chmodFunc<Solaris>), + /* 16 */ SyscallDesc("chown", chownFunc), + /* 17 */ SyscallDesc("brk", obreakFunc), + /* 18 */ SyscallDesc("stat", unimplementedFunc), + /* 19 */ SyscallDesc("lseek", lseekFunc), + /* 20 */ SyscallDesc("getpid", getpidFunc), + /* 21 */ SyscallDesc("mount", unimplementedFunc), + /* 22 */ SyscallDesc("umount", unimplementedFunc), + /* 23 */ SyscallDesc("setuid", setuidFunc), + /* 24 */ SyscallDesc("getuid", getuidFunc), + /* 25 */ SyscallDesc("stime", unimplementedFunc), + /* 26 */ SyscallDesc("pcsample", unimplementedFunc), + /* 27 */ SyscallDesc("alarm", unimplementedFunc), + /* 28 */ SyscallDesc("fstat", fstatFunc<SparcSolaris>), + /* 29 */ SyscallDesc("pause", unimplementedFunc), + /* 30 */ SyscallDesc("utime", unimplementedFunc), + /* 31 */ SyscallDesc("stty", unimplementedFunc), + /* 32 */ SyscallDesc("gtty", unimplementedFunc), + /* 33 */ SyscallDesc("access", unimplementedFunc), + /* 34 */ SyscallDesc("nice", unimplementedFunc), + /* 35 */ SyscallDesc("statfs", unimplementedFunc), + /* 36 */ SyscallDesc("sync", unimplementedFunc), + /* 37 */ SyscallDesc("kill", unimplementedFunc), + /* 38 */ SyscallDesc("fstatfs", unimplementedFunc), + /* 39 */ SyscallDesc("pgrpsys", unimplementedFunc), + /* 40 */ SyscallDesc("xenix", unimplementedFunc), + /* 41 */ SyscallDesc("dup", unimplementedFunc), + /* 42 */ SyscallDesc("pipe", pipePseudoFunc), + /* 43 */ SyscallDesc("times", unimplementedFunc), + /* 44 */ SyscallDesc("profil", unimplementedFunc), + /* 45 */ SyscallDesc("plock", unimplementedFunc), + /* 46 */ SyscallDesc("setgid", unimplementedFunc), + /* 47 */ SyscallDesc("getgid", getgidFunc), + /* 48 */ SyscallDesc("signal", unimplementedFunc), + /* 49 */ SyscallDesc("msgsys", unimplementedFunc), + /* 50 */ SyscallDesc("syssun", unimplementedFunc), + /* 51 */ SyscallDesc("acct", unimplementedFunc), + /* 52 */ SyscallDesc("shmsys", unimplementedFunc), + /* 53 */ SyscallDesc("semsys", unimplementedFunc), + /* 54 */ SyscallDesc("ioctl", unimplementedFunc), + /* 55 */ SyscallDesc("uadmin", unimplementedFunc), + /* 56 */ SyscallDesc("RESERVED", unimplementedFunc), + /* 57 */ SyscallDesc("utssys", unimplementedFunc), + /* 58 */ SyscallDesc("fdsync", unimplementedFunc), + /* 59 */ SyscallDesc("execve", unimplementedFunc), + /* 60 */ SyscallDesc("umask", unimplementedFunc), + /* 61 */ SyscallDesc("chroot", unimplementedFunc), + /* 62 */ SyscallDesc("fcntl", unimplementedFunc), + /* 63 */ SyscallDesc("ulimit", unimplementedFunc), + /* 64 */ SyscallDesc("reserved_64", unimplementedFunc), + /* 65 */ SyscallDesc("reserved_65", unimplementedFunc), + /* 66 */ SyscallDesc("reserved_66", unimplementedFunc), + /* 67 */ SyscallDesc("reserved_67", unimplementedFunc), + /* 68 */ SyscallDesc("reserved_68", unimplementedFunc), + /* 69 */ SyscallDesc("reserved_69", unimplementedFunc), + /* 70 */ SyscallDesc("tasksys", unimplementedFunc), + /* 71 */ SyscallDesc("acctctl", unimplementedFunc), + /* 72 */ SyscallDesc("reserved_72", unimplementedFunc), + /* 73 */ SyscallDesc("getpagesizes", unimplementedFunc), + /* 74 */ SyscallDesc("rctlsys", unimplementedFunc), + /* 75 */ SyscallDesc("issetugid", unimplementedFunc), + /* 76 */ SyscallDesc("fsat", unimplementedFunc), + /* 77 */ SyscallDesc("lwp_park", unimplementedFunc), + /* 78 */ SyscallDesc("sendfilev", unimplementedFunc), + /* 79 */ SyscallDesc("rmdir", unimplementedFunc), + /* 80 */ SyscallDesc("mkdir", unimplementedFunc), + /* 81 */ SyscallDesc("getdents", unimplementedFunc), + /* 82 */ SyscallDesc("reserved_82", unimplementedFunc), + /* 83 */ SyscallDesc("reserved_83", unimplementedFunc), + /* 84 */ SyscallDesc("sysfs", unimplementedFunc), + /* 85 */ SyscallDesc("getmsg", unimplementedFunc), + /* 86 */ SyscallDesc("putmsg", unimplementedFunc), + /* 87 */ SyscallDesc("poll", unimplementedFunc), + /* 88 */ SyscallDesc("lstat", unimplementedFunc), + /* 89 */ SyscallDesc("symlink", unimplementedFunc), + /* 90 */ SyscallDesc("readlink", unimplementedFunc), + /* 91 */ SyscallDesc("setgroups", unimplementedFunc), + /* 92 */ SyscallDesc("getgroups", unimplementedFunc), + /* 93 */ SyscallDesc("fchmod", unimplementedFunc), + /* 94 */ SyscallDesc("fchown", unimplementedFunc), + /* 95 */ SyscallDesc("sigprocmask", unimplementedFunc), + /* 96 */ SyscallDesc("sigsuspend", unimplementedFunc), + /* 97 */ SyscallDesc("sigaltstack", unimplementedFunc), + /* 98 */ SyscallDesc("sigaction", unimplementedFunc), + /* 99 */ SyscallDesc("sigpending", unimplementedFunc), + /* 100 */ SyscallDesc("context", unimplementedFunc), + /* 101 */ SyscallDesc("evsys", unimplementedFunc), + /* 102 */ SyscallDesc("evtrapret", unimplementedFunc), + /* 103 */ SyscallDesc("statvfs", unimplementedFunc), + /* 104 */ SyscallDesc("fstatvfs", unimplementedFunc), + /* 105 */ SyscallDesc("getloadavg", unimplementedFunc), + /* 106 */ SyscallDesc("nfssys", unimplementedFunc), + /* 107 */ SyscallDesc("waitsys", unimplementedFunc), + /* 108 */ SyscallDesc("sigsendsys", unimplementedFunc), + /* 109 */ SyscallDesc("hrtsys", unimplementedFunc), + /* 110 */ SyscallDesc("acancel", unimplementedFunc), + /* 111 */ SyscallDesc("async", unimplementedFunc), + /* 112 */ SyscallDesc("priocntlsys", unimplementedFunc), + /* 113 */ SyscallDesc("pathconf", unimplementedFunc), + /* 114 */ SyscallDesc("mincore", unimplementedFunc), + /* 115 */ SyscallDesc("mmap", mmapFunc<SparcSolaris>), + /* 116 */ SyscallDesc("mprotect", unimplementedFunc), + /* 117 */ SyscallDesc("munmap", munmapFunc), + /* 118 */ SyscallDesc("fpathconf", unimplementedFunc), + /* 119 */ SyscallDesc("vfork", unimplementedFunc), + /* 120 */ SyscallDesc("fchdir", unimplementedFunc), + /* 121 */ SyscallDesc("readv", unimplementedFunc), + /* 122 */ SyscallDesc("writev", unimplementedFunc), + /* 123 */ SyscallDesc("xstat", unimplementedFunc), + /* 124 */ SyscallDesc("lxstat", unimplementedFunc), + /* 125 */ SyscallDesc("fxstat", unimplementedFunc), + /* 126 */ SyscallDesc("xmknod", unimplementedFunc), + /* 127 */ SyscallDesc("clocal", unimplementedFunc), + /* 128 */ SyscallDesc("setrlimit", unimplementedFunc), + /* 129 */ SyscallDesc("getrlimit", unimplementedFunc), + /* 130 */ SyscallDesc("lchown", unimplementedFunc), + /* 131 */ SyscallDesc("memcntl", unimplementedFunc), + /* 132 */ SyscallDesc("getpmsg", unimplementedFunc), + /* 133 */ SyscallDesc("putpmsg", unimplementedFunc), + /* 134 */ SyscallDesc("rename", unimplementedFunc), + /* 135 */ SyscallDesc("uname", unameFunc), + /* 136 */ SyscallDesc("setegid", unimplementedFunc), + /* 137 */ SyscallDesc("sysconfig", unimplementedFunc), + /* 138 */ SyscallDesc("adjtime", unimplementedFunc), + /* 139 */ SyscallDesc("systeminfo", unimplementedFunc), + /* 140 */ SyscallDesc("reserved_140", unimplementedFunc), + /* 141 */ SyscallDesc("seteuid", unimplementedFunc), + /* 142 */ SyscallDesc("vtrace", unimplementedFunc), + /* 143 */ SyscallDesc("fork1", unimplementedFunc), + /* 144 */ SyscallDesc("sigtimedwait", unimplementedFunc), + /* 145 */ SyscallDesc("lwp_info", unimplementedFunc), + /* 146 */ SyscallDesc("yield", unimplementedFunc), + /* 147 */ SyscallDesc("lwp_sema_wait", unimplementedFunc), + /* 148 */ SyscallDesc("lwp_sema_post", unimplementedFunc), + /* 149 */ SyscallDesc("lwp_sema_trywait", unimplementedFunc), + /* 150 */ SyscallDesc("lwp_detach", unimplementedFunc), + /* 151 */ SyscallDesc("corectl", unimplementedFunc), + /* 152 */ SyscallDesc("modctl", unimplementedFunc), + /* 153 */ SyscallDesc("fchroot", unimplementedFunc), + /* 154 */ SyscallDesc("utimes", unimplementedFunc), + /* 155 */ SyscallDesc("vhangup", unimplementedFunc), + /* 156 */ SyscallDesc("gettimeofday", unimplementedFunc), + /* 157 */ SyscallDesc("getitimer", unimplementedFunc), + /* 158 */ SyscallDesc("setitimer", unimplementedFunc), + /* 159 */ SyscallDesc("lwp_create", unimplementedFunc), + /* 160 */ SyscallDesc("lwp_exit", unimplementedFunc), + /* 161 */ SyscallDesc("lwp_suspend", unimplementedFunc), + /* 162 */ SyscallDesc("lwp_continue", unimplementedFunc), + /* 163 */ SyscallDesc("lwp_kill", unimplementedFunc), + /* 164 */ SyscallDesc("lwp_self", unimplementedFunc), + /* 165 */ SyscallDesc("lwp_setprivate", unimplementedFunc), + /* 166 */ SyscallDesc("lwp_getprivate", unimplementedFunc), + /* 167 */ SyscallDesc("lwp_wait", unimplementedFunc), + /* 168 */ SyscallDesc("lwp_mutex_wakeup", unimplementedFunc), + /* 169 */ SyscallDesc("lwp_mutex_lock", unimplementedFunc), + /* 170 */ SyscallDesc("lwp_cond_wait", unimplementedFunc), + /* 171 */ SyscallDesc("lwp_cond_signal", unimplementedFunc), + /* 172 */ SyscallDesc("lwp_cond_broadcast", unimplementedFunc), + /* 173 */ SyscallDesc("pread", unimplementedFunc), + /* 174 */ SyscallDesc("pwrite", unimplementedFunc), + /* 175 */ SyscallDesc("llseek", unimplementedFunc), + /* 176 */ SyscallDesc("inst_sync", unimplementedFunc), + /* 177 */ SyscallDesc("srmlimitsys", unimplementedFunc), + /* 178 */ SyscallDesc("kaio", unimplementedFunc), + /* 179 */ SyscallDesc("cpc", unimplementedFunc), + /* 180 */ SyscallDesc("lgrpsys_meminfosys", unimplementedFunc), + /* 181 */ SyscallDesc("rusagesys", unimplementedFunc), + /* 182 */ SyscallDesc("reserved_182", unimplementedFunc), + /* 183 */ SyscallDesc("reserved_183", unimplementedFunc), + /* 184 */ SyscallDesc("tsolsys", unimplementedFunc), + /* 185 */ SyscallDesc("acl", unimplementedFunc), + /* 186 */ SyscallDesc("auditsys", unimplementedFunc), + /* 187 */ SyscallDesc("processor_bind", unimplementedFunc), + /* 188 */ SyscallDesc("processor_info", unimplementedFunc), + /* 189 */ SyscallDesc("p_online", unimplementedFunc), + /* 190 */ SyscallDesc("sigqueue", unimplementedFunc), + /* 191 */ SyscallDesc("clock_gettime", unimplementedFunc), + /* 192 */ SyscallDesc("clock_settime", unimplementedFunc), + /* 193 */ SyscallDesc("clock_getres", unimplementedFunc), + /* 194 */ SyscallDesc("timer_create", unimplementedFunc), + /* 195 */ SyscallDesc("timer_delete", unimplementedFunc), + /* 196 */ SyscallDesc("timer_settime", unimplementedFunc), + /* 197 */ SyscallDesc("timer_gettime", unimplementedFunc), + /* 198 */ SyscallDesc("timer_getoverrun", unimplementedFunc), + /* 199 */ SyscallDesc("nanosleep", unimplementedFunc), + /* 200 */ SyscallDesc("facl", unimplementedFunc), + /* 201 */ SyscallDesc("door", unimplementedFunc), + /* 202 */ SyscallDesc("setreuid", unimplementedFunc), + /* 203 */ SyscallDesc("setregid", unimplementedFunc), + /* 204 */ SyscallDesc("install_utrap", unimplementedFunc), + /* 205 */ SyscallDesc("signotify", unimplementedFunc), + /* 206 */ SyscallDesc("schedctl", unimplementedFunc), + /* 207 */ SyscallDesc("pset", unimplementedFunc), + /* 208 */ SyscallDesc("sparc_utrap_install", unimplementedFunc), + /* 209 */ SyscallDesc("resolvepath", unimplementedFunc), + /* 210 */ SyscallDesc("signotifywait", unimplementedFunc), + /* 211 */ SyscallDesc("lwp_sigredirect", unimplementedFunc), + /* 212 */ SyscallDesc("lwp_alarm", unimplementedFunc), + /* 213 */ SyscallDesc("getdents64", unimplementedFunc), + /* 214 */ SyscallDesc("mmap64", unimplementedFunc), + /* 215 */ SyscallDesc("stat64", unimplementedFunc), + /* 216 */ SyscallDesc("lstat64", unimplementedFunc), + /* 217 */ SyscallDesc("fstat64", unimplementedFunc), + /* 218 */ SyscallDesc("statvfs64", unimplementedFunc), + /* 219 */ SyscallDesc("fstatvfs64", unimplementedFunc), + /* 220 */ SyscallDesc("setrlimit64", unimplementedFunc), + /* 221 */ SyscallDesc("getrlimit64", unimplementedFunc), + /* 222 */ SyscallDesc("pread64", unimplementedFunc), + /* 223 */ SyscallDesc("pwrite64", unimplementedFunc), + /* 224 */ SyscallDesc("creat64", unimplementedFunc), + /* 225 */ SyscallDesc("open64", unimplementedFunc), + /* 226 */ SyscallDesc("rpcsys", unimplementedFunc), + /* 227 */ SyscallDesc("reserved_227", unimplementedFunc), + /* 228 */ SyscallDesc("reserved_228", unimplementedFunc), + /* 229 */ SyscallDesc("reserved_229", unimplementedFunc), + /* 230 */ SyscallDesc("so_socket", unimplementedFunc), + /* 231 */ SyscallDesc("so_socketpair", unimplementedFunc), + /* 232 */ SyscallDesc("bind", unimplementedFunc), + /* 233 */ SyscallDesc("listen", unimplementedFunc), + /* 234 */ SyscallDesc("accept", unimplementedFunc), + /* 235 */ SyscallDesc("connect", unimplementedFunc), + /* 236 */ SyscallDesc("shutdown", unimplementedFunc), + /* 237 */ SyscallDesc("recv", unimplementedFunc), + /* 238 */ SyscallDesc("recvfrom", unimplementedFunc), + /* 239 */ SyscallDesc("recvmsg", unimplementedFunc), + /* 240 */ SyscallDesc("send", unimplementedFunc), + /* 241 */ SyscallDesc("sendmsg", unimplementedFunc), + /* 242 */ SyscallDesc("sendto", unimplementedFunc), + /* 243 */ SyscallDesc("getpeername", unimplementedFunc), + /* 244 */ SyscallDesc("getsockname", unimplementedFunc), + /* 245 */ SyscallDesc("getsockopt", unimplementedFunc), + /* 246 */ SyscallDesc("setsockopt", unimplementedFunc), + /* 247 */ SyscallDesc("sockconfig", unimplementedFunc), + /* 248 */ SyscallDesc("ntp_gettime", unimplementedFunc), + /* 249 */ SyscallDesc("ntp_adjtime", unimplementedFunc), + /* 250 */ SyscallDesc("lwp_mutex_unlock", unimplementedFunc), + /* 251 */ SyscallDesc("lwp_mutex_trylock", unimplementedFunc), + /* 252 */ SyscallDesc("lwp_mutex_init", unimplementedFunc), + /* 253 */ SyscallDesc("cladm", unimplementedFunc), + /* 254 */ SyscallDesc("lwp_sigtimedwait", unimplementedFunc), + /* 255 */ SyscallDesc("umount2", unimplementedFunc) +}; + +SparcSolarisProcess::SparcSolarisProcess(const std::string &name, + ObjectFile *objFile, + System * system, + int stdin_fd, + int stdout_fd, + int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, + uint64_t _euid, + uint64_t _gid, + uint64_t _egid, + uint64_t _pid, + uint64_t _ppid) + : SparcLiveProcess(name, objFile, system, + stdin_fd, stdout_fd, stderr_fd, argv, envp, + _uid, _euid, _gid, _egid, _pid, _ppid), + Num_Syscall_Descs(sizeof(syscallDescs) / sizeof(SyscallDesc)) +{ + // The sparc syscall table must be <= 284 entries because that is all there + // is space for. + assert(Num_Syscall_Descs <= 284); +} + + + +SyscallDesc* +SparcSolarisProcess::getDesc(int callnum) +{ + if (callnum < 0 || callnum > Num_Syscall_Descs) + return NULL; + return &syscallDescs[callnum]; +} diff --git a/src/arch/sparc/solaris/process.hh b/src/arch/sparc/solaris/process.hh new file mode 100644 index 000000000..2cf329211 --- /dev/null +++ b/src/arch/sparc/solaris/process.hh @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2003-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#ifndef __SPARC_SOLARIS_PROCESS_HH__ +#define __SPARC_SOLARIS_PROCESS_HH__ + +#include "arch/sparc/solaris/solaris.hh" +#include "arch/sparc/process.hh" +#include "sim/process.hh" + +namespace SparcISA { + +/// A process with emulated SPARC/Solaris syscalls. +class SparcSolarisProcess : public SparcLiveProcess +{ + public: + /// Constructor. + SparcSolarisProcess(const std::string &name, + ObjectFile *objFile, + System * system, + int stdin_fd, int stdout_fd, int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid); + + virtual SyscallDesc* getDesc(int callnum); + + /// The target system's hostname. + static const char *hostname; + + /// Array of syscall descriptors, indexed by call number. + static SyscallDesc syscallDescs[]; + + const int Num_Syscall_Descs; +}; + + +} // namespace SparcISA +#endif // __ALPHA_SOLARIS_PROCESS_HH__ diff --git a/src/arch/sparc/solaris/solaris.cc b/src/arch/sparc/solaris/solaris.cc new file mode 100644 index 000000000..c53caa72a --- /dev/null +++ b/src/arch/sparc/solaris/solaris.cc @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#include "arch/sparc/solaris/solaris.hh" + +#include <fcntl.h> + +// open(2) flags translation table +OpenFlagTransTable SparcSolaris::openFlagTable[] = { +#ifdef _MSC_VER + { SparcSolaris::TGT_O_RDONLY, _O_RDONLY }, + { SparcSolaris::TGT_O_WRONLY, _O_WRONLY }, + { SparcSolaris::TGT_O_RDWR, _O_RDWR }, + { SparcSolaris::TGT_O_APPEND, _O_APPEND }, + { SparcSolaris::TGT_O_CREAT, _O_CREAT }, + { SparcSolaris::TGT_O_TRUNC, _O_TRUNC }, + { SparcSolaris::TGT_O_EXCL, _O_EXCL }, +#ifdef _O_NONBLOCK + { SparcSolaris::TGT_O_NONBLOCK, _O_NONBLOCK }, + { SparcSolaris::TGT_O_NDELAY , _O_NONBLOCK }, +#endif +#ifdef _O_NOCTTY + { SparcSolaris::TGT_O_NOCTTY, _O_NOCTTY }, +#endif +#ifdef _O_SYNC + { SparcSolaris::TGT_O_SYNC, _O_SYNC }, + { SparcSolaris::TGT_O_DSYNC, _O_SYNC }, + { SparcSolaris::TGT_O_RSYNC, _O_SYNC }, +#endif +#else /* !_MSC_VER */ + { SparcSolaris::TGT_O_RDONLY, O_RDONLY }, + { SparcSolaris::TGT_O_WRONLY, O_WRONLY }, + { SparcSolaris::TGT_O_RDWR, O_RDWR }, + { SparcSolaris::TGT_O_APPEND, O_APPEND }, + { SparcSolaris::TGT_O_CREAT, O_CREAT }, + { SparcSolaris::TGT_O_TRUNC, O_TRUNC }, + { SparcSolaris::TGT_O_EXCL, O_EXCL }, + { SparcSolaris::TGT_O_NONBLOCK, O_NONBLOCK }, + { SparcSolaris::TGT_O_NDELAY , O_NONBLOCK }, + { SparcSolaris::TGT_O_NOCTTY, O_NOCTTY }, +#ifdef O_SYNC + { SparcSolaris::TGT_O_SYNC, O_SYNC }, + { SparcSolaris::TGT_O_DSYNC, O_SYNC }, + { SparcSolaris::TGT_O_RSYNC, O_SYNC }, +#endif +#endif /* _MSC_VER */ +}; + +const int SparcSolaris::NUM_OPEN_FLAGS = + (sizeof(SparcSolaris::openFlagTable)/sizeof(SparcSolaris::openFlagTable[0])); + diff --git a/src/arch/sparc/solaris/solaris.hh b/src/arch/sparc/solaris/solaris.hh new file mode 100644 index 000000000..0564faba4 --- /dev/null +++ b/src/arch/sparc/solaris/solaris.hh @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#ifndef __ARCH_SPARC_SOLARIS_SOLARIS_HH__ +#define __ARCH_SPARC_SOLARIS_SOLARIS_HH__ + +#include "kern/solaris/solaris.hh" + +class SparcSolaris : public Solaris +{ + public: + + static OpenFlagTransTable openFlagTable[]; + + static const int TGT_O_RDONLY = 0x00000000; //!< O_RDONLY + static const int TGT_O_WRONLY = 0x00000001; //!< O_WRONLY + static const int TGT_O_RDWR = 0x00000002; //!< O_RDWR + static const int TGT_O_NDELAY = 0x00000004; //!< O_NONBLOCK + static const int TGT_O_APPEND = 0x00000008; //!< O_APPEND + static const int TGT_O_SYNC = 0x00000010; //!< O_SYNC + static const int TGT_O_DSYNC = 0x00000040; //!< O_SYNC + static const int TGT_O_RSYNC = 0x00008000; //!< O_SYNC + static const int TGT_O_NONBLOCK = 0x00000080; //!< O_NONBLOCK + static const int TGT_O_PRIV = 0x00001000; //?? + static const int TGT_O_LARGEFILE = 0x00002000; //?? + static const int TGT_O_CREAT = 0x00000100; //!< O_CREAT + static const int TGT_O_TRUNC = 0x00000200; //!< O_TRUNC + static const int TGT_O_EXCL = 0x00000400; //!< O_EXCL + static const int TGT_O_NOCTTY = 0x00000800; //!< O_NOCTTY + static const int TGT_O_XATTR = 0x00004000; //?? + + static const int NUM_OPEN_FLAGS; + + static const unsigned TGT_MAP_ANONYMOUS = 0x100; +}; + +#endif diff --git a/src/arch/sparc/stacktrace.hh b/src/arch/sparc/stacktrace.hh new file mode 100644 index 000000000..54d3d17be --- /dev/null +++ b/src/arch/sparc/stacktrace.hh @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __ARCH_SPARC_STACKTRACE_HH__ +#define __ARCH_SPARC_STACKTRACE_HH__ + +#include "base/trace.hh" +#include "cpu/static_inst.hh" + +class ThreadContext; +class StackTrace; + +class ProcessInfo +{ + private: + ThreadContext *tc; + + int thread_info_size; + int task_struct_size; + int task_off; + int pid_off; + int name_off; + + public: + ProcessInfo(ThreadContext *_tc); + + Addr task(Addr ksp) const; + int pid(Addr ksp) const; + std::string name(Addr ksp) const; +}; + +class StackTrace +{ + protected: + typedef TheISA::MachInst MachInst; + private: + ThreadContext *tc; + std::vector<Addr> stack; + + private: + bool isEntry(Addr addr); + bool decodePrologue(Addr sp, Addr callpc, Addr func, int &size, Addr &ra); + bool decodeSave(MachInst inst, int ®, int &disp); + bool decodeStack(MachInst inst, int &disp); + + void trace(ThreadContext *tc, bool is_call); + + public: + StackTrace(); + StackTrace(ThreadContext *tc, StaticInstPtr inst); + ~StackTrace(); + + void clear() + { + tc = 0; + stack.clear(); + } + + bool valid() const { return tc != NULL; } + bool trace(ThreadContext *tc, StaticInstPtr inst); + + public: + const std::vector<Addr> &getstack() const { return stack; } + + static const int user = 1; + static const int console = 2; + static const int unknown = 3; + +#if TRACING_ON + private: + void dump(); + + public: + void dprintf() { if (DTRACE(Stack)) dump(); } +#else + public: + void dprintf() {} +#endif +}; + +inline bool +StackTrace::trace(ThreadContext *tc, StaticInstPtr inst) +{ + if (!inst->isCall() && !inst->isReturn()) + return false; + + if (valid()) + clear(); + + trace(tc, !inst->isReturn()); + return true; +} + +#endif // __ARCH_SPARC_STACKTRACE_HH__ diff --git a/src/arch/sparc/syscallreturn.hh b/src/arch/sparc/syscallreturn.hh new file mode 100644 index 000000000..75a063da1 --- /dev/null +++ b/src/arch/sparc/syscallreturn.hh @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + */ + +#ifndef __ARCH_SPARC_SYSCALLRETURN_HH__ +#define __ARCH_SPARC_SYSCALLRETURN_HH__ + +#include <inttypes.h> + +#include "arch/sparc/regfile.hh" + +class SyscallReturn +{ + public: + template <class T> + SyscallReturn(T v, bool s) + { + retval = (uint64_t)v; + success = s; + } + + template <class T> + SyscallReturn(T v) + { + success = (v >= 0); + retval = (uint64_t)v; + } + + ~SyscallReturn() {} + + SyscallReturn& operator=(const SyscallReturn& s) + { + retval = s.retval; + success = s.success; + return *this; + } + + bool successful() { return success; } + uint64_t value() { return retval; } + + private: + uint64_t retval; + bool success; +}; + +namespace SparcISA +{ + static inline void setSyscallReturn(SyscallReturn return_value, + RegFile *regs) + { + // check for error condition. SPARC syscall convention is to + // indicate success/failure in reg the carry bit of the ccr + // and put the return value itself in the standard return value reg (). + if (return_value.successful()) { + // no error, clear XCC.C + regs->setMiscReg(MISCREG_CCR, regs->readMiscReg(MISCREG_CCR) & 0xEE); + regs->setIntReg(ReturnValueReg, return_value.value()); + } else { + // got an error, set XCC.C + regs->setMiscReg(MISCREG_CCR, regs->readMiscReg(MISCREG_CCR) | 0x11); + regs->setIntReg(ReturnValueReg, return_value.value()); + } + } +}; + +#endif diff --git a/src/arch/sparc/system.cc b/src/arch/sparc/system.cc new file mode 100644 index 000000000..63cbbe057 --- /dev/null +++ b/src/arch/sparc/system.cc @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2002-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#include "arch/sparc/system.hh" +#include "arch/vtophys.hh" +#include "base/remote_gdb.hh" +#include "base/loader/object_file.hh" +#include "base/loader/symtab.hh" +#include "base/trace.hh" +#include "mem/physical.hh" +#include "sim/byteswap.hh" +#include "sim/builder.hh" + + +using namespace BigEndianGuest; + +SparcSystem::SparcSystem(Params *p) + : System(p), sysTick(0) + +{ + resetSymtab = new SymbolTable; + hypervisorSymtab = new SymbolTable; + openbootSymtab = new SymbolTable; + + + /** + * Load the boot code, and hypervisor into memory. + */ + // Read the reset binary + reset = createObjectFile(params()->reset_bin); + if (reset == NULL) + fatal("Could not load reset binary %s", params()->reset_bin); + + // Read the openboot binary + openboot = createObjectFile(params()->openboot_bin); + if (openboot == NULL) + fatal("Could not load openboot bianry %s", params()->openboot_bin); + + // Read the hypervisor binary + hypervisor = createObjectFile(params()->hypervisor_bin); + if (hypervisor == NULL) + fatal("Could not load hypervisor binary %s", params()->hypervisor_bin); + + + // Load reset binary into memory + reset->loadSections(&functionalPort, SparcISA::LoadAddrMask); + // Load the openboot binary + openboot->loadSections(&functionalPort, SparcISA::LoadAddrMask); + // Load the hypervisor binary + hypervisor->loadSections(&functionalPort, SparcISA::LoadAddrMask); + + // load symbols + if (!reset->loadGlobalSymbols(reset)) + panic("could not load reset symbols\n"); + + if (!openboot->loadGlobalSymbols(openbootSymtab)) + panic("could not load openboot symbols\n"); + + if (!hypervisor->loadLocalSymbols(hypervisorSymtab)) + panic("could not load hypervisor symbols\n"); + + // load symbols into debug table + if (!reset->loadGlobalSymbols(debugSymbolTable)) + panic("could not load reset symbols\n"); + + if (!openboot->loadGlobalSymbols(debugSymbolTable)) + panic("could not load openboot symbols\n"); + + if (!hypervisor->loadLocalSymbols(debugSymbolTable)) + panic("could not load hypervisor symbols\n"); + + + // @todo any fixup code over writing data in binaries on setting break + // events on functions should happen here. + +} + +SparcSystem::~SparcSystem() +{ + delete resetSymtab; + delete hypervisorSymtab; + delete openbootSymtab; + delete reset; + delete openboot; + delete hypervisor; +} + +bool +SparcSystem::breakpoint() +{ + panic("Need to implement"); +} + +void +SparcSystem::serialize(std::ostream &os) +{ + System::serialize(os); + resetSymtab->serialize("reset_symtab", os); + hypervisorSymtab->serialize("hypervisor_symtab", os); + openbootSymtab->serialize("openboot_symtab", os); +} + + +void +SparcSystem::unserialize(Checkpoint *cp, const std::string §ion) +{ + System::unserialize(cp,section); + resetSymtab->unserialize("reset_symtab", cp, section); + hypervisorSymtab->unserialize("hypervisor_symtab", cp, section); + openbootSymtab->unserialize("openboot_symtab", cp, section); +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(SparcSystem) + + SimObjectParam<PhysicalMemory *> physmem; + SimpleEnumParam<System::MemoryMode> mem_mode; + + Param<std::string> kernel; + Param<std::string> reset_bin; + Param<std::string> hypervisor_bin; + Param<std::string> openboot_bin; + + Param<std::string> boot_osflags; + Param<std::string> readfile; + Param<unsigned int> init_param; + + Param<bool> bin; + VectorParam<std::string> binned_fns; + Param<bool> bin_int; + +END_DECLARE_SIM_OBJECT_PARAMS(SparcSystem) + +BEGIN_INIT_SIM_OBJECT_PARAMS(SparcSystem) + + INIT_PARAM(boot_cpu_frequency, "Frequency of the boot CPU"), + INIT_PARAM(physmem, "phsyical memory"), + INIT_ENUM_PARAM(mem_mode, "Memory Mode, (1=atomic, 2=timing)", + System::MemoryModeStrings), + INIT_PARAM(kernel, "file that contains the kernel code"), + INIT_PARAM(reset_bin, "file that contains the reset code"), + INIT_PARAM(hypervisor_bin, "file that contains the hypervisor code"), + INIT_PARAM(openboot_bin, "file that contains the openboot code"), + INIT_PARAM_DFLT(boot_osflags, "flags to pass to the kernel during boot", + "a"), + INIT_PARAM_DFLT(readfile, "file to read startup script from", ""), + INIT_PARAM_DFLT(init_param, "numerical value to pass into simulator", 0), + INIT_PARAM_DFLT(system_type, "Type of system we are emulating", 34), + INIT_PARAM_DFLT(system_rev, "Revision of system we are emulating", 1<<10), + INIT_PARAM_DFLT(bin, "is this system to be binned", false), + INIT_PARAM(binned_fns, "functions to be broken down and binned"), + INIT_PARAM_DFLT(bin_int, "is interrupt code binned seperately?", true) + +END_INIT_SIM_OBJECT_PARAMS(SparcSystem) + +CREATE_SIM_OBJECT(SparcSystem) +{ + SparcSystem::Params *p = new SparcSystem::Params; + p->name = getInstanceName(); + p->boot_cpu_frequency = boot_cpu_frequency; + p->physmem = physmem; + p->mem_mode = mem_mode; + p->kernel_path = kernel; + p->reset_bin = reset_bin; + p->hypervisor_bin = hypervisor_bin; + p->openboot_bin = openboot_bin; + p->boot_osflags = boot_osflags; + p->init_param = init_param; + p->readfile = readfile; + p->system_type = system_type; + p->system_rev = system_rev; + p->bin = bin; + p->binned_fns = binned_fns; + p->bin_int = bin_int; + return new SparcSystem(p); +} + +REGISTER_SIM_OBJECT("SparcSystem", SparcSystem) + + diff --git a/src/arch/sparc/system.hh b/src/arch/sparc/system.hh new file mode 100644 index 000000000..614707f6c --- /dev/null +++ b/src/arch/sparc/system.hh @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#ifndef __ARCH_SPARC_SYSTEM_HH__ +#define __ARCH_SPARC_SYSTEM_HH__ + +#include <string> +#include <vector> + +#include "base/loader/symtab.hh" +#include "cpu/pc_event.hh" +#include "kern/system_events.hh" +#include "sim/sim_object.hh" +#include "sim/system.hh" + +class SparcSystem : public System +{ + public: + struct Params : public System::Params + { + std::string reset_bin; + std::string hypervison_bin; + std::string openboot_bin; + std::string boot_osflags; + uint64_t system_type; + uint64_t system_rev; + }; + + SparcSystem(Params *p); + + ~SparcSystem(); + + virtual bool breakpoint(); + +/** + * Serialization stuff + */ + public: + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + /** reset binary symbol table */ + SymbolTable *resetSymtab; + + /** hypervison binary symbol table */ + SymbolTable *hypervisorSymtab; + + /** openboot symbol table */ + SymbolTable *openbootSymtab; + + /** Object pointer for the reset binary */ + ObjectFile *reset; + + /** Object pointer for the hypervisor code */ + ObjectFile *hypervisor; + + /** Object pointer for the openboot code */ + ObjectFile *openboot; + + /** System Tick for syncronized tick across all cpus. */ + Tick sysTick; + + protected: + const Params *params() const { return (const Params *)_params; } + + /** Add a function-based event to reset binary. */ + template <class T> + T *SparcSystem::addResetFuncEvent(const char *lbl) + { + return addFuncEvent<T>(resetSymtab, lbl); + } + + /** Add a function-based event to the hypervisor. */ + template <class T> + T *SparcSystem::addHypervisorFuncEvent(const char *lbl) + { + return addFuncEvent<T>(hypervisorSymtab, lbl); + } + + /** Add a function-based event to the openboot. */ + template <class T> + T *SparcSystem::addOpenbootFuncEvent(const char *lbl) + { + return addFuncEvent<T>(openbootSymtab, lbl); + } + + virtual Addr fixFuncEventAddr(Addr addr); + +}; + +#endif + diff --git a/src/arch/sparc/tlb.hh b/src/arch/sparc/tlb.hh new file mode 100644 index 000000000..35ff08b43 --- /dev/null +++ b/src/arch/sparc/tlb.hh @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#ifndef __ARCH_SPARC_TLB_HH__ +#define __ARCH_SPARC_TLB_HH__ + + +#endif // __ARCH_SPARC_TLB_HH__ diff --git a/src/arch/sparc/types.hh b/src/arch/sparc/types.hh new file mode 100644 index 000000000..88fb24153 --- /dev/null +++ b/src/arch/sparc/types.hh @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + */ + +#ifndef __ARCH_SPARC_TYPES_HH__ +#define __ARCH_SPARC_TYPES_HH__ + +#include <inttypes.h> + +namespace SparcISA +{ + typedef uint32_t MachInst; + typedef uint64_t ExtMachInst; + + typedef uint64_t IntReg; + typedef uint64_t MiscReg; + typedef double FloatReg; + typedef uint64_t FloatRegBits; + typedef union + { + IntReg intReg; + FloatReg fpreg; + MiscReg ctrlreg; + } AnyReg; + + enum RegContextParam + { + CONTEXT_CWP, + CONTEXT_GLOBALS + }; + + typedef int RegContextVal; + + typedef uint8_t RegIndex; +} + +#endif diff --git a/src/arch/sparc/ua2005.cc b/src/arch/sparc/ua2005.cc new file mode 100644 index 000000000..6493ddfd5 --- /dev/null +++ b/src/arch/sparc/ua2005.cc @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#include "arch/sparc/regfile.hh" + +Fault +SparcISA::MiscRegFile::setFSRegWithEffect(int miscReg, const MiscReg &val, + ThreadContext *tc) +{ + int64_t time; + SparcSystem *sys; + switch (miscReg) { + /* Full system only ASRs */ + case MISCREG_SOFTINT: + if (isNonPriv()) + return new PrivilegedOpcode; + // Check if we are going to interrupt because of something + int oldLevel = InterruptLevel(softint); + int newLevel = InterruptLevel(val); + setReg(miscReg, val); + if (newLevel > oldLevel) + ; // MUST DO SOMETHING HERE TO TELL CPU TO LOOK FOR INTERRUPTS XXX + //tc->getCpuPtr()->checkInterrupts = true; + return NoFault; + + case MISCREG_SOFTINT_CLR: + return setRegWithEffect(miscReg, ~val & softint, tc); + case MISCREG_SOFTINT_SET: + return setRegWithEffect(miscReg, val | softint, tc); + + case MISCREG_TICK_CMPR: + if (isNonPriv()) + return new PrivilegedOpcode; + if (tickCompare == NULL) + tickCompare = new TickCompareEvent(this, tc); + setReg(miscReg, val); + if (tick_cmprFields.int_dis && tickCompare.scheduled()) + tickCompare.deschedule(); + time = tick_cmprFields.tick_cmpr - tickFields.counter; + if (!tick_cmprFields.int_dis && time > 0) + tickCompare.schedule(time * tc->getCpuPtr()->cycles(1)); + return NoFault; + + case MISCREG_STICK: + if (isNonPriv()) + return new PrivilegedOpcode; + if (isPriv()) + return new PrivilegedAction; + sys = dynamic_cast<SparcSystem*>(tc->getSystemPtr()); + assert(sys != NULL); + sys->sysTick = curTick/Clock::Int::ns - val & ~Bit64; + stickFields.npt = val & Bit64 ? 1 : 0; + return NoFault; + + case MISCREG_STICK_CMPR: + if (isNonPriv()) + return new PrivilegedOpcode; + if (sTickCompare == NULL) + sTickCompare = new STickCompareEvent(this, tc); + sys = dynamic_cast<SparcSystem*>(tc->getSystemPtr()); + assert(sys != NULL); + setReg(miscReg, val); + if (stick_cmprFields.int_dis && sTickCompare.scheduled()) + sTickCompare.deschedule(); + time = stick_cmprFields.tick_cmpr - sys->sysTick; + if (!stick_cmprFields.int_dis && time > 0) + sTickCompare.schedule(time * Clock::Int::ns); + return NoFault; + + /* Fullsystem only Priv registers. */ + case MISCREG_PIL: + if (FULL_SYSTEM) { + setReg(miscReg, val); + //tc->getCpuPtr()->checkInterrupts; + // MUST DO SOMETHING HERE TO TELL CPU TO LOOK FOR INTERRUPTS XXX + return NoFault; + } else + panic("PIL not implemented for syscall emulation\n"); + + /* Hyper privileged registers */ + case MISCREG_HPSTATE: + case MISCREG_HINTP: + setReg(miscReg, val); + return NoFault; + case MISCREG_HTSTATE: + if (tl == 0) + return new IllegalInstruction; + setReg(miscReg, val); + return NoFault; + + case MISCREG_HTBA: + // clear lower 7 bits on writes. + setReg(miscReg, val & ULL(~0x7FFF)); + return NoFault; + + case MISCREG_STRAND_STS_REG: + setReg(miscReg, strandStatusReg); + return NoFault; + case MISCREG_HSTICK_CMPR: + if (isNonPriv()) + return new PrivilegedOpcode; + if (hSTickCompare == NULL) + hSTickCompare = new HSTickCompareEvent(this, tc); + sys = dynamic_cast<SparcSystem*>(tc->getSystemPtr()); + assert(sys != NULL); + setReg(miscReg, val); + if (hstick_cmprFields.int_dis && hSTickCompare.scheduled()) + hSTickCompare.deschedule(); + int64_t time = hstick_cmprFields.tick_cmpr - sys->sysTick; + if (!hstick_cmprFields.int_dis && time > 0) + hSTickCompare.schedule(time * Clock::Int::ns); + return NoFault; + default: + return new IllegalInstruction; + } +} + +MiscReg +MiscRegFile::readFSRegWithEffect(int miscReg, Fault &fault, ThreadContext * tc) +{ + switch (miscReg) { + + /* Privileged registers. */ + case MISCREG_SOFTINT: + if (isNonPriv()) { + fault = new PrivilegedOpcode; + return 0; + } + return readReg(miscReg); + case MISCREG_TICK_CMPR: + if (isNonPriv()) { + fault = new PrivilegedOpcode; + return 0; + } + return readReg(miscReg); + case MISCREG_STICK: + SparcSystem *sys; + if (stickFields.npt && !isNonPriv()) { + fault = new PrivilegedAction; + return 0; + } + sys = dynamic_cast<SparcSystem*>(tc->getSystemPtr()); + assert(sys != NULL); + return curTick/Clock::Int::ns - sys->sysTick | stickFields.npt << 63; + case MISCREG_STICK_CMPR: + if (isNonPriv()) { + fault = new PrivilegedOpcode; + return 0; + } + return readReg(miscReg); + + + /* Hyper privileged registers */ + case MISCREG_HPSTATE: + case MISCREG_HINTP: + return readReg(miscReg); + case MISCREG_HTSTATE: + if (tl == 0) { + fault = new IllegalInstruction; + return 0; + } + return readReg(miscReg); + + case MISCREG_HTBA: + return readReg(miscReg) & ULL(~0x7FFF); + case MISCREG_HVER: + return NWindows | MaxTL << 8 | MaxGL << 16; + case MISCREG_STRAND_STS_REG: + return strandStatusReg; + case MISCREG_HSTICK_CMPR: + return hstick_cmpr; + + default: + fault = new IllegalInstruction; + return 0; + } +} + +void +MiscRegFile::processTickCompare(ThreadContext *tc) +{ + panic("tick compare not implemented\n"); +} + +void +MiscRegFile::processSTickCompare(ThreadContext *tc) +{ + panic("tick compare not implemented\n"); +} + +void +MiscRegFile::processHSTickCompare(ThreadContext *tc) +{ + panic("tick compare not implemented\n"); +} + +}; // namespace SparcISA diff --git a/src/arch/sparc/utility.hh b/src/arch/sparc/utility.hh new file mode 100644 index 000000000..f1c071148 --- /dev/null +++ b/src/arch/sparc/utility.hh @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + */ + +#ifndef __ARCH_SPARC_UTILITY_HH__ +#define __ARCH_SPARC_UTILITY_HH__ + +#include "arch/sparc/isa_traits.hh" +#include "base/misc.hh" + +namespace SparcISA +{ + inline ExtMachInst + makeExtMI(MachInst inst, const Addr &pc) { + return ExtMachInst(inst); + } + + inline bool isCallerSaveIntegerRegister(unsigned int reg) { + panic("register classification not implemented"); + return false; + } + + inline bool isCalleeSaveIntegerRegister(unsigned int reg) { + panic("register classification not implemented"); + return false; + } + + inline bool isCallerSaveFloatRegister(unsigned int reg) { + panic("register classification not implemented"); + return false; + } + + inline bool isCalleeSaveFloatRegister(unsigned int reg) { + panic("register classification not implemented"); + return false; + } + + // Instruction address compression hooks + inline Addr realPCToFetchPC(const Addr &addr) + { + return addr; + } + + inline Addr fetchPCToRealPC(const Addr &addr) + { + return addr; + } + + // the size of "fetched" instructions (not necessarily the size + // of real instructions for PISA) + inline size_t fetchInstSize() + { + return sizeof(MachInst); + } + + /** + * Function to insure ISA semantics about 0 registers. + * @param tc The thread context. + */ + template <class TC> + void zeroRegisters(TC *tc); + +} // namespace SparcISA + +#endif diff --git a/src/arch/sparc/vtophys.cc b/src/arch/sparc/vtophys.cc new file mode 100644 index 000000000..f7fd92c15 --- /dev/null +++ b/src/arch/sparc/vtophys.cc @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + * Ali Saidi + */ + +#include <string> + +#include "arch/alpha/ev5.hh" +#include "arch/alpha/vtophys.hh" +#include "base/chunk_generator.hh" +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "mem/vport.hh" + +using namespace std; +using namespace AlphaISA; + +AlphaISA::PageTableEntry +AlphaISA::kernel_pte_lookup(FunctionalPort *mem, Addr ptbr, AlphaISA::VAddr vaddr) +{ + Addr level1_pte = ptbr + vaddr.level1(); + AlphaISA::PageTableEntry level1 = mem->read<uint64_t>(level1_pte); + if (!level1.valid()) { + DPRINTF(VtoPhys, "level 1 PTE not valid, va = %#\n", vaddr); + return 0; + } + + Addr level2_pte = level1.paddr() + vaddr.level2(); + AlphaISA::PageTableEntry level2 = mem->read<uint64_t>(level2_pte); + if (!level2.valid()) { + DPRINTF(VtoPhys, "level 2 PTE not valid, va = %#x\n", vaddr); + return 0; + } + + Addr level3_pte = level2.paddr() + vaddr.level3(); + AlphaISA::PageTableEntry level3 = mem->read<uint64_t>(level3_pte); + if (!level3.valid()) { + DPRINTF(VtoPhys, "level 3 PTE not valid, va = %#x\n", vaddr); + return 0; + } + return level3; +} + +Addr +AlphaISA::vtophys(Addr vaddr) +{ + Addr paddr = 0; + if (AlphaISA::IsUSeg(vaddr)) + DPRINTF(VtoPhys, "vtophys: invalid vaddr %#x", vaddr); + else if (AlphaISA::IsK0Seg(vaddr)) + paddr = AlphaISA::K0Seg2Phys(vaddr); + else + panic("vtophys: ptbr is not set on virtual lookup"); + + DPRINTF(VtoPhys, "vtophys(%#x) -> %#x\n", vaddr, paddr); + + return paddr; +} + +Addr +AlphaISA::vtophys(ThreadContext *tc, Addr addr) +{ + AlphaISA::VAddr vaddr = addr; + Addr ptbr = tc->readMiscReg(AlphaISA::IPR_PALtemp20); + Addr paddr = 0; + //@todo Andrew couldn't remember why he commented some of this code + //so I put it back in. Perhaps something to do with gdb debugging? + if (AlphaISA::PcPAL(vaddr) && (vaddr < EV5::PalMax)) { + paddr = vaddr & ~ULL(1); + } else { + if (AlphaISA::IsK0Seg(vaddr)) { + paddr = AlphaISA::K0Seg2Phys(vaddr); + } else if (!ptbr) { + paddr = vaddr; + } else { + AlphaISA::PageTableEntry pte = + kernel_pte_lookup(tc->getPhysPort(), ptbr, vaddr); + if (pte.valid()) + paddr = pte.paddr() | vaddr.offset(); + } + } + + + DPRINTF(VtoPhys, "vtophys(%#x) -> %#x\n", vaddr, paddr); + + return paddr; +} + + +void +AlphaISA::CopyOut(ThreadContext *tc, void *dest, Addr src, size_t cplen) +{ + uint8_t *dst = (uint8_t *)dest; + VirtualPort *vp = tc->getVirtPort(tc); + + vp->readBlob(src, dst, cplen); + + tc->delVirtPort(vp); + +} + +void +AlphaISA::CopyIn(ThreadContext *tc, Addr dest, void *source, size_t cplen) +{ + uint8_t *src = (uint8_t *)source; + VirtualPort *vp = tc->getVirtPort(tc); + + vp->writeBlob(dest, src, cplen); + + tc->delVirtPort(vp); +} + +void +AlphaISA::CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen) +{ + int len = 0; + VirtualPort *vp = tc->getVirtPort(tc); + + do { + vp->readBlob(vaddr++, (uint8_t*)dst++, 1); + len++; + } while (len < maxlen && dst[len] != 0 ); + + tc->delVirtPort(vp); + dst[len] = 0; +} + +void +AlphaISA::CopyStringIn(ThreadContext *tc, char *src, Addr vaddr) +{ + VirtualPort *vp = tc->getVirtPort(tc); + for (ChunkGenerator gen(vaddr, strlen(src), AlphaISA::PageBytes); !gen.done(); + gen.next()) + { + vp->writeBlob(gen.addr(), (uint8_t*)src, gen.size()); + src += gen.size(); + } + tc->delVirtPort(vp); +} diff --git a/src/arch/sparc/vtophys.hh b/src/arch/sparc/vtophys.hh new file mode 100644 index 000000000..bf2b757d6 --- /dev/null +++ b/src/arch/sparc/vtophys.hh @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __ARCH_SPARC_VTOPHYS_H__ +#define __ARCH_SPARC_VTOPHYS_H__ + +#include "arch/sparc/isa_traits.hh" + +class ThreadContext; +class FunctionalPort; + +namespace SparcISA { + +PageTableEntry +kernel_pte_lookup(FunctionalPort *mem, Addr ptbr, SparcISA::VAddr vaddr); + +Addr vtophys(Addr vaddr); +Addr vtophys(ThreadContext *tc, Addr vaddr); + +void CopyOut(ThreadContext *tc, void *dst, Addr src, size_t len); +void CopyIn(ThreadContext *tc, Addr dst, void *src, size_t len); +void CopyStringOut(ThreadContext *tc, char *dst, Addr vaddr, size_t maxlen); +void CopyStringIn(ThreadContext *tc, char *src, Addr vaddr); + +}; +#endif // __ARCH_SPARC_VTOPHYS_H__ + diff --git a/src/base/annotate.cc b/src/base/annotate.cc new file mode 100644 index 000000000..ba2fb1788 --- /dev/null +++ b/src/base/annotate.cc @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#include "base/annotate.hh" +#include "base/callback.hh" +#include "base/output.hh" +#include "base/trace.hh" +#include "sim/root.hh" +#include "sim/sim_exit.hh" +#include "sim/system.hh" + + + +class AnnotateDumpCallback : public Callback +{ + public: + virtual void process(); +}; + +void +AnnotateDumpCallback::process() +{ + Annotate::annotations.dump(); +} + +namespace Annotate { + + +Annotate annotations; + +Annotate::Annotate() +{ + registerExitCallback(new AnnotateDumpCallback); +} + +void +Annotate::add(System *sys, Addr stack, uint32_t sm, uint32_t st, + uint32_t wm, uint32_t ws) +{ + AnnotateData *an; + + an = new AnnotateData; + an->time = curTick; + + std::map<System*, std::string>::iterator i = nameCache.find(sys); + if (i == nameCache.end()) { + nameCache[sys] = sys->name(); + } + + an->system = nameCache[sys]; + an->stack = stack; + an->stateMachine = sm; + an->curState = st; + an->waitMachine = wm; + an->waitState = ws; + + data.push_back(an); + if (an->waitMachine) + DPRINTF(Annotate, "Annotating: %s(%#llX) %d:%d waiting on %d:%d\n", + an->system, an->stack, an->stateMachine, an->curState, + an->waitMachine, an->waitState); + else + DPRINTF(Annotate, "Annotating: %s(%#llX) %d:%d beginning\n", an->system, + an->stack, an->stateMachine, an->curState); + + DPRINTF(Annotate, "Now %d events on list\n", data.size()); + +} + +void +Annotate::dump() +{ + + std::list<AnnotateData*>::iterator i; + + i = data.begin(); + + if (i == data.end()) + return; + + std::ostream *os = simout.create("annotate.dat"); + + AnnotateData *an; + + while (i != data.end()) { + DPRINTF(Annotate, "Writing\n", data.size()); + an = *i; + ccprintf(*os, "%d %s(%#llX) %d %d %d %d\n", an->time, an->system, + an->stack, an->stateMachine, an->curState, an->waitMachine, + an->waitState); + i++; + } +} + +} //namespace Annotate diff --git a/src/base/annotate.hh b/src/base/annotate.hh new file mode 100644 index 000000000..36607bf90 --- /dev/null +++ b/src/base/annotate.hh @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#ifndef __BASE__ANNOTATE_HH__ +#define __BASE__ANNOTATE_HH__ + +#include "sim/host.hh" + +#include <string> +#include <list> +#include <map> + + +class System; + +namespace Annotate { + + +class Annotate { + + protected: + struct AnnotateData { + Tick time; + std::string system; + Addr stack; + uint32_t stateMachine; + uint32_t curState; + uint32_t waitMachine; + uint32_t waitState; + }; + + std::list<AnnotateData*> data; + std::map<System*, std::string> nameCache; + + public: + Annotate(); + void add(System *sys, Addr stack, uint32_t sm, uint32_t st, uint32_t + wm, uint32_t ws); + void dump(); +}; + +extern Annotate annotations; +} //namespace Annotate + +#endif //__BASE__ANNOTATE_HH__ + diff --git a/src/base/bitfield.hh b/src/base/bitfield.hh new file mode 100644 index 000000000..f4e49aa8d --- /dev/null +++ b/src/base/bitfield.hh @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +#ifndef __BASE_BITFIELD_HH__ +#define __BASE_BITFIELD_HH__ + +#include "sim/host.hh" + +/** + * Generate a 64-bit mask of 'nbits' 1s, right justified. + */ +inline uint64_t +mask(int nbits) +{ + return (nbits == 64) ? (uint64_t)-1LL : (1ULL << nbits) - 1; +} + + +/** + * Extract the bitfield from position 'first' to 'last' (inclusive) + * from 'val' and right justify it. MSB is numbered 63, LSB is 0. + */ +template <class T> +inline +T +bits(T val, int first, int last) +{ + int nbits = first - last + 1; + return (val >> last) & mask(nbits); +} + +/** + * Sign-extend an N-bit value to 64 bits. + */ +template <int N> +inline +int64_t +sext(uint64_t val) +{ + int sign_bit = bits(val, N-1, N-1); + return sign_bit ? (val | ~mask(N)) : val; +} + +#endif // __BASE_BITFIELD_HH__ diff --git a/src/base/callback.hh b/src/base/callback.hh new file mode 100644 index 000000000..a39d7df20 --- /dev/null +++ b/src/base/callback.hh @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __CALLBACK_HH__ +#define __CALLBACK_HH__ + +#include <list> + +/** + * Generic callback class. This base class provides a virtual process + * function that gets called when the callback queue is processed. + */ +class Callback +{ + public: + /** + * virtualize the destructor to make sure that the correct one + * gets called. + */ + virtual ~Callback() {} + + /** + * virtual process function that is invoked when the callback + * queue is executed. + */ + virtual void process() = 0; +}; + +class CallbackQueue +{ + protected: + /** + * Simple typedef for the data structure that stores all of the + * callbacks. + */ + typedef std::list<Callback *> queue; + + /** + * List of all callbacks. To be called in fifo order. + */ + queue callbacks; + + public: + /** + * Add a callback to the end of the queue + * @param callback the callback to be added to the queue + */ + void add(Callback *callback) + { + callbacks.push_back(callback); + } + + /** + * Find out if there are any callbacks in the queue + */ + bool empty() const { return callbacks.empty(); } + + /** + * process all callbacks + */ + void process() + { + queue::iterator i = callbacks.begin(); + queue::iterator end = callbacks.end(); + + while (i != end) { + (*i)->process(); + ++i; + } + } + + /** + * clear the callback queue + */ + void clear() + { + callbacks.clear(); + } +}; + +/// Helper template class to turn a simple class member function into +/// a callback. +template <class T, void (T::* F)()> +class MakeCallback : public Callback +{ + private: + T *object; + + public: + MakeCallback(T *o) + : object(o) + { } + + void process() { (object->*F)(); } +}; + +#endif // __CALLBACK_HH__ diff --git a/src/base/chunk_generator.hh b/src/base/chunk_generator.hh new file mode 100644 index 000000000..e8238464b --- /dev/null +++ b/src/base/chunk_generator.hh @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __BASE__CHUNK_GENERATOR_HH__ +#define __BASE__CHUNK_GENERATOR_HH__ + +/** + * @file + * Declaration and inline definition of ChunkGenerator object. + */ + +#include <algorithm> +#include "base/intmath.hh" +#include "arch/isa_traits.hh" // for Addr + +/** + * This class takes an arbitrary memory region (address/length pair) + * and generates a series of appropriately (e.g. block- or page-) + * aligned chunks covering the same region. + * + * Example usage: + +\code + for (ChunkGenerator gen(addr, size, chunkSize); !gen.done(); gen.next()) { + doSomethingChunky(gen.addr(), gen.size()); + } +\endcode + */ +class ChunkGenerator +{ + private: + /** The starting address of the current chunk. */ + Addr curAddr; + /** The starting address of the next chunk (after the current one). */ + Addr nextAddr; + /** The size of the current chunk (in bytes). */ + int curSize; + /** The number of bytes remaining in the region after the current chunk. */ + int sizeLeft; + /** The start address so we can calculate offset in writing block. */ + const Addr startAddr; + /** The maximum chunk size, e.g., the cache block size or page size. */ + const int chunkSize; + + public: + /** + * Constructor. + * @param _startAddr The starting address of the region. + * @param totalSize The total size of the region. + * @param _chunkSize The size/alignment of chunks into which + * the region should be decomposed. + */ + ChunkGenerator(Addr _startAddr, int totalSize, int _chunkSize) + : startAddr(_startAddr), chunkSize(_chunkSize) + { + // chunkSize must be a power of two + assert(chunkSize == 0 || isPowerOf2(chunkSize)); + + // set up initial chunk. + curAddr = startAddr; + + if (chunkSize == 0) //Special Case, if we see 0, assume no chuncking + { + nextAddr = startAddr + totalSize; + } + else + { + // nextAddr should be *next* chunk start + nextAddr = roundUp(startAddr, chunkSize); + if (curAddr == nextAddr) { + // ... even if startAddr is already chunk-aligned + nextAddr += chunkSize; + } + } + + // how many bytes are left between curAddr and the end of this chunk? + int left_in_chunk = nextAddr - curAddr; + curSize = std::min(totalSize, left_in_chunk); + sizeLeft = totalSize - curSize; + } + + /** Return starting address of current chunk. */ + Addr addr() { return curAddr; } + /** Return size in bytes of current chunk. */ + int size() { return curSize; } + + /** Number of bytes we have already chunked up. */ + int complete() { return curAddr - startAddr; } + /** + * Are we done? That is, did the last call to next() advance + * past the end of the region? + * @return True if yes, false if more to go. + */ + bool done() { return (curSize == 0); } + + /** + * Advance generator to next chunk. + * @return True if successful, false if unsuccessful + * (because we were at the last chunk). + */ + bool next() + { + if (sizeLeft == 0) { + curSize = 0; + return false; + } + + curAddr = nextAddr; + curSize = std::min(sizeLeft, chunkSize); + sizeLeft -= curSize; + nextAddr += curSize; + return true; + } +}; + +#endif // __BASE__CHUNK_GENERATOR_HH__ diff --git a/src/base/circlebuf.cc b/src/base/circlebuf.cc new file mode 100644 index 000000000..a0c015671 --- /dev/null +++ b/src/base/circlebuf.cc @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <algorithm> +#include <string> + +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include "base/circlebuf.hh" +#include "base/cprintf.hh" +#include "base/intmath.hh" + +using namespace std; + +CircleBuf::CircleBuf(int l) + : _rollover(false), _buflen(l), _size(0), _start(0), _stop(0) +{ + _buf = new char[_buflen]; +} + +CircleBuf::~CircleBuf() +{ + if (_buf) + delete [] _buf; +} + +void +CircleBuf::dump() +{ + cprintf("start = %10d, stop = %10d, buflen = %10d\n", + _start, _stop, _buflen); + fflush(stdout); + ::write(STDOUT_FILENO, _buf, _buflen); + ::write(STDOUT_FILENO, "<\n", 2); +} + +void +CircleBuf::flush() +{ + _start = 0; + _stop = 0; + _rollover = false; +} + +void +CircleBuf::read(char *b, int len) +{ + _size -= len; + if (_size < 0) + _size = 0; + + if (_stop > _start) { + len = min(len, _stop - _start); + memcpy(b, _buf + _start, len); + _start += len; + } + else { + int endlen = _buflen - _start; + if (endlen > len) { + memcpy(b, _buf + _start, len); + _start += len; + } + else { + memcpy(b, _buf + _start, endlen); + _start = min(len - endlen, _stop); + memcpy(b + endlen, _buf, _start); + } + } +} + +void +CircleBuf::read(int fd, int len) +{ + _size -= len; + if (_size < 0) + _size = 0; + + if (_stop > _start) { + len = min(len, _stop - _start); + ::write(fd, _buf + _start, len); + _start += len; + } + else { + int endlen = _buflen - _start; + if (endlen > len) { + ::write(fd, _buf + _start, len); + _start += len; + } + else { + ::write(fd, _buf + _start, endlen); + _start = min(len - endlen, _stop); + ::write(fd, _buf, _start); + } + } +} + +void +CircleBuf::read(int fd) +{ + _size = 0; + + if (_stop > _start) { + ::write(fd, _buf + _start, _stop - _start); + } + else { + ::write(fd, _buf + _start, _buflen - _start); + ::write(fd, _buf, _stop); + } + + _start = _stop; +} + +void +CircleBuf::read(ostream &out) +{ + _size = 0; + + if (_stop > _start) { + out.write(_buf + _start, _stop - _start); + } + else { + out.write(_buf + _start, _buflen - _start); + out.write(_buf, _stop); + } + + _start = _stop; +} + +void +CircleBuf::readall(int fd) +{ + if (_rollover) + ::write(fd, _buf + _stop, _buflen - _stop); + + ::write(fd, _buf, _stop); + _start = _stop; +} + +void +CircleBuf::write(char b) +{ + write(&b, 1); +} + +void +CircleBuf::write(const char *b) +{ + write(b, strlen(b)); +} + +void +CircleBuf::write(const char *b, int len) +{ + if (len <= 0) + return; + + _size += len; + if (_size > _buflen) + _size = _buflen; + + int old_start = _start; + int old_stop = _stop; + + if (len >= _buflen) { + _start = 0; + _stop = _buflen; + _rollover = true; + memcpy(_buf, b + (len - _buflen), _buflen); + return; + } + + if (_stop + len <= _buflen) { + memcpy(_buf + _stop, b, len); + _stop += len; + } else { + int end_len = _buflen - old_stop; + _stop = len - end_len; + memcpy(_buf + old_stop, b, end_len); + memcpy(_buf, b + end_len, _stop); + _rollover = true; + } + + if (old_start > old_stop && old_start < _stop || + old_start < old_stop && _stop < old_stop) + _start = _stop + 1; +} diff --git a/src/base/circlebuf.hh b/src/base/circlebuf.hh new file mode 100644 index 000000000..66583b7db --- /dev/null +++ b/src/base/circlebuf.hh @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __CIRCLEBUF_HH__ +#define __CIRCLEBUF_HH__ + +#include <iosfwd> + +class CircleBuf +{ + protected: + char *_buf; + bool _rollover; + int _buflen; + int _size; + int _start; + int _stop; + + public: + explicit CircleBuf(int l); + ~CircleBuf(); + + bool empty() const { return _size == 0; } + int size() const { return _size; } + void dump(); + void flush(); + void read(char *b, int len); + void read(int fd, int len); + void read(int fd); + void read(std::ostream &out); + void readall(int fd); + void write(char b); + void write(const char *b); + void write(const char *b, int len); +}; + +#endif // __CIRCLEBUF_HH__ diff --git a/src/base/compression/lzss_compression.cc b/src/base/compression/lzss_compression.cc new file mode 100644 index 000000000..eb35fb8f1 --- /dev/null +++ b/src/base/compression/lzss_compression.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** @file + * LZSSCompression definitions. + */ + +#include <assert.h> + +#include "base/compression/lzss_compression.hh" + +#include "base/misc.hh" //for fatal + +void +LZSSCompression::findSubString(uint8_t *src, int back, int size, uint16_t &L, + uint16_t &P) +{ + int front = 0; + int max_length = size - back; + L = 0; + P = back - 1; + while (front < back) { + while (src[front] != src[back] && front < back) ++front; + if (front >= back) { + return; + } + int i = 1; + while (src[front+i] == src[back+i] && i < max_length) ++i; + if (i >= L) { + L = i; + P = front; + } + if (src[front+i] != src[back+i-1]) { + // can't find a longer substring until past this point. + front += i; + } else { + ++front; + } + } +} + +int +LZSSCompression::emitByte(uint8_t *dest, uint8_t byte) +{ + if ((byte >> 5 & 0x7) == 0 || (byte >> 5 & 0x7) == 7) { + // If the top 3 bits are the same, emit 00<6bits> + dest[0] = byte & 0x3f; + return 1; + } else { + // emit 01XXXXXX <8 bits> + dest[0] = 0x40; + dest[1] = byte; + return 2; + } +} + +void +LZSSCompression::emitString(uint8_t *dest, uint16_t P, uint16_t L) +{ + // Emit 1<7P> <5P><3L> <8L> + dest[0] = 1<<7 | (P >> 5 & 0x7f); + dest[1] = ((P & 0x1f) << 3) | (L>>8 & 0x3); + dest[2] = L & 0xFF; +} + +int +LZSSCompression::compress(uint8_t *dest, uint8_t *src, int size) +{ + if (size > 4096) { + fatal("Compression can only handle block sizes of 4096 bytes or less"); + } + + // Encode the first byte. + int dest_index = emitByte(dest, src[0]); + int i = 1; + // A 11 bit field + uint16_t L; + // A 12 bit field + uint16_t P = 0; + + while (i < size && dest_index < size) { + L = 0; + + if (dest_index+3 >= size) { + dest_index = size; + continue; + } + + if (i == size - 1) { + // Output the character + dest_index += emitByte(&dest[dest_index], src[i]); + ++i; + continue; + } + findSubString(src, i, size, L, P); + if (L > 1) { + // Output the string reference + emitString(&dest[dest_index], P, L); + dest_index += 3; + i = i+L; + } else { + // Output the character + dest_index += emitByte(&dest[dest_index], src[i]); + ++i; + } + } + + if (dest_index >= size) { + // Have expansion instead of compression, just copy. + memcpy(dest,src,size); + return size; + } + return dest_index; +} + +int +LZSSCompression::uncompress(uint8_t *dest, uint8_t *src, int size) +{ + int index = 0; + int i = 0; + while (i < size) { + if (src[i] & 1<<7 ) { + // We have a string + // Extract P + int start = (src[i] & 0x3f)<<5 | ((src[i+1] >> 3) & 0x1f); + // Extract L + int len = (src[i+1] & 0x07)<<8 | src[i+2]; + i += 3; + for (int j = start; j < start+len; ++j) { + dest[index++] = dest[j]; + } + } else { + // We have a character + if (src[i] & 1<<6) { + // Value is in the next byte + dest[index++] = src[i+1]; + i += 2; + } else { + // just extend the lower 6 bits + dest[index++] = (src[i] & 0x3f) | ((src[i] & 1<<5) ? 0xC0 : 0); + ++i; + } + } + } + return index; +} diff --git a/src/base/compression/lzss_compression.hh b/src/base/compression/lzss_compression.hh new file mode 100644 index 000000000..35e4dcb3f --- /dev/null +++ b/src/base/compression/lzss_compression.hh @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +#ifndef __LZSS_COMPRESSION_HH__ +#define __LZSS_COMPRESSION_HH__ + +/** @file + * LZSSCompression declarations. + */ + +#include "sim/host.hh" // for uint8_t + +/** + * Simple LZSS compression scheme. + */ +class LZSSCompression +{ + /** + * Finds the longest substring for the given offset. + * @param src The source block that we search for substrings. + * @param back The larger offset. + * @param size The size of the source block. + * @param L The length of the largest substring. + * @param P The starting offset of the largest substring. + */ + void findSubString(uint8_t *src, int back, int size, uint16_t &L, + uint16_t &P); + + /** + * Emit an encoded byte to the compressed data array. If the 2 high + * order bits can be signed extended, use 1 byte encoding, if not use 2 + * bytes. + * @param dest The compressed data. + * @param byte The byte to emit. + * @return The number of bytes used to encode. + */ + int emitByte(uint8_t *dest, uint8_t byte); + + /** + * Emit a string reference to the compressed data array. A string reference + * always uses 3 bytes. 1 flag bit, 12 bits for the starting position, and + * 11 bits for the length of the string. This allows compression of 4096 + * byte blocks with string lengths of up to 2048 bytes. + * @param dest The compressed data. + * @param P The starting position in the uncompressed data. + * @param L The length in bytes of the string. + */ + void emitString(uint8_t *dest, uint16_t P, uint16_t L); + + public: + /** + * Compresses the source block and stores it in the destination block. If + * the compressed block grows to larger than the source block, it aborts + * and just performs a copy. + * @param dest The destination block. + * @param src The block to be compressed. + * @param size The size of the source block. + * @return The size of the compressed block. + * + * @pre Destination has enough storage to hold the compressed block. + */ + int compress(uint8_t *dest, uint8_t *src, int size); + + /** + * Unompresses the source block and stores it in the destination block. + * @param dest The destination block. + * @param src The block to be uncompressed. + * @param size The size of the source block. + * @return The size of the uncompressed block. + * + * @pre Destination has enough storage to hold the uncompressed block. + */ + int uncompress(uint8_t *dest, uint8_t *src, int size); +}; + +#endif //__LZSS_COMPRESSION_HH__ diff --git a/src/base/compression/null_compression.hh b/src/base/compression/null_compression.hh new file mode 100644 index 000000000..5a582d828 --- /dev/null +++ b/src/base/compression/null_compression.hh @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Nathan Binkert + */ + +#ifndef __BASE_COMPRESSION_NULL_COMPRESSION_HH__ +#define __BASE_COMPRESSION_NULL_COMPRESSION_HH__ + +/** + * @file + * This file defines a doNothing compression algorithm. + */ + +#include "base/misc.hh" // for fatal() +#include "sim/host.hh" + + +/** + * A dummy compression class to use when no data compression is desired. + */ +class NullCompression +{ + public: + /** + * Uncompress the data, causes a fatal since no data should be compressed. + * @param dest The output buffer. + * @param src The compressed data. + * @param size The number of bytes in src. + * + * @retval The size of the uncompressed data. + */ + static int uncompress(uint8_t * dest, uint8_t *src, int size) + { + fatal("Can't uncompress data"); + } + + /** + * Compress the data, just returns the source data. + * @param dest The output buffer. + * @param src The data to be compressed. + * @param size The number of bytes in src. + * + * @retval The size of the compressed data. + */ + + static int compress(uint8_t *dest, uint8_t *src, int size) + { + memcpy(dest,src,size); + return size; + } +}; + +#endif //__BASE_COMPRESSION_NULL_COMPRESSION_HH__ diff --git a/src/base/cprintf.cc b/src/base/cprintf.cc new file mode 100644 index 000000000..dd8ce858b --- /dev/null +++ b/src/base/cprintf.cc @@ -0,0 +1,270 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <cassert> +#include <iomanip> +#include <iostream> +#include <sstream> + +#include "base/cprintf.hh" + +using namespace std; + +namespace cp { + +ArgList::~ArgList() +{ + while (!objects.empty()) { + delete objects.front(); + objects.pop_front(); + } +} + +void +ArgList::dump(const string &format) +{ + list_t::iterator iter = objects.begin(); + list_t::iterator end = objects.end(); + + const char *p = format.c_str(); + + stream->fill(' '); + stream->flags((ios::fmtflags)0); + + while (*p) { + switch (*p) { + case '%': { + if (p[1] == '%') { + *stream << '%'; + p += 2; + continue; + } + + Format fmt; + bool done = false; + bool end_number = false; + bool have_precision = false; + int number = 0; + + while (!done) { + ++p; + if (*p >= '0' && *p <= '9') { + if (end_number) + continue; + } else if (number > 0) + end_number = true; + + switch (*p) { + case 's': + fmt.format = Format::string; + done = true; + break; + + case 'c': + fmt.format = Format::character; + done = true; + break; + + case 'l': + continue; + + case 'p': + fmt.format = Format::integer; + fmt.base = Format::hex; + fmt.alternate_form = true; + done = true; + break; + + case 'X': + fmt.uppercase = true; + case 'x': + fmt.base = Format::hex; + fmt.format = Format::integer; + done = true; + break; + + case 'o': + fmt.base = Format::oct; + fmt.format = Format::integer; + done = true; + break; + + case 'd': + case 'i': + case 'u': + fmt.format = Format::integer; + done = true; + break; + + case 'G': + fmt.uppercase = true; + case 'g': + fmt.format = Format::floating; + fmt.float_format = Format::best; + done = true; + break; + + case 'E': + fmt.uppercase = true; + case 'e': + fmt.format = Format::floating; + fmt.float_format = Format::scientific; + done = true; + break; + + case 'f': + fmt.format = Format::floating; + fmt.float_format = Format::fixed; + done = true; + break; + + case 'n': + *stream << "we don't do %n!!!\n"; + done = true; + break; + + case '#': + fmt.alternate_form = true; + break; + + case '-': + fmt.flush_left = true; + break; + + case '+': + fmt.print_sign = true; + break; + + case ' ': + fmt.blank_space = true; + break; + + case '.': + fmt.width = number; + fmt.precision = 0; + have_precision = true; + number = 0; + end_number = false; + break; + + case '0': + if (number == 0) { + fmt.fill_zero = true; + break; + } + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + number = number * 10 + (*p - '0'); + break; + + case '%': + assert("we shouldn't get here"); + break; + + default: + done = true; + break; + } + + if (end_number) { + if (have_precision) + fmt.precision = number; + else + fmt.width = number; + + end_number = false; + number = 0; + } + } + + if (iter != end) + { + ios::fmtflags saved_flags = stream->flags(); + char old_fill = stream->fill(); + int old_precision = stream->precision(); + + (*iter)->process(*stream, fmt); + + stream->flags(saved_flags); + stream->fill(old_fill); + stream->precision(old_precision); + + ++iter; + } else { + *stream << "<missing arg for format>"; + } + + ++p; + } + break; + + case '\n': + *stream << endl; + ++p; + break; + case '\r': + ++p; + if (*p != '\n') + *stream << endl; + break; + + default: { + size_t len = strcspn(p, "%\n\r\0"); + stream->write(p, len); + p += len; + } + break; + } + } + + while (iter != end) { + *stream << "<extra arg>"; + ++iter; + } +} + +string +ArgList::dumpToString(const string &format) +{ + stringstream ss; + + dump(ss, format); + + return ss.str(); +} + +} diff --git a/src/base/cprintf.hh b/src/base/cprintf.hh new file mode 100644 index 000000000..6b2a77f90 --- /dev/null +++ b/src/base/cprintf.hh @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __CPRINTF_HH__ +#define __CPRINTF_HH__ + +#include <iostream> +#include <list> +#include <string> + +#include "base/cprintf_formats.hh" + +namespace cp { + +class ArgList +{ + private: + class Base + { + public: + virtual ~Base() {} + virtual void process(std::ostream &out, Format &fmt) = 0; + }; + + template <typename T> + class Node : public Base + { + public: + const T &data; + + public: + Node(const T &d) : data(d) {} + virtual void process(std::ostream &out, Format &fmt) { + switch (fmt.format) { + case Format::character: + format_char(out, data, fmt); + break; + + case Format::integer: + format_integer(out, data, fmt); + break; + + case Format::floating: + format_float(out, data, fmt); + break; + + case Format::string: + format_string(out, data, fmt); + break; + + default: + out << "<bad format>"; + break; + } + } + }; + + typedef std::list<Base *> list_t; + + protected: + list_t objects; + std::ostream *stream; + + public: + ArgList() : stream(&std::cout) {} + ~ArgList(); + + template<class T> + void append(const T &data) { + Base *obj = new ArgList::Node<T>(data); + objects.push_back(obj); + } + + template<class T> + void prepend(const T &data) { + Base *obj = new ArgList::Node<T>(data); + objects.push_front(obj); + } + + void dump(const std::string &format); + void dump(std::ostream &strm, const std::string &fmt) + { stream = &strm; dump(fmt); } + + std::string dumpToString(const std::string &format); + + friend ArgList &operator<<(std::ostream &str, ArgList &list); +}; + +template<class T> +inline ArgList & +operator,(ArgList &alist, const T &data) +{ + alist.append(data); + return alist; +} + +class ArgListNull { +}; + +inline ArgList & +operator,(ArgList &alist, ArgListNull) +{ return alist; } + +// +// cprintf(format, args, ...) prints to cout +// (analogous to printf()) +// +inline void +__cprintf(const std::string &format, ArgList &args) +{ args.dump(format); delete &args; } +#define __cprintf__(format, args...) \ + cp::__cprintf(format, (*(new cp::ArgList), args)) +#define cprintf(args...) \ + __cprintf__(args, cp::ArgListNull()) + +// +// ccprintf(stream, format, args, ...) prints to the specified stream +// (analogous to fprintf()) +// +inline void +__ccprintf(std::ostream &stream, const std::string &format, ArgList &args) +{ args.dump(stream, format); delete &args; } +#define __ccprintf__(stream, format, args...) \ + cp::__ccprintf(stream, format, (*(new cp::ArgList), args)) +#define ccprintf(stream, args...) \ + __ccprintf__(stream, args, cp::ArgListNull()) + +// +// csprintf(format, args, ...) returns a string +// (roughly analogous to sprintf()) +// +inline std::string +__csprintf(const std::string &format, ArgList &args) +{ std::string s = args.dumpToString(format); delete &args; return s; } +#define __csprintf__(format, args...) \ + cp::__csprintf(format, (*(new cp::ArgList), args)) +#define csprintf(args...) \ + __csprintf__(args, cp::ArgListNull()) + +template<class T> +inline ArgList & +operator<<(ArgList &list, const T &data) +{ + list.append(data); + return list; +} + +inline ArgList & +operator<<(std::ostream &str, ArgList &list) +{ + list.stream = &str; + return list; +} + +class ArgListTemp +{ + private: + std::string format; + ArgList *args; + + public: + ArgListTemp(const std::string &f) : format(f) { args = new ArgList; } + ~ArgListTemp() { args->dump(format); delete args; } + + operator ArgList *() { return args; } +}; + +#define cformat(format) \ + (*((cp::ArgList *)cp::ArgListTemp(format))) +} + +#endif // __CPRINTF_HH__ diff --git a/src/base/cprintf_formats.hh b/src/base/cprintf_formats.hh new file mode 100644 index 000000000..58ee7f795 --- /dev/null +++ b/src/base/cprintf_formats.hh @@ -0,0 +1,355 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __CPRINTF_FORMATS_HH__ +#define __CPRINTF_FORMATS_HH__ + +#include <sstream> +#include <ostream> + +namespace cp { + +struct Format +{ + bool alternate_form; + bool flush_left; + bool print_sign; + bool blank_space; + bool fill_zero; + bool uppercase; + enum { dec, hex, oct } base; + enum { none, string, integer, character, floating } format; + enum { best, fixed, scientific } float_format; + int precision; + int width; + + Format() { clear(); } + + void clear() + { + alternate_form = false; + flush_left = false; + print_sign = false; + blank_space = false; + fill_zero = false; + uppercase = false; + base = dec; + format = none; + precision = -1; + width = 0; + } +}; + +template <typename T> +inline void +_format_char(std::ostream &out, const T &data, Format &fmt) +{ + using namespace std; + + out << data; +} + +template <typename T> +inline void +_format_integer(std::ostream &out, const T &data, Format &fmt) +{ + using namespace std; + + switch (fmt.base) { + case Format::hex: + out.setf(ios::hex, ios::basefield); + break; + + case Format::oct: + out.setf(ios::oct, ios::basefield); + break; + + case Format::dec: + out.setf(ios::dec, ios::basefield); + break; + } + + if (fmt.alternate_form) { + if (!fmt.fill_zero) + out.setf(ios::showbase); + else { + switch (fmt.base) { + case Format::hex: + out << "0x"; + fmt.width -= 2; + break; + case Format::oct: + out << "0"; + fmt.width -= 1; + break; + case Format::dec: + break; + } + } + } + + if (fmt.fill_zero) + out.fill('0'); + + if (fmt.width > 0) + out.width(fmt.width); + + if (fmt.flush_left && !fmt.fill_zero) + out.setf(ios::left); + + if (fmt.print_sign) + out.setf(ios::showpos); + + if (fmt.uppercase) + out.setf(ios::uppercase); + + out << data; +} + +template <typename T> +inline void +_format_float(std::ostream &out, const T &data, Format &fmt) +{ + using namespace std; + + switch (fmt.float_format) { + case Format::scientific: + if (fmt.precision != -1) { + if (fmt.width > 0) + out.width(fmt.width); + + if (fmt.precision == 0) + fmt.precision = 1; + else + out.setf(ios::scientific); + + out.precision(fmt.precision); + } else + if (fmt.width > 0) + out.width(fmt.width); + + if (fmt.uppercase) + out.setf(ios::uppercase); + break; + + case Format::fixed: + if (fmt.precision != -1) { + if (fmt.width > 0) + out.width(fmt.width); + + out.setf(ios::fixed); + out.precision(fmt.precision); + } else + if (fmt.width > 0) + out.width(fmt.width); + + break; + + default: + if (fmt.precision != -1) + out.precision(fmt.precision); + + if (fmt.width > 0) + out.width(fmt.width); + + break; + } + + out << data; +} + +template <typename T> +inline void +_format_string(std::ostream &out, const T &data, Format &fmt) +{ + using namespace std; + +#if defined(__GNUC__) && (__GNUC__ < 3) || 1 + if (fmt.width > 0) { + std::stringstream foo; + foo << data; + int flen = foo.str().size(); + + if (fmt.width > flen) { + char *spaces = new char[fmt.width - flen + 1]; + memset(spaces, ' ', fmt.width - flen); + spaces[fmt.width - flen] = 0; + + if (fmt.flush_left) + out << foo.str() << spaces; + else + out << spaces << foo.str(); + + delete [] spaces; + } else + out << data; + } else + out << data; +#else + if (fmt.width > 0) + out.width(fmt.width); + if (fmt.flush_left) + out.setf(ios::left); + + out << data; +#endif +} + +///////////////////////////////////////////////////////////////////////////// +// +// The code below controls the actual usage of formats for various types +// + +// +// character formats +// +template <typename T> +inline void +format_char(std::ostream &out, const T &data, Format &fmt) +{ out << "<bad arg type for char format>"; } + +inline void +format_char(std::ostream &out, char data, Format &fmt) +{ _format_char(out, data, fmt); } + +inline void +format_char(std::ostream &out, unsigned char data, Format &fmt) +{ _format_char(out, data, fmt); } + +inline void +format_char(std::ostream &out, signed char data, Format &fmt) +{ _format_char(out, data, fmt); } + +inline void +format_char(std::ostream &out, short data, Format &fmt) +{ _format_char(out, (char)data, fmt); } + +inline void +format_char(std::ostream &out, unsigned short data, Format &fmt) +{ _format_char(out, (char)data, fmt); } + +inline void +format_char(std::ostream &out, int data, Format &fmt) +{ _format_char(out, (char)data, fmt); } + +inline void +format_char(std::ostream &out, unsigned int data, Format &fmt) +{ _format_char(out, (char)data, fmt); } + +inline void +format_char(std::ostream &out, long data, Format &fmt) +{ _format_char(out, (char)data, fmt); } + +inline void +format_char(std::ostream &out, unsigned long data, Format &fmt) +{ _format_char(out, (char)data, fmt); } + +inline void +format_char(std::ostream &out, long long data, Format &fmt) +{ _format_char(out, (char)data, fmt); } + +inline void +format_char(std::ostream &out, unsigned long long data, Format &fmt) +{ _format_char(out, (char)data, fmt); } + +// +// integer formats +// +template <typename T> +inline void +format_integer(std::ostream &out, const T &data, Format &fmt) +{ _format_integer(out, data, fmt); } +inline void +format_integer(std::ostream &out, char data, Format &fmt) +{ _format_integer(out, data, fmt); } +inline void +format_integer(std::ostream &out, unsigned char data, Format &fmt) +{ _format_integer(out, data, fmt); } +inline void +format_integer(std::ostream &out, signed char data, Format &fmt) +{ _format_integer(out, data, fmt); } +#if 0 +inline void +format_integer(std::ostream &out, short data, Format &fmt) +{ _format_integer(out, data, fmt); } +inline void +format_integer(std::ostream &out, unsigned short data, Format &fmt) +{ _format_integer(out, data, fmt); } +inline void +format_integer(std::ostream &out, int data, Format &fmt) +{ _format_integer(out, data, fmt); } +inline void +format_integer(std::ostream &out, unsigned int data, Format &fmt) +{ _format_integer(out, data, fmt); } +inline void +format_integer(std::ostream &out, long data, Format &fmt) +{ _format_integer(out, data, fmt); } +inline void +format_integer(std::ostream &out, unsigned long data, Format &fmt) +{ _format_integer(out, data, fmt); } +inline void +format_integer(std::ostream &out, long long data, Format &fmt) +{ _format_integer(out, data, fmt); } +inline void +format_integer(std::ostream &out, unsigned long long data, Format &fmt) +{ _format_integer(out, data, fmt); } +#endif + +// +// floating point formats +// +template <typename T> +inline void +format_float(std::ostream &out, const T &data, Format &fmt) +{ out << "<bad arg type for float format>"; } + +inline void +format_float(std::ostream &out, float data, Format &fmt) +{ _format_float(out, data, fmt); } + +inline void +format_float(std::ostream &out, double data, Format &fmt) +{ _format_float(out, data, fmt); } + +// +// string formats +// +template <typename T> +inline void +format_string(std::ostream &out, const T &data, Format &fmt) +{ _format_string(out, data, fmt); } + +inline void +format_string(std::ostream &out, const std::stringstream &data, Format &fmt) +{ _format_string(out, data.str(), fmt); } + +} // namespace cp + +#endif // __CPRINTF_FORMATS_HH__ diff --git a/src/base/crc.cc b/src/base/crc.cc new file mode 100644 index 000000000..08f039577 --- /dev/null +++ b/src/base/crc.cc @@ -0,0 +1,114 @@ +/* + * Copyright (c) 1988, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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 <string> + +#include "sim/host.hh" +#include "base/crc.hh" + +#define ETHER_CRC_POLY_LE 0xedb88320 +#define ETHER_CRC_POLY_BE 0x04c11db6 + +#if 0 +/* + * This is for reference. We have a table-driven version + * of the little-endian crc32 generator, which is faster + * than the double-loop. + */ +uint32_t +crc32le(const uint8_t *buf, size_t len) +{ + uint32_t c, crc, carry; + size_t i, j; + + crc = 0xffffffffU; /* initial value */ + + for (i = 0; i < len; i++) { + c = buf[i]; + for (j = 0; j < 8; j++) { + carry = ((crc & 0x01) ? 1 : 0) ^ (c & 0x01); + crc >>= 1; + c >>= 1; + if (carry) + crc = (crc ^ ETHER_CRC_POLY_LE); + } + } + + return (crc); +} +#else +uint32_t +crc32le(const uint8_t *buf, size_t len) +{ + static const uint32_t crctab[] = { + 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, + 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, + 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, + 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c + }; + uint32_t crc; + int i; + + crc = 0xffffffffU; /* initial value */ + + for (i = 0; i < len; i++) { + crc ^= buf[i]; + crc = (crc >> 4) ^ crctab[crc & 0xf]; + crc = (crc >> 4) ^ crctab[crc & 0xf]; + } + + return (crc); +} +#endif + +uint32_t +crc32be(const uint8_t *buf, size_t len) +{ + uint32_t c, crc, carry; + size_t i, j; + + crc = 0xffffffffU; /* initial value */ + + for (i = 0; i < len; i++) { + c = buf[i]; + for (j = 0; j < 8; j++) { + carry = ((crc & 0x80000000U) ? 1 : 0) ^ (c & 0x01); + crc <<= 1; + c >>= 1; + if (carry) + crc = (crc ^ ETHER_CRC_POLY_BE) | carry; + } + } + + return (crc); +} diff --git a/src/base/crc.hh b/src/base/crc.hh new file mode 100644 index 000000000..16dce79aa --- /dev/null +++ b/src/base/crc.hh @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_CRC_HH__ +#define __BASE_CRC_HH__ + +#include "sim/host.hh" + +uint32_t crc32be(const uint8_t *buf, size_t len); +uint32_t crc32le(const uint8_t *buf, size_t len); + +#endif // __BASE_CRC_HH__ diff --git a/src/base/date.cc b/src/base/date.cc new file mode 100644 index 000000000..9ec8a0b33 --- /dev/null +++ b/src/base/date.cc @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +const char *compileDate = __DATE__ " " __TIME__; diff --git a/src/base/dbl_list.hh b/src/base/dbl_list.hh new file mode 100644 index 000000000..8fca72998 --- /dev/null +++ b/src/base/dbl_list.hh @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2000-2001, 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __DBL_LIST_HH__ +#define __DBL_LIST_HH__ + +class DblListEl { + DblListEl *next; + DblListEl *prev; + + // remove this from list + void remove() { + prev->next = next; + next->prev = prev; + } + + // insert this before old_el + void insertBefore(DblListEl *old_el) { + prev = old_el->prev; + next = old_el; + prev->next = this; + next->prev = this; + } + + // insert this after old_el + void insertAfter(DblListEl *old_el) { + next = old_el->next; + prev = old_el; + next->prev = this; + prev->next = this; + } + + friend class DblListBase; +}; + + +// +// doubly-linked list of DblListEl objects +// +class DblListBase { + // dummy list head element: dummy.next is head, dummy.prev is tail + DblListEl dummy; + + // length counter + unsigned length; + + DblListEl *valid_or_null(DblListEl *el) { + // make sure users never see the dummy element + return (el == &dummy) ? NULL : el; + } + + public: + + DblListEl *head() { + return valid_or_null(dummy.next); + } + + DblListEl *tail() { + return valid_or_null(dummy.prev); + } + + DblListEl *next(DblListEl *el) { + return valid_or_null(el->next); + } + + DblListEl *prev(DblListEl *el) { + return valid_or_null(el->prev); + } + + bool is_empty() { + return (dummy.next == &dummy); + } + + void remove(DblListEl *el) { + el->remove(); + --length; + } + + void insertBefore(DblListEl *new_el, DblListEl *old_el) { + new_el->insertBefore(old_el); + ++length; + } + + void insertAfter(DblListEl *new_el, DblListEl *old_el) { + new_el->insertAfter(old_el); + ++length; + } + + // append to end of list, i.e. as dummy.prev + void append(DblListEl *el) { + insertBefore(el, &dummy); + } + + // prepend to front of list (push), i.e. as dummy.next + void prepend(DblListEl *el) { + insertAfter(el, &dummy); + } + + DblListEl *pop() { + DblListEl *hd = head(); + if (hd != NULL) + remove(hd); + return hd; + } + + // constructor + DblListBase() { + dummy.next = dummy.prev = &dummy; + length = 0; + } +}; + + +// +// Template class serves solely to cast args & return values +// to appropriate type (T *) +// +template<class T> class DblList : private DblListBase { + + public: + + T *head() { return (T *)DblListBase::head(); } + T *tail() { return (T *)DblListBase::tail(); } + + T *next(T *el) { return (T *)DblListBase::next(el); } + T *prev(T *el) { return (T *)DblListBase::prev(el); } + + bool is_empty() { return DblListBase::is_empty(); } + + void remove(T *el) { DblListBase::remove(el); } + + void append(T *el) { DblListBase::append(el); } + void prepend(T *el) { DblListBase::prepend(el); } + + T *pop() { return (T *)DblListBase::pop(); } + + DblList<T>() { } +}; + +#endif // __DBL_LIST_HH__ diff --git a/src/base/endian.hh b/src/base/endian.hh new file mode 100644 index 000000000..d2b9dc65c --- /dev/null +++ b/src/base/endian.hh @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __ENDIAN_HH__ +#define __ENDIAN_HH__ + +#include <arpa/inet.h> + +inline bool +HostBigEndian() +{ + int x = 0x11223344; + return x == htonl(x); +} + +#endif // __ENDIAN_HH__ diff --git a/src/base/fast_alloc.cc b/src/base/fast_alloc.cc new file mode 100644 index 000000000..610dff66c --- /dev/null +++ b/src/base/fast_alloc.cc @@ -0,0 +1,195 @@ +/* + * Copyright (c) 2000-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +/* + * This code was originally written by Steve Reinhardt as part of + * the Wisconsin Wind Tunnel simulator. Relicensed as part of M5 + * by permission. + */ + +#ifndef NO_FAST_ALLOC + +#ifdef __GNUC__ +#pragma implementation +#endif + +#include <assert.h> +#include "base/fast_alloc.hh" + +void *FastAlloc::freeLists[Num_Buckets]; + +#ifdef FAST_ALLOC_STATS +unsigned FastAlloc::newCount[Num_Buckets]; +unsigned FastAlloc::deleteCount[Num_Buckets]; +unsigned FastAlloc::allocCount[Num_Buckets]; +#endif + +void *FastAlloc::moreStructs(int bucket) +{ + assert(bucket > 0 && bucket < Num_Buckets); + + int sz = bucket * Alloc_Quantum; + const int nstructs = Num_Structs_Per_New; // how many to allocate? + char *p = ::new char[nstructs * sz]; + +#ifdef FAST_ALLOC_STATS + ++allocCount[bucket]; +#endif + + freeLists[bucket] = p; + for (int i = 0; i < (nstructs-2); ++i, p += sz) + *(void **)p = p + sz; + *(void **)p = 0; + + return (p + sz); +} + + +#ifdef FAST_ALLOC_DEBUG + +#include <typeinfo> +#include <iostream> +#include <iomanip> +#include <map> +#include <string> + +using namespace std; + +// count of in-use FastAlloc objects +int FastAlloc::numInUse; + +// dummy head & tail object for doubly linked list of in-use FastAlloc +// objects +FastAlloc FastAlloc::inUseHead(&FastAlloc::inUseHead, &FastAlloc::inUseHead); + +// special constructor for dummy head: make inUsePrev & inUseNext +// point to self +FastAlloc::FastAlloc(FastAlloc *prev, FastAlloc *next) +{ + inUsePrev = prev; + inUseNext = next; +} + + +// constructor: marks as in use, add to in-use list +FastAlloc::FastAlloc() +{ + // mark this object in use + inUse = true; + + // update count + ++numInUse; + + // add to tail of list of in-use objects ("before" dummy head) + FastAlloc *myNext = &inUseHead; + FastAlloc *myPrev = inUseHead.inUsePrev; + + inUsePrev = myPrev; + inUseNext = myNext; + myPrev->inUseNext = this; + myNext->inUsePrev = this; +} + +// destructor: mark not in use, remove from in-use list +FastAlloc::~FastAlloc() +{ + assert(inUse); + inUse = false; + + --numInUse; + assert(numInUse >= 0); + + // remove me from in-use list + inUsePrev->inUseNext = inUseNext; + inUseNext->inUsePrev = inUsePrev; +} + + +// summarize in-use list +void +FastAlloc::dump_summary() +{ + map<string, int> typemap; + + for (FastAlloc *p = inUseHead.inUseNext; p != &inUseHead; p = p->inUseNext) + { + ++typemap[typeid(*p).name()]; + } + + map<string, int>::const_iterator mapiter; + + cout << " count type\n" + << " ----- ----\n"; + for (mapiter = typemap.begin(); mapiter != typemap.end(); ++mapiter) + { + cout << setw(6) << mapiter->second << " " << mapiter->first << endl; + } +} + + +// show oldest n items on in-use list +void +FastAlloc::dump_oldest(int n) +{ + // sanity check: don't want to crash the debugger if you forget to + // pass in a parameter + if (n < 0 || n > numInUse) + { + cout << "FastAlloc::dump_oldest: bad arg " << n + << " (" << numInUse << " objects in use" << endl; + return; + } + + for (FastAlloc *p = inUseHead.inUsePrev; + p != &inUseHead && n > 0; + p = p->inUsePrev, --n) + { + cout << p << " " << typeid(*p).name() << endl; + } +} + + +// +// C interfaces to FastAlloc::dump_summary() and FastAlloc::dump_oldest(). +// gdb seems to have trouble with calling C++ functions directly. +// +fast_alloc_summary() +{ + FastAlloc::dump_summary(); +} + +fast_alloc_oldest(int n) +{ + FastAlloc::dump_oldest(n); +} + +#endif + +#endif // NO_FAST_ALLOC diff --git a/src/base/fast_alloc.hh b/src/base/fast_alloc.hh new file mode 100644 index 000000000..3e22e59c1 --- /dev/null +++ b/src/base/fast_alloc.hh @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2000-2001, 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +/* + * This code was originally written by Steve Reinhardt as part of + * the Wisconsin Wind Tunnel simulator. Relicensed as part of M5 + * by permission. + */ + +#ifndef __FAST_ALLOC_H__ +#define __FAST_ALLOC_H__ + +#include <stddef.h> + +// Fast structure allocator. Designed for small objects that are +// frequently allocated and deallocated. This code is derived from the +// 'alloc_struct' package used in WWT and Blizzard. C++ provides a +// much nicer framework for the same optimization. The package is +// implemented as a class, FastAlloc. Allocation and deletion are +// performed using FastAlloc's new and delete operators. Any object +// that derives from the FastAlloc class will transparently use this +// allocation package. + +// The static allocate() and deallocate() methods can also be called +// directly if desired. + +// In order for derived classes to call delete with the correct +// structure size even when they are deallocated via a base-type +// pointer, they must have a virtual destructor. It is sufficient for +// FastAlloc to declare a virtual destructor (as it does); it is not +// required for derived classes to declare their own destructor. The +// compiler will automatically generate a virtual destructor for each +// derived class. However, it is more efficient if each derived class +// defines an inline destructor, so that the compiler can statically +// collapse the destructor call chain back up the inheritance +// hierarchy. + +// Uncomment this #define to track in-use objects +// (for debugging memory leaks). +//#define FAST_ALLOC_DEBUG + +// Uncomment this #define to count news, deletes, and chunk allocations +// (by bucket). +// #define FAST_ALLOC_STATS + +#include "config/no_fast_alloc.hh" + +#if NO_FAST_ALLOC + +class FastAlloc { +}; + +#else + +class FastAlloc { + public: + + static void *allocate(size_t); + static void deallocate(void *, size_t); + + void *operator new(size_t); + void operator delete(void *, size_t); + +#ifdef FAST_ALLOC_DEBUG + FastAlloc(); + FastAlloc(FastAlloc*,FastAlloc*); // for inUseHead, see below + virtual ~FastAlloc(); +#else + virtual ~FastAlloc() {} +#endif + + private: + + // Max_Alloc_Size is the largest object that can be allocated with + // this class. There's no fundamental limit, but this limits the + // size of the freeLists array. Let's not make this really huge + // like in Blizzard. + static const int Max_Alloc_Size = 512; + + // Alloc_Quantum is the difference in size between adjacent + // buckets in the free list array. + static const int Log2_Alloc_Quantum = 3; + static const int Alloc_Quantum = (1 << Log2_Alloc_Quantum); + + // Num_Buckets = bucketFor(Max_Alloc_Size) + 1 + static const int Num_Buckets = + ((Max_Alloc_Size + Alloc_Quantum - 1) >> Log2_Alloc_Quantum) + 1; + + // when we call new() for more structures, how many should we get? + static const int Num_Structs_Per_New = 20; + + static int bucketFor(size_t); + static void *moreStructs(int bucket); + + static void *freeLists[Num_Buckets]; + +#ifdef FAST_ALLOC_STATS + static unsigned newCount[Num_Buckets]; + static unsigned deleteCount[Num_Buckets]; + static unsigned allocCount[Num_Buckets]; +#endif + +#ifdef FAST_ALLOC_DEBUG + // per-object debugging fields + bool inUse; // in-use flag + FastAlloc *inUsePrev; // ptrs to build list of in-use objects + FastAlloc *inUseNext; + + // static (global) debugging vars + static int numInUse; // count in-use objects + static FastAlloc inUseHead; // dummy head for list of in-use objects + + public: + // functions to dump debugging info (see fast_alloc.cc for C + // versions that might be more agreeable to call from gdb) + static void dump_summary(); + static void dump_oldest(int n); +#endif +}; + + +inline +int FastAlloc::bucketFor(size_t sz) +{ + return (sz + Alloc_Quantum - 1) >> Log2_Alloc_Quantum; +} + + +inline +void *FastAlloc::allocate(size_t sz) +{ + int b; + void *p; + + if (sz > Max_Alloc_Size) + return (void *)::new char[sz]; + + b = bucketFor(sz); + p = freeLists[b]; + + if (p) + freeLists[b] = *(void **)p; + else + p = moreStructs(b); + +#ifdef FAST_ALLOC_STATS + ++newCount[b]; +#endif + + return p; +} + + +inline +void FastAlloc::deallocate(void *p, size_t sz) +{ + int b; + + if (sz > Max_Alloc_Size) + { + ::delete [] (char *)p; + return; + } + + b = bucketFor(sz); + *(void **)p = freeLists[b]; + freeLists[b] = p; +#ifdef FAST_ALLOC_STATS + ++deleteCount[b]; +#endif +} + + +inline +void *FastAlloc::operator new(size_t sz) +{ + return allocate(sz); +} + + +inline +void FastAlloc::operator delete(void *p, size_t sz) +{ + deallocate(p, sz); +} + +#endif // NO_FAST_ALLOC + +#endif // __FAST_ALLOC_H__ diff --git a/src/base/fenv.hh b/src/base/fenv.hh new file mode 100644 index 000000000..013d2f09b --- /dev/null +++ b/src/base/fenv.hh @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __BASE_FENV_HH__ +#define __BASE_FENV_HH__ + +#include "config/use_fenv.hh" + +#if USE_FENV + +#include <fenv.h> + +#else + +// Dummy definitions to allow code to compile w/o a real <fenv.h>. + +#define FE_TONEAREST 0 +#define FE_DOWNWARD 0 +#define FE_UPWARD 0 +#define FE_TOWARDZERO 0 + +inline int fesetround(int rounding_mode) { return 0; } + +#endif // USE_FENV + + +#endif // __BASE_FENV_HH__ diff --git a/src/base/fifo_buffer.cc b/src/base/fifo_buffer.cc new file mode 100644 index 000000000..4bafa88bd --- /dev/null +++ b/src/base/fifo_buffer.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Raasch + * Nathan Binkert + */ + +#include "base/fifo_buffer.hh" + +template<class T> +void +FifoBuffer<T>::dump() +{ + if (buffer->count() > 0) + for (iterator i=buffer->tail(); i.notnull(); i=i.prev()) + i->dump(); +} + + diff --git a/src/base/fifo_buffer.hh b/src/base/fifo_buffer.hh new file mode 100644 index 000000000..f6205330b --- /dev/null +++ b/src/base/fifo_buffer.hh @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Raasch + * Nathan Binkert + */ + +#ifndef __FIFO_BUFFER_HH__ +#define __FIFO_BUFFER_HH__ + +#include "base/res_list.hh" + + +// +// The FifoBuffer requires only that the objects to be used have a default +// constructor and a dump() method +// +template<class T> +class FifoBuffer +{ + public: + typedef typename res_list<T>::iterator iterator; + + private: + res_list<T> *buffer; + + unsigned size; + + public: + FifoBuffer(unsigned sz) + { + buffer = new res_list<T>(sz, true, 0); + size = sz; + } + + void add(T &item) + { + assert(buffer->num_free() > 0); + buffer->add_head(item); + } + + iterator head() { return buffer->head(); } + iterator tail() { return buffer->tail(); } + + unsigned count() {return buffer->count();} + unsigned free_slots() {return buffer->num_free();} + + T *peek() { return (count() > 0) ? tail().data_ptr() : 0; } + + T remove() + { + assert(buffer->count() > 0); + T rval = *buffer->tail(); + buffer->remove_tail(); + return rval; + } + + void dump(); + + ~FifoBuffer() { delete buffer; } +}; + + +#endif + diff --git a/src/base/hashmap.hh b/src/base/hashmap.hh new file mode 100644 index 000000000..570cbc152 --- /dev/null +++ b/src/base/hashmap.hh @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __HASHMAP_HH__ +#define __HASHMAP_HH__ + +#if defined(__GNUC__) && __GNUC__ >= 3 +#include <ext/hash_map> +#else +#include <hash_map> +#endif + +#include <string> + +#include "sim/host.hh" + +#if defined(__GNUC__) && __GNUC__ >= 3 + #define __hash_namespace __gnu_cxx +#else + #define __hash_namespace std +#endif + +namespace m5 { + using ::__hash_namespace::hash_multimap; + using ::__hash_namespace::hash_map; + using ::__hash_namespace::hash; +} + + +/////////////////////////////////// +// Some default Hashing Functions +// + +namespace __hash_namespace { +#if !defined(__LP64__) && !defined(__alpha__) + template<> + struct hash<uint64_t> { + size_t operator()(uint64_t r) const { + return r; + } + }; + + template<> + struct hash<int64_t> { + size_t operator()(int64_t r) const { + return r; + }; + }; +#endif + + template<> + struct hash<std::string> { + size_t operator()(const std::string &s) const { + return(__stl_hash_string(s.c_str())); + } + }; +} + + +#endif // __HASHMAP_HH__ diff --git a/src/base/hostinfo.cc b/src/base/hostinfo.cc new file mode 100644 index 000000000..a7c93e712 --- /dev/null +++ b/src/base/hostinfo.cc @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <ctype.h> +#include <errno.h> +#include <math.h> +#include <unistd.h> + +#include <cstdlib> +#include <cstring> +#include <string> + +#include "base/misc.hh" +#include "sim/host.hh" + +using namespace std; + +string +__get_hostname() +{ + char host[256]; + if (gethostname(host, sizeof host) == -1) + warn("could not get host name!"); + return host; +} + +string & +hostname() +{ + static string hostname = __get_hostname(); + return hostname; +} + +uint64_t +procInfo(char *filename, char *target) +{ + int done = 0; + char line[80]; + char format[80]; + long usage; + + FILE *fp = fopen(filename, "r"); + + while (fp && !feof(fp) && !done) { + if (fgets(line, 80, fp)) { + if (strncmp(line, target, strlen(target)) == 0) { + snprintf(format, sizeof(format), "%s %%ld", target); + sscanf(line, format, &usage); + + fclose(fp); + return usage ; + } + } + } + + if (fp) + fclose(fp); + + return 0; +} diff --git a/src/base/hostinfo.hh b/src/base/hostinfo.hh new file mode 100644 index 000000000..b6663ea69 --- /dev/null +++ b/src/base/hostinfo.hh @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __HOSTINFO_HH__ +#define __HOSTINFO_HH__ + +#include <string> + +#include "sim/host.hh" + +std::string &hostname(); + +uint64_t procInfo(char *filename, char *target); + +inline uint64_t memUsage() +{ return procInfo("/proc/self/status", "VmSize:"); } + +#endif // __HOSTINFO_HH__ diff --git a/src/base/hybrid_pred.cc b/src/base/hybrid_pred.cc new file mode 100644 index 000000000..73216489e --- /dev/null +++ b/src/base/hybrid_pred.cc @@ -0,0 +1,276 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Lisa Hsu + */ + +#include <string> +#include <sstream> + +#include "base/hybrid_pred.hh" +#include "base/statistics.hh" +#include "sim/stats.hh" + +using namespace std; + +HybridPredictor::HybridPredictor(const char *_p_name, const char *_z_name, + const char *_o_name, + unsigned _index_bits, unsigned _counter_bits, + unsigned _zero_change, unsigned _one_change, + unsigned _thresh, + unsigned _global_bits, + unsigned _global_thresh, + bool _reg_individual_stats) +{ + stringstream local_name, global_name; + + pred_name = _p_name; + one_name = _o_name; + zero_name = _z_name; + reg_individual_stats = _reg_individual_stats; + + local_name << pred_name.c_str() << ":L"; + local = new SaturatingCounterPred(local_name.str(), zero_name, one_name, + _index_bits, _counter_bits, + _zero_change, _one_change, _thresh); + + global_name << pred_name.c_str() << ":G"; + global = new SaturatingCounterPred(global_name.str(), zero_name, one_name, + 0, _global_bits, 1, 1, _global_thresh); +} + +void HybridPredictor::regStats() +{ + using namespace Stats; + + string p_name; + stringstream description; + + if (reg_individual_stats) + p_name = pred_name + ":A"; + else + p_name = pred_name; + + + // + // Number of predictions + // + stringstream num_zero_preds; + num_zero_preds << p_name << ":" << zero_name << ":preds"; + description << "number of predictions of " << zero_name; + pred_zero + .name(num_zero_preds.str()) + .desc(description.str()); + description.str(""); + + stringstream num_one_preds; + num_one_preds << p_name << ":" << one_name << ":preds"; + description << "number of predictions of " << one_name; + pred_one + .name(num_one_preds.str()) + .desc(description.str()) + ; + description.str(""); + + // + // Count the number of correct predictions + // + stringstream num_zero_correct; + num_zero_correct << p_name << ":" << zero_name << ":corr_preds"; + description << "number of correct " << zero_name << " preds" ; + correct_pred_zero + .name(num_zero_correct.str()) + .desc(description.str()) + ; + description.str(""); + + stringstream num_one_correct; + num_one_correct << p_name << ":" << one_name << ":corr_preds"; + description << "number of correct " << one_name << " preds" ; + correct_pred_one + .name(num_one_correct.str()) + .desc(description.str()) + ; + description.str(""); + + + // + // Number of predictor updates + // + stringstream num_zero_updates; + num_zero_updates << p_name << ":" << zero_name << ":updates" ; + description << "number of actual " << zero_name << "s" ; + record_zero + .name(num_zero_updates.str()) + .desc(description.str()) + ; + description.str(""); + + stringstream num_one_updates; + num_one_updates << p_name << ":" << one_name << ":updates" ; + description << "number of actual " << one_name << "s" ; + record_one + .name(num_one_updates.str()) + .desc(description.str()) + ; + description.str(""); + + // + // Local & Global predictor stats + // + if (reg_individual_stats) { + local->regStats(); + global->regStats(); + } +} + +void HybridPredictor::regFormulas() +{ + using namespace Stats; + + string p_name; + stringstream description; + stringstream name; + + if (reg_individual_stats) + p_name = pred_name + ":A"; + else + p_name = pred_name; + + // + // Number of predictions + // + name << p_name << ":predictions" ; + total_preds + .name(name.str()) + .desc("total number of predictions made") + ; + total_preds = pred_one + pred_zero; + name.str(""); + + // + // Fraction of all predictions that are one or zero + // + name << p_name << ":" << zero_name << ":pred_frac"; + description << "fraction of all preds that were " << zero_name ; + frac_preds_zero + .name(name.str()) + .desc(description.str()) + ; + frac_preds_zero = 100 * record_zero / total_preds; + description.str(""); + name.str(""); + + name << p_name << ":" << one_name << ":pred_frac"; + description << "fraction of all preds that were " << one_name ; + frac_preds_one + .name(name.str()) + .desc(description.str()) + ; + frac_preds_one = 100 * record_one / total_preds; + description.str(""); + name.str(""); + + // + // Count the number of correct predictions + // + name << p_name << ":correct_preds" ; + total_correct + .name(name.str()) + .desc("total number of correct predictions made") + ; + total_correct = correct_pred_one + correct_pred_zero; + name.str(""); + + + // + // Prediction accuracy rates + // + name << p_name << ":pred_rate"; + total_accuracy + .name(name.str()) + .desc("fraction of all preds that were correct") + ; + total_accuracy = 100 * total_correct / total_preds; + name.str(""); + + name << p_name << ":" << zero_name << ":pred_rate" ; + description << "fraction of "<< zero_name <<" preds that were correct"; + zero_accuracy + .name(name.str()) + .desc(description.str()) + ; + zero_accuracy = 100 * correct_pred_zero / pred_zero; + description.str(""); + name.str(""); + + name << p_name << ":" << one_name << ":pred_rate" ; + description << "fraction of "<< one_name <<" preds that were correct"; + one_accuracy + .name(name.str()) + .desc(description.str()) + ; + one_accuracy = 100 * correct_pred_one / pred_one; + description.str(""); + name.str(""); + + // + // Coverage + // + name << p_name << ":" << zero_name << ":coverage"; + description << "fraction of " << zero_name + << "s that were predicted correctly"; + zero_coverage + .name(name.str()) + .desc(description.str()) + ; + zero_coverage = 100 * correct_pred_zero / record_zero; + description.str(""); + name.str(""); + + name << p_name << ":" << one_name << ":coverage"; + description << "fraction of " << one_name + << "s that were predicted correctly"; + one_coverage + .name(name.str()) + .desc(description.str()) + ; + one_coverage = 100 * correct_pred_one / record_one; + description.str(""); + name.str(""); + + // + // Local & Global predictor stats + // + if (reg_individual_stats) { + local->regFormulas(); + global->regFormulas(); + } + +} + diff --git a/src/base/hybrid_pred.hh b/src/base/hybrid_pred.hh new file mode 100644 index 000000000..cb1d6003b --- /dev/null +++ b/src/base/hybrid_pred.hh @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Raasch + * Steve Reinhardt + */ + +//========================================================================== +// +// This predictor takes the AND of a "local" and a "global" predictor +// in order to determine its prediction. +// +// +// +// + +#ifndef __HYBRID_PRED_HH__ +#define __HYBRID_PRED_HH__ + +#include <string> + +#include "base/sat_counter.hh" +#include "base/statistics.hh" + +class HybridPredictor : public GenericPredictor +{ + private: + std::string pred_name; + std::string one_name; + std::string zero_name; + bool reg_individual_stats; + + SaturatingCounterPred *local; + SaturatingCounterPred *global; + + unsigned long max_index; + + // + // Stats + // + Stats::Scalar<> pred_one; //num_one_preds + Stats::Scalar<> pred_zero; //num_zero_preds + Stats::Scalar<> correct_pred_one; //num_one_correct + Stats::Scalar<> correct_pred_zero; //num_zero_correct + Stats::Scalar<> record_one; //num_one_updates + Stats::Scalar<> record_zero; //num_zero_updates + + Stats::Formula total_preds; + Stats::Formula frac_preds_zero; + Stats::Formula frac_preds_one; + Stats::Formula total_correct; + Stats::Formula total_accuracy; + Stats::Formula zero_accuracy; + Stats::Formula one_accuracy; + Stats::Formula zero_coverage; + Stats::Formula one_coverage; + + public: + HybridPredictor(const char *_p_name, const char *_z_name, + const char *_o_name, + unsigned _index_bits, unsigned _counter_bits, + unsigned _zero_change, unsigned _one_change, + unsigned _thresh, + unsigned _global_bits, unsigned _global_thresh, + bool _reg_individual_stats = false); + + void clear() { + global->clear(); + local->clear(); + } + + unsigned peek(unsigned long _index) { + unsigned l = local->peek(_index); + unsigned g = global->peek(_index); + + if (l && g) + return 1; + + return 0; + } + + unsigned value(unsigned long _index) { + unsigned l = local->peek(_index); + unsigned g = global->peek(_index); + + l = l & 0xFFFF; + g = g & 0xFFFF; + + return (l << 16) | g; + + } + + unsigned predict(unsigned long _index) { + unsigned l = local->predict(_index); + unsigned g = global->predict(_index); + + if (l && g) { + ++pred_one; + return 1; + } + + ++pred_zero; + return 0; + } + + + // + // This version need only be used if local/global statistics + // will be maintained + // + unsigned predict(unsigned long _index, unsigned &_pdata) { + unsigned l = local->predict(_index); + unsigned g = global->predict(_index); + + // + // bit 0 => local predictor result + // bit 1 => global predictor result + // + _pdata = 0; + if (l) + _pdata |= 1; + if (g) + _pdata |= 2; + if (l && g) { + ++pred_one; + return 1; + } + + ++pred_zero; + return 0; + } + + void record(unsigned long _index, unsigned _val, unsigned _predicted) { + + if (_val) { + local->record(_index, _val, 0); + global->record(_index, _val, 0); + ++record_one; + + if (_val == _predicted) { + ++correct_pred_one; + } + } else { + local->record(_index, _val, 0); + global->record(_index, _val, 0); + ++record_zero; + + if (_val == _predicted) + ++correct_pred_zero; + } + } + + void record(unsigned long _index, unsigned _val, unsigned _predicted, + unsigned _pdata) + { + + local->record(_index, _val, (_pdata & 1)); + global->record(_index, _val, ((_pdata & 2) ? 1 : 0)); + + + if (_val) { + ++record_one; + + if (_val == _predicted) + ++correct_pred_one; + } else { + ++record_zero; + + if (_val == _predicted) + ++correct_pred_zero; + } + } + + void regStats(); + void regFormulas(); +}; + + +#endif // _HYBRID_PRED_HH__ + diff --git a/src/base/inet.cc b/src/base/inet.cc new file mode 100644 index 000000000..b8da12a99 --- /dev/null +++ b/src/base/inet.cc @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <sstream> +#include <string> + +#include "base/cprintf.hh" +#include "sim/host.hh" +#include "base/inet.hh" + +using namespace std; +namespace Net { + +EthAddr::EthAddr() +{ + memset(data, 0, ETH_ADDR_LEN); +} + +EthAddr::EthAddr(const uint8_t ea[ETH_ADDR_LEN]) +{ + *data = *ea; +} + +EthAddr::EthAddr(const eth_addr &ea) +{ + *data = *ea.data; +} + +EthAddr::EthAddr(const std::string &addr) +{ + parse(addr); +} + +const EthAddr & +EthAddr::operator=(const eth_addr &ea) +{ + *data = *ea.data; + return *this; +} + +const EthAddr & +EthAddr::operator=(const std::string &addr) +{ + parse(addr); + return *this; +} + +void +EthAddr::parse(const std::string &addr) +{ + // the hack below is to make sure that ETH_ADDR_LEN is 6 otherwise + // the sscanf function won't work. + int bytes[ETH_ADDR_LEN == 6 ? ETH_ADDR_LEN : -1]; + if (sscanf(addr.c_str(), "%x:%x:%x:%x:%x:%x", &bytes[0], &bytes[1], + &bytes[2], &bytes[3], &bytes[4], &bytes[5]) != ETH_ADDR_LEN) { + memset(data, 0xff, ETH_ADDR_LEN); + return; + } + + for (int i = 0; i < ETH_ADDR_LEN; ++i) { + if (bytes[i] & ~0xff) { + memset(data, 0xff, ETH_ADDR_LEN); + return; + } + + data[i] = bytes[i]; + } +} + +string +EthAddr::string() const +{ + stringstream stream; + stream << *this; + return stream.str(); +} + +bool +operator==(const EthAddr &left, const EthAddr &right) +{ + return memcmp(left.bytes(), right.bytes(), ETH_ADDR_LEN); +} + +ostream & +operator<<(ostream &stream, const EthAddr &ea) +{ + const uint8_t *a = ea.addr(); + ccprintf(stream, "%x:%x:%x:%x:%x:%x", a[0], a[1], a[2], a[3], a[4], a[5]); + return stream; +} + +uint16_t +cksum(const IpPtr &ptr) +{ + int sum = ip_cksum_add(ptr->bytes(), ptr->hlen(), 0); + return ip_cksum_carry(sum); +} + +uint16_t +__tu_cksum(const IpPtr &ip) +{ + int tcplen = ip->len() - ip->hlen(); + int sum = ip_cksum_add(ip->payload(), tcplen, 0); + sum = ip_cksum_add(&ip->ip_src, 8, sum); // source and destination + sum += htons(ip->ip_p + tcplen); + return ip_cksum_carry(sum); +} + +uint16_t +cksum(const TcpPtr &tcp) +{ return __tu_cksum(IpPtr(tcp.packet())); } + +uint16_t +cksum(const UdpPtr &udp) +{ return __tu_cksum(IpPtr(udp.packet())); } + +bool +IpHdr::options(vector<const IpOpt *> &vec) const +{ + vec.clear(); + + const uint8_t *data = bytes() + sizeof(struct ip_hdr); + int all = hlen() - sizeof(struct ip_hdr); + while (all > 0) { + const IpOpt *opt = (const IpOpt *)data; + int len = opt->len(); + if (all < len) + return false; + + vec.push_back(opt); + all -= len; + data += len; + } + + return true; +} + +bool +TcpHdr::options(vector<const TcpOpt *> &vec) const +{ + vec.clear(); + + const uint8_t *data = bytes() + sizeof(struct tcp_hdr); + int all = off() - sizeof(struct tcp_hdr); + while (all > 0) { + const TcpOpt *opt = (const TcpOpt *)data; + int len = opt->len(); + if (all < len) + return false; + + vec.push_back(opt); + all -= len; + data += len; + } + + return true; +} + +bool +TcpOpt::sack(vector<SackRange> &vec) const +{ + vec.clear(); + + const uint8_t *data = bytes() + sizeof(struct tcp_hdr); + int all = len() - offsetof(tcp_opt, opt_data.sack); + while (all > 0) { + const uint16_t *sack = (const uint16_t *)data; + int len = sizeof(uint16_t) * 2; + if (all < len) { + vec.clear(); + return false; + } + + vec.push_back(RangeIn(ntohs(sack[0]), ntohs(sack[1]))); + all -= len; + data += len; + } + + return false; +} + +/* namespace Net */ } diff --git a/src/base/inet.hh b/src/base/inet.hh new file mode 100644 index 000000000..1bf7c585f --- /dev/null +++ b/src/base/inet.hh @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __BASE_INET_HH__ +#define __BASE_INET_HH__ + +#include <iosfwd> +#include <string> +#include <utility> +#include <vector> + +#include "base/range.hh" +#include "dev/etherpkt.hh" +#include "sim/host.hh" + +#include "dnet/os.h" +#include "dnet/eth.h" +#include "dnet/ip.h" +#include "dnet/ip6.h" +#include "dnet/addr.h" +#include "dnet/arp.h" +#include "dnet/icmp.h" +#include "dnet/tcp.h" +#include "dnet/udp.h" +#include "dnet/intf.h" +#include "dnet/route.h" +#include "dnet/fw.h" +#include "dnet/blob.h" +#include "dnet/rand.h" + +namespace Net { + +/* + * Ethernet Stuff + */ +struct EthAddr : protected eth_addr +{ + protected: + void parse(const std::string &addr); + + public: + EthAddr(); + EthAddr(const uint8_t ea[ETH_ADDR_LEN]); + EthAddr(const eth_addr &ea); + EthAddr(const std::string &addr); + const EthAddr &operator=(const eth_addr &ea); + const EthAddr &operator=(const std::string &addr); + + int size() const { return sizeof(eth_addr); } + + const uint8_t *bytes() const { return &data[0]; } + uint8_t *bytes() { return &data[0]; } + + const uint8_t *addr() const { return &data[0]; } + bool unicast() const { return data[0] == 0x00; } + bool multicast() const { return data[0] == 0x01; } + bool broadcast() const { return data[0] == 0xff; } + std::string string() const; + + operator uint64_t() const + { + uint64_t reg = 0; + reg |= ((uint64_t)data[0]) << 40; + reg |= ((uint64_t)data[1]) << 32; + reg |= ((uint64_t)data[2]) << 24; + reg |= ((uint64_t)data[3]) << 16; + reg |= ((uint64_t)data[4]) << 8; + reg |= ((uint64_t)data[5]) << 0; + return reg; + } + +}; + +std::ostream &operator<<(std::ostream &stream, const EthAddr &ea); +bool operator==(const EthAddr &left, const EthAddr &right); + +struct EthHdr : public eth_hdr +{ + uint16_t type() const { return ntohs(eth_type); } + const EthAddr &src() const { return *(EthAddr *)ð_src; } + const EthAddr &dst() const { return *(EthAddr *)ð_dst; } + + int size() const { return sizeof(eth_hdr); } + + const uint8_t *bytes() const { return (const uint8_t *)this; } + const uint8_t *payload() const { return bytes() + size(); } + uint8_t *bytes() { return (uint8_t *)this; } + uint8_t *payload() { return bytes() + size(); } +}; + +class EthPtr +{ + protected: + friend class IpPtr; + EthPacketPtr p; + + public: + EthPtr() {} + EthPtr(const EthPacketPtr &ptr) : p(ptr) { } + + EthHdr *operator->() { return (EthHdr *)p->data; } + EthHdr &operator*() { return *(EthHdr *)p->data; } + operator EthHdr *() { return (EthHdr *)p->data; } + + const EthHdr *operator->() const { return (const EthHdr *)p->data; } + const EthHdr &operator*() const { return *(const EthHdr *)p->data; } + operator const EthHdr *() const { return (const EthHdr *)p->data; } + + const EthPtr &operator=(const EthPacketPtr &ptr) { p = ptr; return *this; } + + const EthPacketPtr packet() const { return p; } + EthPacketPtr packet() { return p; } + bool operator!() const { return !p; } + operator bool() const { return p; } +}; + +/* + * IP Stuff + */ +struct IpOpt; +struct IpHdr : public ip_hdr +{ + uint8_t version() const { return ip_v; } + uint8_t hlen() const { return ip_hl * 4; } + uint8_t tos() const { return ip_tos; } + uint16_t len() const { return ntohs(ip_len); } + uint16_t id() const { return ntohs(ip_id); } + uint16_t frag_flags() const { return ntohs(ip_off) >> 13; } + uint16_t frag_off() const { return ntohs(ip_off) & 0x1fff; } + uint8_t ttl() const { return ip_ttl; } + uint8_t proto() const { return ip_p; } + uint16_t sum() const { return ip_sum; } + uint32_t src() const { return ntohl(ip_src); } + uint32_t dst() const { return ntohl(ip_dst); } + + void sum(uint16_t sum) { ip_sum = sum; } + + bool options(std::vector<const IpOpt *> &vec) const; + + int size() const { return hlen(); } + const uint8_t *bytes() const { return (const uint8_t *)this; } + const uint8_t *payload() const { return bytes() + size(); } + uint8_t *bytes() { return (uint8_t *)this; } + uint8_t *payload() { return bytes() + size(); } +}; + +class IpPtr +{ + protected: + friend class TcpPtr; + friend class UdpPtr; + EthPacketPtr p; + + const IpHdr *h() const + { return (const IpHdr *)(p->data + sizeof(eth_hdr)); } + IpHdr *h() { return (IpHdr *)(p->data + sizeof(eth_hdr)); } + + void set(const EthPacketPtr &ptr) + { + EthHdr *eth = (EthHdr *)ptr->data; + if (eth->type() == ETH_TYPE_IP) + p = ptr; + else + p = 0; + } + + public: + IpPtr() {} + IpPtr(const EthPacketPtr &ptr) { set(ptr); } + IpPtr(const EthPtr &ptr) { set(ptr.p); } + IpPtr(const IpPtr &ptr) : p(ptr.p) { } + + IpHdr *operator->() { return h(); } + IpHdr &operator*() { return *h(); } + operator IpHdr *() { return h(); } + + const IpHdr *operator->() const { return h(); } + const IpHdr &operator*() const { return *h(); } + operator const IpHdr *() const { return h(); } + + const IpPtr &operator=(const EthPacketPtr &ptr) { set(ptr); return *this; } + const IpPtr &operator=(const EthPtr &ptr) { set(ptr.p); return *this; } + const IpPtr &operator=(const IpPtr &ptr) { p = ptr.p; return *this; } + + const EthPacketPtr packet() const { return p; } + EthPacketPtr packet() { return p; } + bool operator!() const { return !p; } + operator bool() const { return p; } + operator bool() { return p; } +}; + +uint16_t cksum(const IpPtr &ptr); + +struct IpOpt : public ip_opt +{ + uint8_t type() const { return opt_type; } + uint8_t typeNumber() const { return IP_OPT_NUMBER(opt_type); } + uint8_t typeClass() const { return IP_OPT_CLASS(opt_type); } + uint8_t typeCopied() const { return IP_OPT_COPIED(opt_type); } + uint8_t len() const { return IP_OPT_TYPEONLY(type()) ? 1 : opt_len; } + + bool isNumber(int num) const { return typeNumber() == IP_OPT_NUMBER(num); } + bool isClass(int cls) const { return typeClass() == IP_OPT_CLASS(cls); } + bool isCopied(int cpy) const { return typeCopied() == IP_OPT_COPIED(cpy); } + + const uint8_t *data() const { return opt_data.data8; } + void sec(ip_opt_data_sec &sec) const; + void lsrr(ip_opt_data_rr &rr) const; + void ssrr(ip_opt_data_rr &rr) const; + void ts(ip_opt_data_ts &ts) const; + uint16_t satid() const { return ntohs(opt_data.satid); } + uint16_t mtup() const { return ntohs(opt_data.mtu); } + uint16_t mtur() const { return ntohs(opt_data.mtu); } + void tr(ip_opt_data_tr &tr) const; + const uint32_t *addext() const { return &opt_data.addext[0]; } + uint16_t rtralt() const { return ntohs(opt_data.rtralt); } + void sdb(std::vector<uint32_t> &vec) const; +}; + +/* + * TCP Stuff + */ +struct TcpOpt; +struct TcpHdr : public tcp_hdr +{ + uint16_t sport() const { return ntohs(th_sport); } + uint16_t dport() const { return ntohs(th_dport); } + uint32_t seq() const { return ntohl(th_seq); } + uint32_t ack() const { return ntohl(th_ack); } + uint8_t off() const { return th_off; } + uint8_t flags() const { return th_flags & 0x3f; } + uint16_t win() const { return ntohs(th_win); } + uint16_t sum() const { return th_sum; } + uint16_t urp() const { return ntohs(th_urp); } + + void sum(uint16_t sum) { th_sum = sum; } + + bool options(std::vector<const TcpOpt *> &vec) const; + + int size() const { return off(); } + const uint8_t *bytes() const { return (const uint8_t *)this; } + const uint8_t *payload() const { return bytes() + size(); } + uint8_t *bytes() { return (uint8_t *)this; } + uint8_t *payload() { return bytes() + size(); } +}; + +class TcpPtr +{ + protected: + EthPacketPtr p; + int off; + + const TcpHdr *h() const { return (const TcpHdr *)(p->data + off); } + TcpHdr *h() { return (TcpHdr *)(p->data + off); } + + void set(const EthPacketPtr &ptr, int offset) { p = ptr; off = offset; } + void set(const IpPtr &ptr) + { + if (ptr->proto() == IP_PROTO_TCP) + set(ptr.p, sizeof(eth_hdr) + ptr->hlen()); + else + set(0, 0); + } + + public: + TcpPtr() {} + TcpPtr(const IpPtr &ptr) { set(ptr); } + TcpPtr(const TcpPtr &ptr) : p(ptr.p), off(ptr.off) {} + + TcpHdr *operator->() { return h(); } + TcpHdr &operator*() { return *h(); } + operator TcpHdr *() { return h(); } + + const TcpHdr *operator->() const { return h(); } + const TcpHdr &operator*() const { return *h(); } + operator const TcpHdr *() const { return h(); } + + const TcpPtr &operator=(const IpPtr &i) { set(i); return *this; } + const TcpPtr &operator=(const TcpPtr &t) { set(t.p, t.off); return *this; } + + const EthPacketPtr packet() const { return p; } + EthPacketPtr packet() { return p; } + bool operator!() const { return !p; } + operator bool() const { return p; } + operator bool() { return p; } +}; + +uint16_t cksum(const TcpPtr &ptr); + +typedef Range<uint16_t> SackRange; + +struct TcpOpt : public tcp_opt +{ + uint8_t type() const { return opt_type; } + uint8_t len() const { return TCP_OPT_TYPEONLY(type()) ? 1 : opt_len; } + + bool isopt(int opt) const { return type() == opt; } + + const uint8_t *data() const { return opt_data.data8; } + + uint16_t mss() const { return ntohs(opt_data.mss); } + uint8_t wscale() const { return opt_data.wscale; } + bool sack(std::vector<SackRange> &vec) const; + uint32_t echo() const { return ntohl(opt_data.echo); } + uint32_t tsval() const { return ntohl(opt_data.timestamp[0]); } + uint32_t tsecr() const { return ntohl(opt_data.timestamp[1]); } + uint32_t cc() const { return ntohl(opt_data.cc); } + uint8_t cksum() const{ return opt_data.cksum; } + const uint8_t *md5() const { return opt_data.md5; } + + int size() const { return len(); } + const uint8_t *bytes() const { return (const uint8_t *)this; } + const uint8_t *payload() const { return bytes() + size(); } + uint8_t *bytes() { return (uint8_t *)this; } + uint8_t *payload() { return bytes() + size(); } +}; + +/* + * UDP Stuff + */ +struct UdpHdr : public udp_hdr +{ + uint16_t sport() const { return ntohs(uh_sport); } + uint16_t dport() const { return ntohs(uh_dport); } + uint16_t len() const { return ntohs(uh_ulen); } + uint16_t sum() const { return uh_sum; } + + void sum(uint16_t sum) { uh_sum = sum; } + + int size() const { return sizeof(udp_hdr); } + const uint8_t *bytes() const { return (const uint8_t *)this; } + const uint8_t *payload() const { return bytes() + size(); } + uint8_t *bytes() { return (uint8_t *)this; } + uint8_t *payload() { return bytes() + size(); } +}; + +class UdpPtr +{ + protected: + EthPacketPtr p; + int off; + + const UdpHdr *h() const { return (const UdpHdr *)(p->data + off); } + UdpHdr *h() { return (UdpHdr *)(p->data + off); } + + void set(const EthPacketPtr &ptr, int offset) { p = ptr; off = offset; } + void set(const IpPtr &ptr) + { + if (ptr->proto() == IP_PROTO_UDP) + set(ptr.p, sizeof(eth_hdr) + ptr->hlen()); + else + set(0, 0); + } + + public: + UdpPtr() {} + UdpPtr(const IpPtr &ptr) { set(ptr); } + UdpPtr(const UdpPtr &ptr) : p(ptr.p), off(ptr.off) {} + + UdpHdr *operator->() { return h(); } + UdpHdr &operator*() { return *h(); } + operator UdpHdr *() { return h(); } + + const UdpHdr *operator->() const { return h(); } + const UdpHdr &operator*() const { return *h(); } + operator const UdpHdr *() const { return h(); } + + const UdpPtr &operator=(const IpPtr &i) { set(i); return *this; } + const UdpPtr &operator=(const UdpPtr &t) { set(t.p, t.off); return *this; } + + const EthPacketPtr packet() const { return p; } + EthPacketPtr packet() { return p; } + bool operator!() const { return !p; } + operator bool() const { return p; } + operator bool() { return p; } +}; + +uint16_t cksum(const UdpPtr &ptr); + +/* namespace Net */ } + +#endif // __BASE_INET_HH__ diff --git a/src/base/inifile.cc b/src/base/inifile.cc new file mode 100644 index 000000000..4d504d04f --- /dev/null +++ b/src/base/inifile.cc @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#define USE_CPP + +#ifdef USE_CPP +#include <sys/signal.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#endif + +#include <fstream> +#include <iostream> + +#include <vector> +#include <string> + +#include "base/inifile.hh" +#include "base/str.hh" + +using namespace std; + +IniFile::IniFile() +{} + +IniFile::~IniFile() +{ + SectionTable::iterator i = table.begin(); + SectionTable::iterator end = table.end(); + + while (i != end) { + delete (*i).second; + ++i; + } +} + + +#ifdef USE_CPP +bool +IniFile::loadCPP(const string &file, vector<char *> &cppArgs) +{ + // Open the file just to verify that we can. Otherwise if the + // file doesn't exist or has bad permissions the user will get + // confusing errors from cpp/g++. + ifstream tmpf(file.c_str()); + + if (!tmpf.is_open()) + return false; + + tmpf.close(); + + char *cfile = strncpy(new char[file.size() + 1], file.c_str(), + file.size()); + char *dir = dirname(cfile); + char *dir_arg = NULL; + if (*dir != '.') { + string arg = "-I"; + arg += dir; + + dir_arg = new char[arg.size() + 1]; + strncpy(dir_arg, arg.c_str(), arg.size()); + } + + delete [] cfile; + + char tempfile[] = "/tmp/configXXXXXX"; + int tmp_fd = mkstemp(tempfile); + + int pid = fork(); + + if (pid == -1) + return false; + + if (pid == 0) { + char filename[FILENAME_MAX]; + string::size_type i = file.copy(filename, sizeof(filename) - 1); + filename[i] = '\0'; + + int arg_count = cppArgs.size(); + + char **args = new char *[arg_count + 20]; + + int nextArg = 0; + args[nextArg++] = "g++"; + args[nextArg++] = "-E"; + args[nextArg++] = "-P"; + args[nextArg++] = "-nostdinc"; + args[nextArg++] = "-nostdinc++"; + args[nextArg++] = "-x"; + args[nextArg++] = "c++"; + args[nextArg++] = "-undef"; + + for (int i = 0; i < arg_count; i++) + args[nextArg++] = cppArgs[i]; + + if (dir_arg) + args[nextArg++] = dir_arg; + + args[nextArg++] = filename; + args[nextArg++] = NULL; + + close(STDOUT_FILENO); + if (dup2(tmp_fd, STDOUT_FILENO) == -1) + exit(1); + + execvp("g++", args); + + exit(0); + } + + int retval; + waitpid(pid, &retval, 0); + + delete [] dir_arg; + + // check for normal completion of CPP + if (!WIFEXITED(retval) || WEXITSTATUS(retval) != 0) + return false; + + close(tmp_fd); + + bool status = false; + + status = load(tempfile); + + unlink(tempfile); + + return status; +} +#endif + +bool +IniFile::load(const string &file) +{ + ifstream f(file.c_str()); + + if (!f.is_open()) + return false; + + return load(f); +} + + +const string & +IniFile::Entry::getValue() const +{ + referenced = true; + return value; +} + + +void +IniFile::Section::addEntry(const std::string &entryName, + const std::string &value, + bool append) +{ + EntryTable::iterator ei = table.find(entryName); + + if (ei == table.end()) { + // new entry + table[entryName] = new Entry(value); + } + else if (append) { + // append new reult to old entry + ei->second->appendValue(value); + } + else { + // override old entry + ei->second->setValue(value); + } +} + + +bool +IniFile::Section::add(const std::string &assignment) +{ + string::size_type offset = assignment.find('='); + if (offset == string::npos) { + // no '=' found + cerr << "Can't parse .ini line " << assignment << endl; + return false; + } + + // if "+=" rather than just "=" then append value + bool append = (assignment[offset-1] == '+'); + + string entryName = assignment.substr(0, append ? offset-1 : offset); + string value = assignment.substr(offset + 1); + + eat_white(entryName); + eat_white(value); + + addEntry(entryName, value, append); + return true; +} + + +IniFile::Entry * +IniFile::Section::findEntry(const std::string &entryName) const +{ + referenced = true; + + EntryTable::const_iterator ei = table.find(entryName); + + return (ei == table.end()) ? NULL : ei->second; +} + + +IniFile::Section * +IniFile::addSection(const string §ionName) +{ + SectionTable::iterator i = table.find(sectionName); + + if (i != table.end()) { + return i->second; + } + else { + // new entry + Section *sec = new Section(); + table[sectionName] = sec; + return sec; + } +} + + +IniFile::Section * +IniFile::findSection(const string §ionName) const +{ + SectionTable::const_iterator i = table.find(sectionName); + + return (i == table.end()) ? NULL : i->second; +} + + +// Take string of the form "<section>:<parameter>=<value>" and add to +// database. Return true if successful, false if parse error. +bool +IniFile::add(const string &str) +{ + // find ':' + string::size_type offset = str.find(':'); + if (offset == string::npos) // no ':' found + return false; + + string sectionName = str.substr(0, offset); + string rest = str.substr(offset + 1); + + eat_white(sectionName); + Section *s = addSection(sectionName); + + return s->add(rest); +} + +bool +IniFile::load(istream &f) +{ + Section *section = NULL; + + while (!f.eof()) { + f >> ws; // Eat whitespace + if (f.eof()) { + break; + } + + string line; + getline(f, line); + if (line.size() == 0) + continue; + + eat_end_white(line); + int last = line.size() - 1; + + if (line[0] == '[' && line[last] == ']') { + string sectionName = line.substr(1, last - 1); + eat_white(sectionName); + section = addSection(sectionName); + continue; + } + + if (section == NULL) + continue; + + if (!section->add(line)) + return false; + } + + return true; +} + +bool +IniFile::find(const string §ionName, const string &entryName, + string &value) const +{ + Section *section = findSection(sectionName); + if (section == NULL) + return false; + + Entry *entry = section->findEntry(entryName); + if (entry == NULL) + return false; + + value = entry->getValue(); + + return true; +} + +bool +IniFile::sectionExists(const string §ionName) const +{ + return findSection(sectionName) != NULL; +} + + +bool +IniFile::Section::printUnreferenced(const string §ionName) +{ + bool unref = false; + bool search_unref_entries = false; + vector<string> unref_ok_entries; + + Entry *entry = findEntry("unref_entries_ok"); + if (entry != NULL) { + tokenize(unref_ok_entries, entry->getValue(), ' '); + if (unref_ok_entries.size()) { + search_unref_entries = true; + } + } + + for (EntryTable::iterator ei = table.begin(); + ei != table.end(); ++ei) { + const string &entryName = ei->first; + Entry *entry = ei->second; + + if (entryName == "unref_section_ok" || + entryName == "unref_entries_ok") + { + continue; + } + + if (!entry->isReferenced()) { + if (search_unref_entries && + (std::find(unref_ok_entries.begin(), unref_ok_entries.end(), + entryName) != unref_ok_entries.end())) + { + continue; + } + + cerr << "Parameter " << sectionName << ":" << entryName + << " not referenced." << endl; + unref = true; + } + } + + return unref; +} + + +bool +IniFile::printUnreferenced() +{ + bool unref = false; + + for (SectionTable::iterator i = table.begin(); + i != table.end(); ++i) { + const string §ionName = i->first; + Section *section = i->second; + + if (!section->isReferenced()) { + if (section->findEntry("unref_section_ok") == NULL) { + cerr << "Section " << sectionName << " not referenced." + << endl; + unref = true; + } + } + else { + if (section->printUnreferenced(sectionName)) { + unref = true; + } + } + } + + return unref; +} + + +void +IniFile::Section::dump(const string §ionName) +{ + for (EntryTable::iterator ei = table.begin(); + ei != table.end(); ++ei) { + cout << sectionName << ": " << (*ei).first << " => " + << (*ei).second->getValue() << "\n"; + } +} + +void +IniFile::dump() +{ + for (SectionTable::iterator i = table.begin(); + i != table.end(); ++i) { + i->second->dump(i->first); + } +} diff --git a/src/base/inifile.hh b/src/base/inifile.hh new file mode 100644 index 000000000..631b29b87 --- /dev/null +++ b/src/base/inifile.hh @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __INIFILE_HH__ +#define __INIFILE_HH__ + +#include <fstream> +#include <list> +#include <string> +#include <vector> + +#include "base/hashmap.hh" + +/** + * @file + * Declaration of IniFile object. + * @todo Change comments to match documentation style. + */ + +/// +/// This class represents the contents of a ".ini" file. +/// +/// It's basically a two level lookup table: a set of named sections, +/// where each section is a set of key/value pairs. Section names, +/// keys, and values are all uninterpreted strings. +/// +class IniFile +{ + protected: + + /// + /// A single key/value pair. + /// + class Entry + { + std::string value; ///< The entry value. + mutable bool referenced; ///< Has this entry been used? + + public: + /// Constructor. + Entry(const std::string &v) + : value(v), referenced(false) + { + } + + /// Has this entry been used? + bool isReferenced() { return referenced; } + + /// Fetch the value. + const std::string &getValue() const; + + /// Set the value. + void setValue(const std::string &v) { value = v; } + + /// Append the given string to the value. A space is inserted + /// between the existing value and the new value. Since this + /// operation is typically used with values that are + /// space-separated lists of tokens, this keeps the tokens + /// separate. + void appendValue(const std::string &v) { value += " "; value += v; } + }; + + /// + /// A section. + /// + class Section + { + /// EntryTable type. Map of strings to Entry object pointers. + typedef m5::hash_map<std::string, Entry *> EntryTable; + + EntryTable table; ///< Table of entries. + mutable bool referenced; ///< Has this section been used? + + public: + /// Constructor. + Section() + : table(), referenced(false) + { + } + + /// Has this section been used? + bool isReferenced() { return referenced; } + + /// Add an entry to the table. If an entry with the same name + /// already exists, the 'append' parameter is checked If true, + /// the new value will be appended to the existing entry. If + /// false, the new value will replace the existing entry. + void addEntry(const std::string &entryName, const std::string &value, + bool append); + + /// Add an entry to the table given a string assigment. + /// Assignment should be of the form "param=value" or + /// "param+=value" (for append). This funciton parses the + /// assignment statment and calls addEntry(). + /// @retval True for success, false if parse error. + bool add(const std::string &assignment); + + /// Find the entry with the given name. + /// @retval Pointer to the entry object, or NULL if none. + Entry *findEntry(const std::string &entryName) const; + + /// Print the unreferenced entries in this section to cerr. + /// Messages can be suppressed using "unref_section_ok" and + /// "unref_entries_ok". + /// @param sectionName Name of this section, for use in output message. + /// @retval True if any entries were printed. + bool printUnreferenced(const std::string §ionName); + + /// Print the contents of this section to cout (for debugging). + void dump(const std::string §ionName); + }; + + /// SectionTable type. Map of strings to Section object pointers. + typedef m5::hash_map<std::string, Section *> SectionTable; + + protected: + /// Hash of section names to Section object pointers. + SectionTable table; + + /// Look up section with the given name, creating a new section if + /// not found. + /// @retval Pointer to section object. + Section *addSection(const std::string §ionName); + + /// Look up section with the given name. + /// @retval Pointer to section object, or NULL if not found. + Section *findSection(const std::string §ionName) const; + + public: + /// Constructor. + IniFile(); + + /// Destructor. + ~IniFile(); + + /// Load parameter settings from given istream. This is a helper + /// function for load(string) and loadCPP(), which open a file + /// and then pass it here. + /// @retval True if successful, false if errors were encountered. + bool load(std::istream &f); + + /// Load the specified file, passing it through the C preprocessor. + /// Parameter settings found in the file will be merged with any + /// already defined in this object. + /// @param file The path of the file to load. + /// @param cppFlags Vector of extra flags to pass to cpp. + /// @retval True if successful, false if errors were encountered. + bool loadCPP(const std::string &file, std::vector<char *> &cppFlags); + + /// Load the specified file. + /// Parameter settings found in the file will be merged with any + /// already defined in this object. + /// @param file The path of the file to load. + /// @retval True if successful, false if errors were encountered. + bool load(const std::string &file); + + /// Take string of the form "<section>:<parameter>=<value>" or + /// "<section>:<parameter>+=<value>" and add to database. + /// @retval True if successful, false if parse error. + bool add(const std::string &s); + + /// Find value corresponding to given section and entry names. + /// Value is returned by reference in 'value' param. + /// @retval True if found, false if not. + bool find(const std::string §ion, const std::string &entry, + std::string &value) const; + + /// Determine whether the named section exists in the .ini file. + /// Note that the 'Section' class is (intentionally) not public, + /// so all clients can do is get a bool that says whether there + /// are any values in that section or not. + /// @return True if the section exists. + bool sectionExists(const std::string §ion) const; + + /// Print unreferenced entries in object. Iteratively calls + /// printUnreferend() on all the constituent sections. + bool printUnreferenced(); + + /// Dump contents to cout. For debugging. + void dump(); +}; + +#endif // __INIFILE_HH__ diff --git a/src/base/intmath.cc b/src/base/intmath.cc new file mode 100644 index 000000000..22414ea4b --- /dev/null +++ b/src/base/intmath.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2001, 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#include "base/intmath.hh" + +int +prevPrime(int n) +{ + int decr; + + // If the number is even, let's start with the previous odd number. + if (!(n & 1)) + --n; + + // Lets test for divisibility by 3. Then we will be able to easily + // avoid numbers that are divisible by 3 in the future. + decr = n % 3; + if (decr == 0) { + n -= 2; + decr = 2; + } + else if (decr == 1) + decr = 4; + + for (;;) { + if (isPrime(n)) + return n; + n -= decr; + // Toggle between 2 and 4 to prevent trying numbers that are known + // to be divisible by 3. + decr = 6 - decr; + } +} diff --git a/src/base/intmath.hh b/src/base/intmath.hh new file mode 100644 index 000000000..227012e30 --- /dev/null +++ b/src/base/intmath.hh @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2001, 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __INTMATH_HH__ +#define __INTMATH_HH__ + +#include <assert.h> + +#include "sim/host.hh" + +// Returns the prime number one less than n. +int prevPrime(int n); + +// Determine if a number is prime +template <class T> +inline bool +isPrime(T n) +{ + T i; + + if (n == 2 || n == 3) + return true; + + // Don't try every odd number to prove if it is a prime. + // Toggle between every 2nd and 4th number. + // (This is because every 6th odd number is divisible by 3.) + for (i = 5; i*i <= n; i += 6) { + if (((n % i) == 0 ) || ((n % (i + 2)) == 0) ) { + return false; + } + } + + return true; +} + +template <class T> +inline T +leastSigBit(T n) +{ + return n & ~(n - 1); +} + +template <class T> +inline bool +isPowerOf2(T n) +{ + return n != 0 && leastSigBit(n) == n; +} + +inline int +floorLog2(unsigned x) +{ + assert(x > 0); + + int y = 0; + + if (x & 0xffff0000) { y += 16; x >>= 16; } + if (x & 0x0000ff00) { y += 8; x >>= 8; } + if (x & 0x000000f0) { y += 4; x >>= 4; } + if (x & 0x0000000c) { y += 2; x >>= 2; } + if (x & 0x00000002) { y += 1; } + + return y; +} + +inline int +floorLog2(unsigned long x) +{ + assert(x > 0); + + int y = 0; + +#if defined(__LP64__) + if (x & ULL(0xffffffff00000000)) { y += 32; x >>= 32; } +#endif + if (x & 0xffff0000) { y += 16; x >>= 16; } + if (x & 0x0000ff00) { y += 8; x >>= 8; } + if (x & 0x000000f0) { y += 4; x >>= 4; } + if (x & 0x0000000c) { y += 2; x >>= 2; } + if (x & 0x00000002) { y += 1; } + + return y; +} + +inline int +floorLog2(unsigned long long x) +{ + assert(x > 0); + + int y = 0; + + if (x & ULL(0xffffffff00000000)) { y += 32; x >>= 32; } + if (x & ULL(0x00000000ffff0000)) { y += 16; x >>= 16; } + if (x & ULL(0x000000000000ff00)) { y += 8; x >>= 8; } + if (x & ULL(0x00000000000000f0)) { y += 4; x >>= 4; } + if (x & ULL(0x000000000000000c)) { y += 2; x >>= 2; } + if (x & ULL(0x0000000000000002)) { y += 1; } + + return y; +} + +inline int +floorLog2(int x) +{ + assert(x > 0); + return floorLog2((unsigned)x); +} + +inline int +floorLog2(long x) +{ + assert(x > 0); + return floorLog2((unsigned long)x); +} + +inline int +floorLog2(long long x) +{ + assert(x > 0); + return floorLog2((unsigned long long)x); +} + +template <class T> +inline int +ceilLog2(T n) +{ + if (n == 1) + return 0; + + return floorLog2(n - (T)1) + 1; +} + +template <class T> +inline T +floorPow2(T n) +{ + return (T)1 << floorLog2(n); +} + +template <class T> +inline T +ceilPow2(T n) +{ + return (T)1 << ceilLog2(n); +} + +template <class T> +inline T +divCeil(T a, T b) +{ + return (a + b - 1) / b; +} + +template <class T> +inline T +roundUp(T val, int align) +{ + T mask = (T)align - 1; + return (val + mask) & ~mask; +} + +template <class T> +inline T +roundDown(T val, int align) +{ + T mask = (T)align - 1; + return val & ~mask; +} + +inline bool +isHex(char c) +{ + return c >= '0' && c <= '9' || + c >= 'A' && c <= 'F' || + c >= 'a' && c <= 'f'; +} + +inline bool +isOct(char c) +{ + return c >= '0' && c <= '7'; +} + +inline bool +isDec(char c) +{ + return c >= '0' && c <= '9'; +} + +inline int +hex2Int(char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + + if (c >= 'A' && c <= 'F') + return (c - 'A') + 10; + + if (c >= 'a' && c <= 'f') + return (c - 'a') + 10; + + return 0; +} + +#endif // __INTMATH_HH__ diff --git a/src/base/kgdb.h b/src/base/kgdb.h new file mode 100644 index 000000000..104244d0b --- /dev/null +++ b/src/base/kgdb.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)remote-sl.h 8.1 (Berkeley) 6/11/93 + */ + +/* $NetBSD: kgdb.h,v 1.4 1998/08/13 02:10:59 eeh Exp $ */ + +#ifndef __KGDB_H__ +#define __KGDB_H__ + +/* + * Message types. + */ +#define KGDB_SIGNAL '?' // last sigal +#define KGDB_SET_BAUD 'b' // set baud (deprecated) +#define KGDB_SET_BREAK 'B' // set breakpoint (deprecated) +#define KGDB_CONT 'c' // resume +#define KGDB_ASYNC_CONT 'C' // continue with signal +#define KGDB_DEBUG 'd' // toggle debug flags (deprecated) +#define KGDB_DETACH 'D' // detach remote gdb +#define KGDB_REG_R 'g' // read general registers +#define KGDB_REG_W 'G' // write general registers +#define KGDB_SET_THREAD 'H' // set thread +#define KGDB_CYCLE_STEP 'i' // step a single cycle +#define KGDB_SIG_CYCLE_STEP 'I' // signal then single cycle step +#define KGDB_KILL 'k' // kill program +#define KGDB_MEM_R 'm' // read memory +#define KGDB_MEM_W 'M' // write memory +#define KGDB_READ_REG 'p' // read register +#define KGDB_SET_REG 'P' // write register +#define KGDB_QUERY_VAR 'q' // query variable +#define KGDB_SET_VAR 'Q' // set variable +#define KGDB_RESET 'r' // reset system. (Deprecated) +#define KGDB_STEP 's' // step +#define KGDB_ASYNC_STEP 'S' // signal and step +#define KGDB_THREAD_ALIVE 'T' // find out if the thread is alive. +#define KGDB_TARGET_EXIT 'W' // target exited +#define KGDB_BINARY_DLOAD 'X' // write memory +#define KGDB_CLR_HW_BKPT 'z' // remove breakpoint or watchpoint +#define KGDB_SET_HW_BKPT 'Z' // insert breakpoint or watchpoint + +/* + * start of frame/end of frame + */ +#define KGDB_START '$' +#define KGDB_END '#' +#define KGDB_GOODP '+' +#define KGDB_BADP '-' + +/* + * Stuff for KGDB. + */ +#define KGDB_NUMREGS 66 /* from tm-alpha.h, NUM_REGS */ +#define KGDB_REG_V0 0 +#define KGDB_REG_T0 1 +#define KGDB_REG_T1 2 +#define KGDB_REG_T2 3 +#define KGDB_REG_T3 4 +#define KGDB_REG_T4 5 +#define KGDB_REG_T5 6 +#define KGDB_REG_T6 7 +#define KGDB_REG_T7 8 +#define KGDB_REG_S0 9 +#define KGDB_REG_S1 10 +#define KGDB_REG_S2 11 +#define KGDB_REG_S3 12 +#define KGDB_REG_S4 13 +#define KGDB_REG_S5 14 +#define KGDB_REG_S6 15 /* FP */ +#define KGDB_REG_A0 16 +#define KGDB_REG_A1 17 +#define KGDB_REG_A2 18 +#define KGDB_REG_A3 19 +#define KGDB_REG_A4 20 +#define KGDB_REG_A5 21 +#define KGDB_REG_T8 22 +#define KGDB_REG_T9 23 +#define KGDB_REG_T10 24 +#define KGDB_REG_T11 25 +#define KGDB_REG_RA 26 +#define KGDB_REG_T12 27 +#define KGDB_REG_AT 28 +#define KGDB_REG_GP 29 +#define KGDB_REG_SP 30 +#define KGDB_REG_ZERO 31 +#define KGDB_REG_F0 32 +#define KGDB_REG_F1 33 +#define KGDB_REG_F2 34 +#define KGDB_REG_F3 35 +#define KGDB_REG_F4 36 +#define KGDB_REG_F5 37 +#define KGDB_REG_F6 38 +#define KGDB_REG_F7 39 +#define KGDB_REG_F8 40 +#define KGDB_REG_F9 41 +#define KGDB_REG_F10 42 +#define KGDB_REG_F11 43 +#define KGDB_REG_F12 44 +#define KGDB_REG_F13 45 +#define KGDB_REG_F14 46 +#define KGDB_REG_F15 47 +#define KGDB_REG_F16 48 +#define KGDB_REG_F17 49 +#define KGDB_REG_F18 50 +#define KGDB_REG_F19 51 +#define KGDB_REG_F20 52 +#define KGDB_REG_F21 53 +#define KGDB_REG_F22 54 +#define KGDB_REG_F23 55 +#define KGDB_REG_F24 56 +#define KGDB_REG_F25 57 +#define KGDB_REG_F26 58 +#define KGDB_REG_F27 59 +#define KGDB_REG_F28 60 +#define KGDB_REG_F29 61 +#define KGDB_REG_F30 62 +#define KGDB_REG_F31 63 +#define KGDB_REG_PC 64 +#define KGDB_REG_VFP 65 + +/* Too much? Must be large enough for register transfer. */ +#define KGDB_BUFLEN 1024 + +/* + * Kernel Entry Vectors. [OSF/1 PALcode Specific] + */ + +#define ALPHA_KENTRY_INT 0 +#define ALPHA_KENTRY_ARITH 1 +#define ALPHA_KENTRY_MM 2 +#define ALPHA_KENTRY_IF 3 +#define ALPHA_KENTRY_UNA 4 +#define ALPHA_KENTRY_SYS 5 + +#endif /* __KGDB_H__ */ diff --git a/src/base/loader/aout_object.cc b/src/base/loader/aout_object.cc new file mode 100644 index 000000000..6691bd4ae --- /dev/null +++ b/src/base/loader/aout_object.cc @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#include <string> + +#include "base/loader/aout_object.hh" + +#include "base/loader/symtab.hh" + +#include "base/trace.hh" // for DPRINTF + +#include "base/loader/exec_aout.h" + +using namespace std; + +ObjectFile * +AoutObject::tryFile(const string &fname, int fd, size_t len, uint8_t *data) +{ + if (!N_BADMAG(*(aout_exechdr *)data)) { + // right now this is only used for Alpha PAL code + return new AoutObject(fname, fd, len, data, + ObjectFile::Alpha, ObjectFile::UnknownOpSys); + } + else { + return NULL; + } +} + + +AoutObject::AoutObject(const string &_filename, int _fd, + size_t _len, uint8_t *_data, + Arch _arch, OpSys _opSys) + : ObjectFile(_filename, _fd, _len, _data, _arch, _opSys) +{ + execHdr = (aout_exechdr *)fileData; + + entry = execHdr->entry; + + text.baseAddr = N_TXTADDR(*execHdr); + text.size = execHdr->tsize; + text.fileImage = fileData + N_TXTOFF(*execHdr); + + data.baseAddr = N_DATADDR(*execHdr); + data.size = execHdr->dsize; + data.fileImage = fileData + N_DATOFF(*execHdr); + + bss.baseAddr = N_BSSADDR(*execHdr); + bss.size = execHdr->bsize; + bss.fileImage = NULL; + + DPRINTFR(Loader, "text: 0x%x %d\ndata: 0x%x %d\nbss: 0x%x %d\n", + text.baseAddr, text.size, data.baseAddr, data.size, + bss.baseAddr, bss.size); +} + + +bool +AoutObject::loadGlobalSymbols(SymbolTable *symtab) +{ + // a.out symbols not supported yet + return false; +} + +bool +AoutObject::loadLocalSymbols(SymbolTable *symtab) +{ + // a.out symbols not supported yet + return false; +} diff --git a/src/base/loader/aout_object.hh b/src/base/loader/aout_object.hh new file mode 100644 index 000000000..d180d69f3 --- /dev/null +++ b/src/base/loader/aout_object.hh @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __AOUT_OBJECT_HH__ +#define __AOUT_OBJECT_HH__ + +#include "base/loader/object_file.hh" + +// forward decls: avoid including exec_aout.h here +struct aout_exechdr; + +class AoutObject : public ObjectFile +{ + protected: + aout_exechdr *execHdr; + + AoutObject(const std::string &_filename, int _fd, + size_t _len, uint8_t *_data, + Arch _arch, OpSys _opSys); + + public: + virtual ~AoutObject() {} + + virtual bool loadGlobalSymbols(SymbolTable *symtab); + virtual bool loadLocalSymbols(SymbolTable *symtab); + + static ObjectFile *tryFile(const std::string &fname, int fd, + size_t len, uint8_t *data); +}; + +#endif // __AOUT_OBJECT_HH__ diff --git a/src/base/loader/coff_sym.h b/src/base/loader/coff_sym.h new file mode 100644 index 000000000..4c6540395 --- /dev/null +++ b/src/base/loader/coff_sym.h @@ -0,0 +1,519 @@ +/* + * Copyright (c) 2003, 2005-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +/* + * Taken from binutils-2.14.90.0.5 include/coff/sym.h + */ + +/* Declarations of internal format of MIPS ECOFF symbols. + Originally contributed by MIPS Computer Systems and Third Eye Software. + Changes contributed by Cygnus Support are in the public domain. + + This file is just aggregated with the files that make up the GNU + release; it is not considered part of GAS, GDB, or other GNU + programs. */ + +/* + * |-----------------------------------------------------------| + * | Copyright (c) 1992, 1991, 1990 MIPS Computer Systems, Inc.| + * | MIPS Computer Systems, Inc. grants reproduction and use | + * | rights to all parties, PROVIDED that this comment is | + * | maintained in the copy. | + * |-----------------------------------------------------------| + */ +#ifndef _SYM_H +#define _SYM_H + +/* (C) Copyright 1984 by Third Eye Software, Inc. + * + * Third Eye Software, Inc. grants reproduction and use rights to + * all parties, PROVIDED that this comment is maintained in the copy. + * + * Third Eye makes no claims about the applicability of this + * symbol table to a particular use. + */ + +/* + * This file contains the definition of the Third Eye Symbol Table. + * + * Symbols are assumed to be in 'encounter order' - i.e. the order that + * the things they represent were encountered by the compiler/assembler/loader. + * EXCEPT for globals! These are assumed to be bunched together, + * probably right after the last 'normal' symbol. Globals ARE sorted + * in ascending order. + * + * ----------------------------------------------------------------------- + * A brief word about Third Eye naming/use conventions: + * + * All arrays and index's are 0 based. + * All "ifooMax" values are the highest legal value PLUS ONE. This makes + * them good for allocating arrays, etc. All checks are "ifoo < ifooMax". + * + * "isym" Index into the SYMbol table. + * "ipd" Index into the Procedure Descriptor array. + * "ifd" Index into the File Descriptor array. + * "iss" Index into String Space. + * "cb" Count of Bytes. + * "rgPd" array whose domain is "0..ipdMax-1" and RanGe is PDR. + * "rgFd" array whose domain is "0..ifdMax-1" and RanGe is FDR. + */ + + +/* + * Symbolic Header (HDR) structure. + * As long as all the pointers are set correctly, + * we don't care WHAT order the various sections come out in! + * + * A file produced solely for the use of CDB will probably NOT have + * any instructions or data areas in it, as these are available + * in the original. + */ + +typedef struct ecoff_symhdr { + coff_short magic; /* to verify validity of the table */ + coff_short vstamp; /* version stamp */ + coff_int ilineMax; /* number of line number entries */ + coff_int idnMax; /* max index into dense number table */ + coff_int ipdMax; /* number of procedures */ + coff_int isymMax; /* number of local symbols */ + coff_int ioptMax; /* max index into optimization symbol entries */ + coff_int iauxMax; /* number of auxillary symbol entries */ + coff_int issMax; /* max index into local strings */ + coff_int issExtMax; /* max index into external strings */ + coff_int ifdMax; /* number of file descriptor entries */ + coff_int crfd; /* number of relative file descriptor entries */ + coff_int iextMax; /* max index into external symbols */ + coff_addr cbLine; /* number of bytes for line number entries */ + coff_addr cbLineOffset; /* offset to start of line number entries*/ + coff_addr cbDnOffset; /* offset to start dense number table */ + coff_addr cbPdOffset; /* offset to procedure descriptor table */ + coff_addr cbSymOffset; /* offset to start of local symbols*/ + coff_addr cbOptOffset; /* offset to optimization symbol entries */ + coff_addr cbAuxOffset; /* offset to start of auxillary symbol entries*/ + coff_addr cbSsOffset; /* offset to start of local strings */ + coff_addr cbSsExtOffset; /* offset to start of external strings */ + coff_addr cbFdOffset; /* offset to file descriptor table */ + coff_addr cbRfdOffset; /* offset to relative file descriptor table */ + coff_addr cbExtOffset; /* offset to start of external symbol entries*/ + /* If you add machine dependent fields, add them here */ +} HDRR, *pHDRR; +#define cbHDRR sizeof(HDRR) +#define hdrNil ((pHDRR)0) + +/* + * The FDR and PDR structures speed mapping of address <-> name. + * They are sorted in ascending memory order and are kept in + * memory by CDB at runtime. + */ + +/* + * File Descriptor + * + * There is one of these for EVERY FILE, whether compiled with + * full debugging symbols or not. The name of a file should be + * the path name given to the compiler. This allows the user + * to simply specify the names of the directories where the COMPILES + * were done, and we will be able to find their files. + * A field whose comment starts with "R - " indicates that it will be + * setup at runtime. + */ +typedef struct ecoff_fdr { + coff_addr adr; /* memory address of beginning of file */ + coff_addr cbLineOffset; /* byte offset from header for this file ln's */ + coff_addr cbLine; /* size of lines for this file */ + coff_addr cbSs; /* number of bytes in the ss */ + coff_int rss; /* file name (of source, if known) */ + coff_int issBase; /* file's string space */ + coff_int isymBase; /* beginning of symbols */ + coff_int csym; /* count file's of symbols */ + coff_int ilineBase; /* file's line symbols */ + coff_int cline; /* count of file's line symbols */ + coff_int ioptBase; /* file's optimization entries */ + coff_int copt; /* count of file's optimization entries */ + coff_int ipdFirst; /* start of procedures for this file */ + coff_int cpd; /* count of procedures for this file */ + coff_int iauxBase; /* file's auxiliary entries */ + coff_int caux; /* count of file's auxiliary entries */ + coff_int rfdBase; /* index into the file indirect table */ + coff_int crfd; /* count file indirect entries */ + unsigned lang: 5; /* language for this file */ + unsigned fMerge : 1; /* whether this file can be merged */ + unsigned fReadin : 1; /* true if it was read in (not just created) */ + unsigned fBigendian : 1;/* if set, was compiled on big endian machine */ + /* aux's will be in compile host's sex */ + unsigned glevel : 2; /* level this file was compiled with */ + unsigned reserved : 22; /* reserved for future use */ + coff_uint reserved2; +} FDR, *pFDR; +#define cbFDR sizeof(FDR) +#define fdNil ((pFDR)0) +#define ifdNil -1 +#define ifdTemp 0 +#define ilnNil -1 + + +/* + * Procedure Descriptor + * + * There is one of these for EVERY TEXT LABEL. + * If a procedure is in a file with full symbols, then isym + * will point to the PROC symbols, else it will point to the + * global symbol for the label. + */ + +typedef struct pdr { + coff_addr adr; /* memory address of start of procedure */ + coff_addr cbLineOffset; /* byte offset for this procedure from the fd base */ + coff_int isym; /* start of local symbol entries */ + coff_int iline; /* start of line number entries*/ + coff_uint regmask; /* save register mask */ + coff_int regoffset; /* save register offset */ + coff_int iopt; /* start of optimization symbol entries*/ + coff_uint fregmask; /* save floating point register mask */ + coff_int fregoffset; /* save floating point register offset */ + coff_int frameoffset; /* frame size */ + coff_int lnLow; /* lowest line in the procedure */ + coff_int lnHigh; /* highest line in the procedure */ + /* These fields are new for 64 bit ECOFF. */ + unsigned gp_prologue : 8; /* byte size of GP prologue */ + unsigned gp_used : 1; /* true if the procedure uses GP */ + unsigned reg_frame : 1; /* true if register frame procedure */ + unsigned prof : 1; /* true if compiled with -pg */ + unsigned reserved : 13; /* reserved: must be zero */ + unsigned localoff : 8; /* offset of local variables from vfp */ + coff_short framereg; /* frame pointer register */ + coff_short pcreg; /* offset or reg of return pc */ +} PDR, *pPDR; +#define cbPDR sizeof(PDR) +#define pdNil ((pPDR) 0) +#define ipdNil -1 + +/* + * The structure of the runtime procedure descriptor created by the loader + * for use by the static exception system. + */ +/* + * If 0'd out because exception_info chokes Visual C++ and because there + * don't seem to be any references to this structure elsewhere in gdb. + */ +#if 0 +typedef struct runtime_pdr { + coff_addr adr; /* memory address of start of procedure */ + coff_uint regmask; /* save register mask */ + coff_int regoffset; /* save register offset */ + coff_uint fregmask; /* save floating point register mask */ + coff_int fregoffset; /* save floating point register offset */ + coff_int frameoffset; /* frame size */ + coff_ushort framereg; /* frame pointer register */ + coff_ushort pcreg; /* offset or reg of return pc */ + coff_int irpss; /* index into the runtime string table */ + coff_uint reserved; + struct exception_info *exception_info;/* pointer to exception array */ +} RPDR, *pRPDR; +#define cbRPDR sizeof(RPDR) +#define rpdNil ((pRPDR) 0) +#endif + +/* + * Line Numbers + * + * Line Numbers are segregated from the normal symbols because they + * are [1] smaller , [2] are of no interest to your + * average loader, and [3] are never needed in the middle of normal + * scanning and therefore slow things down. + * + * By definition, the first LINER for any given procedure will have + * the first line of a procedure and represent the first address. + */ + +typedef coff_int LINER, *pLINER; +#define lineNil ((pLINER)0) +#define cbLINER sizeof(LINER) +#define ilineNil -1 + + + +/* + * The Symbol Structure (GFW, to those who Know!) + */ + +typedef struct ecoff_sym { + coff_long value; /* value of symbol */ + coff_int iss; /* index into String Space of name */ + unsigned st : 6; /* symbol type */ + unsigned sc : 5; /* storage class - text, data, etc */ + unsigned reserved : 1; /* reserved */ + unsigned index : 20; /* index into sym/aux table */ +} SYMR, *pSYMR; +#define symNil ((pSYMR)0) +#define cbSYMR sizeof(SYMR) +#define isymNil -1 +#define indexNil 0xfffff +#define issNil -1 +#define issNull 0 + + +/* The following converts a memory resident string to an iss. + * This hack is recognized in SbFIss, in sym.c of the debugger. + */ +#define IssFSb(sb) (0x80000000 | ((coff_ulong)(sb))) + +/* E X T E R N A L S Y M B O L R E C O R D + * + * Same as the SYMR except it contains file context to determine where + * the index is. + */ +typedef struct ecoff_extsym { + SYMR asym; /* symbol for the external */ + unsigned jmptbl:1; /* symbol is a jump table entry for shlibs */ + unsigned cobol_main:1; /* symbol is a cobol main procedure */ + unsigned weakext:1; /* symbol is weak external */ + unsigned reserved:29; /* reserved for future use */ + coff_int ifd; /* where the iss and index fields point into */ +} EXTR, *pEXTR; +#define extNil ((pEXTR)0) +#define cbEXTR sizeof(EXTR) + + +/* A U X I L L A R Y T Y P E I N F O R M A T I O N */ + +/* + * Type Information Record + */ +typedef struct { + unsigned fBitfield : 1; /* set if bit width is specified */ + unsigned continued : 1; /* indicates additional TQ info in next AUX */ + unsigned bt : 6; /* basic type */ + unsigned tq4 : 4; + unsigned tq5 : 4; + /* ---- 16 bit boundary ---- */ + unsigned tq0 : 4; + unsigned tq1 : 4; /* 6 type qualifiers - tqPtr, etc. */ + unsigned tq2 : 4; + unsigned tq3 : 4; +} TIR, *pTIR; +#define cbTIR sizeof(TIR) +#define tiNil ((pTIR)0) +#define itqMax 6 + +/* + * Relative symbol record + * + * If the rfd field is 4095, the index field indexes into the global symbol + * table. + */ + +typedef struct { + unsigned rfd : 12; /* index into the file indirect table */ + unsigned index : 20; /* index int sym/aux/iss tables */ +} RNDXR, *pRNDXR; +#define cbRNDXR sizeof(RNDXR) +#define rndxNil ((pRNDXR)0) + +/* dense numbers or sometimes called block numbers are stored in this type, + * a rfd of 0xffffffff is an index into the global table. + */ +typedef struct { + coff_uint rfd; /* index into the file table */ + coff_uint index; /* index int sym/aux/iss tables */ +} DNR, *pDNR; +#define cbDNR sizeof(DNR) +#define dnNil ((pDNR)0) + + + +/* + * Auxillary information occurs only if needed. + * It ALWAYS occurs in this order when present. + + isymMac used by stProc only + TIR type info + TIR additional TQ info (if first TIR was not enough) + rndx if (bt == btStruct,btUnion,btEnum,btSet,btRange, + btTypedef): + rsym.index == iaux for btSet or btRange + else rsym.index == isym + dimLow btRange, btSet + dimMac btRange, btSet + rndx0 As many as there are tq arrays + dimLow0 + dimHigh0 + ... + rndxMax-1 + dimLowMax-1 + dimHighMax-1 + width in bits if (bit field), width in bits. + */ +#define cAuxMax (6 + (idimMax*3)) + +/* a union of all possible info in the AUX universe */ +typedef union { + TIR ti; /* type information record */ + RNDXR rndx; /* relative index into symbol table */ + coff_int dnLow; /* low dimension */ + coff_int dnHigh; /* high dimension */ + coff_int isym; /* symbol table index (end of proc) */ + coff_int iss; /* index into string space (not used) */ + coff_int width; /* width for non-default sized struc fields */ + coff_int count; /* count of ranges for variant arm */ +} AUXU, *pAUXU; +#define cbAUXU sizeof(AUXU) +#define auxNil ((pAUXU)0) +#define iauxNil -1 + + +/* + * Optimization symbols + * + * Optimization symbols contain some overlap information with the normal + * symbol table. In particular, the proc information + * is somewhat redundant but necessary to easily find the other information + * present. + * + * All of the offsets are relative to the beginning of the last otProc + */ + +typedef struct { + unsigned ot: 8; /* optimization type */ + unsigned value: 24; /* address where we are moving it to */ + RNDXR rndx; /* points to a symbol or opt entry */ + coff_ulong offset; /* relative offset this occured */ +} OPTR, *pOPTR; +#define optNil ((pOPTR) 0) +#define cbOPTR sizeof(OPTR) +#define ioptNil -1 + +/* + * File Indirect + * + * When a symbol is referenced across files the following procedure is used: + * 1) use the file index to get the File indirect entry. + * 2) use the file indirect entry to get the File descriptor. + * 3) add the sym index to the base of that file's sym table + * + */ + +typedef coff_long RFDT, *pRFDT; +#define cbRFDT sizeof(RFDT) +#define rfdNil -1 + +/* + * The file indirect table in the mips loader is known as an array of FITs. + * This is done to keep the code in the loader readable in the area where + * these tables are merged. Note this is only a name change. + */ +typedef coff_int FIT, *pFIT; +#define cbFIT sizeof(FIT) +#define ifiNil -1 +#define fiNil ((pFIT) 0) + +#ifdef _LANGUAGE_PASCAL +#define ifdNil -1 +#define ilnNil -1 +#define ipdNil -1 +#define ilineNil -1 +#define isymNil -1 +#define indexNil 16#fffff +#define issNil -1 +#define issNull 0 +#define itqMax 6 +#define iauxNil -1 +#define ioptNil -1 +#define rfdNil -1 +#define ifiNil -1 +#endif /* _LANGUAGE_PASCAL */ + + +/* Dense numbers + * + * Rather than use file index, symbol index pairs to represent symbols + * and globals, we use dense number so that they can be easily embeded + * in intermediate code and the programs that process them can + * use direct access tabls instead of hash table (which would be + * necesary otherwise because of the sparse name space caused by + * file index, symbol index pairs. Dense number are represented + * by RNDXRs. + */ + +/* + * The following table defines the meaning of each SYM field as + * a function of the "st". (scD/B == scData OR scBss) + * + * Note: the value "isymMac" is used by symbols that have the concept + * of enclosing a block of related information. This value is the + * isym of the first symbol AFTER the end associated with the primary + * symbol. For example if a procedure was at isym==90 and had an + * isymMac==155, the associated end would be at isym==154, and the + * symbol at 155 would probably (although not necessarily) be the + * symbol for the next procedure. This allows rapid skipping over + * internal information of various sorts. "stEnd"s ALWAYS have the + * isym of the primary symbol that started the block. + * + +ST SC VALUE INDEX +-------- ------ -------- ------ +stFile scText address isymMac +stLabel scText address --- +stGlobal scD/B address iaux +stStatic scD/B address iaux +stParam scAbs offset iaux +stLocal scAbs offset iaux +stProc scText address iaux (isymMac is first AUX) +stStaticProc scText address iaux (isymMac is first AUX) + +stMember scNil ordinal --- (if member of enum) + (mipsread thinks the case below has a bit, not byte, offset.) +stMember scNil byte offset iaux (if member of struct/union) +stMember scBits bit offset iaux (bit field spec) + +stBlock scText address isymMac (text block) + (the code seems to think that rather than scNil, we see scInfo for + the two cases below.) +stBlock scNil cb isymMac (struct/union member define) +stBlock scNil cMembers isymMac (enum member define) + + (New types added by SGI to simplify things:) +stStruct scInfo cb isymMac (struct type define) +stUnion scInfo cb isymMac (union type define) +stEnum scInfo cMembers isymMac (enum type define) + +stEnd scText address isymStart +stEnd scNil ------- isymStart (struct/union/enum) + +stTypedef scNil ------- iaux +stRegReloc sc??? value old register number +stForward sc??? new address isym to original symbol + +stConstant scInfo value --- (scalar) +stConstant scInfo iss --- (complex, e.g. string) + + * + */ +#endif diff --git a/src/base/loader/coff_symconst.h b/src/base/loader/coff_symconst.h new file mode 100644 index 000000000..f383c19e6 --- /dev/null +++ b/src/base/loader/coff_symconst.h @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2003, 2005-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +/* + * Taken from binutils-2.14.90.0.5 include/coff/symconst.h + */ + +/* Declarations of constants for internal format of MIPS ECOFF symbols. + Originally contributed by MIPS Computer Systems and Third Eye Software. + Changes contributed by Cygnus Support are in the public domain. + + This file is just aggregated with the files that make up the GNU + release; it is not considered part of GAS, GDB, or other GNU + programs. */ + +/* + * |-----------------------------------------------------------| + * | Copyright (c) 1992, 1991, 1990 MIPS Computer Systems, Inc.| + * | MIPS Computer Systems, Inc. grants reproduction and use | + * | rights to all parties, PROVIDED that this comment is | + * | maintained in the copy. | + * |-----------------------------------------------------------| + */ + +/* (C) Copyright 1984 by Third Eye Software, Inc. + * + * Third Eye Software, Inc. grants reproduction and use rights to + * all parties, PROVIDED that this comment is maintained in the copy. + * + * Third Eye makes no claims about the applicability of this + * symbol table to a particular use. + */ + +/* glevels for field in FDR */ +#define GLEVEL_0 2 +#define GLEVEL_1 1 +#define GLEVEL_2 0 /* for upward compat reasons. */ +#define GLEVEL_3 3 + +/* magic number fo symheader */ +#define magicSym 0x7009 +/* The Alpha uses this value instead, for some reason. */ +#define magicSym2 0x1992 + +/* Language codes */ +#define langC 0 +#define langPascal 1 +#define langFortran 2 +#define langAssembler 3 /* one Assembley inst might map to many mach */ +#define langMachine 4 +#define langNil 5 +#define langAda 6 +#define langPl1 7 +#define langCobol 8 +#define langStdc 9 /* FIXME: Collides with SGI langCplusplus */ +#define langCplusplus 9 /* FIXME: Collides with langStdc */ +#define langCplusplusV2 10 /* SGI addition */ +#define langMax 11 /* maximun allowed 32 -- 5 bits */ + +/* The following are value definitions for the fields in the SYMR */ + +/* + * Storage Classes + */ + +#define scNil 0 +#define scText 1 /* text symbol */ +#define scData 2 /* initialized data symbol */ +#define scBss 3 /* un-initialized data symbol */ +#define scRegister 4 /* value of symbol is register number */ +#define scAbs 5 /* value of symbol is absolute */ +#define scUndefined 6 /* who knows? */ +#define scCdbLocal 7 /* variable's value is IN se->va.?? */ +#define scBits 8 /* this is a bit field */ +#define scCdbSystem 9 /* variable's value is IN CDB's address space */ +#define scDbx 9 /* overlap dbx internal use */ +#define scRegImage 10 /* register value saved on stack */ +#define scInfo 11 /* symbol contains debugger information */ +#define scUserStruct 12 /* address in struct user for current process */ +#define scSData 13 /* load time only small data */ +#define scSBss 14 /* load time only small common */ +#define scRData 15 /* load time only read only data */ +#define scVar 16 /* Var parameter (fortran,pascal) */ +#define scCommon 17 /* common variable */ +#define scSCommon 18 /* small common */ +#define scVarRegister 19 /* Var parameter in a register */ +#define scVariant 20 /* Variant record */ +#define scSUndefined 21 /* small undefined(external) data */ +#define scInit 22 /* .init section symbol */ +#define scBasedVar 23 /* Fortran or PL/1 ptr based var */ +#define scXData 24 /* exception handling data */ +#define scPData 25 /* Procedure section */ +#define scFini 26 /* .fini section */ +#define scRConst 27 /* .rconst section */ +#define scMax 32 + + +/* + * Symbol Types + */ + +#define stNil 0 /* Nuthin' special */ +#define stGlobal 1 /* external symbol */ +#define stStatic 2 /* static */ +#define stParam 3 /* procedure argument */ +#define stLocal 4 /* local variable */ +#define stLabel 5 /* label */ +#define stProc 6 /* " " Procedure */ +#define stBlock 7 /* beginnning of block */ +#define stEnd 8 /* end (of anything) */ +#define stMember 9 /* member (of anything - struct/union/enum */ +#define stTypedef 10 /* type definition */ +#define stFile 11 /* file name */ +#define stRegReloc 12 /* register relocation */ +#define stForward 13 /* forwarding address */ +#define stStaticProc 14 /* load time only static procs */ +#define stConstant 15 /* const */ +#define stStaParam 16 /* Fortran static parameters */ + /* These new symbol types have been recently added to SGI machines. */ +#define stStruct 26 /* Beginning of block defining a struct type */ +#define stUnion 27 /* Beginning of block defining a union type */ +#define stEnum 28 /* Beginning of block defining an enum type */ +#define stIndirect 34 /* Indirect type specification */ + /* Pseudo-symbols - internal to debugger */ +#define stStr 60 /* string */ +#define stNumber 61 /* pure number (ie. 4 NOR 2+2) */ +#define stExpr 62 /* 2+2 vs. 4 */ +#define stType 63 /* post-coersion SER */ +#define stMax 64 + +/* definitions for fields in TIR */ + +/* type qualifiers for ti.tq0 -> ti.(itqMax-1) */ +#define tqNil 0 /* bt is what you see */ +#define tqPtr 1 /* pointer */ +#define tqProc 2 /* procedure */ +#define tqArray 3 /* duh */ +#define tqFar 4 /* longer addressing - 8086/8 land */ +#define tqVol 5 /* volatile */ +#define tqConst 6 /* const */ +#define tqMax 8 + +/* basic types as seen in ti.bt */ +#define btNil 0 /* undefined (also, enum members) */ +#define btAdr 1 /* address - integer same size as pointer */ +#define btChar 2 /* character */ +#define btUChar 3 /* unsigned character */ +#define btShort 4 /* short */ +#define btUShort 5 /* unsigned short */ +#define btInt 6 /* int */ +#define btUInt 7 /* unsigned int */ +#define btLong 8 /* long */ +#define btULong 9 /* unsigned long */ +#define btFloat 10 /* float (real) */ +#define btDouble 11 /* Double (real) */ +#define btStruct 12 /* Structure (Record) */ +#define btUnion 13 /* Union (variant) */ +#define btEnum 14 /* Enumerated */ +#define btTypedef 15 /* defined via a typedef, isymRef points */ +#define btRange 16 /* subrange of int */ +#define btSet 17 /* pascal sets */ +#define btComplex 18 /* fortran complex */ +#define btDComplex 19 /* fortran double complex */ +#define btIndirect 20 /* forward or unnamed typedef */ +#define btFixedDec 21 /* Fixed Decimal */ +#define btFloatDec 22 /* Float Decimal */ +#define btString 23 /* Varying Length Character String */ +#define btBit 24 /* Aligned Bit String */ +#define btPicture 25 /* Picture */ +#define btVoid 26 /* void */ +#define btLongLong 27 /* long long */ +#define btULongLong 28 /* unsigned long long */ +#define btMax 64 diff --git a/src/base/loader/ecoff_object.cc b/src/base/loader/ecoff_object.cc new file mode 100644 index 000000000..134f2d98d --- /dev/null +++ b/src/base/loader/ecoff_object.cc @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#include <string> + +#include "base/loader/ecoff_object.hh" +#include "base/misc.hh" +#include "base/loader/symtab.hh" + +#include "base/trace.hh" // for DPRINTF + +#include "base/loader/exec_ecoff.h" +#include "base/loader/coff_sym.h" +#include "base/loader/coff_symconst.h" + +using namespace std; + +ObjectFile * +EcoffObject::tryFile(const string &fname, int fd, size_t len, uint8_t *data) +{ + if (((ecoff_filehdr *)data)->f_magic == ECOFF_MAGIC_ALPHA) { + // it's Alpha ECOFF + return new EcoffObject(fname, fd, len, data, + ObjectFile::Alpha, ObjectFile::Tru64); + } + else { + return NULL; + } +} + + +EcoffObject::EcoffObject(const string &_filename, int _fd, + size_t _len, uint8_t *_data, + Arch _arch, OpSys _opSys) + : ObjectFile(_filename, _fd, _len, _data, _arch, _opSys) +{ + execHdr = (ecoff_exechdr *)fileData; + fileHdr = &(execHdr->f); + aoutHdr = &(execHdr->a); + + entry = aoutHdr->entry; + + text.baseAddr = aoutHdr->text_start; + text.size = aoutHdr->tsize; + text.fileImage = fileData + ECOFF_TXTOFF(execHdr); + + data.baseAddr = aoutHdr->data_start; + data.size = aoutHdr->dsize; + data.fileImage = fileData + ECOFF_DATOFF(execHdr); + + bss.baseAddr = aoutHdr->bss_start; + bss.size = aoutHdr->bsize; + bss.fileImage = NULL; + + DPRINTFR(Loader, "text: 0x%x %d\ndata: 0x%x %d\nbss: 0x%x %d\n", + text.baseAddr, text.size, data.baseAddr, data.size, + bss.baseAddr, bss.size); +} + + +bool +EcoffObject::loadGlobalSymbols(SymbolTable *symtab) +{ + if (!symtab) + return false; + + if (fileHdr->f_magic != ECOFF_MAGIC_ALPHA) { + warn("loadGlobalSymbols: wrong magic on %s\n", filename); + return false; + } + + ecoff_symhdr *syms = (ecoff_symhdr *)(fileData + fileHdr->f_symptr); + if (syms->magic != magicSym2) { + warn("loadGlobalSymbols: bad symbol header magic on %s\n", filename); + return false; + } + + ecoff_extsym *ext_syms = (ecoff_extsym *)(fileData + syms->cbExtOffset); + + char *ext_strings = (char *)(fileData + syms->cbSsExtOffset); + for (int i = 0; i < syms->iextMax; i++) { + ecoff_sym *entry = &(ext_syms[i].asym); + if (entry->iss != -1) + symtab->insert(entry->value, ext_strings + entry->iss); + } + + return true; +} + +bool +EcoffObject::loadLocalSymbols(SymbolTable *symtab) +{ + if (!symtab) + return false; + + if (fileHdr->f_magic != ECOFF_MAGIC_ALPHA) { + warn("loadGlobalSymbols: wrong magic on %s\n", filename); + return false; + } + + ecoff_symhdr *syms = (ecoff_symhdr *)(fileData + fileHdr->f_symptr); + if (syms->magic != magicSym2) { + warn("loadGlobalSymbols: bad symbol header magic on %s\n", filename); + return false; + } + + ecoff_sym *local_syms = (ecoff_sym *)(fileData + syms->cbSymOffset); + char *local_strings = (char *)(fileData + syms->cbSsOffset); + ecoff_fdr *fdesc = (ecoff_fdr *)(fileData + syms->cbFdOffset); + + for (int i = 0; i < syms->ifdMax; i++) { + ecoff_sym *entry = (ecoff_sym *)(local_syms + fdesc[i].isymBase); + char *strings = (char *)(local_strings + fdesc[i].issBase); + for (int j = 0; j < fdesc[i].csym; j++) { + if (entry[j].st == stGlobal || entry[j].st == stProc) + if (entry[j].iss != -1) + symtab->insert(entry[j].value, strings + entry[j].iss); + } + } + + for (int i = 0; i < syms->isymMax; i++) { + ecoff_sym *entry = &(local_syms[i]); + if (entry->st == stProc) + symtab->insert(entry->value, local_strings + entry->iss); + } + + return true; +} diff --git a/src/base/loader/ecoff_object.hh b/src/base/loader/ecoff_object.hh new file mode 100644 index 000000000..05c604b2b --- /dev/null +++ b/src/base/loader/ecoff_object.hh @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __ECOFF_OBJECT_HH__ +#define __ECOFF_OBJECT_HH__ + +#include "base/loader/object_file.hh" + +// forward decls: avoid including exec_ecoff.h here +struct ecoff_exechdr; +struct ecoff_filehdr; +struct ecoff_aouthdr; + +class EcoffObject : public ObjectFile +{ + protected: + ecoff_exechdr *execHdr; + ecoff_filehdr *fileHdr; + ecoff_aouthdr *aoutHdr; + + EcoffObject(const std::string &_filename, int _fd, + size_t _len, uint8_t *_data, + Arch _arch, OpSys _opSys); + + public: + virtual ~EcoffObject() {} + + virtual bool loadGlobalSymbols(SymbolTable *symtab); + virtual bool loadLocalSymbols(SymbolTable *symtab); + + static ObjectFile *tryFile(const std::string &fname, int fd, + size_t len, uint8_t *data); +}; + +#endif // __ECOFF_OBJECT_HH__ diff --git a/src/base/loader/elf_object.cc b/src/base/loader/elf_object.cc new file mode 100644 index 000000000..2ca0d4f4a --- /dev/null +++ b/src/base/loader/elf_object.cc @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Ali Saidi + */ + +#include <string> + +// Because of the -Wundef flag we have to do this +#define __LIBELF_INTERNAL__ 0 +#define __LIBELF_NEED_LINK_H 0 +#define __LIBELF_SYMBOL_VERSIONS 0 + +#include "gelf.h" + +#include "base/loader/elf_object.hh" +#include "base/misc.hh" + +#include "base/loader/symtab.hh" + +#include "base/trace.hh" // for DPRINTF + +#include "sim/byteswap.hh" + + +using namespace std; + +ObjectFile * +ElfObject::tryFile(const string &fname, int fd, size_t len, uint8_t *data) +{ + Elf *elf; + GElf_Ehdr ehdr; + Arch arch = UnknownArch; + OpSys opSys = UnknownOpSys; + + // check that header matches library version + if (elf_version(EV_CURRENT) == EV_NONE) + panic("wrong elf version number!"); + + // get a pointer to elf structure + elf = elf_memory((char*)data,len); + // will only fail if fd is invalid + assert(elf != NULL); + + // Check that we actually have a elf file + if (gelf_getehdr(elf, &ehdr) ==0) { + DPRINTFR(Loader, "Not ELF\n"); + elf_end(elf); + return NULL; + } else { + //Detect the architecture + //Since we don't know how to check for alpha right now, we'll + //just assume if it wasn't something else and it's 64 bit, that's + //what it must be. + if (ehdr.e_machine == EM_SPARC64 || + ehdr.e_machine == EM_SPARC || + ehdr.e_machine == EM_SPARCV9) { + arch = ObjectFile::SPARC; + } else if (ehdr.e_machine == EM_MIPS + && ehdr.e_ident[EI_CLASS] == ELFCLASS32) { + arch = ObjectFile::Mips; + } else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { + arch = ObjectFile::Alpha; + } else { + warn("Unknown architecture: %d\n", ehdr.e_machine); + arch = ObjectFile::UnknownArch; + } + + //Detect the operating system + switch (ehdr.e_ident[EI_OSABI]) + { + + case ELFOSABI_LINUX: + opSys = ObjectFile::Linux; + break; + case ELFOSABI_SOLARIS: + opSys = ObjectFile::Solaris; + break; + case ELFOSABI_TRU64: + opSys = ObjectFile::Tru64; + break; + default: + opSys = ObjectFile::UnknownOpSys; + } + + //take a look at the .note.ABI section + //It can let us know what's what. + if (opSys == ObjectFile::UnknownOpSys) { + Elf_Scn *section; + GElf_Shdr shdr; + Elf_Data *data; + uint32_t osAbi;; + int secIdx = 1; + + // Get the first section + section = elf_getscn(elf, secIdx); + + // While there are no more sections + while (section != NULL && opSys == ObjectFile::UnknownOpSys) { + gelf_getshdr(section, &shdr); + if (shdr.sh_type == SHT_NOTE && !strcmp(".note.ABI-tag", + elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name))) { + // we have found a ABI note section + // Check the 5th 32bit word for OS 0 == linux, 1 == hurd, + // 2 == solaris, 3 == freebsd + data = elf_rawdata(section, NULL); + assert(data->d_buf); + if(ehdr.e_ident[EI_DATA] == ELFDATA2LSB) + osAbi = htole(((uint32_t*)data->d_buf)[4]); + else + osAbi = htobe(((uint32_t*)data->d_buf)[4]); + + switch(osAbi) { + case 0: + opSys = ObjectFile::Linux; + break; + case 2: + opSys = ObjectFile::Solaris; + break; + } + } // if section found + if (!strcmp(".SUNW_version", elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name))) + opSys = ObjectFile::Solaris; + if (!strcmp(".stab.index", elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name))) + opSys = ObjectFile::Solaris; + + section = elf_getscn(elf, ++secIdx); + } // while sections + } + + ElfObject * result = new ElfObject(fname, fd, len, data, arch, opSys); + + //The number of headers in the file + result->_programHeaderCount = ehdr.e_phnum; + //Record the size of each entry + result->_programHeaderSize = ehdr.e_phentsize; + if(result->_programHeaderCount) //If there is a program header table + { + //Figure out the virtual address of the header table in the + //final memory image. We use the program headers themselves + //to translate from a file offset to the address in the image. + GElf_Phdr phdr; + uint64_t e_phoff = ehdr.e_phoff; + result->_programHeaderTable = 0; + for(int hdrnum = 0; hdrnum < result->_programHeaderCount; hdrnum++) + { + gelf_getphdr(elf, hdrnum, &phdr); + //Check if we've found the segment with the headers in it + if(phdr.p_offset <= e_phoff && + phdr.p_offset + phdr.p_filesz > e_phoff) + { + result->_programHeaderTable = phdr.p_vaddr + e_phoff; + break; + } + } + } + else + result->_programHeaderTable = 0; + + + elf_end(elf); + return result; + } +} + + +ElfObject::ElfObject(const string &_filename, int _fd, + size_t _len, uint8_t *_data, + Arch _arch, OpSys _opSys) + : ObjectFile(_filename, _fd, _len, _data, _arch, _opSys) + +{ + Elf *elf; + GElf_Ehdr ehdr; + + // check that header matches library version + if (elf_version(EV_CURRENT) == EV_NONE) + panic("wrong elf version number!"); + + // get a pointer to elf structure + elf = elf_memory((char*)fileData,len); + // will only fail if fd is invalid + assert(elf != NULL); + + // Check that we actually have a elf file + if (gelf_getehdr(elf, &ehdr) ==0) { + panic("Not ELF, shouldn't be here"); + } + + entry = ehdr.e_entry; + + + // initialize segment sizes to 0 in case they're not present + text.size = data.size = bss.size = 0; + + for (int i = 0; i < ehdr.e_phnum; ++i) { + GElf_Phdr phdr; + if (gelf_getphdr(elf, i, &phdr) == 0) { + panic("gelf_getphdr failed for section %d", i); + } + + // for now we don't care about non-loadable segments + if (!(phdr.p_type & PT_LOAD)) + continue; + + // the headers don't explicitly distinguish text from data, + // but empirically the text segment comes first. + if (text.size == 0) { // haven't seen text segment yet + text.baseAddr = phdr.p_vaddr; + text.size = phdr.p_filesz; + text.fileImage = fileData + phdr.p_offset; + // if there's any padding at the end that's not in the + // file, call it the bss. This happens in the "text" + // segment if there's only one loadable segment (as for + // kernel images). + bss.size = phdr.p_memsz - phdr.p_filesz; + bss.baseAddr = phdr.p_vaddr + phdr.p_filesz; + bss.fileImage = NULL; + } else if (data.size == 0) { // have text, this must be data + data.baseAddr = phdr.p_vaddr; + data.size = phdr.p_filesz; + data.fileImage = fileData + phdr.p_offset; + // if there's any padding at the end that's not in the + // file, call it the bss. Warn if this happens for both + // the text & data segments (should only have one bss). + if (phdr.p_memsz - phdr.p_filesz > 0 && bss.size != 0) { + warn("Two implied bss segments in file!\n"); + } + bss.size = phdr.p_memsz - phdr.p_filesz; + bss.baseAddr = phdr.p_vaddr + phdr.p_filesz; + bss.fileImage = NULL; + } else { + warn("More than two loadable segments in ELF object."); + warn("Ignoring segment @ 0x%x length 0x%x.", + phdr.p_vaddr, phdr.p_filesz); + } + } + + // should have found at least one loadable segment + assert(text.size != 0); + + DPRINTFR(Loader, "text: 0x%x %d\ndata: 0x%x %d\nbss: 0x%x %d\n", + text.baseAddr, text.size, data.baseAddr, data.size, + bss.baseAddr, bss.size); + + elf_end(elf); + + // We will actually read the sections when we need to load them +} + + +bool +ElfObject::loadSomeSymbols(SymbolTable *symtab, int binding) +{ + Elf *elf; + int sec_idx = 1; // there is a 0 but it is nothing, go figure + Elf_Scn *section; + GElf_Shdr shdr; + Elf_Data *data; + int count, ii; + bool found = false; + GElf_Sym sym; + + if (!symtab) + return false; + + // check that header matches library version + if (elf_version(EV_CURRENT) == EV_NONE) + panic("wrong elf version number!"); + + // get a pointer to elf structure + elf = elf_memory((char*)fileData,len); + + assert(elf != NULL); + + // Get the first section + section = elf_getscn(elf, sec_idx); + + // While there are no more sections + while (section != NULL) { + gelf_getshdr(section, &shdr); + + if (shdr.sh_type == SHT_SYMTAB) { + found = true; + data = elf_getdata(section, NULL); + count = shdr.sh_size / shdr.sh_entsize; + DPRINTF(Loader, "Found Symbol Table, %d symbols present\n", count); + + // loop through all the symbols, only loading global ones + for (ii = 0; ii < count; ++ii) { + gelf_getsym(data, ii, &sym); + if (GELF_ST_BIND(sym.st_info) == binding) { + symtab->insert(sym.st_value, + elf_strptr(elf, shdr.sh_link, sym.st_name)); + } + } + } + ++sec_idx; + section = elf_getscn(elf, sec_idx); + } + + elf_end(elf); + + return found; +} + +bool +ElfObject::loadGlobalSymbols(SymbolTable *symtab) +{ + return loadSomeSymbols(symtab, STB_GLOBAL); +} + +bool +ElfObject::loadLocalSymbols(SymbolTable *symtab) +{ + return loadSomeSymbols(symtab, STB_LOCAL); +} diff --git a/src/base/loader/elf_object.hh b/src/base/loader/elf_object.hh new file mode 100644 index 000000000..9755426b4 --- /dev/null +++ b/src/base/loader/elf_object.hh @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __ELF_OBJECT_HH__ +#define __ELF_OBJECT_HH__ + +#include "base/loader/object_file.hh" + +class ElfObject : public ObjectFile +{ + protected: + + //These values are provided to a linux process by the kernel, so we + //need to keep them around. + Addr _programHeaderTable; + uint16_t _programHeaderSize; + uint16_t _programHeaderCount; + + /// Helper functions for loadGlobalSymbols() and loadLocalSymbols(). + bool loadSomeSymbols(SymbolTable *symtab, int binding); + + ElfObject(const std::string &_filename, int _fd, + size_t _len, uint8_t *_data, + Arch _arch, OpSys _opSys); + + public: + virtual ~ElfObject() {} + + virtual bool loadGlobalSymbols(SymbolTable *symtab); + virtual bool loadLocalSymbols(SymbolTable *symtab); + + static ObjectFile *tryFile(const std::string &fname, int fd, + size_t len, uint8_t *data); + Addr programHeaderTable() {return _programHeaderTable;} + uint16_t programHeaderSize() {return _programHeaderSize;} + uint16_t programHeaderCount() {return _programHeaderCount;} +}; + +#endif // __ELF_OBJECT_HH__ diff --git a/src/base/loader/exec_aout.h b/src/base/loader/exec_aout.h new file mode 100644 index 000000000..eed44baee --- /dev/null +++ b/src/base/loader/exec_aout.h @@ -0,0 +1,61 @@ +/* + * Taken from NetBSD sys/exec_aout.h + */ + +/* $NetBSD: exec_aout.h,v 1.29 2002/12/10 17:14:31 thorpej Exp $ */ + +/* + * Copyright (c) 1993, 1994 Christopher G. Demetriou + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christopher G. Demetriou. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _SYS_EXEC_AOUT_H_ +#define _SYS_EXEC_AOUT_H_ + +#ifndef N_PAGSIZ +#define N_PAGSIZ(ex) (AOUT_LDPGSZ) +#endif + +/* a_magic */ +#define OMAGIC 0407 /* old impure format */ +#define NMAGIC 0410 /* read-only text */ +#define ZMAGIC 0413 /* demand load format */ + +#define N_ALIGN(ex,x) \ + (N_GETMAGIC(ex) == ZMAGIC ? \ + ((x) + AOUT_LDPGSZ - 1) & ~(AOUT_LDPGSZ - 1) : (x)) + +/* Valid magic number check. */ +#define N_BADMAG(ex) \ + (N_GETMAGIC(ex) != NMAGIC && N_GETMAGIC(ex) != OMAGIC && \ + N_GETMAGIC(ex) != ZMAGIC) + +//Only alpha will be able to load aout for now +#include "arch/alpha/aout_machdep.h" + +#endif /* !_SYS_EXEC_AOUT_H_ */ diff --git a/src/base/loader/exec_ecoff.h b/src/base/loader/exec_ecoff.h new file mode 100644 index 000000000..555589806 --- /dev/null +++ b/src/base/loader/exec_ecoff.h @@ -0,0 +1,110 @@ +/* + * Taken from NetBSD sys/exec_ecoff.h + */ + +/* $NetBSD: exec_ecoff.h,v 1.13 2003/01/18 09:53:18 thorpej Exp $ */ + +/* + * Copyright (c) 1994 Adam Glass + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Glass. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _SYS_EXEC_ECOFF_H_ +#define _SYS_EXEC_ECOFF_H_ + +//Only alpha will be able to load ecoff files for now +#include "arch/alpha/ecoff_machdep.h" + +struct ecoff_filehdr { + coff_ushort f_magic; /* magic number */ + coff_ushort f_nscns; /* # of sections */ + coff_uint f_timdat; /* time and date stamp */ + coff_ulong f_symptr; /* file offset of symbol table */ + coff_uint f_nsyms; /* # of symbol table entries */ + coff_ushort f_opthdr; /* sizeof the optional header */ + coff_ushort f_flags; /* flags??? */ +}; + +struct ecoff_aouthdr { + coff_ushort magic; + coff_ushort vstamp; + ECOFF_PAD + coff_ulong tsize; + coff_ulong dsize; + coff_ulong bsize; + coff_ulong entry; + coff_ulong text_start; + coff_ulong data_start; + coff_ulong bss_start; + ECOFF_MACHDEP; +}; + +struct ecoff_scnhdr { /* needed for size info */ + char s_name[8]; /* name */ + coff_ulong s_paddr; /* physical addr? for ROMing?*/ + coff_ulong s_vaddr; /* virtual addr? */ + coff_ulong s_size; /* size */ + coff_ulong s_scnptr; /* file offset of raw data */ + coff_ulong s_relptr; /* file offset of reloc data */ + coff_ulong s_lnnoptr; /* file offset of line data */ + coff_ushort s_nreloc; /* # of relocation entries */ + coff_ushort s_nlnno; /* # of line entries */ + coff_uint s_flags; /* flags */ +}; + +struct ecoff_exechdr { + struct ecoff_filehdr f; + struct ecoff_aouthdr a; +}; + +#define ECOFF_HDR_SIZE (sizeof(struct ecoff_exechdr)) + +#define ECOFF_OMAGIC 0407 +#define ECOFF_NMAGIC 0410 +#define ECOFF_ZMAGIC 0413 + +#define ECOFF_ROUND(value, by) \ + (((value) + (by) - 1) & ~((by) - 1)) + +#define ECOFF_BLOCK_ALIGN(ep, value) \ + ((ep)->a.magic == ECOFF_ZMAGIC ? ECOFF_ROUND((value), ECOFF_LDPGSZ) : \ + (value)) + +#define ECOFF_TXTOFF(ep) \ + ((ep)->a.magic == ECOFF_ZMAGIC ? 0 : \ + ECOFF_ROUND(ECOFF_HDR_SIZE + (ep)->f.f_nscns * \ + sizeof(struct ecoff_scnhdr), ECOFF_SEGMENT_ALIGNMENT(ep))) + +#define ECOFF_DATOFF(ep) \ + (ECOFF_BLOCK_ALIGN((ep), ECOFF_TXTOFF(ep) + (ep)->a.tsize)) + +#define ECOFF_SEGMENT_ALIGN(ep, value) \ + (ECOFF_ROUND((value), ((ep)->a.magic == ECOFF_ZMAGIC ? ECOFF_LDPGSZ : \ + ECOFF_SEGMENT_ALIGNMENT(ep)))) + +#endif /* !_SYS_EXEC_ECOFF_H_ */ diff --git a/src/base/loader/object_file.cc b/src/base/loader/object_file.cc new file mode 100644 index 000000000..42c74d418 --- /dev/null +++ b/src/base/loader/object_file.cc @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2002-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#include <list> +#include <string> + +#include <sys/types.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> + +#include "base/cprintf.hh" +#include "base/loader/object_file.hh" +#include "base/loader/symtab.hh" + +#include "base/loader/ecoff_object.hh" +#include "base/loader/aout_object.hh" +#include "base/loader/elf_object.hh" + +#include "mem/translating_port.hh" + +using namespace std; + +ObjectFile::ObjectFile(const string &_filename, int _fd, + size_t _len, uint8_t *_data, + Arch _arch, OpSys _opSys) + : filename(_filename), descriptor(_fd), fileData(_data), len(_len), + arch(_arch), opSys(_opSys) +{ +} + + +ObjectFile::~ObjectFile() +{ + close(); +} + + +bool +ObjectFile::loadSection(Section *sec, Port *memPort, Addr addrMask) +{ + if (sec->size != 0) { + Addr addr = sec->baseAddr & addrMask; + if (sec->fileImage) { + memPort->writeBlob(addr, sec->fileImage, sec->size); + } + else { + // no image: must be bss + memPort->memsetBlob(addr, 0, sec->size); + } + } + return true; +} + + +bool +ObjectFile::loadSections(Port *memPort, Addr addrMask) +{ + return (loadSection(&text, memPort, addrMask) + && loadSection(&data, memPort, addrMask) + && loadSection(&bss, memPort, addrMask)); +} + + +void +ObjectFile::close() +{ + if (descriptor >= 0) { + ::close(descriptor); + descriptor = -1; + } + + if (fileData) { + ::munmap(fileData, len); + fileData = NULL; + } +} + + +ObjectFile * +createObjectFile(const string &fname) +{ + // open the file + int fd = open(fname.c_str(), O_RDONLY); + if (fd < 0) { + return NULL; + } + + // find the length of the file by seeking to the end + size_t len = (size_t)lseek(fd, 0, SEEK_END); + + // mmap the whole shebang + uint8_t *fileData = + (uint8_t *)mmap(NULL, len, PROT_READ, MAP_SHARED, fd, 0); + if (fileData == MAP_FAILED) { + close(fd); + return NULL; + } + + ObjectFile *fileObj = NULL; + + // figure out what we have here + if ((fileObj = EcoffObject::tryFile(fname, fd, len, fileData)) != NULL) { + return fileObj; + } + + if ((fileObj = AoutObject::tryFile(fname, fd, len, fileData)) != NULL) { + return fileObj; + } + + if ((fileObj = ElfObject::tryFile(fname, fd, len, fileData)) != NULL) { + return fileObj; + } + + // don't know what it is + close(fd); + munmap(fileData, len); + return NULL; +} diff --git a/src/base/loader/object_file.hh b/src/base/loader/object_file.hh new file mode 100644 index 000000000..79fa394c6 --- /dev/null +++ b/src/base/loader/object_file.hh @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2002-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __OBJECT_FILE_HH__ +#define __OBJECT_FILE_HH__ + +#include <limits> +#include <string> + +#include "sim/host.hh" // for Addr + +class Port; +class SymbolTable; + +class ObjectFile +{ + public: + + enum Arch { + UnknownArch, + Alpha, + SPARC, + Mips + }; + + enum OpSys { + UnknownOpSys, + Tru64, + Linux, + Solaris + }; + + protected: + const std::string filename; + int descriptor; + uint8_t *fileData; + size_t len; + + Arch arch; + OpSys opSys; + + ObjectFile(const std::string &_filename, int _fd, + size_t _len, uint8_t *_data, + Arch _arch, OpSys _opSys); + + public: + virtual ~ObjectFile(); + + void close(); + + virtual bool loadSections(Port *memPort, Addr addrMask = + std::numeric_limits<Addr>::max()); + virtual bool loadGlobalSymbols(SymbolTable *symtab) = 0; + virtual bool loadLocalSymbols(SymbolTable *symtab) = 0; + + Arch getArch() const { return arch; } + OpSys getOpSys() const { return opSys; } + + protected: + + struct Section { + Addr baseAddr; + uint8_t *fileImage; + size_t size; + }; + + Addr entry; + Addr globalPtr; + + Section text; + Section data; + Section bss; + + bool loadSection(Section *sec, Port *memPort, Addr addrMask); + void setGlobalPointer(Addr global_ptr) { globalPtr = global_ptr; } + + public: + Addr entryPoint() const { return entry; } + + Addr globalPointer() const { return globalPtr; } + + Addr textBase() const { return text.baseAddr; } + Addr dataBase() const { return data.baseAddr; } + Addr bssBase() const { return bss.baseAddr; } + + size_t textSize() const { return text.size; } + size_t dataSize() const { return data.size; } + size_t bssSize() const { return bss.size; } +}; + +ObjectFile *createObjectFile(const std::string &fname); + + +#endif // __OBJECT_FILE_HH__ diff --git a/src/base/loader/symtab.cc b/src/base/loader/symtab.cc new file mode 100644 index 000000000..3e73eb7a3 --- /dev/null +++ b/src/base/loader/symtab.cc @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iostream> +#include <fstream> +#include <string> +#include <vector> + +#include "base/loader/symtab.hh" +#include "base/misc.hh" +#include "base/str.hh" +#include "sim/host.hh" +#include "sim/serialize.hh" + +using namespace std; + +SymbolTable *debugSymbolTable = NULL; + +void +SymbolTable::clear() +{ + addrTable.clear(); + symbolTable.clear(); +} + +bool +SymbolTable::insert(Addr address, string symbol) +{ + if (symbol.empty()) + return false; + + if (!addrTable.insert(make_pair(address, symbol)).second) + return false; + + if (!symbolTable.insert(make_pair(symbol, address)).second) + return false; + + return true; +} + + +bool +SymbolTable::load(const string &filename) +{ + string buffer; + ifstream file(filename.c_str()); + + if (!file) + fatal("file error: Can't open symbol table file %s\n", filename); + + while (!file.eof()) { + getline(file, buffer); + if (buffer.empty()) + continue; + + int idx = buffer.find(','); + if (idx == string::npos) + return false; + + string address = buffer.substr(0, idx); + eat_white(address); + if (address.empty()) + return false; + + string symbol = buffer.substr(idx + 1); + eat_white(symbol); + if (symbol.empty()) + return false; + + Addr addr; + if (!to_number(address, addr)) + return false; + + if (!insert(addr, symbol)) + return false; + } + + file.close(); + + return true; +} + +void +SymbolTable::serialize(const string &base, ostream &os) +{ + paramOut(os, base + ".size", addrTable.size()); + + int i = 0; + ATable::const_iterator p, end = addrTable.end(); + for (p = addrTable.begin(); p != end; ++p) { + paramOut(os, csprintf("%s.addr_%d", base, i), p->first); + paramOut(os, csprintf("%s.symbol_%d", base, i), p->second); + ++i; + } +} + +void +SymbolTable::unserialize(const string &base, Checkpoint *cp, + const string §ion) +{ + clear(); + int size; + paramIn(cp, section, base + ".size", size); + for (int i = 0; i < size; ++i) { + Addr addr; + std::string symbol; + + paramIn(cp, section, csprintf("%s.addr_%d", base, i), addr); + paramIn(cp, section, csprintf("%s.symbol_%d", base, i), symbol); + insert(addr, symbol); + } +} diff --git a/src/base/loader/symtab.hh b/src/base/loader/symtab.hh new file mode 100644 index 000000000..184c0a996 --- /dev/null +++ b/src/base/loader/symtab.hh @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __SYMTAB_HH__ +#define __SYMTAB_HH__ + +#include <iosfwd> +#include <map> + +#include "sim/host.hh" // for Addr + +class Checkpoint; +class SymbolTable +{ + public: + typedef std::map<Addr, std::string> ATable; + typedef std::map<std::string, Addr> STable; + + private: + ATable addrTable; + STable symbolTable; + + private: + bool + upperBound(Addr addr, ATable::const_iterator &iter) const + { + // find first key *larger* than desired address + iter = addrTable.upper_bound(addr); + + // if very first key is larger, we're out of luck + if (iter == addrTable.begin()) + return false; + + return true; + } + + public: + SymbolTable() {} + SymbolTable(const std::string &file) { load(file); } + ~SymbolTable() {} + + void clear(); + bool insert(Addr address, std::string symbol); + bool load(const std::string &file); + + const ATable &getAddrTable() const { return addrTable; } + const STable &getSymbolTable() const { return symbolTable; } + + public: + void serialize(const std::string &base, std::ostream &os); + void unserialize(const std::string &base, Checkpoint *cp, + const std::string §ion); + + public: + bool + findSymbol(Addr address, std::string &symbol) const + { + ATable::const_iterator i = addrTable.find(address); + if (i == addrTable.end()) + return false; + + symbol = (*i).second; + return true; + } + + bool + findAddress(const std::string &symbol, Addr &address) const + { + STable::const_iterator i = symbolTable.find(symbol); + if (i == symbolTable.end()) + return false; + + address = (*i).second; + return true; + } + + /// Find the nearest symbol equal to or less than the supplied + /// address (e.g., the label for the enclosing function). + /// @param addr The address to look up. + /// @param symbol Return reference for symbol string. + /// @param symaddr Return reference for symbol address. + /// @param nextaddr Address of following symbol (for + /// determining valid range of symbol). + /// @retval True if a symbol was found. + bool + findNearestSymbol(Addr addr, std::string &symbol, Addr &symaddr, + Addr &nextaddr) const + { + ATable::const_iterator i; + if (!upperBound(addr, i)) + return false; + + nextaddr = i->first; + --i; + symaddr = i->first; + symbol = i->second; + return true; + } + + /// Overload for findNearestSymbol() for callers who don't care + /// about nextaddr. + bool + findNearestSymbol(Addr addr, std::string &symbol, Addr &symaddr) const + { + ATable::const_iterator i; + if (!upperBound(addr, i)) + return false; + + --i; + symaddr = i->first; + symbol = i->second; + return true; + } + + + bool + findNearestAddr(Addr addr, Addr &symaddr, Addr &nextaddr) const + { + ATable::const_iterator i; + if (!upperBound(addr, i)) + return false; + + nextaddr = i->first; + --i; + symaddr = i->first; + return true; + } + + bool + findNearestAddr(Addr addr, Addr &symaddr) const + { + ATable::const_iterator i; + if (!upperBound(addr, i)) + return false; + + --i; + symaddr = i->first; + return true; + } +}; + +/// Global unified debugging symbol table (for target). Conceptually +/// there should be one of these per System object for full system, +/// and per Process object for non-full-system, but so far one big +/// global one has worked well enough. +extern SymbolTable *debugSymbolTable; + +#endif // __SYMTAB_HH__ diff --git a/src/base/match.cc b/src/base/match.cc new file mode 100644 index 000000000..994209864 --- /dev/null +++ b/src/base/match.cc @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include "base/match.hh" +#include "base/str.hh" + +using namespace std; + +ObjectMatch::ObjectMatch() +{ +} + +ObjectMatch::ObjectMatch(const string &expr) +{ + setExpression(expr); +} + +void +ObjectMatch::setExpression(const string &expr) +{ + tokens.resize(1); + tokenize(tokens[0], expr, '.'); +} + +void +ObjectMatch::setExpression(const vector<string> &expr) +{ + if (expr.empty()) { + tokens.resize(0); + } else { + tokens.resize(expr.size()); + for (int i = 0; i < expr.size(); ++i) + tokenize(tokens[i], expr[i], '.'); + } +} + +/** + * @todo this should probably be changed to just use regular + * expression code + */ +bool +ObjectMatch::domatch(const string &name) const +{ + vector<string> name_tokens; + tokenize(name_tokens, name, '.'); + int ntsize = name_tokens.size(); + + int num_expr = tokens.size(); + for (int i = 0; i < num_expr; ++i) { + const vector<string> &token = tokens[i]; + int jstop = token.size(); + + bool match = true; + for (int j = 0; j < jstop; ++j) { + if (j >= ntsize) + break; + + const string &var = token[j]; + if (var != "*" && var != name_tokens[j]) { + match = false; + break; + } + } + + if (match == true) + return true; + } + + return false; +} + diff --git a/src/base/match.hh b/src/base/match.hh new file mode 100644 index 000000000..6e1f03b80 --- /dev/null +++ b/src/base/match.hh @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/* @file + * User Console Definitions + */ + +#ifndef __BASE_MATCH_HH__ +#define __BASE_MATCH_HH__ + +#include <string> +#include <vector> + +class ObjectMatch +{ + protected: + std::vector<std::vector<std::string> > tokens; + bool domatch(const std::string &name) const; + + public: + ObjectMatch(); + ObjectMatch(const std::string &expression); + void setExpression(const std::string &expression); + void setExpression(const std::vector<std::string> &expression); + bool match(const std::string &name) const + { + return tokens.empty() ? false : domatch(name); + } +}; + +#endif // __BASE_MATCH_HH__ + diff --git a/src/base/misc.cc b/src/base/misc.cc new file mode 100644 index 000000000..991a33736 --- /dev/null +++ b/src/base/misc.cc @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iostream> +#include <string> + +#include "base/cprintf.hh" +#include "base/hostinfo.hh" +#include "base/misc.hh" +#include "base/output.hh" +#include "base/trace.hh" +#include "sim/host.hh" +#include "sim/root.hh" + +using namespace std; + +void +__panic(const string &format, cp::ArgList &args, const char *func, + const char *file, int line) +{ + string fmt = "panic: " + format; + switch (fmt[fmt.size() - 1]) { + case '\n': + case '\r': + break; + default: + fmt += "\n"; + } + + fmt += " @ cycle %d\n[%s:%s, line %d]\n"; + + args.append(curTick); + args.append(func); + args.append(file); + args.append(line); + args.dump(cerr, fmt); + + delete &args; + + abort(); +} + +void +__fatal(const string &format, cp::ArgList &args, const char *func, + const char *file, int line) +{ + string fmt = "fatal: " + format; + + switch (fmt[fmt.size() - 1]) { + case '\n': + case '\r': + break; + default: + fmt += "\n"; + } + + fmt += " @ cycle %d\n[%s:%s, line %d]\n"; + fmt += "Memory Usage: %ld KBytes\n"; + + args.append(curTick); + args.append(func); + args.append(file); + args.append(line); + args.append(memUsage()); + args.dump(cerr, fmt); + + delete &args; + + exit(1); +} + +void +__warn(const string &format, cp::ArgList &args, const char *func, + const char *file, int line) +{ + string fmt = "warn: " + format; + + switch (fmt[fmt.size() - 1]) { + case '\n': + case '\r': + break; + default: + fmt += "\n"; + } + +#ifdef VERBOSE_WARN + fmt += " @ cycle %d\n[%s:%s, line %d]\n"; + args.append(curTick); + args.append(func); + args.append(file); + args.append(line); +#endif + + args.dump(cerr, fmt); + if (simout.isFile(*outputStream)) + args.dump(*outputStream, fmt); + + delete &args; +} diff --git a/src/base/misc.hh b/src/base/misc.hh new file mode 100644 index 000000000..87faf20e6 --- /dev/null +++ b/src/base/misc.hh @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Dave Greene + */ + +#ifndef __MISC_HH__ +#define __MISC_HH__ + +#include <assert.h> +#include "base/cprintf.hh" + +// +// This implements a cprintf based panic() function. panic() should +// be called when something happens that should never ever happen +// regardless of what the user does (i.e., an acutal m5 bug). panic() +// calls abort which can dump core or enter the debugger. +// +// +void __panic(const std::string&, cp::ArgList &, const char*, const char*, int) + __attribute__((noreturn)); +#define __panic__(format, args...) \ + __panic(format, (*(new cp::ArgList), args), \ + __FUNCTION__, __FILE__, __LINE__) +#define panic(args...) \ + __panic__(args, cp::ArgListNull()) + +// +// This implements a cprintf based fatal() function. fatal() should +// be called when the simulation cannot continue due to some condition +// that is the user's fault (bad configuration, invalid arguments, +// etc.) and not a simulator bug. fatal() calls exit(1), i.e., a +// "normal" exit with an error code, as opposed to abort() like +// panic() does. +// +void __fatal(const std::string&, cp::ArgList &, const char*, const char*, int) + __attribute__((noreturn)); +#define __fatal__(format, args...) \ + __fatal(format, (*(new cp::ArgList), args), \ + __FUNCTION__, __FILE__, __LINE__) +#define fatal(args...) \ + __fatal__(args, cp::ArgListNull()) + +// +// This implements a cprintf based warn +// +void __warn(const std::string&, cp::ArgList &, const char*, const char*, int); +#define __warn__(format, args...) \ + __warn(format, (*(new cp::ArgList), args), \ + __FUNCTION__, __FILE__, __LINE__) +#define warn(args...) \ + __warn__(args, cp::ArgListNull()) + +// +// assert() that prints out the current cycle +// +#define m5_assert(TEST) \ + if (!(TEST)) { \ + std::cerr << "Assertion failure, curTick = " << curTick << std::endl; \ + } \ + assert(TEST); + +#endif // __MISC_HH__ diff --git a/src/base/mod_num.hh b/src/base/mod_num.hh new file mode 100644 index 000000000..6962dc0fd --- /dev/null +++ b/src/base/mod_num.hh @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Raasch + */ + +template<class T, T MV> +class ModNum { + private: + T value; + + // Compiler should optimize this + void setValue(T n) { value = n % MV; } + + public: + ModNum() {} + ModNum(T n) { setValue(n); } + ModNum(const ModNum<T, MV> &n) : value(n.value) {} + + ModNum operator=(T n) { + setValue(n); + return *this; + } + + const ModNum operator=(ModNum n) { + value = n.value; + return *this; + } + + // Return the value if object used as RHS + operator T() const { return value; } + + // + // Operator "+=" + // + const ModNum<T, MV> operator+=(ModNum<T, MV> r) { + setValue(value + r.value); + return *this; + } + + const ModNum<T, MV> operator+=(T r) { + setValue(value + r); + return *this; + } + + // + // Operator "-=" + // + const ModNum<T, MV> operator-=(ModNum<T, MV> r) { + setValue(value - r.value); + return *this; + } + + const ModNum<T, MV> operator-=(T r) { + setValue(value - r); + return *this; + } + + // + // Operator "++" + // + // PREFIX (like ++a) + const ModNum<T, MV> operator++() { + *this += 1; + return *this; + } + + // POSTFIX (like a++) + const ModNum<T, MV> operator++(int) { + ModNum<T, MV> rv = *this; + + *this += 1; + + return rv; + } + + // + // Operator "--" + // + // PREFIX (like --a) + const ModNum<T, MV> operator--() { + *this -= 1; + return *this; + } + + // POSTFIX (like a--) + const ModNum<T, MV> operator--(int) { + ModNum<T, MV> rv = *this; + *this -= 1; + return rv; + } +}; + + +// +// Define operator "+" like this to avoid creating a temporary +// +template<class T, T MV> +inline ModNum<T, MV> +operator+(ModNum<T, MV> l, ModNum<T, MV> r) { + l += r; + return l; +} + +template<class T, T MV> +inline ModNum<T, MV> +operator+(ModNum<T, MV> l, T r) { + l += r; + return l; +} + +template<class T, T MV> +inline ModNum<T, MV> +operator+(T l, ModNum<T, MV> r) { + r += l; + return r; +} + + +// +// Define operator "-" like this to avoid creating a temporary +// +template<class T, T MV> +inline ModNum<T, MV> +operator-(ModNum<T, MV> l, ModNum<T, MV> r) { + l -= r; + return l; +} + +template<class T, T MV> +inline ModNum<T, MV> +operator-(ModNum<T, MV> l, T r) { + l -= r; + return l; +} + +template<class T, T MV> +inline ModNum<T, MV> +operator-(T l, ModNum<T, MV> r) { + r -= l; + return r; +} + + +// +// Comparison operators +// (all other cases are handled with conversons) +// +template<class T, T MV> +inline bool +operator<(ModNum<T, MV> l, ModNum<T, MV> r) { + return l.value < r.value; +} + +template<class T, T MV> +inline bool +operator>(ModNum<T, MV> l, ModNum<T, MV> r) { + return l.value > r.value; +} + +template<class T, T MV> +inline bool +operator==(ModNum<T, MV> l, ModNum<T, MV> r) { + return l.value == r.value; +} + +template<class T, T MV> +inline bool +operator<=(ModNum<T, MV> l, ModNum<T, MV> r) { + return l.value <= r.value; +} + +template<class T, T MV> +inline bool +operator>=(ModNum<T, MV> l, ModNum<T, MV> r) { + return l.value >= r.value; +} + + diff --git a/src/base/mysql.cc b/src/base/mysql.cc new file mode 100644 index 000000000..2416c766a --- /dev/null +++ b/src/base/mysql.cc @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iostream> + +#include "base/mysql.hh" +#include "base/trace.hh" + +using namespace std; + +namespace MySQL { + +inline const char * +charstar(const string &string) +{ + return string.empty() ? NULL : string.c_str(); +} + +ostream & +operator<<(ostream &stream, const Error &error) +{ + stream << error.string(); + return stream; +} + +/* + * The connection class + */ +Connection::Connection() + : valid(false) +{ +} + +Connection::~Connection() +{ + if (valid) + close(); +} + + +bool +Connection::connect(const string &xhost, const string &xuser, + const string &xpasswd, const string &xdatabase) +{ + if (connected()) + return error.set("Already Connected"); + + _host = xhost; + _user = xuser; + _passwd = xpasswd; + _database = xdatabase; + + error.clear(); + + mysql_init(&mysql); + mysql_options(&mysql, MYSQL_OPT_COMPRESS, 0); // might want to be 1 + mysql_options(&mysql, MYSQL_READ_DEFAULT_GROUP, "odbc"); + if (!mysql_real_connect(&mysql, charstar(_host), charstar(_user), + charstar(_passwd), charstar(_database), + 0, NULL, 0)) + return error.set(mysql_error(&mysql)); + + valid = true; + return false; +} + +void +Connection::close() +{ + mysql_close(&mysql); +} + +bool +Connection::query(const string &sql) +{ + DPRINTF(SQL, "Sending SQL query to server:\n%s", sql); + error.clear(); + if (mysql_real_query(&mysql, sql.c_str(), sql.size())) + error.set(mysql_error(&mysql)); + + return error; +} + + +/* namespace MySQL */ } diff --git a/src/base/mysql.hh b/src/base/mysql.hh new file mode 100644 index 000000000..272a0f07c --- /dev/null +++ b/src/base/mysql.hh @@ -0,0 +1,425 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_MYSQL_HH__ +#define __BASE_MYSQL_HH__ + +#define TO_BE_INCLUDED_LATER 0 + +#include <cassert> +#include <iosfwd> +#include <mysql_version.h> +#include <mysql.h> +#include <string> +#include <sstream> + +namespace MySQL { + +class Error +{ + protected: + const char *error; + + public: + Error() : error(NULL) {} + + Error &clear() { error = NULL; return *this; } + Error &set(const char *err) { error = err; return *this; } + + const char *string() const { return error; } + + operator bool() const { return error != NULL; } + bool operator!() const { return error == NULL; } +}; + +std::ostream &operator<<(std::ostream &stream, const Error &error); + +class Result +{ + private: + MYSQL_RES *result; + int *refcount; + + void + decref() + { + if (!refcount) + return; + + *refcount -= 1; + if (*refcount == 0) { + mysql_free_result(result); + delete refcount; + } + + refcount = NULL; + } + + public: + Result() + : result(0), refcount(NULL) + { } + + Result(MYSQL_RES *res) + : result(res) + { + if (result) + refcount = new int(1); + else + refcount = NULL; + } + + Result(const Result &result) + : result(result.result), refcount(result.refcount) + { + if (result) + *refcount += 1; + } + + ~Result() + { + decref(); + } + + const Result & + operator=(MYSQL_RES *res) + { + decref(); + result = res; + if (result) + refcount = new int(1); + + return *this; + } + + const Result & + operator=(const Result &res) + { + decref(); + result = res.result; + refcount = res.refcount; + if (result) + *refcount += 1; + + return *this; + } + + operator bool() const { return result != NULL; } + bool operator!() const { return result == NULL; } + + unsigned + num_fields() + { + assert(result); + return mysql_num_fields(result); + } + + MYSQL_ROW + fetch_row() + { + return mysql_fetch_row(result); + } + + unsigned long * + fetch_lengths() + { + return mysql_fetch_lengths(result); + } +}; + +typedef MYSQL_ROW Row; + +class Connection +{ + protected: + MYSQL mysql; + bool valid; + + protected: + std::string _host; + std::string _user; + std::string _passwd; + std::string _database; + + public: + Connection(); + virtual ~Connection(); + + bool connected() const { return valid; } + bool connect(const std::string &host, const std::string &user, + const std::string &passwd, const std::string &database); + void close(); + + public: + Error error; + operator MYSQL *() { return &mysql; } + + public: + bool query(const std::string &sql); + + bool + query(const std::stringstream &sql) + { + return query(sql.str()); + } + + bool + autocommit(bool mode) + { + return mysql_autocommit(&mysql, mode); + } + + bool + commit() + { + return mysql_commit(&mysql); + } + + bool + rollback() + { + return mysql_rollback(&mysql); + } + + unsigned + field_count() + { + return mysql_field_count(&mysql); + } + + unsigned + affected_rows() + { + return mysql_affected_rows(&mysql); + } + + unsigned + insert_id() + { + return mysql_insert_id(&mysql); + } + + + Result + store_result() + { + error.clear(); + Result result = mysql_store_result(&mysql); + if (!result) + error.set(mysql_error(&mysql)); + + return result; + } +}; + +#if 0 +class BindProxy +{ + MYSQL_BIND *bind; + BindProxy(MYSQL_BIND *b) : bind(b) {} + + void operator=(bool &buffer) + { + bind->buffer_type = MYSQL_TYPE_TINY; + bind->buffer = (char *)&buffer; + } + + void operator=(int8_t &buffer) + { + bind->buffer_type = MYSQL_TYPE_TINY; + bind->buffer = (char *)&buffer; + } + + void operator=(int16_t &buffer) + { + bind->buffer_type = MYSQL_TYPE_SHORT; + bind->buffer = (char *)&buffer; + } + + void operator=(int32_t &buffer) + { + bind->buffer_type = MYSQL_TYPE_LONG; + bind->buffer = (char *)&buffer; + } + + void operator=(int64_t &buffer) + { + bind->buffer_type = MYSQL_TYPE_LONGLONG; + bind->buffer = (char *)&buffer; + } + + void operator=(uint8_t &buffer) + { + bind->buffer_type = MYSQL_TYPE_TINY; + bind->buffer = (char *)&buffer; + } + + void operator=(uint16_t &buffer) + { + bind->buffer_type = MYSQL_TYPE_SHORT; + bind->buffer = (char *)&buffer; + } + + void operator=(uint32_t &buffer) + { + bind->buffer_type = MYSQL_TYPE_LONG; + bind->buffer = (char *)&buffer; + } + + void operator=(uint64_t &buffer) + { + bind->buffer_type = MYSQL_TYPE_LONGLONG; + bind->buffer = (char *)&buffer; + } + + void operator=(float &buffer) + { + bind->buffer_type = MYSQL_TYPE_FLOAT; + bind->buffer = (char *)&buffer; + } + + void operator=(double &buffer) + { + bind->buffer_type = MYSQL_TYPE_DOUBLE; + bind->buffer = (char *)&buffer; + } + + void operator=(Time &buffer) + { + bind->buffer_type = MYSQL_TYPE_DATE; + bind->buffer = (char *)&buffer; + } + + void operator=(const char *buffer) + { + bind->buffer_type = MYSQL_TYPE_VAR_STRING; + bind->buffer = buffer; + } + + void operator=(const std::string &buffer) + { + bind->buffer_type = MYSQL_TYPE_VAR_STRING; + bind->buffer = (char *)&buffer; + bind->length = buffer.length; + } + + bool + set_null(bool null) + { + bind->is_null = null; + } +}; + +class Statement +{ + protected: + Error &error; + MYSQL_STMT *stmt; + MYSQL_BIND *bind; + int size; + + public: + Statement(Connection &mysql) + : error(mysql.error), bind(NULL), size(0) + { + stmt = mysql_stmt_init(mysql); + assert(valid() && "mysql_stmt_init(), out of memory\n"); + } + + ~Statement() + { + assert(valid()); + error.clear(); + if (mysql_stmt_close(stmt)) + error.set(mysql_stmt_error(stmt)); + + if (bind) + delete [] bind; + } + + bool valid() + { + return stmt != NULL; + } + + void prepare(const std::string &query) + { + assert(valid()); + mysql.error.clear(); + if (mysql_stmt_prepare(mysql, query, strlen(query))) + mysql.error.set(mysql_stmt_error(stmt)); + + int size = count(); + bind = new MYSQL_BIND[size]; + } + + unsigned count() + { + assert(valid()); + return mysql_stmt_param_count(stmt); + } + + unsigned affected() + { + assert(valid()); + return mysql_stmt_affected_rows(stmt); + } + + void bind(MYSQL_BIND *bind) + { + mysql.error.clear(); + if (mysql_stmt_bind_param(stmt, bind)) + mysql.error.set(mysql_stmt_error(stmt)); + } + + BindProxy operator[](int index) + { + assert(index > 0 && index < N); + return &bind[N]; + } + + operator MYSQL_BIND *() + { + return bind; + } + + void operator()() + { + assert(valid()); + error.clear(); + if (mysql_stmt_execute(stmt)) + error.set(mysql_stmt_error(stmt)); + } +} +#endif + +/* namespace MySQL */ } + +#endif // __BASE_MYSQL_HH__ diff --git a/src/base/output.cc b/src/base/output.cc new file mode 100644 index 000000000..afcac03a5 --- /dev/null +++ b/src/base/output.cc @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <fstream> + +#include "base/misc.hh" +#include "base/output.hh" + +using namespace std; + +OutputDirectory simout; + +/** + * + */ +OutputDirectory::OutputDirectory() +{} + +OutputDirectory::~OutputDirectory() +{} + +void +OutputDirectory::setDirectory(const string &d) +{ + if (!dir.empty()) + panic("Output directory already set!\n"); + + dir = d; + + if (dir != ".") { + if (mkdir(dir.c_str(), 0777) < 0 && errno != EEXIST) + panic("couldn't make output dir %s: %s\n", + dir, strerror(errno)); + } + + // guarantee that directory ends with a '/' + if (dir[dir.size() - 1] != '/') + dir += "/"; +} + +const string & +OutputDirectory::directory() +{ + if (dir.empty()) + panic("Output directory not set!"); + + return dir; +} + +string +OutputDirectory::resolve(const string &name) +{ + return (name[0] != '/') ? dir + name : name; +} + +ostream * +OutputDirectory::create(const string &name) +{ + if (name == "cerr" || name == "stderr") + return &cerr; + + if (name == "cout" || name == "stdout") + return &cout; + + ofstream *file = new ofstream(resolve(name).c_str(), ios::trunc); + if (!file->is_open()) + panic("Cannot open file %s", name); + + return file; +} + +ostream * +OutputDirectory::find(const string &name) +{ + if (name == "cerr" || name == "stderr") + return &cerr; + + if (name == "cout" || name == "stdout") + return &cout; + + string filename = resolve(name); + map_t::iterator i = files.find(filename); + if (i != files.end()) + return (*i).second; + + ofstream *file = new ofstream(filename.c_str(), ios::trunc); + if (!file->is_open()) + panic("Cannot open file %s", filename); + + files[filename] = file; + return file; +} + +bool +OutputDirectory::isFile(const std::ostream *os) +{ + return os && os != &cerr && os != &cout; +} diff --git a/src/base/output.hh b/src/base/output.hh new file mode 100644 index 000000000..0aae4ae81 --- /dev/null +++ b/src/base/output.hh @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_OUTPUT_HH__ +#define __BASE_OUTPUT_HH__ + +#include <iosfwd> +#include <map> +#include <string> + +class OutputDirectory +{ + private: + typedef std::map<std::string, std::ostream *> map_t; + + map_t files; + std::string dir; + + public: + OutputDirectory(); + ~OutputDirectory(); + + void setDirectory(const std::string &dir); + const std::string &directory(); + + std::string resolve(const std::string &name); + std::ostream *create(const std::string &name); + std::ostream *find(const std::string &name); + + static bool isFile(const std::ostream *os); + static inline bool isFile(const std::ostream &os) { return isFile(&os); } +}; + +extern OutputDirectory simout; + +#endif // __BASE_OUTPUT_HH__ diff --git a/src/base/pollevent.cc b/src/base/pollevent.cc new file mode 100644 index 000000000..2743cd95d --- /dev/null +++ b/src/base/pollevent.cc @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <sys/ioctl.h> +#include <sys/types.h> + +#include <fcntl.h> +#include <signal.h> +#include <unistd.h> + +#include "sim/async.hh" +#include "sim/host.hh" +#include "base/misc.hh" +#include "base/pollevent.hh" +#include "sim/root.hh" +#include "sim/serialize.hh" + +using namespace std; + +PollQueue pollQueue; + +///////////////////////////////////////////////////// +// +PollEvent::PollEvent(int _fd, int _events) + : queue(NULL), enabled(true) +{ + pfd.fd = _fd; + pfd.events = _events; +} + +PollEvent::~PollEvent() +{ + if (queue) + queue->remove(this); +} + +void +PollEvent::disable() +{ + if (!enabled) return; + enabled = false; + + if (queue) + queue->copy(); +} + +void +PollEvent::enable() +{ + if (enabled) return; + enabled = true; + + if (queue) + queue->copy(); +} + +void +PollEvent::serialize(ostream &os) +{ + SERIALIZE_SCALAR(pfd.fd); + SERIALIZE_SCALAR(pfd.events); + SERIALIZE_SCALAR(enabled); +} + +void +PollEvent::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(pfd.fd); + UNSERIALIZE_SCALAR(pfd.events); + UNSERIALIZE_SCALAR(enabled); +} + +///////////////////////////////////////////////////// +// +PollQueue::PollQueue() + : poll_fds(NULL), max_size(0), num_fds(0) +{ } + +PollQueue::~PollQueue() +{ + removeHandler(); + for (int i = 0; i < num_fds; i++) + setupAsyncIO(poll_fds[0].fd, false); + + delete [] poll_fds; +} + +void +PollQueue::copy() +{ + eventvec_t::iterator i = events.begin(); + eventvec_t::iterator end = events.end(); + + num_fds = 0; + + while (i < end) { + if ((*i)->enabled) + poll_fds[num_fds++] = (*i)->pfd; + ++i; + } +} + +void +PollQueue::remove(PollEvent *event) +{ + eventvec_t::iterator i = events.begin(); + eventvec_t::iterator end = events.end(); + + while (i < end) { + if (*i == event) { + events.erase(i); + copy(); + event->queue = NULL; + return; + } + + ++i; + } + + panic("Event does not exist. Cannot remove."); +} + +void +PollQueue::schedule(PollEvent *event) +{ + if (event->queue) + panic("Event already scheduled!"); + + event->queue = this; + events.push_back(event); + setupAsyncIO(event->pfd.fd, true); + + // if we ran out of space in the fd array, double the capacity + // if this is the first time that we've scheduled an event, create + // the array with an initial size of 16 + if (++num_fds > max_size) { + if (max_size > 0) { + delete [] poll_fds; + max_size *= 2; + } else { + max_size = 16; + setupHandler(); + } + + poll_fds = new pollfd[max_size]; + } + + copy(); +} + +void +PollQueue::service() +{ + int ret = poll(poll_fds, num_fds, 0); + + if (ret <= 0) + return; + + for (int i = 0; i < num_fds; i++) { + int revents = poll_fds[i].revents; + if (revents) { + events[i]->process(revents); + if (--ret <= 0) + break; + } + } +} + +struct sigaction PollQueue::oldio; +struct sigaction PollQueue::oldalrm; +bool PollQueue::handler = false; + +void +PollQueue::setupAsyncIO(int fd, bool set) +{ + int flags = fcntl(fd, F_GETFL); + if (flags == -1) + panic("Could not set up async IO"); + + if (set) + flags |= FASYNC; + else + flags &= ~(FASYNC); + + if (fcntl(fd, F_SETFL, flags) == -1) + panic("Could not set up async IO"); + + if (set) { + if (fcntl(fd, F_SETOWN, getpid()) == -1) + panic("Could not set up async IO"); + } +} + +void +PollQueue::setupHandler() +{ + struct sigaction act; + + act.sa_handler = handleIO; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + + if (sigaction(SIGIO, &act, &oldio) == -1) + panic("could not do sigaction"); + + act.sa_handler = handleALRM; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + + if (sigaction(SIGALRM, &act, &oldalrm) == -1) + panic("could not do sigaction"); + + alarm(1); + + handler = true; +} + +void +PollQueue::removeHandler() +{ + if (sigaction(SIGIO, &oldio, NULL) == -1) + panic("could not remove handler"); + + if (sigaction(SIGIO, &oldalrm, NULL) == -1) + panic("could not remove handler"); +} + +void +PollQueue::handleIO(int sig) +{ + if (sig != SIGIO) + panic("Wrong Handler"); + + async_event = true; + async_io = true; +} + +void +PollQueue::handleALRM(int sig) +{ + if (sig != SIGALRM) + panic("Wrong Handler"); + + async_event = true; + async_alarm = true; + alarm(1); +} + diff --git a/src/base/pollevent.hh b/src/base/pollevent.hh new file mode 100644 index 000000000..5b84650cb --- /dev/null +++ b/src/base/pollevent.hh @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __POLLEVENT_H__ +#define __POLLEVENT_H__ + +#include <vector> +#include <poll.h> +#include "sim/root.hh" + +class Checkpoint; +class PollQueue; + +class PollEvent +{ + private: + friend class PollQueue; + + protected: + pollfd pfd; + PollQueue *queue; + bool enabled; + + public: + PollEvent(int fd, int event); + virtual ~PollEvent(); + + void disable(); + void enable(); + virtual void process(int revent) = 0; + + bool queued() { return queue != 0; } + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +class PollQueue +{ + private: + typedef std::vector<PollEvent *> eventvec_t; + eventvec_t events; + + pollfd *poll_fds; + int max_size; + int num_fds; + + public: + PollQueue(); + ~PollQueue(); + + void copy(); + void remove(PollEvent *event); + void schedule(PollEvent *event); + void service(); + + protected: + static bool handler; + static struct sigaction oldio; + static struct sigaction oldalrm; + + public: + static void setupAsyncIO(int fd, bool set); + static void handleIO(int); + static void handleALRM(int); + static void removeHandler(); + static void setupHandler(); +}; + +extern PollQueue pollQueue; + +#endif // __POLLEVENT_H__ diff --git a/src/base/predictor.hh b/src/base/predictor.hh new file mode 100644 index 000000000..94f19ca28 --- /dev/null +++ b/src/base/predictor.hh @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Raasch + * Nathan Binkert + */ + +// +// Abstract base class for a generic predictor +// +// + +#ifndef __PREDICTOR_HH__ +#define __PREDICTOR_HH__ + +class GenericPredictor { + + public: + virtual void clear() = 0; + + virtual unsigned predict(unsigned long _index) = 0; + virtual unsigned predict(unsigned long _index, unsigned &pdata) = 0; + + virtual unsigned peek(unsigned long _index) = 0; + + virtual void record(unsigned long _index, unsigned _actual_value, + unsigned _pred_value) = 0; + virtual void record(unsigned long _index, unsigned _actual_value, + unsigned _pred_value, unsigned _pdata) = 0; + + virtual unsigned value(unsigned long _index) = 0; + + virtual void regStats() = 0; + virtual void regFormulas() = 0; + + virtual ~GenericPredictor() {}; +}; + +#endif // __PREDICTOR_HH__ diff --git a/src/base/random.cc b/src/base/random.cc new file mode 100644 index 000000000..e135b55f5 --- /dev/null +++ b/src/base/random.cc @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ali Saidi + */ + +#include <cstdlib> +#include <cmath> + +#include "sim/param.hh" +#include "base/random.hh" +#include "base/trace.hh" + +using namespace std; + +class RandomContext : public ParamContext +{ + public: + RandomContext(const string &_iniSection) + : ::ParamContext(_iniSection) {} + ~RandomContext() {} + + void checkParams(); +}; + +RandomContext paramContext("random"); + +Param<unsigned> +seed(¶mContext, "seed", "seed to random number generator", 1); + +void +RandomContext::checkParams() +{ + ::srand48(seed); +} + +long +getLong() +{ + return mrand48(); +} + +int64_t +getUniform(int64_t min, int64_t max) +{ + double r; + r = drand48() * (max-min) + min; + return (int64_t)round(r); +} + +uint64_t +getUniformPos(uint64_t min, uint64_t max) +{ + double r; + r = drand48() * (max-min) + min; + return (uint64_t)round(r); +} + + +// idea for generating a double from erand48 +double +getDouble() +{ + union { + uint32_t _long[2]; + uint16_t _short[4]; + }; + + _long[0] = mrand48(); + _long[1] = mrand48(); + + return ldexp((double) _short[0], -48) + + ldexp((double) _short[1], -32) + + ldexp((double) _short[2], -16); +} diff --git a/src/base/random.hh b/src/base/random.hh new file mode 100644 index 000000000..b5eb39f94 --- /dev/null +++ b/src/base/random.hh @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ali Saidi + */ + +#ifndef __BASE_RANDOM_HH__ +#define __BASE_RANDOM_HH__ + +#include "sim/host.hh" + +long getLong(); +double getDouble(); +uint64_t getUniformPos(uint64_t min, uint64_t max); +int64_t getUniform(int64_t min, int64_t max); + +template <typename T> +struct Random; + +template<> struct Random<int8_t> +{ + static int8_t get() + { return getLong() & (int8_t)-1; } + + static int8_t uniform(int8_t min, int8_t max) + { return getUniform(min, max); } +}; + +template<> struct Random<uint8_t> +{ + static uint8_t get() + { return getLong() & (uint8_t)-1; } + + static uint8_t uniform(uint8_t min, uint8_t max) + { return getUniformPos(min, max); } +}; + +template<> struct Random<int16_t> +{ + static int16_t get() + { return getLong() & (int16_t)-1; } + + static int16_t uniform(int16_t min, int16_t max) + { return getUniform(min, max); } +}; + +template<> struct Random<uint16_t> +{ + static uint16_t get() + { return getLong() & (uint16_t)-1; } + + static uint16_t uniform(uint16_t min, uint16_t max) + { return getUniformPos(min, max); } +}; + +template<> struct Random<int32_t> +{ + static int32_t get() + { return (int32_t)getLong(); } + + static int32_t uniform(int32_t min, int32_t max) + { return getUniform(min, max); } +}; + +template<> struct Random<uint32_t> +{ + static uint32_t get() + { return (uint32_t)getLong(); } + + static uint32_t uniform(uint32_t min, uint32_t max) + { return getUniformPos(min, max); } +}; + +template<> struct Random<int64_t> +{ + static int64_t get() + { return (int64_t)getLong() << 32 || (uint64_t)getLong(); } + + static int64_t uniform(int64_t min, int64_t max) + { return getUniform(min, max); } +}; + +template<> struct Random<uint64_t> +{ + static uint64_t get() + { return (uint64_t)getLong() << 32 || (uint64_t)getLong(); } + + static uint64_t uniform(uint64_t min, uint64_t max) + { return getUniformPos(min, max); } +}; + +template<> struct Random<float> +{ + static float get() + { return getDouble(); } +}; + +template<> struct Random<double> +{ + static double get() + { return getDouble(); } +}; + +#endif // __BASE_RANDOM_HH__ diff --git a/src/base/range.cc b/src/base/range.cc new file mode 100644 index 000000000..442e5fdf8 --- /dev/null +++ b/src/base/range.cc @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include "base/intmath.hh" +#include "base/range.hh" +#include "base/str.hh" + +using namespace std; + +template <class T> +bool +__x_parse_range(const std::string &str, T &first, T &last) +{ + std::vector<std::string> values; + tokenize(values, str, ':'); + + T thefirst, thelast; + + if (values.size() != 2) + return false; + + std::string s = values[0]; + std::string e = values[1]; + + if (!to_number(s, thefirst)) + return false; + + bool increment = (e[0] == '+'); + if (increment) + e = e.substr(1); + + if (!to_number(e, thelast)) + return false; + + if (increment) + thelast += thefirst - 1; + + first = thefirst; + last = thelast; + + return true; +} + +#define RANGE_PARSE(type) \ +template<> bool \ +__parse_range(const std::string &s, type &first, type &last) \ +{ return __x_parse_range(s, first, last); } + +RANGE_PARSE(unsigned long long); +RANGE_PARSE(signed long long); +RANGE_PARSE(unsigned long); +RANGE_PARSE(signed long); +RANGE_PARSE(unsigned int); +RANGE_PARSE(signed int); +RANGE_PARSE(unsigned short); +RANGE_PARSE(signed short); +RANGE_PARSE(unsigned char); +RANGE_PARSE(signed char); diff --git a/src/base/range.hh b/src/base/range.hh new file mode 100644 index 000000000..d9542c0ca --- /dev/null +++ b/src/base/range.hh @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __BASE_RANGE_HH__ +#define __BASE_RANGE_HH__ + +#include <cassert> +#include <iostream> +#include <string> + +/** + * @param s range string + * EndExclusive Ranges are in the following format: + * @verbatim + * <range> := {<start_val>}:{<end>} + * <start> := <end_val> | +<delta> + * @endverbatim + */ +template <class T> +bool __parse_range(const std::string &s, T &start, T &end); + +template <class T> +struct Range +{ + T start; + T end; + + Range() { invalidate(); } + + template <class U> + Range(const std::pair<U, U> &r) + : start(r.first), end(r.second) + {} + + template <class U> + Range(const Range<U> &r) + : start(r.start), end(r.end) + {} + + Range(const std::string &s) + { + if (!__parse_range(s, start, end)) + invalidate(); + } + + template <class U> + const Range<T> &operator=(const Range<U> &r) + { + start = r.start; + end = r.end; + return *this; + } + + template <class U> + const Range<T> &operator=(const std::pair<U, U> &r) + { + start = r.first; + end = r.second; + return *this; + } + + const Range &operator=(const std::string &s) + { + if (!__parse_range(s, start, end)) + invalidate(); + return *this; + } + + void invalidate() { start = 1; end = 0; } + T size() const { return end - start + 1; } + bool valid() const { return start < end; } +}; + +template <class T> +inline std::ostream & +operator<<(std::ostream &o, const Range<T> &r) +{ + o << '[' << r.start << "," << r.end << ']'; + return o; +} + +template <class T> +inline Range<T> +RangeEx(T start, T end) +{ return std::make_pair(start, end - 1); } + +template <class T> +inline Range<T> +RangeIn(T start, T end) +{ return std::make_pair(start, end); } + +template <class T, class U> +inline Range<T> +RangeSize(T start, U size) +{ return std::make_pair(start, start + size - 1); } + +//////////////////////////////////////////////////////////////////////// +// +// Range to Range Comparisons +// + +/** + * @param range1 is a range. + * @param range2 is a range. + * @return if range1 and range2 are identical. + */ +template <class T, class U> +inline bool +operator==(const Range<T> &range1, const Range<U> &range2) +{ + return range1.start == range2.start && range1.end == range2.end; +} + +/** + * @param range1 is a range. + * @param range2 is a range. + * @return if range1 and range2 are not identical. + */ +template <class T, class U> +inline bool +operator!=(const Range<T> &range1, const Range<U> &range2) +{ + return range1.start != range2.start || range1.end != range2.end; +} + +/** + * @param range1 is a range. + * @param range2 is a range. + * @return if range1 is less than range2 and does not overlap range1. + */ +template <class T, class U> +inline bool +operator<(const Range<T> &range1, const Range<U> &range2) +{ + return range1.start < range2.start; +} + +/** + * @param range1 is a range. + * @param range2 is a range. + * @return if range1 is less than range2. range1 may overlap range2, + * but not extend beyond the end of range2. + */ +template <class T, class U> +inline bool +operator<=(const Range<T> &range1, const Range<U> &range2) +{ + return range1.start <= range2.start; +} + +/** + * @param range1 is a range. + * @param range2 is a range. + * @return if range1 is greater than range2 and does not overlap range2. + */ +template <class T, class U> +inline bool +operator>(const Range<T> &range1, const Range<U> &range2) +{ + return range1.start > range2.start; +} + +/** + * @param range1 is a range. + * @param range2 is a range. + * @return if range1 is greater than range2. range1 may overlap range2, + * but not extend beyond the beginning of range2. + */ +template <class T, class U> +inline bool +operator>=(const Range<T> &range1, const Range<U> &range2) +{ + return range1.start >= range2.start; +} + +//////////////////////////////////////////////////////////////////////// +// +// Position to Range Comparisons +// + +/** + * @param pos position compared to the range. + * @param range range compared against. + * @return indicates that position pos is within the range. + */ +template <class T, class U> +inline bool +operator==(const T &pos, const Range<U> &range) +{ + return pos >= range.start && pos <= range.end; +} + +/** + * @param pos position compared to the range. + * @param range range compared against. + * @return indicates that position pos is not within the range. + */ +template <class T, class U> +inline bool +operator!=(const T &pos, const Range<U> &range) +{ + return pos < range.start || pos > range.end; +} + +/** + * @param pos position compared to the range. + * @param range range compared against. + * @return indicates that position pos is below the range. + */ +template <class T, class U> +inline bool +operator<(const T &pos, const Range<U> &range) +{ + return pos < range.start; +} + +/** + * @param pos position compared to the range. + * @param range range compared against. + * @return indicates that position pos is below or in the range. + */ +template <class T, class U> +inline bool +operator<=(const T &pos, const Range<U> &range) +{ + return pos <= range.end; +} + +/** + * @param pos position compared to the range. + * @param range range compared against. + * @return indicates that position pos is above the range. + */ +template <class T, class U> +inline bool +operator>(const T &pos, const Range<U> &range) +{ + return pos > range.end; +} + +/** + * @param pos position compared to the range. + * @param range range compared against. + * @return indicates that position pos is above or in the range. + */ +template <class T, class U> +inline bool +operator>=(const T &pos, const Range<U> &range) +{ + return pos >= range.start; +} + +//////////////////////////////////////////////////////////////////////// +// +// Range to Position Comparisons (for symmetry) +// + +/** + * @param range range compared against. + * @param pos position compared to the range. + * @return indicates that position pos is within the range. + */ +template <class T, class U> +inline bool +operator==(const Range<T> &range, const U &pos) +{ + return pos >= range.start && pos <= range.end; +} + +/** + * @param range range compared against. + * @param pos position compared to the range. + * @return indicates that position pos is not within the range. + */ +template <class T, class U> +inline bool +operator!=(const Range<T> &range, const U &pos) +{ + return pos < range.start || pos > range.end; +} + +/** + * @param range range compared against. + * @param pos position compared to the range. + * @return indicates that position pos is above the range. + */ +template <class T, class U> +inline bool +operator<(const Range<T> &range, const U &pos) +{ + return range.end < pos; +} + +/** + * @param range range compared against. + * @param pos position compared to the range. + * @return indicates that position pos is above or in the range. + */ +template <class T, class U> +inline bool +operator<=(const Range<T> &range, const U &pos) +{ + return range.start <= pos; +} + +/** + * @param range range compared against. + * @param pos position compared to the range. + * 'range > pos' indicates that position pos is below the range. + */ +template <class T, class U> +inline bool +operator>(const Range<T> &range, const U &pos) +{ + return range.start > pos; +} + +/** + * @param range range compared against. + * @param pos position compared to the range. + * 'range >= pos' indicates that position pos is below or in the range. + */ +template <class T, class U> +inline bool +operator>=(const Range<T> &range, const U &pos) +{ + return range.end >= pos; +} + +#endif // __BASE_RANGE_HH__ diff --git a/src/base/refcnt.hh b/src/base/refcnt.hh new file mode 100644 index 000000000..6672d4a5f --- /dev/null +++ b/src/base/refcnt.hh @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __REFCNT_HH__ +#define __REFCNT_HH__ + +#include <stddef.h> //For the NULL macro definition + +class RefCounted +{ + private: + int count; + + private: + RefCounted(const RefCounted &); + + public: + RefCounted() : count(0) {} + virtual ~RefCounted() {} + + void incref() { ++count; } + void decref() { if (--count <= 0) delete this; } +}; + +template <class T> +class RefCountingPtr +{ + protected: + T *data; + + void copy(T *d) + { + data = d; + if (data) + data->incref(); + } + void del() + { + if (data) + data->decref(); + } + void set(T *d) + { + if (data == d) + return; + + del(); + copy(d); + } + + + public: + RefCountingPtr() : data(NULL) {} + RefCountingPtr(T *data) { copy(data); } + RefCountingPtr(const RefCountingPtr &r) { copy(r.data); } + ~RefCountingPtr() { del(); } + + T *operator->() { return data; } + T &operator*() { return *data; } + T *get() { return data; } + + const T *operator->() const { return data; } + const T &operator*() const { return *data; } + const T *get() const { return data; } + + RefCountingPtr &operator=(T *p) { set(p); return *this; } + RefCountingPtr &operator=(const RefCountingPtr &r) + { return operator=(r.data); } + + bool operator!() const { return data == 0; } + operator bool() const { return data != 0; } +}; + +template<class T> +bool operator==(const RefCountingPtr<T> &l, const RefCountingPtr<T> &r) +{ return l.get() == r.get(); } + +template<class T> +bool operator==(const RefCountingPtr<T> &l, const T *r) +{ return l.get() == r; } + +template<class T> +bool operator==(const T &l, const RefCountingPtr<T> &r) +{ return l == r.get(); } + +template<class T> +bool operator!=(const RefCountingPtr<T> &l, const RefCountingPtr<T> &r) +{ return l.get() != r.get(); } + +template<class T> +bool operator!=(const RefCountingPtr<T> &l, const T *r) +{ return l.get() != r; } + +template<class T> +bool operator!=(const T &l, const RefCountingPtr<T> &r) +{ return l != r.get(); } + +#endif // __REFCNT_HH__ diff --git a/src/base/remote_gdb.cc b/src/base/remote_gdb.cc new file mode 100644 index 000000000..e4efa31e3 --- /dev/null +++ b/src/base/remote_gdb.cc @@ -0,0 +1,1183 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)kgdb_stub.c 8.4 (Berkeley) 1/12/94 + */ + +/*- + * Copyright (c) 2001 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * $NetBSD: kgdb_stub.c,v 1.8 2001/07/07 22:58:00 wdk Exp $ + * + * Taken from NetBSD + * + * "Stub" to allow remote cpu to debug over a serial line using gdb. + */ + +#include <sys/signal.h> + +#include <string> +#include <unistd.h> + +#include "arch/vtophys.hh" +#include "base/intmath.hh" +#include "base/kgdb.h" +#include "base/remote_gdb.hh" +#include "base/socket.hh" +#include "base/trace.hh" +#include "config/full_system.hh" +#include "cpu/thread_context.hh" +#include "cpu/static_inst.hh" +#include "mem/physical.hh" +#include "mem/port.hh" +#include "sim/system.hh" + +using namespace std; +using namespace TheISA; + +#ifndef NDEBUG +vector<RemoteGDB *> debuggers; +int current_debugger = -1; + +void +debugger() +{ + if (current_debugger >= 0 && current_debugger < debuggers.size()) { + RemoteGDB *gdb = debuggers[current_debugger]; + if (!gdb->isattached()) + gdb->listener->accept(); + if (gdb->isattached()) + gdb->trap(ALPHA_KENTRY_IF); + } +} +#endif + +/////////////////////////////////////////////////////////// +// +// +// + +GDBListener::Event::Event(GDBListener *l, int fd, int e) + : PollEvent(fd, e), listener(l) +{} + +void +GDBListener::Event::process(int revent) +{ + listener->accept(); +} + +GDBListener::GDBListener(RemoteGDB *g, int p) + : event(NULL), gdb(g), port(p) +{ + assert(!gdb->listener); + gdb->listener = this; +} + +GDBListener::~GDBListener() +{ + if (event) + delete event; +} + +string +GDBListener::name() +{ + return gdb->name() + ".listener"; +} + +void +GDBListener::listen() +{ + while (!listener.listen(port, true)) { + DPRINTF(GDBMisc, "Can't bind port %d\n", port); + port++; + } + + event = new Event(this, listener.getfd(), POLLIN); + pollQueue.schedule(event); + +#ifndef NDEBUG + gdb->number = debuggers.size(); + debuggers.push_back(gdb); +#endif + +#ifndef NDEBUG + ccprintf(cerr, "%d: %s: listening for remote gdb #%d on port %d\n", + curTick, name(), gdb->number, port); +#else + ccprintf(cerr, "%d: %s: listening for remote gdb on port %d\n", + curTick, name(), port); +#endif +} + +void +GDBListener::accept() +{ + if (!listener.islistening()) + panic("GDBListener::accept(): cannot accept if we're not listening!"); + + int sfd = listener.accept(true); + + if (sfd != -1) { + if (gdb->isattached()) + close(sfd); + else + gdb->attach(sfd); + } +} + +/////////////////////////////////////////////////////////// +// +// +// +int digit2i(char); +char i2digit(int); +void mem2hex(void *, const void *, int); +const char *hex2mem(void *, const char *, int); +Addr hex2i(const char **); + +RemoteGDB::Event::Event(RemoteGDB *g, int fd, int e) + : PollEvent(fd, e), gdb(g) +{} + +void +RemoteGDB::Event::process(int revent) +{ + if (revent & POLLIN) + gdb->trap(ALPHA_KENTRY_IF); + else if (revent & POLLNVAL) + gdb->detach(); +} + +RemoteGDB::RemoteGDB(System *_system, ThreadContext *c) + : event(NULL), listener(NULL), number(-1), fd(-1), + active(false), attached(false), + system(_system), pmem(_system->physmem), context(c) +{ + memset(gdbregs, 0, sizeof(gdbregs)); +} + +RemoteGDB::~RemoteGDB() +{ + if (event) + delete event; +} + +string +RemoteGDB::name() +{ + return system->name() + ".remote_gdb"; +} + +bool +RemoteGDB::isattached() +{ return attached; } + +void +RemoteGDB::attach(int f) +{ + fd = f; + + event = new Event(this, fd, POLLIN); + pollQueue.schedule(event); + + attached = true; + DPRINTFN("remote gdb attached\n"); +} + +void +RemoteGDB::detach() +{ + attached = false; + close(fd); + fd = -1; + + pollQueue.remove(event); + DPRINTFN("remote gdb detached\n"); +} + +const char * +gdb_command(char cmd) +{ + switch (cmd) { + case KGDB_SIGNAL: return "KGDB_SIGNAL"; + case KGDB_SET_BAUD: return "KGDB_SET_BAUD"; + case KGDB_SET_BREAK: return "KGDB_SET_BREAK"; + case KGDB_CONT: return "KGDB_CONT"; + case KGDB_ASYNC_CONT: return "KGDB_ASYNC_CONT"; + case KGDB_DEBUG: return "KGDB_DEBUG"; + case KGDB_DETACH: return "KGDB_DETACH"; + case KGDB_REG_R: return "KGDB_REG_R"; + case KGDB_REG_W: return "KGDB_REG_W"; + case KGDB_SET_THREAD: return "KGDB_SET_THREAD"; + case KGDB_CYCLE_STEP: return "KGDB_CYCLE_STEP"; + case KGDB_SIG_CYCLE_STEP: return "KGDB_SIG_CYCLE_STEP"; + case KGDB_KILL: return "KGDB_KILL"; + case KGDB_MEM_W: return "KGDB_MEM_W"; + case KGDB_MEM_R: return "KGDB_MEM_R"; + case KGDB_SET_REG: return "KGDB_SET_REG"; + case KGDB_READ_REG: return "KGDB_READ_REG"; + case KGDB_QUERY_VAR: return "KGDB_QUERY_VAR"; + case KGDB_SET_VAR: return "KGDB_SET_VAR"; + case KGDB_RESET: return "KGDB_RESET"; + case KGDB_STEP: return "KGDB_STEP"; + case KGDB_ASYNC_STEP: return "KGDB_ASYNC_STEP"; + case KGDB_THREAD_ALIVE: return "KGDB_THREAD_ALIVE"; + case KGDB_TARGET_EXIT: return "KGDB_TARGET_EXIT"; + case KGDB_BINARY_DLOAD: return "KGDB_BINARY_DLOAD"; + case KGDB_CLR_HW_BKPT: return "KGDB_CLR_HW_BKPT"; + case KGDB_SET_HW_BKPT: return "KGDB_SET_HW_BKPT"; + case KGDB_START: return "KGDB_START"; + case KGDB_END: return "KGDB_END"; + case KGDB_GOODP: return "KGDB_GOODP"; + case KGDB_BADP: return "KGDB_BADP"; + default: return "KGDB_UNKNOWN"; + } +} + +/////////////////////////////////////////////////////////// +// RemoteGDB::acc +// +// Determine if the mapping at va..(va+len) is valid. +// +bool +RemoteGDB::acc(Addr va, size_t len) +{ + Addr last_va; + + va = TheISA::TruncPage(va); + last_va = TheISA::RoundPage(va + len); + + do { + if (TheISA::IsK0Seg(va)) { + if (va < (TheISA::K0SegBase + pmem->size())) { + DPRINTF(GDBAcc, "acc: Mapping is valid K0SEG <= " + "%#x < K0SEG + size\n", va); + return true; + } else { + DPRINTF(GDBAcc, "acc: Mapping invalid %#x > K0SEG + size\n", + va); + return false; + } + } + + /** + * This code says that all accesses to palcode (instruction and data) + * are valid since there isn't a va->pa mapping because palcode is + * accessed physically. At some point this should probably be cleaned up + * but there is no easy way to do it. + */ + + if (AlphaISA::PcPAL(va) || va < 0x10000) + return true; + + Addr ptbr = context->readMiscReg(AlphaISA::IPR_PALtemp20); + TheISA::PageTableEntry pte = TheISA::kernel_pte_lookup(context->getPhysPort(), ptbr, va); + if (!pte.valid()) { + DPRINTF(GDBAcc, "acc: %#x pte is invalid\n", va); + return false; + } + va += TheISA::PageBytes; + } while (va < last_va); + + DPRINTF(GDBAcc, "acc: %#x mapping is valid\n", va); + return true; +} + +/////////////////////////////////////////////////////////// +// RemoteGDB::signal +// +// Translate a trap number into a Unix-compatible signal number. +// (GDB only understands Unix signal numbers.) +// +int +RemoteGDB::signal(int type) +{ + switch (type) { + case ALPHA_KENTRY_INT: + return (SIGTRAP); + + case ALPHA_KENTRY_UNA: + return (SIGBUS); + + case ALPHA_KENTRY_ARITH: + return (SIGFPE); + + case ALPHA_KENTRY_IF: + return (SIGILL); + + case ALPHA_KENTRY_MM: + return (SIGSEGV); + + default: + panic("unknown signal type"); + return 0; + } +} + +/////////////////////////////////////////////////////////// +// RemoteGDB::getregs +// +// Translate the kernel debugger register format into +// the GDB register format. +void +RemoteGDB::getregs() +{ + memset(gdbregs, 0, sizeof(gdbregs)); + + gdbregs[KGDB_REG_PC] = context->readPC(); + + // @todo: Currently this is very Alpha specific. + if (AlphaISA::PcPAL(gdbregs[KGDB_REG_PC])) { + for (int i = 0; i < TheISA::NumIntArchRegs; ++i) { + gdbregs[i] = context->readIntReg(AlphaISA::reg_redir[i]); + } + } else { + for (int i = 0; i < TheISA::NumIntArchRegs; ++i) { + gdbregs[i] = context->readIntReg(i); + } + } + +#ifdef KGDB_FP_REGS + for (int i = 0; i < TheISA::NumFloatArchRegs; ++i) { + gdbregs[i + KGDB_REG_F0] = context->readFloatRegBits(i); + } +#endif +} + +/////////////////////////////////////////////////////////// +// RemoteGDB::setregs +// +// Translate the GDB register format into the kernel +// debugger register format. +// +void +RemoteGDB::setregs() +{ + // @todo: Currently this is very Alpha specific. + if (AlphaISA::PcPAL(gdbregs[KGDB_REG_PC])) { + for (int i = 0; i < TheISA::NumIntArchRegs; ++i) { + context->setIntReg(AlphaISA::reg_redir[i], gdbregs[i]); + } + } else { + for (int i = 0; i < TheISA::NumIntArchRegs; ++i) { + context->setIntReg(i, gdbregs[i]); + } + } + +#ifdef KGDB_FP_REGS + for (int i = 0; i < TheISA::NumFloatArchRegs; ++i) { + context->setFloatRegBits(i, gdbregs[i + KGDB_REG_F0]); + } +#endif + context->setPC(gdbregs[KGDB_REG_PC]); +} + +void +RemoteGDB::setTempBreakpoint(TempBreakpoint &bkpt, Addr addr) +{ + DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", addr); + + bkpt.address = addr; + insertHardBreak(addr, 4); +} + +void +RemoteGDB::clearTempBreakpoint(TempBreakpoint &bkpt) +{ + DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", + bkpt.address); + + + removeHardBreak(bkpt.address, 4); + bkpt.address = 0; +} + +void +RemoteGDB::clearSingleStep() +{ + DPRINTF(GDBMisc, "clearSingleStep bt_addr=%#x nt_addr=%#x\n", + takenBkpt.address, notTakenBkpt.address); + + if (takenBkpt.address != 0) + clearTempBreakpoint(takenBkpt); + + if (notTakenBkpt.address != 0) + clearTempBreakpoint(notTakenBkpt); +} + +void +RemoteGDB::setSingleStep() +{ + Addr pc = context->readPC(); + Addr npc, bpc; + bool set_bt = false; + + npc = pc + sizeof(MachInst); + + // User was stopped at pc, e.g. the instruction at pc was not + // executed. + MachInst inst = read<MachInst>(pc); + StaticInstPtr si(inst); + if (si->hasBranchTarget(pc, context, bpc)) { + // Don't bother setting a breakpoint on the taken branch if it + // is the same as the next pc + if (bpc != npc) + set_bt = true; + } + + DPRINTF(GDBMisc, "setSingleStep bt_addr=%#x nt_addr=%#x\n", + takenBkpt.address, notTakenBkpt.address); + + setTempBreakpoint(notTakenBkpt, npc); + + if (set_bt) + setTempBreakpoint(takenBkpt, bpc); +} + +///////////////////////// +// +// + +uint8_t +RemoteGDB::getbyte() +{ + uint8_t b; + ::read(fd, &b, 1); + return b; +} + +void +RemoteGDB::putbyte(uint8_t b) +{ + ::write(fd, &b, 1); +} + +// Send a packet to gdb +void +RemoteGDB::send(const char *bp) +{ + const char *p; + uint8_t csum, c; + + DPRINTF(GDBSend, "send: %s\n", bp); + + do { + p = bp; + putbyte(KGDB_START); + for (csum = 0; (c = *p); p++) { + putbyte(c); + csum += c; + } + putbyte(KGDB_END); + putbyte(i2digit(csum >> 4)); + putbyte(i2digit(csum)); + } while ((c = getbyte() & 0x7f) == KGDB_BADP); +} + +// Receive a packet from gdb +int +RemoteGDB::recv(char *bp, int maxlen) +{ + char *p; + int c, csum; + int len; + + do { + p = bp; + csum = len = 0; + while ((c = getbyte()) != KGDB_START) + ; + + while ((c = getbyte()) != KGDB_END && len < maxlen) { + c &= 0x7f; + csum += c; + *p++ = c; + len++; + } + csum &= 0xff; + *p = '\0'; + + if (len >= maxlen) { + putbyte(KGDB_BADP); + continue; + } + + csum -= digit2i(getbyte()) * 16; + csum -= digit2i(getbyte()); + + if (csum == 0) { + putbyte(KGDB_GOODP); + // Sequence present? + if (bp[2] == ':') { + putbyte(bp[0]); + putbyte(bp[1]); + len -= 3; + bcopy(bp + 3, bp, len); + } + break; + } + putbyte(KGDB_BADP); + } while (1); + + DPRINTF(GDBRecv, "recv: %s: %s\n", gdb_command(*bp), bp); + + return (len); +} + +// Read bytes from kernel address space for debugger. +bool +RemoteGDB::read(Addr vaddr, size_t size, char *data) +{ + static Addr lastaddr = 0; + static size_t lastsize = 0; + + if (vaddr < 10) { + DPRINTF(GDBRead, "read: reading memory location zero!\n"); + vaddr = lastaddr + lastsize; + } + + DPRINTF(GDBRead, "read: addr=%#x, size=%d", vaddr, size); + + VirtualPort *vp = context->getVirtPort(context); + vp->readBlob(vaddr, (uint8_t*)data, size); + context->delVirtPort(vp); + +#if TRACING_ON + if (DTRACE(GDBRead)) { + if (DTRACE(GDBExtra)) { + char buf[1024]; + mem2hex(buf, data, size); + DPRINTFNR(": %s\n", buf); + } else + DPRINTFNR("\n"); + } +#endif + + return true; +} + +// Write bytes to kernel address space for debugger. +bool +RemoteGDB::write(Addr vaddr, size_t size, const char *data) +{ + static Addr lastaddr = 0; + static size_t lastsize = 0; + + if (vaddr < 10) { + DPRINTF(GDBWrite, "write: writing memory location zero!\n"); + vaddr = lastaddr + lastsize; + } + + if (DTRACE(GDBWrite)) { + DPRINTFN("write: addr=%#x, size=%d", vaddr, size); + if (DTRACE(GDBExtra)) { + char buf[1024]; + mem2hex(buf, data, size); + DPRINTFNR(": %s\n", buf); + } else + DPRINTFNR("\n"); + } + VirtualPort *vp = context->getVirtPort(context); + vp->writeBlob(vaddr, (uint8_t*)data, size); + context->delVirtPort(vp); + +#ifdef IMB + alpha_pal_imb(); +#endif + + return true; +} + + +PCEventQueue *RemoteGDB::getPcEventQueue() +{ + return &system->pcEventQueue; +} + + +RemoteGDB::HardBreakpoint::HardBreakpoint(RemoteGDB *_gdb, Addr pc) + : PCEvent(_gdb->getPcEventQueue(), "HardBreakpoint Event", pc), + gdb(_gdb), refcount(0) +{ + DPRINTF(GDBMisc, "creating hardware breakpoint at %#x\n", evpc); +} + +void +RemoteGDB::HardBreakpoint::process(ThreadContext *tc) +{ + DPRINTF(GDBMisc, "handling hardware breakpoint at %#x\n", pc()); + + if (tc == gdb->context) + gdb->trap(ALPHA_KENTRY_INT); +} + +bool +RemoteGDB::insertSoftBreak(Addr addr, size_t len) +{ + if (len != sizeof(MachInst)) + panic("invalid length\n"); + + return insertHardBreak(addr, len); +} + +bool +RemoteGDB::removeSoftBreak(Addr addr, size_t len) +{ + if (len != sizeof(MachInst)) + panic("invalid length\n"); + + return removeHardBreak(addr, len); +} + +bool +RemoteGDB::insertHardBreak(Addr addr, size_t len) +{ + if (len != sizeof(MachInst)) + panic("invalid length\n"); + + DPRINTF(GDBMisc, "inserting hardware breakpoint at %#x\n", addr); + + HardBreakpoint *&bkpt = hardBreakMap[addr]; + if (bkpt == 0) + bkpt = new HardBreakpoint(this, addr); + + bkpt->refcount++; + + return true; +} + +bool +RemoteGDB::removeHardBreak(Addr addr, size_t len) +{ + if (len != sizeof(MachInst)) + panic("invalid length\n"); + + DPRINTF(GDBMisc, "removing hardware breakpoint at %#x\n", addr); + + break_iter_t i = hardBreakMap.find(addr); + if (i == hardBreakMap.end()) + return false; + + HardBreakpoint *hbp = (*i).second; + if (--hbp->refcount == 0) { + delete hbp; + hardBreakMap.erase(i); + } + + return true; +} + +const char * +break_type(char c) +{ + switch(c) { + case '0': return "software breakpoint"; + case '1': return "hardware breakpoint"; + case '2': return "write watchpoint"; + case '3': return "read watchpoint"; + case '4': return "access watchpoint"; + default: return "unknown breakpoint/watchpoint"; + } +} + +// This function does all command processing for interfacing to a +// remote gdb. Note that the error codes are ignored by gdb at +// present, but might eventually become meaningful. (XXX) It might +// makes sense to use POSIX errno values, because that is what the +// gdb/remote.c functions want to return. +bool +RemoteGDB::trap(int type) +{ + uint64_t val; + size_t datalen, len; + char data[KGDB_BUFLEN + 1]; + char buffer[sizeof(gdbregs) * 2 + 256]; + const char *p; + char command, subcmd; + string var; + bool ret; + + if (!attached) + return false; + + DPRINTF(GDBMisc, "trap: PC=%#x NPC=%#x\n", + context->readPC(), context->readNextPC()); + + clearSingleStep(); + + /* + * The first entry to this function is normally through + * a breakpoint trap in kgdb_connect(), in which case we + * must advance past the breakpoint because gdb will not. + * + * On the first entry here, we expect that gdb is not yet + * listening to us, so just enter the interaction loop. + * After the debugger is "active" (connected) it will be + * waiting for a "signaled" message from us. + */ + if (!active) + active = true; + else + // Tell remote host that an exception has occurred. + snprintf((char *)buffer, sizeof(buffer), "S%02x", signal(type)); + send(buffer); + + // Stick frame regs into our reg cache. + getregs(); + + for (;;) { + datalen = recv(data, sizeof(data)); + data[sizeof(data) - 1] = 0; // Sentinel + command = data[0]; + subcmd = 0; + p = data + 1; + switch (command) { + + case KGDB_SIGNAL: + // if this command came from a running gdb, answer it -- + // the other guy has no way of knowing if we're in or out + // of this loop when he issues a "remote-signal". + snprintf((char *)buffer, sizeof(buffer), "S%02x", signal(type)); + send(buffer); + continue; + + case KGDB_REG_R: + if (2 * sizeof(gdbregs) > sizeof(buffer)) + panic("buffer too small"); + + mem2hex(buffer, gdbregs, sizeof(gdbregs)); + send(buffer); + continue; + + case KGDB_REG_W: + p = hex2mem(gdbregs, p, sizeof(gdbregs)); + if (p == NULL || *p != '\0') + send("E01"); + else { + setregs(); + send("OK"); + } + continue; + +#if 0 + case KGDB_SET_REG: + val = hex2i(&p); + if (*p++ != '=') { + send("E01"); + continue; + } + if (val < 0 && val >= KGDB_NUMREGS) { + send("E01"); + continue; + } + + gdbregs[val] = hex2i(&p); + setregs(); + send("OK"); + + continue; +#endif + + case KGDB_MEM_R: + val = hex2i(&p); + if (*p++ != ',') { + send("E02"); + continue; + } + len = hex2i(&p); + if (*p != '\0') { + send("E03"); + continue; + } + if (len > sizeof(buffer)) { + send("E04"); + continue; + } + if (!acc(val, len)) { + send("E05"); + continue; + } + + if (read(val, (size_t)len, (char *)buffer)) { + // variable length array would be nice, but C++ doesn't + // officially support those... + char *temp = new char[2*len+1]; + mem2hex(temp, buffer, len); + send(temp); + delete [] temp; + } else { + send("E05"); + } + continue; + + case KGDB_MEM_W: + val = hex2i(&p); + if (*p++ != ',') { + send("E06"); + continue; + } + len = hex2i(&p); + if (*p++ != ':') { + send("E07"); + continue; + } + if (len > datalen - (p - data)) { + send("E08"); + continue; + } + p = hex2mem(buffer, p, sizeof(buffer)); + if (p == NULL) { + send("E09"); + continue; + } + if (!acc(val, len)) { + send("E0A"); + continue; + } + if (write(val, (size_t)len, (char *)buffer)) + send("OK"); + else + send("E0B"); + continue; + + case KGDB_SET_THREAD: + subcmd = *p++; + val = hex2i(&p); + if (val == 0) + send("OK"); + else + send("E01"); + continue; + + case KGDB_DETACH: + case KGDB_KILL: + active = false; + clearSingleStep(); + detach(); + goto out; + + case KGDB_ASYNC_CONT: + subcmd = hex2i(&p); + if (*p++ == ';') { + val = hex2i(&p); + context->setPC(val); + context->setNextPC(val + sizeof(MachInst)); + } + clearSingleStep(); + goto out; + + case KGDB_CONT: + if (p - data < datalen) { + val = hex2i(&p); + context->setPC(val); + context->setNextPC(val + sizeof(MachInst)); + } + clearSingleStep(); + goto out; + + case KGDB_ASYNC_STEP: + subcmd = hex2i(&p); + if (*p++ == ';') { + val = hex2i(&p); + context->setPC(val); + context->setNextPC(val + sizeof(MachInst)); + } + setSingleStep(); + goto out; + + case KGDB_STEP: + if (p - data < datalen) { + val = hex2i(&p); + context->setPC(val); + context->setNextPC(val + sizeof(MachInst)); + } + setSingleStep(); + goto out; + + case KGDB_CLR_HW_BKPT: + subcmd = *p++; + if (*p++ != ',') send("E0D"); + val = hex2i(&p); + if (*p++ != ',') send("E0D"); + len = hex2i(&p); + + DPRINTF(GDBMisc, "clear %s, addr=%#x, len=%d\n", + break_type(subcmd), val, len); + + ret = false; + + switch (subcmd) { + case '0': // software breakpoint + ret = removeSoftBreak(val, len); + break; + + case '1': // hardware breakpoint + ret = removeHardBreak(val, len); + break; + + case '2': // write watchpoint + case '3': // read watchpoint + case '4': // access watchpoint + default: // unknown + send(""); + break; + } + + send(ret ? "OK" : "E0C"); + continue; + + case KGDB_SET_HW_BKPT: + subcmd = *p++; + if (*p++ != ',') send("E0D"); + val = hex2i(&p); + if (*p++ != ',') send("E0D"); + len = hex2i(&p); + + DPRINTF(GDBMisc, "set %s, addr=%#x, len=%d\n", + break_type(subcmd), val, len); + + ret = false; + + switch (subcmd) { + case '0': // software breakpoint + ret = insertSoftBreak(val, len); + break; + + case '1': // hardware breakpoint + ret = insertHardBreak(val, len); + break; + + case '2': // write watchpoint + case '3': // read watchpoint + case '4': // access watchpoint + default: // unknown + send(""); + break; + } + + send(ret ? "OK" : "E0C"); + continue; + + case KGDB_QUERY_VAR: + var = string(p, datalen - 1); + if (var == "C") + send("QC0"); + else + send(""); + continue; + + case KGDB_SET_BAUD: + case KGDB_SET_BREAK: + case KGDB_DEBUG: + case KGDB_CYCLE_STEP: + case KGDB_SIG_CYCLE_STEP: + case KGDB_READ_REG: + case KGDB_SET_VAR: + case KGDB_RESET: + case KGDB_THREAD_ALIVE: + case KGDB_TARGET_EXIT: + case KGDB_BINARY_DLOAD: + // Unsupported command + DPRINTF(GDBMisc, "Unsupported command: %s\n", + gdb_command(command)); + DDUMP(GDBMisc, (uint8_t *)data, datalen); + send(""); + continue; + + default: + // Unknown command. + DPRINTF(GDBMisc, "Unknown command: %c(%#x)\n", + command, command); + send(""); + continue; + + + } + } + + out: + return true; +} + +// Convert a hex digit into an integer. +// This returns -1 if the argument passed is no valid hex digit. +int +digit2i(char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + else if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + else if (c >= 'A' && c <= 'F') + + return (c - 'A' + 10); + else + return (-1); +} + +// Convert the low 4 bits of an integer into an hex digit. +char +i2digit(int n) +{ + return ("0123456789abcdef"[n & 0x0f]); +} + +// Convert a byte array into an hex string. +void +mem2hex(void *vdst, const void *vsrc, int len) +{ + char *dst = (char *)vdst; + const char *src = (const char *)vsrc; + + while (len--) { + *dst++ = i2digit(*src >> 4); + *dst++ = i2digit(*src++); + } + *dst = '\0'; +} + +// Convert an hex string into a byte array. +// This returns a pointer to the character following the last valid +// hex digit. If the string ends in the middle of a byte, NULL is +// returned. +const char * +hex2mem(void *vdst, const char *src, int maxlen) +{ + char *dst = (char *)vdst; + int msb, lsb; + + while (*src && maxlen--) { + msb = digit2i(*src++); + if (msb < 0) + return (src - 1); + lsb = digit2i(*src++); + if (lsb < 0) + return (NULL); + *dst++ = (msb << 4) | lsb; + } + return (src); +} + +// Convert an hex string into an integer. +// This returns a pointer to the character following the last valid +// hex digit. +Addr +hex2i(const char **srcp) +{ + const char *src = *srcp; + Addr r = 0; + int nibble; + + while ((nibble = digit2i(*src)) >= 0) { + r *= 16; + r += nibble; + src++; + } + *srcp = src; + return (r); +} + diff --git a/src/base/remote_gdb.hh b/src/base/remote_gdb.hh new file mode 100644 index 000000000..8c3ce7572 --- /dev/null +++ b/src/base/remote_gdb.hh @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __REMOTE_GDB_HH__ +#define __REMOTE_GDB_HH__ + +#include <map> + +#include "arch/types.hh" +#include "base/kgdb.h" +#include "cpu/pc_event.hh" +#include "base/pollevent.hh" +#include "base/socket.hh" + +class System; +class ThreadContext; +class PhysicalMemory; + +class GDBListener; +class RemoteGDB +{ + protected: + typedef TheISA::MachInst MachInst; + private: + friend void debugger(); + friend class GDBListener; + + protected: + class Event : public PollEvent + { + protected: + RemoteGDB *gdb; + + public: + Event(RemoteGDB *g, int fd, int e); + void process(int revent); + }; + + friend class Event; + Event *event; + GDBListener *listener; + int number; + + protected: + int fd; + uint64_t gdbregs[KGDB_NUMREGS]; + + protected: +#ifdef notyet + label_t recover; +#endif + bool active; + bool attached; + + System *system; + PhysicalMemory *pmem; + ThreadContext *context; + + protected: + uint8_t getbyte(); + void putbyte(uint8_t b); + + int recv(char *data, int len); + void send(const char *data); + + protected: + // Machine memory + bool read(Addr addr, size_t size, char *data); + bool write(Addr addr, size_t size, const char *data); + + template <class T> T read(Addr addr); + template <class T> void write(Addr addr, T data); + + public: + RemoteGDB(System *system, ThreadContext *context); + ~RemoteGDB(); + + void replaceThreadContext(ThreadContext *tc) { context = tc; } + + void attach(int fd); + void detach(); + bool isattached(); + + bool acc(Addr addr, size_t len); + static int signal(int type); + bool trap(int type); + + protected: + void getregs(); + void setregs(); + + void clearSingleStep(); + void setSingleStep(); + + PCEventQueue *getPcEventQueue(); + + protected: + class HardBreakpoint : public PCEvent + { + private: + RemoteGDB *gdb; + + public: + int refcount; + + public: + HardBreakpoint(RemoteGDB *_gdb, Addr addr); + std::string name() { return gdb->name() + ".hwbkpt"; } + + virtual void process(ThreadContext *tc); + }; + friend class HardBreakpoint; + + typedef std::map<Addr, HardBreakpoint *> break_map_t; + typedef break_map_t::iterator break_iter_t; + break_map_t hardBreakMap; + + bool insertSoftBreak(Addr addr, size_t len); + bool removeSoftBreak(Addr addr, size_t len); + bool insertHardBreak(Addr addr, size_t len); + bool removeHardBreak(Addr addr, size_t len); + + protected: + struct TempBreakpoint { + Addr address; // set here + MachInst bkpt_inst; // saved instruction at bkpt + int init_count; // number of times to skip bkpt + int count; // current count + }; + + TempBreakpoint notTakenBkpt; + TempBreakpoint takenBkpt; + + void clearTempBreakpoint(TempBreakpoint &bkpt); + void setTempBreakpoint(TempBreakpoint &bkpt, Addr addr); + + public: + std::string name(); +}; + +template <class T> +inline T +RemoteGDB::read(Addr addr) +{ + T temp; + read(addr, sizeof(T), (char *)&temp); + return temp; +} + +template <class T> +inline void +RemoteGDB::write(Addr addr, T data) +{ write(addr, sizeof(T), (const char *)&data); } + +class GDBListener +{ + protected: + class Event : public PollEvent + { + protected: + GDBListener *listener; + + public: + Event(GDBListener *l, int fd, int e); + void process(int revent); + }; + + friend class Event; + Event *event; + + protected: + ListenSocket listener; + RemoteGDB *gdb; + int port; + + public: + GDBListener(RemoteGDB *g, int p); + ~GDBListener(); + + void accept(); + void listen(); + std::string name(); +}; + +#endif /* __REMOTE_GDB_H__ */ diff --git a/src/base/res_list.hh b/src/base/res_list.hh new file mode 100644 index 000000000..442280e15 --- /dev/null +++ b/src/base/res_list.hh @@ -0,0 +1,758 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Raasch + * Nathan Binkert + */ + +#ifndef __RES_LIST_HH__ +#define __RES_LIST_HH__ + +#include "base/cprintf.hh" +#include <assert.h> + +#define DEBUG_REMOVE 0 + +#define DEBUG_MEMORY 0 +//#define DEBUG_MEMORY DEBUG + +class res_list_base +{ +#if DEBUG_MEMORY + protected: + static long long allocated_elements; + static long long allocated_lists; + + public: + long long get_elements(void) { + return allocated_elements; + } + long long get_lists(void) { + return allocated_lists; + } + +#endif +}; + +#if DEBUG_MEMORY +extern void what_the(void); +#endif + +template<class T> +class res_list : public res_list_base +{ + public: + class iterator; + + class res_element + { + res_element *next; + res_element *prev; + T *data; + bool allocate_data; + + public: + // always adds to the END of the list + res_element(res_element *_prev, bool allocate); + ~res_element(); + void dump(void); + + friend class res_list<T>; + friend class res_list<T>::iterator; + }; + + class iterator + { + private: + res_element *p; + + friend class res_list<T>; + + public: + // Constructors + iterator(res_element *q) : p(q) {} + iterator(void) { p=0; }; + + void dump(void); + T* data_ptr(void); + res_element *res_el_ptr(void) { return p;} + void point_to(T &d) { p->data = &d; } + + iterator next(void) { return iterator(p->next); } + iterator prev(void) { return iterator(p->prev); } + bool operator== (iterator x) { return (x.p == this->p); } + bool operator != (iterator x) { return (x.p != this->p); } + T &operator * (void) { return *(p->data); } + T* operator -> (void) { return p->data; } + bool isnull(void) { return (p==0); } + bool notnull(void) { return (p!=0); } + }; + + private: + iterator unused_elements; + iterator head_ptr; + iterator tail_ptr; + + unsigned base_elements; + unsigned extra_elements; + unsigned active_elements; + bool allocate_storage; + unsigned build_size; + + int remove_count; + + // + // Allocate new elements, and assign them to the unused_elements + // list. + // + unsigned allocate_elements(unsigned num, bool allocate_storage); + + public: + // + // List Constructor + // + res_list(unsigned size, bool alloc_storage = false, + unsigned build_sz = 5); + + // + // List Destructor + // + ~res_list(); + + iterator head(void) {return head_ptr;}; + iterator tail(void) {return tail_ptr;}; + + unsigned num_free(void) { return size() - count(); } + unsigned size(void) { return base_elements + extra_elements; } + unsigned count(void) { return active_elements; } + bool empty(void) { return count() == 0; } + bool full(void); + + // + // Insert with data copy + // + iterator insert_after(iterator prev, T *d); + iterator insert_after(iterator prev, T &d); + iterator insert_before(iterator prev, T *d); + iterator insert_before(iterator prev, T &d); + + // + // Insert new list element (no data copy) + // + iterator insert_after(iterator prev); + iterator insert_before(iterator prev); + + iterator add_tail(T *d) { return insert_after(tail_ptr, d); } + iterator add_tail(T &d) { return insert_after(tail_ptr, d); } + iterator add_tail(void) { return insert_after(tail_ptr); } + iterator add_head(T *d) { return insert_before(head_ptr, d); } + iterator add_head(T &d) { return insert_before(head_ptr, d); } + iterator add_head(void) { return insert_before(head_ptr); } + + iterator remove(iterator q); + iterator remove_head(void) {return remove(head_ptr);} + iterator remove_tail(void) {return remove(tail_ptr);} + + bool in_list(iterator j); + void free_extras(void); + void clear(void); + void dump(void); + void raw_dump(void); +}; + +template <class T> +inline +res_list<T>::res_element::res_element(res_element *_prev, bool allocate) +{ + allocate_data = allocate; + prev = _prev; + next = 0; + + if (prev) + prev->next = this; + + if (allocate) + data = new T; + else + data = 0; + +#if DEBUG_MEMORY + ++allocated_elements; +#endif +} + +template <class T> +inline +res_list<T>::res_element::~res_element(void) +{ + if (prev) + prev->next = next; + + if (next) + next->prev = prev; + + if (allocate_data) + delete data; + +#if DEBUG_MEMORY + --allocated_elements; +#endif +} + +template <class T> +inline void +res_list<T>::res_element::dump(void) +{ + cprintf(" prev = %#x\n", prev); + cprintf(" next = %#x\n", next); + cprintf(" data = %#x\n", data); +} + +template <class T> +inline void +res_list<T>::iterator::dump(void) +{ + if (p && p->data) + p->data->dump(); + else { + if (!p) + cprintf(" Null Pointer\n"); + else + cprintf(" Null 'data' Pointer\n"); + } +} + +template <class T> +inline T * +res_list<T>::iterator::data_ptr(void) +{ + if (p) + return p->data; + else + return 0; +} + + +// +// Allocate new elements, and assign them to the unused_elements +// list. +// +template <class T> +inline unsigned +res_list<T>::allocate_elements(unsigned num, bool allocate_storage) +{ + res_element *pnew, *plast = 0, *pfirst=0; + + for (int i=0; i<num; ++i) { + pnew = new res_element(plast, allocate_storage); + if (i==0) + pfirst = pnew; + plast = pnew; + } + + if (unused_elements.notnull()) { + // Add these new elements to the front of the list + plast->next = unused_elements.res_el_ptr(); + unused_elements.res_el_ptr()->prev = plast; + } + + unused_elements = iterator(pfirst); + + return num; +} + +template <class T> +inline +res_list<T>::res_list(unsigned size, bool alloc_storage, unsigned build_sz) +{ +#if DEBUG_MEMORY + ++allocated_lists; +#endif + extra_elements = 0; + active_elements = 0; + build_size = build_sz; + allocate_storage = alloc_storage; + remove_count = 0; + + // Create the new elements + base_elements = allocate_elements(size, alloc_storage); + + // The list of active elements + head_ptr = iterator(0); + tail_ptr = iterator(0); +} + +// +// List Destructor +// +template <class T> +inline +res_list<T>::~res_list(void) +{ + iterator n; + +#if DEBUG_MEMORY + --allocated_lists; +#endif + + // put everything into the unused list + clear(); + + // rudely delete all the res_elements + for (iterator p = unused_elements; + p.notnull(); + p = n) { + + n = p.next(); + + // delete the res_element + // (it will take care of deleting the data) + delete p.res_el_ptr(); + } +} + +template <class T> +inline bool +res_list<T>::full(void) +{ + if (build_size) + return false; + else + return unused_elements.isnull(); +} + +// +// Insert with data copy +// +template <class T> +inline typename res_list<T>::iterator +res_list<T>::insert_after(iterator prev, T *d) +{ + iterator p; + + if (!allocate_storage) + this->panic("Can't copy data... not allocating storage"); + + p = insert_after(prev); + if (p.notnull()) + *p = *d; + + return p; +} + + +template <class T> +inline typename res_list<T>::iterator +res_list<T>::insert_after(iterator prev, T &d) +{ + iterator p; + + p = insert_after(prev); + if (p.notnull()) { + + if (allocate_storage) { + // if we allocate storage, then copy the contents of the + // specified object to our object + *p = d; + } + else { + // if we don't allocate storage, then we just want to + // point to the specified object + p.point_to(d); + } + } + + return p; +} + + +template <class T> +inline typename res_list<T>::iterator +res_list<T>::insert_after(iterator prev) +{ + +#if DEBUG_MEMORY + if (active_elements > 2*base_elements) { + what_the(); + } +#endif + + // If we have no unused elements, make some more + if (unused_elements.isnull()) { + + if (build_size == 0) { + return 0; // No space left, and can't allocate more.... + } + + extra_elements += allocate_elements(build_size, allocate_storage); + } + + // grab the first unused element + res_element *p = unused_elements.res_el_ptr(); + + unused_elements = unused_elements.next(); + + ++active_elements; + + // Insert the new element + if (head_ptr.isnull()) { + // + // Special case #1: Empty List + // + head_ptr = p; + tail_ptr = p; + p->prev = 0; + p->next = 0; + } + else if (prev.isnull()) { + // + // Special case #2: Insert at head + // + + // our next ptr points to old head element + p->next = head_ptr.res_el_ptr(); + + // our element becomes the new head element + head_ptr = p; + + // no previous element for the head + p->prev = 0; + + // old head element points back to this element + p->next->prev = p; + } + else if (prev.next().isnull()) { + // + // Special case #3 Insert at tail + // + + // our prev pointer points to old tail element + p->prev = tail_ptr.res_el_ptr(); + + // our element becomes the new tail + tail_ptr = p; + + // no next element for the tail + p->next = 0; + + // old tail element point to this element + p->prev->next = p; + } + else { + // + // Normal insertion (after prev) + // + p->prev = prev.res_el_ptr(); + p->next = prev.next().res_el_ptr(); + + prev.res_el_ptr()->next = p; + p->next->prev = p; + } + + return iterator(p); +} + +template <class T> +inline typename res_list<T>::iterator +res_list<T>::insert_before(iterator next, T &d) +{ + iterator p; + + p = insert_before(next); + if (p.notnull()) { + + if (allocate_storage) { + // if we allocate storage, then copy the contents of the + // specified object to our object + *p = d; + } + else { + // if we don't allocate storage, then we just want to + // point to the specified object + p.point_to(d); + } + } + + return p; +} + + +template <class T> +inline typename res_list<T>::iterator +res_list<T>::insert_before(iterator next) +{ + +#if DEBUG_MEMORY + if (active_elements > 2*base_elements) { + what_the(); + } +#endif + + // If we have no unused elements, make some more + if (unused_elements.isnull()) { + + if (build_size == 0) { + return 0; // No space left, and can't allocate more.... + } + + extra_elements += allocate_elements(build_size, allocate_storage); + } + + // grab the first unused element + res_element *p = unused_elements.res_el_ptr(); + + unused_elements = unused_elements.next(); + + ++active_elements; + + // Insert the new element + if (head_ptr.isnull()) { + // + // Special case #1: Empty List + // + head_ptr = p; + tail_ptr = p; + p->prev = 0; + p->next = 0; + } + else if (next.isnull()) { + // + // Special case #2 Insert at tail + // + + // our prev pointer points to old tail element + p->prev = tail_ptr.res_el_ptr(); + + // our element becomes the new tail + tail_ptr = p; + + // no next element for the tail + p->next = 0; + + // old tail element point to this element + p->prev->next = p; + } + else if (next.prev().isnull()) { + // + // Special case #3: Insert at head + // + + // our next ptr points to old head element + p->next = head_ptr.res_el_ptr(); + + // our element becomes the new head element + head_ptr = p; + + // no previous element for the head + p->prev = 0; + + // old head element points back to this element + p->next->prev = p; + } + else { + // + // Normal insertion (before next) + // + p->next = next.res_el_ptr(); + p->prev = next.prev().res_el_ptr(); + + next.res_el_ptr()->prev = p; + p->prev->next = p; + } + + return iterator(p); +} + + +template <class T> +inline typename res_list<T>::iterator +res_list<T>::remove(iterator q) +{ + res_element *p = q.res_el_ptr(); + iterator n = 0; + + // Handle the special cases + if (active_elements == 1) { // This is the only element + head_ptr = 0; + tail_ptr = 0; + } + else if (q == head_ptr) { // This is the head element + head_ptr = q.next(); + head_ptr.res_el_ptr()->prev = 0; + + n = head_ptr; + } + else if (q == tail_ptr) { // This is the tail element + tail_ptr = q.prev(); + tail_ptr.res_el_ptr()->next = 0; + } + else { // This is between two elements + p->prev->next = p->next; + p->next->prev = p->prev; + + // Get the "next" element for return + n = p->next; + } + + --active_elements; + + // Put this element back onto the unused list + p->next = unused_elements.res_el_ptr(); + p->prev = 0; + if (p->next) { // NULL if unused list is empty + p->next->prev = p; + } + + if (!allocate_storage) { + p->data = 0; + } + + unused_elements = q; + + // A little "garbage collection" + if (++remove_count > 10) { + // free_extras(); + remove_count = 0; + } + +#if DEBUG_REMOVE + unsigned unused_count = 0; + for (iterator i=unused_elements; + i.notnull(); + i = i.next()) { + + ++unused_count; + } + + assert((active_elements+unused_count) == (base_elements+extra_elements)); +#endif + + return iterator(n); +} + + +template <class T> +inline bool +res_list<T>::in_list(iterator j) +{ + iterator i; + + for (i=head(); i.notnull(); i=i.next()) { + if (j.res_el_ptr() == i.res_el_ptr()) { + return true; + } + } + + return false; +} + +template <class T> +inline void +res_list<T>::free_extras(void) +{ + unsigned num_unused = base_elements + extra_elements - active_elements; + unsigned to_free = extra_elements; + res_element *p; + + + if (extra_elements != 0) { + // + // Free min(extra_elements, # unused elements) + // + if (extra_elements > num_unused) { + to_free = num_unused; + } + + p = unused_elements.res_el_ptr(); + for (int i=0; i<to_free; ++i) { + res_element *q = p->next; + + delete p; + + p = q; + } + + // update the unused element pointer to point to the first + // element that wasn't deleted. + unused_elements = iterator(p); + + // Update the number of extra elements + extra_elements -= to_free; + } + + return; +} + + +template <class T> +inline void +res_list<T>::clear(void) +{ + iterator i,n; + + for (i=head_ptr; i.notnull(); i=n) { + n = i.next(); + remove(i); + } + + free_extras(); +} + +template <class T> +inline void +res_list<T>::dump(void) +{ + for (iterator i=head(); !i.isnull(); i=i.next()) + i->dump(); +} + +template <class T> +inline void +res_list<T>::raw_dump(void) +{ + int j = 0; + res_element *p; + for (iterator i=head(); !i.isnull(); i=i.next()) { + cprintf("Element %d:\n", j); + + if (i.notnull()) { + p = i.res_el_ptr(); + cprintf(" points to res_element @ %#x\n", p); + p->dump(); + cprintf(" Data Element:\n"); + i->dump(); + } + else { + cprintf(" NULL iterator!\n"); + } + + ++j; + } + +} + +#endif // __RES_LIST_HH__ diff --git a/src/base/sat_counter.cc b/src/base/sat_counter.cc new file mode 100644 index 000000000..8980275eb --- /dev/null +++ b/src/base/sat_counter.cc @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Lisa Hsu + */ + +#include <sstream> + +#include "base/sat_counter.hh" +#include "base/statistics.hh" +#include "sim/stats.hh" + + +using namespace std; + + +SaturatingCounterPred::SaturatingCounterPred(string p_name, + string z_name, + string o_name, + unsigned _index_bits, + unsigned _counter_bits, + unsigned _zero_change, + unsigned _one_change, + unsigned _thresh, + unsigned _init_value) +{ + pred_name = p_name; + zero_name = z_name; + one_name = o_name; + + index_bits = _index_bits; + counter_bits = _counter_bits; + zero_change = _zero_change; + one_change = _one_change; + thresh = _thresh; + init_value = _init_value; + + max_index = (1 << index_bits) - 1; + max_value = (1 << counter_bits) - 1; + + table = new unsigned[max_index + 1]; + + // Initialize with the right parameters & clear the counter + for (int i = 0; i <= max_index; ++i) + table[i] = init_value; +} + +void SaturatingCounterPred::regStats() +{ + using namespace Stats; + stringstream name, description; + + // + // Number of predictions + // + name << pred_name << ":" << zero_name << ":preds"; + description << "number of predictions of " << zero_name; + predicted_zero + .name(name.str()) + .desc(description.str()) + ; + description.str(""); + name.str(""); + + name << pred_name << ":" << one_name << ":preds"; + description << "number of predictions of " << one_name; + predicted_one + .name(name.str()) + .desc(description.str()) + ; + description.str(""); + name.str(""); + + // + // Count the number of correct predictions + // + name << pred_name << ":" << zero_name << ":corr_preds"; + description << "number of correct " << zero_name << " preds"; + correct_pred_zero + .name(name.str()) + .desc(description.str()) + ; + description.str(""); + name.str(""); + + name << pred_name << ":" << one_name << ":corr_preds"; + description << "number of correct " << one_name << " preds"; + correct_pred_one + .name(name.str()) + .desc(description.str()) + ; + description.str(""); + name.str(""); + + // + // Number of predictor updates + // + name << pred_name << ":" << zero_name << ":updates"; + description << "number of actual " << zero_name << "s"; + record_zero + .name(name.str()) + .desc(description.str()) + ; + description.str(""); + name.str(""); + + name << pred_name << ":" << one_name << ":updates"; + description << "number of actual " << one_name << "s"; + record_one + .name(name.str()) + .desc(description.str()) + ; + description.str(""); + name.str(""); +} + +void SaturatingCounterPred::regFormulas() +{ + using namespace Stats; + stringstream name, description; + + // + // Number of predictions + // + name << pred_name << ":predictions"; + preds_total + .name(name.str()) + .desc("total number of predictions made") + ; + preds_total = predicted_zero + predicted_one; + name.str(""); + + // + // Fraction of all predictions that are one or zero + // + name << pred_name << ":" << zero_name << ":pred_frac"; + description << "fraction of all preds that were " << zero_name; + pred_frac_zero + .name(name.str()) + .desc(description.str()) + ; + pred_frac_zero = 100 * predicted_zero / preds_total; + description.str(""); + name.str(""); + + name << pred_name << ":" << one_name << ":pred_frac"; + description << "fraction of all preds that were " << one_name; + pred_frac_one + .name(name.str()) + .desc(description.str()) + ; + pred_frac_one = 100 * predicted_one / preds_total; + description.str(""); + name.str(""); + + + // + // Count the number of correct predictions + // + name << pred_name << ":correct_preds"; + correct_total + .name(name.str()) + .desc("total correct predictions made") + ; + correct_total = correct_pred_one + correct_pred_zero; + name.str(""); + + // + // Number of predictor updates + // + name << pred_name << ":updates"; + updates_total + .name(name.str()) + .desc("total number of updates") + ; + updates_total = record_zero + record_one; + name.str(""); + + // + // Prediction accuracy rates + // + name << pred_name << ":pred_rate"; + pred_rate + .name(name.str()) + .desc("correct fraction of all preds") + ; + pred_rate = correct_total / updates_total; + name.str(""); + + name << pred_name << ":" << zero_name << ":pred_rate"; + description << "fraction of " << zero_name << " preds that were correct"; + frac_correct_zero + .name(name.str()) + .desc(description.str()) + ; + frac_correct_zero = 100 * correct_pred_zero / + (correct_pred_zero + record_one - correct_pred_one); + description.str(""); + name.str(""); + + name << pred_name << ":" << one_name << ":pred_rate"; + description << "fraction of " << one_name << " preds that were correct"; + frac_correct_one + .name(name.str()) + .desc(description.str()) + ; + frac_correct_one = 100 * correct_pred_one / + (correct_pred_one + record_zero - correct_pred_zero); + description.str(""); + name.str(""); + + // + // Coverage + // + name << pred_name << ":" << zero_name << ":coverage"; + description << "fraction of " << zero_name + << "s that were predicted correctly"; + coverage_zero + .name(name.str()) + .desc(description.str()) + ; + coverage_zero = 100 * correct_pred_zero / record_zero; + description.str(""); + name.str(""); + + name << pred_name << ":" << one_name << ":coverage"; + description << "fraction of " << one_name + << "s that were predicted correctly"; + coverage_one + .name(name.str()) + .desc(description.str()) + ; + coverage_one = 100 * correct_pred_one / record_one; + description.str(""); + name.str(""); +} + + + + + + + + + + diff --git a/src/base/sat_counter.hh b/src/base/sat_counter.hh new file mode 100644 index 000000000..79de11156 --- /dev/null +++ b/src/base/sat_counter.hh @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Raasch + * Steve Reinhardt + */ + +#ifndef __SAT_COUNTER_HH__ +#define __SAT_COUNTER_HH__ + +#include <string> + +#include "base/predictor.hh" + +#include "base/statistics.hh" +#include "sim/stats.hh" + +// +// +// A simple saturating counter predictor +// +// +class SaturatingCounterPred : public GenericPredictor +{ + private: + std::string pred_name; + std::string zero_name; + std::string one_name; + + unsigned index_bits; + unsigned counter_bits; + unsigned zero_change; + unsigned one_change; + unsigned thresh; + unsigned init_value; + + unsigned max_value; // maximum counter value + + unsigned long max_index; // also the index mask value + unsigned *table; + + // Statistics + Stats::Scalar<> predicted_one; // Total predictions of one, preds_one + Stats::Scalar<> predicted_zero; // Total predictions of zero, preds_zero + Stats::Scalar<> correct_pred_one; // Total correct predictions of one, correct_one + Stats::Scalar<> correct_pred_zero; // Total correct predictions of zero, correct_zero + + Stats::Scalar<> record_zero; //updates_zero + Stats::Scalar<> record_one; //updates_one + + Stats::Formula preds_total; + Stats::Formula pred_frac_zero; + Stats::Formula pred_frac_one; + Stats::Formula correct_total; + Stats::Formula updates_total; + Stats::Formula pred_rate; + Stats::Formula frac_correct_zero; + Stats::Formula frac_correct_one; + Stats::Formula coverage_zero; + Stats::Formula coverage_one; + + private: + bool pred_one(unsigned &counter) { return counter > thresh; } + bool pred_zero(unsigned &counter) { return counter <= thresh; } + + void update_one(unsigned &counter) { + + if (one_change) + counter += one_change; + else + counter = 0; + + // check for wrap + if (counter > max_value) + counter = max_value; + } + + void update_zero(unsigned &counter) { + if (zero_change) { + // check for wrap + if (counter < zero_change) + counter = 0; + else + counter -= zero_change; + } else + counter = 0; + } + + + public: + + SaturatingCounterPred(std::string p_name, + std::string z_name, std::string o_name, + unsigned _index_bits, unsigned _counter_bits = 2, + unsigned _zero_change = 1, unsigned _one_change = 1, + unsigned _thresh = 1, unsigned _init_value = 0); + + void clear() { + for (int i = 0; i <= max_index; ++i) + table[i] = init_value; + } + + // Record the ACTUAL result... and indicate whether the prediction + // corresponding to this event was correct + void record(unsigned long _index, unsigned _val, unsigned _predicted, + unsigned _pdata) + { + record(_index, _val, _predicted); + } + + void record(unsigned long _index, unsigned _val, unsigned _predicted) { + unsigned long index = _index & max_index; + + if (_val) { + update_one(table[index]); + ++record_one; + + if (_predicted) + ++correct_pred_one; + } else { + update_zero(table[index]); + ++record_zero; + + if (!_predicted) + ++correct_pred_zero; + } + } + + unsigned value(unsigned long _index) { + unsigned long index = _index & max_index; + + return table[index]; + } + + + unsigned predict(unsigned long _index, unsigned &pdata) { + return predict(_index); + } + + unsigned predict(unsigned long _index) { + unsigned long index = _index & max_index; + + if (pred_one(table[index])) { + ++predicted_one; + return 1; + } + + ++predicted_zero; + return 0; + } + + // No internal state is changed here + unsigned peek(unsigned long _index) { + unsigned long index = _index & max_index; + + if (pred_one(table[index])) + return 1; + + return 0; + } + + + //======================================================= + void regStats(); + void regFormulas(); +}; + + +#endif // __SAT_COUNTER_HH__ diff --git a/src/base/sched_list.hh b/src/base/sched_list.hh new file mode 100644 index 000000000..56ee2f8ec --- /dev/null +++ b/src/base/sched_list.hh @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Raasch + */ + +#ifndef SCHED_LIST_HH +#define SCHED_LIST_HH + +#include <list> +#include "base/intmath.hh" +#include "base/misc.hh" + + +// Any types you use this class for must be covered here... +namespace { + void ClearEntry(int &i) { i = 0; }; + void ClearEntry(unsigned &i) { i = 0; }; + void ClearEntry(double &i) { i = 0; }; + template <class T> void ClearEntry(std::list<T> &l) { l.clear(); }; +}; + + +// +// this is a special list type that allows the user to insert elements at a +// specified positive offset from the "current" element, but only allow them +// be extracted from the "current" element +// + + +template <class T> +class SchedList +{ + T *data_array; + unsigned position; + unsigned size; + unsigned mask; + + public: + SchedList(unsigned size); + SchedList(void); + + void init(unsigned size); + + T &operator[](unsigned offset); + + void advance(void); + + void clear(void); +}; + + + +// +// Constructor +// +template<class T> +SchedList<T>::SchedList(unsigned _size) +{ + size = _size; + + // size must be a power of two + if (!isPowerOf2(size)) { + panic("SchedList: size must be a power of two"); + } + + if (size < 2) { + panic("SchedList: you don't want a list that small"); + } + + // calculate the bit mask for the modulo operation + mask = size - 1; + + data_array = new T[size]; + + if (!data_array) { + panic("SchedList: could not allocate memory"); + } + + clear(); +} + +template<class T> +SchedList<T>::SchedList(void) +{ + data_array = 0; + size = 0; +} + + +template<class T> void +SchedList<T>::init(unsigned _size) +{ + size = _size; + + if (!data_array) { + // size must be a power of two + if (size & (size-1)) { + panic("SchedList: size must be a power of two"); + } + + if (size < 2) { + panic("SchedList: you don't want a list that small"); + } + + // calculate the bit mask for the modulo operation + mask = size - 1; + + data_array = new T[size]; + + if (!data_array) { + panic("SchedList: could not allocate memory"); + } + + clear(); + } +} + + +template<class T> void +SchedList<T>::advance(void) +{ + ClearEntry(data_array[position]); + + // position = (++position % size); + position = ++position & mask; +} + + +template<class T> void +SchedList<T>::clear(void) +{ + for (unsigned i=0; i<size; ++i) { + ClearEntry(data_array[i]); + } + + position = 0; +} + + +template<class T> T& +SchedList<T>::operator[](unsigned offset) +{ + if (offset >= size) { + panic("SchedList: can't access element beyond current pointer"); + } + + // unsigned p = (position + offset) % size; + unsigned p = (position + offset) & mask; + + return data_array[p]; +} + + + +#endif diff --git a/src/base/socket.cc b/src/base/socket.cc new file mode 100644 index 000000000..adcc48735 --- /dev/null +++ b/src/base/socket.cc @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <netinet/tcp.h> + +#include <errno.h> +#include <unistd.h> + +#include "sim/host.hh" +#include "base/misc.hh" +#include "base/socket.hh" + +using namespace std; + +//////////////////////////////////////////////////////////////////////// +// +// + +ListenSocket::ListenSocket() + : listening(false), fd(-1) +{} + +ListenSocket::~ListenSocket() +{ + if (fd != -1) + close(fd); +} + +// Create a socket and configure it for listening +bool +ListenSocket::listen(int port, bool reuse) +{ + if (listening) + panic("Socket already listening!"); + + fd = ::socket(PF_INET, SOCK_STREAM, 0); + if (fd < 0) + panic("Can't create socket:%s !", strerror(errno)); + + if (reuse) { + int i = 1; + if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&i, + sizeof(i)) < 0) + panic("ListenSocket(listen): setsockopt() SO_REUSEADDR failed!"); + } + + struct sockaddr_in sockaddr; + sockaddr.sin_family = PF_INET; + sockaddr.sin_addr.s_addr = INADDR_ANY; + + sockaddr.sin_port = htons(port); + int ret = ::bind(fd, (struct sockaddr *)&sockaddr, sizeof (sockaddr)); + if (ret != 0) { + if (ret == -1 && errno != EADDRINUSE) + panic("ListenSocket(listen): bind() failed!"); + return false; + } + + if (::listen(fd, 1) == -1) + panic("ListenSocket(listen): listen() failed!"); + + listening = true; + + return true; +} + + +// Open a connection. Accept will block, so if you don't want it to, +// make sure a connection is ready before you call accept. +int +ListenSocket::accept(bool nodelay) +{ + struct sockaddr_in sockaddr; + socklen_t slen = sizeof (sockaddr); + int sfd = ::accept(fd, (struct sockaddr *)&sockaddr, &slen); + if (sfd != -1 && nodelay) { + int i = 1; + ::setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, (char *)&i, sizeof(i)); + } + + return sfd; +} diff --git a/src/base/socket.hh b/src/base/socket.hh new file mode 100644 index 000000000..8e55eae72 --- /dev/null +++ b/src/base/socket.hh @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __SOCKET_HH__ +#define __SOCKET_HH__ + +class ListenSocket +{ + protected: + bool listening; + int fd; + + public: + ListenSocket(); + virtual ~ListenSocket(); + + virtual int accept(bool nodelay = false); + virtual bool listen(int port, bool reuse = true); + + int getfd() const { return fd; } + bool islistening() const { return listening; } +}; + +#endif //__SOCKET_HH__ diff --git a/src/base/statistics.cc b/src/base/statistics.cc new file mode 100644 index 000000000..2acef83c5 --- /dev/null +++ b/src/base/statistics.cc @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iomanip> +#include <fstream> +#include <list> +#include <map> +#include <string> + +#include "base/callback.hh" +#include "base/cprintf.hh" +#include "base/hostinfo.hh" +#include "base/misc.hh" +#include "base/statistics.hh" +#include "base/str.hh" +#include "base/time.hh" +#include "base/trace.hh" +#include "base/stats/statdb.hh" + +using namespace std; + +namespace Stats { + +StatData * +DataAccess::find() const +{ + return Database::find(const_cast<void *>((const void *)this)); +} + +const StatData * +getStatData(const void *stat) +{ + return Database::find(const_cast<void *>(stat)); +} + +void +DataAccess::map(StatData *data) +{ + Database::regStat(this, data); +} + +StatData * +DataAccess::statData() +{ + StatData *ptr = find(); + assert(ptr); + return ptr; +} + +const StatData * +DataAccess::statData() const +{ + const StatData *ptr = find(); + assert(ptr); + return ptr; +} + +void +DataAccess::setInit() +{ + statData()->flags |= init; +} + +void +DataAccess::setPrint() +{ + Database::regPrint(this); +} + +StatData::StatData() + : flags(none), precision(-1), prereq(0) +{ + static int count = 0; + id = count++; +} + +StatData::~StatData() +{ +} + +bool +StatData::less(StatData *stat1, StatData *stat2) +{ + const string &name1 = stat1->name; + const string &name2 = stat2->name; + + vector<string> v1; + vector<string> v2; + + tokenize(v1, name1, '.'); + tokenize(v2, name2, '.'); + + int last = min(v1.size(), v2.size()) - 1; + for (int i = 0; i < last; ++i) + if (v1[i] != v2[i]) + return v1[i] < v2[i]; + + // Special compare for last element. + if (v1[last] == v2[last]) + return v1.size() < v2.size(); + else + return v1[last] < v2[last]; + + return false; +} + +bool +StatData::baseCheck() const +{ + if (!(flags & init)) { +#ifdef DEBUG + cprintf("this is stat number %d\n", id); +#endif + panic("Not all stats have been initialized"); + return false; + } + + if ((flags & print) && name.empty()) { + panic("all printable stats must be named"); + return false; + } + + return true; +} + + +void +FormulaBase::result(VResult &vec) const +{ + if (root) + vec = root->result(); +} + +Result +FormulaBase::total() const +{ + return root ? root->total() : 0.0; +} + +size_t +FormulaBase::size() const +{ + if (!root) + return 0; + else + return root->size(); +} + +void +FormulaBase::reset() +{ +} + +bool +FormulaBase::zero() const +{ + VResult vec; + result(vec); + for (int i = 0; i < vec.size(); ++i) + if (vec[i] != 0.0) + return false; + return true; +} + +void +FormulaBase::update(StatData *) +{ +} + +string +FormulaBase::str() const +{ + return root ? root->str() : ""; +} + +Formula::Formula() +{ + setInit(); +} + +Formula::Formula(Temp r) +{ + root = r; + assert(size()); +} + +const Formula & +Formula::operator=(Temp r) +{ + assert(!root && "Can't change formulas"); + root = r; + assert(size()); + return *this; +} + +const Formula & +Formula::operator+=(Temp r) +{ + if (root) + root = NodePtr(new BinaryNode<std::plus<Result> >(root, r)); + else + root = r; + assert(size()); + return *this; +} + +void +check() +{ + typedef Database::stat_list_t::iterator iter_t; + + iter_t i, end = Database::stats().end(); + for (i = Database::stats().begin(); i != end; ++i) { + StatData *data = *i; + assert(data); + if (!data->check() || !data->baseCheck()) + panic("stat check failed for %s\n", data->name); + } + + int j = 0; + for (i = Database::stats().begin(); i != end; ++i) { + StatData *data = *i; + if (!(data->flags & print)) + data->name = "__Stat" + to_string(j++); + } + + Database::stats().sort(StatData::less); + + if (i == end) + return; + + iter_t last = i; + ++i; + + for (i = Database::stats().begin(); i != end; ++i) { + if ((*i)->name == (*last)->name) + panic("same name used twice! name=%s\n", (*i)->name); + + last = i; + } +} + +CallbackQueue resetQueue; + +void +reset() +{ + Database::stat_list_t::iterator i = Database::stats().begin(); + Database::stat_list_t::iterator end = Database::stats().end(); + while (i != end) { + StatData *data = *i; + data->reset(); + ++i; + } + + resetQueue.process(); +} + +void +registerResetCallback(Callback *cb) +{ + resetQueue.add(cb); +} + +/* namespace Stats */ } diff --git a/src/base/statistics.hh b/src/base/statistics.hh new file mode 100644 index 000000000..59f219c07 --- /dev/null +++ b/src/base/statistics.hh @@ -0,0 +1,2887 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Erik Hallnor + */ + +/** @file + * Declaration of Statistics objects. + */ + +/** +* @todo +* +* Generalized N-dimensinal vector +* documentation +* key stats +* interval stats +* -- these both can use the same function that prints out a +* specific set of stats +* VectorStandardDeviation totals +* Document Namespaces +*/ +#ifndef __BASE_STATISTICS_HH__ +#define __BASE_STATISTICS_HH__ + +#include <algorithm> +#include <cassert> +#include <cmath> +#include <functional> +#include <iosfwd> +#include <string> +#include <vector> + +#include "base/cprintf.hh" +#include "base/intmath.hh" +#include "base/refcnt.hh" +#include "base/str.hh" +#include "base/stats/flags.hh" +#include "base/stats/visit.hh" +#include "base/stats/types.hh" +#include "sim/host.hh" + +class Callback; + +/** The current simulated cycle. */ +extern Tick curTick; + +/* A namespace for all of the Statistics */ +namespace Stats { + +/* Contains the statistic implementation details */ +////////////////////////////////////////////////////////////////////// +// +// Statistics Framework Base classes +// +////////////////////////////////////////////////////////////////////// +struct StatData +{ + /** The name of the stat. */ + std::string name; + /** The description of the stat. */ + std::string desc; + /** The formatting flags. */ + StatFlags flags; + /** The display precision. */ + int precision; + /** A pointer to a prerequisite Stat. */ + const StatData *prereq; + /** + * A unique stat ID for each stat in the simulator. + * Can be used externally for lookups as well as for debugging. + */ + int id; + + StatData(); + virtual ~StatData(); + + /** + * Reset the corresponding stat to the default state. + */ + virtual void reset() = 0; + + /** + * @return true if this stat has a value and satisfies its + * requirement as a prereq + */ + virtual bool zero() const = 0; + + /** + * Check that this stat has been set up properly and is ready for + * use + * @return true for success + */ + virtual bool check() const = 0; + bool baseCheck() const; + + /** + * Visitor entry for outputing statistics data + */ + virtual void visit(Visit &visitor) = 0; + + /** + * Checks if the first stat's name is alphabetically less than the second. + * This function breaks names up at periods and considers each subname + * separately. + * @param stat1 The first stat. + * @param stat2 The second stat. + * @return stat1's name is alphabetically before stat2's + */ + static bool less(StatData *stat1, StatData *stat2); +}; + +class ScalarData : public StatData +{ + public: + virtual Counter value() const = 0; + virtual Result result() const = 0; + virtual Result total() const = 0; + virtual void visit(Visit &visitor) { visitor.visit(*this); } +}; + +template <class Stat> +class ScalarStatData : public ScalarData +{ + protected: + Stat &s; + + public: + ScalarStatData(Stat &stat) : s(stat) {} + + virtual bool check() const { return s.check(); } + virtual Counter value() const { return s.value(); } + virtual Result result() const { return s.result(); } + virtual Result total() const { return s.total(); } + virtual void reset() { s.reset(); } + virtual bool zero() const { return s.zero(); } +}; + +struct VectorData : public StatData +{ + /** Names and descriptions of subfields. */ + mutable std::vector<std::string> subnames; + mutable std::vector<std::string> subdescs; + + virtual size_t size() const = 0; + virtual const VCounter &value() const = 0; + virtual const VResult &result() const = 0; + virtual Result total() const = 0; + void update() + { + if (!subnames.empty()) { + int s = size(); + if (subnames.size() < s) + subnames.resize(s); + + if (subdescs.size() < s) + subdescs.resize(s); + } + } +}; + +template <class Stat> +class VectorStatData : public VectorData +{ + protected: + Stat &s; + mutable VCounter cvec; + mutable VResult rvec; + + public: + VectorStatData(Stat &stat) : s(stat) {} + + virtual bool check() const { return s.check(); } + virtual bool zero() const { return s.zero(); } + virtual void reset() { s.reset(); } + + virtual size_t size() const { return s.size(); } + virtual VCounter &value() const + { + s.value(cvec); + return cvec; + } + virtual const VResult &result() const + { + s.result(rvec); + return rvec; + } + virtual Result total() const { return s.total(); } + virtual void visit(Visit &visitor) + { + update(); + s.update(this); + visitor.visit(*this); + } +}; + +struct DistDataData +{ + Counter min_val; + Counter max_val; + Counter underflow; + Counter overflow; + VCounter cvec; + Counter sum; + Counter squares; + Counter samples; + + Counter min; + Counter max; + Counter bucket_size; + int size; + bool fancy; +}; + +struct DistData : public StatData +{ + /** Local storage for the entry values, used for printing. */ + DistDataData data; +}; + +template <class Stat> +class DistStatData : public DistData +{ + protected: + Stat &s; + + public: + DistStatData(Stat &stat) : s(stat) {} + + virtual bool check() const { return s.check(); } + virtual void reset() { s.reset(); } + virtual bool zero() const { return s.zero(); } + virtual void visit(Visit &visitor) + { + s.update(this); + visitor.visit(*this); + } +}; + +struct VectorDistData : public StatData +{ + std::vector<DistDataData> data; + + /** Names and descriptions of subfields. */ + mutable std::vector<std::string> subnames; + mutable std::vector<std::string> subdescs; + + /** Local storage for the entry values, used for printing. */ + mutable VResult rvec; + + virtual size_t size() const = 0; + void update() + { + int s = size(); + if (subnames.size() < s) + subnames.resize(s); + + if (subdescs.size() < s) + subdescs.resize(s); + } +}; + +template <class Stat> +class VectorDistStatData : public VectorDistData +{ + protected: + Stat &s; + + public: + VectorDistStatData(Stat &stat) : s(stat) {} + + virtual bool check() const { return s.check(); } + virtual void reset() { s.reset(); } + virtual size_t size() const { return s.size(); } + virtual bool zero() const { return s.zero(); } + virtual void visit(Visit &visitor) + { + update(); + s.update(this); + visitor.visit(*this); + } +}; + +struct Vector2dData : public StatData +{ + /** Names and descriptions of subfields. */ + std::vector<std::string> subnames; + std::vector<std::string> subdescs; + std::vector<std::string> y_subnames; + + /** Local storage for the entry values, used for printing. */ + mutable VCounter cvec; + mutable int x; + mutable int y; + + void update() + { + if (subnames.size() < x) + subnames.resize(x); + } +}; + +template <class Stat> +class Vector2dStatData : public Vector2dData +{ + protected: + Stat &s; + + public: + Vector2dStatData(Stat &stat) : s(stat) {} + + virtual bool check() const { return s.check(); } + virtual void reset() { s.reset(); } + virtual bool zero() const { return s.zero(); } + virtual void visit(Visit &visitor) + { + update(); + s.update(this); + visitor.visit(*this); + } +}; + +class DataAccess +{ + protected: + StatData *find() const; + void map(StatData *data); + + StatData *statData(); + const StatData *statData() const; + + void setInit(); + void setPrint(); +}; + +template <class Parent, class Child, template <class> class Data> +class Wrap : public Child +{ + protected: + Parent &self() { return *reinterpret_cast<Parent *>(this); } + + protected: + Data<Child> *statData() + { + StatData *__data = DataAccess::statData(); + Data<Child> *ptr = dynamic_cast<Data<Child> *>(__data); + assert(ptr); + return ptr; + } + + public: + const Data<Child> *statData() const + { + const StatData *__data = DataAccess::statData(); + const Data<Child> *ptr = dynamic_cast<const Data<Child> *>(__data); + assert(ptr); + return ptr; + } + + protected: + /** + * Copy constructor, copies are not allowed. + */ + Wrap(const Wrap &stat); + /** + * Can't copy stats. + */ + void operator=(const Wrap &); + + public: + Wrap() + { + map(new Data<Child>(*this)); + } + + /** + * Set the name and marks this stat to print at the end of simulation. + * @param name The new name. + * @return A reference to this stat. + */ + Parent &name(const std::string &_name) + { + Data<Child> *data = this->statData(); + data->name = _name; + this->setPrint(); + return this->self(); + } + + /** + * Set the description and marks this stat to print at the end of + * simulation. + * @param desc The new description. + * @return A reference to this stat. + */ + Parent &desc(const std::string &_desc) + { + this->statData()->desc = _desc; + return this->self(); + } + + /** + * Set the precision and marks this stat to print at the end of simulation. + * @param p The new precision + * @return A reference to this stat. + */ + Parent &precision(int _precision) + { + this->statData()->precision = _precision; + return this->self(); + } + + /** + * Set the flags and marks this stat to print at the end of simulation. + * @param f The new flags. + * @return A reference to this stat. + */ + Parent &flags(StatFlags _flags) + { + this->statData()->flags |= _flags; + return this->self(); + } + + /** + * Set the prerequisite stat and marks this stat to print at the end of + * simulation. + * @param prereq The prerequisite stat. + * @return A reference to this stat. + */ + template <class Stat> + Parent &prereq(const Stat &prereq) + { + this->statData()->prereq = prereq.statData(); + return this->self(); + } +}; + +template <class Parent, class Child, template <class Child> class Data> +class WrapVec : public Wrap<Parent, Child, Data> +{ + public: + // The following functions are specific to vectors. If you use them + // in a non vector context, you will get a nice compiler error! + + /** + * Set the subfield name for the given index, and marks this stat to print + * at the end of simulation. + * @param index The subfield index. + * @param name The new name of the subfield. + * @return A reference to this stat. + */ + Parent &subname(int index, const std::string &name) + { + std::vector<std::string> &subn = this->statData()->subnames; + if (subn.size() <= index) + subn.resize(index + 1); + subn[index] = name; + return this->self(); + } + + /** + * Set the subfield description for the given index and marks this stat to + * print at the end of simulation. + * @param index The subfield index. + * @param desc The new description of the subfield + * @return A reference to this stat. + */ + Parent &subdesc(int index, const std::string &desc) + { + std::vector<std::string> &subd = this->statData()->subdescs; + if (subd.size() <= index) + subd.resize(index + 1); + subd[index] = desc; + + return this->self(); + } + +}; + +template <class Parent, class Child, template <class Child> class Data> +class WrapVec2d : public WrapVec<Parent, Child, Data> +{ + public: + /** + * @warning This makes the assumption that if you're gonna subnames a 2d + * vector, you're subnaming across all y + */ + Parent &ysubnames(const char **names) + { + Data<Child> *data = this->statData(); + data->y_subnames.resize(this->y); + for (int i = 0; i < this->y; ++i) + data->y_subnames[i] = names[i]; + return this->self(); + } + Parent &ysubname(int index, const std::string subname) + { + Data<Child> *data = this->statData(); + assert(index < this->y); + data->y_subnames.resize(this->y); + data->y_subnames[index] = subname.c_str(); + return this->self(); + } +}; + +////////////////////////////////////////////////////////////////////// +// +// Simple Statistics +// +////////////////////////////////////////////////////////////////////// + +/** + * Templatized storage and interface for a simple scalar stat. + */ +struct StatStor +{ + public: + /** The paramaters for this storage type, none for a scalar. */ + struct Params { }; + + private: + /** The statistic value. */ + Counter data; + + public: + /** + * Builds this storage element and calls the base constructor of the + * datatype. + */ + StatStor(const Params &) : data(Counter()) {} + + /** + * The the stat to the given value. + * @param val The new value. + * @param p The paramters of this storage type. + */ + void set(Counter val, const Params &p) { data = val; } + /** + * Increment the stat by the given value. + * @param val The new value. + * @param p The paramters of this storage type. + */ + void inc(Counter val, const Params &p) { data += val; } + /** + * Decrement the stat by the given value. + * @param val The new value. + * @param p The paramters of this storage type. + */ + void dec(Counter val, const Params &p) { data -= val; } + /** + * Return the value of this stat as its base type. + * @param p The params of this storage type. + * @return The value of this stat. + */ + Counter value(const Params &p) const { return data; } + /** + * Return the value of this stat as a result type. + * @param p The parameters of this storage type. + * @return The value of this stat. + */ + Result result(const Params &p) const { return (Result)data; } + /** + * Reset stat value to default + */ + void reset() { data = Counter(); } + + /** + * @return true if zero value + */ + bool zero() const { return data == Counter(); } +}; + +/** + * Templatized storage and interface to a per-cycle average stat. This keeps + * a current count and updates a total (count * cycles) when this count + * changes. This allows the quick calculation of a per cycle count of the item + * being watched. This is good for keeping track of residencies in structures + * among other things. + */ +struct AvgStor +{ + public: + /** The paramaters for this storage type */ + struct Params { }; + + private: + /** The current count. */ + Counter current; + /** The total count for all cycles. */ + mutable Result total; + /** The cycle that current last changed. */ + mutable Tick last; + + public: + /** + * Build and initializes this stat storage. + */ + AvgStor(Params &p) : current(0), total(0), last(0) { } + + /** + * Set the current count to the one provided, update the total and last + * set values. + * @param val The new count. + * @param p The parameters for this storage. + */ + void set(Counter val, Params &p) { + total += current * (curTick - last); + last = curTick; + current = val; + } + + /** + * Increment the current count by the provided value, calls set. + * @param val The amount to increment. + * @param p The parameters for this storage. + */ + void inc(Counter val, Params &p) { set(current + val, p); } + + /** + * Deccrement the current count by the provided value, calls set. + * @param val The amount to decrement. + * @param p The parameters for this storage. + */ + void dec(Counter val, Params &p) { set(current - val, p); } + + /** + * Return the current count. + * @param p The parameters for this storage. + * @return The current count. + */ + Counter value(const Params &p) const { return current; } + + /** + * Return the current average. + * @param p The parameters for this storage. + * @return The current average. + */ + Result result(const Params &p) const + { + total += current * (curTick - last); + last = curTick; + return (Result)(total + current) / (Result)(curTick + 1); + } + + /** + * Reset stat value to default + */ + void reset() + { + total = 0; + last = curTick; + } + + /** + * @return true if zero value + */ + bool zero() const { return total == 0.0; } +}; + +/** + * Implementation of a scalar stat. The type of stat is determined by the + * Storage template. + */ +template <class Stor> +class ScalarBase : public DataAccess +{ + public: + typedef Stor Storage; + + /** Define the params of the storage class. */ + typedef typename Storage::Params Params; + + protected: + /** The storage of this stat. */ + char storage[sizeof(Storage)]; + + /** The parameters for this stat. */ + Params params; + + protected: + /** + * Retrieve the storage. + * @param index The vector index to access. + * @return The storage object at the given index. + */ + Storage * + data() + { + return reinterpret_cast<Storage *>(storage); + } + + /** + * Retrieve a const pointer to the storage. + * for the given index. + * @param index The vector index to access. + * @return A const pointer to the storage object at the given index. + */ + const Storage * + data() const + { + return reinterpret_cast<const Storage *>(storage); + } + + void + doInit() + { + new (storage) Storage(params); + setInit(); + } + + public: + /** + * Return the current value of this stat as its base type. + * @return The current value. + */ + Counter value() const { return data()->value(params); } + + public: + /** + * Create and initialize this stat, register it with the database. + */ + ScalarBase() + { } + + public: + // Common operators for stats + /** + * Increment the stat by 1. This calls the associated storage object inc + * function. + */ + void operator++() { data()->inc(1, params); } + /** + * Decrement the stat by 1. This calls the associated storage object dec + * function. + */ + void operator--() { data()->dec(1, params); } + + /** Increment the stat by 1. */ + void operator++(int) { ++*this; } + /** Decrement the stat by 1. */ + void operator--(int) { --*this; } + + /** + * Set the data value to the given value. This calls the associated storage + * object set function. + * @param v The new value. + */ + template <typename U> + void operator=(const U &v) { data()->set(v, params); } + + /** + * Increment the stat by the given value. This calls the associated + * storage object inc function. + * @param v The value to add. + */ + template <typename U> + void operator+=(const U &v) { data()->inc(v, params); } + + /** + * Decrement the stat by the given value. This calls the associated + * storage object dec function. + * @param v The value to substract. + */ + template <typename U> + void operator-=(const U &v) { data()->dec(v, params); } + + /** + * Return the number of elements, always 1 for a scalar. + * @return 1. + */ + size_t size() const { return 1; } + + bool check() const { return true; } + + /** + * Reset stat value to default + */ + void reset() { data()->reset(); } + + Counter value() { return data()->value(params); } + + Result result() { return data()->result(params); } + + Result total() { return result(); } + + bool zero() { return result() == 0.0; } + +}; + +class ProxyData : public ScalarData +{ + public: + virtual void visit(Visit &visitor) { visitor.visit(*this); } + virtual std::string str() const { return to_string(value()); } + virtual size_t size() const { return 1; } + virtual bool zero() const { return value() == 0; } + virtual bool check() const { return true; } + virtual void reset() { } +}; + +template <class T> +class ValueProxy : public ProxyData +{ + private: + T *scalar; + + public: + ValueProxy(T &val) : scalar(&val) {} + virtual Counter value() const { return *scalar; } + virtual Result result() const { return *scalar; } + virtual Result total() const { return *scalar; } +}; + +template <class T> +class FunctorProxy : public ProxyData +{ + private: + T *functor; + + public: + FunctorProxy(T &func) : functor(&func) {} + virtual Counter value() const { return (*functor)(); } + virtual Result result() const { return (*functor)(); } + virtual Result total() const { return (*functor)(); } +}; + +class ValueBase : public DataAccess +{ + private: + ProxyData *proxy; + + public: + ValueBase() : proxy(NULL) { } + ~ValueBase() { if (proxy) delete proxy; } + + template <class T> + void scalar(T &value) + { + proxy = new ValueProxy<T>(value); + setInit(); + } + + template <class T> + void functor(T &func) + { + proxy = new FunctorProxy<T>(func); + setInit(); + } + + Counter value() { return proxy->value(); } + Result result() const { return proxy->result(); } + Result total() const { return proxy->total(); }; + size_t size() const { return proxy->size(); } + + std::string str() const { return proxy->str(); } + bool zero() const { return proxy->zero(); } + bool check() const { return proxy != NULL; } + void reset() { } +}; + +////////////////////////////////////////////////////////////////////// +// +// Vector Statistics +// +////////////////////////////////////////////////////////////////////// + +/** + * A proxy class to access the stat at a given index in a VectorBase stat. + * Behaves like a ScalarBase. + */ +template <class Stat> +class ScalarProxy +{ + private: + /** Pointer to the parent Vector. */ + Stat *stat; + + /** The index to access in the parent VectorBase. */ + int index; + + public: + /** + * Return the current value of this stat as its base type. + * @return The current value. + */ + Counter value() const { return stat->data(index)->value(stat->params); } + + /** + * Return the current value of this statas a result type. + * @return The current value. + */ + Result result() const { return stat->data(index)->result(stat->params); } + + public: + /** + * Create and initialize this proxy, do not register it with the database. + * @param p The params to use. + * @param i The index to access. + */ + ScalarProxy(Stat *s, int i) + : stat(s), index(i) + { + assert(stat); + } + + /** + * Create a copy of the provided ScalarProxy. + * @param sp The proxy to copy. + */ + ScalarProxy(const ScalarProxy &sp) + : stat(sp.stat), index(sp.index) + {} + + /** + * Set this proxy equal to the provided one. + * @param sp The proxy to copy. + * @return A reference to this proxy. + */ + const ScalarProxy &operator=(const ScalarProxy &sp) { + stat = sp.stat; + index = sp.index; + return *this; + } + + public: + // Common operators for stats + /** + * Increment the stat by 1. This calls the associated storage object inc + * function. + */ + void operator++() { stat->data(index)->inc(1, stat->params); } + /** + * Decrement the stat by 1. This calls the associated storage object dec + * function. + */ + void operator--() { stat->data(index)->dec(1, stat->params); } + + /** Increment the stat by 1. */ + void operator++(int) { ++*this; } + /** Decrement the stat by 1. */ + void operator--(int) { --*this; } + + /** + * Set the data value to the given value. This calls the associated storage + * object set function. + * @param v The new value. + */ + template <typename U> + void operator=(const U &v) { stat->data(index)->set(v, stat->params); } + + /** + * Increment the stat by the given value. This calls the associated + * storage object inc function. + * @param v The value to add. + */ + template <typename U> + void operator+=(const U &v) { stat->data(index)->inc(v, stat->params); } + + /** + * Decrement the stat by the given value. This calls the associated + * storage object dec function. + * @param v The value to substract. + */ + template <typename U> + void operator-=(const U &v) { stat->data(index)->dec(v, stat->params); } + + /** + * Return the number of elements, always 1 for a scalar. + * @return 1. + */ + size_t size() const { return 1; } + + /** + * This stat has no state. Nothing to reset + */ + void reset() { } + + public: + std::string + str() const + { + return csprintf("%s[%d]", stat->str(), index); + + } +}; + +/** + * Implementation of a vector of stats. The type of stat is determined by the + * Storage class. @sa ScalarBase + */ +template <class Stor> +class VectorBase : public DataAccess +{ + public: + typedef Stor Storage; + + /** Define the params of the storage class. */ + typedef typename Storage::Params Params; + + /** Proxy type */ + typedef ScalarProxy<VectorBase<Storage> > Proxy; + + friend class ScalarProxy<VectorBase<Storage> >; + + protected: + /** The storage of this stat. */ + Storage *storage; + size_t _size; + + /** The parameters for this stat. */ + Params params; + + protected: + /** + * Retrieve the storage. + * @param index The vector index to access. + * @return The storage object at the given index. + */ + Storage *data(int index) { return &storage[index]; } + + /** + * Retrieve a const pointer to the storage. + * @param index The vector index to access. + * @return A const pointer to the storage object at the given index. + */ + const Storage *data(int index) const { return &storage[index]; } + + void + doInit(int s) + { + assert(s > 0 && "size must be positive!"); + assert(!storage && "already initialized"); + _size = s; + + char *ptr = new char[_size * sizeof(Storage)]; + storage = reinterpret_cast<Storage *>(ptr); + + for (int i = 0; i < _size; ++i) + new (&storage[i]) Storage(params); + + setInit(); + } + + public: + void value(VCounter &vec) const + { + vec.resize(size()); + for (int i = 0; i < size(); ++i) + vec[i] = data(i)->value(params); + } + + /** + * Copy the values to a local vector and return a reference to it. + * @return A reference to a vector of the stat values. + */ + void result(VResult &vec) const + { + vec.resize(size()); + for (int i = 0; i < size(); ++i) + vec[i] = data(i)->result(params); + } + + /** + * Return a total of all entries in this vector. + * @return The total of all vector entries. + */ + Result total() const { + Result total = 0.0; + for (int i = 0; i < size(); ++i) + total += data(i)->result(params); + return total; + } + + /** + * @return the number of elements in this vector. + */ + size_t size() const { return _size; } + + bool + zero() const + { + for (int i = 0; i < size(); ++i) + if (data(i)->zero()) + return false; + return true; + } + + bool + check() const + { + return storage != NULL; + } + + void + reset() + { + for (int i = 0; i < size(); ++i) + data(i)->reset(); + } + + public: + VectorBase() + : storage(NULL) + {} + + ~VectorBase() + { + if (!storage) + return; + + for (int i = 0; i < _size; ++i) + data(i)->~Storage(); + delete [] reinterpret_cast<char *>(storage); + } + + /** + * Return a reference (ScalarProxy) to the stat at the given index. + * @param index The vector index to access. + * @return A reference of the stat. + */ + Proxy + operator[](int index) + { + assert (index >= 0 && index < size()); + return Proxy(this, index); + } + + void update(StatData *data) {} +}; + +template <class Stat> +class VectorProxy +{ + private: + Stat *stat; + int offset; + int len; + + private: + mutable VResult vec; + + typename Stat::Storage * + data(int index) + { + assert(index < len); + return stat->data(offset + index); + } + + const typename Stat::Storage * + data(int index) const + { + assert(index < len); + return const_cast<Stat *>(stat)->data(offset + index); + } + + public: + const VResult & + result() const + { + vec.resize(size()); + + for (int i = 0; i < size(); ++i) + vec[i] = data(i)->result(stat->params); + + return vec; + } + + Result + total() const + { + Result total = 0; + for (int i = 0; i < size(); ++i) + total += data(i)->result(stat->params); + return total; + } + + public: + VectorProxy(Stat *s, int o, int l) + : stat(s), offset(o), len(l) + { + } + + VectorProxy(const VectorProxy &sp) + : stat(sp.stat), offset(sp.offset), len(sp.len) + { + } + + const VectorProxy & + operator=(const VectorProxy &sp) + { + stat = sp.stat; + offset = sp.offset; + len = sp.len; + return *this; + } + + ScalarProxy<Stat> operator[](int index) + { + assert (index >= 0 && index < size()); + return ScalarProxy<Stat>(stat, offset + index); + } + + size_t size() const { return len; } + + /** + * This stat has no state. Nothing to reset. + */ + void reset() { } +}; + +template <class Stor> +class Vector2dBase : public DataAccess +{ + public: + typedef Stor Storage; + typedef typename Storage::Params Params; + typedef VectorProxy<Vector2dBase<Storage> > Proxy; + friend class ScalarProxy<Vector2dBase<Storage> >; + friend class VectorProxy<Vector2dBase<Storage> >; + + protected: + size_t x; + size_t y; + size_t _size; + Storage *storage; + Params params; + + protected: + Storage *data(int index) { return &storage[index]; } + const Storage *data(int index) const { return &storage[index]; } + + void + doInit(int _x, int _y) + { + assert(_x > 0 && _y > 0 && "sizes must be positive!"); + assert(!storage && "already initialized"); + + Vector2dData *statdata = dynamic_cast<Vector2dData *>(find()); + + x = _x; + y = _y; + statdata->x = _x; + statdata->y = _y; + _size = x * y; + + char *ptr = new char[_size * sizeof(Storage)]; + storage = reinterpret_cast<Storage *>(ptr); + + for (int i = 0; i < _size; ++i) + new (&storage[i]) Storage(params); + + setInit(); + } + + public: + Vector2dBase() + : storage(NULL) + {} + + ~Vector2dBase() + { + if (!storage) + return; + + for (int i = 0; i < _size; ++i) + data(i)->~Storage(); + delete [] reinterpret_cast<char *>(storage); + } + + void + update(Vector2dData *newdata) + { + int size = this->size(); + newdata->cvec.resize(size); + for (int i = 0; i < size; ++i) + newdata->cvec[i] = data(i)->value(params); + } + + std::string ysubname(int i) const { return (*this->y_subnames)[i]; } + + Proxy + operator[](int index) + { + int offset = index * y; + assert (index >= 0 && offset + index < size()); + return Proxy(this, offset, y); + } + + + size_t + size() const + { + return _size; + } + + bool + zero() const + { + return data(0)->zero(); +#if 0 + for (int i = 0; i < size(); ++i) + if (!data(i)->zero()) + return false; + return true; +#endif + } + + /** + * Reset stat value to default + */ + void + reset() + { + for (int i = 0; i < size(); ++i) + data(i)->reset(); + } + + bool + check() + { + return storage != NULL; + } +}; + +////////////////////////////////////////////////////////////////////// +// +// Non formula statistics +// +////////////////////////////////////////////////////////////////////// + +/** + * Templatized storage and interface for a distrbution stat. + */ +struct DistStor +{ + public: + /** The parameters for a distribution stat. */ + struct Params + { + /** The minimum value to track. */ + Counter min; + /** The maximum value to track. */ + Counter max; + /** The number of entries in each bucket. */ + Counter bucket_size; + /** The number of buckets. Equal to (max-min)/bucket_size. */ + int size; + }; + enum { fancy = false }; + + private: + /** The smallest value sampled. */ + Counter min_val; + /** The largest value sampled. */ + Counter max_val; + /** The number of values sampled less than min. */ + Counter underflow; + /** The number of values sampled more than max. */ + Counter overflow; + /** The current sum. */ + Counter sum; + /** The sum of squares. */ + Counter squares; + /** The number of samples. */ + Counter samples; + /** Counter for each bucket. */ + VCounter cvec; + + public: + DistStor(const Params ¶ms) + : cvec(params.size) + { + reset(); + } + + /** + * Add a value to the distribution for the given number of times. + * @param val The value to add. + * @param number The number of times to add the value. + * @param params The paramters of the distribution. + */ + void sample(Counter val, int number, const Params ¶ms) + { + if (val < params.min) + underflow += number; + else if (val > params.max) + overflow += number; + else { + int index = (int)floor((val - params.min) / params.bucket_size); + assert(index < size(params)); + cvec[index] += number; + } + + if (val < min_val) + min_val = val; + + if (val > max_val) + max_val = val; + + Counter sample = val * number; + sum += sample; + squares += sample * sample; + samples += number; + } + + /** + * Return the number of buckets in this distribution. + * @return the number of buckets. + * @todo Is it faster to return the size from the parameters? + */ + size_t size(const Params &) const { return cvec.size(); } + + /** + * Returns true if any calls to sample have been made. + * @param params The paramters of the distribution. + * @return True if any values have been sampled. + */ + bool zero(const Params ¶ms) const + { + return samples == Counter(); + } + + void update(DistDataData *data, const Params ¶ms) + { + data->min = params.min; + data->max = params.max; + data->bucket_size = params.bucket_size; + data->size = params.size; + + data->min_val = (min_val == INT_MAX) ? 0 : min_val; + data->max_val = (max_val == INT_MIN) ? 0 : max_val; + data->underflow = underflow; + data->overflow = overflow; + data->cvec.resize(params.size); + for (int i = 0; i < params.size; ++i) + data->cvec[i] = cvec[i]; + + data->sum = sum; + data->squares = squares; + data->samples = samples; + } + + /** + * Reset stat value to default + */ + void reset() + { + min_val = INT_MAX; + max_val = INT_MIN; + underflow = 0; + overflow = 0; + + int size = cvec.size(); + for (int i = 0; i < size; ++i) + cvec[i] = Counter(); + + sum = Counter(); + squares = Counter(); + samples = Counter(); + } +}; + +/** + * Templatized storage and interface for a distribution that calculates mean + * and variance. + */ +struct FancyStor +{ + public: + /** + * No paramters for this storage. + */ + struct Params {}; + enum { fancy = true }; + + private: + /** The current sum. */ + Counter sum; + /** The sum of squares. */ + Counter squares; + /** The number of samples. */ + Counter samples; + + public: + /** + * Create and initialize this storage. + */ + FancyStor(const Params &) + : sum(Counter()), squares(Counter()), samples(Counter()) + { } + + /** + * Add a value the given number of times to this running average. + * Update the running sum and sum of squares, increment the number of + * values seen by the given number. + * @param val The value to add. + * @param number The number of times to add the value. + * @param p The parameters of this stat. + */ + void sample(Counter val, int number, const Params &p) + { + Counter value = val * number; + sum += value; + squares += value * value; + samples += number; + } + + void update(DistDataData *data, const Params ¶ms) + { + data->sum = sum; + data->squares = squares; + data->samples = samples; + } + + /** + * Return the number of entries in this stat, 1 + * @return 1. + */ + size_t size(const Params &) const { return 1; } + + /** + * Return true if no samples have been added. + * @return True if no samples have been added. + */ + bool zero(const Params &) const { return samples == Counter(); } + + /** + * Reset stat value to default + */ + void reset() + { + sum = Counter(); + squares = Counter(); + samples = Counter(); + } +}; + +/** + * Templatized storage for distribution that calculates per cycle mean and + * variance. + */ +struct AvgFancy +{ + public: + /** No parameters for this storage. */ + struct Params {}; + enum { fancy = true }; + + private: + /** Current total. */ + Counter sum; + /** Current sum of squares. */ + Counter squares; + + public: + /** + * Create and initialize this storage. + */ + AvgFancy(const Params &) : sum(Counter()), squares(Counter()) {} + + /** + * Add a value to the distribution for the given number of times. + * Update the running sum and sum of squares. + * @param val The value to add. + * @param number The number of times to add the value. + * @param p The paramters of the distribution. + */ + void sample(Counter val, int number, const Params &p) + { + Counter value = val * number; + sum += value; + squares += value * value; + } + + void update(DistDataData *data, const Params ¶ms) + { + data->sum = sum; + data->squares = squares; + data->samples = curTick; + } + + /** + * Return the number of entries, in this case 1. + * @return 1. + */ + size_t size(const Params ¶ms) const { return 1; } + /** + * Return true if no samples have been added. + * @return True if the sum is zero. + */ + bool zero(const Params ¶ms) const { return sum == Counter(); } + /** + * Reset stat value to default + */ + void reset() + { + sum = Counter(); + squares = Counter(); + } +}; + +/** + * Implementation of a distribution stat. The type of distribution is + * determined by the Storage template. @sa ScalarBase + */ +template <class Stor> +class DistBase : public DataAccess +{ + public: + typedef Stor Storage; + /** Define the params of the storage class. */ + typedef typename Storage::Params Params; + + protected: + /** The storage for this stat. */ + char storage[sizeof(Storage)]; + + /** The parameters for this stat. */ + Params params; + + protected: + /** + * Retrieve the storage. + * @return The storage object for this stat. + */ + Storage *data() + { + return reinterpret_cast<Storage *>(storage); + } + + /** + * Retrieve a const pointer to the storage. + * @return A const pointer to the storage object for this stat. + */ + const Storage * + data() const + { + return reinterpret_cast<const Storage *>(storage); + } + + void + doInit() + { + new (storage) Storage(params); + setInit(); + } + + public: + DistBase() { } + + /** + * Add a value to the distribtion n times. Calls sample on the storage + * class. + * @param v The value to add. + * @param n The number of times to add it, defaults to 1. + */ + template <typename U> + void sample(const U &v, int n = 1) { data()->sample(v, n, params); } + + /** + * Return the number of entries in this stat. + * @return The number of entries. + */ + size_t size() const { return data()->size(params); } + /** + * Return true if no samples have been added. + * @return True if there haven't been any samples. + */ + bool zero() const { return data()->zero(params); } + + void update(DistData *base) + { + base->data.fancy = Storage::fancy; + data()->update(&(base->data), params); + } + + /** + * Reset stat value to default + */ + void + reset() + { + data()->reset(); + } + + bool + check() + { + return true; + } +}; + +template <class Stat> +class DistProxy; + +template <class Stor> +class VectorDistBase : public DataAccess +{ + public: + typedef Stor Storage; + typedef typename Storage::Params Params; + typedef DistProxy<VectorDistBase<Storage> > Proxy; + friend class DistProxy<VectorDistBase<Storage> >; + + protected: + Storage *storage; + size_t _size; + Params params; + + protected: + Storage * + data(int index) + { + return &storage[index]; + } + + const Storage * + data(int index) const + { + return &storage[index]; + } + + void + doInit(int s) + { + assert(s > 0 && "size must be positive!"); + assert(!storage && "already initialized"); + _size = s; + + char *ptr = new char[_size * sizeof(Storage)]; + storage = reinterpret_cast<Storage *>(ptr); + + for (int i = 0; i < _size; ++i) + new (&storage[i]) Storage(params); + + setInit(); + } + + public: + VectorDistBase() + : storage(NULL) + {} + + ~VectorDistBase() + { + if (!storage) + return ; + + for (int i = 0; i < _size; ++i) + data(i)->~Storage(); + delete [] reinterpret_cast<char *>(storage); + } + + Proxy operator[](int index); + + size_t + size() const + { + return _size; + } + + bool + zero() const + { + return false; +#if 0 + for (int i = 0; i < size(); ++i) + if (!data(i)->zero(params)) + return false; + return true; +#endif + } + + /** + * Reset stat value to default + */ + void + reset() + { + for (int i = 0; i < size(); ++i) + data(i)->reset(); + } + + bool + check() + { + return storage != NULL; + } + + void + update(VectorDistData *base) + { + int size = this->size(); + base->data.resize(size); + for (int i = 0; i < size; ++i) { + base->data[i].fancy = Storage::fancy; + data(i)->update(&(base->data[i]), params); + } + } +}; + +template <class Stat> +class DistProxy +{ + private: + Stat *stat; + int index; + + protected: + typename Stat::Storage *data() { return stat->data(index); } + const typename Stat::Storage *data() const { return stat->data(index); } + + public: + DistProxy(Stat *s, int i) + : stat(s), index(i) + {} + + DistProxy(const DistProxy &sp) + : stat(sp.stat), index(sp.index) + {} + + const DistProxy &operator=(const DistProxy &sp) + { + stat = sp.stat; + index = sp.index; + return *this; + } + + public: + template <typename U> + void + sample(const U &v, int n = 1) + { + data()->sample(v, n, stat->params); + } + + size_t + size() const + { + return 1; + } + + bool + zero() const + { + return data()->zero(stat->params); + } + + /** + * Proxy has no state. Nothing to reset. + */ + void reset() { } +}; + +template <class Storage> +inline typename VectorDistBase<Storage>::Proxy +VectorDistBase<Storage>::operator[](int index) +{ + assert (index >= 0 && index < size()); + return typename VectorDistBase<Storage>::Proxy(this, index); +} + +#if 0 +template <class Storage> +Result +VectorDistBase<Storage>::total(int index) const +{ + int total = 0; + for (int i = 0; i < x_size(); ++i) { + total += data(i)->result(stat->params); + } +} +#endif + +////////////////////////////////////////////////////////////////////// +// +// Formula Details +// +////////////////////////////////////////////////////////////////////// + +/** + * Base class for formula statistic node. These nodes are used to build a tree + * that represents the formula. + */ +class Node : public RefCounted +{ + public: + /** + * Return the number of nodes in the subtree starting at this node. + * @return the number of nodes in this subtree. + */ + virtual size_t size() const = 0; + /** + * Return the result vector of this subtree. + * @return The result vector of this subtree. + */ + virtual const VResult &result() const = 0; + /** + * Return the total of the result vector. + * @return The total of the result vector. + */ + virtual Result total() const = 0; + + /** + * + */ + virtual std::string str() const = 0; +}; + +/** Reference counting pointer to a function Node. */ +typedef RefCountingPtr<Node> NodePtr; + +class ScalarStatNode : public Node +{ + private: + const ScalarData *data; + mutable VResult vresult; + + public: + ScalarStatNode(const ScalarData *d) : data(d), vresult(1) {} + virtual const VResult &result() const + { + vresult[0] = data->result(); + return vresult; + } + virtual Result total() const { return data->result(); }; + + virtual size_t size() const { return 1; } + + /** + * + */ + virtual std::string str() const { return data->name; } +}; + +template <class Stat> +class ScalarProxyNode : public Node +{ + private: + const ScalarProxy<Stat> proxy; + mutable VResult vresult; + + public: + ScalarProxyNode(const ScalarProxy<Stat> &p) + : proxy(p), vresult(1) + { } + + virtual const VResult & + result() const + { + vresult[0] = proxy.result(); + return vresult; + } + + virtual Result + total() const + { + return proxy.result(); + } + + virtual size_t + size() const + { + return 1; + } + + /** + * + */ + virtual std::string + str() const + { + return proxy.str(); + } +}; + +class VectorStatNode : public Node +{ + private: + const VectorData *data; + + public: + VectorStatNode(const VectorData *d) : data(d) { } + virtual const VResult &result() const { return data->result(); } + virtual Result total() const { return data->total(); }; + + virtual size_t size() const { return data->size(); } + + virtual std::string str() const { return data->name; } +}; + +template <class T> +class ConstNode : public Node +{ + private: + VResult vresult; + + public: + ConstNode(T s) : vresult(1, (Result)s) {} + const VResult &result() const { return vresult; } + virtual Result total() const { return vresult[0]; }; + virtual size_t size() const { return 1; } + virtual std::string str() const { return to_string(vresult[0]); } +}; + +template <class Op> +struct OpString; + +template<> +struct OpString<std::plus<Result> > +{ + static std::string str() { return "+"; } +}; + +template<> +struct OpString<std::minus<Result> > +{ + static std::string str() { return "-"; } +}; + +template<> +struct OpString<std::multiplies<Result> > +{ + static std::string str() { return "*"; } +}; + +template<> +struct OpString<std::divides<Result> > +{ + static std::string str() { return "/"; } +}; + +template<> +struct OpString<std::modulus<Result> > +{ + static std::string str() { return "%"; } +}; + +template<> +struct OpString<std::negate<Result> > +{ + static std::string str() { return "-"; } +}; + +template <class Op> +class UnaryNode : public Node +{ + public: + NodePtr l; + mutable VResult vresult; + + public: + UnaryNode(NodePtr &p) : l(p) {} + + const VResult &result() const + { + const VResult &lvec = l->result(); + int size = lvec.size(); + + assert(size > 0); + + vresult.resize(size); + Op op; + for (int i = 0; i < size; ++i) + vresult[i] = op(lvec[i]); + + return vresult; + } + + Result total() const { + Op op; + return op(l->total()); + } + + virtual size_t size() const { return l->size(); } + + virtual std::string str() const + { + return OpString<Op>::str() + l->str(); + } +}; + +template <class Op> +class BinaryNode : public Node +{ + public: + NodePtr l; + NodePtr r; + mutable VResult vresult; + + public: + BinaryNode(NodePtr &a, NodePtr &b) : l(a), r(b) {} + + const VResult &result() const + { + Op op; + const VResult &lvec = l->result(); + const VResult &rvec = r->result(); + + assert(lvec.size() > 0 && rvec.size() > 0); + + if (lvec.size() == 1 && rvec.size() == 1) { + vresult.resize(1); + vresult[0] = op(lvec[0], rvec[0]); + } else if (lvec.size() == 1) { + int size = rvec.size(); + vresult.resize(size); + for (int i = 0; i < size; ++i) + vresult[i] = op(lvec[0], rvec[i]); + } else if (rvec.size() == 1) { + int size = lvec.size(); + vresult.resize(size); + for (int i = 0; i < size; ++i) + vresult[i] = op(lvec[i], rvec[0]); + } else if (rvec.size() == lvec.size()) { + int size = rvec.size(); + vresult.resize(size); + for (int i = 0; i < size; ++i) + vresult[i] = op(lvec[i], rvec[i]); + } + + return vresult; + } + + Result total() const { + Op op; + return op(l->total(), r->total()); + } + + virtual size_t size() const { + int ls = l->size(); + int rs = r->size(); + if (ls == 1) + return rs; + else if (rs == 1) + return ls; + else { + assert(ls == rs && "Node vector sizes are not equal"); + return ls; + } + } + + virtual std::string str() const + { + return csprintf("(%s %s %s)", l->str(), OpString<Op>::str(), r->str()); + } +}; + +template <class Op> +class SumNode : public Node +{ + public: + NodePtr l; + mutable VResult vresult; + + public: + SumNode(NodePtr &p) : l(p), vresult(1) {} + + const VResult &result() const + { + const VResult &lvec = l->result(); + int size = lvec.size(); + assert(size > 0); + + vresult[0] = 0.0; + + Op op; + for (int i = 0; i < size; ++i) + vresult[0] = op(vresult[0], lvec[i]); + + return vresult; + } + + Result total() const + { + const VResult &lvec = l->result(); + int size = lvec.size(); + assert(size > 0); + + Result vresult = 0.0; + + Op op; + for (int i = 0; i < size; ++i) + vresult = op(vresult, lvec[i]); + + return vresult; + } + + virtual size_t size() const { return 1; } + + virtual std::string str() const + { + return csprintf("total(%s)", l->str()); + } +}; + + +////////////////////////////////////////////////////////////////////// +// +// Visible Statistics Types +// +////////////////////////////////////////////////////////////////////// +/** + * @defgroup VisibleStats "Statistic Types" + * These are the statistics that are used in the simulator. + * @{ + */ + +/** + * This is a simple scalar statistic, like a counter. + * @sa Stat, ScalarBase, StatStor + */ +template<int N = 0> +class Scalar : public Wrap<Scalar<N>, ScalarBase<StatStor>, ScalarStatData> +{ + public: + /** The base implementation. */ + typedef ScalarBase<StatStor> Base; + + Scalar() + { + this->doInit(); + } + + /** + * Sets the stat equal to the given value. Calls the base implementation + * of operator= + * @param v The new value. + */ + template <typename U> + void operator=(const U &v) { Base::operator=(v); } +}; + +class Value : public Wrap<Value, ValueBase, ScalarStatData> +{ + public: + /** The base implementation. */ + typedef ValueBase Base; + + template <class T> + Value &scalar(T &value) + { + Base::scalar(value); + return *this; + } + + template <class T> + Value &functor(T &func) + { + Base::functor(func); + return *this; + } +}; + +/** + * A stat that calculates the per cycle average of a value. + * @sa Stat, ScalarBase, AvgStor + */ +template<int N = 0> +class Average : public Wrap<Average<N>, ScalarBase<AvgStor>, ScalarStatData> +{ + public: + /** The base implementation. */ + typedef ScalarBase<AvgStor> Base; + + Average() + { + this->doInit(); + } + + /** + * Sets the stat equal to the given value. Calls the base implementation + * of operator= + * @param v The new value. + */ + template <typename U> + void operator=(const U &v) { Base::operator=(v); } +}; + +/** + * A vector of scalar stats. + * @sa Stat, VectorBase, StatStor + */ +template<int N = 0> +class Vector : public WrapVec<Vector<N>, VectorBase<StatStor>, VectorStatData> +{ + public: + /** The base implementation. */ + typedef ScalarBase<StatStor> Base; + + /** + * Set this vector to have the given size. + * @param size The new size. + * @return A reference to this stat. + */ + Vector &init(size_t size) { + this->doInit(size); + return *this; + } +}; + +/** + * A vector of Average stats. + * @sa Stat, VectorBase, AvgStor + */ +template<int N = 0> +class AverageVector + : public WrapVec<AverageVector<N>, VectorBase<AvgStor>, VectorStatData> +{ + public: + /** + * Set this vector to have the given size. + * @param size The new size. + * @return A reference to this stat. + */ + AverageVector &init(size_t size) { + this->doInit(size); + return *this; + } +}; + +/** + * A 2-Dimensional vecto of scalar stats. + * @sa Stat, Vector2dBase, StatStor + */ +template<int N = 0> +class Vector2d + : public WrapVec2d<Vector2d<N>, Vector2dBase<StatStor>, Vector2dStatData> +{ + public: + Vector2d &init(size_t x, size_t y) { + this->doInit(x, y); + return *this; + } +}; + +/** + * A simple distribution stat. + * @sa Stat, DistBase, DistStor + */ +template<int N = 0> +class Distribution + : public Wrap<Distribution<N>, DistBase<DistStor>, DistStatData> +{ + public: + /** Base implementation. */ + typedef DistBase<DistStor> Base; + /** The Parameter type. */ + typedef DistStor::Params Params; + + public: + /** + * Set the parameters of this distribution. @sa DistStor::Params + * @param min The minimum value of the distribution. + * @param max The maximum value of the distribution. + * @param bkt The number of values in each bucket. + * @return A reference to this distribution. + */ + Distribution &init(Counter min, Counter max, Counter bkt) { + this->params.min = min; + this->params.max = max; + this->params.bucket_size = bkt; + this->params.size = (int)rint((max - min) / bkt + 1.0); + this->doInit(); + return *this; + } +}; + +/** + * Calculates the mean and variance of all the samples. + * @sa Stat, DistBase, FancyStor + */ +template<int N = 0> +class StandardDeviation + : public Wrap<StandardDeviation<N>, DistBase<FancyStor>, DistStatData> +{ + public: + /** The base implementation */ + typedef DistBase<DistStor> Base; + /** The parameter type. */ + typedef DistStor::Params Params; + + public: + /** + * Construct and initialize this distribution. + */ + StandardDeviation() { + this->doInit(); + } +}; + +/** + * Calculates the per cycle mean and variance of the samples. + * @sa Stat, DistBase, AvgFancy + */ +template<int N = 0> +class AverageDeviation + : public Wrap<AverageDeviation<N>, DistBase<AvgFancy>, DistStatData> +{ + public: + /** The base implementation */ + typedef DistBase<DistStor> Base; + /** The parameter type. */ + typedef DistStor::Params Params; + + public: + /** + * Construct and initialize this distribution. + */ + AverageDeviation() + { + this->doInit(); + } +}; + +/** + * A vector of distributions. + * @sa Stat, VectorDistBase, DistStor + */ +template<int N = 0> +class VectorDistribution + : public WrapVec<VectorDistribution<N>, + VectorDistBase<DistStor>, + VectorDistStatData> +{ + public: + /** The base implementation */ + typedef VectorDistBase<DistStor> Base; + /** The parameter type. */ + typedef DistStor::Params Params; + + public: + /** + * Initialize storage and parameters for this distribution. + * @param size The size of the vector (the number of distributions). + * @param min The minimum value of the distribution. + * @param max The maximum value of the distribution. + * @param bkt The number of values in each bucket. + * @return A reference to this distribution. + */ + VectorDistribution &init(int size, Counter min, Counter max, Counter bkt) { + this->params.min = min; + this->params.max = max; + this->params.bucket_size = bkt; + this->params.size = (int)rint((max - min) / bkt + 1.0); + this->doInit(size); + return *this; + } +}; + +/** + * This is a vector of StandardDeviation stats. + * @sa Stat, VectorDistBase, FancyStor + */ +template<int N = 0> +class VectorStandardDeviation + : public WrapVec<VectorStandardDeviation<N>, + VectorDistBase<FancyStor>, + VectorDistStatData> +{ + public: + /** The base implementation */ + typedef VectorDistBase<FancyStor> Base; + /** The parameter type. */ + typedef DistStor::Params Params; + + public: + /** + * Initialize storage for this distribution. + * @param size The size of the vector. + * @return A reference to this distribution. + */ + VectorStandardDeviation &init(int size) { + this->doInit(size); + return *this; + } +}; + +/** + * This is a vector of AverageDeviation stats. + * @sa Stat, VectorDistBase, AvgFancy + */ +template<int N = 0> +class VectorAverageDeviation + : public WrapVec<VectorAverageDeviation<N>, + VectorDistBase<AvgFancy>, + VectorDistStatData> +{ + public: + /** The base implementation */ + typedef VectorDistBase<AvgFancy> Base; + /** The parameter type. */ + typedef DistStor::Params Params; + + public: + /** + * Initialize storage for this distribution. + * @param size The size of the vector. + * @return A reference to this distribution. + */ + VectorAverageDeviation &init(int size) { + this->doInit(size); + return *this; + } +}; + +/** + * A formula for statistics that is calculated when printed. A formula is + * stored as a tree of Nodes that represent the equation to calculate. + * @sa Stat, ScalarStat, VectorStat, Node, Temp + */ +class FormulaBase : public DataAccess +{ + protected: + /** The root of the tree which represents the Formula */ + NodePtr root; + friend class Temp; + + public: + /** + * Return the result of the Fomula in a vector. If there were no Vector + * components to the Formula, then the vector is size 1. If there were, + * like x/y with x being a vector of size 3, then the result returned will + * be x[0]/y, x[1]/y, x[2]/y, respectively. + * @return The result vector. + */ + void result(VResult &vec) const; + + /** + * Return the total Formula result. If there is a Vector + * component to this Formula, then this is the result of the + * Formula if the formula is applied after summing all the + * components of the Vector. For example, if Formula is x/y where + * x is size 3, then total() will return (x[1]+x[2]+x[3])/y. If + * there is no Vector component, total() returns the same value as + * the first entry in the VResult val() returns. + * @return The total of the result vector. + */ + Result total() const; + + /** + * Return the number of elements in the tree. + */ + size_t size() const; + + bool check() const { return true; } + + /** + * Formulas don't need to be reset + */ + void reset(); + + /** + * + */ + bool zero() const; + + /** + * + */ + void update(StatData *); + + std::string str() const; +}; + +class FormulaData : public VectorData +{ + public: + virtual std::string str() const = 0; + virtual bool check() const { return true; } +}; + +template <class Stat> +class FormulaStatData : public FormulaData +{ + protected: + Stat &s; + mutable VResult vec; + mutable VCounter cvec; + + public: + FormulaStatData(Stat &stat) : s(stat) {} + + virtual bool zero() const { return s.zero(); } + virtual void reset() { s.reset(); } + + virtual size_t size() const { return s.size(); } + virtual const VResult &result() const + { + s.result(vec); + return vec; + } + virtual Result total() const { return s.total(); } + virtual VCounter &value() const { return cvec; } + virtual void visit(Visit &visitor) + { + update(); + s.update(this); + visitor.visit(*this); + } + virtual std::string str() const { return s.str(); } +}; + +class Temp; +class Formula + : public WrapVec<Formula, + FormulaBase, + FormulaStatData> +{ + public: + /** + * Create and initialize thie formula, and register it with the database. + */ + Formula(); + + /** + * Create a formula with the given root node, register it with the + * database. + * @param r The root of the expression tree. + */ + Formula(Temp r); + + /** + * Set an unitialized Formula to the given root. + * @param r The root of the expression tree. + * @return a reference to this formula. + */ + const Formula &operator=(Temp r); + + /** + * Add the given tree to the existing one. + * @param r The root of the expression tree. + * @return a reference to this formula. + */ + const Formula &operator+=(Temp r); +}; + +class FormulaNode : public Node +{ + private: + const Formula &formula; + mutable VResult vec; + + public: + FormulaNode(const Formula &f) : formula(f) {} + + virtual size_t size() const { return formula.size(); } + virtual const VResult &result() const { formula.result(vec); return vec; } + virtual Result total() const { return formula.total(); } + + virtual std::string str() const { return formula.str(); } +}; + +/** + * Helper class to construct formula node trees. + */ +class Temp +{ + protected: + /** + * Pointer to a Node object. + */ + NodePtr node; + + public: + /** + * Copy the given pointer to this class. + * @param n A pointer to a Node object to copy. + */ + Temp(NodePtr n) : node(n) { } + + /** + * Return the node pointer. + * @return the node pointer. + */ + operator NodePtr&() { return node;} + + public: + /** + * Create a new ScalarStatNode. + * @param s The ScalarStat to place in a node. + */ + template <int N> + Temp(const Scalar<N> &s) + : node(new ScalarStatNode(s.statData())) { } + + /** + * Create a new ScalarStatNode. + * @param s The ScalarStat to place in a node. + */ + Temp(const Value &s) + : node(new ScalarStatNode(s.statData())) { } + + /** + * Create a new ScalarStatNode. + * @param s The ScalarStat to place in a node. + */ + template <int N> + Temp(const Average<N> &s) + : node(new ScalarStatNode(s.statData())) { } + + /** + * Create a new VectorStatNode. + * @param s The VectorStat to place in a node. + */ + template <int N> + Temp(const Vector<N> &s) + : node(new VectorStatNode(s.statData())) { } + + /** + * + */ + Temp(const Formula &f) + : node(new FormulaNode(f)) { } + + /** + * Create a new ScalarProxyNode. + * @param p The ScalarProxy to place in a node. + */ + template <class Stat> + Temp(const ScalarProxy<Stat> &p) + : node(new ScalarProxyNode<Stat>(p)) { } + + /** + * Create a ConstNode + * @param value The value of the const node. + */ + Temp(signed char value) + : node(new ConstNode<signed char>(value)) {} + + /** + * Create a ConstNode + * @param value The value of the const node. + */ + Temp(unsigned char value) + : node(new ConstNode<unsigned char>(value)) {} + + /** + * Create a ConstNode + * @param value The value of the const node. + */ + Temp(signed short value) + : node(new ConstNode<signed short>(value)) {} + + /** + * Create a ConstNode + * @param value The value of the const node. + */ + Temp(unsigned short value) + : node(new ConstNode<unsigned short>(value)) {} + + /** + * Create a ConstNode + * @param value The value of the const node. + */ + Temp(signed int value) + : node(new ConstNode<signed int>(value)) {} + + /** + * Create a ConstNode + * @param value The value of the const node. + */ + Temp(unsigned int value) + : node(new ConstNode<unsigned int>(value)) {} + + /** + * Create a ConstNode + * @param value The value of the const node. + */ + Temp(signed long value) + : node(new ConstNode<signed long>(value)) {} + + /** + * Create a ConstNode + * @param value The value of the const node. + */ + Temp(unsigned long value) + : node(new ConstNode<unsigned long>(value)) {} + + /** + * Create a ConstNode + * @param value The value of the const node. + */ + Temp(signed long long value) + : node(new ConstNode<signed long long>(value)) {} + + /** + * Create a ConstNode + * @param value The value of the const node. + */ + Temp(unsigned long long value) + : node(new ConstNode<unsigned long long>(value)) {} + + /** + * Create a ConstNode + * @param value The value of the const node. + */ + Temp(float value) + : node(new ConstNode<float>(value)) {} + + /** + * Create a ConstNode + * @param value The value of the const node. + */ + Temp(double value) + : node(new ConstNode<double>(value)) {} +}; + + +/** + * @} + */ + +void check(); +void reset(); +void registerResetCallback(Callback *cb); + +inline Temp +operator+(Temp l, Temp r) +{ + return NodePtr(new BinaryNode<std::plus<Result> >(l, r)); +} + +inline Temp +operator-(Temp l, Temp r) +{ + return NodePtr(new BinaryNode<std::minus<Result> >(l, r)); +} + +inline Temp +operator*(Temp l, Temp r) +{ + return NodePtr(new BinaryNode<std::multiplies<Result> >(l, r)); +} + +inline Temp +operator/(Temp l, Temp r) +{ + return NodePtr(new BinaryNode<std::divides<Result> >(l, r)); +} + +inline Temp +operator-(Temp l) +{ + return NodePtr(new UnaryNode<std::negate<Result> >(l)); +} + +template <typename T> +inline Temp +constant(T val) +{ + return NodePtr(new ConstNode<T>(val)); +} + +inline Temp +sum(Temp val) +{ + return NodePtr(new SumNode<std::plus<Result> >(val)); +} + +/* namespace Stats */ } + +#endif // __BASE_STATISTICS_HH__ diff --git a/src/base/stats/events.cc b/src/base/stats/events.cc new file mode 100644 index 000000000..6ecc5434c --- /dev/null +++ b/src/base/stats/events.cc @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <vector> + +#include "base/stats/events.hh" + +#if USE_MYSQL +#include "base/cprintf.hh" +#include "base/misc.hh" +#include "base/mysql.hh" +#include "base/stats/mysql.hh" +#include "base/stats/mysql_run.hh" +#include "base/str.hh" +#endif + +#include "base/match.hh" +#include "sim/host.hh" +#include "sim/sim_object.hh" +#include "sim/root.hh" + +using namespace std; + +namespace Stats { + +Tick EventStart = ULL(0x7fffffffffffffff); + +ObjectMatch event_ignore; + +#if USE_MYSQL +class InsertEvent +{ + private: + char *query; + int size; + bool first; + static const int maxsize = 1024*1024; + + typedef map<string, uint32_t> event_map_t; + event_map_t events; + + MySQL::Connection &mysql; + uint16_t run; + + public: + InsertEvent() + : mysql(MySqlDB.conn()), run(MySqlDB.run()) + { + query = new char[maxsize + 1]; + size = 0; + first = true; + flush(); + } + ~InsertEvent() + { + flush(); + } + + void flush(); + void insert(const string &stat); +}; + +void +InsertEvent::insert(const string &stat) +{ + assert(mysql.connected()); + + event_map_t::iterator i = events.find(stat); + uint32_t event; + if (i == events.end()) { + mysql.query( + csprintf("SELECT en_id " + "from event_names " + "where en_name=\"%s\"", + stat)); + + MySQL::Result result = mysql.store_result(); + if (!result) + panic("could not get a run\n%s\n", mysql.error); + + assert(result.num_fields() == 1); + MySQL::Row row = result.fetch_row(); + if (row) { + if (!to_number(row[0], event)) + panic("invalid event id: %s\n", row[0]); + } else { + mysql.query( + csprintf("INSERT INTO " + "event_names(en_name)" + "values(\"%s\")", + stat)); + + if (mysql.error) + panic("could not get a run\n%s\n", mysql.error); + + event = mysql.insert_id(); + } + } else { + event = (*i).second; + } + + if (size + 1024 > maxsize) + flush(); + + if (!first) { + query[size++] = ','; + query[size] = '\0'; + } + + first = false; + + size += sprintf(query + size, "(%u,%u,%llu)", + event, run, (unsigned long long)curTick); +} + +void +InsertEvent::flush() +{ + static const char query_header[] = "INSERT INTO " + "events(ev_event, ev_run, ev_tick)" + "values"; + + if (size) { + MySQL::Connection &mysql = MySqlDB.conn(); + assert(mysql.connected()); + mysql.query(query); + } + + query[0] = '\0'; + size = sizeof(query_header); + first = true; + memcpy(query, query_header, size); +} + +void +__event(const string &stat) +{ + static InsertEvent event; + event.insert(stat); +} + +#endif + +/* namespace Stats */ } diff --git a/src/base/stats/events.hh b/src/base/stats/events.hh new file mode 100644 index 000000000..b09b91c7c --- /dev/null +++ b/src/base/stats/events.hh @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_STATS_EVENTS_HH__ +#define __BASE_STATS_EVENTS_HH__ + +#include <string> + +#include "base/trace.hh" +#include "config/use_mysql.hh" + +namespace Stats { + +extern Tick EventStart; + +#if USE_MYSQL +void __event(const std::string &stat); +bool MySqlConnected(); +#endif + +bool ignoreEvent(const std::string &name); + +inline void +recordEvent(const std::string &stat) +{ + if (EventStart > curTick) + return; + + DPRINTF(StatEvents, "Statistics Event: %s\n", stat); + +#if USE_MYSQL + if (!MySqlConnected()) + return; + + __event(stat); +#endif +} + +/* namespace Stats */ } + +#endif // __BASE_STATS_EVENTS_HH__ diff --git a/src/base/stats/flags.hh b/src/base/stats/flags.hh new file mode 100644 index 000000000..ada1a4a87 --- /dev/null +++ b/src/base/stats/flags.hh @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_STATS_FLAGS_HH__ +#define __BASE_STATS_FLAGS_HH__ +namespace Stats { + +/** + * Define the storage for format flags. + * @todo Can probably shrink this. + */ +typedef u_int32_t StatFlags; + +/** Nothing extra to print. */ +const StatFlags none = 0x00000000; +/** This Stat is Initialized */ +const StatFlags init = 0x00000001; +/** Print this stat. */ +const StatFlags print = 0x00000002; +/** Print the total. */ +const StatFlags total = 0x00000010; +/** Print the percent of the total that this entry represents. */ +const StatFlags pdf = 0x00000020; +/** Print the cumulative percentage of total upto this entry. */ +const StatFlags cdf = 0x00000040; +/** Print the distribution. */ +const StatFlags dist = 0x00000080; +/** Don't print if this is zero. */ +const StatFlags nozero = 0x00000100; +/** Don't print if this is NAN */ +const StatFlags nonan = 0x00000200; +/** Used for SS compatability. */ +const StatFlags __substat = 0x80000000; + +/** Mask of flags that can't be set directly */ +const StatFlags __reserved = init | print | __substat; + +enum DisplayMode +{ + mode_m5, + mode_simplescalar +}; + +extern DisplayMode DefaultMode; + +/* namespace Stats */ } + +#endif // __BASE_STATS_FLAGS_HH__ diff --git a/src/base/stats/mysql.cc b/src/base/stats/mysql.cc new file mode 100644 index 000000000..0fb31f4ce --- /dev/null +++ b/src/base/stats/mysql.cc @@ -0,0 +1,828 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <cassert> +#include <map> +#include <sstream> +#include <string> +#include <vector> + +#include "base/misc.hh" +#include "base/mysql.hh" +#include "base/statistics.hh" +#include "base/stats/flags.hh" +#include "base/stats/mysql.hh" +#include "base/stats/mysql_run.hh" +#include "base/stats/statdb.hh" +#include "base/stats/types.hh" +#include "base/str.hh" +#include "sim/host.hh" + +using namespace std; + +namespace Stats { + +MySqlRun MySqlDB; + +bool +MySqlConnected() +{ + return MySqlDB.connected(); +} + +void +MySqlRun::connect(const string &host, const string &user, const string &passwd, + const string &db, const string &name, const string &sample, + const string &project) +{ + if (connected()) + panic("can only get one database connection at this time!"); + + mysql.connect(host, user, passwd, db); + if (mysql.error) + panic("could not connect to database server\n%s\n", mysql.error); + + if (mysql.autocommit(false)) + panic("could not set autocommit\n%s\n", mysql.error); + + remove(name); + //cleanup(); + setup(name, sample, user, project); +} + +void +MySqlRun::setup(const string &name, const string &sample, const string &user, + const string &project) +{ + assert(mysql.connected()); + + stringstream insert; + ccprintf(insert, + "INSERT INTO " + "runs(rn_name,rn_sample,rn_user,rn_project,rn_date,rn_expire)" + "values(\"%s\", \"%s\", \"%s\", \"%s\", NOW()," + "DATE_ADD(CURDATE(), INTERVAL 31 DAY))", + name, sample, user, project); + + mysql.query(insert); + if (mysql.error) + panic("could not get a run\n%s\n", mysql.error); + + run_id = mysql.insert_id(); + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); +} + +void +MySqlRun::remove(const string &name) +{ + assert(mysql.connected()); + stringstream sql; + ccprintf(sql, "DELETE FROM runs WHERE rn_name=\"%s\"", name); + mysql.query(sql); + if (mysql.error) + panic("could not delete run\n%s\n", mysql.error); + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); +} + +void +MySqlRun::cleanup() +{ + assert(mysql.connected()); + + mysql.query("DELETE data " + "FROM data " + "LEFT JOIN runs ON dt_run=rn_id " + "WHERE rn_id IS NULL"); + + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); + + mysql.query("DELETE formula_ref " + "FROM formula_ref " + "LEFT JOIN runs ON fr_run=rn_id " + "WHERE rn_id IS NULL"); + + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); + + mysql.query("DELETE formulas " + "FROM formulas " + "LEFT JOIN formula_ref ON fm_stat=fr_stat " + "WHERE fr_stat IS NULL"); + + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); + + mysql.query("DELETE stats " + "FROM stats " + "LEFT JOIN data ON st_id=dt_stat " + "WHERE dt_stat IS NULL"); + + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); + + mysql.query("DELETE subdata " + "FROM subdata " + "LEFT JOIN data ON sd_stat=dt_stat " + "WHERE dt_stat IS NULL"); + + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); + + mysql.query("DELETE events" + "FROM events" + "LEFT JOIN runs ON ev_run=rn_id" + "WHERE rn_id IS NULL"); + + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); + + mysql.query("DELETE event_names" + "FROM event_names" + "LEFT JOIN events ON en_id=ev_event" + "WHERE ev_event IS NULL"); + + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); +} + +void +SetupStat::init() +{ + name = ""; + descr = ""; + type = ""; + print = false; + prereq = 0; + prec = -1; + nozero = false; + nonan = false; + total = false; + pdf = false; + cdf = false; + min = 0; + max = 0; + bktsize = 0; + size = 0; +} + +unsigned +SetupStat::setup() +{ + MySQL::Connection &mysql = MySqlDB.conn(); + + stringstream insert; + ccprintf(insert, + "INSERT INTO " + "stats(st_name, st_descr, st_type, st_print, st_prereq, " + "st_prec, st_nozero, st_nonan, st_total, st_pdf, st_cdf, " + "st_min, st_max, st_bktsize, st_size)" + "values(\"%s\",\"%s\",\"%s\"," + " %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)", + name, descr, type, print, prereq, (int)prec, nozero, nonan, + total, pdf, cdf, + min, max, bktsize, size); + + mysql.query(insert); + if (!mysql.error) { + int id = mysql.insert_id(); + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); + return id; + } + + stringstream select; + ccprintf(select, "SELECT * FROM stats WHERE st_name=\"%s\"", name); + + mysql.query(select); + MySQL::Result result = mysql.store_result(); + if (!result) + panic("could not find stat\n%s\n", mysql.error); + + assert(result.num_fields() == 16); + MySQL::Row row = result.fetch_row(); + if (!row) + panic("could not get stat row\n%s\n", mysql.error); + + bool tb; + int8_t ti8; + uint16_t tu16; + int64_t ti64; + uint64_t tu64; + + if (name != (char *)row[1]) + panic("failed stat check on %s:name. %s != %s\n", + name, name, row[1]); + + if (descr != (char *)row[2]) + panic("failed stat check on %s:descr. %s != %s\n", + name, descr, row[2]); + + if (type != (char *)row[3]) + panic("failed stat check on %s:type. %s != %s\n", + name, type, row[3]); + + if (!to_number(row[4], tb) || print != tb) + panic("failed stat check on %s:print. %d != %d\n", + name, print, tb); + + if (!to_number(row[6], ti8) || prec != ti8) + panic("failed stat check on %s:prec. %d != %d\n", + name, prec, ti8); + + if (!to_number(row[7], tb) || nozero != tb) + panic("failed stat check on %s:nozero. %d != %d\n", + name, nozero, tb); + + if (!to_number(row[8], tb) || nonan != tb) + panic("failed stat check on %s:nonan. %d != %d\n", + name, nonan, tb); + + if (!to_number(row[9], tb) || total != tb) + panic("failed stat check on %s:total. %d != %d\n", + name, total, tb); + + if (!to_number(row[10], tb) || pdf != tb) + panic("failed stat check on %s:pdf. %d != %d\n", + name, pdf, tb); + + if (!to_number(row[11], tb) || cdf != tb) + panic("failed stat check on %s:cdf. %d != %d\n", + name, cdf, tb); + + if (!to_number(row[12], ti64) || min != ti64) + panic("failed stat check on %s:min. %d != %d\n", + name, min, ti64); + + if (!to_number(row[13], ti64) || max != ti64) + panic("failed stat check on %s:max. %d != %d\n", + name, max, ti64); + + if (!to_number(row[14], tu64) || bktsize != tu64) + panic("failed stat check on %s:bktsize. %d != %d\n", + name, bktsize, tu64); + + if (!to_number(row[15], tu16) || size != tu16) + panic("failed stat check on %s:size. %d != %d\n", + name, size, tu16); + + to_number(row[5], prereq); + uint16_t statid; + to_number(row[0], statid); + return statid; +} + +InsertData::InsertData() +{ + query = new char[maxsize + 1]; + size = 0; + flush(); +} + +InsertData::~InsertData() +{ + delete [] query; +} + +void +InsertData::flush() +{ + if (size) { + MySQL::Connection &mysql = MySqlDB.conn(); + assert(mysql.connected()); + mysql.query(query); + if (mysql.error) + panic("could not insert data\n%s\n", mysql.error); + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); + } + + query[0] = '\0'; + size = 0; + first = true; + strcpy(query, "INSERT INTO " + "data(dt_stat,dt_x,dt_y,dt_run,dt_tick,dt_data) " + "values"); + size = strlen(query); +} + +void +InsertData::insert() +{ + if (size + 1024 > maxsize) + flush(); + + if (!first) { + query[size++] = ','; + query[size] = '\0'; + } + + first = false; + + size += sprintf(query + size, "(%u,%d,%d,%u,%llu,\"%f\")", + stat, x, y, MySqlDB.run(), (unsigned long long)tick, + data); +} + +struct InsertSubData +{ + uint16_t stat; + int16_t x; + int16_t y; + string name; + string descr; + + void setup(); +}; + +void +InsertSubData::setup() +{ + MySQL::Connection &mysql = MySqlDB.conn(); + assert(mysql.connected()); + stringstream insert; + ccprintf(insert, + "INSERT INTO subdata(sd_stat,sd_x,sd_y,sd_name,sd_descr) " + "values(%d,%d,%d,\"%s\",\"%s\")", + stat, x, y, name, descr); + + mysql.query(insert); +// if (mysql.error) +// panic("could not insert subdata\n%s\n", mysql.error); + + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); +} + +void +InsertFormula(uint16_t stat, const string &formula) +{ + MySQL::Connection &mysql = MySqlDB.conn(); + assert(mysql.connected()); + stringstream insert_formula; + ccprintf(insert_formula, + "INSERT INTO formulas(fm_stat,fm_formula) values(%d, \"%s\")", + stat, formula); + + mysql.query(insert_formula); +// if (mysql.error) +// panic("could not insert formula\n%s\n", mysql.error); + + stringstream insert_ref; + ccprintf(insert_ref, + "INSERT INTO formula_ref(fr_stat,fr_run) values(%d, %d)", + stat, MySqlDB.run()); + + mysql.query(insert_ref); +// if (mysql.error) +// panic("could not insert formula reference\n%s\n", mysql.error); + + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); +} + +void +UpdatePrereq(uint16_t stat, uint16_t prereq) +{ + MySQL::Connection &mysql = MySqlDB.conn(); + assert(mysql.connected()); + stringstream update; + ccprintf(update, "UPDATE stats SET st_prereq=%d WHERE st_id=%d", + prereq, stat); + mysql.query(update); + if (mysql.error) + panic("could not update prereq\n%s\n", mysql.error); + + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); +} + +void +MySql::configure() +{ + /* + * set up all stats! + */ + using namespace Database; + + MySQL::Connection &mysql = MySqlDB.conn(); + + stat_list_t::const_iterator i, end = stats().end(); + for (i = stats().begin(); i != end; ++i) { + (*i)->visit(*this); + } + + for (i = stats().begin(); i != end; ++i) { + StatData *data = *i; + if (data->prereq) { + uint16_t stat_id = find(data->id); + uint16_t prereq_id = find(data->prereq->id); + assert(stat_id && prereq_id); + + UpdatePrereq(stat_id, prereq_id); + } + } + + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); + + configured = true; +} + + +bool +MySql::configure(const StatData &data, string type) +{ + stat.init(); + stat.name = data.name; + stat.descr = data.desc; + stat.type = type; + stat.print = data.flags & print; + stat.prec = data.precision; + stat.nozero = data.flags & nozero; + stat.nonan = data.flags & nonan; + stat.total = data.flags & total; + stat.pdf = data.flags & pdf; + stat.cdf = data.flags & cdf; + + return stat.print; +} + +void +MySql::configure(const ScalarData &data) +{ + if (!configure(data, "SCALAR")) + return; + + insert(data.id, stat.setup()); +} + +void +MySql::configure(const VectorData &data) +{ + if (!configure(data, "VECTOR")) + return; + + uint16_t statid = stat.setup(); + + if (!data.subnames.empty()) { + InsertSubData subdata; + subdata.stat = statid; + subdata.y = 0; + for (int i = 0; i < data.subnames.size(); ++i) { + subdata.x = i; + subdata.name = data.subnames[i]; + subdata.descr = data.subdescs.empty() ? "" : data.subdescs[i]; + + if (!subdata.name.empty() || !subdata.descr.empty()) + subdata.setup(); + } + } + + insert(data.id, statid); +} + +void +MySql::configure(const DistData &data) +{ + if (!configure(data, "DIST")) + return; + + if (!data.data.fancy) { + stat.size = data.data.size; + stat.min = data.data.min; + stat.max = data.data.max; + stat.bktsize = data.data.bucket_size; + } + insert(data.id, stat.setup()); +} + +void +MySql::configure(const VectorDistData &data) +{ + if (!configure(data, "VECTORDIST")) + return; + + if (!data.data[0].fancy) { + stat.size = data.data[0].size; + stat.min = data.data[0].min; + stat.max = data.data[0].max; + stat.bktsize = data.data[0].bucket_size; + } + + uint16_t statid = stat.setup(); + + if (!data.subnames.empty()) { + InsertSubData subdata; + subdata.stat = statid; + subdata.y = 0; + for (int i = 0; i < data.subnames.size(); ++i) { + subdata.x = i; + subdata.name = data.subnames[i]; + subdata.descr = data.subdescs.empty() ? "" : data.subdescs[i]; + if (!subdata.name.empty() || !subdata.descr.empty()) + subdata.setup(); + } + } + + insert(data.id, statid); +} + +void +MySql::configure(const Vector2dData &data) +{ + if (!configure(data, "VECTOR2D")) + return; + + uint16_t statid = stat.setup(); + + if (!data.subnames.empty()) { + InsertSubData subdata; + subdata.stat = statid; + subdata.y = -1; + for (int i = 0; i < data.subnames.size(); ++i) { + subdata.x = i; + subdata.name = data.subnames[i]; + subdata.descr = data.subdescs.empty() ? "" : data.subdescs[i]; + if (!subdata.name.empty() || !subdata.descr.empty()) + subdata.setup(); + } + } + + if (!data.y_subnames.empty()) { + InsertSubData subdata; + subdata.stat = statid; + subdata.x = -1; + subdata.descr = ""; + for (int i = 0; i < data.y_subnames.size(); ++i) { + subdata.y = i; + subdata.name = data.y_subnames[i]; + if (!subdata.name.empty()) + subdata.setup(); + } + } + + insert(data.id, statid); +} + +void +MySql::configure(const FormulaData &data) +{ + configure(data, "FORMULA"); + insert(data.id, stat.setup()); + InsertFormula(find(data.id), data.str()); +} + +bool +MySql::valid() const +{ + return MySqlDB.connected(); +} + +void +MySql::output() +{ + using namespace Database; + assert(valid()); + + if (!configured) + configure(); + + // store sample # + newdata.tick = curTick; + + MySQL::Connection &mysql = MySqlDB.conn(); + + Database::stat_list_t::const_iterator i, end = Database::stats().end(); + for (i = Database::stats().begin(); i != end; ++i) { + StatData *stat = *i; + stat->visit(*this); + if (mysql.commit()) + panic("could not commit transaction\n%s\n", mysql.error); + } + + newdata.flush(); +} + +void +MySql::output(const ScalarData &data) +{ + if (!(data.flags & print)) + return; + + newdata.stat = find(data.id); + newdata.x = 0; + newdata.y = 0; + newdata.data = data.value(); + + newdata.insert(); +} + +void +MySql::output(const VectorData &data) +{ + if (!(data.flags & print)) + return; + + newdata.stat = find(data.id); + newdata.y = 0; + + const VCounter &cvec = data.value(); + int size = data.size(); + for (int x = 0; x < size; x++) { + newdata.x = x; + newdata.data = cvec[x]; + newdata.insert(); + } +} + +void +MySql::output(const DistDataData &data) +{ + const int db_sum = -1; + const int db_squares = -2; + const int db_samples = -3; + const int db_min_val = -4; + const int db_max_val = -5; + const int db_underflow = -6; + const int db_overflow = -7; + + newdata.x = db_sum; + newdata.data = data.sum; + newdata.insert(); + + newdata.x = db_squares; + newdata.data = data.squares; + newdata.insert(); + + newdata.x = db_samples; + newdata.data = data.samples; + newdata.insert(); + + if (data.samples && !data.fancy) { + newdata.x = db_min_val; + newdata.data = data.min_val; + newdata.insert(); + + newdata.x = db_max_val; + newdata.data = data.max_val; + newdata.insert(); + + newdata.x = db_underflow; + newdata.data = data.underflow; + newdata.insert(); + + newdata.x = db_overflow; + newdata.data = data.overflow; + newdata.insert(); + + int size = data.cvec.size(); + for (int x = 0; x < size; x++) { + newdata.x = x; + newdata.data = data.cvec[x]; + newdata.insert(); + } + } +} + + +void +MySql::output(const DistData &data) +{ + if (!(data.flags & print)) + return; + + newdata.stat = find(data.id); + newdata.y = 0; + output(data.data); +} + +void +MySql::output(const VectorDistData &data) +{ + if (!(data.flags & print)) + return; + + newdata.stat = find(data.id); + + int size = data.data.size(); + for (int y = 0; y < size; ++y) { + newdata.y = y; + output(data.data[y]); + } +} + +void +MySql::output(const Vector2dData &data) +{ + if (!(data.flags & print)) + return; + + newdata.stat = find(data.id); + + int index = 0; + for (int x = 0; x < data.x; x++) { + newdata.x = x; + for (int y = 0; y < data.y; y++) { + newdata.y = y; + newdata.data = data.cvec[index++]; + newdata.insert(); + } + } +} + +void +MySql::output(const FormulaData &data) +{ +} + +/* + * Implement the visitor + */ +void +MySql::visit(const ScalarData &data) +{ + if (!configured) + configure(data); + else + output(data); +} + +void +MySql::visit(const VectorData &data) +{ + if (!configured) + configure(data); + else + output(data); +} + +void +MySql::visit(const DistData &data) +{ + return; + if (!configured) + configure(data); + else + output(data); +} + +void +MySql::visit(const VectorDistData &data) +{ + return; + if (!configured) + configure(data); + else + output(data); +} + +void +MySql::visit(const Vector2dData &data) +{ + return; + if (!configured) + configure(data); + else + output(data); +} + +void +MySql::visit(const FormulaData &data) +{ + if (!configured) + configure(data); + else + output(data); +} + +/* namespace Stats */ } diff --git a/src/base/stats/mysql.hh b/src/base/stats/mysql.hh new file mode 100644 index 000000000..50f7d9e97 --- /dev/null +++ b/src/base/stats/mysql.hh @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_STATS_MYSQL_HH__ +#define __BASE_STATS_MYSQL_HH__ + +#include <map> +#include <string> + +#include "base/stats/output.hh" + +namespace MySQL { class Connection; } +namespace Stats { + +class DistDataData; +class MySqlRun; +bool MySqlConnected(); +extern MySqlRun MySqlDB; + +struct SetupStat +{ + std::string name; + std::string descr; + std::string type; + bool print; + uint16_t prereq; + int8_t prec; + bool nozero; + bool nonan; + bool total; + bool pdf; + bool cdf; + double min; + double max; + double bktsize; + uint16_t size; + + void init(); + unsigned setup(); +}; + +class InsertData +{ + private: + char *query; + int size; + bool first; + static const int maxsize = 1024*1024; + + public: + MySqlRun *run; + + public: + uint64_t tick; + double data; + uint16_t stat; + int16_t x; + int16_t y; + + public: + InsertData(); + ~InsertData(); + + void flush(); + void insert(); +}; + +class MySql : public Output +{ + protected: + SetupStat stat; + InsertData newdata; + std::list<FormulaData *> formulas; + bool configured; + + protected: + std::map<int, int> idmap; + + void insert(int sim_id, int db_id) + { + using namespace std; + idmap.insert(make_pair(sim_id, db_id)); + } + + int find(int sim_id) + { + using namespace std; + map<int,int>::const_iterator i = idmap.find(sim_id); + assert(i != idmap.end()); + return (*i).second; + } + public: + // Implement Visit + virtual void visit(const ScalarData &data); + virtual void visit(const VectorData &data); + virtual void visit(const DistData &data); + virtual void visit(const VectorDistData &data); + virtual void visit(const Vector2dData &data); + virtual void visit(const FormulaData &data); + + // Implement Output + virtual bool valid() const; + virtual void output(); + + protected: + // Output helper + void output(const DistDataData &data); + void output(const ScalarData &data); + void output(const VectorData &data); + void output(const DistData &data); + void output(const VectorDistData &data); + void output(const Vector2dData &data); + void output(const FormulaData &data); + + void configure(); + bool configure(const StatData &data, std::string type); + void configure(const ScalarData &data); + void configure(const VectorData &data); + void configure(const DistData &data); + void configure(const VectorDistData &data); + void configure(const Vector2dData &data); + void configure(const FormulaData &data); +}; + +/* namespace Stats */ } + +#endif // __BASE_STATS_MYSQL_HH__ diff --git a/src/base/stats/mysql_run.hh b/src/base/stats/mysql_run.hh new file mode 100644 index 000000000..487224551 --- /dev/null +++ b/src/base/stats/mysql_run.hh @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_STATS_MYSQL_RUN_HH__ +#define __BASE_STATS_MYSQL_RUN_HH__ + +#include <string> + +#include "base/mysql.hh" +#include "sim/host.hh" + +namespace Stats { + +struct MySqlRun +{ + private: + MySQL::Connection mysql; + uint16_t run_id; + + protected: + void setup(const std::string &name, const std::string &sample, + const std::string &user, const std::string &project); + + void remove(const std::string &name); + void cleanup(); + + public: + bool connected() const { return mysql.connected(); } + void connect(const std::string &host, const std::string &user, + const std::string &passwd, const std::string &db, + const std::string &name, const std::string &sample, + const std::string &project); + + MySQL::Connection &conn() { return mysql; } + uint16_t run() const { return run_id; } +}; + +/* namespace Stats */ } + +#endif // __BASE_STATS_MYSQL_RUN_HH__ diff --git a/src/base/stats/output.hh b/src/base/stats/output.hh new file mode 100644 index 000000000..4fe93791f --- /dev/null +++ b/src/base/stats/output.hh @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_STATS_OUTPUT_HH__ +#define __BASE_STATS_OUTPUT_HH__ + +#include <string> + +#include "base/stats/visit.hh" + +namespace Stats { + +struct Output : public Visit +{ + inline void operator()() { output(); } + virtual void output() = 0; + virtual bool valid() const = 0; +}; + +/* namespace Stats */ } + +#endif // __BASE_STATS_OUTPUT_HH__ diff --git a/src/base/stats/statdb.cc b/src/base/stats/statdb.cc new file mode 100644 index 000000000..f9136807a --- /dev/null +++ b/src/base/stats/statdb.cc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include "base/misc.hh" +#include "base/trace.hh" +#include "base/statistics.hh" +#include "base/stats/statdb.hh" + +using namespace std; + +namespace Stats { +namespace Database { + +StatData * +find(void *stat) +{ + stat_map_t::const_iterator i = map().find(stat); + + if (i == map().end()) + return NULL; + + return (*i).second; +} + +void +regStat(void *stat, StatData *data) +{ + if (map().find(stat) != map().end()) + panic("shouldn't register stat twice!"); + + stats().push_back(data); + +#ifndef NDEBUG + pair<stat_map_t::iterator, bool> result = +#endif + map().insert(make_pair(stat, data)); + assert(result.second && "this should never fail"); + assert(map().find(stat) != map().end()); +} + +void +regPrint(void *stat) +{ + StatData *data = find(stat); + assert(data); + data->flags |= print; +} + +TheDatabase &db() +{ + static TheDatabase db; + return db; +} + +/* namespace Database */ } +/* namespace Stats */ } diff --git a/src/base/stats/statdb.hh b/src/base/stats/statdb.hh new file mode 100644 index 000000000..a5b9be7eb --- /dev/null +++ b/src/base/stats/statdb.hh @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_STATS_STATDB_HH__ +#define __BASE_STATS_STATDB_HH__ + +#include <iosfwd> +#include <list> +#include <map> +#include <string> + +class Python; + +namespace Stats { + +class StatData; + +namespace Database { + +typedef std::map<void *, StatData *> stat_map_t; +typedef std::list<StatData *> stat_list_t; + +// We wrap the database in a struct to make sure it is built in time. +struct TheDatabase +{ + stat_map_t map; + stat_list_t stats; +}; + +TheDatabase &db(); +inline stat_map_t &map() { return db().map; } +inline stat_list_t &stats() { return db().stats; } + +StatData *find(void *stat); +void regStat(void *stat, StatData *data); +void regPrint(void *stat); + +inline std::string name() { return "Statistics Database"; } + +/* namespace Database */ } +/* namespace Stats */ } + +#endif // __BASE_STATS_STATDB_HH__ diff --git a/src/base/stats/text.cc b/src/base/stats/text.cc new file mode 100644 index 000000000..c4448efc9 --- /dev/null +++ b/src/base/stats/text.cc @@ -0,0 +1,724 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#if defined(__APPLE__) +#define _GLIBCPP_USE_C99 1 +#endif + +#include <iostream> +#include <sstream> +#include <fstream> +#include <string> + +#include "base/misc.hh" +#include "base/statistics.hh" +#include "base/stats/statdb.hh" +#include "base/stats/text.hh" +#include "base/stats/visit.hh" + +using namespace std; + +#ifndef NAN +float __nan(); +/** Define Not a number. */ +#define NAN (__nan()) +/** Need to define __nan() */ +#define __M5_NAN +#endif + +#ifdef __M5_NAN +float +__nan() +{ + union { + uint32_t ui; + float f; + } nan; + + nan.ui = 0x7fc00000; + return nan.f; +} +#endif + +namespace Stats { + +Text::Text() + : mystream(false), stream(NULL), compat(false), descriptions(false) +{ +} + +Text::Text(std::ostream &stream) + : mystream(false), stream(NULL), compat(false), descriptions(false) +{ + open(stream); +} + +Text::Text(const std::string &file) + : mystream(false), stream(NULL), compat(false), descriptions(false) +{ + open(file); +} + + +Text::~Text() +{ + if (mystream) { + assert(stream); + delete stream; + } +} + +void +Text::open(std::ostream &_stream) +{ + if (stream) + panic("stream already set!"); + + mystream = false; + stream = &_stream; + assert(valid()); +} + +void +Text::open(const std::string &file) +{ + if (stream) + panic("stream already set!"); + + mystream = true; + stream = new ofstream(file.c_str(), ios::trunc); + assert(valid()); +} + +bool +Text::valid() const +{ + return stream != NULL; +} + +void +Text::output() +{ + using namespace Database; + + ccprintf(*stream, "\n---------- Begin Simulation Statistics ----------\n"); + stat_list_t::const_iterator i, end = stats().end(); + for (i = stats().begin(); i != end; ++i) + (*i)->visit(*this); + ccprintf(*stream, "\n---------- End Simulation Statistics ----------\n"); + stream->flush(); +} + +bool +Text::noOutput(const StatData &data) +{ + if (!(data.flags & print)) + return true; + + if (data.prereq && data.prereq->zero()) + return true; + + return false; +} + +string +ValueToString(Result value, int precision, bool compat) +{ + stringstream val; + + if (!isnan(value)) { + if (precision != -1) + val.precision(precision); + else if (value == rint(value)) + val.precision(0); + + val.unsetf(ios::showpoint); + val.setf(ios::fixed); + val << value; + } else { + val << (compat ? "<err: div-0>" : "no value"); + } + + return val.str(); +} + +struct ScalarPrint +{ + Result value; + string name; + string desc; + StatFlags flags; + bool compat; + bool descriptions; + int precision; + Result pdf; + Result cdf; + + void operator()(ostream &stream) const; +}; + +void +ScalarPrint::operator()(ostream &stream) const +{ + if (flags & nozero && value == 0.0 || + flags & nonan && isnan(value)) + return; + + stringstream pdfstr, cdfstr; + + if (!isnan(pdf)) + ccprintf(pdfstr, "%.2f%%", pdf * 100.0); + + if (!isnan(cdf)) + ccprintf(cdfstr, "%.2f%%", cdf * 100.0); + + if (compat && flags & __substat) { + ccprintf(stream, "%32s %12s %10s %10s", name, + ValueToString(value, precision, compat), pdfstr, cdfstr); + } else { + ccprintf(stream, "%-40s %12s %10s %10s", name, + ValueToString(value, precision, compat), pdfstr, cdfstr); + } + + if (descriptions) { + if (!desc.empty()) + ccprintf(stream, " # %s", desc); + } + stream << endl; +} + +struct VectorPrint +{ + string name; + string desc; + vector<string> subnames; + vector<string> subdescs; + StatFlags flags; + bool compat; + bool descriptions; + int precision; + VResult vec; + Result total; + + void operator()(ostream &stream) const; +}; + +void +VectorPrint::operator()(std::ostream &stream) const +{ + int _size = vec.size(); + Result _total = 0.0; + + if (flags & (pdf | cdf)) { + for (int i = 0; i < _size; ++i) { + _total += vec[i]; + } + } + + string base = name + (compat ? "_" : "::"); + + ScalarPrint print; + print.name = name; + print.desc = desc; + print.precision = precision; + print.descriptions = descriptions; + print.flags = flags; + print.pdf = NAN; + print.cdf = NAN; + + bool havesub = !subnames.empty(); + + if (_size == 1) { + print.value = vec[0]; + print(stream); + } else if (!compat) { + for (int i = 0; i < _size; ++i) { + if (havesub && (i >= subnames.size() || subnames[i].empty())) + continue; + + print.name = base + (havesub ? subnames[i] : to_string(i)); + print.desc = subdescs.empty() ? desc : subdescs[i]; + print.value = vec[i]; + + if (_total && (flags & pdf)) { + print.pdf = vec[i] / _total; + print.cdf += print.pdf; + } + + print(stream); + } + + if (flags & ::Stats::total) { + print.name = base + "total"; + print.desc = desc; + print.value = total; + print(stream); + } + } else { + if (flags & ::Stats::total) { + print.value = total; + print(stream); + } + + Result _pdf = 0.0; + Result _cdf = 0.0; + if (flags & dist) { + ccprintf(stream, "%s.start_dist\n", name); + for (int i = 0; i < _size; ++i) { + print.name = havesub ? subnames[i] : to_string(i); + print.desc = subdescs.empty() ? desc : subdescs[i]; + print.flags |= __substat; + print.value = vec[i]; + + if (_total) { + _pdf = vec[i] / _total; + _cdf += _pdf; + } + + if (flags & pdf) + print.pdf = _pdf; + if (flags & cdf) + print.cdf = _cdf; + + print(stream); + } + ccprintf(stream, "%s.end_dist\n", name); + } else { + for (int i = 0; i < _size; ++i) { + if (havesub && subnames[i].empty()) + continue; + + print.name = base; + print.name += havesub ? subnames[i] : to_string(i); + print.desc = subdescs.empty() ? desc : subdescs[i]; + print.value = vec[i]; + + if (_total) { + _pdf = vec[i] / _total; + _cdf += _pdf; + } else { + _pdf = _cdf = NAN; + } + + if (flags & pdf) { + print.pdf = _pdf; + print.cdf = _cdf; + } + + print(stream); + } + } + } +} + +struct DistPrint +{ + string name; + string desc; + StatFlags flags; + bool compat; + bool descriptions; + int precision; + + Result min_val; + Result max_val; + Result underflow; + Result overflow; + VResult vec; + Result sum; + Result squares; + Result samples; + + Counter min; + Counter max; + Counter bucket_size; + int size; + bool fancy; + + void operator()(ostream &stream) const; +}; + +void +DistPrint::operator()(ostream &stream) const +{ + if (fancy) { + ScalarPrint print; + string base = name + (compat ? "_" : "::"); + + print.precision = precision; + print.flags = flags; + print.compat = compat; + print.descriptions = descriptions; + print.desc = desc; + print.pdf = NAN; + print.cdf = NAN; + + print.name = base + "mean"; + print.value = samples ? sum / samples : NAN; + print(stream); + + print.name = base + "stdev"; + print.value = samples ? sqrt((samples * squares - sum * sum) / + (samples * (samples - 1.0))) : NAN; + print(stream); + + print.name = "**Ignore: " + base + "TOT"; + print.value = samples; + print(stream); + return; + } + + assert(size == vec.size()); + + Result total = 0.0; + + total += underflow; + for (int i = 0; i < size; ++i) + total += vec[i]; + total += overflow; + + string base = name + (compat ? "." : "::"); + + ScalarPrint print; + print.desc = compat ? "" : desc; + print.flags = flags; + print.compat = compat; + print.descriptions = descriptions; + print.precision = precision; + print.pdf = NAN; + print.cdf = NAN; + + if (compat) { + ccprintf(stream, "%-42s", base + "start_dist"); + if (descriptions && !desc.empty()) + ccprintf(stream, " # %s", desc); + stream << endl; + } + + print.name = base + "samples"; + print.value = samples; + print(stream); + + print.name = base + "min_value"; + print.value = min_val; + print(stream); + + if (!compat || underflow > 0.0) { + print.name = base + "underflows"; + print.value = underflow; + if (!compat && total) { + print.pdf = underflow / total; + print.cdf += print.pdf; + } + print(stream); + } + + + if (!compat) { + for (int i = 0; i < size; ++i) { + stringstream namestr; + namestr << name; + + Counter low = i * bucket_size + min; + Counter high = ::min(low + bucket_size, max); + namestr << low; + if (low < high) + namestr << "-" << high; + + print.name = namestr.str(); + print.value = vec[i]; + if (total) { + print.pdf = vec[i] / total; + print.cdf += print.pdf; + } + print(stream); + } + + } else { + Counter _min; + Result _pdf; + Result _cdf = 0.0; + + print.flags = flags | __substat; + + for (int i = 0; i < size; ++i) { + if (flags & nozero && vec[i] == 0.0 || + flags & nonan && isnan(vec[i])) + continue; + + _min = i * bucket_size + min; + _pdf = vec[i] / total * 100.0; + _cdf += _pdf; + + + print.name = ValueToString(_min, 0, compat); + print.value = vec[i]; + print.pdf = (flags & pdf) ? _pdf : NAN; + print.cdf = (flags & cdf) ? _cdf : NAN; + print(stream); + } + + print.flags = flags; + } + + if (!compat || overflow > 0.0) { + print.name = base + "overflows"; + print.value = overflow; + if (!compat && total) { + print.pdf = overflow / total; + print.cdf += print.pdf; + } else { + print.pdf = NAN; + print.cdf = NAN; + } + print(stream); + } + + print.pdf = NAN; + print.cdf = NAN; + + if (!compat) { + print.name = base + "total"; + print.value = total; + print(stream); + } + + print.name = base + "max_value"; + print.value = max_val; + print(stream); + + if (!compat && samples != 0) { + print.name = base + "mean"; + print.value = sum / samples; + print(stream); + + print.name = base + "stdev"; + print.value = sqrt((samples * squares - sum * sum) / + (samples * (samples - 1.0))); + print(stream); + } + + if (compat) + ccprintf(stream, "%send_dist\n\n", base); +} + +void +Text::visit(const ScalarData &data) +{ + if (noOutput(data)) + return; + + ScalarPrint print; + print.value = data.result(); + print.name = data.name; + print.desc = data.desc; + print.flags = data.flags; + print.compat = compat; + print.descriptions = descriptions; + print.precision = data.precision; + print.pdf = NAN; + print.cdf = NAN; + + print(*stream); +} + +void +Text::visit(const VectorData &data) +{ + if (noOutput(data)) + return; + + int size = data.size(); + VectorPrint print; + + print.name = data.name; + print.desc = data.desc; + print.flags = data.flags; + print.compat = compat; + print.descriptions = descriptions; + print.precision = data.precision; + print.vec = data.result(); + print.total = data.total(); + + if (!data.subnames.empty()) { + for (int i = 0; i < size; ++i) { + if (!data.subnames[i].empty()) { + print.subnames = data.subnames; + print.subnames.resize(size); + for (int i = 0; i < size; ++i) { + if (!data.subnames[i].empty() && + !data.subdescs[i].empty()) { + print.subdescs = data.subdescs; + print.subdescs.resize(size); + break; + } + } + break; + } + } + } + + print(*stream); +} + +void +Text::visit(const Vector2dData &data) +{ + if (noOutput(data)) + return; + + bool havesub = false; + VectorPrint print; + + print.subnames = data.y_subnames; + print.flags = data.flags; + print.compat = compat; + print.descriptions = descriptions; + print.precision = data.precision; + + if (!data.subnames.empty()) { + for (int i = 0; i < data.x; ++i) + if (!data.subnames[i].empty()) + havesub = true; + } + + VResult tot_vec(data.y); + Result super_total = 0.0; + for (int i = 0; i < data.x; ++i) { + if (havesub && (i >= data.subnames.size() || data.subnames[i].empty())) + continue; + + int iy = i * data.y; + VResult yvec(data.y); + + Result total = 0.0; + for (int j = 0; j < data.y; ++j) { + yvec[j] = data.cvec[iy + j]; + tot_vec[j] += yvec[j]; + total += yvec[j]; + super_total += yvec[j]; + } + + print.name = data.name + "_" + (havesub ? data.subnames[i] : to_string(i)); + print.desc = data.desc; + print.vec = yvec; + print.total = total; + print(*stream); + } + + if ((data.flags & ::Stats::total) && (data.x > 1)) { + print.name = data.name; + print.desc = data.desc; + print.vec = tot_vec; + print.total = super_total; + print(*stream); + } +} + +void +Text::visit(const DistData &data) +{ + if (noOutput(data)) + return; + + DistPrint print; + + print.name = data.name; + print.desc = data.desc; + print.flags = data.flags; + print.compat = compat; + print.descriptions = descriptions; + print.precision = data.precision; + + print.min_val = data.data.min_val; + print.max_val = data.data.max_val; + print.underflow = data.data.underflow; + print.overflow = data.data.overflow; + print.vec.resize(data.data.cvec.size()); + for (int i = 0; i < print.vec.size(); ++i) + print.vec[i] = (Result)data.data.cvec[i]; + print.sum = data.data.sum; + print.squares = data.data.squares; + print.samples = data.data.samples; + + print.min = data.data.min; + print.max = data.data.max; + print.bucket_size = data.data.bucket_size; + print.size = data.data.size; + print.fancy = data.data.fancy; + + print(*stream); +} + +void +Text::visit(const VectorDistData &data) +{ + if (noOutput(data)) + return; + + for (int i = 0; i < data.size(); ++i) { + DistPrint print; + + print.name = data.name + + (data.subnames[i].empty() ? ("_" + to_string(i)) : data.subnames[i]); + print.desc = data.subdescs[i].empty() ? data.desc : data.subdescs[i]; + print.flags = data.flags; + print.compat = compat; + print.descriptions = descriptions; + print.precision = data.precision; + + print.min_val = data.data[i].min_val; + print.max_val = data.data[i].max_val; + print.underflow = data.data[i].underflow; + print.overflow = data.data[i].overflow; + print.vec.resize(data.data[i].cvec.size()); + for (int j = 0; j < print.vec.size(); ++j) + print.vec[j] = (Result)data.data[i].cvec[j]; + print.sum = data.data[i].sum; + print.squares = data.data[i].squares; + print.samples = data.data[i].samples; + + print.min = data.data[i].min; + print.max = data.data[i].max; + print.bucket_size = data.data[i].bucket_size; + print.size = data.data[i].size; + print.fancy = data.data[i].fancy; + + print(*stream); + } +} + +void +Text::visit(const FormulaData &data) +{ + visit((const VectorData &)data); +} + +/* namespace Stats */ } diff --git a/src/base/stats/text.hh b/src/base/stats/text.hh new file mode 100644 index 000000000..b3faf5ad5 --- /dev/null +++ b/src/base/stats/text.hh @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_STATS_TEXT_HH__ +#define __BASE_STATS_TEXT_HH__ + +#include <iosfwd> +#include <string> + +#include "base/stats/output.hh" + +namespace Stats { + +class Text : public Output +{ + protected: + bool mystream; + std::ostream *stream; + + protected: + bool noOutput(const StatData &data); + + public: + bool compat; + bool descriptions; + + public: + Text(); + Text(std::ostream &stream); + Text(const std::string &file); + ~Text(); + + void open(std::ostream &stream); + void open(const std::string &file); + + // Implement Visit + virtual void visit(const ScalarData &data); + virtual void visit(const VectorData &data); + virtual void visit(const DistData &data); + virtual void visit(const VectorDistData &data); + virtual void visit(const Vector2dData &data); + virtual void visit(const FormulaData &data); + + // Implement Output + virtual bool valid() const; + virtual void output(); +}; + +/* namespace Stats */ } + +#endif // __BASE_STATS_TEXT_HH__ diff --git a/src/base/stats/types.hh b/src/base/stats/types.hh new file mode 100644 index 000000000..b64e8fb17 --- /dev/null +++ b/src/base/stats/types.hh @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_STATS_TYPES_HH__ +#define __BASE_STATS_TYPES_HH__ + +#include <vector> +#include "sim/host.hh" + +namespace Stats { + +/** All counters are of 64-bit values. */ +typedef double Counter; +/** vector of counters. */ +typedef std::vector<Counter> VCounter; + +/** All results are doubles. */ +typedef double Result; +/** vector of results. */ +typedef std::vector<Result> VResult; + +/* namespace Stats */ } + +#endif // __BASE_STATS_TYPES_HH__ diff --git a/src/base/stats/visit.cc b/src/base/stats/visit.cc new file mode 100644 index 000000000..6680eaa09 --- /dev/null +++ b/src/base/stats/visit.cc @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include "base/stats/visit.hh" + +namespace Stats { + +Visit::Visit() +{} + +Visit::~Visit() +{} + +/* namespace Stats */ } diff --git a/src/base/stats/visit.hh b/src/base/stats/visit.hh new file mode 100644 index 000000000..0087c227c --- /dev/null +++ b/src/base/stats/visit.hh @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_STATS_VISIT_HH__ +#define __BASE_STATS_VISIT_HH__ + +#include <string> + +#include "base/time.hh" +#include "sim/host.hh" + +namespace Stats { + +class StatData; +class ScalarData; +class VectorData; +class DistDataData; +class DistData; +class VectorDistData; +class Vector2dData; +class FormulaData; + +struct Visit +{ + Visit(); + virtual ~Visit(); + + virtual void visit(const ScalarData &data) = 0; + virtual void visit(const VectorData &data) = 0; + virtual void visit(const DistData &data) = 0; + virtual void visit(const VectorDistData &data) = 0; + virtual void visit(const Vector2dData &data) = 0; + virtual void visit(const FormulaData &data) = 0; +}; + +/* namespace Stats */ } + +#endif // __BASE_STATS_VISIT_HH__ diff --git a/src/base/str.cc b/src/base/str.cc new file mode 100644 index 000000000..0a517dff5 --- /dev/null +++ b/src/base/str.cc @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <ctype.h> + +#include <cstring> +#include <iostream> +#include <string> +#include <vector> + +#include "base/intmath.hh" +#include "base/str.hh" + +using namespace std; + +bool +split_first(const string &s, string &lhs, string &rhs, char c) +{ + string::size_type offset = s.find(c); + if (offset == string::npos) { + lhs = s; + rhs = ""; + return false; + } + + lhs = s.substr(0, offset); + rhs = s.substr(offset + 1); + return true; +} + +bool +split_last(const string &s, string &lhs, string &rhs, char c) +{ + string::size_type offset = s.rfind(c); + if (offset == string::npos) { + lhs = s; + rhs = ""; + return false; + } + + lhs = s.substr(0, offset); + rhs = s.substr(offset + 1); + return true; +} + +void +tokenize(vector<string>& v, const string &s, char token, bool ignore) +{ + string::size_type first = 0; + string::size_type last = s.find_first_of(token); + + if (s.empty()) + return; + + if (ignore && last == first) { + while (last == first) + last = s.find_first_of(token, ++first); + + if (last == string::npos) { + if (first != s.size()) + v.push_back(s.substr(first)); + return; + } + } + + while (last != string::npos) { + v.push_back(s.substr(first, last - first)); + + if (ignore) { + first = s.find_first_not_of(token, last + 1); + + if (first == string::npos) + return; + } else + first = last + 1; + + last = s.find_first_of(token, first); + } + + v.push_back(s.substr(first)); +} + +/** + * @todo This function will not handle the smallest negative decimal + * value for a signed type + */ + +template <class T> +inline bool +__to_number(string value, T &retval) +{ + static const T maxnum = ((T)-1); + static const bool sign = maxnum < 0; + static const int bits = sizeof(T) * 8; + static const T hexmax = maxnum & (((T)1 << (bits - 4 - sign)) - 1); + static const T octmax = maxnum & (((T)1 << (bits - 3 - sign)) - 1); + static const T signmax = + (sign) ? maxnum & (((T)1 << (bits - 1)) - 1) : maxnum; + static const T decmax = signmax / 10; + +#if 0 + cout << "maxnum = 0x" << hex << (unsigned long long)maxnum << "\n" + << "sign = 0x" << hex << (unsigned long long)sign << "\n" + << "hexmax = 0x" << hex << (unsigned long long)hexmax << "\n" + << "octmax = 0x" << hex << (unsigned long long)octmax << "\n" + << "signmax = 0x" << hex << (unsigned long long)signmax << "\n" + << "decmax = 0x" << hex << (unsigned long long)decmax << "\n"; +#endif + + eat_white(value); + + bool negative = false; + bool hex = false; + bool oct = false; + int last = value.size() - 1; + retval = 0; + int i = 0; + + char c = value[i]; + if (!isDec(c)) { + if (c == '-' && sign) + negative = true; + else + return false; + } + else { + retval += c - '0'; + if (last == 0) return true; + } + + if (c == '0') + oct = true; + + c = value[++i]; + if (oct) { + if (sign && negative) + return false; + + if (!isOct(c)) { + if (c == 'X' || c == 'x') { + hex = true; + oct = false; + } else + return false; + } + else + retval += c - '0'; + } else if (!isDec(c)) + goto multiply; + else { + if (sign && negative && c == '0') + return false; + + retval *= 10; + retval += c - '0'; + if (last == 1) { + if (sign && negative) retval = -retval; + return true; + } + } + + if (hex) { + if (last == 1) + return false; + + for (i = 2; i <= last ; i++) { + c = value[i]; + if (!isHex(c)) + return false; + + if (retval > hexmax) return false; + retval *= 16; + retval += hex2Int(c); + } + return true; + } else if (oct) { + for (i = 2; i <= last ; i++) { + c = value[i]; + if (!isOct(c)) + return false; + + if (retval > octmax) return false; + retval *= 8; + retval += (c - '0'); + } + return true; + } + + for (i = 2; i < last ; i++) { + c = value[i]; + if (!isDec(c)) + goto multiply; + + if (retval > decmax) return false; + bool atmax = retval == decmax; + retval *= 10; + retval += c - '0'; + if (atmax && retval < decmax) return false; + if (sign && (retval & ((T)1 << (sizeof(T) * 8 - 1)))) + return false; + } + + c = value[last]; + if (isDec(c)) { + + if (retval > decmax) return false; + bool atmax = retval == decmax; + retval *= 10; + retval += c - '0'; + if (atmax && retval < decmax) return false; + if (sign && negative) { + if ((retval & ((T)1 << (sizeof(T) * 8 - 1))) && + retval >= (T)-signmax) + return false; + retval = -retval; + } + else + if (sign && (retval & ((T)1 << ((sizeof(T) * 8) - 1)))) + return false; + return true; + } + + multiply: + signed long long mult = 1; + T val; + switch (c) { + case 'k': + case 'K': + if (i != last) return false; + mult = 1024; + val = signmax / mult; + break; + case 'm': + case 'M': + if (i != last) return false; + mult = 1024 * 1024; + val = signmax / mult; + break; + case 'g': + case 'G': + if (i != last) return false; + mult = 1024 * 1024 * 1024; + val = signmax / mult; + break; + case 'e': + case 'E': + if (i >= last) return false; + + mult = 0; + for (i++; i <= last; i++) { + c = value[i]; + if (!isDec(c)) + return false; + + mult *= 10; + mult += c - '0'; + } + + for (i = 0; i < mult; i++) { + if (retval > signmax / 10) + return false; + retval *= 10; + if (sign && (retval & ((T)1 << (sizeof(T) * 8 - 1)))) + return false; + } + if (sign && negative) { + if ((retval & ((T)1 << (sizeof(T) * 8 - 1))) && + retval >= (T)-signmax) + return false; + retval = -retval; + } + else + if (sign && (retval & ((T)1 << ((sizeof(T) * 8) - 1)))) + return false; + + return true; + + default: + return false; + } + + if (sign && negative) + return false; + + if (mult > (unsigned long long)signmax) + return false; + + if (retval > val) + return false; + + retval *= mult; + + return true; +} + +#define STN(type) \ +template<> \ +bool to_number<type>(const string &value, type &retval) \ +{ return __to_number(value, retval); } + +STN(unsigned long long); +STN(signed long long); +STN(unsigned long); +STN(signed long); +STN(unsigned int); +STN(signed int); +STN(unsigned short); +STN(signed short); +STN(unsigned char); +STN(signed char); + +template<> +bool to_number<bool>(const string &value, bool &retval) +{ + string lowered = to_lower(value); + + if (value == "0") { + retval = false; + return true; + } + + if (value == "1"){ + retval = true; + return true; + } + + if (lowered == "false") { + retval = false; + return true; + } + + if (lowered == "true"){ + retval = true; + return true; + } + + if (lowered == "no") { + retval = false; + return true; + } + + if (lowered == "yes"){ + retval = true; + return true; + } + + return false; +} diff --git a/src/base/str.hh b/src/base/str.hh new file mode 100644 index 000000000..08953caf3 --- /dev/null +++ b/src/base/str.hh @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __STR_HH__ +#define __STR_HH__ + +#include <sstream> +#include <string> +#include <vector> + +#include <ctype.h> + +template<class> class Hash; +template<> +class Hash<std::string> { +public: + unsigned operator()(const std::string &s) { + std::string::const_iterator i = s.begin(); + std::string::const_iterator end = s.end(); + unsigned hash = 5381; + + while (i < end) + hash = ((hash << 5) + hash) + *i++; + + return hash; + } +}; + +inline void +eat_lead_white(std::string &s) +{ + std::string::size_type off = s.find_first_not_of(' '); + if (off != std::string::npos) { + std::string::iterator begin = s.begin(); + s.erase(begin, begin + off); + } +} + +inline void +eat_end_white(std::string &s) +{ + std::string::size_type off = s.find_last_not_of(' '); + if (off != std::string::npos) + s.erase(s.begin() + off + 1, s.end()); +} + +inline void +eat_white(std::string &s) +{ + eat_lead_white(s); + eat_end_white(s); +} + +inline std::string +to_lower(const std::string &s) +{ + std::string lower; + int len = s.size(); + + lower.reserve(len); + + for (int i = 0; i < len; ++i) + lower.push_back(tolower(s[i])); + + return lower; +} + +// Split the string s into lhs and rhs on the first occurence of the +// character c. +bool +split_first(const std::string &s, std::string &lhs, std::string &rhs, char c); + +// Split the string s into lhs and rhs on the last occurence of the +// character c. +bool +split_last(const std::string &s, std::string &lhs, std::string &rhs, char c); + +// Tokenize the string <s> splitting on the character <token>, and +// place the result in the string vector <vector>. If <ign> is true, +// then empty result strings (due to trailing tokens, or consecutive +// tokens) are skipped. +void +tokenize(std::vector<std::string> &vector, const std::string &s, + char token, bool ign = true); + +template <class T> bool +to_number(const std::string &value, T &retval); + +template <class T> +inline std::string +to_string(const T &value) +{ + std::stringstream str; + str << value; + return str.str(); +} + +// Put quotes around string arg if it contains spaces. +inline std::string +quote(const std::string &s) +{ + std::string ret; + bool quote = s.find(' ') != std::string::npos; + + if (quote) + ret = '"'; + + ret += s; + + if (quote) + ret += '"'; + + return ret; +} + +#endif //__STR_HH__ diff --git a/src/base/time.cc b/src/base/time.cc new file mode 100644 index 000000000..cbc7256ee --- /dev/null +++ b/src/base/time.cc @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <time.h> +#include <iostream> +#include <string> + +#include "base/time.hh" + +using namespace std; + +struct _timeval +{ + timeval tv; +}; + +double +convert(const timeval &tv) +{ + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; +} + +Time::Time(bool set_now) +{ + time = new _timeval; + if (set_now) + set(); +} + +Time::Time(const timeval &val) +{ + time = new _timeval; + set(val); +} + +Time::Time(const Time &val) +{ + time = new _timeval; + set(val.get()); +} + +Time::~Time() +{ + delete time; +} + +const timeval & +Time::get() const +{ + return time->tv; +} + +void +Time::set() +{ + ::gettimeofday(&time->tv, NULL); +} + +void +Time::set(const timeval &tv) +{ + memcpy(&time->tv, &tv, sizeof(timeval)); +} + +double +Time::operator()() const +{ + return convert(get()); +} + +string +Time::date(string format) const +{ + const timeval &tv = get(); + time_t sec = tv.tv_sec; + char buf[256]; + + if (format.empty()) { + ctime_r(&sec, buf); + buf[24] = '\0'; + return buf; + } + + struct tm *tm = localtime(&sec); + strftime(buf, sizeof(buf), format.c_str(), tm); + return buf; +} + +ostream & +operator<<(ostream &out, const Time &start) +{ + out << start.date(); + return out; +} + +Time +operator-(const Time &l, const Time &r) +{ + timeval tv; + timersub(&l.get(), &r.get(), &tv); + return tv; +} + +const Time Time::start(true); diff --git a/src/base/time.hh b/src/base/time.hh new file mode 100644 index 000000000..24e8a8a53 --- /dev/null +++ b/src/base/time.hh @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +#ifndef __SIM_TIME_HH__ +#define __SIM_TIME_HH__ + +#include <sys/time.h> + +#include <iosfwd> +#include <string> + +struct _timeval; + +class Time +{ + protected: + mutable _timeval *time; + + public: + explicit Time(bool set_now = false); + Time(const timeval &val); + Time(const Time &val); + ~Time(); + + void set(); + const timeval &get() const; + void set(const timeval &val); + + double operator()() const; + std::string date(std::string format = "") const; + + public: + static const Time start; +}; + +Time operator-(const Time &l, const Time &r); + +std::ostream &operator<<(std::ostream &out, const Time &time); + +#endif // __SIM_TIME_HH__ diff --git a/src/base/timebuf.hh b/src/base/timebuf.hh new file mode 100644 index 000000000..1d0de8278 --- /dev/null +++ b/src/base/timebuf.hh @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Kevin Lim + */ + +#ifndef __BASE_TIMEBUF_HH__ +#define __BASE_TIMEBUF_HH__ + +#include <cassert> +#include <vector> + +template <class T> +class TimeBuffer +{ + protected: + int past; + int future; + int size; + + char *data; + std::vector<char *> index; + int base; + + void valid(int idx) + { + assert (idx >= -past && idx <= future); + } + + public: + friend class wire; + class wire + { + friend class TimeBuffer; + protected: + TimeBuffer<T> *buffer; + int index; + + void set(int idx) + { + buffer->valid(idx); + index = idx; + } + + wire(TimeBuffer<T> *buf, int i) + : buffer(buf), index(i) + { } + + public: + wire() + { } + + wire(const wire &i) + : buffer(i.buffer), index(i.index) + { } + + const wire &operator=(const wire &i) + { + buffer = i.buffer; + set(i.index); + return *this; + } + + const wire &operator=(int idx) + { + set(idx); + return *this; + } + + const wire &operator+=(int offset) + { + set(index + offset); + return *this; + } + + const wire &operator-=(int offset) + { + set(index - offset); + return *this; + } + + wire &operator++() + { + set(index + 1); + return *this; + } + + wire &operator++(int) + { + int i = index; + set(index + 1); + return wire(this, i); + } + + wire &operator--() + { + set(index - 1); + return *this; + } + + wire &operator--(int) + { + int i = index; + set(index - 1); + return wire(this, i); + } + T &operator*() const { return *buffer->access(index); } + T *operator->() const { return buffer->access(index); } + }; + + + public: + TimeBuffer(int p, int f) + : past(p), future(f), size(past + future + 1), + data(new char[size * sizeof(T)]), index(size), base(0) + { + assert(past >= 0 && future >= 0); + char *ptr = data; + for (int i = 0; i < size; i++) { + index[i] = ptr; + memset(ptr, 0, sizeof(T)); + new (ptr) T; + ptr += sizeof(T); + } + } + + TimeBuffer() + : data(NULL) + { + } + + ~TimeBuffer() + { + for (int i = 0; i < size; ++i) + (reinterpret_cast<T *>(index[i]))->~T(); + delete [] data; + } + + void + advance() + { + if (++base >= size) + base = 0; + + int ptr = base + future; + if (ptr >= size) + ptr -= size; + (reinterpret_cast<T *>(index[ptr]))->~T(); + memset(index[ptr], 0, sizeof(T)); + new (index[ptr]) T; + } + + T *access(int idx) + { + //Need more complex math here to calculate index. + valid(idx); + + int vector_index = idx + base; + if (vector_index >= size) { + vector_index -= size; + } else if (vector_index < 0) { + vector_index += size; + } + + return reinterpret_cast<T *>(index[vector_index]); + } + + T &operator[](int idx) + { + //Need more complex math here to calculate index. + valid(idx); + + int vector_index = idx + base; + if (vector_index >= size) { + vector_index -= size; + } else if (vector_index < 0) { + vector_index += size; + } + + return reinterpret_cast<T &>(*index[vector_index]); + } + + wire getWire(int idx) + { + valid(idx); + + return wire(this, idx); + } + + wire zero() + { + return wire(this, 0); + } + + int getSize() + { + return size; + } +}; + +#endif // __BASE_TIMEBUF_HH__ + diff --git a/src/base/trace.cc b/src/base/trace.cc new file mode 100644 index 000000000..9fa615f4d --- /dev/null +++ b/src/base/trace.cc @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#include <ctype.h> +#include <fstream> +#include <iostream> +#include <list> +#include <string> +#include <vector> + +#include "base/misc.hh" +#include "base/trace.hh" +#include "base/str.hh" + +using namespace std; + +namespace Trace { +const string DefaultName("global"); +FlagVec flags(NumFlags, false); + +// +// This variable holds the output stream for debug information. Other +// than setting up/redirecting this stream, do *NOT* reference this +// directly; use DebugOut() (see below) to access this stream for +// output. +// +ostream *dprintf_stream = &cerr; + +ObjectMatch ignore; + +Log theLog; + +Log::Log() +{ + size = 0; + buffer = NULL; +} + + +void +Log::init(int _size) +{ + if (buffer != NULL) { + fatal("Trace::Log::init called twice!"); + } + + size = _size; + + buffer = new Record *[size]; + + for (int i = 0; i < size; ++i) { + buffer[i] = NULL; + } + + nextRecPtr = &buffer[0]; + wrapRecPtr = &buffer[size]; +} + + +Log::~Log() +{ + for (int i = 0; i < size; ++i) { + delete buffer[i]; + } + + delete [] buffer; +} + + +void +Log::append(Record *rec) +{ + // dump record to output stream if there's one open + if (dprintf_stream != NULL) { + rec->dump(*dprintf_stream); + } else { + rec->dump(cout); + } + + // no buffering: justget rid of it now + if (buffer == NULL) { + delete rec; + return; + } + + Record *oldRec = *nextRecPtr; + + if (oldRec != NULL) { + // log has wrapped: overwrite + delete oldRec; + } + + *nextRecPtr = rec; + + if (++nextRecPtr == wrapRecPtr) { + nextRecPtr = &buffer[0]; + } +} + + +void +Log::dump(ostream &os) +{ + if (buffer == NULL) { + return; + } + + Record **bufPtr = nextRecPtr; + + if (*bufPtr == NULL) { + // next record slot is empty: log must not be full yet. + // start dumping from beginning of buffer + bufPtr = buffer; + } + + do { + Record *rec = *bufPtr; + + rec->dump(os); + + if (++bufPtr == wrapRecPtr) { + bufPtr = &buffer[0]; + } + } while (bufPtr != nextRecPtr); +} + +PrintfRecord::~PrintfRecord() +{ + delete &args; +} + +void +PrintfRecord::dump(ostream &os) +{ + string fmt = ""; + + if (!name.empty()) { + fmt = "%s: " + fmt; + args.prepend(name); + } + + if (cycle != (Tick)-1) { + fmt = "%7d: " + fmt; + args.prepend(cycle); + } + + fmt += format; + + args.dump(os, fmt); + os.flush(); +} + +DataRecord::DataRecord(Tick _cycle, const string &_name, + const void *_data, int _len) + : Record(_cycle), name(_name), len(_len) +{ + data = new uint8_t[len]; + memcpy(data, _data, len); +} + +DataRecord::~DataRecord() +{ + delete [] data; +} + +void +DataRecord::dump(ostream &os) +{ + int c, i, j; + + for (i = 0; i < len; i += 16) { + ccprintf(os, "%d: %s: %08x ", cycle, name, i); + c = len - i; + if (c > 16) c = 16; + + for (j = 0; j < c; j++) { + ccprintf(os, "%02x ", data[i + j] & 0xff); + if ((j & 0xf) == 7 && j > 0) + ccprintf(os, " "); + } + + for (; j < 16; j++) + ccprintf(os, " "); + ccprintf(os, " "); + + for (j = 0; j < c; j++) { + int ch = data[i + j] & 0x7f; + ccprintf(os, + "%c", (char)(isprint(ch) ? ch : ' ')); + } + + ccprintf(os, "\n"); + + if (c < 16) + break; + } +} +} // namespace Trace + +// +// Returns the current output stream for debug information. As a +// wrapper around Trace::dprintf_stream, this handles cases where debug +// information is generated in the process of parsing .ini options, +// before we process the option that sets up the debug output stream +// itself. +// +std::ostream & +DebugOut() +{ + return *Trace::dprintf_stream; +} + +///////////////////////////////////////////// +// +// C-linkage functions for invoking from gdb +// +///////////////////////////////////////////// + +// +// Dump trace buffer to specified file (cout if NULL) +// +void +dumpTrace(const char *filename) +{ + if (filename != NULL) { + ofstream out(filename); + Trace::theLog.dump(out); + out.close(); + } + else { + Trace::theLog.dump(cout); + } +} + + +// +// Turn on/off trace output to cerr. Typically used when trace output +// is only going to circular buffer, but you want to see what's being +// sent there as you step through some code in gdb. This uses the +// same facility as the "trace to file" feature, and will print error +// messages rather than clobbering an existing ostream pointer. +// +void +echoTrace(bool on) +{ + if (on) { + if (Trace::dprintf_stream != NULL) { + cerr << "Already echoing trace to a file... go do a 'tail -f'" + << " on that file instead." << endl; + } else { + Trace::dprintf_stream = &cerr; + } + } else { + if (Trace::dprintf_stream != &cerr) { + cerr << "Not echoing trace to cerr." << endl; + } else { + Trace::dprintf_stream = NULL; + } + } +} + +void +printTraceFlags() +{ + using namespace Trace; + for (int i = 0; i < numFlagStrings; ++i) + if (flags[i]) + cprintf("%s\n", flagStrings[i]); +} + +void +tweakTraceFlag(const char *string, bool value) +{ + using namespace Trace; + std::string str(string); + + for (int i = 0; i < numFlagStrings; ++i) { + if (str != flagStrings[i]) + continue; + + int idx = i; + + if (idx < NumFlags) { + flags[idx] = value; + } else { + idx -= NumFlags; + if (idx >= NumCompoundFlags) { + ccprintf(cerr, "Invalid compound flag"); + return; + } + + const Flags *flagVec = compoundFlags[idx]; + + for (int j = 0; flagVec[j] != -1; ++j) { + if (flagVec[j] >= NumFlags) { + ccprintf(cerr, "Invalid compound flag"); + return; + } + flags[flagVec[j]] = value; + } + } + + cprintf("flag %s was %s\n", string, value ? "set" : "cleared"); + return; + } + + cprintf("could not find flag %s\n", string); +} + +void +setTraceFlag(const char *string) +{ + tweakTraceFlag(string, true); +} + +void +clearTraceFlag(const char *string) +{ + tweakTraceFlag(string, false); +} diff --git a/src/base/trace.hh b/src/base/trace.hh new file mode 100644 index 000000000..8df5dd893 --- /dev/null +++ b/src/base/trace.hh @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __BASE_TRACE_HH__ +#define __BASE_TRACE_HH__ + +#include <vector> + +#include "base/cprintf.hh" +#include "base/match.hh" +#include "sim/host.hh" +#include "sim/root.hh" + +#ifndef TRACING_ON +#ifndef NDEBUG +#define TRACING_ON 1 +#else +#define TRACING_ON 0 +#endif +#endif + +#include "base/traceflags.hh" + +namespace Trace { + + typedef std::vector<bool> FlagVec; + + extern FlagVec flags; + +#if TRACING_ON + const bool On = true; +#else + const bool On = false; +#endif + + inline bool + IsOn(int t) + { + return flags[t]; + + } + + void dump(const uint8_t *data, int count); + + class Record + { + protected: + Tick cycle; + + Record(Tick _cycle) + : cycle(_cycle) + { + } + + public: + virtual ~Record() {} + + virtual void dump(std::ostream &) = 0; + }; + + class PrintfRecord : public Record + { + private: + const char *format; + const std::string &name; + cp::ArgList &args; + + public: + PrintfRecord(const char *_format, cp::ArgList &_args, + Tick cycle, const std::string &_name) + : Record(cycle), format(_format), name(_name), args(_args) + { + } + + virtual ~PrintfRecord(); + + virtual void dump(std::ostream &); + }; + + class DataRecord : public Record + { + private: + const std::string &name; + uint8_t *data; + int len; + + public: + DataRecord(Tick cycle, const std::string &name, + const void *_data, int _len); + virtual ~DataRecord(); + + virtual void dump(std::ostream &); + }; + + class Log + { + private: + int size; // number of records in log + Record **buffer; // array of 'size' Record ptrs (circular buf) + Record **nextRecPtr; // next slot to use in buffer + Record **wrapRecPtr; // &buffer[size], for quick wrap check + + public: + + Log(); + ~Log(); + + void init(int _size); + + void append(Record *); // append trace record to log + void dump(std::ostream &); // dump contents to stream + }; + + extern Log theLog; + + extern ObjectMatch ignore; + + inline void + dprintf(const char *format, cp::ArgList &args, Tick cycle, + const std::string &name) + { + if (name.empty() || !ignore.match(name)) + theLog.append(new Trace::PrintfRecord(format, args, cycle, name)); + } + + inline void + dataDump(Tick cycle, const std::string &name, const void *data, int len) + { + theLog.append(new Trace::DataRecord(cycle, name, data, len)); + } + + extern const std::string DefaultName; +}; + +// This silly little class allows us to wrap a string in a functor +// object so that we can give a name() that DPRINTF will like +struct StringWrap +{ + std::string str; + StringWrap(const std::string &s) : str(s) {} + const std::string &operator()() const { return str; } +}; + +inline const std::string &name() { return Trace::DefaultName; } +std::ostream &DebugOut(); + +// +// DPRINTF is a debugging trace facility that allows one to +// selectively enable tracing statements. To use DPRINTF, there must +// be a function or functor called name() that returns a const +// std::string & in the current scope. +// +// If you desire that the automatic printing not occur, use DPRINTFR +// (R for raw) +// + +#if TRACING_ON + +#define DTRACE(x) (Trace::IsOn(Trace::x)) + +#define DCOUT(x) if (Trace::IsOn(Trace::x)) DebugOut() + +#define DDUMP(x, data, count) \ +do { \ + if (Trace::IsOn(Trace::x)) \ + Trace::dataDump(curTick, name(), data, count); \ +} while (0) + +#define __dprintf(cycle, name, format, args...) \ + Trace::dprintf(format, (*(new cp::ArgList), args), cycle, name) + +#define DPRINTF(x, args...) \ +do { \ + if (Trace::IsOn(Trace::x)) \ + __dprintf(curTick, name(), args, cp::ArgListNull()); \ +} while (0) + +#define DPRINTFR(x, args...) \ +do { \ + if (Trace::IsOn(Trace::x)) \ + __dprintf((Tick)-1, std::string(), args, cp::ArgListNull()); \ +} while (0) + +#define DPRINTFN(args...) \ +do { \ + __dprintf(curTick, name(), args, cp::ArgListNull()); \ +} while (0) + +#define DPRINTFNR(args...) \ +do { \ + __dprintf((Tick)-1, string(), args, cp::ArgListNull()); \ +} while (0) + +#else // !TRACING_ON + +#define DTRACE(x) (false) +#define DCOUT(x) if (0) DebugOut() +#define DPRINTF(x, args...) do {} while (0) +#define DPRINTFR(args...) do {} while (0) +#define DPRINTFN(args...) do {} while (0) +#define DPRINTFNR(args...) do {} while (0) +#define DDUMP(x, data, count) do {} while (0) + +#endif // TRACING_ON + +#endif // __BASE_TRACE_HH__ diff --git a/src/base/traceflags.py b/src/base/traceflags.py new file mode 100644 index 000000000..8e8153b68 --- /dev/null +++ b/src/base/traceflags.py @@ -0,0 +1,333 @@ +#!/usr/bin/env python + +# Copyright (c) 2004-2005 The Regents of The University of Michigan +# 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. +# +# Authors: Nathan Binkert +# Steve Reinhardt + +# +# This file generates the header and source files for the flags +# that control the tracing facility. +# + +import sys + +if len(sys.argv) != 2: + print "%s: Need argument (basename of cc/hh files)" % sys.argv[0] + sys.exit(1) + +hhfilename = sys.argv[1] + '.hh' +ccfilename = sys.argv[1] + '.cc' + +# +# The list of trace flags that can be used to condition DPRINTFs etc. +# To define a new flag, simply add it to this list. +# +baseFlags = [ + 'Activity', + 'AlphaConsole', + 'Annotate', + 'BADADDR', + 'BE', + 'BPredRAS', + 'Bus', + 'BusAddrRanges', + 'BusBridge', + 'Cache', + 'Chains', + 'Checker', + 'Clock', + 'Commit', + 'CommitRate', + 'Config', + 'Console', + 'ConsolePoll', + 'ConsoleVerbose', + 'Context', + 'Cycle', + 'DMA', + 'DMAReadVerbose', + 'DMAWriteVerbose', + 'DebugPrintf', + 'Decode', + 'DiskImage', + 'DiskImageRead', + 'DiskImageWrite', + 'DynInst', + 'Ethernet', + 'EthernetCksum', + 'EthernetDMA', + 'EthernetData', + 'EthernetDesc', + 'EthernetIntr', + 'EthernetPIO', + 'EthernetSM', + 'Event', + 'FE', + 'Fault', + 'Fetch', + 'Flow', + 'FreeList', + 'FullCPU', + 'GDBAcc', + 'GDBExtra', + 'GDBMisc', + 'GDBRead', + 'GDBRecv', + 'GDBSend', + 'GDBWrite', + 'HWPrefetch', + 'IBE', + 'IEW', + 'IIC', + 'IICMore', + 'IPI', + 'IQ', + 'ISP', + 'IdeCtrl', + 'IdeDisk', + 'InstExec', + 'Interrupt', + 'LSQ', + 'LSQUnit', + 'Loader', + 'MC146818', + 'MMU', + 'MSHR', + 'Mbox', + 'MemDepUnit', + 'O3CPU', + 'OzoneCPU', + 'OzoneLSQ', + 'PCEvent', + 'PCIA', + 'PCIDEV', + 'PciConfigAll', + 'Pipeline', + 'Printf', + 'ROB', + 'Regs', + 'Rename', + 'RenameMap', + 'SQL', + 'Sampler', + 'Scoreboard', + 'ScsiCtrl', + 'ScsiDisk', + 'ScsiNone', + 'Serialize', + 'SimpleCPU', + 'SimpleDisk', + 'SimpleDiskData', + 'Sparc', + 'Split', + 'Stack', + 'StatEvents', + 'Stats', + 'StoreSet', + 'Syscall', + 'SyscallVerbose', + 'TCPIP', + 'TLB', + 'Thread', + 'Timer', + 'Tsunami', + 'Uart', + 'VtoPhys', + 'WriteBarrier', + 'Writeback', + ] + +# +# "Compound" flags correspond to a set of base flags. These exist +# solely for convenience in setting them via the command line: if a +# compound flag is specified, all of the corresponding base flags are +# set. Compound flags cannot be used directly in DPRINTFs etc. +# To define a new compound flag, add a new entry to this hash +# following the existing examples. +# +compoundFlagMap = { + 'GDBAll' : [ 'GDBMisc', 'GDBAcc', 'GDBRead', 'GDBWrite', 'GDBSend', 'GDBRecv', 'GDBExtra' ], + 'ScsiAll' : [ 'ScsiDisk', 'ScsiCtrl', 'ScsiNone' ], + 'DiskImageAll' : [ 'DiskImage', 'DiskImageRead', 'DiskImageWrite' ], + 'EthernetAll' : [ 'Ethernet', 'EthernetPIO', 'EthernetDMA', 'EthernetData' , 'EthernetDesc', 'EthernetIntr', 'EthernetSM', 'EthernetCksum' ], + 'EthernetNoData' : [ 'Ethernet', 'EthernetPIO', 'EthernetDesc', 'EthernetIntr', 'EthernetSM', 'EthernetCksum' ], + 'IdeAll' : [ 'IdeCtrl', 'IdeDisk' ], + 'O3CPUAll' : [ 'Fetch', 'Decode', 'Rename', 'IEW', 'Commit', 'IQ', 'ROB', 'FreeList', 'RenameMap', 'LSQ', 'LSQUnit', 'StoreSet', 'MemDepUnit', 'DynInst', 'FullCPU', 'O3CPU', 'Activity','Scoreboard','Writeback'], + 'OzoneCPUAll' : [ 'BE', 'FE', 'IBE', 'OzoneLSQ', 'OzoneCPU'] +} + +############################################################# +# +# Everything below this point generates the appropriate C++ +# declarations and definitions for the trace flags. If you are simply +# adding or modifying flag definitions, you should not have to change +# anything below. +# + +import sys + +# extract just the compound flag names into a list +compoundFlags = [] +compoundFlags.extend(compoundFlagMap.keys()) +compoundFlags.sort() + +# +# First generate the header file. This defines the Flag enum +# and some extern declarations for the .cc file. +# +try: + hhfile = file(hhfilename, 'w') +except IOError, e: + sys.exit("can't open %s: %s" % (hhfilename, e)) + +# file header boilerplate +print >>hhfile, ''' +/* + * DO NOT EDIT THIS FILE! + * + * Automatically generated from traceflags.py + */ + +#ifndef __BASE_TRACE_FLAGS_HH__ +#define __BASE_TRACE_FLAGS_HH__ + +namespace Trace { + +enum Flags { +''', + +# Generate the enum. Base flags come first, then compound flags. +idx = 0 +for flag in baseFlags: + print >>hhfile, ' %s = %d,' % (flag, idx) + idx += 1 + +numBaseFlags = idx +print >>hhfile, ' NumFlags = %d,' % idx + +# put a comment in here to separate base from compound flags +print >>hhfile, ''' + // The remaining enum values are *not* valid indices for Trace::flags. + // They are "compound" flags, which correspond to sets of base + // flags, and are used only by TraceParamContext::setFlags(). +''', + +for flag in compoundFlags: + print >>hhfile, ' %s = %d,' % (flag, idx) + idx += 1 + +numCompoundFlags = idx - numBaseFlags +print >>hhfile, ' NumCompoundFlags = %d' % numCompoundFlags + +# trailer boilerplate +print >>hhfile, '''\ +}; // enum Flags + +// Array of strings for SimpleEnumParam +extern const char *flagStrings[]; +extern const int numFlagStrings; + +// Array of arraay pointers: for each compound flag, gives the list of +// base flags to set. Inidividual flag arrays are terminated by -1. +extern const Flags *compoundFlags[]; + +/* namespace Trace */ } + +#endif // __BASE_TRACE_FLAGS_HH__ +''', + +hhfile.close() + +# +# +# Print out .cc file with array definitions. +# +# +try: + ccfile = file(ccfilename, 'w') +except OSError, e: + sys.exit("can't open %s: %s" % (ccfilename, e)) + +# file header +print >>ccfile, ''' +/* + * DO NOT EDIT THIS FILE! + * + * Automatically generated from traceflags.pl. + */ + +#include "base/traceflags.hh" + +using namespace Trace; + +const char *Trace::flagStrings[] = +{ +''', + +# The string array is used by SimpleEnumParam to map the strings +# provided by the user to enum values. +for flag in baseFlags: + print >>ccfile, ' "%s",' % flag + +for flag in compoundFlags: + print >>ccfile, ' "%s",' % flag + +print >>ccfile, '};\n' + +numFlagStrings = len(baseFlags) + len(compoundFlags); + +print >>ccfile, 'const int Trace::numFlagStrings = %d;' % numFlagStrings +print >>ccfile + +# +# Now define the individual compound flag arrays. There is an array +# for each compound flag listing the component base flags. +# + +for flag in compoundFlags: + flags = compoundFlagMap[flag] + flags.append('(Flags)-1') + print >>ccfile, 'static const Flags %sMap[] =' % flag + print >>ccfile, '{ %s };' % (', '.join(flags)) + print >>ccfile + +# +# Finally the compoundFlags[] array maps the compound flags +# to their individual arrays/ +# +print >>ccfile, 'const Flags *Trace::compoundFlags[] =' +print >>ccfile, '{' + +for flag in compoundFlags: + print >>ccfile, ' %sMap,' % flag + +# file trailer +print >>ccfile, '};' + +ccfile.close() + diff --git a/src/base/userinfo.cc b/src/base/userinfo.cc new file mode 100644 index 000000000..62f7b7b5c --- /dev/null +++ b/src/base/userinfo.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <sys/types.h> +#include <pwd.h> +#include <unistd.h> + +#include <string> + +std::string +username() +{ + struct passwd *pwd = getpwuid(getuid()); + + return pwd->pw_name; +} diff --git a/src/base/userinfo.hh b/src/base/userinfo.hh new file mode 100644 index 000000000..a2838ab3e --- /dev/null +++ b/src/base/userinfo.hh @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BASE_USERINFO_HH__ +#define __BASE_USERINFO_HH__ + +#include <string> + +std::string username(); + +#endif // __BASE_USERINFO_HH__ diff --git a/src/cpu/SConscript b/src/cpu/SConscript new file mode 100644 index 000000000..2bb9a2399 --- /dev/null +++ b/src/cpu/SConscript @@ -0,0 +1,212 @@ +# -*- mode:python -*- + +# Copyright (c) 2006 The Regents of The University of Michigan +# 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. +# +# Authors: Steve Reinhardt + +import os +import os.path + +# Import build environment variable from SConstruct. +Import('env') + +################################################################# +# +# Generate StaticInst execute() method signatures. +# +# There must be one signature for each CPU model compiled in. +# Since the set of compiled-in models is flexible, we generate a +# header containing the appropriate set of signatures on the fly. +# +################################################################# + +# CPU model-specific data is contained in cpu_models.py +# Convert to SCons File node to get path handling +models_db = File('cpu_models.py') +# slurp in contents of file +execfile(models_db.srcnode().abspath) + +# Template for execute() signature. +exec_sig_template = ''' +virtual Fault execute(%s *xc, Trace::InstRecord *traceData) const = 0; +virtual Fault initiateAcc(%s *xc, Trace::InstRecord *traceData) const +{ panic("initiateAcc not defined!"); }; +virtual Fault completeAcc(Packet *pkt, %s *xc, + Trace::InstRecord *traceData) const +{ panic("completeAcc not defined!"); }; +''' + +mem_ini_sig_template = ''' +virtual Fault initiateAcc(%s *xc, Trace::InstRecord *traceData) const { panic("Not defined!"); }; +''' + +mem_comp_sig_template = ''' +virtual Fault completeAcc(uint8_t *data, %s *xc, Trace::InstRecord *traceData) const { panic("Not defined!"); return NoFault; }; +''' + +# Generate a temporary CPU list, including the CheckerCPU if +# it's enabled. This isn't used for anything else other than StaticInst +# headers. +temp_cpu_list = env['CPU_MODELS'][:] + +if env['USE_CHECKER']: + temp_cpu_list.append('CheckerCPU') + +# Generate header. +def gen_cpu_exec_signatures(target, source, env): + f = open(str(target[0]), 'w') + print >> f, ''' +#ifndef __CPU_STATIC_INST_EXEC_SIGS_HH__ +#define __CPU_STATIC_INST_EXEC_SIGS_HH__ +''' + for cpu in temp_cpu_list: + xc_type = CpuModel.dict[cpu].strings['CPU_exec_context'] + print >> f, exec_sig_template % (xc_type, xc_type, xc_type) + print >> f, ''' +#endif // __CPU_STATIC_INST_EXEC_SIGS_HH__ +''' + +# Generate string that gets printed when header is rebuilt +def gen_sigs_string(target, source, env): + return "Generating static_inst_exec_sigs.hh: " \ + + ', '.join(temp_cpu_list) + +# Add command to generate header to environment. +env.Command('static_inst_exec_sigs.hh', models_db, + Action(gen_cpu_exec_signatures, gen_sigs_string, + varlist = temp_cpu_list)) + +env.Depends('static_inst_exec_sigs.hh', Value(env['USE_CHECKER'])) +env.Depends('static_inst_exec_sigs.hh', Value(env['CPU_MODELS'])) + +# List of suppported CPUs by the Checker. Errors out if USE_CHECKER=True +# and one of these are not being used. +CheckerSupportedCPUList = ['O3CPU', 'OzoneCPU'] + +################################################################# +# +# Include CPU-model-specific files based on set of models +# specified in CPU_MODELS build option. +# +################################################################# + +# Keep a list of CPU models that support SMT +env['SMT_CPU_MODELS'] = [] + +sources = [] + +need_simple_base = False +if 'AtomicSimpleCPU' in env['CPU_MODELS']: + need_simple_base = True + sources += Split('simple/atomic.cc') + +if 'TimingSimpleCPU' in env['CPU_MODELS']: + need_simple_base = True + sources += Split('simple/timing.cc') + +if need_simple_base: + sources += Split('simple/base.cc') + +if 'FastCPU' in env['CPU_MODELS']: + sources += Split('fast/cpu.cc') + +need_bp_unit = False +if 'O3CPU' in env['CPU_MODELS']: + need_bp_unit = True + sources += SConscript('o3/SConscript', exports = 'env') + sources += Split(''' + o3/base_dyn_inst.cc + o3/bpred_unit.cc + o3/commit.cc + o3/decode.cc + o3/fetch.cc + o3/free_list.cc + o3/fu_pool.cc + o3/cpu.cc + o3/iew.cc + o3/inst_queue.cc + o3/lsq_unit.cc + o3/lsq.cc + o3/mem_dep_unit.cc + o3/rename.cc + o3/rename_map.cc + o3/rob.cc + o3/scoreboard.cc + o3/store_set.cc + ''') + if env['USE_CHECKER']: + sources += Split('o3/checker_builder.cc') + else: + env['SMT_CPU_MODELS'].append('O3CPU') # Checker doesn't support SMT right now + +if 'OzoneCPU' in env['CPU_MODELS']: + need_bp_unit = True + sources += Split(''' + ozone/base_dyn_inst.cc + ozone/bpred_unit.cc + ozone/cpu.cc + ozone/cpu_builder.cc + ozone/dyn_inst.cc + ozone/front_end.cc + ozone/lw_back_end.cc + ozone/lw_lsq.cc + ozone/rename_table.cc + ''') + if env['USE_CHECKER']: + sources += Split('ozone/checker_builder.cc') + +if need_bp_unit: + sources += Split(''' + o3/2bit_local_pred.cc + o3/btb.cc + o3/ras.cc + o3/tournament_pred.cc + ''') + +if env['USE_CHECKER']: + sources += Split('checker/cpu.cc') + checker_supports = False + for i in CheckerSupportedCPUList: + if i in env['CPU_MODELS']: + checker_supports = True + if not checker_supports: + print "Checker only supports CPU models", + for i in CheckerSupportedCPUList: + print i, + print ", please set USE_CHECKER=False or use one of those CPU models" + Exit(1) + + +# FullCPU sources are included from src/SConscript since they're not +# below this point in the file hierarchy. + +# Convert file names to SCons File objects. This takes care of the +# path relative to the top of the directory tree. +sources = [File(s) for s in sources] + +Return('sources') + diff --git a/src/cpu/activity.cc b/src/cpu/activity.cc new file mode 100644 index 000000000..9a0f6d98d --- /dev/null +++ b/src/cpu/activity.cc @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "base/timebuf.hh" +#include "cpu/activity.hh" + +ActivityRecorder::ActivityRecorder(int num_stages, int longest_latency, + int activity) + : activityBuffer(longest_latency, 0), longestLatency(longest_latency), + activityCount(activity), numStages(num_stages) +{ + stageActive = new bool[numStages]; + memset(stageActive, 0, numStages); +} + +void +ActivityRecorder::activity() +{ + // If we've already recorded activity for this cycle, we don't + // want to increment the count any more. + if (activityBuffer[0]) { + return; + } + + activityBuffer[0] = true; + + ++activityCount; + + DPRINTF(Activity, "Activity: %i\n", activityCount); +} + +void +ActivityRecorder::advance() +{ + // If there's a 1 in the slot that is about to be erased once the + // time buffer advances, then decrement the activityCount. + if (activityBuffer[-longestLatency]) { + --activityCount; + + assert(activityCount >= 0); + + DPRINTF(Activity, "Activity: %i\n", activityCount); + + if (activityCount == 0) { + DPRINTF(Activity, "No activity left!\n"); + } + } + + activityBuffer.advance(); +} + +void +ActivityRecorder::activateStage(const int idx) +{ + // Increment the activity count if this stage wasn't already active. + if (!stageActive[idx]) { + ++activityCount; + + stageActive[idx] = true; + + DPRINTF(Activity, "Activity: %i\n", activityCount); + } else { + DPRINTF(Activity, "Stage %i already active.\n", idx); + } + +// assert(activityCount < longestLatency + numStages + 1); +} + +void +ActivityRecorder::deactivateStage(const int idx) +{ + // Decrement the activity count if this stage was active. + if (stageActive[idx]) { + --activityCount; + + stageActive[idx] = false; + + DPRINTF(Activity, "Activity: %i\n", activityCount); + } else { + DPRINTF(Activity, "Stage %i already inactive.\n", idx); + } + + assert(activityCount >= 0); +} + +void +ActivityRecorder::reset() +{ + activityCount = 0; + memset(stageActive, 0, numStages); + for (int i = 0; i < longestLatency + 1; ++i) + activityBuffer.advance(); +} + +void +ActivityRecorder::dump() +{ + for (int i = 0; i <= longestLatency; ++i) { + cprintf("[Idx:%i %i] ", i, activityBuffer[-i]); + } + + cprintf("\n"); + + for (int i = 0; i < numStages; ++i) { + cprintf("[Stage:%i %i]\n", i, stageActive[i]); + } + + cprintf("\n"); + + cprintf("Activity count: %i\n", activityCount); +} + +void +ActivityRecorder::validate() +{ + int count = 0; + for (int i = 0; i <= longestLatency; ++i) { + if (activityBuffer[-i]) { + count++; + } + } + + for (int i = 0; i < numStages; ++i) { + if (stageActive[i]) { + count++; + } + } + + assert(count == activityCount); +} diff --git a/src/cpu/activity.hh b/src/cpu/activity.hh new file mode 100644 index 000000000..e99927339 --- /dev/null +++ b/src/cpu/activity.hh @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_ACTIVITY_HH__ +#define __CPU_ACTIVITY_HH__ + +#include "base/timebuf.hh" +#include "base/trace.hh" + +/** + * ActivityRecorder helper class that informs the CPU if it can switch + * over to being idle or not. It works by having a time buffer as + * long as any time buffer in the CPU, and the CPU and all of its + * stages inform the ActivityRecorder when they write to any time + * buffer. The ActivityRecorder marks a 1 in the "0" slot of the time + * buffer any time a stage writes to a time buffer, and it advances + * its time buffer at the same time as all other stages. The + * ActivityRecorder also records if a stage has activity to do next + * cycle. The recorder keeps a count of these two. Thus any time the + * count is non-zero, there is either communication still in flight, + * or activity that still must be done, meaning that the CPU can not + * idle. If count is zero, then the CPU can safely idle as it has no + * more outstanding work to do. + */ +class ActivityRecorder { + public: + ActivityRecorder(int num_stages, int longest_latency, int count); + + /** Records that there is activity this cycle. */ + void activity(); + + /** Advances the activity buffer, decrementing the activityCount + * if active communication just left the time buffer, and + * determining if there is no activity. + */ + void advance(); + + /** Marks a stage as active. */ + void activateStage(const int idx); + + /** Deactivates a stage. */ + void deactivateStage(const int idx); + + /** Returns how many things are active within the recorder. */ + int getActivityCount() { return activityCount; } + + /** Sets the count to a starting value. Can be used to disable + * the idling option. + */ + void setActivityCount(int count) + { activityCount = count; } + + /** Returns if the CPU should be active. */ + bool active() { return activityCount; } + + /** Clears the time buffer and the activity count. */ + void reset(); + + /** Debug function to dump the contents of the time buffer. */ + void dump(); + + /** Debug function to ensure that the activity count matches the + * contents of the time buffer. + */ + void validate(); + + private: + /** Time buffer that tracks if any cycles has active communication + * in them. It should be as long as the longest communication + * latency in the system. Each time any time buffer is written, + * the activity buffer should also be written to. The + * activityBuffer is advanced along with all the other time + * buffers, so it should have a 1 somewhere in it only if there + * is active communication in a time buffer. + */ + TimeBuffer<bool> activityBuffer; + + /** Longest latency time buffer in the CPU. */ + int longestLatency; + + /** Tracks how many stages and cycles of time buffer have + * activity. Stages increment this count when they switch to + * active, and decrement it when they switch to + * inactive. Whenever a cycle that previously had no information + * is written in the time buffer, this is incremented. When a + * cycle that had information exits the time buffer due to age, + * this count is decremented. When the count is 0, there is no + * activity in the CPU, and it can be descheduled. + */ + int activityCount; + + /** Number of stages that can be marked as active or inactive. */ + int numStages; + + /** Records which stages are active/inactive. */ + bool *stageActive; +}; + +#endif // __CPU_ACTIVITY_HH__ diff --git a/src/cpu/base.cc b/src/cpu/base.cc new file mode 100644 index 000000000..f00dad7d6 --- /dev/null +++ b/src/cpu/base.cc @@ -0,0 +1,428 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +#include <iostream> +#include <string> +#include <sstream> + +#include "base/cprintf.hh" +#include "base/loader/symtab.hh" +#include "base/misc.hh" +#include "base/output.hh" +#include "cpu/base.hh" +#include "cpu/cpuevent.hh" +#include "cpu/thread_context.hh" +#include "cpu/profile.hh" +#include "sim/param.hh" +#include "sim/process.hh" +#include "sim/sim_events.hh" +#include "sim/system.hh" + +#include "base/trace.hh" + +// Hack +#include "sim/stat_control.hh" + +using namespace std; + +vector<BaseCPU *> BaseCPU::cpuList; + +// This variable reflects the max number of threads in any CPU. Be +// careful to only use it once all the CPUs that you care about have +// been initialized +int maxThreadsPerCPU = 1; + +void +CPUProgressEvent::process() +{ + Counter temp = cpu->totalInstructions(); +#ifndef NDEBUG + double ipc = double(temp - lastNumInst) / (interval / cpu->cycles(1)); + + DPRINTFN("%s progress event, instructions committed: %lli, IPC: %0.8d\n", + cpu->name(), temp - lastNumInst, ipc); + ipc = 0.0; +#else + cprintf("%lli: %s progress event, instructions committed: %lli\n", + curTick, cpu->name(), temp - lastNumInst); +#endif + lastNumInst = temp; + schedule(curTick + interval); +} + +const char * +CPUProgressEvent::description() +{ + return "CPU Progress event"; +} + +#if FULL_SYSTEM +BaseCPU::BaseCPU(Params *p) + : MemObject(p->name), clock(p->clock), checkInterrupts(true), + params(p), number_of_threads(p->numberOfThreads), system(p->system) +#else +BaseCPU::BaseCPU(Params *p) + : MemObject(p->name), clock(p->clock), params(p), + number_of_threads(p->numberOfThreads), system(p->system) +#endif +{ +// currentTick = curTick; + DPRINTF(FullCPU, "BaseCPU: Creating object, mem address %#x.\n", this); + + // add self to global list of CPUs + cpuList.push_back(this); + + DPRINTF(FullCPU, "BaseCPU: CPU added to cpuList, mem address %#x.\n", + this); + + if (number_of_threads > maxThreadsPerCPU) + maxThreadsPerCPU = number_of_threads; + + // allocate per-thread instruction-based event queues + comInstEventQueue = new EventQueue *[number_of_threads]; + for (int i = 0; i < number_of_threads; ++i) + comInstEventQueue[i] = new EventQueue("instruction-based event queue"); + + // + // set up instruction-count-based termination events, if any + // + if (p->max_insts_any_thread != 0) + for (int i = 0; i < number_of_threads; ++i) + new SimLoopExitEvent(comInstEventQueue[i], p->max_insts_any_thread, + "a thread reached the max instruction count"); + + if (p->max_insts_all_threads != 0) { + // allocate & initialize shared downcounter: each event will + // decrement this when triggered; simulation will terminate + // when counter reaches 0 + int *counter = new int; + *counter = number_of_threads; + for (int i = 0; i < number_of_threads; ++i) + new CountedExitEvent(comInstEventQueue[i], + "all threads reached the max instruction count", + p->max_insts_all_threads, *counter); + } + + // allocate per-thread load-based event queues + comLoadEventQueue = new EventQueue *[number_of_threads]; + for (int i = 0; i < number_of_threads; ++i) + comLoadEventQueue[i] = new EventQueue("load-based event queue"); + + // + // set up instruction-count-based termination events, if any + // + if (p->max_loads_any_thread != 0) + for (int i = 0; i < number_of_threads; ++i) + new SimLoopExitEvent(comLoadEventQueue[i], p->max_loads_any_thread, + "a thread reached the max load count"); + + if (p->max_loads_all_threads != 0) { + // allocate & initialize shared downcounter: each event will + // decrement this when triggered; simulation will terminate + // when counter reaches 0 + int *counter = new int; + *counter = number_of_threads; + for (int i = 0; i < number_of_threads; ++i) + new CountedExitEvent(comLoadEventQueue[i], + "all threads reached the max load count", + p->max_loads_all_threads, *counter); + } + + if (p->stats_reset_inst != 0) { + Stats::SetupEvent(Stats::Reset, p->stats_reset_inst, 0, comInstEventQueue[0]); + cprintf("Stats reset event scheduled for %lli insts\n", + p->stats_reset_inst); + } + +#if FULL_SYSTEM + memset(interrupts, 0, sizeof(interrupts)); + intstatus = 0; +#endif + + functionTracingEnabled = false; + if (p->functionTrace) { + functionTraceStream = simout.find(csprintf("ftrace.%s", name())); + currentFunctionStart = currentFunctionEnd = 0; + functionEntryTick = p->functionTraceStart; + + if (p->functionTraceStart == 0) { + functionTracingEnabled = true; + } else { + Event *e = + new EventWrapper<BaseCPU, &BaseCPU::enableFunctionTrace>(this, + true); + e->schedule(p->functionTraceStart); + } + } +#if FULL_SYSTEM + profileEvent = NULL; + if (params->profile) + profileEvent = new ProfileEvent(this, params->profile); +#endif +} + +BaseCPU::Params::Params() +{ +#if FULL_SYSTEM + profile = false; +#endif + checker = NULL; +} + +void +BaseCPU::enableFunctionTrace() +{ + functionTracingEnabled = true; +} + +BaseCPU::~BaseCPU() +{ +} + +void +BaseCPU::init() +{ + if (!params->deferRegistration) + registerThreadContexts(); +} + +void +BaseCPU::startup() +{ +#if FULL_SYSTEM + if (!params->deferRegistration && profileEvent) + profileEvent->schedule(curTick); +#endif + + if (params->progress_interval) { + new CPUProgressEvent(&mainEventQueue, params->progress_interval, + this); + } +} + + +void +BaseCPU::regStats() +{ + using namespace Stats; + + numCycles + .name(name() + ".numCycles") + .desc("number of cpu cycles simulated") + ; + + int size = threadContexts.size(); + if (size > 1) { + for (int i = 0; i < size; ++i) { + stringstream namestr; + ccprintf(namestr, "%s.ctx%d", name(), i); + threadContexts[i]->regStats(namestr.str()); + } + } else if (size == 1) + threadContexts[0]->regStats(name()); + +#if FULL_SYSTEM +#endif +} + + +void +BaseCPU::registerThreadContexts() +{ + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + +#if FULL_SYSTEM + int id = params->cpu_id; + if (id != -1) + id += i; + + tc->setCpuId(system->registerThreadContext(tc, id)); +#else + tc->setCpuId(tc->getProcessPtr()->registerThreadContext(tc)); +#endif + } +} + + +void +BaseCPU::switchOut() +{ +// panic("This CPU doesn't support sampling!"); +#if FULL_SYSTEM + if (profileEvent && profileEvent->scheduled()) + profileEvent->deschedule(); +#endif +} + +void +BaseCPU::takeOverFrom(BaseCPU *oldCPU) +{ + assert(threadContexts.size() == oldCPU->threadContexts.size()); + + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *newTC = threadContexts[i]; + ThreadContext *oldTC = oldCPU->threadContexts[i]; + + newTC->takeOverFrom(oldTC); + + CpuEvent::replaceThreadContext(oldTC, newTC); + + assert(newTC->readCpuId() == oldTC->readCpuId()); +#if FULL_SYSTEM + system->replaceThreadContext(newTC, newTC->readCpuId()); +#else + assert(newTC->getProcessPtr() == oldTC->getProcessPtr()); + newTC->getProcessPtr()->replaceThreadContext(newTC, newTC->readCpuId()); +#endif + +// TheISA::compareXCs(oldXC, newXC); + } + +#if FULL_SYSTEM + for (int i = 0; i < TheISA::NumInterruptLevels; ++i) + interrupts[i] = oldCPU->interrupts[i]; + intstatus = oldCPU->intstatus; + checkInterrupts = oldCPU->checkInterrupts; + + for (int i = 0; i < threadContexts.size(); ++i) + threadContexts[i]->profileClear(); + + // The Sampler must take care of this! +// if (profileEvent) +// profileEvent->schedule(curTick); +#endif +} + + +#if FULL_SYSTEM +BaseCPU::ProfileEvent::ProfileEvent(BaseCPU *_cpu, int _interval) + : Event(&mainEventQueue), cpu(_cpu), interval(_interval) +{ } + +void +BaseCPU::ProfileEvent::process() +{ + for (int i = 0, size = cpu->threadContexts.size(); i < size; ++i) { + ThreadContext *tc = cpu->threadContexts[i]; + tc->profileSample(); + } + + schedule(curTick + interval); +} + +void +BaseCPU::post_interrupt(int int_num, int index) +{ + DPRINTF(Interrupt, "Interrupt %d:%d posted\n", int_num, index); + + if (int_num < 0 || int_num >= TheISA::NumInterruptLevels) + panic("int_num out of bounds\n"); + + if (index < 0 || index >= sizeof(uint64_t) * 8) + panic("int_num out of bounds\n"); + + checkInterrupts = true; + interrupts[int_num] |= 1 << index; + intstatus |= (ULL(1) << int_num); +} + +void +BaseCPU::clear_interrupt(int int_num, int index) +{ + DPRINTF(Interrupt, "Interrupt %d:%d cleared\n", int_num, index); + + if (int_num < 0 || int_num >= TheISA::NumInterruptLevels) + panic("int_num out of bounds\n"); + + if (index < 0 || index >= sizeof(uint64_t) * 8) + panic("int_num out of bounds\n"); + + interrupts[int_num] &= ~(1 << index); + if (interrupts[int_num] == 0) + intstatus &= ~(ULL(1) << int_num); +} + +void +BaseCPU::clear_interrupts() +{ + DPRINTF(Interrupt, "Interrupts all cleared\n"); + + memset(interrupts, 0, sizeof(interrupts)); + intstatus = 0; +} + + +void +BaseCPU::serialize(std::ostream &os) +{ + SERIALIZE_ARRAY(interrupts, TheISA::NumInterruptLevels); + SERIALIZE_SCALAR(intstatus); +} + +void +BaseCPU::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_ARRAY(interrupts, TheISA::NumInterruptLevels); + UNSERIALIZE_SCALAR(intstatus); +} + +#endif // FULL_SYSTEM + +void +BaseCPU::traceFunctionsInternal(Addr pc) +{ + if (!debugSymbolTable) + return; + + // if pc enters different function, print new function symbol and + // update saved range. Otherwise do nothing. + if (pc < currentFunctionStart || pc >= currentFunctionEnd) { + string sym_str; + bool found = debugSymbolTable->findNearestSymbol(pc, sym_str, + currentFunctionStart, + currentFunctionEnd); + + if (!found) { + // no symbol found: use addr as label + sym_str = csprintf("0x%x", pc); + currentFunctionStart = pc; + currentFunctionEnd = pc + 1; + } + + ccprintf(*functionTraceStream, " (%d)\n%d: %s", + curTick - functionEntryTick, curTick, sym_str); + functionEntryTick = curTick; + } +} + + +DEFINE_SIM_OBJECT_CLASS_NAME("BaseCPU", BaseCPU) diff --git a/src/cpu/base.hh b/src/cpu/base.hh new file mode 100644 index 000000000..2a3fd9b56 --- /dev/null +++ b/src/cpu/base.hh @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +#ifndef __CPU_BASE_HH__ +#define __CPU_BASE_HH__ + +#include <vector> + +#include "base/statistics.hh" +#include "config/full_system.hh" +#include "sim/eventq.hh" +#include "mem/mem_object.hh" +#include "arch/isa_traits.hh" + +class BranchPred; +class CheckerCPU; +class ThreadContext; +class System; +class Port; + +class CPUProgressEvent : public Event +{ + protected: + Tick interval; + Counter lastNumInst; + BaseCPU *cpu; + + public: + CPUProgressEvent(EventQueue *q, Tick ival, BaseCPU *_cpu) + : Event(q, Event::Stat_Event_Pri), interval(ival), lastNumInst(0), cpu(_cpu) + { schedule(curTick + interval); } + + void process(); + + virtual const char *description(); +}; + +class BaseCPU : public MemObject +{ + protected: + // CPU's clock period in terms of the number of ticks of curTime. + Tick clock; + + public: +// Tick currentTick; + inline Tick frequency() const { return Clock::Frequency / clock; } + inline Tick cycles(int numCycles) const { return clock * numCycles; } + inline Tick curCycle() const { return curTick / clock; } + +#if FULL_SYSTEM + protected: + uint64_t interrupts[TheISA::NumInterruptLevels]; + uint64_t intstatus; + + public: + virtual void post_interrupt(int int_num, int index); + virtual void clear_interrupt(int int_num, int index); + virtual void clear_interrupts(); + bool checkInterrupts; + + bool check_interrupt(int int_num) const { + if (int_num > TheISA::NumInterruptLevels) + panic("int_num out of bounds\n"); + + return interrupts[int_num] != 0; + } + + bool check_interrupts() const { return intstatus != 0; } + uint64_t intr_status() const { return intstatus; } + + class ProfileEvent : public Event + { + private: + BaseCPU *cpu; + int interval; + + public: + ProfileEvent(BaseCPU *cpu, int interval); + void process(); + }; + ProfileEvent *profileEvent; +#endif + + protected: + std::vector<ThreadContext *> threadContexts; + + public: + + /// Notify the CPU that the indicated context is now active. The + /// delay parameter indicates the number of ticks to wait before + /// executing (typically 0 or 1). + virtual void activateContext(int thread_num, int delay) {} + + /// Notify the CPU that the indicated context is now suspended. + virtual void suspendContext(int thread_num) {} + + /// Notify the CPU that the indicated context is now deallocated. + virtual void deallocateContext(int thread_num) {} + + /// Notify the CPU that the indicated context is now halted. + virtual void haltContext(int thread_num) {} + + public: + struct Params + { + std::string name; + int numberOfThreads; + bool deferRegistration; + Counter max_insts_any_thread; + Counter max_insts_all_threads; + Counter max_loads_any_thread; + Counter max_loads_all_threads; + Counter stats_reset_inst; + Tick clock; + bool functionTrace; + Tick functionTraceStart; + System *system; +#if FULL_SYSTEM + int cpu_id; + Tick profile; +#endif + Tick progress_interval; + BaseCPU *checker; + + Params(); + }; + + const Params *params; + + BaseCPU(Params *params); + virtual ~BaseCPU(); + + virtual void init(); + virtual void startup(); + virtual void regStats(); + + virtual void activateWhenReady(int tid) {}; + + void registerThreadContexts(); + + /// Prepare for another CPU to take over execution. When it is + /// is ready (drained pipe) it signals the sampler. + virtual void switchOut(); + + /// Take over execution from the given CPU. Used for warm-up and + /// sampling. + virtual void takeOverFrom(BaseCPU *); + + /** + * Number of threads we're actually simulating (<= SMT_MAX_THREADS). + * This is a constant for the duration of the simulation. + */ + int number_of_threads; + + /** + * Vector of per-thread instruction-based event queues. Used for + * scheduling events based on number of instructions committed by + * a particular thread. + */ + EventQueue **comInstEventQueue; + + /** + * Vector of per-thread load-based event queues. Used for + * scheduling events based on number of loads committed by + *a particular thread. + */ + EventQueue **comLoadEventQueue; + + System *system; + +#if FULL_SYSTEM + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +#endif + + /** + * Return pointer to CPU's branch predictor (NULL if none). + * @return Branch predictor pointer. + */ + virtual BranchPred *getBranchPred() { return NULL; }; + + virtual Counter totalInstructions() const { return 0; } + + // Function tracing + private: + bool functionTracingEnabled; + std::ostream *functionTraceStream; + Addr currentFunctionStart; + Addr currentFunctionEnd; + Tick functionEntryTick; + void enableFunctionTrace(); + void traceFunctionsInternal(Addr pc); + + protected: + void traceFunctions(Addr pc) + { + if (functionTracingEnabled) + traceFunctionsInternal(pc); + } + + private: + static std::vector<BaseCPU *> cpuList; //!< Static global cpu list + + public: + static int numSimulatedCPUs() { return cpuList.size(); } + static Counter numSimulatedInstructions() + { + Counter total = 0; + + int size = cpuList.size(); + for (int i = 0; i < size; ++i) + total += cpuList[i]->totalInstructions(); + + return total; + } + + public: + // Number of CPU cycles simulated + Stats::Scalar<> numCycles; +}; + +#endif // __CPU_BASE_HH__ diff --git a/src/cpu/base_dyn_inst.hh b/src/cpu/base_dyn_inst.hh new file mode 100644 index 000000000..926bfcbb2 --- /dev/null +++ b/src/cpu/base_dyn_inst.hh @@ -0,0 +1,732 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_BASE_DYN_INST_HH__ +#define __CPU_BASE_DYN_INST_HH__ + +#include <bitset> +#include <list> +#include <string> + +#include "arch/faults.hh" +#include "base/fast_alloc.hh" +#include "base/trace.hh" +#include "config/full_system.hh" +#include "cpu/exetrace.hh" +#include "cpu/inst_seq.hh" +#include "cpu/op_class.hh" +#include "cpu/static_inst.hh" +#include "mem/packet.hh" +#include "sim/system.hh" + +/** + * @file + * Defines a dynamic instruction context. + */ + +// Forward declaration. +class StaticInstPtr; + +template <class Impl> +class BaseDynInst : public FastAlloc, public RefCounted +{ + public: + // Typedef for the CPU. + typedef typename Impl::CPUType ImplCPU; + typedef typename ImplCPU::ImplState ImplState; + + // Binary machine instruction type. + typedef TheISA::MachInst MachInst; + // Extended machine instruction type + typedef TheISA::ExtMachInst ExtMachInst; + // Logical register index type. + typedef TheISA::RegIndex RegIndex; + // Integer register type. + typedef TheISA::IntReg IntReg; + // Floating point register type. + typedef TheISA::FloatReg FloatReg; + + // The DynInstPtr type. + typedef typename Impl::DynInstPtr DynInstPtr; + + // The list of instructions iterator type. + typedef typename std::list<DynInstPtr>::iterator ListIt; + + enum { + MaxInstSrcRegs = TheISA::MaxInstSrcRegs, /// Max source regs + MaxInstDestRegs = TheISA::MaxInstDestRegs, /// Max dest regs + }; + + /** The StaticInst used by this BaseDynInst. */ + StaticInstPtr staticInst; + + //////////////////////////////////////////// + // + // INSTRUCTION EXECUTION + // + //////////////////////////////////////////// + /** InstRecord that tracks this instructions. */ + Trace::InstRecord *traceData; + + /** + * Does a read to a given address. + * @param addr The address to read. + * @param data The read's data is written into this parameter. + * @param flags The request's flags. + * @return Returns any fault due to the read. + */ + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + /** + * Does a write to a given address. + * @param data The data to be written. + * @param addr The address to write to. + * @param flags The request's flags. + * @param res The result of the write (for load locked/store conditionals). + * @return Returns any fault due to the write. + */ + template <class T> + Fault write(T data, Addr addr, unsigned flags, + uint64_t *res); + + void prefetch(Addr addr, unsigned flags); + void writeHint(Addr addr, int size, unsigned flags); + Fault copySrcTranslate(Addr src); + Fault copy(Addr dest); + + /** @todo: Consider making this private. */ + public: + /** The sequence number of the instruction. */ + InstSeqNum seqNum; + + enum Status { + IqEntry, /// Instruction is in the IQ + RobEntry, /// Instruction is in the ROB + LsqEntry, /// Instruction is in the LSQ + Completed, /// Instruction has completed + ResultReady, /// Instruction has its result + CanIssue, /// Instruction can issue and execute + Issued, /// Instruction has issued + Executed, /// Instruction has executed + CanCommit, /// Instruction can commit + AtCommit, /// Instruction has reached commit + Committed, /// Instruction has committed + Squashed, /// Instruction is squashed + SquashedInIQ, /// Instruction is squashed in the IQ + SquashedInLSQ, /// Instruction is squashed in the LSQ + SquashedInROB, /// Instruction is squashed in the ROB + RecoverInst, /// Is a recover instruction + BlockingInst, /// Is a blocking instruction + ThreadsyncWait, /// Is a thread synchronization instruction + SerializeBefore, /// Needs to serialize on + /// instructions ahead of it + SerializeAfter, /// Needs to serialize instructions behind it + SerializeHandled, /// Serialization has been handled + NumStatus + }; + + /** The status of this BaseDynInst. Several bits can be set. */ + std::bitset<NumStatus> status; + + /** The thread this instruction is from. */ + short threadNumber; + + /** data address space ID, for loads & stores. */ + short asid; + + /** How many source registers are ready. */ + unsigned readyRegs; + + /** Pointer to the Impl's CPU object. */ + ImplCPU *cpu; + + /** Pointer to the thread state. */ + ImplState *thread; + + /** The kind of fault this instruction has generated. */ + Fault fault; + + /** The memory request. */ + Request *req; + + /** Pointer to the data for the memory access. */ + uint8_t *memData; + + /** The effective virtual address (lds & stores only). */ + Addr effAddr; + + /** The effective physical address. */ + Addr physEffAddr; + + /** Effective virtual address for a copy source. */ + Addr copySrcEffAddr; + + /** Effective physical address for a copy source. */ + Addr copySrcPhysEffAddr; + + /** The memory request flags (from translation). */ + unsigned memReqFlags; + + union Result { + uint64_t integer; +// float fp; + double dbl; + }; + + /** The result of the instruction; assumes for now that there's only one + * destination register. + */ + Result instResult; + + /** PC of this instruction. */ + Addr PC; + + /** Next non-speculative PC. It is not filled in at fetch, but rather + * once the target of the branch is truly known (either decode or + * execute). + */ + Addr nextPC; + + /** Next non-speculative NPC. Target PC for Mips or Sparc. */ + Addr nextNPC; + + /** Predicted next PC. */ + Addr predPC; + + /** Count of total number of dynamic instructions. */ + static int instcount; + +#ifdef DEBUG + void dumpSNList(); +#endif + + /** Whether or not the source register is ready. + * @todo: Not sure this should be here vs the derived class. + */ + bool _readySrcRegIdx[MaxInstSrcRegs]; + + public: + /** BaseDynInst constructor given a binary instruction. + * @param inst The binary instruction. + * @param PC The PC of the instruction. + * @param pred_PC The predicted next PC. + * @param seq_num The sequence number of the instruction. + * @param cpu Pointer to the instruction's CPU. + */ + BaseDynInst(ExtMachInst inst, Addr PC, Addr pred_PC, InstSeqNum seq_num, + ImplCPU *cpu); + + /** BaseDynInst constructor given a StaticInst pointer. + * @param _staticInst The StaticInst for this BaseDynInst. + */ + BaseDynInst(StaticInstPtr &_staticInst); + + /** BaseDynInst destructor. */ + ~BaseDynInst(); + + private: + /** Function to initialize variables in the constructors. */ + void initVars(); + + public: + /** Dumps out contents of this BaseDynInst. */ + void dump(); + + /** Dumps out contents of this BaseDynInst into given string. */ + void dump(std::string &outstring); + + /** Returns the fault type. */ + Fault getFault() { return fault; } + + /** Checks whether or not this instruction has had its branch target + * calculated yet. For now it is not utilized and is hacked to be + * always false. + * @todo: Actually use this instruction. + */ + bool doneTargCalc() { return false; } + + /** Returns the next PC. This could be the speculative next PC if it is + * called prior to the actual branch target being calculated. + */ + Addr readNextPC() { return nextPC; } + + /** Returns the next NPC. This could be the speculative next NPC if it is + * called prior to the actual branch target being calculated. + */ + Addr readNextNPC() { return nextNPC; } + + /** Set the predicted target of this current instruction. */ + void setPredTarg(Addr predicted_PC) { predPC = predicted_PC; } + + /** Returns the predicted target of the branch. */ + Addr readPredTarg() { return predPC; } + + /** Returns whether the instruction was predicted taken or not. */ + bool predTaken() +#if ISA_HAS_DELAY_SLOT + { return predPC != (nextPC + sizeof(MachInst)); } +#else + { return predPC != (PC + sizeof(MachInst)); } +#endif + + /** Returns whether the instruction mispredicted. */ + bool mispredicted() +#if ISA_HAS_DELAY_SLOT + { return predPC != nextNPC; } +#else + { return predPC != nextPC; } +#endif + // + // Instruction types. Forward checks to StaticInst object. + // + bool isNop() const { return staticInst->isNop(); } + bool isMemRef() const { return staticInst->isMemRef(); } + bool isLoad() const { return staticInst->isLoad(); } + bool isStore() const { return staticInst->isStore(); } + bool isStoreConditional() const + { return staticInst->isStoreConditional(); } + bool isInstPrefetch() const { return staticInst->isInstPrefetch(); } + bool isDataPrefetch() const { return staticInst->isDataPrefetch(); } + bool isCopy() const { return staticInst->isCopy(); } + bool isInteger() const { return staticInst->isInteger(); } + bool isFloating() const { return staticInst->isFloating(); } + bool isControl() const { return staticInst->isControl(); } + bool isCall() const { return staticInst->isCall(); } + bool isReturn() const { return staticInst->isReturn(); } + bool isDirectCtrl() const { return staticInst->isDirectCtrl(); } + bool isIndirectCtrl() const { return staticInst->isIndirectCtrl(); } + bool isCondCtrl() const { return staticInst->isCondCtrl(); } + bool isUncondCtrl() const { return staticInst->isUncondCtrl(); } + bool isCondDelaySlot() const { return staticInst->isCondDelaySlot(); } + bool isThreadSync() const { return staticInst->isThreadSync(); } + bool isSerializing() const { return staticInst->isSerializing(); } + bool isSerializeBefore() const + { return staticInst->isSerializeBefore() || status[SerializeBefore]; } + bool isSerializeAfter() const + { return staticInst->isSerializeAfter() || status[SerializeAfter]; } + bool isMemBarrier() const { return staticInst->isMemBarrier(); } + bool isWriteBarrier() const { return staticInst->isWriteBarrier(); } + bool isNonSpeculative() const { return staticInst->isNonSpeculative(); } + bool isQuiesce() const { return staticInst->isQuiesce(); } + bool isIprAccess() const { return staticInst->isIprAccess(); } + bool isUnverifiable() const { return staticInst->isUnverifiable(); } + + /** Temporarily sets this instruction as a serialize before instruction. */ + void setSerializeBefore() { status.set(SerializeBefore); } + + /** Clears the serializeBefore part of this instruction. */ + void clearSerializeBefore() { status.reset(SerializeBefore); } + + /** Checks if this serializeBefore is only temporarily set. */ + bool isTempSerializeBefore() { return status[SerializeBefore]; } + + /** Temporarily sets this instruction as a serialize after instruction. */ + void setSerializeAfter() { status.set(SerializeAfter); } + + /** Clears the serializeAfter part of this instruction.*/ + void clearSerializeAfter() { status.reset(SerializeAfter); } + + /** Checks if this serializeAfter is only temporarily set. */ + bool isTempSerializeAfter() { return status[SerializeAfter]; } + + /** Sets the serialization part of this instruction as handled. */ + void setSerializeHandled() { status.set(SerializeHandled); } + + /** Checks if the serialization part of this instruction has been + * handled. This does not apply to the temporary serializing + * state; it only applies to this instruction's own permanent + * serializing state. + */ + bool isSerializeHandled() { return status[SerializeHandled]; } + + /** Returns the opclass of this instruction. */ + OpClass opClass() const { return staticInst->opClass(); } + + /** Returns the branch target address. */ + Addr branchTarget() const { return staticInst->branchTarget(PC); } + + /** Returns the number of source registers. */ + int8_t numSrcRegs() const { return staticInst->numSrcRegs(); } + + /** Returns the number of destination registers. */ + int8_t numDestRegs() const { return staticInst->numDestRegs(); } + + // the following are used to track physical register usage + // for machines with separate int & FP reg files + int8_t numFPDestRegs() const { return staticInst->numFPDestRegs(); } + int8_t numIntDestRegs() const { return staticInst->numIntDestRegs(); } + + /** Returns the logical register index of the i'th destination register. */ + RegIndex destRegIdx(int i) const { return staticInst->destRegIdx(i); } + + /** Returns the logical register index of the i'th source register. */ + RegIndex srcRegIdx(int i) const { return staticInst->srcRegIdx(i); } + + /** Returns the result of an integer instruction. */ + uint64_t readIntResult() { return instResult.integer; } + + /** Returns the result of a floating point instruction. */ + float readFloatResult() { return (float)instResult.dbl; } + + /** Returns the result of a floating point (double) instruction. */ + double readDoubleResult() { return instResult.dbl; } + + /** Records an integer register being set to a value. */ + void setIntReg(const StaticInst *si, int idx, uint64_t val) + { + instResult.integer = val; + } + + /** Records an fp register being set to a value. */ + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) + { + if (width == 32) + instResult.fp = val; + else if (width == 64) + instResult.dbl = val; + else + panic("Unsupported width!"); + } + + /** Records an fp register being set to a value. */ + void setFloatReg(const StaticInst *si, int idx, FloatReg val) + { +// instResult.fp = val; + instResult.dbl = (double)val; + } + + /** Records an fp register being set to an integer value. */ + void setFloatRegBits(const StaticInst *si, int idx, uint64_t val, int width) + { + instResult.integer = val; + } + + /** Records an fp register being set to an integer value. */ + void setFloatRegBits(const StaticInst *si, int idx, uint64_t val) + { + instResult.integer = val; + } + + /** Records that one of the source registers is ready. */ + void markSrcRegReady(); + + /** Marks a specific register as ready. */ + void markSrcRegReady(RegIndex src_idx); + + /** Returns if a source register is ready. */ + bool isReadySrcRegIdx(int idx) const + { + return this->_readySrcRegIdx[idx]; + } + + /** Sets this instruction as completed. */ + void setCompleted() { status.set(Completed); } + + /** Returns whether or not this instruction is completed. */ + bool isCompleted() const { return status[Completed]; } + + /** Marks the result as ready. */ + void setResultReady() { status.set(ResultReady); } + + /** Returns whether or not the result is ready. */ + bool isResultReady() const { return status[ResultReady]; } + + /** Sets this instruction as ready to issue. */ + void setCanIssue() { status.set(CanIssue); } + + /** Returns whether or not this instruction is ready to issue. */ + bool readyToIssue() const { return status[CanIssue]; } + + /** Sets this instruction as issued from the IQ. */ + void setIssued() { status.set(Issued); } + + /** Returns whether or not this instruction has issued. */ + bool isIssued() const { return status[Issued]; } + + /** Sets this instruction as executed. */ + void setExecuted() { status.set(Executed); } + + /** Returns whether or not this instruction has executed. */ + bool isExecuted() const { return status[Executed]; } + + /** Sets this instruction as ready to commit. */ + void setCanCommit() { status.set(CanCommit); } + + /** Clears this instruction as being ready to commit. */ + void clearCanCommit() { status.reset(CanCommit); } + + /** Returns whether or not this instruction is ready to commit. */ + bool readyToCommit() const { return status[CanCommit]; } + + void setAtCommit() { status.set(AtCommit); } + + bool isAtCommit() { return status[AtCommit]; } + + /** Sets this instruction as committed. */ + void setCommitted() { status.set(Committed); } + + /** Returns whether or not this instruction is committed. */ + bool isCommitted() const { return status[Committed]; } + + /** Sets this instruction as squashed. */ + void setSquashed() { status.set(Squashed); } + + /** Returns whether or not this instruction is squashed. */ + bool isSquashed() const { return status[Squashed]; } + + //Instruction Queue Entry + //----------------------- + /** Sets this instruction as a entry the IQ. */ + void setInIQ() { status.set(IqEntry); } + + /** Sets this instruction as a entry the IQ. */ + void clearInIQ() { status.reset(IqEntry); } + + /** Returns whether or not this instruction has issued. */ + bool isInIQ() const { return status[IqEntry]; } + + /** Sets this instruction as squashed in the IQ. */ + void setSquashedInIQ() { status.set(SquashedInIQ); status.set(Squashed);} + + /** Returns whether or not this instruction is squashed in the IQ. */ + bool isSquashedInIQ() const { return status[SquashedInIQ]; } + + + //Load / Store Queue Functions + //----------------------- + /** Sets this instruction as a entry the LSQ. */ + void setInLSQ() { status.set(LsqEntry); } + + /** Sets this instruction as a entry the LSQ. */ + void removeInLSQ() { status.reset(LsqEntry); } + + /** Returns whether or not this instruction is in the LSQ. */ + bool isInLSQ() const { return status[LsqEntry]; } + + /** Sets this instruction as squashed in the LSQ. */ + void setSquashedInLSQ() { status.set(SquashedInLSQ);} + + /** Returns whether or not this instruction is squashed in the LSQ. */ + bool isSquashedInLSQ() const { return status[SquashedInLSQ]; } + + + //Reorder Buffer Functions + //----------------------- + /** Sets this instruction as a entry the ROB. */ + void setInROB() { status.set(RobEntry); } + + /** Sets this instruction as a entry the ROB. */ + void clearInROB() { status.reset(RobEntry); } + + /** Returns whether or not this instruction is in the ROB. */ + bool isInROB() const { return status[RobEntry]; } + + /** Sets this instruction as squashed in the ROB. */ + void setSquashedInROB() { status.set(SquashedInROB); } + + /** Returns whether or not this instruction is squashed in the ROB. */ + bool isSquashedInROB() const { return status[SquashedInROB]; } + + /** Read the PC of this instruction. */ + const Addr readPC() const { return PC; } + + /** Set the next PC of this instruction (its actual target). */ + void setNextPC(uint64_t val) + { + nextPC = val; + } + + /** Set the next NPC of this instruction (the target in Mips or Sparc).*/ + void setNextNPC(uint64_t val) + { + nextNPC = val; + } + + /** Sets the ASID. */ + void setASID(short addr_space_id) { asid = addr_space_id; } + + /** Sets the thread id. */ + void setTid(unsigned tid) { threadNumber = tid; } + + /** Sets the pointer to the thread state. */ + void setThreadState(ImplState *state) { thread = state; } + + /** Returns the thread context. */ + ThreadContext *tcBase() { return thread->getTC(); } + + private: + /** Instruction effective address. + * @todo: Consider if this is necessary or not. + */ + Addr instEffAddr; + + /** Whether or not the effective address calculation is completed. + * @todo: Consider if this is necessary or not. + */ + bool eaCalcDone; + + public: + /** Sets the effective address. */ + void setEA(Addr &ea) { instEffAddr = ea; eaCalcDone = true; } + + /** Returns the effective address. */ + const Addr &getEA() const { return instEffAddr; } + + /** Returns whether or not the eff. addr. calculation has been completed. */ + bool doneEACalc() { return eaCalcDone; } + + /** Returns whether or not the eff. addr. source registers are ready. */ + bool eaSrcsReady(); + + /** Whether or not the memory operation is done. */ + bool memOpDone; + + public: + /** Load queue index. */ + int16_t lqIdx; + + /** Store queue index. */ + int16_t sqIdx; + + /** Iterator pointing to this BaseDynInst in the list of all insts. */ + ListIt instListIt; + + /** Returns iterator to this instruction in the list of all insts. */ + ListIt &getInstListIt() { return instListIt; } + + /** Sets iterator for this instruction in the list of all insts. */ + void setInstListIt(ListIt _instListIt) { instListIt = _instListIt; } +}; + +template<class Impl> +template<class T> +inline Fault +BaseDynInst<Impl>::read(Addr addr, T &data, unsigned flags) +{ + // Sometimes reads will get retried, so they may come through here + // twice. + if (!req) { + req = new Request(); + req->setVirt(asid, addr, sizeof(T), flags, this->PC); + req->setThreadContext(thread->readCpuId(), threadNumber); + } else { + assert(addr == req->getVaddr()); + } + + if ((req->getVaddr() & (TheISA::VMPageSize - 1)) + req->getSize() > + TheISA::VMPageSize) { + return TheISA::genAlignmentFault(); + } + + fault = cpu->translateDataReadReq(req, thread); + + if (fault == NoFault) { + effAddr = req->getVaddr(); + physEffAddr = req->getPaddr(); + memReqFlags = req->getFlags(); + +#if 0 + if (cpu->system->memctrl->badaddr(physEffAddr)) { + fault = TheISA::genMachineCheckFault(); + data = (T)-1; + this->setExecuted(); + } else { + fault = cpu->read(req, data, lqIdx); + } +#else + fault = cpu->read(req, data, lqIdx); +#endif + } else { + // Return a fixed value to keep simulation deterministic even + // along misspeculated paths. + data = (T)-1; + + // Commit will have to clean up whatever happened. Set this + // instruction as executed. + this->setExecuted(); + } + + if (traceData) { + traceData->setAddr(addr); + traceData->setData(data); + } + + return fault; +} + +template<class Impl> +template<class T> +inline Fault +BaseDynInst<Impl>::write(T data, Addr addr, unsigned flags, uint64_t *res) +{ + if (traceData) { + traceData->setAddr(addr); + traceData->setData(data); + } + + assert(req == NULL); + + req = new Request(); + req->setVirt(asid, addr, sizeof(T), flags, this->PC); + req->setThreadContext(thread->readCpuId(), threadNumber); + + if ((req->getVaddr() & (TheISA::VMPageSize - 1)) + req->getSize() > + TheISA::VMPageSize) { + return TheISA::genAlignmentFault(); + } + + fault = cpu->translateDataWriteReq(req, thread); + + if (fault == NoFault) { + effAddr = req->getVaddr(); + physEffAddr = req->getPaddr(); + memReqFlags = req->getFlags(); +#if 0 + if (cpu->system->memctrl->badaddr(physEffAddr)) { + fault = TheISA::genMachineCheckFault(); + } else { + fault = cpu->write(req, data, sqIdx); + } +#else + fault = cpu->write(req, data, sqIdx); +#endif + } + + if (res) { + // always return some result to keep misspeculated paths + // (which will ignore faults) deterministic + *res = (fault == NoFault) ? req->getScResult() : 0; + } + + return fault; +} + +#endif // __CPU_BASE_DYN_INST_HH__ diff --git a/src/cpu/base_dyn_inst_impl.hh b/src/cpu/base_dyn_inst_impl.hh new file mode 100644 index 000000000..f2109e88d --- /dev/null +++ b/src/cpu/base_dyn_inst_impl.hh @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include <iostream> +#include <set> +#include <string> +#include <sstream> + +#include "base/cprintf.hh" +#include "base/trace.hh" + +#include "sim/faults.hh" +#include "cpu/exetrace.hh" +#include "mem/request.hh" + +#include "cpu/base_dyn_inst.hh" + +#define NOHASH +#ifndef NOHASH + +#include "base/hashmap.hh" + +unsigned int MyHashFunc(const BaseDynInst *addr) +{ + unsigned a = (unsigned)addr; + unsigned hash = (((a >> 14) ^ ((a >> 2) & 0xffff))) & 0x7FFFFFFF; + + return hash; +} + +typedef m5::hash_map<const BaseDynInst *, const BaseDynInst *, MyHashFunc> +my_hash_t; + +my_hash_t thishash; +#endif + +template <class Impl> +BaseDynInst<Impl>::BaseDynInst(TheISA::ExtMachInst machInst, Addr inst_PC, + Addr pred_PC, InstSeqNum seq_num, + ImplCPU *cpu) + : staticInst(machInst), traceData(NULL), cpu(cpu) +{ + seqNum = seq_num; + + PC = inst_PC; + nextPC = PC + sizeof(TheISA::MachInst); + nextNPC = nextPC + sizeof(TheISA::MachInst); + predPC = pred_PC; + + initVars(); +} + +template <class Impl> +BaseDynInst<Impl>::BaseDynInst(StaticInstPtr &_staticInst) + : staticInst(_staticInst), traceData(NULL) +{ + seqNum = 0; + initVars(); +} + +template <class Impl> +void +BaseDynInst<Impl>::initVars() +{ + req = NULL; + memData = NULL; + effAddr = 0; + physEffAddr = 0; + + readyRegs = 0; + + instResult.integer = 0; + + status.reset(); + + eaCalcDone = false; + memOpDone = false; + + lqIdx = -1; + sqIdx = -1; + + // Eventually make this a parameter. + threadNumber = 0; + + // Also make this a parameter, or perhaps get it from xc or cpu. + asid = 0; + + // Initialize the fault to be NoFault. + fault = NoFault; + + ++instcount; + + if (instcount > 1500) { + cpu->dumpInsts(); +#ifdef DEBUG + dumpSNList(); +#endif + assert(instcount <= 1500); + } + + DPRINTF(DynInst, "DynInst: [sn:%lli] Instruction created. Instcount=%i\n", + seqNum, instcount); + +#ifdef DEBUG + cpu->snList.insert(seqNum); +#endif +} + +template <class Impl> +BaseDynInst<Impl>::~BaseDynInst() +{ + if (req) { + delete req; + } + + if (memData) { + delete [] memData; + } + + if (traceData) { + delete traceData; + } + + fault = NoFault; + + --instcount; + + DPRINTF(DynInst, "DynInst: [sn:%lli] Instruction destroyed. Instcount=%i\n", + seqNum, instcount); +#ifdef DEBUG + cpu->snList.erase(seqNum); +#endif +} + +#ifdef DEBUG +template <class Impl> +void +BaseDynInst<Impl>::dumpSNList() +{ + std::set<InstSeqNum>::iterator sn_it = cpu->snList.begin(); + + int count = 0; + while (sn_it != cpu->snList.end()) { + cprintf("%i: [sn:%lli] not destroyed\n", count, (*sn_it)); + count++; + sn_it++; + } +} +#endif + +template <class Impl> +void +BaseDynInst<Impl>::prefetch(Addr addr, unsigned flags) +{ + // This is the "functional" implementation of prefetch. Not much + // happens here since prefetches don't affect the architectural + // state. +/* + // Generate a MemReq so we can translate the effective address. + MemReqPtr req = new MemReq(addr, thread->getXCProxy(), 1, flags); + req->asid = asid; + + // Prefetches never cause faults. + fault = NoFault; + + // note this is a local, not BaseDynInst::fault + Fault trans_fault = cpu->translateDataReadReq(req); + + if (trans_fault == NoFault && !(req->flags & UNCACHEABLE)) { + // It's a valid address to cacheable space. Record key MemReq + // parameters so we can generate another one just like it for + // the timing access without calling translate() again (which + // might mess up the TLB). + effAddr = req->vaddr; + physEffAddr = req->paddr; + memReqFlags = req->flags; + } else { + // Bogus address (invalid or uncacheable space). Mark it by + // setting the eff_addr to InvalidAddr. + effAddr = physEffAddr = MemReq::inval_addr; + } + + if (traceData) { + traceData->setAddr(addr); + } +*/ +} + +template <class Impl> +void +BaseDynInst<Impl>::writeHint(Addr addr, int size, unsigned flags) +{ + // Not currently supported. +} + +/** + * @todo Need to find a way to get the cache block size here. + */ +template <class Impl> +Fault +BaseDynInst<Impl>::copySrcTranslate(Addr src) +{ + // Not currently supported. + return NoFault; +} + +/** + * @todo Need to find a way to get the cache block size here. + */ +template <class Impl> +Fault +BaseDynInst<Impl>::copy(Addr dest) +{ + // Not currently supported. + return NoFault; +} + +template <class Impl> +void +BaseDynInst<Impl>::dump() +{ + cprintf("T%d : %#08d `", threadNumber, PC); + std::cout << staticInst->disassemble(PC); + cprintf("'\n"); +} + +template <class Impl> +void +BaseDynInst<Impl>::dump(std::string &outstring) +{ + std::ostringstream s; + s << "T" << threadNumber << " : 0x" << PC << " " + << staticInst->disassemble(PC); + + outstring = s.str(); +} + +template <class Impl> +void +BaseDynInst<Impl>::markSrcRegReady() +{ + if (++readyRegs == numSrcRegs()) { + status.set(CanIssue); + } +} + +template <class Impl> +void +BaseDynInst<Impl>::markSrcRegReady(RegIndex src_idx) +{ + _readySrcRegIdx[src_idx] = true; + + markSrcRegReady(); +} + +template <class Impl> +bool +BaseDynInst<Impl>::eaSrcsReady() +{ + // For now I am assuming that src registers 1..n-1 are the ones that the + // EA calc depends on. (i.e. src reg 0 is the source of the data to be + // stored) + + for (int i = 1; i < numSrcRegs(); ++i) { + if (!_readySrcRegIdx[i]) + return false; + } + + return true; +} diff --git a/src/cpu/checker/cpu.cc b/src/cpu/checker/cpu.cc new file mode 100644 index 000000000..1540a6b94 --- /dev/null +++ b/src/cpu/checker/cpu.cc @@ -0,0 +1,408 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include <list> +#include <string> + +#include "cpu/base.hh" +#include "cpu/checker/cpu.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" +#include "cpu/static_inst.hh" +#include "mem/packet_impl.hh" +#include "sim/byteswap.hh" + +#if FULL_SYSTEM +#include "arch/vtophys.hh" +#include "kern/kernel_stats.hh" +#endif // FULL_SYSTEM + +using namespace std; +//The CheckerCPU does alpha only +using namespace AlphaISA; + +void +CheckerCPU::init() +{ +} + +CheckerCPU::CheckerCPU(Params *p) + : BaseCPU(p), thread(NULL), tc(NULL) +{ + memReq = NULL; + + numInst = 0; + startNumInst = 0; + numLoad = 0; + startNumLoad = 0; + youngestSN = 0; + + changedPC = willChangePC = changedNextPC = false; + + exitOnError = p->exitOnError; + warnOnlyOnLoadError = p->warnOnlyOnLoadError; +#if FULL_SYSTEM + itb = p->itb; + dtb = p->dtb; + systemPtr = NULL; +#else + process = p->process; +#endif + + result.integer = 0; +} + +CheckerCPU::~CheckerCPU() +{ +} + +void +CheckerCPU::setMemory(MemObject *mem) +{ +#if !FULL_SYSTEM + memPtr = mem; + thread = new SimpleThread(this, /* thread_num */ 0, process, + /* asid */ 0, mem); + + thread->setStatus(ThreadContext::Suspended); + tc = thread->getTC(); + threadContexts.push_back(tc); +#endif +} + +void +CheckerCPU::setSystem(System *system) +{ +#if FULL_SYSTEM + systemPtr = system; + + thread = new SimpleThread(this, 0, systemPtr, itb, dtb, false); + + thread->setStatus(ThreadContext::Suspended); + tc = thread->getTC(); + threadContexts.push_back(tc); + delete thread->kernelStats; + thread->kernelStats = NULL; +#endif +} + +void +CheckerCPU::setIcachePort(Port *icache_port) +{ + icachePort = icache_port; +} + +void +CheckerCPU::setDcachePort(Port *dcache_port) +{ + dcachePort = dcache_port; +} + +void +CheckerCPU::serialize(ostream &os) +{ +/* + BaseCPU::serialize(os); + SERIALIZE_SCALAR(inst); + nameOut(os, csprintf("%s.xc", name())); + thread->serialize(os); + cacheCompletionEvent.serialize(os); +*/ +} + +void +CheckerCPU::unserialize(Checkpoint *cp, const string §ion) +{ +/* + BaseCPU::unserialize(cp, section); + UNSERIALIZE_SCALAR(inst); + thread->unserialize(cp, csprintf("%s.xc", section)); +*/ +} + +Fault +CheckerCPU::copySrcTranslate(Addr src) +{ + panic("Unimplemented!"); +} + +Fault +CheckerCPU::copy(Addr dest) +{ + panic("Unimplemented!"); +} + +template <class T> +Fault +CheckerCPU::read(Addr addr, T &data, unsigned flags) +{ + // need to fill in CPU & thread IDs here + memReq = new Request(); + + memReq->setVirt(0, addr, sizeof(T), flags, thread->readPC()); + + // translate to physical address + translateDataReadReq(memReq); + + Packet *pkt = new Packet(memReq, Packet::ReadReq, Packet::Broadcast); + + pkt->dataStatic(&data); + + if (!(memReq->getFlags() & UNCACHEABLE)) { + // Access memory to see if we have the same data + dcachePort->sendFunctional(pkt); + } else { + // Assume the data is correct if it's an uncached access + memcpy(&data, &unverifiedResult.integer, sizeof(T)); + } + + delete pkt; + + return NoFault; +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template +Fault +CheckerCPU::read(Addr addr, uint64_t &data, unsigned flags); + +template +Fault +CheckerCPU::read(Addr addr, uint32_t &data, unsigned flags); + +template +Fault +CheckerCPU::read(Addr addr, uint16_t &data, unsigned flags); + +template +Fault +CheckerCPU::read(Addr addr, uint8_t &data, unsigned flags); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +CheckerCPU::read(Addr addr, double &data, unsigned flags) +{ + return read(addr, *(uint64_t*)&data, flags); +} + +template<> +Fault +CheckerCPU::read(Addr addr, float &data, unsigned flags) +{ + return read(addr, *(uint32_t*)&data, flags); +} + +template<> +Fault +CheckerCPU::read(Addr addr, int32_t &data, unsigned flags) +{ + return read(addr, (uint32_t&)data, flags); +} + +template <class T> +Fault +CheckerCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) +{ + // need to fill in CPU & thread IDs here + memReq = new Request(); + + memReq->setVirt(0, addr, sizeof(T), flags, thread->readPC()); + + // translate to physical address + thread->translateDataWriteReq(memReq); + + // Can compare the write data and result only if it's cacheable, + // not a store conditional, or is a store conditional that + // succeeded. + // @todo: Verify that actual memory matches up with these values. + // Right now it only verifies that the instruction data is the + // same as what was in the request that got sent to memory; there + // is no verification that it is the same as what is in memory. + // This is because the LSQ would have to be snooped in the CPU to + // verify this data. + if (unverifiedReq && + !(unverifiedReq->getFlags() & UNCACHEABLE) && + (!(unverifiedReq->getFlags() & LOCKED) || + ((unverifiedReq->getFlags() & LOCKED) && + unverifiedReq->getScResult() == 1))) { + T inst_data; +/* + // This code would work if the LSQ allowed for snooping. + Packet *pkt = new Packet(memReq, Packet::ReadReq, Packet::Broadcast); + pkt.dataStatic(&inst_data); + + dcachePort->sendFunctional(pkt); + + delete pkt; +*/ + memcpy(&inst_data, unverifiedMemData, sizeof(T)); + + if (data != inst_data) { + warn("%lli: Store value does not match value in memory! " + "Instruction: %#x, memory: %#x", + curTick, inst_data, data); + handleError(); + } + } + + // Assume the result was the same as the one passed in. This checker + // doesn't check if the SC should succeed or fail, it just checks the + // value. + if (res && unverifiedReq->scResultValid()) + *res = unverifiedReq->getScResult(); + + return NoFault; +} + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +template +Fault +CheckerCPU::write(uint64_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +CheckerCPU::write(uint32_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +CheckerCPU::write(uint16_t data, Addr addr, unsigned flags, uint64_t *res); + +template +Fault +CheckerCPU::write(uint8_t data, Addr addr, unsigned flags, uint64_t *res); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +CheckerCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint64_t*)&data, addr, flags, res); +} + +template<> +Fault +CheckerCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint32_t*)&data, addr, flags, res); +} + +template<> +Fault +CheckerCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) +{ + return write((uint32_t)data, addr, flags, res); +} + + +#if FULL_SYSTEM +Addr +CheckerCPU::dbg_vtophys(Addr addr) +{ + return vtophys(tc, addr); +} +#endif // FULL_SYSTEM + +bool +CheckerCPU::translateInstReq(Request *req) +{ +#if FULL_SYSTEM + return (thread->translateInstReq(req) == NoFault); +#else + thread->translateInstReq(req); + return true; +#endif +} + +void +CheckerCPU::translateDataReadReq(Request *req) +{ + thread->translateDataReadReq(req); + + if (req->getVaddr() != unverifiedReq->getVaddr()) { + warn("%lli: Request virtual addresses do not match! Inst: %#x, " + "checker: %#x", + curTick, unverifiedReq->getVaddr(), req->getVaddr()); + handleError(); + } + req->setPaddr(unverifiedReq->getPaddr()); + + if (checkFlags(req)) { + warn("%lli: Request flags do not match! Inst: %#x, checker: %#x", + curTick, unverifiedReq->getFlags(), req->getFlags()); + handleError(); + } +} + +void +CheckerCPU::translateDataWriteReq(Request *req) +{ + thread->translateDataWriteReq(req); + + if (req->getVaddr() != unverifiedReq->getVaddr()) { + warn("%lli: Request virtual addresses do not match! Inst: %#x, " + "checker: %#x", + curTick, unverifiedReq->getVaddr(), req->getVaddr()); + handleError(); + } + req->setPaddr(unverifiedReq->getPaddr()); + + if (checkFlags(req)) { + warn("%lli: Request flags do not match! Inst: %#x, checker: %#x", + curTick, unverifiedReq->getFlags(), req->getFlags()); + handleError(); + } +} + +bool +CheckerCPU::checkFlags(Request *req) +{ + // Remove any dynamic flags that don't have to do with the request itself. + unsigned flags = unverifiedReq->getFlags(); + unsigned mask = LOCKED | PHYSICAL | VPTE | ALTMODE | UNCACHEABLE | NO_FAULT; + flags = flags & (mask); + if (flags == req->getFlags()) { + return false; + } else { + return true; + } +} + +void +CheckerCPU::dumpAndExit() +{ + warn("%lli: Checker PC:%#x, next PC:%#x", + curTick, thread->readPC(), thread->readNextPC()); + panic("Checker found an error!"); +} diff --git a/src/cpu/checker/cpu.hh b/src/cpu/checker/cpu.hh new file mode 100644 index 000000000..737b4b5d4 --- /dev/null +++ b/src/cpu/checker/cpu.hh @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_CHECKER_CPU_HH__ +#define __CPU_CHECKER_CPU_HH__ + +#include <list> +#include <queue> +#include <map> + +#include "arch/types.hh" +#include "base/statistics.hh" +#include "config/full_system.hh" +#include "cpu/base.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/simple_thread.hh" +#include "cpu/pc_event.hh" +#include "cpu/static_inst.hh" +#include "sim/eventq.hh" + +// forward declarations +#if FULL_SYSTEM +class Processor; +class AlphaITB; +class AlphaDTB; +class PhysicalMemory; + +class RemoteGDB; +class GDBListener; + +#else + +class Process; + +#endif // FULL_SYSTEM +template <class> +class BaseDynInst; +class ThreadContext; +class MemInterface; +class Checkpoint; +class Request; + +/** + * CheckerCPU class. Dynamically verifies instructions as they are + * completed by making sure that the instruction and its results match + * the independent execution of the benchmark inside the checker. The + * checker verifies instructions in order, regardless of the order in + * which instructions complete. There are certain results that can + * not be verified, specifically the result of a store conditional or + * the values of uncached accesses. In these cases, and with + * instructions marked as "IsUnverifiable", the checker assumes that + * the value from the main CPU's execution is correct and simply + * copies that value. It provides a CheckerThreadContext (see + * checker/thread_context.hh) that provides hooks for updating the + * Checker's state through any ThreadContext accesses. This allows the + * checker to be able to correctly verify instructions, even with + * external accesses to the ThreadContext that change state. + */ +class CheckerCPU : public BaseCPU +{ + protected: + typedef TheISA::MachInst MachInst; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscReg MiscReg; + public: + virtual void init(); + + struct Params : public BaseCPU::Params + { +#if FULL_SYSTEM + AlphaITB *itb; + AlphaDTB *dtb; +#else + Process *process; +#endif + bool exitOnError; + bool updateOnError; + bool warnOnlyOnLoadError; + }; + + public: + CheckerCPU(Params *p); + virtual ~CheckerCPU(); + + Process *process; + + void setMemory(MemObject *mem); + + MemObject *memPtr; + + void setSystem(System *system); + + System *systemPtr; + + void setIcachePort(Port *icache_port); + + Port *icachePort; + + void setDcachePort(Port *dcache_port); + + Port *dcachePort; + + virtual Port *getPort(const std::string &name, int idx) + { + panic("Not supported on checker!"); + return NULL; + } + + public: + // Primary thread being run. + SimpleThread *thread; + + ThreadContext *tc; + + AlphaITB *itb; + AlphaDTB *dtb; + +#if FULL_SYSTEM + Addr dbg_vtophys(Addr addr); +#endif + + union Result { + uint64_t integer; +// float fp; + double dbl; + }; + + Result result; + + // current instruction + MachInst machInst; + + // Pointer to the one memory request. + RequestPtr memReq; + + StaticInstPtr curStaticInst; + + // number of simulated instructions + Counter numInst; + Counter startNumInst; + + std::queue<int> miscRegIdxs; + + virtual Counter totalInstructions() const + { + return 0; + } + + // number of simulated loads + Counter numLoad; + Counter startNumLoad; + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + template <class T> + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); + + // These functions are only used in CPU models that split + // effective address computation from the actual memory access. + void setEA(Addr EA) { panic("SimpleCPU::setEA() not implemented\n"); } + Addr getEA() { panic("SimpleCPU::getEA() not implemented\n"); } + + void prefetch(Addr addr, unsigned flags) + { + // need to do this... + } + + void writeHint(Addr addr, int size, unsigned flags) + { + // need to do this... + } + + Fault copySrcTranslate(Addr src); + + Fault copy(Addr dest); + + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to redice overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + uint64_t readIntReg(const StaticInst *si, int idx) + { + return thread->readIntReg(si->srcRegIdx(idx)); + } + + FloatReg readFloatReg(const StaticInst *si, int idx, int width) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatReg(reg_idx, width); + } + + FloatReg readFloatReg(const StaticInst *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatReg(reg_idx); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatRegBits(reg_idx, width); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatRegBits(reg_idx); + } + + void setIntReg(const StaticInst *si, int idx, uint64_t val) + { + thread->setIntReg(si->destRegIdx(idx), val); + result.integer = val; + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatReg(reg_idx, val, width); + switch(width) { + case 32: + result.fp = val; + break; + case 64: + result.dbl = val; + break; + }; + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatReg(reg_idx, val); + result.dbl = (double)val; + } + + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val, + int width) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatRegBits(reg_idx, val, width); + result.integer = val; + } + + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatRegBits(reg_idx, val); + result.integer = val; + } + + uint64_t readPC() { return thread->readPC(); } + + uint64_t readNextPC() { return thread->readNextPC(); } + + void setNextPC(uint64_t val) { + thread->setNextPC(val); + } + + MiscReg readMiscReg(int misc_reg) + { + return thread->readMiscReg(misc_reg); + } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { + return thread->readMiscRegWithEffect(misc_reg, fault); + } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + result.integer = val; + miscRegIdxs.push(misc_reg); + return thread->setMiscReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + miscRegIdxs.push(misc_reg); + return thread->setMiscRegWithEffect(misc_reg, val); + } + + void recordPCChange(uint64_t val) { changedPC = true; newPC = val; } + void recordNextPCChange(uint64_t val) { changedNextPC = true; } + + bool translateInstReq(Request *req); + void translateDataWriteReq(Request *req); + void translateDataReadReq(Request *req); + +#if FULL_SYSTEM + Fault hwrei() { return thread->hwrei(); } + int readIntrFlag() { return thread->readIntrFlag(); } + void setIntrFlag(int val) { thread->setIntrFlag(val); } + bool inPalMode() { return thread->inPalMode(); } + void ev5_trap(Fault fault) { fault->invoke(tc); } + bool simPalCheck(int palFunc) { return thread->simPalCheck(palFunc); } +#else + // Assume that the normal CPU's call to syscall was successful. + // The checker's state would have already been updated by the syscall. + void syscall(uint64_t callnum) { } +#endif + + void handleError() + { + if (exitOnError) + dumpAndExit(); + } + + bool checkFlags(Request *req); + + void dumpAndExit(); + + ThreadContext *tcBase() { return tc; } + SimpleThread *threadBase() { return thread; } + + Result unverifiedResult; + Request *unverifiedReq; + uint8_t *unverifiedMemData; + + bool changedPC; + bool willChangePC; + uint64_t newPC; + bool changedNextPC; + bool exitOnError; + bool updateOnError; + bool warnOnlyOnLoadError; + + InstSeqNum youngestSN; +}; + +/** + * Templated Checker class. This Checker class is templated on the + * DynInstPtr of the instruction type that will be verified. Proper + * template instantiations of the Checker must be placed at the bottom + * of checker/cpu.cc. + */ +template <class DynInstPtr> +class Checker : public CheckerCPU +{ + public: + Checker(Params *p) + : CheckerCPU(p), updateThisCycle(false), unverifiedInst(NULL) + { } + + void switchOut(); + void takeOverFrom(BaseCPU *oldCPU); + + void verify(DynInstPtr &inst); + + void validateInst(DynInstPtr &inst); + void validateExecution(DynInstPtr &inst); + void validateState(); + + void copyResult(DynInstPtr &inst); + + private: + void handleError(DynInstPtr &inst) + { + if (exitOnError) { + dumpAndExit(inst); + } else if (updateOnError) { + updateThisCycle = true; + } + } + + void dumpAndExit(DynInstPtr &inst); + + bool updateThisCycle; + + DynInstPtr unverifiedInst; + + std::list<DynInstPtr> instList; + typedef typename std::list<DynInstPtr>::iterator InstListIt; + void dumpInsts(); +}; + +#endif // __CPU_CHECKER_CPU_HH__ diff --git a/src/cpu/checker/cpu_impl.hh b/src/cpu/checker/cpu_impl.hh new file mode 100644 index 000000000..3bb81c4b9 --- /dev/null +++ b/src/cpu/checker/cpu_impl.hh @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include <list> +#include <string> + +#include "base/refcnt.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/checker/cpu.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" +#include "cpu/static_inst.hh" +#include "mem/packet_impl.hh" +#include "sim/byteswap.hh" +#include "sim/sim_object.hh" +#include "sim/stats.hh" + +#if FULL_SYSTEM +#include "arch/vtophys.hh" +#endif // FULL_SYSTEM + +using namespace std; +//The CheckerCPU does alpha only +using namespace AlphaISA; + +template <class DynInstPtr> +void +Checker<DynInstPtr>::verify(DynInstPtr &completed_inst) +{ + DynInstPtr inst; + + // Either check this instruction, or add it to a list of + // instructions waiting to be checked. Instructions must be + // checked in program order, so if a store has committed yet not + // completed, there may be some instructions that are waiting + // behind it that have completed and must be checked. + if (!instList.empty()) { + if (youngestSN < completed_inst->seqNum) { + DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%#x to list.\n", + completed_inst->seqNum, completed_inst->readPC()); + instList.push_back(completed_inst); + youngestSN = completed_inst->seqNum; + } + + if (!instList.front()->isCompleted()) { + return; + } else { + inst = instList.front(); + instList.pop_front(); + } + } else { + if (!completed_inst->isCompleted()) { + if (youngestSN < completed_inst->seqNum) { + DPRINTF(Checker, "Adding instruction [sn:%lli] PC:%#x to list.\n", + completed_inst->seqNum, completed_inst->readPC()); + instList.push_back(completed_inst); + youngestSN = completed_inst->seqNum; + } + return; + } else { + if (youngestSN < completed_inst->seqNum) { + inst = completed_inst; + youngestSN = completed_inst->seqNum; + } else { + return; + } + } + } + + unverifiedInst = inst; + + // Try to check all instructions that are completed, ending if we + // run out of instructions to check or if an instruction is not + // yet completed. + while (1) { + DPRINTF(Checker, "Processing instruction [sn:%lli] PC:%#x.\n", + inst->seqNum, inst->readPC()); + unverifiedResult.integer = inst->readIntResult(); + unverifiedReq = inst->req; + unverifiedMemData = inst->memData; + numCycles++; + + Fault fault = NoFault; + + // maintain $r0 semantics + thread->setIntReg(ZeroReg, 0); +#ifdef TARGET_ALPHA + thread->setFloatRegDouble(ZeroReg, 0.0); +#endif // TARGET_ALPHA + + // Check if any recent PC changes match up with anything we + // expect to happen. This is mostly to check if traps or + // PC-based events have occurred in both the checker and CPU. + if (changedPC) { + DPRINTF(Checker, "Changed PC recently to %#x\n", + thread->readPC()); + if (willChangePC) { + if (newPC == thread->readPC()) { + DPRINTF(Checker, "Changed PC matches expected PC\n"); + } else { + warn("%lli: Changed PC does not match expected PC, " + "changed: %#x, expected: %#x", + curTick, thread->readPC(), newPC); + CheckerCPU::handleError(); + } + willChangePC = false; + } + changedPC = false; + } + if (changedNextPC) { + DPRINTF(Checker, "Changed NextPC recently to %#x\n", + thread->readNextPC()); + changedNextPC = false; + } + + // Try to fetch the instruction + +#if FULL_SYSTEM +#define IFETCH_FLAGS(pc) ((pc) & 1) ? PHYSICAL : 0 +#else +#define IFETCH_FLAGS(pc) 0 +#endif + + uint64_t fetch_PC = thread->readPC() & ~3; + + // set up memory request for instruction fetch + memReq = new Request(inst->threadNumber, fetch_PC, + sizeof(uint32_t), + IFETCH_FLAGS(thread->readPC()), + fetch_PC, thread->readCpuId(), inst->threadNumber); + + bool succeeded = translateInstReq(memReq); + + if (!succeeded) { + if (inst->getFault() == NoFault) { + // In this case the instruction was not a dummy + // instruction carrying an ITB fault. In the single + // threaded case the ITB should still be able to + // translate this instruction; in the SMT case it's + // possible that its ITB entry was kicked out. + warn("%lli: Instruction PC %#x was not found in the ITB!", + curTick, thread->readPC()); + handleError(inst); + + // go to the next instruction + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); + + break; + } else { + // The instruction is carrying an ITB fault. Handle + // the fault and see if our results match the CPU on + // the next tick(). + fault = inst->getFault(); + } + } + + if (fault == NoFault) { + Packet *pkt = new Packet(memReq, Packet::ReadReq, + Packet::Broadcast); + + pkt->dataStatic(&machInst); + + icachePort->sendFunctional(pkt); + + delete pkt; + + // keep an instruction count + numInst++; + + // decode the instruction + machInst = gtoh(machInst); + // Checks that the instruction matches what we expected it to be. + // Checks both the machine instruction and the PC. + validateInst(inst); + + curStaticInst = StaticInst::decode(makeExtMI(machInst, + thread->readPC())); + +#if FULL_SYSTEM + thread->setInst(machInst); +#endif // FULL_SYSTEM + + fault = inst->getFault(); + } + + // Discard fetch's memReq. + delete memReq; + memReq = NULL; + + // Either the instruction was a fault and we should process the fault, + // or we should just go ahead execute the instruction. This assumes + // that the instruction is properly marked as a fault. + if (fault == NoFault) { + + thread->funcExeInst++; + + if (!inst->isUnverifiable()) + fault = curStaticInst->execute(this, NULL); + + // Checks to make sure instrution results are correct. + validateExecution(inst); + + if (curStaticInst->isLoad()) { + ++numLoad; + } + } + + if (fault != NoFault) { +#if FULL_SYSTEM + fault->invoke(tc); + willChangePC = true; + newPC = thread->readPC(); + DPRINTF(Checker, "Fault, PC is now %#x\n", newPC); +#endif + } else { +#if THE_ISA != MIPS_ISA + // go to the next instruction + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); +#else + // go to the next instruction + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readNextNPC()); + thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst)); +#endif + + } + +#if FULL_SYSTEM + // @todo: Determine if these should happen only if the + // instruction hasn't faulted. In the SimpleCPU case this may + // not be true, but in the O3 or Ozone case this may be true. + Addr oldpc; + int count = 0; + do { + oldpc = thread->readPC(); + system->pcEventQueue.service(tc); + count++; + } while (oldpc != thread->readPC()); + if (count > 1) { + willChangePC = true; + newPC = thread->readPC(); + DPRINTF(Checker, "PC Event, PC is now %#x\n", newPC); + } +#endif + + // @todo: Optionally can check all registers. (Or just those + // that have been modified). + validateState(); + + if (memReq) { + delete memReq; + memReq = NULL; + } + + // Continue verifying instructions if there's another completed + // instruction waiting to be verified. + if (instList.empty()) { + break; + } else if (instList.front()->isCompleted()) { + inst = instList.front(); + instList.pop_front(); + } else { + break; + } + } + unverifiedInst = NULL; +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::switchOut() +{ + instList.clear(); +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::takeOverFrom(BaseCPU *oldCPU) +{ +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::validateInst(DynInstPtr &inst) +{ + if (inst->readPC() != thread->readPC()) { + warn("%lli: PCs do not match! Inst: %#x, checker: %#x", + curTick, inst->readPC(), thread->readPC()); + if (changedPC) { + warn("%lli: Changed PCs recently, may not be an error", + curTick); + } else { + handleError(inst); + } + } + + MachInst mi = static_cast<MachInst>(inst->staticInst->machInst); + + if (mi != machInst) { + warn("%lli: Binary instructions do not match! Inst: %#x, " + "checker: %#x", + curTick, mi, machInst); + handleError(inst); + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::validateExecution(DynInstPtr &inst) +{ + bool result_mismatch = false; + if (inst->numDestRegs()) { + // @todo: Support more destination registers. + if (inst->isUnverifiable()) { + // Unverifiable instructions assume they were executed + // properly by the CPU. Grab the result from the + // instruction and write it to the register. + copyResult(inst); + } else if (result.integer != inst->readIntResult()) { + result_mismatch = true; + } + } + + if (result_mismatch) { + warn("%lli: Instruction results do not match! (Values may not " + "actually be integers) Inst: %#x, checker: %#x", + curTick, inst->readIntResult(), result.integer); + + // It's useful to verify load values from memory, but in MP + // systems the value obtained at execute may be different than + // the value obtained at completion. Similarly DMA can + // present the same problem on even UP systems. Thus there is + // the option to only warn on loads having a result error. + if (inst->isLoad() && warnOnlyOnLoadError) { + copyResult(inst); + } else { + handleError(inst); + } + } + + if (inst->readNextPC() != thread->readNextPC()) { + warn("%lli: Instruction next PCs do not match! Inst: %#x, " + "checker: %#x", + curTick, inst->readNextPC(), thread->readNextPC()); + handleError(inst); + } + + // Checking side effect registers can be difficult if they are not + // checked simultaneously with the execution of the instruction. + // This is because other valid instructions may have modified + // these registers in the meantime, and their values are not + // stored within the DynInst. + while (!miscRegIdxs.empty()) { + int misc_reg_idx = miscRegIdxs.front(); + miscRegIdxs.pop(); + + if (inst->tcBase()->readMiscReg(misc_reg_idx) != + thread->readMiscReg(misc_reg_idx)) { + warn("%lli: Misc reg idx %i (side effect) does not match! " + "Inst: %#x, checker: %#x", + curTick, misc_reg_idx, + inst->tcBase()->readMiscReg(misc_reg_idx), + thread->readMiscReg(misc_reg_idx)); + handleError(inst); + } + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::validateState() +{ + if (updateThisCycle) { + warn("%lli: Instruction PC %#x results didn't match up, copying all " + "registers from main CPU", curTick, unverifiedInst->readPC()); + // Heavy-weight copying of all registers + cpuXC->copyArchRegs(unverifiedInst->xcBase()); + // Also advance the PC. Hopefully no PC-based events happened. +#if THE_ISA != MIPS_ISA + // go to the next instruction + cpuXC->setPC(cpuXC->readNextPC()); + cpuXC->setNextPC(cpuXC->readNextPC() + sizeof(MachInst)); +#else + // go to the next instruction + cpuXC->setPC(cpuXC->readNextPC()); + cpuXC->setNextPC(cpuXC->readNextNPC()); + cpuXC->setNextNPC(cpuXC->readNextNPC() + sizeof(MachInst)); +#endif + updateThisCycle = false; +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::copyResult(DynInstPtr &inst) +{ + RegIndex idx = inst->destRegIdx(0); + if (idx < TheISA::FP_Base_DepTag) { + thread->setIntReg(idx, inst->readIntResult()); + } else if (idx < TheISA::Fpcr_DepTag) { + thread->setFloatRegBits(idx, inst->readIntResult()); + } else { + thread->setMiscReg(idx, inst->readIntResult()); + } +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::dumpAndExit(DynInstPtr &inst) +{ + cprintf("Error detected, instruction information:\n"); + cprintf("PC:%#x, nextPC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Completed:%i\n", + inst->readPC(), + inst->readNextPC(), + inst->seqNum, + inst->threadNumber, + inst->isCompleted()); + inst->dump(); + CheckerCPU::dumpAndExit(); +} + +template <class DynInstPtr> +void +Checker<DynInstPtr>::dumpInsts() +{ + int num = 0; + + InstListIt inst_list_it = --(instList.end()); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.end()) + { + cprintf("Instruction:%i\n", + num); + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Completed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isCompleted()); + + cprintf("\n"); + + inst_list_it--; + ++num; + } + +} diff --git a/src/cpu/checker/thread_context.hh b/src/cpu/checker/thread_context.hh new file mode 100644 index 000000000..8c0186dae --- /dev/null +++ b/src/cpu/checker/thread_context.hh @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_CHECKER_THREAD_CONTEXT_HH__ +#define __CPU_CHECKER_THREAD_CONTEXT_HH__ + +#include "arch/types.hh" +#include "cpu/checker/cpu.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" + +class EndQuiesceEvent; +namespace Kernel { + class Statistics; +}; + +/** + * Derived ThreadContext class for use with the Checker. The template + * parameter is the ThreadContext class used by the specific CPU being + * verified. This CheckerThreadContext is then used by the main CPU + * in place of its usual ThreadContext class. It handles updating the + * checker's state any time state is updated externally through the + * ThreadContext. + */ +template <class TC> +class CheckerThreadContext : public ThreadContext +{ + public: + CheckerThreadContext(TC *actual_tc, + CheckerCPU *checker_cpu) + : actualTC(actual_tc), checkerTC(checker_cpu->thread), + checkerCPU(checker_cpu) + { } + + private: + /** The main CPU's ThreadContext, or class that implements the + * ThreadContext interface. */ + TC *actualTC; + /** The checker's own SimpleThread. Will be updated any time + * anything uses this ThreadContext to externally update a + * thread's state. */ + SimpleThread *checkerTC; + /** Pointer to the checker CPU. */ + CheckerCPU *checkerCPU; + + public: + + BaseCPU *getCpuPtr() { return actualTC->getCpuPtr(); } + + void setCpuId(int id) + { + actualTC->setCpuId(id); + checkerTC->setCpuId(id); + } + + int readCpuId() { return actualTC->readCpuId(); } + +#if FULL_SYSTEM + System *getSystemPtr() { return actualTC->getSystemPtr(); } + + PhysicalMemory *getPhysMemPtr() { return actualTC->getPhysMemPtr(); } + + AlphaITB *getITBPtr() { return actualTC->getITBPtr(); } + + AlphaDTB *getDTBPtr() { return actualTC->getDTBPtr(); } + + Kernel::Statistics *getKernelStats() { return actualTC->getKernelStats(); } + + FunctionalPort *getPhysPort() { return actualTC->getPhysPort(); } + + VirtualPort *getVirtPort(ThreadContext *tc = NULL) + { return actualTC->getVirtPort(); } + + void delVirtPort(VirtualPort *vp) { actualTC->delVirtPort(vp); } +#else + TranslatingPort *getMemPort() { return actualTC->getMemPort(); } + + Process *getProcessPtr() { return actualTC->getProcessPtr(); } +#endif + + Status status() const { return actualTC->status(); } + + void setStatus(Status new_status) + { + actualTC->setStatus(new_status); + checkerTC->setStatus(new_status); + } + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + void activate(int delay = 1) { actualTC->activate(delay); } + + /// Set the status to Suspended. + void suspend() { actualTC->suspend(); } + + /// Set the status to Unallocated. + void deallocate(int delay = 0) { actualTC->deallocate(delay); } + + /// Set the status to Halted. + void halt() { actualTC->halt(); } + +#if FULL_SYSTEM + void dumpFuncProfile() { actualTC->dumpFuncProfile(); } +#endif + + void takeOverFrom(ThreadContext *oldContext) + { + actualTC->takeOverFrom(oldContext); + checkerTC->takeOverFrom(oldContext); + } + + void regStats(const std::string &name) { actualTC->regStats(name); } + + void serialize(std::ostream &os) { actualTC->serialize(os); } + void unserialize(Checkpoint *cp, const std::string §ion) + { actualTC->unserialize(cp, section); } + +#if FULL_SYSTEM + EndQuiesceEvent *getQuiesceEvent() { return actualTC->getQuiesceEvent(); } + + Tick readLastActivate() { return actualTC->readLastActivate(); } + Tick readLastSuspend() { return actualTC->readLastSuspend(); } + + void profileClear() { return actualTC->profileClear(); } + void profileSample() { return actualTC->profileSample(); } +#endif + + int getThreadNum() { return actualTC->getThreadNum(); } + + // @todo: Do I need this? + MachInst getInst() { return actualTC->getInst(); } + + // @todo: Do I need this? + void copyArchRegs(ThreadContext *tc) + { + actualTC->copyArchRegs(tc); + checkerTC->copyArchRegs(tc); + } + + void clearArchRegs() + { + actualTC->clearArchRegs(); + checkerTC->clearArchRegs(); + } + + // + // New accessors for new decoder. + // + uint64_t readIntReg(int reg_idx) + { return actualTC->readIntReg(reg_idx); } + + FloatReg readFloatReg(int reg_idx, int width) + { return actualTC->readFloatReg(reg_idx, width); } + + FloatReg readFloatReg(int reg_idx) + { return actualTC->readFloatReg(reg_idx); } + + FloatRegBits readFloatRegBits(int reg_idx, int width) + { return actualTC->readFloatRegBits(reg_idx, width); } + + FloatRegBits readFloatRegBits(int reg_idx) + { return actualTC->readFloatRegBits(reg_idx); } + + void setIntReg(int reg_idx, uint64_t val) + { + actualTC->setIntReg(reg_idx, val); + checkerTC->setIntReg(reg_idx, val); + } + + void setFloatReg(int reg_idx, FloatReg val, int width) + { + actualTC->setFloatReg(reg_idx, val, width); + checkerTC->setFloatReg(reg_idx, val, width); + } + + void setFloatReg(int reg_idx, FloatReg val) + { + actualTC->setFloatReg(reg_idx, val); + checkerTC->setFloatReg(reg_idx, val); + } + + void setFloatRegBits(int reg_idx, FloatRegBits val, int width) + { + actualTC->setFloatRegBits(reg_idx, val, width); + checkerTC->setFloatRegBits(reg_idx, val, width); + } + + void setFloatRegBits(int reg_idx, FloatRegBits val) + { + actualTC->setFloatRegBits(reg_idx, val); + checkerTC->setFloatRegBits(reg_idx, val); + } + + uint64_t readPC() { return actualTC->readPC(); } + + void setPC(uint64_t val) + { + actualTC->setPC(val); + checkerTC->setPC(val); + checkerCPU->recordPCChange(val); + } + + uint64_t readNextPC() { return actualTC->readNextPC(); } + + void setNextPC(uint64_t val) + { + actualTC->setNextPC(val); + checkerTC->setNextPC(val); + checkerCPU->recordNextPCChange(val); + } + + uint64_t readNextNPC() { return actualTC->readNextNPC(); } + + void setNextNPC(uint64_t val) + { + actualTC->setNextNPC(val); + checkerTC->setNextNPC(val); + checkerCPU->recordNextPCChange(val); + } + + MiscReg readMiscReg(int misc_reg) + { return actualTC->readMiscReg(misc_reg); } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { return actualTC->readMiscRegWithEffect(misc_reg, fault); } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + checkerTC->setMiscReg(misc_reg, val); + return actualTC->setMiscReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + checkerTC->setMiscRegWithEffect(misc_reg, val); + return actualTC->setMiscRegWithEffect(misc_reg, val); + } + + unsigned readStCondFailures() + { return actualTC->readStCondFailures(); } + + void setStCondFailures(unsigned sc_failures) + { + checkerTC->setStCondFailures(sc_failures); + actualTC->setStCondFailures(sc_failures); + } +#if FULL_SYSTEM + bool inPalMode() { return actualTC->inPalMode(); } +#endif + + // @todo: Fix this! + bool misspeculating() { return actualTC->misspeculating(); } + +#if !FULL_SYSTEM + IntReg getSyscallArg(int i) { return actualTC->getSyscallArg(i); } + + // used to shift args for indirect syscall + void setSyscallArg(int i, IntReg val) + { + checkerTC->setSyscallArg(i, val); + actualTC->setSyscallArg(i, val); + } + + void setSyscallReturn(SyscallReturn return_value) + { + checkerTC->setSyscallReturn(return_value); + actualTC->setSyscallReturn(return_value); + } + + Counter readFuncExeInst() { return actualTC->readFuncExeInst(); } +#endif + void changeRegFileContext(TheISA::RegContextParam param, + TheISA::RegContextVal val) + { + actualTC->changeRegFileContext(param, val); + checkerTC->changeRegFileContext(param, val); + } +}; + +#endif // __CPU_CHECKER_EXEC_CONTEXT_HH__ diff --git a/src/cpu/cpu_models.py b/src/cpu/cpu_models.py new file mode 100644 index 000000000..5b0c6c4da --- /dev/null +++ b/src/cpu/cpu_models.py @@ -0,0 +1,84 @@ +# Copyright (c) 2003-2006 The Regents of The University of Michigan +# 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. +# +# Authors: Steve Reinhardt + +import os +import os.path +import sys + +################ +# CpuModel class +# +# The CpuModel class encapsulates everything the ISA parser needs to +# know about a particular CPU model. + +class CpuModel: + # Dict of available CPU model objects. Accessible as CpuModel.dict. + dict = {} + + # Constructor. Automatically adds models to CpuModel.dict. + def __init__(self, name, filename, includes, strings): + self.name = name + self.filename = filename # filename for output exec code + self.includes = includes # include files needed in exec file + # The 'strings' dict holds all the per-CPU symbols we can + # substitute into templates etc. + self.strings = strings + # Add self to dict + CpuModel.dict[name] = self + +# +# Define CPU models. +# +# Parameters are: +# - name of model +# - filename for generated ISA execution file +# - includes needed for generated ISA execution file +# - substitution strings for ISA description templates +# + +CpuModel('AtomicSimpleCPU', 'atomic_simple_cpu_exec.cc', + '#include "cpu/simple/atomic.hh"', + { 'CPU_exec_context': 'AtomicSimpleCPU' }) +CpuModel('TimingSimpleCPU', 'timing_simple_cpu_exec.cc', + '#include "cpu/simple/timing.hh"', + { 'CPU_exec_context': 'TimingSimpleCPU' }) +CpuModel('FullCPU', 'full_cpu_exec.cc', + '#include "encumbered/cpu/full/dyn_inst.hh"', + { 'CPU_exec_context': 'DynInst' }) +CpuModel('OzoneSimpleCPU', 'ozone_simple_exec.cc', + '#include "cpu/ozone/dyn_inst.hh"', + { 'CPU_exec_context': 'OzoneDynInst<SimpleImpl>' }) +CpuModel('OzoneCPU', 'ozone_exec.cc', + '#include "cpu/ozone/dyn_inst.hh"', + { 'CPU_exec_context': 'OzoneDynInst<OzoneImpl>' }) +CpuModel('CheckerCPU', 'checker_cpu_exec.cc', + '#include "cpu/checker/cpu.hh"', + { 'CPU_exec_context': 'CheckerCPU' }) +CpuModel('O3CPU', 'o3_cpu_exec.cc', + '#include "cpu/o3/isa_specific.hh"', + { 'CPU_exec_context': 'O3DynInst' }) diff --git a/src/cpu/cpuevent.cc b/src/cpu/cpuevent.cc new file mode 100644 index 000000000..679244a6b --- /dev/null +++ b/src/cpu/cpuevent.cc @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#include "cpu/cpuevent.hh" + +/** Static list of all CpuEvent objects so we can modify their thread + * contexts as needed. */ +CpuEvent::CpuEventList CpuEvent::cpuEventList; + +CpuEvent::~CpuEvent() +{ + CpuEventList::iterator i; + + // delete the event from the global list + for (i = cpuEventList.begin(); i != cpuEventList.end(); ) { + if (*i == this) + i = cpuEventList.erase(i); + else + i++; + } +} + +void +CpuEvent::replaceThreadContext(ThreadContext *oldTc, ThreadContext *newTc) +{ + CpuEventList::iterator i; + + // Update any events that have the old thread context with the new thread + // context + for (i = cpuEventList.begin(); i != cpuEventList.end(); i++) { + if ((*i)->tc == oldTc) + (*i)->tc = newTc; + } +} diff --git a/src/cpu/cpuevent.hh b/src/cpu/cpuevent.hh new file mode 100644 index 000000000..9dfae27cf --- /dev/null +++ b/src/cpu/cpuevent.hh @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#ifndef __CPU_CPUEVENT_HH__ +#define __CPU_CPUEVENT_HH__ + +#include <vector> +#include "sim/eventq.hh" + +class ThreadContext; + +/** This class creates a global list of events that need a pointer to a + * thread context. When a switchover takes place the events can be migrated + * to the new thread context, otherwise you could have a wake timer interrupt + * go off on a switched out cpu or other unfortunate events. This object MUST be + * dynamically allocated to avoid it being deleted after a cpu switch happens. + * */ +class CpuEvent : public Event +{ + private: + /** type of global list of cpu events. */ + typedef std::vector<CpuEvent *> CpuEventList; + + /** Static list of cpu events that is searched every time a cpu switch + * happens. */ + static CpuEventList cpuEventList; + + /** The thread context that is switched to the new cpus. */ + ThreadContext *tc; + + public: + CpuEvent(EventQueue *q, ThreadContext *_tc, Priority p = Default_Pri) + : Event(q, p), tc(_tc) + { cpuEventList.push_back(this); } + + /** delete the cpu event from the global list. */ + ~CpuEvent(); + + /** Update all events switching old tc to new tc. + * @param oldTc the old thread context we are switching from + * @param newTc the new thread context we are switching to. + */ + static void replaceThreadContext(ThreadContext *oldTc, + ThreadContext *newTc); +}; + +template <class T, void (T::* F)(ThreadContext *tc)> +class CpuEventWrapper : public CpuEvent +{ + private: + T *object; + + public: + CpuEventWrapper(T *obj, ThreadContext *_tc, EventQueue *q = &mainEventQueue, + Priority p = Default_Pri) + : CpuEvent(q, _tc, p), object(obj) + { } + void process() { (object->*F)(tc); } +}; + +#endif // __CPU_CPUEVENT_HH__ + diff --git a/src/cpu/exec_context.hh b/src/cpu/exec_context.hh new file mode 100644 index 000000000..f6e8d7c25 --- /dev/null +++ b/src/cpu/exec_context.hh @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#error "Cannot include this file" + +/** + * The ExecContext is not a usable class. It is simply here for + * documentation purposes. It shows the interface that is used by the + * ISA to access and change CPU state. + */ +class ExecContext { + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to reduce overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + /** Reads an integer register. */ + uint64_t readIntReg(const StaticInst *si, int idx); + + /** Reads a floating point register of a specific width. */ + FloatReg readFloatReg(const StaticInst *si, int idx, int width); + + /** Reads a floating point register of single register width. */ + FloatReg readFloatReg(const StaticInst *si, int idx); + + /** Reads a floating point register of a specific width in its + * binary format, instead of by value. */ + FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width); + + /** Reads a floating point register in its binary format, instead + * of by value. */ + FloatRegBits readFloatRegBits(const StaticInst *si, int idx); + + /** Sets an integer register to a value. */ + void setIntReg(const StaticInst *si, int idx, uint64_t val); + + /** Sets a floating point register of a specific width to a value. */ + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width); + + /** Sets a floating point register of single width to a value. */ + void setFloatReg(const StaticInst *si, int idx, FloatReg val); + + /** Sets the bits of a floating point register of a specific width + * to a binary value. */ + void setFloatRegBits(const StaticInst *si, int idx, + FloatRegBits val, int width); + + /** Sets the bits of a floating point register of single width + * to a binary value. */ + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val); + + /** Reads the PC. */ + uint64_t readPC(); + /** Reads the NextPC. */ + uint64_t readNextPC(); + /** Reads the Next-NextPC. Only for architectures like SPARC or MIPS. */ + uint64_t readNextNPC(); + + /** Sets the PC. */ + void setPC(uint64_t val); + /** Sets the NextPC. */ + void setNextPC(uint64_t val); + /** Sets the Next-NextPC. Only for architectures like SPARC or MIPS. */ + void setNextNPC(uint64_t val); + + /** Reads a miscellaneous register. */ + MiscReg readMiscReg(int misc_reg); + + /** Reads a miscellaneous register, handling any architectural + * side effects due to reading that register. */ + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault); + + /** Sets a miscellaneous register. */ + Fault setMiscReg(int misc_reg, const MiscReg &val); + + /** Sets a miscellaneous register, handling any architectural + * side effects due to writing that register. */ + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val); + + /** Records the effective address of the instruction. Only valid + * for memory ops. */ + void setEA(Addr EA); + /** Returns the effective address of the instruction. Only valid + * for memory ops. */ + Addr getEA(); + + /** Returns a pointer to the ThreadContext. */ + ThreadContext *tcBase(); + + /** Reads an address, creating a memory request with the given + * flags. Stores result of read in data. */ + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + /** Writes to an address, creating a memory request with the given + * flags. Writes data to memory. For store conditionals, returns + * the result of the store in res. */ + template <class T> + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); + + /** Prefetches an address, creating a memory request with the + * given flags. */ + void prefetch(Addr addr, unsigned flags); + + /** Hints to the memory system that an address will be written to + * soon, with the given size. Creates a memory request with the + * given flags. */ + void writeHint(Addr addr, int size, unsigned flags); + +#if FULL_SYSTEM + /** Somewhat Alpha-specific function that handles returning from + * an error or interrupt. */ + Fault hwrei(); + /** Reads the interrupt flags. */ + int readIntrFlag(); + /** Sets the interrupt flags to a value. */ + void setIntrFlag(int val); + + /** + * Check for special simulator handling of specific PAL calls. If + * return value is false, actual PAL call will be suppressed. + */ + bool simPalCheck(int palFunc); +#else + /** Executes a syscall specified by the callnum. */ + void syscall(int64_t callnum); +#endif +}; diff --git a/src/cpu/exetrace.cc b/src/cpu/exetrace.cc new file mode 100644 index 000000000..8b1e60aea --- /dev/null +++ b/src/cpu/exetrace.cc @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Lisa Hsu + * Nathan Binkert + * Steve Raasch + */ + +#include <fstream> +#include <iomanip> + +#include "arch/regfile.hh" +#include "base/loader/symtab.hh" +#include "cpu/base.hh" +#include "cpu/exetrace.hh" +#include "cpu/static_inst.hh" +#include "sim/param.hh" +#include "sim/system.hh" + +//XXX This is temporary +#include "arch/isa_specific.hh" + +using namespace std; +using namespace TheISA; + +//////////////////////////////////////////////////////////////////////// +// +// Methods for the InstRecord object +// + + +void +Trace::InstRecord::dump(ostream &outs) +{ + if (flags[PRINT_REG_DELTA]) + { +#if THE_ISA == SPARC_ISA + static uint64_t regs[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}; + static uint64_t ccr = 0; + static uint64_t y = 0; + static uint64_t floats[32]; + uint64_t newVal; + static const char * prefixes[4] = {"G", "O", "L", "I"}; + + char buf[256]; + sprintf(buf, "PC = 0x%016llx", thread->readNextPC()); + outs << buf; + sprintf(buf, " NPC = 0x%016llx", thread->readNextNPC()); + outs << buf; + newVal = thread->readMiscReg(SparcISA::MISCREG_CCR); + if(newVal != ccr) + { + sprintf(buf, " CCR = 0x%016llx", newVal); + outs << buf; + ccr = newVal; + } + newVal = thread->readMiscReg(SparcISA::MISCREG_Y); + if(newVal != y) + { + sprintf(buf, " Y = 0x%016llx", newVal); + outs << buf; + y = newVal; + } + for(int y = 0; y < 4; y++) + { + for(int x = 0; x < 8; x++) + { + int index = x + 8 * y; + newVal = thread->readIntReg(index); + if(regs[index] != newVal) + { + sprintf(buf, " %s%d = 0x%016llx", prefixes[y], x, newVal); + outs << buf; + regs[index] = newVal; + } + } + } + for(int y = 0; y < 32; y++) + { + newVal = thread->readFloatRegBits(2 * y, 64); + if(floats[y] != newVal) + { + sprintf(buf, " F%d = 0x%016llx", y, newVal); + outs << buf; + floats[y] = newVal; + } + } + outs << endl; +#endif + } + else if (flags[INTEL_FORMAT]) { +#if FULL_SYSTEM + bool is_trace_system = (thread->getCpuPtr()->system->name() == trace_system); +#else + bool is_trace_system = true; +#endif + if (is_trace_system) { + ccprintf(outs, "%7d ) ", cycle); + outs << "0x" << hex << PC << ":\t"; + if (staticInst->isLoad()) { + outs << "<RD 0x" << hex << addr; + outs << ">"; + } else if (staticInst->isStore()) { + outs << "<WR 0x" << hex << addr; + outs << ">"; + } + outs << endl; + } + } else { + if (flags[PRINT_CYCLE]) + ccprintf(outs, "%7d: ", cycle); + + outs << thread->getCpuPtr()->name() << " "; + + if (flags[TRACE_MISSPEC]) + outs << (misspeculating ? "-" : "+") << " "; + + if (flags[PRINT_THREAD_NUM]) + outs << "T" << thread->getThreadNum() << " : "; + + + std::string sym_str; + Addr sym_addr; + if (debugSymbolTable + && debugSymbolTable->findNearestSymbol(PC, sym_str, sym_addr) + && flags[PC_SYMBOL]) { + if (PC != sym_addr) + sym_str += csprintf("+%d", PC - sym_addr); + outs << "@" << sym_str << " : "; + } + else { + outs << "0x" << hex << PC << " : "; + } + + // + // Print decoded instruction + // + +#if defined(__GNUC__) && (__GNUC__ < 3) + // There's a bug in gcc 2.x library that prevents setw() + // from working properly on strings + string mc(staticInst->disassemble(PC, debugSymbolTable)); + while (mc.length() < 26) + mc += " "; + outs << mc; +#else + outs << setw(26) << left << staticInst->disassemble(PC, debugSymbolTable); +#endif + + outs << " : "; + + if (flags[PRINT_OP_CLASS]) { + outs << opClassStrings[staticInst->opClass()] << " : "; + } + + if (flags[PRINT_RESULT_DATA] && data_status != DataInvalid) { + outs << " D="; +#if 0 + if (data_status == DataDouble) + ccprintf(outs, "%f", data.as_double); + else + ccprintf(outs, "%#018x", data.as_int); +#else + ccprintf(outs, "%#018x", data.as_int); +#endif + } + + if (flags[PRINT_EFF_ADDR] && addr_valid) + outs << " A=0x" << hex << addr; + + if (flags[PRINT_INT_REGS] && regs_valid) { + for (int i = 0; i < TheISA::NumIntRegs;) + for (int j = i + 1; i <= j; i++) + ccprintf(outs, "r%02d = %#018x%s", i, + iregs->regs.readReg(i), + ((i == j) ? "\n" : " ")); + outs << "\n"; + } + + if (flags[PRINT_FETCH_SEQ] && fetch_seq_valid) + outs << " FetchSeq=" << dec << fetch_seq; + + if (flags[PRINT_CP_SEQ] && cp_seq_valid) + outs << " CPSeq=" << dec << cp_seq; + + // + // End of line... + // + outs << endl; + } +} + + +vector<bool> Trace::InstRecord::flags(NUM_BITS); +string Trace::InstRecord::trace_system; + +//////////////////////////////////////////////////////////////////////// +// +// Parameter space for per-cycle execution address tracing options. +// Derive from ParamContext so we can override checkParams() function. +// +class ExecutionTraceParamContext : public ParamContext +{ + public: + ExecutionTraceParamContext(const string &_iniSection) + : ParamContext(_iniSection) + { + } + + void checkParams(); // defined at bottom of file +}; + +ExecutionTraceParamContext exeTraceParams("exetrace"); + +Param<bool> exe_trace_spec(&exeTraceParams, "speculative", + "capture speculative instructions", true); + +Param<bool> exe_trace_print_cycle(&exeTraceParams, "print_cycle", + "print cycle number", true); +Param<bool> exe_trace_print_opclass(&exeTraceParams, "print_opclass", + "print op class", true); +Param<bool> exe_trace_print_thread(&exeTraceParams, "print_thread", + "print thread number", true); +Param<bool> exe_trace_print_effaddr(&exeTraceParams, "print_effaddr", + "print effective address", true); +Param<bool> exe_trace_print_data(&exeTraceParams, "print_data", + "print result data", true); +Param<bool> exe_trace_print_iregs(&exeTraceParams, "print_iregs", + "print all integer regs", false); +Param<bool> exe_trace_print_fetchseq(&exeTraceParams, "print_fetchseq", + "print fetch sequence number", false); +Param<bool> exe_trace_print_cp_seq(&exeTraceParams, "print_cpseq", + "print correct-path sequence number", false); +Param<bool> exe_trace_print_reg_delta(&exeTraceParams, "print_reg_delta", + "print which registers changed to what", false); +Param<bool> exe_trace_pc_symbol(&exeTraceParams, "pc_symbol", + "Use symbols for the PC if available", true); +Param<bool> exe_trace_intel_format(&exeTraceParams, "intel_format", + "print trace in intel compatible format", false); +Param<string> exe_trace_system(&exeTraceParams, "trace_system", + "print trace of which system (client or server)", + "client"); + + +// +// Helper function for ExecutionTraceParamContext::checkParams() just +// to get us into the InstRecord namespace +// +void +Trace::InstRecord::setParams() +{ + flags[TRACE_MISSPEC] = exe_trace_spec; + + flags[PRINT_CYCLE] = exe_trace_print_cycle; + flags[PRINT_OP_CLASS] = exe_trace_print_opclass; + flags[PRINT_THREAD_NUM] = exe_trace_print_thread; + flags[PRINT_RESULT_DATA] = exe_trace_print_effaddr; + flags[PRINT_EFF_ADDR] = exe_trace_print_data; + flags[PRINT_INT_REGS] = exe_trace_print_iregs; + flags[PRINT_FETCH_SEQ] = exe_trace_print_fetchseq; + flags[PRINT_CP_SEQ] = exe_trace_print_cp_seq; + flags[PRINT_REG_DELTA] = exe_trace_print_reg_delta; + flags[PC_SYMBOL] = exe_trace_pc_symbol; + flags[INTEL_FORMAT] = exe_trace_intel_format; + trace_system = exe_trace_system; +} + +void +ExecutionTraceParamContext::checkParams() +{ + Trace::InstRecord::setParams(); +} + diff --git a/src/cpu/exetrace.hh b/src/cpu/exetrace.hh new file mode 100644 index 000000000..02ea162f0 --- /dev/null +++ b/src/cpu/exetrace.hh @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +#ifndef __EXETRACE_HH__ +#define __EXETRACE_HH__ + +#include <fstream> +#include <vector> + +#include "sim/host.hh" +#include "cpu/inst_seq.hh" // for InstSeqNum +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "cpu/static_inst.hh" + +class ThreadContext; + + +namespace Trace { + +class InstRecord : public Record +{ + protected: + typedef TheISA::IntRegFile IntRegFile; + + // The following fields are initialized by the constructor and + // thus guaranteed to be valid. + ThreadContext *thread; + // need to make this ref-counted so it doesn't go away before we + // dump the record + StaticInstPtr staticInst; + Addr PC; + bool misspeculating; + + // The remaining fields are only valid for particular instruction + // types (e.g, addresses for memory ops) or when particular + // options are enabled (e.g., tracing full register contents). + // Each data field has an associated valid flag to indicate + // whether the data field is valid. + Addr addr; + bool addr_valid; + + union { + uint64_t as_int; + double as_double; + } data; + enum { + DataInvalid = 0, + DataInt8 = 1, // set to equal number of bytes + DataInt16 = 2, + DataInt32 = 4, + DataInt64 = 8, + DataDouble = 3 + } data_status; + + InstSeqNum fetch_seq; + bool fetch_seq_valid; + + InstSeqNum cp_seq; + bool cp_seq_valid; + + struct iRegFile { + IntRegFile regs; + }; + iRegFile *iregs; + bool regs_valid; + + public: + InstRecord(Tick _cycle, ThreadContext *_thread, + const StaticInstPtr &_staticInst, + Addr _pc, bool spec) + : Record(_cycle), thread(_thread), + staticInst(_staticInst), PC(_pc), + misspeculating(spec) + { + data_status = DataInvalid; + addr_valid = false; + regs_valid = false; + + fetch_seq_valid = false; + cp_seq_valid = false; + } + + virtual ~InstRecord() { } + + virtual void dump(std::ostream &outs); + + void setAddr(Addr a) { addr = a; addr_valid = true; } + + void setData(uint64_t d) { data.as_int = d; data_status = DataInt64; } + void setData(uint32_t d) { data.as_int = d; data_status = DataInt32; } + void setData(uint16_t d) { data.as_int = d; data_status = DataInt16; } + void setData(uint8_t d) { data.as_int = d; data_status = DataInt8; } + + void setData(int64_t d) { setData((uint64_t)d); } + void setData(int32_t d) { setData((uint32_t)d); } + void setData(int16_t d) { setData((uint16_t)d); } + void setData(int8_t d) { setData((uint8_t)d); } + + void setData(double d) { data.as_double = d; data_status = DataDouble; } + + void setFetchSeq(InstSeqNum seq) + { fetch_seq = seq; fetch_seq_valid = true; } + + void setCPSeq(InstSeqNum seq) + { cp_seq = seq; cp_seq_valid = true; } + + void setRegs(const IntRegFile ®s); + + void finalize() { theLog.append(this); } + + enum InstExecFlagBits { + TRACE_MISSPEC = 0, + PRINT_CYCLE, + PRINT_OP_CLASS, + PRINT_THREAD_NUM, + PRINT_RESULT_DATA, + PRINT_EFF_ADDR, + PRINT_INT_REGS, + PRINT_FETCH_SEQ, + PRINT_CP_SEQ, + PRINT_REG_DELTA, + PC_SYMBOL, + INTEL_FORMAT, + NUM_BITS + }; + + static std::vector<bool> flags; + static std::string trace_system; + + static void setParams(); + + static bool traceMisspec() { return flags[TRACE_MISSPEC]; } +}; + + +inline void +InstRecord::setRegs(const IntRegFile ®s) +{ + if (!iregs) + iregs = new iRegFile; + + memcpy(&iregs->regs, ®s, sizeof(IntRegFile)); + regs_valid = true; +} + +inline +InstRecord * +getInstRecord(Tick cycle, ThreadContext *tc, + const StaticInstPtr staticInst, + Addr pc) +{ + if (DTRACE(InstExec) && + (InstRecord::traceMisspec() || !tc->misspeculating())) { + return new InstRecord(cycle, tc, staticInst, pc, + tc->misspeculating()); + } + + return NULL; +} + + +} + +#endif // __EXETRACE_HH__ diff --git a/src/cpu/func_unit.cc b/src/cpu/func_unit.cc new file mode 100644 index 000000000..c20578a43 --- /dev/null +++ b/src/cpu/func_unit.cc @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2002-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Raasch + */ + +#include <sstream> + +#include "base/misc.hh" +#include "cpu/func_unit.hh" +#include "sim/builder.hh" + +using namespace std; + + +//////////////////////////////////////////////////////////////////////////// +// +// The funciton unit +// +FuncUnit::FuncUnit() +{ + capabilityList.reset(); +} + + +// Copy constructor +FuncUnit::FuncUnit(const FuncUnit &fu) +{ + + for (int i = 0; i < Num_OpClasses; ++i) { + opLatencies[i] = fu.opLatencies[i]; + issueLatencies[i] = fu.issueLatencies[i]; + } + + capabilityList = fu.capabilityList; +} + + +void +FuncUnit::addCapability(OpClass cap, unsigned oplat, unsigned issuelat) +{ + if (issuelat == 0 || oplat == 0) + panic("FuncUnit: you don't really want a zero-cycle latency do you?"); + + capabilityList.set(cap); + + opLatencies[cap] = oplat; + issueLatencies[cap] = issuelat; +} + +bool +FuncUnit::provides(OpClass capability) +{ + return capabilityList[capability]; +} + +bitset<Num_OpClasses> +FuncUnit::capabilities() +{ + return capabilityList; +} + +unsigned & +FuncUnit::opLatency(OpClass cap) +{ + return opLatencies[cap]; +} + +unsigned +FuncUnit::issueLatency(OpClass capability) +{ + return issueLatencies[capability]; +} + +//////////////////////////////////////////////////////////////////////////// +// +// The SimObjects we use to get the FU information into the simulator +// +//////////////////////////////////////////////////////////////////////////// + +// +// We use 2 objects to specify this data in the INI file: +// (1) OpDesc - Describes the operation class & latencies +// (multiple OpDesc objects can refer to the same +// operation classes) +// (2) FUDesc - Describes the operations available in the unit & +// the number of these units +// +// + + +// +// The operation-class description object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(OpDesc) + + SimpleEnumParam<OpClass> opClass; + Param<unsigned> opLat; + Param<unsigned> issueLat; + +END_DECLARE_SIM_OBJECT_PARAMS(OpDesc) + +BEGIN_INIT_SIM_OBJECT_PARAMS(OpDesc) + + INIT_ENUM_PARAM(opClass, "type of operation", opClassStrings), + INIT_PARAM(opLat, "cycles until result is available"), + INIT_PARAM(issueLat, "cycles until another can be issued") + +END_INIT_SIM_OBJECT_PARAMS(OpDesc) + + +CREATE_SIM_OBJECT(OpDesc) +{ + return new OpDesc(getInstanceName(), opClass, opLat, issueLat); +} + +REGISTER_SIM_OBJECT("OpDesc", OpDesc) + + +// +// The FuDesc object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(FUDesc) + + SimObjectVectorParam<OpDesc *> opList; + Param<unsigned> count; + +END_DECLARE_SIM_OBJECT_PARAMS(FUDesc) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(FUDesc) + + INIT_PARAM(opList, "list of operation classes for this FU type"), + INIT_PARAM(count, "number of these FU's available") + +END_INIT_SIM_OBJECT_PARAMS(FUDesc) + + +CREATE_SIM_OBJECT(FUDesc) +{ + return new FUDesc(getInstanceName(), opList, count); +} + +REGISTER_SIM_OBJECT("FUDesc", FUDesc) + diff --git a/src/cpu/func_unit.hh b/src/cpu/func_unit.hh new file mode 100644 index 000000000..780143096 --- /dev/null +++ b/src/cpu/func_unit.hh @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2002-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Raasch + */ + +#ifndef __CPU_FUNC_UNIT_HH__ +#define __CPU_FUNC_UNIT_HH__ + +#include <bitset> +#include <string> +#include <vector> + +#include "cpu/op_class.hh" +#include "sim/sim_object.hh" + +//////////////////////////////////////////////////////////////////////////// +// +// Structures used ONLY during the initialization phase... +// +// +// + +struct OpDesc : public SimObject +{ + OpClass opClass; + unsigned opLat; + unsigned issueLat; + + OpDesc(std::string name, OpClass c, unsigned o, unsigned i) + : SimObject(name), opClass(c), opLat(o), issueLat(i) {}; +}; + +struct FUDesc : public SimObject +{ + std::vector<OpDesc *> opDescList; + unsigned number; + + FUDesc(std::string name, std::vector<OpDesc *> l, unsigned n) + : SimObject(name), opDescList(l), number(n) {}; +}; + +typedef std::vector<OpDesc *>::iterator OPDDiterator; +typedef std::vector<FUDesc *>::iterator FUDDiterator; + + + + +//////////////////////////////////////////////////////////////////////////// +// +// The actual FU object +// +// +// +class FuncUnit +{ + private: + unsigned opLatencies[Num_OpClasses]; + unsigned issueLatencies[Num_OpClasses]; + std::bitset<Num_OpClasses> capabilityList; + + public: + FuncUnit(); + FuncUnit(const FuncUnit &fu); + + std::string name; + + void addCapability(OpClass cap, unsigned oplat, unsigned issuelat); + + bool provides(OpClass capability); + std::bitset<Num_OpClasses> capabilities(); + + unsigned &opLatency(OpClass capability); + unsigned issueLatency(OpClass capability); +}; + +#endif // __FU_POOL_HH__ diff --git a/src/cpu/inst_seq.hh b/src/cpu/inst_seq.hh new file mode 100644 index 000000000..e7acd215f --- /dev/null +++ b/src/cpu/inst_seq.hh @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2001, 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Raasch + * Nathan Binkert + */ + +#ifndef __STD_TYPES_HH__ +#define __STD_TYPES_HH__ + +#include <stdint.h> + +// inst sequence type, used to order instructions in the ready list, +// if this rolls over the ready list order temporarily will get messed +// up, but execution will continue and complete correctly +typedef uint64_t InstSeqNum; + +// inst tag type, used to tag an operation instance in the IQ +typedef unsigned int InstTag; + +#endif // __STD_TYPES_HH__ diff --git a/src/cpu/intr_control.cc b/src/cpu/intr_control.cc new file mode 100644 index 000000000..4cbc86891 --- /dev/null +++ b/src/cpu/intr_control.cc @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ron Dreslinski + */ + +#include <string> +#include <vector> + +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "cpu/intr_control.hh" +#include "sim/builder.hh" +#include "sim/sim_object.hh" + +using namespace std; + +IntrControl::IntrControl(const string &name, BaseCPU *c) + : SimObject(name), cpu(c) +{} + +/* @todo + *Fix the cpu sim object parameter to be a system pointer + *instead, to avoid some extra dereferencing + */ +void +IntrControl::post(int int_num, int index) +{ + std::vector<ThreadContext *> &tcvec = cpu->system->threadContexts; + BaseCPU *temp = tcvec[0]->getCpuPtr(); + temp->post_interrupt(int_num, index); +} + +void +IntrControl::post(int cpu_id, int int_num, int index) +{ + std::vector<ThreadContext *> &tcvec = cpu->system->threadContexts; + BaseCPU *temp = tcvec[cpu_id]->getCpuPtr(); + temp->post_interrupt(int_num, index); +} + +void +IntrControl::clear(int int_num, int index) +{ + std::vector<ThreadContext *> &tcvec = cpu->system->threadContexts; + BaseCPU *temp = tcvec[0]->getCpuPtr(); + temp->clear_interrupt(int_num, index); +} + +void +IntrControl::clear(int cpu_id, int int_num, int index) +{ + std::vector<ThreadContext *> &tcvec = cpu->system->threadContexts; + BaseCPU *temp = tcvec[cpu_id]->getCpuPtr(); + temp->clear_interrupt(int_num, index); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(IntrControl) + + SimObjectParam<BaseCPU *> cpu; + +END_DECLARE_SIM_OBJECT_PARAMS(IntrControl) + +BEGIN_INIT_SIM_OBJECT_PARAMS(IntrControl) + + INIT_PARAM(cpu, "the cpu") + +END_INIT_SIM_OBJECT_PARAMS(IntrControl) + +CREATE_SIM_OBJECT(IntrControl) +{ + return new IntrControl(getInstanceName(), cpu); +} + +REGISTER_SIM_OBJECT("IntrControl", IntrControl) diff --git a/src/cpu/intr_control.hh b/src/cpu/intr_control.hh new file mode 100644 index 000000000..2e3f9e038 --- /dev/null +++ b/src/cpu/intr_control.hh @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ron Dreslinski + */ + +#ifndef __INTR_CONTROL_HH__ +#define __INTR_CONTROL_HH__ + +#include <vector> +#include "base/misc.hh" +#include "cpu/base.hh" +#include "sim/sim_object.hh" +#include "sim/system.hh" + + +class IntrControl : public SimObject +{ + public: + BaseCPU *cpu; + IntrControl(const std::string &name, BaseCPU *c); + + void clear(int int_num, int index = 0); + void post(int int_num, int index = 0); + void clear(int cpu_id, int int_num, int index); + void post(int cpu_id, int int_num, int index); +}; + +#endif // __INTR_CONTROL_HH__ + + + + + + + diff --git a/src/cpu/memtest/memtest.cc b/src/cpu/memtest/memtest.cc new file mode 100644 index 000000000..7ea9eaefc --- /dev/null +++ b/src/cpu/memtest/memtest.cc @@ -0,0 +1,443 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Steve Reinhardt + */ + +// FIX ME: make trackBlkAddr use blocksize from actual cache, not hard coded + +#include <iomanip> +#include <set> +#include <string> +#include <vector> + +#include "base/misc.hh" +#include "base/statistics.hh" +#include "cpu/simple_thread.hh" +#include "cpu/memtest/memtest.hh" +#include "mem/cache/base_cache.hh" +#include "sim/builder.hh" +#include "sim/sim_events.hh" +#include "sim/stats.hh" + +using namespace std; +using namespace TheISA; + +int TESTER_ALLOCATOR=0; + +MemTest::MemTest(const string &name, + MemInterface *_cache_interface, + FunctionalMemory *main_mem, + FunctionalMemory *check_mem, + unsigned _memorySize, + unsigned _percentReads, + unsigned _percentCopies, + unsigned _percentUncacheable, + unsigned _progressInterval, + unsigned _percentSourceUnaligned, + unsigned _percentDestUnaligned, + Addr _traceAddr, + Counter _max_loads) + : SimObject(name), + tickEvent(this), + cacheInterface(_cache_interface), + mainMem(main_mem), + checkMem(check_mem), + size(_memorySize), + percentReads(_percentReads), + percentCopies(_percentCopies), + percentUncacheable(_percentUncacheable), + progressInterval(_progressInterval), + nextProgressMessage(_progressInterval), + percentSourceUnaligned(_percentSourceUnaligned), + percentDestUnaligned(percentDestUnaligned), + maxLoads(_max_loads) +{ + vector<string> cmd; + cmd.push_back("/bin/ls"); + vector<string> null_vec; + thread = new SimpleThread(NULL, 0, mainMem, 0); + + blockSize = cacheInterface->getBlockSize(); + blockAddrMask = blockSize - 1; + traceBlockAddr = blockAddr(_traceAddr); + + //setup data storage with interesting values + uint8_t *data1 = new uint8_t[size]; + uint8_t *data2 = new uint8_t[size]; + uint8_t *data3 = new uint8_t[size]; + memset(data1, 1, size); + memset(data2, 2, size); + memset(data3, 3, size); + curTick = 0; + + baseAddr1 = 0x100000; + baseAddr2 = 0x400000; + uncacheAddr = 0x800000; + + // set up intial memory contents here + mainMem->prot_write(baseAddr1, data1, size); + checkMem->prot_write(baseAddr1, data1, size); + mainMem->prot_write(baseAddr2, data2, size); + checkMem->prot_write(baseAddr2, data2, size); + mainMem->prot_write(uncacheAddr, data3, size); + checkMem->prot_write(uncacheAddr, data3, size); + + delete [] data1; + delete [] data2; + delete [] data3; + + // set up counters + noResponseCycles = 0; + numReads = 0; + tickEvent.schedule(0); + + id = TESTER_ALLOCATOR++; +} + +static void +printData(ostream &os, uint8_t *data, int nbytes) +{ + os << hex << setfill('0'); + // assume little-endian: print bytes from highest address to lowest + for (uint8_t *dp = data + nbytes - 1; dp >= data; --dp) { + os << setw(2) << (unsigned)*dp; + } + os << dec; +} + +void +MemTest::completeRequest(MemReqPtr &req, uint8_t *data) +{ + //Remove the address from the list of outstanding + std::set<unsigned>::iterator removeAddr = outstandingAddrs.find(req->paddr); + assert(removeAddr != outstandingAddrs.end()); + outstandingAddrs.erase(removeAddr); + + switch (req->cmd) { + case Read: + if (memcmp(req->data, data, req->size) != 0) { + cerr << name() << ": on read of 0x" << hex << req->paddr + << " (0x" << hex << blockAddr(req->paddr) << ")" + << "@ cycle " << dec << curTick + << ", cache returns 0x"; + printData(cerr, req->data, req->size); + cerr << ", expected 0x"; + printData(cerr, data, req->size); + cerr << endl; + fatal(""); + } + + numReads++; + numReadsStat++; + + if (numReads == nextProgressMessage) { + ccprintf(cerr, "%s: completed %d read accesses @%d\n", + name(), numReads, curTick); + nextProgressMessage += progressInterval; + } + + if (numReads >= maxLoads) + SimExit(curTick, "Maximum number of loads reached!"); + break; + + case Write: + numWritesStat++; + break; + + case Copy: + //Also remove dest from outstanding list + removeAddr = outstandingAddrs.find(req->dest); + assert(removeAddr != outstandingAddrs.end()); + outstandingAddrs.erase(removeAddr); + numCopiesStat++; + break; + + default: + panic("invalid command"); + } + + if (blockAddr(req->paddr) == traceBlockAddr) { + cerr << name() << ": completed " + << (req->cmd.isWrite() ? "write" : "read") + << " access of " + << dec << req->size << " bytes at address 0x" + << hex << req->paddr + << " (0x" << hex << blockAddr(req->paddr) << ")" + << ", value = 0x"; + printData(cerr, req->data, req->size); + cerr << " @ cycle " << dec << curTick; + + cerr << endl; + } + + noResponseCycles = 0; + delete [] data; +} + + +void +MemTest::regStats() +{ + using namespace Stats; + + + numReadsStat + .name(name() + ".num_reads") + .desc("number of read accesses completed") + ; + + numWritesStat + .name(name() + ".num_writes") + .desc("number of write accesses completed") + ; + + numCopiesStat + .name(name() + ".num_copies") + .desc("number of copy accesses completed") + ; +} + +void +MemTest::tick() +{ + if (!tickEvent.scheduled()) + tickEvent.schedule(curTick + cycles(1)); + + if (++noResponseCycles >= 500000) { + cerr << name() << ": deadlocked at cycle " << curTick << endl; + fatal(""); + } + + if (cacheInterface->isBlocked()) { + return; + } + + //make new request + unsigned cmd = random() % 100; + unsigned offset = random() % size; + unsigned base = random() % 2; + uint64_t data = random(); + unsigned access_size = random() % 4; + unsigned cacheable = random() % 100; + + //If we aren't doing copies, use id as offset, and do a false sharing + //mem tester + if (percentCopies == 0) { + //We can eliminate the lower bits of the offset, and then use the id + //to offset within the blks + offset &= ~63; //Not the low order bits + offset += id; + access_size = 0; + } + + MemReqPtr req = new MemReq(); + + if (cacheable < percentUncacheable) { + req->flags |= UNCACHEABLE; + req->paddr = uncacheAddr + offset; + } else { + req->paddr = ((base) ? baseAddr1 : baseAddr2) + offset; + } + // bool probe = (random() % 2 == 1) && !req->isUncacheable(); + bool probe = false; + + req->size = 1 << access_size; + req->data = new uint8_t[req->size]; + req->paddr &= ~(req->size - 1); + req->time = curTick; + req->xc = thread->getProxy(); + + if (cmd < percentReads) { + // read + + //For now we only allow one outstanding request per addreess per tester + //This means we assume CPU does write forwarding to reads that alias something + //in the cpu store buffer. + if (outstandingAddrs.find(req->paddr) != outstandingAddrs.end()) return; + else outstandingAddrs.insert(req->paddr); + + req->cmd = Read; + uint8_t *result = new uint8_t[8]; + checkMem->access(Read, req->paddr, result, req->size); + if (blockAddr(req->paddr) == traceBlockAddr) { + cerr << name() + << ": initiating read " + << ((probe) ? "probe of " : "access of ") + << dec << req->size << " bytes from addr 0x" + << hex << req->paddr + << " (0x" << hex << blockAddr(req->paddr) << ")" + << " at cycle " + << dec << curTick << endl; + } + if (probe) { + cacheInterface->probeAndUpdate(req); + completeRequest(req, result); + } else { + req->completionEvent = new MemCompleteEvent(req, result, this); + cacheInterface->access(req); + } + } else if (cmd < (100 - percentCopies)){ + // write + + //For now we only allow one outstanding request per addreess per tester + //This means we assume CPU does write forwarding to reads that alias something + //in the cpu store buffer. + if (outstandingAddrs.find(req->paddr) != outstandingAddrs.end()) return; + else outstandingAddrs.insert(req->paddr); + + req->cmd = Write; + memcpy(req->data, &data, req->size); + checkMem->access(Write, req->paddr, req->data, req->size); + if (blockAddr(req->paddr) == traceBlockAddr) { + cerr << name() << ": initiating write " + << ((probe)?"probe of ":"access of ") + << dec << req->size << " bytes (value = 0x"; + printData(cerr, req->data, req->size); + cerr << ") to addr 0x" + << hex << req->paddr + << " (0x" << hex << blockAddr(req->paddr) << ")" + << " at cycle " + << dec << curTick << endl; + } + if (probe) { + cacheInterface->probeAndUpdate(req); + completeRequest(req, NULL); + } else { + req->completionEvent = new MemCompleteEvent(req, NULL, this); + cacheInterface->access(req); + } + } else { + // copy + unsigned source_align = random() % 100; + unsigned dest_align = random() % 100; + unsigned offset2 = random() % size; + + Addr source = ((base) ? baseAddr1 : baseAddr2) + offset; + Addr dest = ((base) ? baseAddr2 : baseAddr1) + offset2; + if (outstandingAddrs.find(source) != outstandingAddrs.end()) return; + else outstandingAddrs.insert(source); + if (outstandingAddrs.find(dest) != outstandingAddrs.end()) return; + else outstandingAddrs.insert(dest); + + if (source_align >= percentSourceUnaligned) { + source = blockAddr(source); + } + if (dest_align >= percentDestUnaligned) { + dest = blockAddr(dest); + } + req->cmd = Copy; + req->flags &= ~UNCACHEABLE; + req->paddr = source; + req->dest = dest; + delete [] req->data; + req->data = new uint8_t[blockSize]; + req->size = blockSize; + if (source == traceBlockAddr || dest == traceBlockAddr) { + cerr << name() + << ": initiating copy of " + << dec << req->size << " bytes from addr 0x" + << hex << source + << " (0x" << hex << blockAddr(source) << ")" + << " to addr 0x" + << hex << dest + << " (0x" << hex << blockAddr(dest) << ")" + << " at cycle " + << dec << curTick << endl; + } + cacheInterface->access(req); + uint8_t result[blockSize]; + checkMem->access(Read, source, &result, blockSize); + checkMem->access(Write, dest, &result, blockSize); + } +} + + +void +MemCompleteEvent::process() +{ + tester->completeRequest(req, data); + delete this; +} + + +const char * +MemCompleteEvent::description() +{ + return "memory access completion"; +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(MemTest) + + SimObjectParam<BaseCache *> cache; + SimObjectParam<FunctionalMemory *> main_mem; + SimObjectParam<FunctionalMemory *> check_mem; + Param<unsigned> memory_size; + Param<unsigned> percent_reads; + Param<unsigned> percent_copies; + Param<unsigned> percent_uncacheable; + Param<unsigned> progress_interval; + Param<unsigned> percent_source_unaligned; + Param<unsigned> percent_dest_unaligned; + Param<Addr> trace_addr; + Param<Counter> max_loads; + +END_DECLARE_SIM_OBJECT_PARAMS(MemTest) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(MemTest) + + INIT_PARAM(cache, "L1 cache"), + INIT_PARAM(main_mem, "hierarchical memory"), + INIT_PARAM(check_mem, "check memory"), + INIT_PARAM(memory_size, "memory size"), + INIT_PARAM(percent_reads, "target read percentage"), + INIT_PARAM(percent_copies, "target copy percentage"), + INIT_PARAM(percent_uncacheable, "target uncacheable percentage"), + INIT_PARAM(progress_interval, "progress report interval (in accesses)"), + INIT_PARAM(percent_source_unaligned, + "percent of copy source address that are unaligned"), + INIT_PARAM(percent_dest_unaligned, + "percent of copy dest address that are unaligned"), + INIT_PARAM(trace_addr, "address to trace"), + INIT_PARAM(max_loads, "terminate when we have reached this load count") + +END_INIT_SIM_OBJECT_PARAMS(MemTest) + + +CREATE_SIM_OBJECT(MemTest) +{ + return new MemTest(getInstanceName(), cache->getInterface(), main_mem, + check_mem, memory_size, percent_reads, percent_copies, + percent_uncacheable, progress_interval, + percent_source_unaligned, percent_dest_unaligned, + trace_addr, max_loads); +} + +REGISTER_SIM_OBJECT("MemTest", MemTest) diff --git a/src/cpu/memtest/memtest.hh b/src/cpu/memtest/memtest.hh new file mode 100644 index 000000000..42fb235db --- /dev/null +++ b/src/cpu/memtest/memtest.hh @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Steve Reinhardt + */ + +#ifndef __CPU_MEMTEST_MEMTEST_HH__ +#define __CPU_MEMTEST_MEMTEST_HH__ + +#include <set> + +#include "base/statistics.hh" +#include "mem/functional/functional.hh" +#include "mem/mem_interface.hh" +#include "sim/eventq.hh" +#include "sim/sim_exit.hh" +#include "sim/sim_object.hh" +#include "sim/stats.hh" + +class ThreadContext; +class MemTest : public SimObject +{ + public: + + MemTest(const std::string &name, + MemInterface *_cache_interface, + FunctionalMemory *main_mem, + FunctionalMemory *check_mem, + unsigned _memorySize, + unsigned _percentReads, + unsigned _percentCopies, + unsigned _percentUncacheable, + unsigned _progressInterval, + unsigned _percentSourceUnaligned, + unsigned _percentDestUnaligned, + Addr _traceAddr, + Counter _max_loads); + + // register statistics + virtual void regStats(); + + inline Tick cycles(int numCycles) const { return numCycles; } + + // main simulation loop (one cycle) + void tick(); + + protected: + class TickEvent : public Event + { + private: + MemTest *cpu; + public: + TickEvent(MemTest *c) + : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c) {} + void process() {cpu->tick();} + virtual const char *description() { return "tick event"; } + }; + + TickEvent tickEvent; + + MemInterface *cacheInterface; + FunctionalMemory *mainMem; + FunctionalMemory *checkMem; + SimpleThread *thread; + + unsigned size; // size of testing memory region + + unsigned percentReads; // target percentage of read accesses + unsigned percentCopies; // target percentage of copy accesses + unsigned percentUncacheable; + + int id; + + std::set<unsigned> outstandingAddrs; + + unsigned blockSize; + + Addr blockAddrMask; + + Addr blockAddr(Addr addr) + { + return (addr & ~blockAddrMask); + } + + Addr traceBlockAddr; + + Addr baseAddr1; // fix this to option + Addr baseAddr2; // fix this to option + Addr uncacheAddr; + + unsigned progressInterval; // frequency of progress reports + Tick nextProgressMessage; // access # for next progress report + + unsigned percentSourceUnaligned; + unsigned percentDestUnaligned; + + Tick noResponseCycles; + + uint64_t numReads; + uint64_t maxLoads; + Stats::Scalar<> numReadsStat; + Stats::Scalar<> numWritesStat; + Stats::Scalar<> numCopiesStat; + + // called by MemCompleteEvent::process() + void completeRequest(MemReqPtr &req, uint8_t *data); + + friend class MemCompleteEvent; +}; + + +class MemCompleteEvent : public Event +{ + MemReqPtr req; + uint8_t *data; + MemTest *tester; + + public: + + MemCompleteEvent(MemReqPtr &_req, uint8_t *_data, MemTest *_tester) + : Event(&mainEventQueue), + req(_req), data(_data), tester(_tester) + { + } + + void process(); + + virtual const char *description(); +}; + +#endif // __CPU_MEMTEST_MEMTEST_HH__ + + + diff --git a/src/cpu/o3/2bit_local_pred.cc b/src/cpu/o3/2bit_local_pred.cc new file mode 100644 index 000000000..77a45ea26 --- /dev/null +++ b/src/cpu/o3/2bit_local_pred.cc @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "base/intmath.hh" +#include "base/misc.hh" +#include "base/trace.hh" +#include "cpu/o3/2bit_local_pred.hh" + +LocalBP::LocalBP(unsigned _localPredictorSize, + unsigned _localCtrBits, + unsigned _instShiftAmt) + : localPredictorSize(_localPredictorSize), + localCtrBits(_localCtrBits), + instShiftAmt(_instShiftAmt) +{ + if (!isPowerOf2(localPredictorSize)) { + fatal("Invalid local predictor size!\n"); + } + + localPredictorSets = localPredictorSize / localCtrBits; + + if (!isPowerOf2(localPredictorSets)) { + fatal("Invalid number of local predictor sets! Check localCtrBits.\n"); + } + + // Setup the index mask. + indexMask = localPredictorSets - 1; + + DPRINTF(Fetch, "Branch predictor: index mask: %#x\n", indexMask); + + // Setup the array of counters for the local predictor. + localCtrs.resize(localPredictorSets); + + for (int i = 0; i < localPredictorSets; ++i) + localCtrs[i].setBits(_localCtrBits); + + DPRINTF(Fetch, "Branch predictor: local predictor size: %i\n", + localPredictorSize); + + DPRINTF(Fetch, "Branch predictor: local counter bits: %i\n", localCtrBits); + + DPRINTF(Fetch, "Branch predictor: instruction shift amount: %i\n", + instShiftAmt); +} + +void +LocalBP::reset() +{ + for (int i = 0; i < localPredictorSets; ++i) { + localCtrs[i].reset(); + } +} + +bool +LocalBP::lookup(Addr &branch_addr, void * &bp_history) +{ + bool taken; + uint8_t counter_val; + unsigned local_predictor_idx = getLocalIndex(branch_addr); + + DPRINTF(Fetch, "Branch predictor: Looking up index %#x\n", + local_predictor_idx); + + counter_val = localCtrs[local_predictor_idx].read(); + + DPRINTF(Fetch, "Branch predictor: prediction is %i.\n", + (int)counter_val); + + taken = getPrediction(counter_val); + +#if 0 + // Speculative update. + if (taken) { + DPRINTF(Fetch, "Branch predictor: Branch updated as taken.\n"); + localCtrs[local_predictor_idx].increment(); + } else { + DPRINTF(Fetch, "Branch predictor: Branch updated as not taken.\n"); + localCtrs[local_predictor_idx].decrement(); + } +#endif + + return taken; +} + +void +LocalBP::update(Addr &branch_addr, bool taken, void *bp_history) +{ + assert(bp_history == NULL); + unsigned local_predictor_idx; + + // Update the local predictor. + local_predictor_idx = getLocalIndex(branch_addr); + + DPRINTF(Fetch, "Branch predictor: Looking up index %#x\n", + local_predictor_idx); + + if (taken) { + DPRINTF(Fetch, "Branch predictor: Branch updated as taken.\n"); + localCtrs[local_predictor_idx].increment(); + } else { + DPRINTF(Fetch, "Branch predictor: Branch updated as not taken.\n"); + localCtrs[local_predictor_idx].decrement(); + } +} + +inline +bool +LocalBP::getPrediction(uint8_t &count) +{ + // Get the MSB of the count + return (count >> (localCtrBits - 1)); +} + +inline +unsigned +LocalBP::getLocalIndex(Addr &branch_addr) +{ + return (branch_addr >> instShiftAmt) & indexMask; +} diff --git a/src/cpu/o3/2bit_local_pred.hh b/src/cpu/o3/2bit_local_pred.hh new file mode 100644 index 000000000..954b86b4c --- /dev/null +++ b/src/cpu/o3/2bit_local_pred.hh @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_2BIT_LOCAL_PRED_HH__ +#define __CPU_O3_2BIT_LOCAL_PRED_HH__ + +#include "cpu/o3/sat_counter.hh" +#include "sim/host.hh" + +#include <vector> + +/** + * Implements a local predictor that uses the PC to index into a table of + * counters. Note that any time a pointer to the bp_history is given, it + * should be NULL using this predictor because it does not have any branch + * predictor state that needs to be recorded or updated; the update can be + * determined solely by the branch being taken or not taken. + */ +class LocalBP +{ + public: + /** + * Default branch predictor constructor. + * @param localPredictorSize Size of the local predictor. + * @param localCtrBits Number of bits per counter. + * @param instShiftAmt Offset amount for instructions to ignore alignment. + */ + LocalBP(unsigned localPredictorSize, unsigned localCtrBits, + unsigned instShiftAmt); + + /** + * Looks up the given address in the branch predictor and returns + * a true/false value as to whether it is taken. + * @param branch_addr The address of the branch to look up. + * @param bp_history Pointer to any bp history state. + * @return Whether or not the branch is taken. + */ + bool lookup(Addr &branch_addr, void * &bp_history); + + /** + * Updates the branch predictor with the actual result of a branch. + * @param branch_addr The address of the branch to update. + * @param taken Whether or not the branch was taken. + */ + void update(Addr &branch_addr, bool taken, void *bp_history); + + void squash(void *bp_history) + { assert(bp_history == NULL); } + + void reset(); + + private: + /** + * Returns the taken/not taken prediction given the value of the + * counter. + * @param count The value of the counter. + * @return The prediction based on the counter value. + */ + inline bool getPrediction(uint8_t &count); + + /** Calculates the local index based on the PC. */ + inline unsigned getLocalIndex(Addr &PC); + + /** Array of counters that make up the local predictor. */ + std::vector<SatCounter> localCtrs; + + /** Size of the local predictor. */ + unsigned localPredictorSize; + + /** Number of sets. */ + unsigned localPredictorSets; + + /** Number of bits of the local predictor's counters. */ + unsigned localCtrBits; + + /** Number of bits to shift the PC when calculating index. */ + unsigned instShiftAmt; + + /** Mask to get index bits. */ + unsigned indexMask; +}; + +#endif // __CPU_O3_2BIT_LOCAL_PRED_HH__ diff --git a/src/cpu/o3/SConscript b/src/cpu/o3/SConscript new file mode 100755 index 000000000..afbd4c533 --- /dev/null +++ b/src/cpu/o3/SConscript @@ -0,0 +1,77 @@ +# -*- mode:python -*- + +# Copyright (c) 2006 The Regents of The University of Michigan +# 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. +# +# Authors: Korey Sewell + +import os +import os.path +import sys + +# Import build environment variable from SConstruct. +Import('env') + + +################################################################# +# +# Include ISA-specific files for the O3 CPU-model +# +################################################################# + +sources = [] + +if env['TARGET_ISA'] == 'alpha': + sources += Split(''' + alpha/dyn_inst.cc + alpha/cpu.cc + alpha/thread_context.cc + alpha/cpu_builder.cc + ''') +elif env['TARGET_ISA'] == 'mips': + sources += Split(''' + mips/dyn_inst.cc + mips/cpu.cc + mips/thread_context.cc + mips/cpu_builder.cc + ''') +elif env['TARGET_ISA'] == 'sparc': + sources += Split(''' + sparc/dyn_inst.cc + sparc/cpu.cc + sparc/thread_context.cc + sparc/cpu_builder.cc + ''') +else: + sys.exit('O3 CPU does not support the \'%s\' ISA' % env['TARGET_ISA']) + + +# Convert file names to SCons File objects. This takes care of the +# path relative to the top of the directory tree. +sources = [File(s) for s in sources] + +Return('sources') + diff --git a/src/cpu/o3/alpha/cpu.cc b/src/cpu/o3/alpha/cpu.cc new file mode 100644 index 000000000..ed10b2fd1 --- /dev/null +++ b/src/cpu/o3/alpha/cpu.cc @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/alpha/impl.hh" +#include "cpu/o3/alpha/cpu_impl.hh" +#include "cpu/o3/alpha/dyn_inst.hh" + +// Force instantiation of AlphaO3CPU for all the implemntations that are +// needed. Consider merging this and alpha_dyn_inst.cc, and maybe all +// classes that depend on a certain impl, into one file (alpha_impl.cc?). +template class AlphaO3CPU<AlphaSimpleImpl>; diff --git a/src/cpu/o3/alpha/cpu.hh b/src/cpu/o3/alpha/cpu.hh new file mode 100644 index 000000000..9d97f9701 --- /dev/null +++ b/src/cpu/o3/alpha/cpu.hh @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_ALPHA_CPU_HH__ +#define __CPU_O3_ALPHA_CPU_HH__ + +#include "arch/regfile.hh" +#include "arch/types.hh" +#include "cpu/thread_context.hh" +#include "cpu/o3/cpu.hh" +#include "sim/byteswap.hh" + +class EndQuiesceEvent; +namespace Kernel { + class Statistics; +}; + +class TranslatingPort; + +/** + * AlphaO3CPU class. Derives from the FullO3CPU class, and + * implements all ISA and implementation specific functions of the + * CPU. This is the CPU class that is used for the SimObjects, and is + * what is given to the DynInsts. Most of its state exists in the + * FullO3CPU; the state is has is mainly for ISA specific + * functionality. + */ +template <class Impl> +class AlphaO3CPU : public FullO3CPU<Impl> +{ + protected: + typedef TheISA::IntReg IntReg; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscReg MiscReg; + typedef TheISA::RegFile RegFile; + typedef TheISA::MiscRegFile MiscRegFile; + + public: + typedef O3ThreadState<Impl> ImplState; + typedef O3ThreadState<Impl> Thread; + typedef typename Impl::Params Params; + + /** Constructs an AlphaO3CPU with the given parameters. */ + AlphaO3CPU(Params *params); + +#if FULL_SYSTEM + /** ITB pointer. */ + AlphaITB *itb; + /** DTB pointer. */ + AlphaDTB *dtb; +#endif + + /** Registers statistics. */ + void regStats(); + +#if FULL_SYSTEM + /** Translates instruction requestion. */ + Fault translateInstReq(RequestPtr &req, Thread *thread) + { + return itb->translate(req, thread->getTC()); + } + + /** Translates data read request. */ + Fault translateDataReadReq(RequestPtr &req, Thread *thread) + { + return dtb->translate(req, thread->getTC(), false); + } + + /** Translates data write request. */ + Fault translateDataWriteReq(RequestPtr &req, Thread *thread) + { + return dtb->translate(req, thread->getTC(), true); + } + +#else + /** Translates instruction requestion in syscall emulation mode. */ + Fault translateInstReq(RequestPtr &req, Thread *thread) + { + return thread->getProcessPtr()->pTable->translate(req); + } + + /** Translates data read request in syscall emulation mode. */ + Fault translateDataReadReq(RequestPtr &req, Thread *thread) + { + return thread->getProcessPtr()->pTable->translate(req); + } + + /** Translates data write request in syscall emulation mode. */ + Fault translateDataWriteReq(RequestPtr &req, Thread *thread) + { + return thread->getProcessPtr()->pTable->translate(req); + } + +#endif + /** Reads a miscellaneous register. */ + MiscReg readMiscReg(int misc_reg, unsigned tid); + + /** Reads a misc. register, including any side effects the read + * might have as defined by the architecture. + */ + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault, unsigned tid); + + /** Sets a miscellaneous register. */ + Fault setMiscReg(int misc_reg, const MiscReg &val, unsigned tid); + + /** Sets a misc. register, including any side effects the write + * might have as defined by the architecture. + */ + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val, unsigned tid); + + /** Initiates a squash of all in-flight instructions for a given + * thread. The source of the squash is an external update of + * state through the TC. + */ + void squashFromTC(unsigned tid); + +#if FULL_SYSTEM + /** Posts an interrupt. */ + void post_interrupt(int int_num, int index); + /** Reads the interrupt flag. */ + int readIntrFlag(); + /** Sets the interrupt flags. */ + void setIntrFlag(int val); + /** HW return from error interrupt. */ + Fault hwrei(unsigned tid); + /** Returns if a specific PC is a PAL mode PC. */ + bool inPalMode(uint64_t PC) + { return AlphaISA::PcPAL(PC); } + + bool simPalCheck(int palFunc, unsigned tid); + + /** Processes any interrupts. */ + void processInterrupts(); + + /** Halts the CPU. */ + void halt() { panic("Halt not implemented!\n"); } +#endif + + /** Traps to handle given fault. */ + void trap(Fault fault, unsigned tid); + +#if !FULL_SYSTEM + /** Executes a syscall. + * @todo: Determine if this needs to be virtual. + */ + void syscall(int64_t callnum, int tid); + /** Gets a syscall argument. */ + IntReg getSyscallArg(int i, int tid); + + /** Used to shift args for indirect syscall. */ + void setSyscallArg(int i, IntReg val, int tid); + + /** Sets the return value of a syscall. */ + void setSyscallReturn(SyscallReturn return_value, int tid); +#endif + + /** CPU read function, forwards read to LSQ. */ + template <class T> + Fault read(RequestPtr &req, T &data, int load_idx) + { + return this->iew.ldstQueue.read(req, data, load_idx); + } + + /** CPU write function, forwards write to LSQ. */ + template <class T> + Fault write(RequestPtr &req, T &data, int store_idx) + { + return this->iew.ldstQueue.write(req, data, store_idx); + } + + Addr lockAddr; + + /** Temporary fix for the lock flag, works in the UP case. */ + bool lockFlag; +}; + +#endif // __CPU_O3_ALPHA_CPU_HH__ diff --git a/src/cpu/o3/alpha/cpu_builder.cc b/src/cpu/o3/alpha/cpu_builder.cc new file mode 100644 index 000000000..fbf1f342c --- /dev/null +++ b/src/cpu/o3/alpha/cpu_builder.cc @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include <string> + +#include "cpu/base.hh" +#include "cpu/o3/alpha/cpu.hh" +#include "cpu/o3/alpha/impl.hh" +#include "cpu/o3/alpha/params.hh" +#include "cpu/o3/fu_pool.hh" +#include "sim/builder.hh" + +class DerivO3CPU : public AlphaO3CPU<AlphaSimpleImpl> +{ + public: + DerivO3CPU(AlphaSimpleParams *p) + : AlphaO3CPU<AlphaSimpleImpl>(p) + { } +}; + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(DerivO3CPU) + + Param<int> clock; + Param<int> numThreads; +Param<int> activity; + +#if FULL_SYSTEM +SimObjectParam<System *> system; +Param<int> cpu_id; +SimObjectParam<AlphaITB *> itb; +SimObjectParam<AlphaDTB *> dtb; +Param<Tick> profile; +#else +SimObjectVectorParam<Process *> workload; +#endif // FULL_SYSTEM + +SimObjectParam<MemObject *> mem; + +SimObjectParam<BaseCPU *> checker; + +Param<Counter> max_insts_any_thread; +Param<Counter> max_insts_all_threads; +Param<Counter> max_loads_any_thread; +Param<Counter> max_loads_all_threads; +Param<Counter> stats_reset_inst; +Param<Tick> progress_interval; + +Param<unsigned> cachePorts; + +Param<unsigned> decodeToFetchDelay; +Param<unsigned> renameToFetchDelay; +Param<unsigned> iewToFetchDelay; +Param<unsigned> commitToFetchDelay; +Param<unsigned> fetchWidth; + +Param<unsigned> renameToDecodeDelay; +Param<unsigned> iewToDecodeDelay; +Param<unsigned> commitToDecodeDelay; +Param<unsigned> fetchToDecodeDelay; +Param<unsigned> decodeWidth; + +Param<unsigned> iewToRenameDelay; +Param<unsigned> commitToRenameDelay; +Param<unsigned> decodeToRenameDelay; +Param<unsigned> renameWidth; + +Param<unsigned> commitToIEWDelay; +Param<unsigned> renameToIEWDelay; +Param<unsigned> issueToExecuteDelay; +Param<unsigned> dispatchWidth; +Param<unsigned> issueWidth; +Param<unsigned> wbWidth; +Param<unsigned> wbDepth; +SimObjectParam<FUPool *> fuPool; + +Param<unsigned> iewToCommitDelay; +Param<unsigned> renameToROBDelay; +Param<unsigned> commitWidth; +Param<unsigned> squashWidth; +Param<Tick> trapLatency; + +Param<unsigned> backComSize; +Param<unsigned> forwardComSize; + +Param<std::string> predType; +Param<unsigned> localPredictorSize; +Param<unsigned> localCtrBits; +Param<unsigned> localHistoryTableSize; +Param<unsigned> localHistoryBits; +Param<unsigned> globalPredictorSize; +Param<unsigned> globalCtrBits; +Param<unsigned> globalHistoryBits; +Param<unsigned> choicePredictorSize; +Param<unsigned> choiceCtrBits; + +Param<unsigned> BTBEntries; +Param<unsigned> BTBTagSize; + +Param<unsigned> RASSize; + +Param<unsigned> LQEntries; +Param<unsigned> SQEntries; +Param<unsigned> LFSTSize; +Param<unsigned> SSITSize; + +Param<unsigned> numPhysIntRegs; +Param<unsigned> numPhysFloatRegs; +Param<unsigned> numIQEntries; +Param<unsigned> numROBEntries; + +Param<unsigned> smtNumFetchingThreads; +Param<std::string> smtFetchPolicy; +Param<std::string> smtLSQPolicy; +Param<unsigned> smtLSQThreshold; +Param<std::string> smtIQPolicy; +Param<unsigned> smtIQThreshold; +Param<std::string> smtROBPolicy; +Param<unsigned> smtROBThreshold; +Param<std::string> smtCommitPolicy; + +Param<unsigned> instShiftAmt; + +Param<bool> defer_registration; + +Param<bool> function_trace; +Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(DerivO3CPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(DerivO3CPU) + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(numThreads, "number of HW thread contexts"), + INIT_PARAM_DFLT(activity, "Initial activity count", 0), + +#if FULL_SYSTEM + INIT_PARAM(system, "System object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(itb, "Instruction translation buffer"), + INIT_PARAM(dtb, "Data translation buffer"), + INIT_PARAM(profile, ""), +#else + INIT_PARAM(workload, "Processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM(mem, "Memory"), + + INIT_PARAM_DFLT(checker, "Checker CPU", NULL), + + INIT_PARAM_DFLT(max_insts_any_thread, + "Terminate when any thread reaches this inst count", + 0), + INIT_PARAM_DFLT(max_insts_all_threads, + "Terminate when all threads have reached" + "this inst count", + 0), + INIT_PARAM_DFLT(max_loads_any_thread, + "Terminate when any thread reaches this load count", + 0), + INIT_PARAM_DFLT(max_loads_all_threads, + "Terminate when all threads have reached this load" + "count", + 0), + INIT_PARAM_DFLT(stats_reset_inst, + "blah", + 0), + INIT_PARAM_DFLT(progress_interval, "Progress interval", 0), + + INIT_PARAM_DFLT(cachePorts, "Cache Ports", 200), + + INIT_PARAM(decodeToFetchDelay, "Decode to fetch delay"), + INIT_PARAM(renameToFetchDelay, "Rename to fetch delay"), + INIT_PARAM(iewToFetchDelay, "Issue/Execute/Writeback to fetch" + "delay"), + INIT_PARAM(commitToFetchDelay, "Commit to fetch delay"), + INIT_PARAM(fetchWidth, "Fetch width"), + INIT_PARAM(renameToDecodeDelay, "Rename to decode delay"), + INIT_PARAM(iewToDecodeDelay, "Issue/Execute/Writeback to decode" + "delay"), + INIT_PARAM(commitToDecodeDelay, "Commit to decode delay"), + INIT_PARAM(fetchToDecodeDelay, "Fetch to decode delay"), + INIT_PARAM(decodeWidth, "Decode width"), + + INIT_PARAM(iewToRenameDelay, "Issue/Execute/Writeback to rename" + "delay"), + INIT_PARAM(commitToRenameDelay, "Commit to rename delay"), + INIT_PARAM(decodeToRenameDelay, "Decode to rename delay"), + INIT_PARAM(renameWidth, "Rename width"), + + INIT_PARAM(commitToIEWDelay, "Commit to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(renameToIEWDelay, "Rename to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(issueToExecuteDelay, "Issue to execute delay (internal" + "to the IEW stage)"), + INIT_PARAM(dispatchWidth, "Dispatch width"), + INIT_PARAM(issueWidth, "Issue width"), + INIT_PARAM(wbWidth, "Writeback width"), + INIT_PARAM(wbDepth, "Writeback depth (number of cycles it can buffer)"), + INIT_PARAM_DFLT(fuPool, "Functional unit pool", NULL), + + INIT_PARAM(iewToCommitDelay, "Issue/Execute/Writeback to commit " + "delay"), + INIT_PARAM(renameToROBDelay, "Rename to reorder buffer delay"), + INIT_PARAM(commitWidth, "Commit width"), + INIT_PARAM(squashWidth, "Squash width"), + INIT_PARAM_DFLT(trapLatency, "Number of cycles before the trap is handled", 6), + + INIT_PARAM(backComSize, "Time buffer size for backwards communication"), + INIT_PARAM(forwardComSize, "Time buffer size for forward communication"), + + INIT_PARAM(predType, "Type of branch predictor ('local', 'tournament')"), + INIT_PARAM(localPredictorSize, "Size of local predictor"), + INIT_PARAM(localCtrBits, "Bits per counter"), + INIT_PARAM(localHistoryTableSize, "Size of local history table"), + INIT_PARAM(localHistoryBits, "Bits for the local history"), + INIT_PARAM(globalPredictorSize, "Size of global predictor"), + INIT_PARAM(globalCtrBits, "Bits per counter"), + INIT_PARAM(globalHistoryBits, "Bits of history"), + INIT_PARAM(choicePredictorSize, "Size of choice predictor"), + INIT_PARAM(choiceCtrBits, "Bits of choice counters"), + + INIT_PARAM(BTBEntries, "Number of BTB entries"), + INIT_PARAM(BTBTagSize, "Size of the BTB tags, in bits"), + + INIT_PARAM(RASSize, "RAS size"), + + INIT_PARAM(LQEntries, "Number of load queue entries"), + INIT_PARAM(SQEntries, "Number of store queue entries"), + INIT_PARAM(LFSTSize, "Last fetched store table size"), + INIT_PARAM(SSITSize, "Store set ID table size"), + + INIT_PARAM(numPhysIntRegs, "Number of physical integer registers"), + INIT_PARAM(numPhysFloatRegs, "Number of physical floating point " + "registers"), + INIT_PARAM(numIQEntries, "Number of instruction queue entries"), + INIT_PARAM(numROBEntries, "Number of reorder buffer entries"), + + INIT_PARAM_DFLT(smtNumFetchingThreads, "SMT Number of Fetching Threads", 1), + INIT_PARAM_DFLT(smtFetchPolicy, "SMT Fetch Policy", "SingleThread"), + INIT_PARAM_DFLT(smtLSQPolicy, "SMT LSQ Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtLSQThreshold,"SMT LSQ Threshold", 100), + INIT_PARAM_DFLT(smtIQPolicy, "SMT IQ Policy", "Partitioned"), + INIT_PARAM_DFLT(smtIQThreshold, "SMT IQ Threshold", 100), + INIT_PARAM_DFLT(smtROBPolicy, "SMT ROB Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtROBThreshold,"SMT ROB Threshold", 100), + INIT_PARAM_DFLT(smtCommitPolicy,"SMT Commit Fetch Policy", "RoundRobin"), + + INIT_PARAM(instShiftAmt, "Number of bits to shift instructions by"), + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(DerivO3CPU) + +CREATE_SIM_OBJECT(DerivO3CPU) +{ + DerivO3CPU *cpu; + +#if FULL_SYSTEM + // Full-system only supports a single thread for the moment. + int actual_num_threads = 1; +#else + // In non-full-system mode, we infer the number of threads from + // the workload if it's not explicitly specified. + int actual_num_threads = + (numThreads.isValid() && numThreads >= workload.size()) ? + numThreads : workload.size(); + + if (workload.size() == 0) { + fatal("Must specify at least one workload!"); + } +#endif + + AlphaSimpleParams *params = new AlphaSimpleParams; + + params->clock = clock; + + params->name = getInstanceName(); + params->numberOfThreads = actual_num_threads; + params->activity = activity; + +#if FULL_SYSTEM + params->system = system; + params->cpu_id = cpu_id; + params->itb = itb; + params->dtb = dtb; + params->profile = profile; +#else + params->workload = workload; +#endif // FULL_SYSTEM + + params->mem = mem; + + params->checker = checker; + + params->max_insts_any_thread = max_insts_any_thread; + params->max_insts_all_threads = max_insts_all_threads; + params->max_loads_any_thread = max_loads_any_thread; + params->max_loads_all_threads = max_loads_all_threads; + params->stats_reset_inst = stats_reset_inst; + params->progress_interval = progress_interval; + + // + // Caches + // + params->cachePorts = cachePorts; + + params->decodeToFetchDelay = decodeToFetchDelay; + params->renameToFetchDelay = renameToFetchDelay; + params->iewToFetchDelay = iewToFetchDelay; + params->commitToFetchDelay = commitToFetchDelay; + params->fetchWidth = fetchWidth; + + params->renameToDecodeDelay = renameToDecodeDelay; + params->iewToDecodeDelay = iewToDecodeDelay; + params->commitToDecodeDelay = commitToDecodeDelay; + params->fetchToDecodeDelay = fetchToDecodeDelay; + params->decodeWidth = decodeWidth; + + params->iewToRenameDelay = iewToRenameDelay; + params->commitToRenameDelay = commitToRenameDelay; + params->decodeToRenameDelay = decodeToRenameDelay; + params->renameWidth = renameWidth; + + params->commitToIEWDelay = commitToIEWDelay; + params->renameToIEWDelay = renameToIEWDelay; + params->issueToExecuteDelay = issueToExecuteDelay; + params->dispatchWidth = dispatchWidth; + params->issueWidth = issueWidth; + params->wbWidth = wbWidth; + params->wbDepth = wbDepth; + params->fuPool = fuPool; + + params->iewToCommitDelay = iewToCommitDelay; + params->renameToROBDelay = renameToROBDelay; + params->commitWidth = commitWidth; + params->squashWidth = squashWidth; + params->trapLatency = trapLatency; + + params->backComSize = backComSize; + params->forwardComSize = forwardComSize; + + params->predType = predType; + params->localPredictorSize = localPredictorSize; + params->localCtrBits = localCtrBits; + params->localHistoryTableSize = localHistoryTableSize; + params->localHistoryBits = localHistoryBits; + params->globalPredictorSize = globalPredictorSize; + params->globalCtrBits = globalCtrBits; + params->globalHistoryBits = globalHistoryBits; + params->choicePredictorSize = choicePredictorSize; + params->choiceCtrBits = choiceCtrBits; + + params->BTBEntries = BTBEntries; + params->BTBTagSize = BTBTagSize; + + params->RASSize = RASSize; + + params->LQEntries = LQEntries; + params->SQEntries = SQEntries; + + params->SSITSize = SSITSize; + params->LFSTSize = LFSTSize; + + params->numPhysIntRegs = numPhysIntRegs; + params->numPhysFloatRegs = numPhysFloatRegs; + params->numIQEntries = numIQEntries; + params->numROBEntries = numROBEntries; + + params->smtNumFetchingThreads = smtNumFetchingThreads; + + // Default smtFetchPolicy to "RoundRobin", if necessary. + std::string round_robin_policy = "RoundRobin"; + std::string single_thread = "SingleThread"; + + if (actual_num_threads > 1 && single_thread.compare(smtFetchPolicy) == 0) + params->smtFetchPolicy = round_robin_policy; + else + params->smtFetchPolicy = smtFetchPolicy; + + params->smtIQPolicy = smtIQPolicy; + params->smtLSQPolicy = smtLSQPolicy; + params->smtLSQThreshold = smtLSQThreshold; + params->smtROBPolicy = smtROBPolicy; + params->smtROBThreshold = smtROBThreshold; + params->smtCommitPolicy = smtCommitPolicy; + + params->instShiftAmt = 2; + + params->deferRegistration = defer_registration; + + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + + cpu = new DerivO3CPU(params); + + return cpu; +} + +REGISTER_SIM_OBJECT("DerivO3CPU", DerivO3CPU) + diff --git a/src/cpu/o3/alpha/cpu_impl.hh b/src/cpu/o3/alpha/cpu_impl.hh new file mode 100644 index 000000000..b7362fad9 --- /dev/null +++ b/src/cpu/o3/alpha/cpu_impl.hh @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "config/use_checker.hh" + +#include "arch/alpha/faults.hh" +#include "arch/alpha/isa_traits.hh" +#include "base/cprintf.hh" +#include "base/statistics.hh" +#include "base/timebuf.hh" +#include "cpu/checker/thread_context.hh" +#include "sim/sim_events.hh" +#include "sim/stats.hh" + +#include "cpu/o3/alpha/cpu.hh" +#include "cpu/o3/alpha/params.hh" +#include "cpu/o3/alpha/thread_context.hh" +#include "cpu/o3/comm.hh" +#include "cpu/o3/thread_state.hh" + +#if FULL_SYSTEM +#include "arch/alpha/osfpal.hh" +#include "arch/isa_traits.hh" +#include "cpu/quiesce_event.hh" +#include "kern/kernel_stats.hh" +#include "sim/sim_exit.hh" +#include "sim/system.hh" +#endif + +template <class Impl> +AlphaO3CPU<Impl>::AlphaO3CPU(Params *params) +#if FULL_SYSTEM + : FullO3CPU<Impl>(params), itb(params->itb), dtb(params->dtb) +#else + : FullO3CPU<Impl>(params) +#endif +{ + DPRINTF(O3CPU, "Creating AlphaO3CPU object.\n"); + + // Setup any thread state. + this->thread.resize(this->numThreads); + + for (int i = 0; i < this->numThreads; ++i) { +#if FULL_SYSTEM + // SMT is not supported in FS mode yet. + assert(this->numThreads == 1); + this->thread[i] = new Thread(this, 0); + this->thread[i]->setStatus(ThreadContext::Suspended); +#else + if (i < params->workload.size()) { + DPRINTF(O3CPU, "Workload[%i] process is %#x", + i, this->thread[i]); + this->thread[i] = new Thread(this, i, params->workload[i], + i, params->mem); + + this->thread[i]->setStatus(ThreadContext::Suspended); + +#if !FULL_SYSTEM + /* Use this port to for syscall emulation writes to memory. */ + Port *mem_port; + TranslatingPort *trans_port; + trans_port = new TranslatingPort(csprintf("%s-%d-funcport", + name(), i), + params->workload[i]->pTable, + false); + mem_port = params->mem->getPort("functional"); + mem_port->setPeer(trans_port); + trans_port->setPeer(mem_port); + this->thread[i]->setMemPort(trans_port); +#endif + //usedTids[i] = true; + //threadMap[i] = i; + } else { + //Allocate Empty thread so M5 can use later + //when scheduling threads to CPU + Process* dummy_proc = NULL; + + this->thread[i] = new Thread(this, i, dummy_proc, i, params->mem); + //usedTids[i] = false; + } +#endif // !FULL_SYSTEM + + ThreadContext *tc; + + // Setup the TC that will serve as the interface to the threads/CPU. + AlphaTC<Impl> *alpha_tc = + new AlphaTC<Impl>; + + tc = alpha_tc; + + // If we're using a checker, then the TC should be the + // CheckerThreadContext. +#if USE_CHECKER + if (params->checker) { + tc = new CheckerThreadContext<AlphaTC<Impl> >( + alpha_tc, this->checker); + } +#endif + + alpha_tc->cpu = this; + alpha_tc->thread = this->thread[i]; + +#if FULL_SYSTEM + // Setup quiesce event. + this->thread[i]->quiesceEvent = new EndQuiesceEvent(tc); + + Port *mem_port; + FunctionalPort *phys_port; + VirtualPort *virt_port; + phys_port = new FunctionalPort(csprintf("%s-%d-funcport", + name(), i)); + mem_port = this->system->physmem->getPort("functional"); + mem_port->setPeer(phys_port); + phys_port->setPeer(mem_port); + + virt_port = new VirtualPort(csprintf("%s-%d-vport", + name(), i)); + mem_port = this->system->physmem->getPort("functional"); + mem_port->setPeer(virt_port); + virt_port->setPeer(mem_port); + + this->thread[i]->setPhysPort(phys_port); + this->thread[i]->setVirtPort(virt_port); +#endif + // Give the thread the TC. + this->thread[i]->tc = tc; + + // Add the TC to the CPU's list of TC's. + this->threadContexts.push_back(tc); + } + + for (int i=0; i < this->numThreads; i++) { + this->thread[i]->setFuncExeInst(0); + } + + // Sets CPU pointers. These must be set at this level because the CPU + // pointers are defined to be the highest level of CPU class. + this->fetch.setCPU(this); + this->decode.setCPU(this); + this->rename.setCPU(this); + this->iew.setCPU(this); + this->commit.setCPU(this); + + this->rob.setCPU(this); + this->regFile.setCPU(this); + + lockAddr = 0; + lockFlag = false; +} + +template <class Impl> +void +AlphaO3CPU<Impl>::regStats() +{ + // Register stats for everything that has stats. + this->fullCPURegStats(); + this->fetch.regStats(); + this->decode.regStats(); + this->rename.regStats(); + this->iew.regStats(); + this->commit.regStats(); +} + + +template <class Impl> +TheISA::MiscReg +AlphaO3CPU<Impl>::readMiscReg(int misc_reg, unsigned tid) +{ + return this->regFile.readMiscReg(misc_reg, tid); +} + +template <class Impl> +TheISA::MiscReg +AlphaO3CPU<Impl>::readMiscRegWithEffect(int misc_reg, Fault &fault, + unsigned tid) +{ + return this->regFile.readMiscRegWithEffect(misc_reg, fault, tid); +} + +template <class Impl> +Fault +AlphaO3CPU<Impl>::setMiscReg(int misc_reg, const MiscReg &val, unsigned tid) +{ + return this->regFile.setMiscReg(misc_reg, val, tid); +} + +template <class Impl> +Fault +AlphaO3CPU<Impl>::setMiscRegWithEffect(int misc_reg, const MiscReg &val, + unsigned tid) +{ + return this->regFile.setMiscRegWithEffect(misc_reg, val, tid); +} + +template <class Impl> +void +AlphaO3CPU<Impl>::squashFromTC(unsigned tid) +{ + this->thread[tid]->inSyscall = true; + this->commit.generateTCEvent(tid); +} + +#if FULL_SYSTEM + +template <class Impl> +void +AlphaO3CPU<Impl>::post_interrupt(int int_num, int index) +{ + BaseCPU::post_interrupt(int_num, index); + + if (this->thread[0]->status() == ThreadContext::Suspended) { + DPRINTF(IPI,"Suspended Processor awoke\n"); + this->threadContexts[0]->activate(); + } +} + +template <class Impl> +int +AlphaO3CPU<Impl>::readIntrFlag() +{ + return this->regFile.readIntrFlag(); +} + +template <class Impl> +void +AlphaO3CPU<Impl>::setIntrFlag(int val) +{ + this->regFile.setIntrFlag(val); +} + +template <class Impl> +Fault +AlphaO3CPU<Impl>::hwrei(unsigned tid) +{ + // Need to clear the lock flag upon returning from an interrupt. + this->lockFlag = false; + + this->thread[tid]->kernelStats->hwrei(); + + this->checkInterrupts = true; + + // FIXME: XXX check for interrupts? XXX + return NoFault; +} + +template <class Impl> +bool +AlphaO3CPU<Impl>::simPalCheck(int palFunc, unsigned tid) +{ + if (this->thread[tid]->kernelStats) + this->thread[tid]->kernelStats->callpal(palFunc, + this->threadContexts[tid]); + + switch (palFunc) { + case PAL::halt: + halt(); + if (--System::numSystemsRunning == 0) + exitSimLoop("all cpus halted"); + break; + + case PAL::bpt: + case PAL::bugchk: + if (this->system->breakpoint()) + return false; + break; + } + + return true; +} + +template <class Impl> +void +AlphaO3CPU<Impl>::processInterrupts() +{ + using namespace TheISA; + // Check for interrupts here. For now can copy the code that + // exists within isa_fullsys_traits.hh. Also assume that thread 0 + // is the one that handles the interrupts. + // @todo: Possibly consolidate the interrupt checking code. + // @todo: Allow other threads to handle interrupts. + + // Check if there are any outstanding interrupts + //Handle the interrupts + int ipl = 0; + int summary = 0; + + this->checkInterrupts = false; + + if (this->readMiscReg(IPR_ASTRR, 0)) + panic("asynchronous traps not implemented\n"); + + if (this->readMiscReg(IPR_SIRR, 0)) { + for (int i = INTLEVEL_SOFTWARE_MIN; + i < INTLEVEL_SOFTWARE_MAX; i++) { + if (this->readMiscReg(IPR_SIRR, 0) & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; + summary |= (ULL(1) << i); + } + } + } + + uint64_t interrupts = this->intr_status(); + + if (interrupts) { + for (int i = INTLEVEL_EXTERNAL_MIN; + i < INTLEVEL_EXTERNAL_MAX; i++) { + if (interrupts & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = i; + summary |= (ULL(1) << i); + } + } + } + + if (ipl && ipl > this->readMiscReg(IPR_IPLR, 0)) { + this->setMiscReg(IPR_ISR, summary, 0); + this->setMiscReg(IPR_INTID, ipl, 0); + // Checker needs to know these two registers were updated. +#if USE_CHECKER + if (this->checker) { + this->checker->threadBase()->setMiscReg(IPR_ISR, summary); + this->checker->threadBase()->setMiscReg(IPR_INTID, ipl); + } +#endif + this->trap(Fault(new InterruptFault), 0); + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + this->readMiscReg(IPR_IPLR, 0), ipl, summary); + } +} + +#endif // FULL_SYSTEM + +template <class Impl> +void +AlphaO3CPU<Impl>::trap(Fault fault, unsigned tid) +{ + // Pass the thread's TC into the invoke method. + fault->invoke(this->threadContexts[tid]); +} + +#if !FULL_SYSTEM + +template <class Impl> +void +AlphaO3CPU<Impl>::syscall(int64_t callnum, int tid) +{ + DPRINTF(O3CPU, "[tid:%i] Executing syscall().\n\n", tid); + + DPRINTF(Activity,"Activity: syscall() called.\n"); + + // Temporarily increase this by one to account for the syscall + // instruction. + ++(this->thread[tid]->funcExeInst); + + // Execute the actual syscall. + this->thread[tid]->syscall(callnum); + + // Decrease funcExeInst by one as the normal commit will handle + // incrementing it. + --(this->thread[tid]->funcExeInst); +} + +template <class Impl> +TheISA::IntReg +AlphaO3CPU<Impl>::getSyscallArg(int i, int tid) +{ + return this->readArchIntReg(AlphaISA::ArgumentReg0 + i, tid); +} + +template <class Impl> +void +AlphaO3CPU<Impl>::setSyscallArg(int i, IntReg val, int tid) +{ + this->setArchIntReg(AlphaISA::ArgumentReg0 + i, val, tid); +} + +template <class Impl> +void +AlphaO3CPU<Impl>::setSyscallReturn(SyscallReturn return_value, int tid) +{ + // check for error condition. Alpha syscall convention is to + // indicate success/failure in reg a3 (r19) and put the + // return value itself in the standard return value reg (v0). + if (return_value.successful()) { + // no error + this->setArchIntReg(TheISA::SyscallSuccessReg, 0, tid); + this->setArchIntReg(TheISA::ReturnValueReg, return_value.value(), tid); + } else { + // got an error, return details + this->setArchIntReg(TheISA::SyscallSuccessReg, (IntReg) -1, tid); + this->setArchIntReg(TheISA::ReturnValueReg, -return_value.value(), tid); + } +} +#endif diff --git a/src/cpu/o3/alpha/dyn_inst.cc b/src/cpu/o3/alpha/dyn_inst.cc new file mode 100644 index 000000000..97d2f3d08 --- /dev/null +++ b/src/cpu/o3/alpha/dyn_inst.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/alpha/dyn_inst_impl.hh" +#include "cpu/o3/alpha/impl.hh" + +// Force instantiation of AlphaDynInst for all the implementations that +// are needed. +template class AlphaDynInst<AlphaSimpleImpl>; diff --git a/src/cpu/o3/alpha/dyn_inst.hh b/src/cpu/o3/alpha/dyn_inst.hh new file mode 100644 index 000000000..9dee610b6 --- /dev/null +++ b/src/cpu/o3/alpha/dyn_inst.hh @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_ALPHA_DYN_INST_HH__ +#define __CPU_O3_ALPHA_DYN_INST_HH__ + +#include "arch/isa_traits.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/alpha/cpu.hh" +#include "cpu/o3/alpha/impl.hh" + +class Packet; + +/** + * Mostly implementation & ISA specific AlphaDynInst. As with most + * other classes in the new CPU model, it is templated on the Impl to + * allow for passing in of all types, such as the CPU type and the ISA + * type. The AlphaDynInst serves as the primary interface to the CPU + * for instructions that are executing. + */ +template <class Impl> +class AlphaDynInst : public BaseDynInst<Impl> +{ + public: + /** Typedef for the CPU. */ + typedef typename Impl::O3CPU O3CPU; + + /** Binary machine instruction type. */ + typedef TheISA::MachInst MachInst; + /** Extended machine instruction type. */ + typedef TheISA::ExtMachInst ExtMachInst; + /** Logical register index type. */ + typedef TheISA::RegIndex RegIndex; + /** Integer register index type. */ + typedef TheISA::IntReg IntReg; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + /** Misc register index type. */ + typedef TheISA::MiscReg MiscReg; + + enum { + MaxInstSrcRegs = TheISA::MaxInstSrcRegs, //< Max source regs + MaxInstDestRegs = TheISA::MaxInstDestRegs, //< Max dest regs + }; + + public: + /** BaseDynInst constructor given a binary instruction. */ + AlphaDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, InstSeqNum seq_num, + O3CPU *cpu); + + /** BaseDynInst constructor given a static inst pointer. */ + AlphaDynInst(StaticInstPtr &_staticInst); + + /** Executes the instruction.*/ + Fault execute(); + + /** Initiates the access. Only valid for memory operations. */ + Fault initiateAcc(); + + /** Completes the access. Only valid for memory operations. */ + Fault completeAcc(Packet *pkt); + + private: + /** Initializes variables. */ + void initVars(); + + public: + /** Reads a miscellaneous register. */ + MiscReg readMiscReg(int misc_reg) + { + return this->cpu->readMiscReg(misc_reg, this->threadNumber); + } + + /** Reads a misc. register, including any side-effects the read + * might have as defined by the architecture. + */ + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { + return this->cpu->readMiscRegWithEffect(misc_reg, fault, + this->threadNumber); + } + + /** Sets a misc. register. */ + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + this->instResult.integer = val; + return this->cpu->setMiscReg(misc_reg, val, this->threadNumber); + } + + /** Sets a misc. register, including any side-effects the write + * might have as defined by the architecture. + */ + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + return this->cpu->setMiscRegWithEffect(misc_reg, val, + this->threadNumber); + } + +#if FULL_SYSTEM + /** Calls hardware return from error interrupt. */ + Fault hwrei(); + /** Reads interrupt flag. */ + int readIntrFlag(); + /** Sets interrupt flag. */ + void setIntrFlag(int val); + /** Checks if system is in PAL mode. */ + bool inPalMode(); + /** Traps to handle specified fault. */ + void trap(Fault fault); + bool simPalCheck(int palFunc); +#else + /** Calls a syscall. */ + void syscall(int64_t callnum); +#endif + + private: + /** Physical register index of the destination registers of this + * instruction. + */ + PhysRegIndex _destRegIdx[MaxInstDestRegs]; + + /** Physical register index of the source registers of this + * instruction. + */ + PhysRegIndex _srcRegIdx[MaxInstSrcRegs]; + + /** Physical register index of the previous producers of the + * architected destinations. + */ + PhysRegIndex _prevDestRegIdx[MaxInstDestRegs]; + + public: + + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to redice overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + uint64_t readIntReg(const StaticInst *si, int idx) + { + return this->cpu->readIntReg(_srcRegIdx[idx]); + } + + FloatReg readFloatReg(const StaticInst *si, int idx, int width) + { + return this->cpu->readFloatReg(_srcRegIdx[idx], width); + } + + FloatReg readFloatReg(const StaticInst *si, int idx) + { + return this->cpu->readFloatReg(_srcRegIdx[idx]); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width) + { + return this->cpu->readFloatRegBits(_srcRegIdx[idx], width); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx) + { + return this->cpu->readFloatRegBits(_srcRegIdx[idx]); + } + + /** @todo: Make results into arrays so they can handle multiple dest + * registers. + */ + void setIntReg(const StaticInst *si, int idx, uint64_t val) + { + this->cpu->setIntReg(_destRegIdx[idx], val); + BaseDynInst<Impl>::setIntReg(si, idx, val); + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) + { + this->cpu->setFloatReg(_destRegIdx[idx], val, width); + BaseDynInst<Impl>::setFloatReg(si, idx, val, width); + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val) + { + this->cpu->setFloatReg(_destRegIdx[idx], val); + BaseDynInst<Impl>::setFloatReg(si, idx, val); + } + + void setFloatRegBits(const StaticInst *si, int idx, + FloatRegBits val, int width) + { + this->cpu->setFloatRegBits(_destRegIdx[idx], val, width); + BaseDynInst<Impl>::setFloatRegBits(si, idx, val); + } + + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val) + { + this->cpu->setFloatRegBits(_destRegIdx[idx], val); + BaseDynInst<Impl>::setFloatRegBits(si, idx, val); + } + + /** Returns the physical register index of the i'th destination + * register. + */ + PhysRegIndex renamedDestRegIdx(int idx) const + { + return _destRegIdx[idx]; + } + + /** Returns the physical register index of the i'th source register. */ + PhysRegIndex renamedSrcRegIdx(int idx) const + { + return _srcRegIdx[idx]; + } + + /** Returns the physical register index of the previous physical register + * that remapped to the same logical register index. + */ + PhysRegIndex prevDestRegIdx(int idx) const + { + return _prevDestRegIdx[idx]; + } + + /** Renames a destination register to a physical register. Also records + * the previous physical register that the logical register mapped to. + */ + void renameDestReg(int idx, + PhysRegIndex renamed_dest, + PhysRegIndex previous_rename) + { + _destRegIdx[idx] = renamed_dest; + _prevDestRegIdx[idx] = previous_rename; + } + + /** Renames a source logical register to the physical register which + * has/will produce that logical register's result. + * @todo: add in whether or not the source register is ready. + */ + void renameSrcReg(int idx, PhysRegIndex renamed_src) + { + _srcRegIdx[idx] = renamed_src; + } + + public: + /** Calculates EA part of a memory instruction. Currently unused, + * though it may be useful in the future if we want to split + * memory operations into EA calculation and memory access parts. + */ + Fault calcEA() + { + return this->staticInst->eaCompInst()->execute(this, this->traceData); + } + + /** Does the memory access part of a memory instruction. Currently unused, + * though it may be useful in the future if we want to split + * memory operations into EA calculation and memory access parts. + */ + Fault memAccess() + { + return this->staticInst->memAccInst()->execute(this, this->traceData); + } +}; + +#endif // __CPU_O3_ALPHA_DYN_INST_HH__ + diff --git a/src/cpu/o3/alpha/dyn_inst_impl.hh b/src/cpu/o3/alpha/dyn_inst_impl.hh new file mode 100644 index 000000000..2d1b4b309 --- /dev/null +++ b/src/cpu/o3/alpha/dyn_inst_impl.hh @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/alpha/dyn_inst.hh" + +template <class Impl> +AlphaDynInst<Impl>::AlphaDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, + InstSeqNum seq_num, O3CPU *cpu) + : BaseDynInst<Impl>(inst, PC, Pred_PC, seq_num, cpu) +{ + initVars(); +} + +template <class Impl> +AlphaDynInst<Impl>::AlphaDynInst(StaticInstPtr &_staticInst) + : BaseDynInst<Impl>(_staticInst) +{ + initVars(); +} + +template <class Impl> +void +AlphaDynInst<Impl>::initVars() +{ + // Make sure to have the renamed register entries set to the same + // as the normal register entries. It will allow the IQ to work + // without any modifications. + for (int i = 0; i < this->staticInst->numDestRegs(); i++) { + _destRegIdx[i] = this->staticInst->destRegIdx(i); + } + + for (int i = 0; i < this->staticInst->numSrcRegs(); i++) { + _srcRegIdx[i] = this->staticInst->srcRegIdx(i); + this->_readySrcRegIdx[i] = 0; + } +} + +template <class Impl> +Fault +AlphaDynInst<Impl>::execute() +{ + // @todo: Pretty convoluted way to avoid squashing from happening + // when using the TC during an instruction's execution + // (specifically for instructions that have side-effects that use + // the TC). Fix this. + bool in_syscall = this->thread->inSyscall; + this->thread->inSyscall = true; + + this->fault = this->staticInst->execute(this, this->traceData); + + this->thread->inSyscall = in_syscall; + + return this->fault; +} + +template <class Impl> +Fault +AlphaDynInst<Impl>::initiateAcc() +{ + // @todo: Pretty convoluted way to avoid squashing from happening + // when using the TC during an instruction's execution + // (specifically for instructions that have side-effects that use + // the TC). Fix this. + bool in_syscall = this->thread->inSyscall; + this->thread->inSyscall = true; + + this->fault = this->staticInst->initiateAcc(this, this->traceData); + + this->thread->inSyscall = in_syscall; + + return this->fault; +} + +template <class Impl> +Fault +AlphaDynInst<Impl>::completeAcc(Packet *pkt) +{ + this->fault = this->staticInst->completeAcc(pkt, this, this->traceData); + + return this->fault; +} + +#if FULL_SYSTEM +template <class Impl> +Fault +AlphaDynInst<Impl>::hwrei() +{ + // Can only do a hwrei when in pal mode. + if (!this->cpu->inPalMode(this->readPC())) + return new AlphaISA::UnimplementedOpcodeFault; + + // Set the next PC based on the value of the EXC_ADDR IPR. + this->setNextPC(this->cpu->readMiscReg(AlphaISA::IPR_EXC_ADDR, + this->threadNumber)); + + // Tell CPU to clear any state it needs to if a hwrei is taken. + this->cpu->hwrei(this->threadNumber); + + // FIXME: XXX check for interrupts? XXX + return NoFault; +} + +template <class Impl> +int +AlphaDynInst<Impl>::readIntrFlag() +{ + return this->cpu->readIntrFlag(); +} + +template <class Impl> +void +AlphaDynInst<Impl>::setIntrFlag(int val) +{ + this->cpu->setIntrFlag(val); +} + +template <class Impl> +bool +AlphaDynInst<Impl>::inPalMode() +{ + return this->cpu->inPalMode(this->PC); +} + +template <class Impl> +void +AlphaDynInst<Impl>::trap(Fault fault) +{ + this->cpu->trap(fault, this->threadNumber); +} + +template <class Impl> +bool +AlphaDynInst<Impl>::simPalCheck(int palFunc) +{ + return this->cpu->simPalCheck(palFunc, this->threadNumber); +} +#else +template <class Impl> +void +AlphaDynInst<Impl>::syscall(int64_t callnum) +{ + this->cpu->syscall(callnum, this->threadNumber); +} +#endif + diff --git a/src/cpu/o3/alpha/impl.hh b/src/cpu/o3/alpha/impl.hh new file mode 100644 index 000000000..b928ae654 --- /dev/null +++ b/src/cpu/o3/alpha/impl.hh @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_ALPHA_IMPL_HH__ +#define __CPU_O3_ALPHA_IMPL_HH__ + +#include "arch/alpha/isa_traits.hh" + +#include "cpu/o3/alpha/params.hh" +#include "cpu/o3/cpu_policy.hh" + + +// Forward declarations. +template <class Impl> +class AlphaDynInst; + +template <class Impl> +class AlphaO3CPU; + +/** Implementation specific struct that defines several key types to the + * CPU, the stages within the CPU, the time buffers, and the DynInst. + * The struct defines the ISA, the CPU policy, the specific DynInst, the + * specific O3CPU, and all of the structs from the time buffers to do + * communication. + * This is one of the key things that must be defined for each hardware + * specific CPU implementation. + */ +struct AlphaSimpleImpl +{ + /** The type of MachInst. */ + typedef TheISA::MachInst MachInst; + + /** The CPU policy to be used, which defines all of the CPU stages. */ + typedef SimpleCPUPolicy<AlphaSimpleImpl> CPUPol; + + /** The DynInst type to be used. */ + typedef AlphaDynInst<AlphaSimpleImpl> DynInst; + + /** The refcounted DynInst pointer to be used. In most cases this is + * what should be used, and not DynInst *. + */ + typedef RefCountingPtr<DynInst> DynInstPtr; + + /** The O3CPU type to be used. */ + typedef AlphaO3CPU<AlphaSimpleImpl> O3CPU; + + /** Same typedef, but for CPUType. BaseDynInst may not always use + * an O3 CPU, so it's clearer to call it CPUType instead in that + * case. + */ + typedef O3CPU CPUType; + + /** The Params to be passed to each stage. */ + typedef AlphaSimpleParams Params; + + enum { + MaxWidth = 8, + MaxThreads = 4 + }; +}; + +/** The O3Impl to be used. */ +typedef AlphaSimpleImpl O3CPUImpl; + +#endif // __CPU_O3_ALPHA_IMPL_HH__ diff --git a/src/cpu/o3/alpha/params.hh b/src/cpu/o3/alpha/params.hh new file mode 100644 index 000000000..c618cee08 --- /dev/null +++ b/src/cpu/o3/alpha/params.hh @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_ALPHA_PARAMS_HH__ +#define __CPU_O3_ALPHA_PARAMS_HH__ + +#include "cpu/o3/cpu.hh" +#include "cpu/o3/params.hh" + +//Forward declarations +class AlphaDTB; +class AlphaITB; +class MemObject; +class Process; +class System; + +/** + * This file defines the parameters that will be used for the AlphaO3CPU. + * This must be defined externally so that the Impl can have a params class + * defined that it can pass to all of the individual stages. + */ + +class AlphaSimpleParams : public O3Params +{ + public: + +#if FULL_SYSTEM + AlphaITB *itb; + AlphaDTB *dtb; +#endif +}; + +#endif // __CPU_O3_ALPHA_PARAMS_HH__ diff --git a/src/cpu/o3/alpha/thread_context.cc b/src/cpu/o3/alpha/thread_context.cc new file mode 100755 index 000000000..4a02715bc --- /dev/null +++ b/src/cpu/o3/alpha/thread_context.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "cpu/o3/thread_context.hh" +#include "cpu/o3/thread_context_impl.hh" + +template class O3ThreadContext<AlphaSimpleImpl>; + diff --git a/src/cpu/o3/alpha/thread_context.hh b/src/cpu/o3/alpha/thread_context.hh new file mode 100644 index 000000000..70a09940f --- /dev/null +++ b/src/cpu/o3/alpha/thread_context.hh @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "arch/alpha/types.hh" +#include "cpu/o3/thread_context.hh" + +template <class Impl> +class AlphaTC : public O3ThreadContext<Impl> +{ + public: +#if FULL_SYSTEM + /** Returns a pointer to the ITB. */ + virtual AlphaITB *getITBPtr() { return this->cpu->itb; } + + /** Returns a pointer to the DTB. */ + virtual AlphaDTB *getDTBPtr() { return this->cpu->dtb; } + + /** Returns pointer to the quiesce event. */ + virtual EndQuiesceEvent *getQuiesceEvent() + { + return this->thread->quiesceEvent; + } + + /** Returns if the thread is currently in PAL mode, based on + * the PC's value. */ + virtual bool inPalMode() + { return TheISA::PcPAL(this->cpu->readPC(this->thread->readTid())); } +#endif + + virtual uint64_t readNextNPC() + { + panic("Alpha has no NextNPC!"); + return 0; + } + + virtual void setNextNPC(uint64_t val) + { + panic("Alpha has no NextNPC!"); + } + + virtual void changeRegFileContext(TheISA::RegContextParam param, + TheISA::RegContextVal val) + { panic("Not supported on Alpha!"); } + + + /** This function exits the thread context in the CPU and returns + * 1 if the CPU has no more active threads (meaning it's OK to exit); + * Used in syscall-emulation mode when a thread executes the 'exit' + * syscall. + */ + virtual int exit() + { + this->deallocate(); + + // If there are still threads executing in the system + if (this->cpu->numActiveThreads()) + return 0; // don't exit simulation + else + return 1; // exit simulation + } +}; diff --git a/src/cpu/o3/base_dyn_inst.cc b/src/cpu/o3/base_dyn_inst.cc new file mode 100644 index 000000000..0979c5c8f --- /dev/null +++ b/src/cpu/o3/base_dyn_inst.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/base_dyn_inst_impl.hh" +#include "cpu/o3/isa_specific.hh" + +// Explicit instantiation +template class BaseDynInst<O3CPUImpl>; + +template <> +int +BaseDynInst<O3CPUImpl>::instcount = 0; diff --git a/src/cpu/o3/bpred_unit.cc b/src/cpu/o3/bpred_unit.cc new file mode 100644 index 000000000..08fd4e8ea --- /dev/null +++ b/src/cpu/o3/bpred_unit.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/bpred_unit_impl.hh" +#include "cpu/o3/isa_specific.hh" + +template class BPredUnit<O3CPUImpl>; diff --git a/src/cpu/o3/bpred_unit.hh b/src/cpu/o3/bpred_unit.hh new file mode 100644 index 000000000..3c4c8e478 --- /dev/null +++ b/src/cpu/o3/bpred_unit.hh @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_BPRED_UNIT_HH__ +#define __CPU_O3_BPRED_UNIT_HH__ + +#include "base/statistics.hh" +#include "cpu/inst_seq.hh" + +#include "cpu/o3/2bit_local_pred.hh" +#include "cpu/o3/btb.hh" +#include "cpu/o3/ras.hh" +#include "cpu/o3/tournament_pred.hh" + +#include "sim/host.hh" + +#include <list> + +/** + * Basically a wrapper class to hold both the branch predictor + * and the BTB. + */ +template<class Impl> +class BPredUnit +{ + private: + typedef typename Impl::Params Params; + typedef typename Impl::DynInstPtr DynInstPtr; + + enum PredType { + Local, + Tournament + }; + + PredType predictor; + + public: + + /** + * @param params The params object, that has the size of the BP and BTB. + */ + BPredUnit(Params *params); + + /** + * Registers statistics. + */ + void regStats(); + + void switchOut(); + + void takeOverFrom(); + + /** + * Predicts whether or not the instruction is a taken branch, and the + * target of the branch if it is taken. + * @param inst The branch instruction. + * @param PC The predicted PC is passed back through this parameter. + * @param tid The thread id. + * @return Returns if the branch is taken or not. + */ + bool predict(DynInstPtr &inst, Addr &PC, unsigned tid); + + // @todo: Rename this function. + void BPUncond(void * &bp_history); + + /** + * Tells the branch predictor to commit any updates until the given + * sequence number. + * @param done_sn The sequence number to commit any older updates up until. + * @param tid The thread id. + */ + void update(const InstSeqNum &done_sn, unsigned tid); + + /** + * Squashes all outstanding updates until a given sequence number. + * @param squashed_sn The sequence number to squash any younger updates up + * until. + * @param tid The thread id. + */ + void squash(const InstSeqNum &squashed_sn, unsigned tid); + + /** + * Squashes all outstanding updates until a given sequence number, and + * corrects that sn's update with the proper address and taken/not taken. + * @param squashed_sn The sequence number to squash any younger updates up + * until. + * @param corr_target The correct branch target. + * @param actually_taken The correct branch direction. + * @param tid The thread id. + */ + void squash(const InstSeqNum &squashed_sn, const Addr &corr_target, + bool actually_taken, unsigned tid); + + /** + * @param bp_history Pointer to the history object. The predictor + * will need to update any state and delete the object. + */ + void BPSquash(void *bp_history); + + /** + * Looks up a given PC in the BP to see if it is taken or not taken. + * @param inst_PC The PC to look up. + * @param bp_history Pointer that will be set to an object that + * has the branch predictor state associated with the lookup. + * @return Whether the branch is taken or not taken. + */ + bool BPLookup(Addr &inst_PC, void * &bp_history); + + /** + * Looks up a given PC in the BTB to see if a matching entry exists. + * @param inst_PC The PC to look up. + * @return Whether the BTB contains the given PC. + */ + bool BTBValid(Addr &inst_PC) + { return BTB.valid(inst_PC, 0); } + + /** + * Looks up a given PC in the BTB to get the predicted target. + * @param inst_PC The PC to look up. + * @return The address of the target of the branch. + */ + Addr BTBLookup(Addr &inst_PC) + { return BTB.lookup(inst_PC, 0); } + + /** + * Updates the BP with taken/not taken information. + * @param inst_PC The branch's PC that will be updated. + * @param taken Whether the branch was taken or not taken. + * @param bp_history Pointer to the branch predictor state that is + * associated with the branch lookup that is being updated. + * @todo Make this update flexible enough to handle a global predictor. + */ + void BPUpdate(Addr &inst_PC, bool taken, void *bp_history); + + /** + * Updates the BTB with the target of a branch. + * @param inst_PC The branch's PC that will be updated. + * @param target_PC The branch's target that will be added to the BTB. + */ + void BTBUpdate(Addr &inst_PC, Addr &target_PC) + { BTB.update(inst_PC, target_PC,0); } + + void dump(); + + private: + struct PredictorHistory { + /** + * Makes a predictor history struct that contains any + * information needed to update the predictor, BTB, and RAS. + */ + PredictorHistory(const InstSeqNum &seq_num, const Addr &inst_PC, + const bool pred_taken, void *bp_history, + const unsigned _tid) + : seqNum(seq_num), PC(inst_PC), RASTarget(0), + RASIndex(0), tid(_tid), predTaken(pred_taken), usedRAS(0), + wasCall(0), bpHistory(bp_history) + { } + + /** The sequence number for the predictor history entry. */ + InstSeqNum seqNum; + + /** The PC associated with the sequence number. */ + Addr PC; + + /** The RAS target (only valid if a return). */ + Addr RASTarget; + + /** The RAS index of the instruction (only valid if a call). */ + unsigned RASIndex; + + /** The thread id. */ + unsigned tid; + + /** Whether or not it was predicted taken. */ + bool predTaken; + + /** Whether or not the RAS was used. */ + bool usedRAS; + + /** Whether or not the instruction was a call. */ + bool wasCall; + + /** Pointer to the history object passed back from the branch + * predictor. It is used to update or restore state of the + * branch predictor. + */ + void *bpHistory; + }; + + typedef std::list<PredictorHistory> History; + + /** + * The per-thread predictor history. This is used to update the predictor + * as instructions are committed, or restore it to the proper state after + * a squash. + */ + History predHist[Impl::MaxThreads]; + + /** The local branch predictor. */ + LocalBP *localBP; + + /** The tournament branch predictor. */ + TournamentBP *tournamentBP; + + /** The BTB. */ + DefaultBTB BTB; + + /** The per-thread return address stack. */ + ReturnAddrStack RAS[Impl::MaxThreads]; + + /** Stat for number of BP lookups. */ + Stats::Scalar<> lookups; + /** Stat for number of conditional branches predicted. */ + Stats::Scalar<> condPredicted; + /** Stat for number of conditional branches predicted incorrectly. */ + Stats::Scalar<> condIncorrect; + /** Stat for number of BTB lookups. */ + Stats::Scalar<> BTBLookups; + /** Stat for number of BTB hits. */ + Stats::Scalar<> BTBHits; + /** Stat for number of times the BTB is correct. */ + Stats::Scalar<> BTBCorrect; + /** Stat for number of times the RAS is used to get a target. */ + Stats::Scalar<> usedRAS; + /** Stat for number of times the RAS is incorrect. */ + Stats::Scalar<> RASIncorrect; +}; + +#endif // __CPU_O3_BPRED_UNIT_HH__ diff --git a/src/cpu/o3/bpred_unit_impl.hh b/src/cpu/o3/bpred_unit_impl.hh new file mode 100644 index 000000000..477c8e4cb --- /dev/null +++ b/src/cpu/o3/bpred_unit_impl.hh @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "arch/types.hh" +#include "arch/isa_traits.hh" +#include "base/trace.hh" +#include "base/traceflags.hh" +#include "cpu/o3/bpred_unit.hh" + +template<class Impl> +BPredUnit<Impl>::BPredUnit(Params *params) + : BTB(params->BTBEntries, + params->BTBTagSize, + params->instShiftAmt) +{ + // Setup the selected predictor. + if (params->predType == "local") { + localBP = new LocalBP(params->localPredictorSize, + params->localCtrBits, + params->instShiftAmt); + predictor = Local; + } else if (params->predType == "tournament") { + tournamentBP = new TournamentBP(params->localPredictorSize, + params->localCtrBits, + params->localHistoryTableSize, + params->localHistoryBits, + params->globalPredictorSize, + params->globalHistoryBits, + params->globalCtrBits, + params->choicePredictorSize, + params->choiceCtrBits, + params->instShiftAmt); + predictor = Tournament; + } else { + fatal("Invalid BP selected!"); + } + + for (int i=0; i < Impl::MaxThreads; i++) + RAS[i].init(params->RASSize); +} + +template <class Impl> +void +BPredUnit<Impl>::regStats() +{ + lookups + .name(name() + ".BPredUnit.lookups") + .desc("Number of BP lookups") + ; + + condPredicted + .name(name() + ".BPredUnit.condPredicted") + .desc("Number of conditional branches predicted") + ; + + condIncorrect + .name(name() + ".BPredUnit.condIncorrect") + .desc("Number of conditional branches incorrect") + ; + + BTBLookups + .name(name() + ".BPredUnit.BTBLookups") + .desc("Number of BTB lookups") + ; + + BTBHits + .name(name() + ".BPredUnit.BTBHits") + .desc("Number of BTB hits") + ; + + BTBCorrect + .name(name() + ".BPredUnit.BTBCorrect") + .desc("Number of correct BTB predictions (this stat may not " + "work properly.") + ; + + usedRAS + .name(name() + ".BPredUnit.usedRAS") + .desc("Number of times the RAS was used to get a target.") + ; + + RASIncorrect + .name(name() + ".BPredUnit.RASInCorrect") + .desc("Number of incorrect RAS predictions.") + ; +} + +template <class Impl> +void +BPredUnit<Impl>::switchOut() +{ + // Clear any state upon switch out. + for (int i = 0; i < Impl::MaxThreads; ++i) { + squash(0, i); + } +} + +template <class Impl> +void +BPredUnit<Impl>::takeOverFrom() +{ + // Can reset all predictor state, but it's not necessarily better + // than leaving it be. +/* + for (int i = 0; i < Impl::MaxThreads; ++i) + RAS[i].reset(); + + BP.reset(); + BTB.reset(); +*/ +} + +template <class Impl> +bool +BPredUnit<Impl>::predict(DynInstPtr &inst, Addr &PC, unsigned tid) +{ + // See if branch predictor predicts taken. + // If so, get its target addr either from the BTB or the RAS. + // Save off record of branch stuff so the RAS can be fixed + // up once it's done. + + using TheISA::MachInst; + + bool pred_taken = false; + Addr target; + + ++lookups; + + void *bp_history = NULL; + + if (inst->isUncondCtrl()) { + DPRINTF(Fetch, "BranchPred: [tid:%i]: Unconditional control.\n", tid); + pred_taken = true; + // Tell the BP there was an unconditional branch. + BPUncond(bp_history); + } else { + ++condPredicted; + + pred_taken = BPLookup(PC, bp_history); + + DPRINTF(Fetch, "BranchPred: [tid:%i]: Branch predictor predicted %i " + "for PC %#x\n", + tid, pred_taken, inst->readPC()); + } + + PredictorHistory predict_record(inst->seqNum, PC, pred_taken, + bp_history, tid); + + // Now lookup in the BTB or RAS. + if (pred_taken) { + if (inst->isReturn()) { + ++usedRAS; + + // If it's a function return call, then look up the address + // in the RAS. + target = RAS[tid].top(); + + // Record the top entry of the RAS, and its index. + predict_record.usedRAS = true; + predict_record.RASIndex = RAS[tid].topIdx(); + predict_record.RASTarget = target; + + assert(predict_record.RASIndex < 16); + + RAS[tid].pop(); + + DPRINTF(Fetch, "BranchPred: [tid:%i]: Instruction %#x is a return, " + "RAS predicted target: %#x, RAS index: %i.\n", + tid, inst->readPC(), target, predict_record.RASIndex); + } else { + ++BTBLookups; + + if (inst->isCall()) { +#if ISA_HAS_DELAY_SLOT + Addr ras_pc = PC + (2 * sizeof(MachInst)); // Next Next PC +#else + Addr ras_pc = PC + sizeof(MachInst); // Next PC +#endif + RAS[tid].push(ras_pc); + + // Record that it was a call so that the top RAS entry can + // be popped off if the speculation is incorrect. + predict_record.wasCall = true; + + DPRINTF(Fetch, "BranchPred: [tid:%i]: Instruction %#x was a call" + ", adding %#x to the RAS index: %i.\n", + tid, inst->readPC(), ras_pc, RAS[tid].topIdx()); + } + + if (BTB.valid(PC, tid)) { + ++BTBHits; + + // If it's not a return, use the BTB to get the target addr. + target = BTB.lookup(PC, tid); + + DPRINTF(Fetch, "BranchPred: [tid:%i]: Instruction %#x predicted" + " target is %#x.\n", + tid, inst->readPC(), target); + + } else { + DPRINTF(Fetch, "BranchPred: [tid:%i]: BTB doesn't have a " + "valid entry.\n",tid); + pred_taken = false; + } + + } + } + + if (pred_taken) { + // Set the PC and the instruction's predicted target. + PC = target; + inst->setPredTarg(target); + } else { + PC = PC + sizeof(MachInst); + inst->setPredTarg(PC); + } + + predHist[tid].push_front(predict_record); + + DPRINTF(Fetch, "[tid:%i]: predHist.size(): %i\n", tid, predHist[tid].size()); + + return pred_taken; +} + +template <class Impl> +void +BPredUnit<Impl>::update(const InstSeqNum &done_sn, unsigned tid) +{ + DPRINTF(Fetch, "BranchPred: [tid:%i]: Commiting branches until " + "[sn:%lli].\n", tid, done_sn); + + while (!predHist[tid].empty() && + predHist[tid].back().seqNum <= done_sn) { + // Update the branch predictor with the correct results. + BPUpdate(predHist[tid].back().PC, + predHist[tid].back().predTaken, + predHist[tid].back().bpHistory); + + predHist[tid].pop_back(); + } +} + +template <class Impl> +void +BPredUnit<Impl>::squash(const InstSeqNum &squashed_sn, unsigned tid) +{ + History &pred_hist = predHist[tid]; + + while (!pred_hist.empty() && + pred_hist.front().seqNum > squashed_sn) { + if (pred_hist.front().usedRAS) { + DPRINTF(Fetch, "BranchPred: [tid:%i]: Restoring top of RAS to: %i," + " target: %#x.\n", + tid, + pred_hist.front().RASIndex, + pred_hist.front().RASTarget); + + RAS[tid].restore(pred_hist.front().RASIndex, + pred_hist.front().RASTarget); + } else if (pred_hist.front().wasCall) { + DPRINTF(Fetch, "BranchPred: [tid:%i]: Removing speculative entry " + "added to the RAS.\n",tid); + + RAS[tid].pop(); + } + + // This call should delete the bpHistory. + BPSquash(pred_hist.front().bpHistory); + + pred_hist.pop_front(); + } + +} + +template <class Impl> +void +BPredUnit<Impl>::squash(const InstSeqNum &squashed_sn, + const Addr &corr_target, + const bool actually_taken, + unsigned tid) +{ + // Now that we know that a branch was mispredicted, we need to undo + // all the branches that have been seen up until this branch and + // fix up everything. + + History &pred_hist = predHist[tid]; + + ++condIncorrect; + + DPRINTF(Fetch, "BranchPred: [tid:%i]: Squashing from sequence number %i, " + "setting target to %#x.\n", + tid, squashed_sn, corr_target); + + squash(squashed_sn, tid); + + // If there's a squash due to a syscall, there may not be an entry + // corresponding to the squash. In that case, don't bother trying to + // fix up the entry. + if (!pred_hist.empty()) { + assert(pred_hist.front().seqNum == squashed_sn); + if (pred_hist.front().usedRAS) { + ++RASIncorrect; + } + + BPUpdate(pred_hist.front().PC, actually_taken, + pred_hist.front().bpHistory); + + BTB.update(pred_hist.front().PC, corr_target, tid); + pred_hist.pop_front(); + } +} + +template <class Impl> +void +BPredUnit<Impl>::BPUncond(void * &bp_history) +{ + // Only the tournament predictor cares about unconditional branches. + if (predictor == Tournament) { + tournamentBP->uncondBr(bp_history); + } +} + +template <class Impl> +void +BPredUnit<Impl>::BPSquash(void *bp_history) +{ + if (predictor == Local) { + localBP->squash(bp_history); + } else if (predictor == Tournament) { + tournamentBP->squash(bp_history); + } else { + panic("Predictor type is unexpected value!"); + } +} + +template <class Impl> +bool +BPredUnit<Impl>::BPLookup(Addr &inst_PC, void * &bp_history) +{ + if (predictor == Local) { + return localBP->lookup(inst_PC, bp_history); + } else if (predictor == Tournament) { + return tournamentBP->lookup(inst_PC, bp_history); + } else { + panic("Predictor type is unexpected value!"); + } +} + +template <class Impl> +void +BPredUnit<Impl>::BPUpdate(Addr &inst_PC, bool taken, void *bp_history) +{ + if (predictor == Local) { + localBP->update(inst_PC, taken, bp_history); + } else if (predictor == Tournament) { + tournamentBP->update(inst_PC, taken, bp_history); + } else { + panic("Predictor type is unexpected value!"); + } +} + +template <class Impl> +void +BPredUnit<Impl>::dump() +{ + typename History::iterator pred_hist_it; + + for (int i = 0; i < Impl::MaxThreads; ++i) { + if (!predHist[i].empty()) { + pred_hist_it = predHist[i].begin(); + + cprintf("predHist[%i].size(): %i\n", i, predHist[i].size()); + + while (pred_hist_it != predHist[i].end()) { + cprintf("[sn:%lli], PC:%#x, tid:%i, predTaken:%i, " + "bpHistory:%#x\n", + (*pred_hist_it).seqNum, (*pred_hist_it).PC, + (*pred_hist_it).tid, (*pred_hist_it).predTaken, + (*pred_hist_it).bpHistory); + pred_hist_it++; + } + + cprintf("\n"); + } + } +} diff --git a/src/cpu/o3/btb.cc b/src/cpu/o3/btb.cc new file mode 100644 index 000000000..93d6ee768 --- /dev/null +++ b/src/cpu/o3/btb.cc @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "base/intmath.hh" +#include "base/trace.hh" +#include "cpu/o3/btb.hh" + +DefaultBTB::DefaultBTB(unsigned _numEntries, + unsigned _tagBits, + unsigned _instShiftAmt) + : numEntries(_numEntries), + tagBits(_tagBits), + instShiftAmt(_instShiftAmt) +{ + DPRINTF(Fetch, "BTB: Creating BTB object.\n"); + + if (!isPowerOf2(numEntries)) { + fatal("BTB entries is not a power of 2!"); + } + + btb.resize(numEntries); + + for (int i = 0; i < numEntries; ++i) { + btb[i].valid = false; + } + + idxMask = numEntries - 1; + + tagMask = (1 << tagBits) - 1; + + tagShiftAmt = instShiftAmt + floorLog2(numEntries); +} + +void +DefaultBTB::reset() +{ + for (int i = 0; i < numEntries; ++i) { + btb[i].valid = false; + } +} + +inline +unsigned +DefaultBTB::getIndex(const Addr &inst_PC) +{ + // Need to shift PC over by the word offset. + return (inst_PC >> instShiftAmt) & idxMask; +} + +inline +Addr +DefaultBTB::getTag(const Addr &inst_PC) +{ + return (inst_PC >> tagShiftAmt) & tagMask; +} + +bool +DefaultBTB::valid(const Addr &inst_PC, unsigned tid) +{ + unsigned btb_idx = getIndex(inst_PC); + + Addr inst_tag = getTag(inst_PC); + + assert(btb_idx < numEntries); + + if (btb[btb_idx].valid + && inst_tag == btb[btb_idx].tag + && btb[btb_idx].tid == tid) { + return true; + } else { + return false; + } +} + +// @todo Create some sort of return struct that has both whether or not the +// address is valid, and also the address. For now will just use addr = 0 to +// represent invalid entry. +Addr +DefaultBTB::lookup(const Addr &inst_PC, unsigned tid) +{ + unsigned btb_idx = getIndex(inst_PC); + + Addr inst_tag = getTag(inst_PC); + + assert(btb_idx < numEntries); + + if (btb[btb_idx].valid + && inst_tag == btb[btb_idx].tag + && btb[btb_idx].tid == tid) { + return btb[btb_idx].target; + } else { + return 0; + } +} + +void +DefaultBTB::update(const Addr &inst_PC, const Addr &target, unsigned tid) +{ + unsigned btb_idx = getIndex(inst_PC); + + assert(btb_idx < numEntries); + + btb[btb_idx].tid = tid; + btb[btb_idx].valid = true; + btb[btb_idx].target = target; + btb[btb_idx].tag = getTag(inst_PC); +} diff --git a/src/cpu/o3/btb.hh b/src/cpu/o3/btb.hh new file mode 100644 index 000000000..3c4899e89 --- /dev/null +++ b/src/cpu/o3/btb.hh @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_BTB_HH__ +#define __CPU_O3_BTB_HH__ + +#include "base/misc.hh" +#include "sim/host.hh" + +class DefaultBTB +{ + private: + struct BTBEntry + { + BTBEntry() + : tag(0), target(0), valid(false) + { + } + + /** The entry's tag. */ + Addr tag; + + /** The entry's target. */ + Addr target; + + /** The entry's thread id. */ + unsigned tid; + + /** Whether or not the entry is valid. */ + bool valid; + }; + + public: + /** Creates a BTB with the given number of entries, number of bits per + * tag, and instruction offset amount. + * @param numEntries Number of entries for the BTB. + * @param tagBits Number of bits for each tag in the BTB. + * @param instShiftAmt Offset amount for instructions to ignore alignment. + */ + DefaultBTB(unsigned numEntries, unsigned tagBits, + unsigned instShiftAmt); + + void reset(); + + /** Looks up an address in the BTB. Must call valid() first on the address. + * @param inst_PC The address of the branch to look up. + * @param tid The thread id. + * @return Returns the target of the branch. + */ + Addr lookup(const Addr &inst_PC, unsigned tid); + + /** Checks if a branch is in the BTB. + * @param inst_PC The address of the branch to look up. + * @param tid The thread id. + * @return Whether or not the branch exists in the BTB. + */ + bool valid(const Addr &inst_PC, unsigned tid); + + /** Updates the BTB with the target of a branch. + * @param inst_PC The address of the branch being updated. + * @param target_PC The target address of the branch. + * @param tid The thread id. + */ + void update(const Addr &inst_PC, const Addr &target_PC, + unsigned tid); + + private: + /** Returns the index into the BTB, based on the branch's PC. + * @param inst_PC The branch to look up. + * @return Returns the index into the BTB. + */ + inline unsigned getIndex(const Addr &inst_PC); + + /** Returns the tag bits of a given address. + * @param inst_PC The branch's address. + * @return Returns the tag bits. + */ + inline Addr getTag(const Addr &inst_PC); + + /** The actual BTB. */ + std::vector<BTBEntry> btb; + + /** The number of entries in the BTB. */ + unsigned numEntries; + + /** The index mask. */ + unsigned idxMask; + + /** The number of tag bits per entry. */ + unsigned tagBits; + + /** The tag mask. */ + unsigned tagMask; + + /** Number of bits to shift PC when calculating index. */ + unsigned instShiftAmt; + + /** Number of bits to shift PC when calculating tag. */ + unsigned tagShiftAmt; +}; + +#endif // __CPU_O3_BTB_HH__ diff --git a/src/cpu/o3/checker_builder.cc b/src/cpu/o3/checker_builder.cc new file mode 100644 index 000000000..ad83ec57a --- /dev/null +++ b/src/cpu/o3/checker_builder.cc @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include <string> + +#include "cpu/checker/cpu_impl.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/alpha/dyn_inst.hh" +#include "cpu/o3/alpha/impl.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +class MemObject; + +template +class Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > >; + +/** + * Specific non-templated derived class used for SimObject configuration. + */ +class O3Checker : public Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > > +{ + public: + O3Checker(Params *p) + : Checker<RefCountingPtr<AlphaDynInst<AlphaSimpleImpl> > >(p) + { } +}; + +//////////////////////////////////////////////////////////////////////// +// +// CheckerCPU Simulation Object +// +BEGIN_DECLARE_SIM_OBJECT_PARAMS(O3Checker) + + Param<Counter> max_insts_any_thread; + Param<Counter> max_insts_all_threads; + Param<Counter> max_loads_any_thread; + Param<Counter> max_loads_all_threads; + Param<Counter> stats_reset_inst; + Param<Tick> progress_interval; + +#if FULL_SYSTEM + SimObjectParam<AlphaITB *> itb; + SimObjectParam<AlphaDTB *> dtb; + SimObjectParam<System *> system; + Param<int> cpu_id; + Param<Tick> profile; +#else + SimObjectParam<Process *> workload; +#endif // FULL_SYSTEM + Param<int> clock; + + Param<bool> defer_registration; + Param<bool> exitOnError; + Param<bool> updateOnError; + Param<bool> warnOnlyOnLoadError; + Param<bool> function_trace; + Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(O3Checker) + +BEGIN_INIT_SIM_OBJECT_PARAMS(O3Checker) + + INIT_PARAM(max_insts_any_thread, + "terminate when any thread reaches this inst count"), + INIT_PARAM(max_insts_all_threads, + "terminate when all threads have reached this inst count"), + INIT_PARAM(max_loads_any_thread, + "terminate when any thread reaches this load count"), + INIT_PARAM(max_loads_all_threads, + "terminate when all threads have reached this load count"), + INIT_PARAM(stats_reset_inst, + "blah"), + INIT_PARAM_DFLT(progress_interval, "CPU Progress Interval", 0), + +#if FULL_SYSTEM + INIT_PARAM(itb, "Instruction TLB"), + INIT_PARAM(dtb, "Data TLB"), + INIT_PARAM(system, "system object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(profile, ""), +#else + INIT_PARAM(workload, "processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM(clock, "clock speed"), + + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + INIT_PARAM(exitOnError, "exit on error"), + INIT_PARAM(updateOnError, "Update the checker with the main CPU's state on error"), + INIT_PARAM_DFLT(warnOnlyOnLoadError, "warn, but don't exit, if a load " + "result errors", false), + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(O3Checker) + + +CREATE_SIM_OBJECT(O3Checker) +{ + O3Checker::Params *params = new O3Checker::Params(); + params->name = getInstanceName(); + params->numberOfThreads = 1; + params->max_insts_any_thread = 0; + params->max_insts_all_threads = 0; + params->max_loads_any_thread = 0; + params->max_loads_all_threads = 0; + params->stats_reset_inst = 0; + params->exitOnError = exitOnError; + params->updateOnError = updateOnError; + params->warnOnlyOnLoadError = warnOnlyOnLoadError; + params->deferRegistration = defer_registration; + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + params->clock = clock; + // Hack to touch all parameters. Consider not deriving Checker + // from BaseCPU..it's not really a CPU in the end. + Counter temp; + temp = max_insts_any_thread; + temp = max_insts_all_threads; + temp = max_loads_any_thread; + temp = max_loads_all_threads; + temp = stats_reset_inst; + Tick temp2 = progress_interval; + params->progress_interval = 0; + temp2++; + +#if FULL_SYSTEM + params->itb = itb; + params->dtb = dtb; + params->system = system; + params->cpu_id = cpu_id; + params->profile = profile; +#else + params->process = workload; +#endif + + O3Checker *cpu = new O3Checker(params); + return cpu; +} + +REGISTER_SIM_OBJECT("O3Checker", O3Checker) diff --git a/src/cpu/o3/comm.hh b/src/cpu/o3/comm.hh new file mode 100644 index 000000000..aa58fc20e --- /dev/null +++ b/src/cpu/o3/comm.hh @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_COMM_HH__ +#define __CPU_O3_COMM_HH__ + +#include <vector> + +#include "sim/faults.hh" +#include "cpu/inst_seq.hh" +#include "sim/host.hh" + +// Typedef for physical register index type. Although the Impl would be the +// most likely location for this, there are a few classes that need this +// typedef yet are not templated on the Impl. For now it will be defined here. +typedef short int PhysRegIndex; + +/** Struct that defines the information passed from fetch to decode. */ +template<class Impl> +struct DefaultFetchDefaultDecode { + typedef typename Impl::DynInstPtr DynInstPtr; + + int size; + + DynInstPtr insts[Impl::MaxWidth]; + Fault fetchFault; + InstSeqNum fetchFaultSN; + bool clearFetchFault; +}; + +/** Struct that defines the information passed from decode to rename. */ +template<class Impl> +struct DefaultDecodeDefaultRename { + typedef typename Impl::DynInstPtr DynInstPtr; + + int size; + + DynInstPtr insts[Impl::MaxWidth]; +}; + +/** Struct that defines the information passed from rename to IEW. */ +template<class Impl> +struct DefaultRenameDefaultIEW { + typedef typename Impl::DynInstPtr DynInstPtr; + + int size; + + DynInstPtr insts[Impl::MaxWidth]; +}; + +/** Struct that defines the information passed from IEW to commit. */ +template<class Impl> +struct DefaultIEWDefaultCommit { + typedef typename Impl::DynInstPtr DynInstPtr; + + int size; + + DynInstPtr insts[Impl::MaxWidth]; + + bool squash[Impl::MaxThreads]; + bool branchMispredict[Impl::MaxThreads]; + bool branchTaken[Impl::MaxThreads]; + bool condDelaySlotBranch[Impl::MaxThreads]; + uint64_t mispredPC[Impl::MaxThreads]; + uint64_t nextPC[Impl::MaxThreads]; + InstSeqNum squashedSeqNum[Impl::MaxThreads]; + + bool includeSquashInst[Impl::MaxThreads]; +}; + +template<class Impl> +struct IssueStruct { + typedef typename Impl::DynInstPtr DynInstPtr; + + int size; + + DynInstPtr insts[Impl::MaxWidth]; +}; + +/** Struct that defines all backwards communication. */ +template<class Impl> +struct TimeBufStruct { + struct decodeComm { + bool squash; + bool predIncorrect; + uint64_t branchAddr; + + InstSeqNum doneSeqNum; + InstSeqNum bdelayDoneSeqNum; + + // @todo: Might want to package this kind of branch stuff into a single + // struct as it is used pretty frequently. + bool branchMispredict; + bool branchTaken; + uint64_t mispredPC; + uint64_t nextPC; + + unsigned branchCount; + }; + + decodeComm decodeInfo[Impl::MaxThreads]; + + struct renameComm { + }; + + renameComm renameInfo[Impl::MaxThreads]; + + struct iewComm { + // Also eventually include skid buffer space. + bool usedIQ; + unsigned freeIQEntries; + bool usedLSQ; + unsigned freeLSQEntries; + + unsigned iqCount; + unsigned ldstqCount; + + unsigned dispatched; + unsigned dispatchedToLSQ; + }; + + iewComm iewInfo[Impl::MaxThreads]; + + struct commitComm { + bool usedROB; + unsigned freeROBEntries; + bool emptyROB; + + bool squash; + bool robSquashing; + + bool branchMispredict; + bool branchTaken; + uint64_t mispredPC; + uint64_t nextPC; + + // Represents the instruction that has either been retired or + // squashed. Similar to having a single bus that broadcasts the + // retired or squashed sequence number. + InstSeqNum doneSeqNum; + + InstSeqNum bdelayDoneSeqNum; + bool squashDelaySlot; + + //Just in case we want to do a commit/squash on a cycle + //(necessary for multiple ROBs?) + bool commitInsts; + InstSeqNum squashSeqNum; + + // Communication specifically to the IQ to tell the IQ that it can + // schedule a non-speculative instruction. + InstSeqNum nonSpecSeqNum; + + // Hack for now to send back an uncached access to the IEW stage. + typedef typename Impl::DynInstPtr DynInstPtr; + bool uncached; + DynInstPtr uncachedLoad; + + bool interruptPending; + bool clearInterrupt; + }; + + commitComm commitInfo[Impl::MaxThreads]; + + bool decodeBlock[Impl::MaxThreads]; + bool decodeUnblock[Impl::MaxThreads]; + bool renameBlock[Impl::MaxThreads]; + bool renameUnblock[Impl::MaxThreads]; + bool iewBlock[Impl::MaxThreads]; + bool iewUnblock[Impl::MaxThreads]; + bool commitBlock[Impl::MaxThreads]; + bool commitUnblock[Impl::MaxThreads]; +}; + +#endif //__CPU_O3_COMM_HH__ diff --git a/src/cpu/o3/commit.cc b/src/cpu/o3/commit.cc new file mode 100644 index 000000000..637d59f52 --- /dev/null +++ b/src/cpu/o3/commit.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/commit_impl.hh" + +template class DefaultCommit<O3CPUImpl>; diff --git a/src/cpu/o3/commit.hh b/src/cpu/o3/commit.hh new file mode 100644 index 000000000..7575783f7 --- /dev/null +++ b/src/cpu/o3/commit.hh @@ -0,0 +1,455 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#ifndef __CPU_O3_COMMIT_HH__ +#define __CPU_O3_COMMIT_HH__ + +#include "base/statistics.hh" +#include "base/timebuf.hh" +#include "cpu/exetrace.hh" +#include "cpu/inst_seq.hh" + +template <class> +class O3ThreadState; + +/** + * DefaultCommit handles single threaded and SMT commit. Its width is + * specified by the parameters; each cycle it tries to commit that + * many instructions. The SMT policy decides which thread it tries to + * commit instructions from. Non- speculative instructions must reach + * the head of the ROB before they are ready to execute; once they + * reach the head, commit will broadcast the instruction's sequence + * number to the previous stages so that they can issue/ execute the + * instruction. Only one non-speculative instruction is handled per + * cycle. Commit is responsible for handling all back-end initiated + * redirects. It receives the redirect, and then broadcasts it to all + * stages, indicating the sequence number they should squash until, + * and any necessary branch misprediction information as well. It + * priortizes redirects by instruction's age, only broadcasting a + * redirect if it corresponds to an instruction that should currently + * be in the ROB. This is done by tracking the sequence number of the + * youngest instruction in the ROB, which gets updated to any + * squashing instruction's sequence number, and only broadcasting a + * redirect if it corresponds to an older instruction. Commit also + * supports multiple cycle squashing, to model a ROB that can only + * remove a certain number of instructions per cycle. + */ +template<class Impl> +class DefaultCommit +{ + public: + // Typedefs from the Impl. + typedef typename Impl::O3CPU O3CPU; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::Params Params; + typedef typename Impl::CPUPol CPUPol; + + typedef typename CPUPol::RenameMap RenameMap; + typedef typename CPUPol::ROB ROB; + + typedef typename CPUPol::TimeStruct TimeStruct; + typedef typename CPUPol::FetchStruct FetchStruct; + typedef typename CPUPol::IEWStruct IEWStruct; + typedef typename CPUPol::RenameStruct RenameStruct; + + typedef typename CPUPol::Fetch Fetch; + typedef typename CPUPol::IEW IEW; + + typedef O3ThreadState<Impl> Thread; + + /** Event class used to schedule a squash due to a trap (fault or + * interrupt) to happen on a specific cycle. + */ + class TrapEvent : public Event { + private: + DefaultCommit<Impl> *commit; + unsigned tid; + + public: + TrapEvent(DefaultCommit<Impl> *_commit, unsigned _tid); + + void process(); + const char *description(); + }; + + /** Overall commit status. Used to determine if the CPU can deschedule + * itself due to a lack of activity. + */ + enum CommitStatus{ + Active, + Inactive + }; + + /** Individual thread status. */ + enum ThreadStatus { + Running, + Idle, + ROBSquashing, + TrapPending, + FetchTrapPending + }; + + /** Commit policy for SMT mode. */ + enum CommitPolicy { + Aggressive, + RoundRobin, + OldestReady + }; + + private: + /** Overall commit status. */ + CommitStatus _status; + /** Next commit status, to be set at the end of the cycle. */ + CommitStatus _nextStatus; + /** Per-thread status. */ + ThreadStatus commitStatus[Impl::MaxThreads]; + /** Commit policy used in SMT mode. */ + CommitPolicy commitPolicy; + + public: + /** Construct a DefaultCommit with the given parameters. */ + DefaultCommit(Params *params); + + /** Returns the name of the DefaultCommit. */ + std::string name() const; + + /** Registers statistics. */ + void regStats(); + + /** Sets the CPU pointer. */ + void setCPU(O3CPU *cpu_ptr); + + /** Sets the list of threads. */ + void setThreads(std::vector<Thread *> &threads); + + /** Sets the main time buffer pointer, used for backwards communication. */ + void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + + void setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr); + + /** Sets the pointer to the queue coming from rename. */ + void setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr); + + /** Sets the pointer to the queue coming from IEW. */ + void setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr); + + /** Sets the pointer to the IEW stage. */ + void setIEWStage(IEW *iew_stage); + + /** Skid buffer between rename and commit. */ + std::queue<DynInstPtr> skidBuffer; + + /** The pointer to the IEW stage. Used solely to ensure that + * various events (traps, interrupts, syscalls) do not occur until + * all stores have written back. + */ + IEW *iewStage; + + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Sets pointer to the commited state rename map. */ + void setRenameMap(RenameMap rm_ptr[Impl::MaxThreads]); + + /** Sets pointer to the ROB. */ + void setROB(ROB *rob_ptr); + + /** Initializes stage by sending back the number of free entries. */ + void initStage(); + + /** Initializes the draining of commit. */ + bool drain(); + + /** Resumes execution after draining. */ + void resume(); + + /** Completes the switch out of commit. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Ticks the commit stage, which tries to commit instructions. */ + void tick(); + + /** Handles any squashes that are sent from IEW, and adds instructions + * to the ROB and tries to commit instructions. + */ + void commit(); + + /** Returns the number of free ROB entries for a specific thread. */ + unsigned numROBFreeEntries(unsigned tid); + + /** Generates an event to schedule a squash due to a trap. */ + void generateTrapEvent(unsigned tid); + + /** Records that commit needs to initiate a squash due to an + * external state update through the TC. + */ + void generateTCEvent(unsigned tid); + + private: + /** Updates the overall status of commit with the nextStatus, and + * tell the CPU if commit is active/inactive. + */ + void updateStatus(); + + /** Sets the next status based on threads' statuses, which becomes the + * current status at the end of the cycle. + */ + void setNextStatus(); + + /** Checks if the ROB is completed with squashing. This is for the case + * where the ROB can take multiple cycles to complete squashing. + */ + bool robDoneSquashing(); + + /** Returns if any of the threads have the number of ROB entries changed + * on this cycle. Used to determine if the number of free ROB entries needs + * to be sent back to previous stages. + */ + bool changedROBEntries(); + + /** Squashes all in flight instructions. */ + void squashAll(unsigned tid); + + /** Handles squashing due to a trap. */ + void squashFromTrap(unsigned tid); + + /** Handles squashing due to an TC write. */ + void squashFromTC(unsigned tid); + + /** Commits as many instructions as possible. */ + void commitInsts(); + + /** Tries to commit the head ROB instruction passed in. + * @param head_inst The instruction to be committed. + */ + bool commitHead(DynInstPtr &head_inst, unsigned inst_num); + + /** Gets instructions from rename and inserts them into the ROB. */ + void getInsts(); + + /** Insert all instructions from rename into skidBuffer */ + void skidInsert(); + + /** Marks completed instructions using information sent from IEW. */ + void markCompletedInsts(); + + /** Gets the thread to commit, based on the SMT policy. */ + int getCommittingThread(); + + /** Returns the thread ID to use based on a round robin policy. */ + int roundRobin(); + + /** Returns the thread ID to use based on an oldest instruction policy. */ + int oldestReady(); + + public: + /** Returns the PC of the head instruction of the ROB. + * @todo: Probably remove this function as it returns only thread 0. + */ + uint64_t readPC() { return PC[0]; } + + /** Returns the PC of a specific thread. */ + uint64_t readPC(unsigned tid) { return PC[tid]; } + + /** Sets the PC of a specific thread. */ + void setPC(uint64_t val, unsigned tid) { PC[tid] = val; } + + /** Reads the next PC of a specific thread. */ + uint64_t readNextPC(unsigned tid) { return nextPC[tid]; } + + /** Sets the next PC of a specific thread. */ + void setNextPC(uint64_t val, unsigned tid) { nextPC[tid] = val; } + + /** Reads the next NPC of a specific thread. */ + uint64_t readNextNPC(unsigned tid) { return nextNPC[tid]; } + + /** Sets the next NPC of a specific thread. */ + void setNextNPC(uint64_t val, unsigned tid) { nextNPC[tid] = val; } + + private: + /** Time buffer interface. */ + TimeBuffer<TimeStruct> *timeBuffer; + + /** Wire to write information heading to previous stages. */ + typename TimeBuffer<TimeStruct>::wire toIEW; + + /** Wire to read information from IEW (for ROB). */ + typename TimeBuffer<TimeStruct>::wire robInfoFromIEW; + + TimeBuffer<FetchStruct> *fetchQueue; + + typename TimeBuffer<FetchStruct>::wire fromFetch; + + /** IEW instruction queue interface. */ + TimeBuffer<IEWStruct> *iewQueue; + + /** Wire to read information from IEW queue. */ + typename TimeBuffer<IEWStruct>::wire fromIEW; + + /** Rename instruction queue interface, for ROB. */ + TimeBuffer<RenameStruct> *renameQueue; + + /** Wire to read information from rename queue. */ + typename TimeBuffer<RenameStruct>::wire fromRename; + + public: + /** ROB interface. */ + ROB *rob; + + private: + /** Pointer to O3CPU. */ + O3CPU *cpu; + + /** Vector of all of the threads. */ + std::vector<Thread *> thread; + + /** Records that commit has written to the time buffer this cycle. Used for + * the CPU to determine if it can deschedule itself if there is no activity. + */ + bool wroteToTimeBuffer; + + /** Records if the number of ROB entries has changed this cycle. If it has, + * then the number of free entries must be re-broadcast. + */ + bool changedROBNumEntries[Impl::MaxThreads]; + + /** A counter of how many threads are currently squashing. */ + int squashCounter; + + /** Records if a thread has to squash this cycle due to a trap. */ + bool trapSquash[Impl::MaxThreads]; + + /** Records if a thread has to squash this cycle due to an XC write. */ + bool tcSquash[Impl::MaxThreads]; + + /** Priority List used for Commit Policy */ + std::list<unsigned> priority_list; + + /** IEW to Commit delay, in ticks. */ + unsigned iewToCommitDelay; + + /** Commit to IEW delay, in ticks. */ + unsigned commitToIEWDelay; + + /** Rename to ROB delay, in ticks. */ + unsigned renameToROBDelay; + + unsigned fetchToCommitDelay; + + /** Rename width, in instructions. Used so ROB knows how many + * instructions to get from the rename instruction queue. + */ + unsigned renameWidth; + + /** Commit width, in instructions. */ + unsigned commitWidth; + + /** Number of Reorder Buffers */ + unsigned numRobs; + + /** Number of Active Threads */ + unsigned numThreads; + + /** Is a drain pending. */ + bool drainPending; + + /** Is commit switched out. */ + bool switchedOut; + + /** The latency to handle a trap. Used when scheduling trap + * squash event. + */ + Tick trapLatency; + + /** The commit PC of each thread. Refers to the instruction that + * is currently being processed/committed. + */ + Addr PC[Impl::MaxThreads]; + + /** The next PC of each thread. */ + Addr nextPC[Impl::MaxThreads]; + + /** The next NPC of each thread. */ + Addr nextNPC[Impl::MaxThreads]; + + /** The sequence number of the youngest valid instruction in the ROB. */ + InstSeqNum youngestSeqNum[Impl::MaxThreads]; + + /** Pointer to the list of active threads. */ + std::list<unsigned> *activeThreads; + + /** Rename map interface. */ + RenameMap *renameMap[Impl::MaxThreads]; + + /** Updates commit stats based on this instruction. */ + void updateComInstStats(DynInstPtr &inst); + + /** Stat for the total number of committed instructions. */ + Stats::Scalar<> commitCommittedInsts; + /** Stat for the total number of squashed instructions discarded by commit. + */ + Stats::Scalar<> commitSquashedInsts; + /** Stat for the total number of times commit is told to squash. + * @todo: Actually increment this stat. + */ + Stats::Scalar<> commitSquashEvents; + /** Stat for the total number of times commit has had to stall due to a non- + * speculative instruction reaching the head of the ROB. + */ + Stats::Scalar<> commitNonSpecStalls; + /** Stat for the total number of branch mispredicts that caused a squash. */ + Stats::Scalar<> branchMispredicts; + /** Distribution of the number of committed instructions each cycle. */ + Stats::Distribution<> numCommittedDist; + + /** Total number of instructions committed. */ + Stats::Vector<> statComInst; + /** Total number of software prefetches committed. */ + Stats::Vector<> statComSwp; + /** Stat for the total number of committed memory references. */ + Stats::Vector<> statComRefs; + /** Stat for the total number of committed loads. */ + Stats::Vector<> statComLoads; + /** Total number of committed memory barriers. */ + Stats::Vector<> statComMembars; + /** Total number of committed branches. */ + Stats::Vector<> statComBranches; + + /** Number of cycles where the commit bandwidth limit is reached. */ + Stats::Scalar<> commitEligibleSamples; + /** Number of instructions not committed due to bandwidth limits. */ + Stats::Vector<> commitEligible; +}; + +#endif // __CPU_O3_COMMIT_HH__ diff --git a/src/cpu/o3/commit_impl.hh b/src/cpu/o3/commit_impl.hh new file mode 100644 index 000000000..6ae01ae67 --- /dev/null +++ b/src/cpu/o3/commit_impl.hh @@ -0,0 +1,1419 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "config/full_system.hh" +#include "config/use_checker.hh" + +#include <algorithm> +#include <string> + +#include "base/loader/symtab.hh" +#include "base/timebuf.hh" +#include "cpu/exetrace.hh" +#include "cpu/o3/commit.hh" +#include "cpu/o3/thread_state.hh" + +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + +template <class Impl> +DefaultCommit<Impl>::TrapEvent::TrapEvent(DefaultCommit<Impl> *_commit, + unsigned _tid) + : Event(&mainEventQueue, CPU_Tick_Pri), commit(_commit), tid(_tid) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +DefaultCommit<Impl>::TrapEvent::process() +{ + // This will get reset by commit if it was switched out at the + // time of this event processing. + commit->trapSquash[tid] = true; +} + +template <class Impl> +const char * +DefaultCommit<Impl>::TrapEvent::description() +{ + return "Trap event"; +} + +template <class Impl> +DefaultCommit<Impl>::DefaultCommit(Params *params) + : squashCounter(0), + iewToCommitDelay(params->iewToCommitDelay), + commitToIEWDelay(params->commitToIEWDelay), + renameToROBDelay(params->renameToROBDelay), + fetchToCommitDelay(params->commitToFetchDelay), + renameWidth(params->renameWidth), + commitWidth(params->commitWidth), + numThreads(params->numberOfThreads), + drainPending(false), + switchedOut(false), + trapLatency(params->trapLatency) +{ + _status = Active; + _nextStatus = Inactive; + std::string policy = params->smtCommitPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Assign commit policy + if (policy == "aggressive"){ + commitPolicy = Aggressive; + + DPRINTF(Commit,"Commit Policy set to Aggressive."); + } else if (policy == "roundrobin"){ + commitPolicy = RoundRobin; + + //Set-Up Priority List + for (int tid=0; tid < numThreads; tid++) { + priority_list.push_back(tid); + } + + DPRINTF(Commit,"Commit Policy set to Round Robin."); + } else if (policy == "oldestready"){ + commitPolicy = OldestReady; + + DPRINTF(Commit,"Commit Policy set to Oldest Ready."); + } else { + assert(0 && "Invalid SMT Commit Policy. Options Are: {Aggressive," + "RoundRobin,OldestReady}"); + } + + for (int i=0; i < numThreads; i++) { + commitStatus[i] = Idle; + changedROBNumEntries[i] = false; + trapSquash[i] = false; + tcSquash[i] = false; + PC[i] = nextPC[i] = nextNPC[i] = 0; + } +} + +template <class Impl> +std::string +DefaultCommit<Impl>::name() const +{ + return cpu->name() + ".commit"; +} + +template <class Impl> +void +DefaultCommit<Impl>::regStats() +{ + using namespace Stats; + commitCommittedInsts + .name(name() + ".commitCommittedInsts") + .desc("The number of committed instructions") + .prereq(commitCommittedInsts); + commitSquashedInsts + .name(name() + ".commitSquashedInsts") + .desc("The number of squashed insts skipped by commit") + .prereq(commitSquashedInsts); + commitSquashEvents + .name(name() + ".commitSquashEvents") + .desc("The number of times commit is told to squash") + .prereq(commitSquashEvents); + commitNonSpecStalls + .name(name() + ".commitNonSpecStalls") + .desc("The number of times commit has been forced to stall to " + "communicate backwards") + .prereq(commitNonSpecStalls); + branchMispredicts + .name(name() + ".branchMispredicts") + .desc("The number of times a branch was mispredicted") + .prereq(branchMispredicts); + numCommittedDist + .init(0,commitWidth,1) + .name(name() + ".COM:committed_per_cycle") + .desc("Number of insts commited each cycle") + .flags(Stats::pdf) + ; + + statComInst + .init(cpu->number_of_threads) + .name(name() + ".COM:count") + .desc("Number of instructions committed") + .flags(total) + ; + + statComSwp + .init(cpu->number_of_threads) + .name(name() + ".COM:swp_count") + .desc("Number of s/w prefetches committed") + .flags(total) + ; + + statComRefs + .init(cpu->number_of_threads) + .name(name() + ".COM:refs") + .desc("Number of memory references committed") + .flags(total) + ; + + statComLoads + .init(cpu->number_of_threads) + .name(name() + ".COM:loads") + .desc("Number of loads committed") + .flags(total) + ; + + statComMembars + .init(cpu->number_of_threads) + .name(name() + ".COM:membars") + .desc("Number of memory barriers committed") + .flags(total) + ; + + statComBranches + .init(cpu->number_of_threads) + .name(name() + ".COM:branches") + .desc("Number of branches committed") + .flags(total) + ; + + commitEligible + .init(cpu->number_of_threads) + .name(name() + ".COM:bw_limited") + .desc("number of insts not committed due to BW limits") + .flags(total) + ; + + commitEligibleSamples + .name(name() + ".COM:bw_lim_events") + .desc("number cycles where commit BW limit reached") + ; +} + +template <class Impl> +void +DefaultCommit<Impl>::setCPU(O3CPU *cpu_ptr) +{ + DPRINTF(Commit, "Commit: Setting CPU pointer.\n"); + cpu = cpu_ptr; + + // Commit must broadcast the number of free entries it has at the start of + // the simulation, so it starts as active. + cpu->activateStage(O3CPU::CommitIdx); + + trapLatency = cpu->cycles(trapLatency); +} + +template <class Impl> +void +DefaultCommit<Impl>::setThreads(std::vector<Thread *> &threads) +{ + thread = threads; +} + +template <class Impl> +void +DefaultCommit<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +{ + DPRINTF(Commit, "Commit: Setting time buffer pointer.\n"); + timeBuffer = tb_ptr; + + // Setup wire to send information back to IEW. + toIEW = timeBuffer->getWire(0); + + // Setup wire to read data from IEW (for the ROB). + robInfoFromIEW = timeBuffer->getWire(-iewToCommitDelay); +} + +template <class Impl> +void +DefaultCommit<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) +{ + DPRINTF(Commit, "Commit: Setting fetch queue pointer.\n"); + fetchQueue = fq_ptr; + + // Setup wire to get instructions from rename (for the ROB). + fromFetch = fetchQueue->getWire(-fetchToCommitDelay); +} + +template <class Impl> +void +DefaultCommit<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) +{ + DPRINTF(Commit, "Commit: Setting rename queue pointer.\n"); + renameQueue = rq_ptr; + + // Setup wire to get instructions from rename (for the ROB). + fromRename = renameQueue->getWire(-renameToROBDelay); +} + +template <class Impl> +void +DefaultCommit<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) +{ + DPRINTF(Commit, "Commit: Setting IEW queue pointer.\n"); + iewQueue = iq_ptr; + + // Setup wire to get instructions from IEW. + fromIEW = iewQueue->getWire(-iewToCommitDelay); +} + +template <class Impl> +void +DefaultCommit<Impl>::setIEWStage(IEW *iew_stage) +{ + iewStage = iew_stage; +} + +template<class Impl> +void +DefaultCommit<Impl>::setActiveThreads(std::list<unsigned> *at_ptr) +{ + DPRINTF(Commit, "Commit: Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template <class Impl> +void +DefaultCommit<Impl>::setRenameMap(RenameMap rm_ptr[]) +{ + DPRINTF(Commit, "Setting rename map pointers.\n"); + + for (int i=0; i < numThreads; i++) { + renameMap[i] = &rm_ptr[i]; + } +} + +template <class Impl> +void +DefaultCommit<Impl>::setROB(ROB *rob_ptr) +{ + DPRINTF(Commit, "Commit: Setting ROB pointer.\n"); + rob = rob_ptr; +} + +template <class Impl> +void +DefaultCommit<Impl>::initStage() +{ + rob->setActiveThreads(activeThreads); + rob->resetEntries(); + + // Broadcast the number of free entries. + for (int i=0; i < numThreads; i++) { + toIEW->commitInfo[i].usedROB = true; + toIEW->commitInfo[i].freeROBEntries = rob->numFreeEntries(i); + } + + cpu->activityThisCycle(); +} + +template <class Impl> +bool +DefaultCommit<Impl>::drain() +{ + drainPending = true; + + // If it's already drained, return true. + if (rob->isEmpty() && !iewStage->hasStoresToWB()) { + cpu->signalDrained(); + return true; + } + + return false; +} + +template <class Impl> +void +DefaultCommit<Impl>::switchOut() +{ + switchedOut = true; + drainPending = false; + rob->switchOut(); +} + +template <class Impl> +void +DefaultCommit<Impl>::resume() +{ + drainPending = false; +} + +template <class Impl> +void +DefaultCommit<Impl>::takeOverFrom() +{ + switchedOut = false; + _status = Active; + _nextStatus = Inactive; + for (int i=0; i < numThreads; i++) { + commitStatus[i] = Idle; + changedROBNumEntries[i] = false; + trapSquash[i] = false; + tcSquash[i] = false; + } + squashCounter = 0; + rob->takeOverFrom(); +} + +template <class Impl> +void +DefaultCommit<Impl>::updateStatus() +{ + // reset ROB changed variable + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + changedROBNumEntries[tid] = false; + + // Also check if any of the threads has a trap pending + if (commitStatus[tid] == TrapPending || + commitStatus[tid] == FetchTrapPending) { + _nextStatus = Active; + } + } + + if (_nextStatus == Inactive && _status == Active) { + DPRINTF(Activity, "Deactivating stage.\n"); + cpu->deactivateStage(O3CPU::CommitIdx); + } else if (_nextStatus == Active && _status == Inactive) { + DPRINTF(Activity, "Activating stage.\n"); + cpu->activateStage(O3CPU::CommitIdx); + } + + _status = _nextStatus; +} + +template <class Impl> +void +DefaultCommit<Impl>::setNextStatus() +{ + int squashes = 0; + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (commitStatus[tid] == ROBSquashing) { + squashes++; + } + } + + squashCounter = squashes; + + // If commit is currently squashing, then it will have activity for the + // next cycle. Set its next status as active. + if (squashCounter) { + _nextStatus = Active; + } +} + +template <class Impl> +bool +DefaultCommit<Impl>::changedROBEntries() +{ + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (changedROBNumEntries[tid]) { + return true; + } + } + + return false; +} + +template <class Impl> +unsigned +DefaultCommit<Impl>::numROBFreeEntries(unsigned tid) +{ + return rob->numFreeEntries(tid); +} + +template <class Impl> +void +DefaultCommit<Impl>::generateTrapEvent(unsigned tid) +{ + DPRINTF(Commit, "Generating trap event for [tid:%i]\n", tid); + + TrapEvent *trap = new TrapEvent(this, tid); + + trap->schedule(curTick + trapLatency); + + thread[tid]->trapPending = true; +} + +template <class Impl> +void +DefaultCommit<Impl>::generateTCEvent(unsigned tid) +{ + DPRINTF(Commit, "Generating TC squash event for [tid:%i]\n", tid); + + tcSquash[tid] = true; +} + +template <class Impl> +void +DefaultCommit<Impl>::squashAll(unsigned tid) +{ + // If we want to include the squashing instruction in the squash, + // then use one older sequence number. + // Hopefully this doesn't mess things up. Basically I want to squash + // all instructions of this thread. + InstSeqNum squashed_inst = rob->isEmpty() ? + 0 : rob->readHeadInst(tid)->seqNum - 1;; + + // All younger instructions will be squashed. Set the sequence + // number as the youngest instruction in the ROB (0 in this case. + // Hopefully nothing breaks.) + youngestSeqNum[tid] = 0; + + rob->squash(squashed_inst, tid); + changedROBNumEntries[tid] = true; + + // Send back the sequence number of the squashed instruction. + toIEW->commitInfo[tid].doneSeqNum = squashed_inst; + + // Send back the squash signal to tell stages that they should + // squash. + toIEW->commitInfo[tid].squash = true; + + // Send back the rob squashing signal so other stages know that + // the ROB is in the process of squashing. + toIEW->commitInfo[tid].robSquashing = true; + + toIEW->commitInfo[tid].branchMispredict = false; + + toIEW->commitInfo[tid].nextPC = PC[tid]; +} + +template <class Impl> +void +DefaultCommit<Impl>::squashFromTrap(unsigned tid) +{ + squashAll(tid); + + DPRINTF(Commit, "Squashing from trap, restarting at PC %#x\n", PC[tid]); + + thread[tid]->trapPending = false; + thread[tid]->inSyscall = false; + + trapSquash[tid] = false; + + commitStatus[tid] = ROBSquashing; + cpu->activityThisCycle(); +} + +template <class Impl> +void +DefaultCommit<Impl>::squashFromTC(unsigned tid) +{ + squashAll(tid); + + DPRINTF(Commit, "Squashing from TC, restarting at PC %#x\n", PC[tid]); + + thread[tid]->inSyscall = false; + assert(!thread[tid]->trapPending); + + commitStatus[tid] = ROBSquashing; + cpu->activityThisCycle(); + + tcSquash[tid] = false; +} + +template <class Impl> +void +DefaultCommit<Impl>::tick() +{ + wroteToTimeBuffer = false; + _nextStatus = Inactive; + + if (drainPending && rob->isEmpty() && !iewStage->hasStoresToWB()) { + cpu->signalDrained(); + drainPending = false; + return; + } + + if ((*activeThreads).size() <= 0) + return; + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + // Check if any of the threads are done squashing. Change the + // status if they are done. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (commitStatus[tid] == ROBSquashing) { + + if (rob->isDoneSquashing(tid)) { + commitStatus[tid] = Running; + } else { + DPRINTF(Commit,"[tid:%u]: Still Squashing, cannot commit any" + " insts this cycle.\n", tid); + rob->doSquash(tid); + toIEW->commitInfo[tid].robSquashing = true; + wroteToTimeBuffer = true; + } + } + } + + commit(); + + markCompletedInsts(); + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (!rob->isEmpty(tid) && rob->readHeadInst(tid)->readyToCommit()) { + // The ROB has more instructions it can commit. Its next status + // will be active. + _nextStatus = Active; + + DynInstPtr inst = rob->readHeadInst(tid); + + DPRINTF(Commit,"[tid:%i]: Instruction [sn:%lli] PC %#x is head of" + " ROB and ready to commit\n", + tid, inst->seqNum, inst->readPC()); + + } else if (!rob->isEmpty(tid)) { + DynInstPtr inst = rob->readHeadInst(tid); + + DPRINTF(Commit,"[tid:%i]: Can't commit, Instruction [sn:%lli] PC " + "%#x is head of ROB and not ready\n", + tid, inst->seqNum, inst->readPC()); + } + + DPRINTF(Commit, "[tid:%i]: ROB has %d insts & %d free entries.\n", + tid, rob->countInsts(tid), rob->numFreeEntries(tid)); + } + + + if (wroteToTimeBuffer) { + DPRINTF(Activity, "Activity This Cycle.\n"); + cpu->activityThisCycle(); + } + + updateStatus(); +} + +template <class Impl> +void +DefaultCommit<Impl>::commit() +{ + + ////////////////////////////////////// + // Check for interrupts + ////////////////////////////////////// + +#if FULL_SYSTEM + // Process interrupts if interrupts are enabled, not in PAL mode, + // and no other traps or external squashes are currently pending. + // @todo: Allow other threads to handle interrupts. + if (cpu->checkInterrupts && + cpu->check_interrupts() && + !cpu->inPalMode(readPC()) && + !trapSquash[0] && + !tcSquash[0]) { + // Tell fetch that there is an interrupt pending. This will + // make fetch wait until it sees a non PAL-mode PC, at which + // point it stops fetching instructions. + toIEW->commitInfo[0].interruptPending = true; + + // Wait until the ROB is empty and all stores have drained in + // order to enter the interrupt. + if (rob->isEmpty() && !iewStage->hasStoresToWB()) { + // Not sure which thread should be the one to interrupt. For now + // always do thread 0. + assert(!thread[0]->inSyscall); + thread[0]->inSyscall = true; + + // CPU will handle implementation of the interrupt. + cpu->processInterrupts(); + + // Now squash or record that I need to squash this cycle. + commitStatus[0] = TrapPending; + + // Exit state update mode to avoid accidental updating. + thread[0]->inSyscall = false; + + // Generate trap squash event. + generateTrapEvent(0); + + toIEW->commitInfo[0].clearInterrupt = true; + + DPRINTF(Commit, "Interrupt detected.\n"); + } else { + DPRINTF(Commit, "Interrupt pending, waiting for ROB to empty.\n"); + } + } +#endif // FULL_SYSTEM + + //////////////////////////////////// + // Check for any possible squashes, handle them first + //////////////////////////////////// + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + // Not sure which one takes priority. I think if we have + // both, that's a bad sign. + if (trapSquash[tid] == true) { + assert(!tcSquash[tid]); + squashFromTrap(tid); + } else if (tcSquash[tid] == true) { + squashFromTC(tid); + } + + // Squashed sequence number must be older than youngest valid + // instruction in the ROB. This prevents squashes from younger + // instructions overriding squashes from older instructions. + if (fromIEW->squash[tid] && + commitStatus[tid] != TrapPending && + fromIEW->squashedSeqNum[tid] <= youngestSeqNum[tid]) { + + DPRINTF(Commit, "[tid:%i]: Squashing due to PC %#x [sn:%i]\n", + tid, + fromIEW->mispredPC[tid], + fromIEW->squashedSeqNum[tid]); + + DPRINTF(Commit, "[tid:%i]: Redirecting to PC %#x\n", + tid, + fromIEW->nextPC[tid]); + + commitStatus[tid] = ROBSquashing; + + // If we want to include the squashing instruction in the squash, + // then use one older sequence number. + InstSeqNum squashed_inst = fromIEW->squashedSeqNum[tid]; + +#if ISA_HAS_DELAY_SLOT + InstSeqNum bdelay_done_seq_num; + bool squash_bdelay_slot; + + if (fromIEW->branchMispredict[tid]) { + if (fromIEW->branchTaken[tid] && + fromIEW->condDelaySlotBranch[tid]) { + DPRINTF(Commit, "[tid:%i]: Cond. delay slot branch" + "mispredicted as taken. Squashing after previous " + "inst, [sn:%i]\n", + tid, squashed_inst); + bdelay_done_seq_num = squashed_inst; + squash_bdelay_slot = true; + } else { + DPRINTF(Commit, "[tid:%i]: Branch Mispredict. Squashing " + "after delay slot [sn:%i]\n", tid, squashed_inst+1); + bdelay_done_seq_num = squashed_inst + 1; + squash_bdelay_slot = false; + } + } else { + bdelay_done_seq_num = squashed_inst; + } +#endif + + if (fromIEW->includeSquashInst[tid] == true) { + squashed_inst--; +#if ISA_HAS_DELAY_SLOT + bdelay_done_seq_num--; +#endif + } + // All younger instructions will be squashed. Set the sequence + // number as the youngest instruction in the ROB. + youngestSeqNum[tid] = squashed_inst; + +#if ISA_HAS_DELAY_SLOT + rob->squash(bdelay_done_seq_num, tid); + toIEW->commitInfo[tid].squashDelaySlot = squash_bdelay_slot; + toIEW->commitInfo[tid].bdelayDoneSeqNum = bdelay_done_seq_num; +#else + rob->squash(squashed_inst, tid); + toIEW->commitInfo[tid].squashDelaySlot = true; +#endif + changedROBNumEntries[tid] = true; + + toIEW->commitInfo[tid].doneSeqNum = squashed_inst; + + toIEW->commitInfo[tid].squash = true; + + // Send back the rob squashing signal so other stages know that + // the ROB is in the process of squashing. + toIEW->commitInfo[tid].robSquashing = true; + + toIEW->commitInfo[tid].branchMispredict = + fromIEW->branchMispredict[tid]; + + toIEW->commitInfo[tid].branchTaken = + fromIEW->branchTaken[tid]; + + toIEW->commitInfo[tid].nextPC = fromIEW->nextPC[tid]; + + toIEW->commitInfo[tid].mispredPC = fromIEW->mispredPC[tid]; + + if (toIEW->commitInfo[tid].branchMispredict) { + ++branchMispredicts; + } + } + + } + + setNextStatus(); + + if (squashCounter != numThreads) { + // If we're not currently squashing, then get instructions. + getInsts(); + + // Try to commit any instructions. + commitInsts(); + } else { +#if ISA_HAS_DELAY_SLOT + skidInsert(); +#endif + } + + //Check for any activity + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (changedROBNumEntries[tid]) { + toIEW->commitInfo[tid].usedROB = true; + toIEW->commitInfo[tid].freeROBEntries = rob->numFreeEntries(tid); + + if (rob->isEmpty(tid)) { + toIEW->commitInfo[tid].emptyROB = true; + } + + wroteToTimeBuffer = true; + changedROBNumEntries[tid] = false; + } + } +} + +template <class Impl> +void +DefaultCommit<Impl>::commitInsts() +{ + //////////////////////////////////// + // Handle commit + // Note that commit will be handled prior to putting new + // instructions in the ROB so that the ROB only tries to commit + // instructions it has in this current cycle, and not instructions + // it is writing in during this cycle. Can't commit and squash + // things at the same time... + //////////////////////////////////// + + DPRINTF(Commit, "Trying to commit instructions in the ROB.\n"); + + unsigned num_committed = 0; + + DynInstPtr head_inst; + + // Commit as many instructions as possible until the commit bandwidth + // limit is reached, or it becomes impossible to commit any more. + while (num_committed < commitWidth) { + int commit_thread = getCommittingThread(); + + if (commit_thread == -1 || !rob->isHeadReady(commit_thread)) + break; + + head_inst = rob->readHeadInst(commit_thread); + + int tid = head_inst->threadNumber; + + assert(tid == commit_thread); + + DPRINTF(Commit, "Trying to commit head instruction, [sn:%i] [tid:%i]\n", + head_inst->seqNum, tid); + + // If the head instruction is squashed, it is ready to retire + // (be removed from the ROB) at any time. + if (head_inst->isSquashed()) { + + DPRINTF(Commit, "Retiring squashed instruction from " + "ROB.\n"); + + rob->retireHead(commit_thread); + + ++commitSquashedInsts; + + // Record that the number of ROB entries has changed. + changedROBNumEntries[tid] = true; + } else { + PC[tid] = head_inst->readPC(); + nextPC[tid] = head_inst->readNextPC(); + nextNPC[tid] = head_inst->readNextNPC(); + + // Increment the total number of non-speculative instructions + // executed. + // Hack for now: it really shouldn't happen until after the + // commit is deemed to be successful, but this count is needed + // for syscalls. + thread[tid]->funcExeInst++; + + // Try to commit the head instruction. + bool commit_success = commitHead(head_inst, num_committed); + + if (commit_success) { + ++num_committed; + + changedROBNumEntries[tid] = true; + + // Set the doneSeqNum to the youngest committed instruction. + toIEW->commitInfo[tid].doneSeqNum = head_inst->seqNum; + + ++commitCommittedInsts; + + // To match the old model, don't count nops and instruction + // prefetches towards the total commit count. + if (!head_inst->isNop() && !head_inst->isInstPrefetch()) { + cpu->instDone(tid); + } + + PC[tid] = nextPC[tid]; +#if ISA_HAS_DELAY_SLOT + nextPC[tid] = nextNPC[tid]; + nextNPC[tid] = nextNPC[tid] + sizeof(TheISA::MachInst); +#else + nextPC[tid] = nextPC[tid] + sizeof(TheISA::MachInst); +#endif + +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + // Debug statement. Checks to make sure we're not + // currently updating state while handling PC events. + if (count == 0) + assert(!thread[tid]->inSyscall && + !thread[tid]->trapPending); + oldpc = PC[tid]; + cpu->system->pcEventQueue.service( + thread[tid]->getTC()); + count++; + } while (oldpc != PC[tid]); + if (count > 1) { + DPRINTF(Commit, "PC skip function event, stopping commit\n"); + break; + } +#endif + } else { + DPRINTF(Commit, "Unable to commit head instruction PC:%#x " + "[tid:%i] [sn:%i].\n", + head_inst->readPC(), tid ,head_inst->seqNum); + break; + } + } + } + + DPRINTF(CommitRate, "%i\n", num_committed); + numCommittedDist.sample(num_committed); + + if (num_committed == commitWidth) { + commitEligibleSamples++; + } +} + +template <class Impl> +bool +DefaultCommit<Impl>::commitHead(DynInstPtr &head_inst, unsigned inst_num) +{ + assert(head_inst); + + int tid = head_inst->threadNumber; + + // If the instruction is not executed yet, then it will need extra + // handling. Signal backwards that it should be executed. + if (!head_inst->isExecuted()) { + // Keep this number correct. We have not yet actually executed + // and committed this instruction. + thread[tid]->funcExeInst--; + + head_inst->setAtCommit(); + + if (head_inst->isNonSpeculative() || + head_inst->isStoreConditional() || + head_inst->isMemBarrier() || + head_inst->isWriteBarrier()) { + + DPRINTF(Commit, "Encountered a barrier or non-speculative " + "instruction [sn:%lli] at the head of the ROB, PC %#x.\n", + head_inst->seqNum, head_inst->readPC()); + +#if !FULL_SYSTEM + // Hack to make sure syscalls/memory barriers/quiesces + // aren't executed until all stores write back their data. + // This direct communication shouldn't be used for + // anything other than this. + if (inst_num > 0 || iewStage->hasStoresToWB()) +#else + if ((head_inst->isMemBarrier() || head_inst->isWriteBarrier() || + head_inst->isQuiesce()) && + iewStage->hasStoresToWB()) +#endif + { + DPRINTF(Commit, "Waiting for all stores to writeback.\n"); + return false; + } + + toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum; + + // Change the instruction so it won't try to commit again until + // it is executed. + head_inst->clearCanCommit(); + + ++commitNonSpecStalls; + + return false; + } else if (head_inst->isLoad()) { + DPRINTF(Commit, "[sn:%lli]: Uncached load, PC %#x.\n", + head_inst->seqNum, head_inst->readPC()); + + // Send back the non-speculative instruction's sequence + // number. Tell the lsq to re-execute the load. + toIEW->commitInfo[tid].nonSpecSeqNum = head_inst->seqNum; + toIEW->commitInfo[tid].uncached = true; + toIEW->commitInfo[tid].uncachedLoad = head_inst; + + head_inst->clearCanCommit(); + + return false; + } else { + panic("Trying to commit un-executed instruction " + "of unknown type!\n"); + } + } + + if (head_inst->isThreadSync()) { + // Not handled for now. + panic("Thread sync instructions are not handled yet.\n"); + } + + // Stores mark themselves as completed. + if (!head_inst->isStore()) { + head_inst->setCompleted(); + } + +#if USE_CHECKER + // Use checker prior to updating anything due to traps or PC + // based events. + if (cpu->checker) { + cpu->checker->verify(head_inst); + } +#endif + + // Check if the instruction caused a fault. If so, trap. + Fault inst_fault = head_inst->getFault(); + + // DTB will sometimes need the machine instruction for when + // faults happen. So we will set it here, prior to the DTB + // possibly needing it for its fault. + thread[tid]->setInst( + static_cast<TheISA::MachInst>(head_inst->staticInst->machInst)); + + if (inst_fault != NoFault) { + head_inst->setCompleted(); + DPRINTF(Commit, "Inst [sn:%lli] PC %#x has a fault\n", + head_inst->seqNum, head_inst->readPC()); + + if (iewStage->hasStoresToWB() || inst_num > 0) { + DPRINTF(Commit, "Stores outstanding, fault must wait.\n"); + return false; + } + +#if USE_CHECKER + if (cpu->checker && head_inst->isStore()) { + cpu->checker->verify(head_inst); + } +#endif + + assert(!thread[tid]->inSyscall); + + // Mark that we're in state update mode so that the trap's + // execution doesn't generate extra squashes. + thread[tid]->inSyscall = true; + + // Execute the trap. Although it's slightly unrealistic in + // terms of timing (as it doesn't wait for the full timing of + // the trap event to complete before updating state), it's + // needed to update the state as soon as possible. This + // prevents external agents from changing any specific state + // that the trap need. + cpu->trap(inst_fault, tid); + + // Exit state update mode to avoid accidental updating. + thread[tid]->inSyscall = false; + + commitStatus[tid] = TrapPending; + + // Generate trap squash event. + generateTrapEvent(tid); +// warn("%lli fault (%d) handled @ PC %08p", curTick, inst_fault->name(), head_inst->readPC()); + return false; + } + + updateComInstStats(head_inst); + +#if FULL_SYSTEM + if (thread[tid]->profile) { +// bool usermode = +// (cpu->readMiscReg(AlphaISA::IPR_DTB_CM, tid) & 0x18) != 0; +// thread[tid]->profilePC = usermode ? 1 : head_inst->readPC(); + thread[tid]->profilePC = head_inst->readPC(); + ProfileNode *node = thread[tid]->profile->consume(thread[tid]->getXCProxy(), + head_inst->staticInst); + + if (node) + thread[tid]->profileNode = node; + } +#endif + + if (head_inst->traceData) { + head_inst->traceData->setFetchSeq(head_inst->seqNum); + head_inst->traceData->setCPSeq(thread[tid]->numInst); + head_inst->traceData->finalize(); + head_inst->traceData = NULL; + } + + // Update the commit rename map + for (int i = 0; i < head_inst->numDestRegs(); i++) { + renameMap[tid]->setEntry(head_inst->destRegIdx(i), + head_inst->renamedDestRegIdx(i)); + } + + if (head_inst->isCopy()) + panic("Should not commit any copy instructions!"); + + // Finally clear the head ROB entry. + rob->retireHead(tid); + + // Return true to indicate that we have committed an instruction. + return true; +} + +template <class Impl> +void +DefaultCommit<Impl>::getInsts() +{ + DPRINTF(Commit, "Getting instructions from Rename stage.\n"); + +#if ISA_HAS_DELAY_SLOT + // Read any renamed instructions and place them into the ROB. + int insts_to_process = std::min((int)renameWidth, + (int)(fromRename->size + skidBuffer.size())); + int rename_idx = 0; + + DPRINTF(Commit, "%i insts available to process. Rename Insts:%i " + "SkidBuffer Insts:%i\n", insts_to_process, fromRename->size, + skidBuffer.size()); +#else + // Read any renamed instructions and place them into the ROB. + int insts_to_process = std::min((int)renameWidth, fromRename->size); +#endif + + + for (int inst_num = 0; inst_num < insts_to_process; ++inst_num) { + DynInstPtr inst; + +#if ISA_HAS_DELAY_SLOT + // Get insts from skidBuffer or from Rename + if (skidBuffer.size() > 0) { + DPRINTF(Commit, "Grabbing skidbuffer inst.\n"); + inst = skidBuffer.front(); + skidBuffer.pop(); + } else { + DPRINTF(Commit, "Grabbing rename inst.\n"); + inst = fromRename->insts[rename_idx++]; + } +#else + inst = fromRename->insts[inst_num]; +#endif + int tid = inst->threadNumber; + + if (!inst->isSquashed() && + commitStatus[tid] != ROBSquashing) { + changedROBNumEntries[tid] = true; + + DPRINTF(Commit, "Inserting PC %#x [sn:%i] [tid:%i] into ROB.\n", + inst->readPC(), inst->seqNum, tid); + + rob->insertInst(inst); + + assert(rob->getThreadEntries(tid) <= rob->getMaxEntries(tid)); + + youngestSeqNum[tid] = inst->seqNum; + } else { + DPRINTF(Commit, "Instruction PC %#x [sn:%i] [tid:%i] was " + "squashed, skipping.\n", + inst->readPC(), inst->seqNum, tid); + } + } + +#if ISA_HAS_DELAY_SLOT + if (rename_idx < fromRename->size) { + DPRINTF(Commit,"Placing Rename Insts into skidBuffer.\n"); + + for (; + rename_idx < fromRename->size; + rename_idx++) { + DynInstPtr inst = fromRename->insts[rename_idx]; + int tid = inst->threadNumber; + + if (!inst->isSquashed()) { + DPRINTF(Commit, "Inserting PC %#x [sn:%i] [tid:%i] into ", + "skidBuffer.\n", inst->readPC(), inst->seqNum, tid); + skidBuffer.push(inst); + } else { + DPRINTF(Commit, "Instruction PC %#x [sn:%i] [tid:%i] was " + "squashed, skipping.\n", + inst->readPC(), inst->seqNum, tid); + } + } + } +#endif + +} + +template <class Impl> +void +DefaultCommit<Impl>::skidInsert() +{ + DPRINTF(Commit, "Attempting to any instructions from rename into " + "skidBuffer.\n"); + + for (int inst_num = 0; inst_num < fromRename->size; ++inst_num) { + DynInstPtr inst = fromRename->insts[inst_num]; + int tid = inst->threadNumber; + + if (!inst->isSquashed()) { + DPRINTF(Commit, "Inserting PC %#x [sn:%i] [tid:%i] into ", + "skidBuffer.\n", inst->readPC(), inst->seqNum, tid); + skidBuffer.push(inst); + } else { + DPRINTF(Commit, "Instruction PC %#x [sn:%i] [tid:%i] was " + "squashed, skipping.\n", + inst->readPC(), inst->seqNum, tid); + } + } +} + +template <class Impl> +void +DefaultCommit<Impl>::markCompletedInsts() +{ + // Grab completed insts out of the IEW instruction queue, and mark + // instructions completed within the ROB. + for (int inst_num = 0; + inst_num < fromIEW->size && fromIEW->insts[inst_num]; + ++inst_num) + { + if (!fromIEW->insts[inst_num]->isSquashed()) { + DPRINTF(Commit, "[tid:%i]: Marking PC %#x, [sn:%lli] ready " + "within ROB.\n", + fromIEW->insts[inst_num]->threadNumber, + fromIEW->insts[inst_num]->readPC(), + fromIEW->insts[inst_num]->seqNum); + + // Mark the instruction as ready to commit. + fromIEW->insts[inst_num]->setCanCommit(); + } + } +} + +template <class Impl> +bool +DefaultCommit<Impl>::robDoneSquashing() +{ + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (!rob->isDoneSquashing(tid)) + return false; + } + + return true; +} + +template <class Impl> +void +DefaultCommit<Impl>::updateComInstStats(DynInstPtr &inst) +{ + unsigned thread = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) { + statComSwp[thread]++; + } else { + statComInst[thread]++; + } +#else + statComInst[thread]++; +#endif + + // + // Control Instructions + // + if (inst->isControl()) + statComBranches[thread]++; + + // + // Memory references + // + if (inst->isMemRef()) { + statComRefs[thread]++; + + if (inst->isLoad()) { + statComLoads[thread]++; + } + } + + if (inst->isMemBarrier()) { + statComMembars[thread]++; + } +} + +//////////////////////////////////////// +// // +// SMT COMMIT POLICY MAINTAINED HERE // +// // +//////////////////////////////////////// +template <class Impl> +int +DefaultCommit<Impl>::getCommittingThread() +{ + if (numThreads > 1) { + switch (commitPolicy) { + + case Aggressive: + //If Policy is Aggressive, commit will call + //this function multiple times per + //cycle + return oldestReady(); + + case RoundRobin: + return roundRobin(); + + case OldestReady: + return oldestReady(); + + default: + return -1; + } + } else { + int tid = (*activeThreads).front(); + + if (commitStatus[tid] == Running || + commitStatus[tid] == Idle || + commitStatus[tid] == FetchTrapPending) { + return tid; + } else { + return -1; + } + } +} + +template<class Impl> +int +DefaultCommit<Impl>::roundRobin() +{ + std::list<unsigned>::iterator pri_iter = priority_list.begin(); + std::list<unsigned>::iterator end = priority_list.end(); + + while (pri_iter != end) { + unsigned tid = *pri_iter; + + if (commitStatus[tid] == Running || + commitStatus[tid] == Idle || + commitStatus[tid] == FetchTrapPending) { + + if (rob->isHeadReady(tid)) { + priority_list.erase(pri_iter); + priority_list.push_back(tid); + + return tid; + } + } + + pri_iter++; + } + + return -1; +} + +template<class Impl> +int +DefaultCommit<Impl>::oldestReady() +{ + unsigned oldest = 0; + bool first = true; + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (!rob->isEmpty(tid) && + (commitStatus[tid] == Running || + commitStatus[tid] == Idle || + commitStatus[tid] == FetchTrapPending)) { + + if (rob->isHeadReady(tid)) { + + DynInstPtr head_inst = rob->readHeadInst(tid); + + if (first) { + oldest = tid; + first = false; + } else if (head_inst->seqNum < oldest) { + oldest = tid; + } + } + } + } + + if (!first) { + return oldest; + } else { + return -1; + } +} diff --git a/src/cpu/o3/cpu.cc b/src/cpu/o3/cpu.cc new file mode 100644 index 000000000..4279df6f7 --- /dev/null +++ b/src/cpu/o3/cpu.cc @@ -0,0 +1,1437 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "config/full_system.hh" +#include "config/use_checker.hh" + +#if FULL_SYSTEM +#include "cpu/quiesce_event.hh" +#include "sim/system.hh" +#else +#include "sim/process.hh" +#endif + +#include "cpu/activity.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/cpu.hh" + +#include "sim/root.hh" +#include "sim/stat_control.hh" + +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + +using namespace std; +using namespace TheISA; + +BaseO3CPU::BaseO3CPU(Params *params) + : BaseCPU(params), cpu_id(0) +{ +} + +void +BaseO3CPU::regStats() +{ + BaseCPU::regStats(); +} + +template <class Impl> +FullO3CPU<Impl>::TickEvent::TickEvent(FullO3CPU<Impl> *c) + : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c) +{ +} + +template <class Impl> +void +FullO3CPU<Impl>::TickEvent::process() +{ + cpu->tick(); +} + +template <class Impl> +const char * +FullO3CPU<Impl>::TickEvent::description() +{ + return "FullO3CPU tick event"; +} + +template <class Impl> +FullO3CPU<Impl>::ActivateThreadEvent::ActivateThreadEvent() + : Event(&mainEventQueue, CPU_Tick_Pri) +{ +} + +template <class Impl> +void +FullO3CPU<Impl>::ActivateThreadEvent::init(int thread_num, + FullO3CPU<Impl> *thread_cpu) +{ + tid = thread_num; + cpu = thread_cpu; +} + +template <class Impl> +void +FullO3CPU<Impl>::ActivateThreadEvent::process() +{ + cpu->activateThread(tid); +} + +template <class Impl> +const char * +FullO3CPU<Impl>::ActivateThreadEvent::description() +{ + return "FullO3CPU \"Activate Thread\" event"; +} + +template <class Impl> +FullO3CPU<Impl>::DeallocateContextEvent::DeallocateContextEvent() + : Event(&mainEventQueue, CPU_Tick_Pri) +{ +} + +template <class Impl> +void +FullO3CPU<Impl>::DeallocateContextEvent::init(int thread_num, + FullO3CPU<Impl> *thread_cpu) +{ + tid = thread_num; + cpu = thread_cpu; +} + +template <class Impl> +void +FullO3CPU<Impl>::DeallocateContextEvent::process() +{ + cpu->deactivateThread(tid); + cpu->removeThread(tid); +} + +template <class Impl> +const char * +FullO3CPU<Impl>::DeallocateContextEvent::description() +{ + return "FullO3CPU \"Deallocate Context\" event"; +} + +template <class Impl> +FullO3CPU<Impl>::FullO3CPU(Params *params) + : BaseO3CPU(params), + tickEvent(this), + removeInstsThisCycle(false), + fetch(params), + decode(params), + rename(params), + iew(params), + commit(params), + + regFile(params->numPhysIntRegs, params->numPhysFloatRegs), + + freeList(params->numberOfThreads, + TheISA::NumIntRegs, params->numPhysIntRegs, + TheISA::NumFloatRegs, params->numPhysFloatRegs), + + rob(params->numROBEntries, params->squashWidth, + params->smtROBPolicy, params->smtROBThreshold, + params->numberOfThreads), + + scoreboard(params->numberOfThreads, + TheISA::NumIntRegs, params->numPhysIntRegs, + TheISA::NumFloatRegs, params->numPhysFloatRegs, + TheISA::NumMiscRegs * number_of_threads, + TheISA::ZeroReg), + + timeBuffer(params->backComSize, params->forwardComSize), + fetchQueue(params->backComSize, params->forwardComSize), + decodeQueue(params->backComSize, params->forwardComSize), + renameQueue(params->backComSize, params->forwardComSize), + iewQueue(params->backComSize, params->forwardComSize), + activityRec(NumStages, + params->backComSize + params->forwardComSize, + params->activity), + + globalSeqNum(1), +#if FULL_SYSTEM + system(params->system), + physmem(system->physmem), +#endif // FULL_SYSTEM + mem(params->mem), + drainCount(0), + deferRegistration(params->deferRegistration), + numThreads(number_of_threads) +{ + _status = Idle; + + checker = NULL; + + if (params->checker) { +#if USE_CHECKER + BaseCPU *temp_checker = params->checker; + checker = dynamic_cast<Checker<DynInstPtr> *>(temp_checker); + checker->setMemory(mem); +#if FULL_SYSTEM + checker->setSystem(params->system); +#endif +#else + panic("Checker enabled but not compiled in!"); +#endif // USE_CHECKER + } + +#if !FULL_SYSTEM + thread.resize(number_of_threads); + tids.resize(number_of_threads); +#endif + + // The stages also need their CPU pointer setup. However this + // must be done at the upper level CPU because they have pointers + // to the upper level CPU, and not this FullO3CPU. + + // Set up Pointers to the activeThreads list for each stage + fetch.setActiveThreads(&activeThreads); + decode.setActiveThreads(&activeThreads); + rename.setActiveThreads(&activeThreads); + iew.setActiveThreads(&activeThreads); + commit.setActiveThreads(&activeThreads); + + // Give each of the stages the time buffer they will use. + fetch.setTimeBuffer(&timeBuffer); + decode.setTimeBuffer(&timeBuffer); + rename.setTimeBuffer(&timeBuffer); + iew.setTimeBuffer(&timeBuffer); + commit.setTimeBuffer(&timeBuffer); + + // Also setup each of the stages' queues. + fetch.setFetchQueue(&fetchQueue); + decode.setFetchQueue(&fetchQueue); + commit.setFetchQueue(&fetchQueue); + decode.setDecodeQueue(&decodeQueue); + rename.setDecodeQueue(&decodeQueue); + rename.setRenameQueue(&renameQueue); + iew.setRenameQueue(&renameQueue); + iew.setIEWQueue(&iewQueue); + commit.setIEWQueue(&iewQueue); + commit.setRenameQueue(&renameQueue); + + commit.setIEWStage(&iew); + rename.setIEWStage(&iew); + rename.setCommitStage(&commit); + +#if !FULL_SYSTEM + int active_threads = params->workload.size(); + + if (active_threads > Impl::MaxThreads) { + panic("Workload Size too large. Increase the 'MaxThreads'" + "constant in your O3CPU impl. file (e.g. o3/alpha/impl.hh) or " + "edit your workload size."); + } +#else + int active_threads = 1; +#endif + + //Make Sure That this a Valid Architeture + assert(params->numPhysIntRegs >= numThreads * TheISA::NumIntRegs); + assert(params->numPhysFloatRegs >= numThreads * TheISA::NumFloatRegs); + + rename.setScoreboard(&scoreboard); + iew.setScoreboard(&scoreboard); + + // Setup the rename map for whichever stages need it. + PhysRegIndex lreg_idx = 0; + PhysRegIndex freg_idx = params->numPhysIntRegs; //Index to 1 after int regs + + for (int tid=0; tid < numThreads; tid++) { + bool bindRegs = (tid <= active_threads - 1); + + commitRenameMap[tid].init(TheISA::NumIntRegs, + params->numPhysIntRegs, + lreg_idx, //Index for Logical. Regs + + TheISA::NumFloatRegs, + params->numPhysFloatRegs, + freg_idx, //Index for Float Regs + + TheISA::NumMiscRegs, + + TheISA::ZeroReg, + TheISA::ZeroReg, + + tid, + false); + + renameMap[tid].init(TheISA::NumIntRegs, + params->numPhysIntRegs, + lreg_idx, //Index for Logical. Regs + + TheISA::NumFloatRegs, + params->numPhysFloatRegs, + freg_idx, //Index for Float Regs + + TheISA::NumMiscRegs, + + TheISA::ZeroReg, + TheISA::ZeroReg, + + tid, + bindRegs); + } + + rename.setRenameMap(renameMap); + commit.setRenameMap(commitRenameMap); + + // Give renameMap & rename stage access to the freeList; + for (int i=0; i < numThreads; i++) { + renameMap[i].setFreeList(&freeList); + } + rename.setFreeList(&freeList); + + // Setup the ROB for whichever stages need it. + commit.setROB(&rob); + + lastRunningCycle = curTick; + + lastActivatedCycle = -1; + + // Give renameMap & rename stage access to the freeList; + //for (int i=0; i < numThreads; i++) { + //globalSeqNum[i] = 1; + //} + + contextSwitch = false; +} + +template <class Impl> +FullO3CPU<Impl>::~FullO3CPU() +{ +} + +template <class Impl> +void +FullO3CPU<Impl>::fullCPURegStats() +{ + BaseO3CPU::regStats(); + + // Register any of the O3CPU's stats here. + timesIdled + .name(name() + ".timesIdled") + .desc("Number of times that the entire CPU went into an idle state and" + " unscheduled itself") + .prereq(timesIdled); + + idleCycles + .name(name() + ".idleCycles") + .desc("Total number of cycles that the CPU has spent unscheduled due " + "to idling") + .prereq(idleCycles); + + // Number of Instructions simulated + // -------------------------------- + // Should probably be in Base CPU but need templated + // MaxThreads so put in here instead + committedInsts + .init(numThreads) + .name(name() + ".committedInsts") + .desc("Number of Instructions Simulated"); + + totalCommittedInsts + .name(name() + ".committedInsts_total") + .desc("Number of Instructions Simulated"); + + cpi + .name(name() + ".cpi") + .desc("CPI: Cycles Per Instruction") + .precision(6); + cpi = simTicks / committedInsts; + + totalCpi + .name(name() + ".cpi_total") + .desc("CPI: Total CPI of All Threads") + .precision(6); + totalCpi = simTicks / totalCommittedInsts; + + ipc + .name(name() + ".ipc") + .desc("IPC: Instructions Per Cycle") + .precision(6); + ipc = committedInsts / simTicks; + + totalIpc + .name(name() + ".ipc_total") + .desc("IPC: Total IPC of All Threads") + .precision(6); + totalIpc = totalCommittedInsts / simTicks; + +} + +template <class Impl> +Port * +FullO3CPU<Impl>::getPort(const std::string &if_name, int idx) +{ + if (if_name == "dcache_port") + return iew.getDcachePort(); + else if (if_name == "icache_port") + return fetch.getIcachePort(); + else + panic("No Such Port\n"); +} + +template <class Impl> +void +FullO3CPU<Impl>::tick() +{ + DPRINTF(O3CPU, "\n\nFullO3CPU: Ticking main, FullO3CPU.\n"); + + ++numCycles; + +// activity = false; + + //Tick each of the stages + fetch.tick(); + + decode.tick(); + + rename.tick(); + + iew.tick(); + + commit.tick(); + +#if !FULL_SYSTEM + doContextSwitch(); +#endif + + // Now advance the time buffers + timeBuffer.advance(); + + fetchQueue.advance(); + decodeQueue.advance(); + renameQueue.advance(); + iewQueue.advance(); + + activityRec.advance(); + + if (removeInstsThisCycle) { + cleanUpRemovedInsts(); + } + + if (!tickEvent.scheduled()) { + if (_status == SwitchedOut || + getState() == SimObject::Drained) { + // increment stat + lastRunningCycle = curTick; + } else if (!activityRec.active()) { + lastRunningCycle = curTick; + timesIdled++; + } else { + tickEvent.schedule(curTick + cycles(1)); + } + } + +#if !FULL_SYSTEM + updateThreadPriority(); +#endif + +} + +template <class Impl> +void +FullO3CPU<Impl>::init() +{ + if (!deferRegistration) { + registerThreadContexts(); + } + + // Set inSyscall so that the CPU doesn't squash when initially + // setting up registers. + for (int i = 0; i < number_of_threads; ++i) + thread[i]->inSyscall = true; + + for (int tid=0; tid < number_of_threads; tid++) { +#if FULL_SYSTEM + ThreadContext *src_tc = threadContexts[tid]; +#else + ThreadContext *src_tc = thread[tid]->getTC(); +#endif + // Threads start in the Suspended State + if (src_tc->status() != ThreadContext::Suspended) { + continue; + } + +#if FULL_SYSTEM + TheISA::initCPU(src_tc, src_tc->readCpuId()); +#endif + } + + // Clear inSyscall. + for (int i = 0; i < number_of_threads; ++i) + thread[i]->inSyscall = false; + + // Initialize stages. + fetch.initStage(); + iew.initStage(); + rename.initStage(); + commit.initStage(); + + commit.setThreads(thread); +} + +template <class Impl> +void +FullO3CPU<Impl>::activateThread(unsigned tid) +{ + list<unsigned>::iterator isActive = find( + activeThreads.begin(), activeThreads.end(), tid); + + if (isActive == activeThreads.end()) { + DPRINTF(O3CPU, "[tid:%i]: Adding to active threads list\n", + tid); + + activeThreads.push_back(tid); + } +} + +template <class Impl> +void +FullO3CPU<Impl>::deactivateThread(unsigned tid) +{ + //Remove From Active List, if Active + list<unsigned>::iterator thread_it = + find(activeThreads.begin(), activeThreads.end(), tid); + + if (thread_it != activeThreads.end()) { + DPRINTF(O3CPU,"[tid:%i]: Removing from active threads list\n", + tid); + activeThreads.erase(thread_it); + } +} + +template <class Impl> +void +FullO3CPU<Impl>::activateContext(int tid, int delay) +{ + // Needs to set each stage to running as well. + if (delay){ + DPRINTF(O3CPU, "[tid:%i]: Scheduling thread context to activate " + "on cycle %d\n", tid, curTick + cycles(delay)); + scheduleActivateThreadEvent(tid, delay); + } else { + activateThread(tid); + } + + if(lastActivatedCycle < curTick) { + scheduleTickEvent(delay); + + // Be sure to signal that there's some activity so the CPU doesn't + // deschedule itself. + activityRec.activity(); + fetch.wakeFromQuiesce(); + + lastActivatedCycle = curTick; + + _status = Running; + } +} + +template <class Impl> +void +FullO3CPU<Impl>::deallocateContext(int tid, int delay) +{ + // Schedule removal of thread data from CPU + if (delay){ + DPRINTF(O3CPU, "[tid:%i]: Scheduling thread context to deallocate " + "on cycle %d\n", tid, curTick + cycles(delay)); + scheduleDeallocateContextEvent(tid, delay); + } else { + deactivateThread(tid); + removeThread(tid); + } +} + +template <class Impl> +void +FullO3CPU<Impl>::suspendContext(int tid) +{ + DPRINTF(O3CPU,"[tid: %i]: Suspending Thread Context.\n", tid); + deactivateThread(tid); + if (activeThreads.size() == 0) + unscheduleTickEvent(); + _status = Idle; +} + +template <class Impl> +void +FullO3CPU<Impl>::haltContext(int tid) +{ + //For now, this is the same as deallocate + DPRINTF(O3CPU,"[tid:%i]: Halt Context called. Deallocating", tid); + deallocateContext(tid, 1); +} + +template <class Impl> +void +FullO3CPU<Impl>::insertThread(unsigned tid) +{ + DPRINTF(O3CPU,"[tid:%i] Initializing thread into CPU"); + // Will change now that the PC and thread state is internal to the CPU + // and not in the ThreadContext. +#if FULL_SYSTEM + ThreadContext *src_tc = system->threadContexts[tid]; +#else + ThreadContext *src_tc = tcBase(tid); +#endif + + //Bind Int Regs to Rename Map + for (int ireg = 0; ireg < TheISA::NumIntRegs; ireg++) { + PhysRegIndex phys_reg = freeList.getIntReg(); + + renameMap[tid].setEntry(ireg,phys_reg); + scoreboard.setReg(phys_reg); + } + + //Bind Float Regs to Rename Map + for (int freg = 0; freg < TheISA::NumFloatRegs; freg++) { + PhysRegIndex phys_reg = freeList.getFloatReg(); + + renameMap[tid].setEntry(freg,phys_reg); + scoreboard.setReg(phys_reg); + } + + //Copy Thread Data Into RegFile + //this->copyFromTC(tid); + + //Set PC/NPC/NNPC + setPC(src_tc->readPC(), tid); + setNextPC(src_tc->readNextPC(), tid); +#if ISA_HAS_DELAY_SLOT + setNextNPC(src_tc->readNextNPC(), tid); +#endif + + src_tc->setStatus(ThreadContext::Active); + + activateContext(tid,1); + + //Reset ROB/IQ/LSQ Entries + commit.rob->resetEntries(); + iew.resetEntries(); +} + +template <class Impl> +void +FullO3CPU<Impl>::removeThread(unsigned tid) +{ + DPRINTF(O3CPU,"[tid:%i] Removing thread context from CPU.\n", tid); + + // Copy Thread Data From RegFile + // If thread is suspended, it might be re-allocated + //this->copyToTC(tid); + + // Unbind Int Regs from Rename Map + for (int ireg = 0; ireg < TheISA::NumIntRegs; ireg++) { + PhysRegIndex phys_reg = renameMap[tid].lookup(ireg); + + scoreboard.unsetReg(phys_reg); + freeList.addReg(phys_reg); + } + + // Unbind Float Regs from Rename Map + for (int freg = 0; freg < TheISA::NumFloatRegs; freg++) { + PhysRegIndex phys_reg = renameMap[tid].lookup(freg); + + scoreboard.unsetReg(phys_reg); + freeList.addReg(phys_reg); + } + + // Squash Throughout Pipeline + InstSeqNum squash_seq_num = commit.rob->readHeadInst(tid)->seqNum; + fetch.squash(0, squash_seq_num, true, tid); + decode.squash(tid); + rename.squash(squash_seq_num, tid); + iew.squash(tid); + commit.rob->squash(squash_seq_num, tid); + + assert(iew.ldstQueue.getCount(tid) == 0); + + // Reset ROB/IQ/LSQ Entries + if (activeThreads.size() >= 1) { + commit.rob->resetEntries(); + iew.resetEntries(); + } +} + + +template <class Impl> +void +FullO3CPU<Impl>::activateWhenReady(int tid) +{ + DPRINTF(O3CPU,"[tid:%i]: Checking if resources are available for incoming" + "(e.g. PhysRegs/ROB/IQ/LSQ) \n", + tid); + + bool ready = true; + + if (freeList.numFreeIntRegs() >= TheISA::NumIntRegs) { + DPRINTF(O3CPU,"[tid:%i] Suspending thread due to not enough " + "Phys. Int. Regs.\n", + tid); + ready = false; + } else if (freeList.numFreeFloatRegs() >= TheISA::NumFloatRegs) { + DPRINTF(O3CPU,"[tid:%i] Suspending thread due to not enough " + "Phys. Float. Regs.\n", + tid); + ready = false; + } else if (commit.rob->numFreeEntries() >= + commit.rob->entryAmount(activeThreads.size() + 1)) { + DPRINTF(O3CPU,"[tid:%i] Suspending thread due to not enough " + "ROB entries.\n", + tid); + ready = false; + } else if (iew.instQueue.numFreeEntries() >= + iew.instQueue.entryAmount(activeThreads.size() + 1)) { + DPRINTF(O3CPU,"[tid:%i] Suspending thread due to not enough " + "IQ entries.\n", + tid); + ready = false; + } else if (iew.ldstQueue.numFreeEntries() >= + iew.ldstQueue.entryAmount(activeThreads.size() + 1)) { + DPRINTF(O3CPU,"[tid:%i] Suspending thread due to not enough " + "LSQ entries.\n", + tid); + ready = false; + } + + if (ready) { + insertThread(tid); + + contextSwitch = false; + + cpuWaitList.remove(tid); + } else { + suspendContext(tid); + + //blocks fetch + contextSwitch = true; + + //@todo: dont always add to waitlist + //do waitlist + cpuWaitList.push_back(tid); + } +} + +template <class Impl> +void +FullO3CPU<Impl>::serialize(std::ostream &os) +{ + SimObject::State so_state = SimObject::getState(); + SERIALIZE_ENUM(so_state); + BaseCPU::serialize(os); + nameOut(os, csprintf("%s.tickEvent", name())); + tickEvent.serialize(os); + + // Use SimpleThread's ability to checkpoint to make it easier to + // write out the registers. Also make this static so it doesn't + // get instantiated multiple times (causes a panic in statistics). + static SimpleThread temp; + + for (int i = 0; i < thread.size(); i++) { + nameOut(os, csprintf("%s.xc.%i", name(), i)); + temp.copyTC(thread[i]->getTC()); + temp.serialize(os); + } +} + +template <class Impl> +void +FullO3CPU<Impl>::unserialize(Checkpoint *cp, const std::string §ion) +{ + SimObject::State so_state; + UNSERIALIZE_ENUM(so_state); + BaseCPU::unserialize(cp, section); + tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); + + // Use SimpleThread's ability to checkpoint to make it easier to + // read in the registers. Also make this static so it doesn't + // get instantiated multiple times (causes a panic in statistics). + static SimpleThread temp; + + for (int i = 0; i < thread.size(); i++) { + temp.copyTC(thread[i]->getTC()); + temp.unserialize(cp, csprintf("%s.xc.%i", section, i)); + thread[i]->getTC()->copyArchRegs(temp.getTC()); + } +} + +template <class Impl> +unsigned int +FullO3CPU<Impl>::drain(Event *drain_event) +{ + DPRINTF(O3CPU, "Switching out\n"); + BaseCPU::switchOut(_sampler); + drainCount = 0; + fetch.drain(); + decode.drain(); + rename.drain(); + iew.drain(); + commit.drain(); + + // Wake the CPU and record activity so everything can drain out if + // the CPU was not able to immediately drain. + if (getState() != SimObject::Drained) { + // A bit of a hack...set the drainEvent after all the drain() + // calls have been made, that way if all of the stages drain + // immediately, the signalDrained() function knows not to call + // process on the drain event. + drainEvent = drain_event; + + wakeCPU(); + activityRec.activity(); + + return 1; + } else { + return 0; + } +} + +template <class Impl> +void +FullO3CPU<Impl>::resume() +{ + assert(system->getMemoryMode() == System::Timing); + fetch.resume(); + decode.resume(); + rename.resume(); + iew.resume(); + commit.resume(); + + changeState(SimObject::Running); + + if (_status == SwitchedOut || _status == Idle) + return; + + if (!tickEvent.scheduled()) + tickEvent.schedule(curTick); + _status = Running; +} + +template <class Impl> +void +FullO3CPU<Impl>::signalDrained() +{ + if (++drainCount == NumStages) { + if (tickEvent.scheduled()) + tickEvent.squash(); + + changeState(SimObject::Drained); + + if (drainEvent) { + drainEvent->process(); + drainEvent = NULL; + } + } + assert(drainCount <= 5); +} + +template <class Impl> +void +FullO3CPU<Impl>::switchOut() +{ + fetch.switchOut(); + rename.switchOut(); + iew.switchOut(); + commit.switchOut(); + instList.clear(); + while (!removeList.empty()) { + removeList.pop(); + } + + _status = SwitchedOut; +#if USE_CHECKER + if (checker) + checker->switchOut(); +#endif +} + +template <class Impl> +void +FullO3CPU<Impl>::takeOverFrom(BaseCPU *oldCPU) +{ + // Flush out any old data from the time buffers. + for (int i = 0; i < timeBuffer.getSize(); ++i) { + timeBuffer.advance(); + fetchQueue.advance(); + decodeQueue.advance(); + renameQueue.advance(); + iewQueue.advance(); + } + + activityRec.reset(); + + BaseCPU::takeOverFrom(oldCPU); + + fetch.takeOverFrom(); + decode.takeOverFrom(); + rename.takeOverFrom(); + iew.takeOverFrom(); + commit.takeOverFrom(); + + assert(!tickEvent.scheduled()); + + // @todo: Figure out how to properly select the tid to put onto + // the active threads list. + int tid = 0; + + list<unsigned>::iterator isActive = find( + activeThreads.begin(), activeThreads.end(), tid); + + if (isActive == activeThreads.end()) { + //May Need to Re-code this if the delay variable is the delay + //needed for thread to activate + DPRINTF(O3CPU, "Adding Thread %i to active threads list\n", + tid); + + activeThreads.push_back(tid); + } + + // Set all statuses to active, schedule the CPU's tick event. + // @todo: Fix up statuses so this is handled properly + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + if (tc->status() == ThreadContext::Active && _status != Running) { + _status = Running; + tickEvent.schedule(curTick); + } + } + if (!tickEvent.scheduled()) + tickEvent.schedule(curTick); +} + +template <class Impl> +void +FullO3CPU<Impl>::serialize(std::ostream &os) +{ + BaseCPU::serialize(os); + nameOut(os, csprintf("%s.tickEvent", name())); + tickEvent.serialize(os); + + // Use SimpleThread's ability to checkpoint to make it easier to + // write out the registers. Also make this static so it doesn't + // get instantiated multiple times (causes a panic in statistics). + static CPUExecContext temp; + + for (int i = 0; i < thread.size(); i++) { + nameOut(os, csprintf("%s.xc.%i", name(), i)); + temp.copyXC(thread[i]->getXCProxy()); + temp.serialize(os); + } +} + +template <class Impl> +void +FullO3CPU<Impl>::unserialize(Checkpoint *cp, const std::string §ion) +{ + BaseCPU::unserialize(cp, section); + tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); + + // Use SimpleThread's ability to checkpoint to make it easier to + // read in the registers. Also make this static so it doesn't + // get instantiated multiple times (causes a panic in statistics). + static CPUExecContext temp; + + for (int i = 0; i < thread.size(); i++) { + temp.copyXC(thread[i]->getXCProxy()); + temp.unserialize(cp, csprintf("%s.xc.%i", section, i)); + thread[i]->getXCProxy()->copyArchRegs(temp.getProxy()); + } +} + +template <class Impl> +uint64_t +FullO3CPU<Impl>::readIntReg(int reg_idx) +{ + return regFile.readIntReg(reg_idx); +} + +template <class Impl> +FloatReg +FullO3CPU<Impl>::readFloatReg(int reg_idx, int width) +{ + return regFile.readFloatReg(reg_idx, width); +} + +template <class Impl> +FloatReg +FullO3CPU<Impl>::readFloatReg(int reg_idx) +{ + return regFile.readFloatReg(reg_idx); +} + +template <class Impl> +FloatRegBits +FullO3CPU<Impl>::readFloatRegBits(int reg_idx, int width) +{ + return regFile.readFloatRegBits(reg_idx, width); +} + +template <class Impl> +FloatRegBits +FullO3CPU<Impl>::readFloatRegBits(int reg_idx) +{ + return regFile.readFloatRegBits(reg_idx); +} + +template <class Impl> +void +FullO3CPU<Impl>::setIntReg(int reg_idx, uint64_t val) +{ + regFile.setIntReg(reg_idx, val); +} + +template <class Impl> +void +FullO3CPU<Impl>::setFloatReg(int reg_idx, FloatReg val, int width) +{ + regFile.setFloatReg(reg_idx, val, width); +} + +template <class Impl> +void +FullO3CPU<Impl>::setFloatReg(int reg_idx, FloatReg val) +{ + regFile.setFloatReg(reg_idx, val); +} + +template <class Impl> +void +FullO3CPU<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val, int width) +{ + regFile.setFloatRegBits(reg_idx, val, width); +} + +template <class Impl> +void +FullO3CPU<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val) +{ + regFile.setFloatRegBits(reg_idx, val); +} + +template <class Impl> +uint64_t +FullO3CPU<Impl>::readArchIntReg(int reg_idx, unsigned tid) +{ + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + return regFile.readIntReg(phys_reg); +} + +template <class Impl> +float +FullO3CPU<Impl>::readArchFloatRegSingle(int reg_idx, unsigned tid) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx); + + return regFile.readFloatReg(phys_reg); +} + +template <class Impl> +double +FullO3CPU<Impl>::readArchFloatRegDouble(int reg_idx, unsigned tid) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx); + + return regFile.readFloatReg(phys_reg, 64); +} + +template <class Impl> +uint64_t +FullO3CPU<Impl>::readArchFloatRegInt(int reg_idx, unsigned tid) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx); + + return regFile.readFloatRegBits(phys_reg); +} + +template <class Impl> +void +FullO3CPU<Impl>::setArchIntReg(int reg_idx, uint64_t val, unsigned tid) +{ + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(reg_idx); + + regFile.setIntReg(phys_reg, val); +} + +template <class Impl> +void +FullO3CPU<Impl>::setArchFloatRegSingle(int reg_idx, float val, unsigned tid) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx); + + regFile.setFloatReg(phys_reg, val); +} + +template <class Impl> +void +FullO3CPU<Impl>::setArchFloatRegDouble(int reg_idx, double val, unsigned tid) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx); + + regFile.setFloatReg(phys_reg, val, 64); +} + +template <class Impl> +void +FullO3CPU<Impl>::setArchFloatRegInt(int reg_idx, uint64_t val, unsigned tid) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + PhysRegIndex phys_reg = commitRenameMap[tid].lookup(idx); + + regFile.setFloatRegBits(phys_reg, val); +} + +template <class Impl> +uint64_t +FullO3CPU<Impl>::readPC(unsigned tid) +{ + return commit.readPC(tid); +} + +template <class Impl> +void +FullO3CPU<Impl>::setPC(Addr new_PC,unsigned tid) +{ + commit.setPC(new_PC, tid); +} + +template <class Impl> +uint64_t +FullO3CPU<Impl>::readNextPC(unsigned tid) +{ + return commit.readNextPC(tid); +} + +template <class Impl> +void +FullO3CPU<Impl>::setNextPC(uint64_t val,unsigned tid) +{ + commit.setNextPC(val, tid); +} + +template <class Impl> +uint64_t +FullO3CPU<Impl>::readNextNPC(unsigned tid) +{ + return commit.readNextNPC(tid); +} + +template <class Impl> +void +FullO3CPU<Impl>::setNextNPC(uint64_t val,unsigned tid) +{ + commit.setNextNPC(val, tid); +} + +template <class Impl> +typename FullO3CPU<Impl>::ListIt +FullO3CPU<Impl>::addInst(DynInstPtr &inst) +{ + instList.push_back(inst); + + return --(instList.end()); +} + +template <class Impl> +void +FullO3CPU<Impl>::instDone(unsigned tid) +{ + // Keep an instruction count. + thread[tid]->numInst++; + thread[tid]->numInsts++; + committedInsts[tid]++; + totalCommittedInsts++; + + // Check for instruction-count-based events. + comInstEventQueue[tid]->serviceEvents(thread[tid]->numInst); +} + +template <class Impl> +void +FullO3CPU<Impl>::addToRemoveList(DynInstPtr &inst) +{ + removeInstsThisCycle = true; + + removeList.push(inst->getInstListIt()); +} + +template <class Impl> +void +FullO3CPU<Impl>::removeFrontInst(DynInstPtr &inst) +{ + DPRINTF(O3CPU, "Removing committed instruction [tid:%i] PC %#x " + "[sn:%lli]\n", + inst->threadNumber, inst->readPC(), inst->seqNum); + + removeInstsThisCycle = true; + + // Remove the front instruction. + removeList.push(inst->getInstListIt()); +} + +template <class Impl> +void +FullO3CPU<Impl>::removeInstsNotInROB(unsigned tid, + bool squash_delay_slot, + const InstSeqNum &delay_slot_seq_num) +{ + DPRINTF(O3CPU, "Thread %i: Deleting instructions from instruction" + " list.\n", tid); + + ListIt end_it; + + bool rob_empty = false; + + if (instList.empty()) { + return; + } else if (rob.isEmpty(/*tid*/)) { + DPRINTF(O3CPU, "ROB is empty, squashing all insts.\n"); + end_it = instList.begin(); + rob_empty = true; + } else { + end_it = (rob.readTailInst(tid))->getInstListIt(); + DPRINTF(O3CPU, "ROB is not empty, squashing insts not in ROB.\n"); + } + + removeInstsThisCycle = true; + + ListIt inst_it = instList.end(); + + inst_it--; + + // Walk through the instruction list, removing any instructions + // that were inserted after the given instruction iterator, end_it. + while (inst_it != end_it) { + assert(!instList.empty()); + +#if ISA_HAS_DELAY_SLOT + if(!squash_delay_slot && + delay_slot_seq_num >= (*inst_it)->seqNum) { + break; + } +#endif + squashInstIt(inst_it, tid); + + inst_it--; + } + + // If the ROB was empty, then we actually need to remove the first + // instruction as well. + if (rob_empty) { + squashInstIt(inst_it, tid); + } +} + +template <class Impl> +void +FullO3CPU<Impl>::removeInstsUntil(const InstSeqNum &seq_num, + unsigned tid) +{ + assert(!instList.empty()); + + removeInstsThisCycle = true; + + ListIt inst_iter = instList.end(); + + inst_iter--; + + DPRINTF(O3CPU, "Deleting instructions from instruction " + "list that are from [tid:%i] and above [sn:%lli] (end=%lli).\n", + tid, seq_num, (*inst_iter)->seqNum); + + while ((*inst_iter)->seqNum > seq_num) { + + bool break_loop = (inst_iter == instList.begin()); + + squashInstIt(inst_iter, tid); + + inst_iter--; + + if (break_loop) + break; + } +} + +template <class Impl> +inline void +FullO3CPU<Impl>::squashInstIt(const ListIt &instIt, const unsigned &tid) +{ + if ((*instIt)->threadNumber == tid) { + DPRINTF(O3CPU, "Squashing instruction, " + "[tid:%i] [sn:%lli] PC %#x\n", + (*instIt)->threadNumber, + (*instIt)->seqNum, + (*instIt)->readPC()); + + // Mark it as squashed. + (*instIt)->setSquashed(); + + // @todo: Formulate a consistent method for deleting + // instructions from the instruction list + // Remove the instruction from the list. + removeList.push(instIt); + } +} + +template <class Impl> +void +FullO3CPU<Impl>::cleanUpRemovedInsts() +{ + while (!removeList.empty()) { + DPRINTF(O3CPU, "Removing instruction, " + "[tid:%i] [sn:%lli] PC %#x\n", + (*removeList.front())->threadNumber, + (*removeList.front())->seqNum, + (*removeList.front())->readPC()); + + instList.erase(removeList.front()); + + removeList.pop(); + } + + removeInstsThisCycle = false; +} +/* +template <class Impl> +void +FullO3CPU<Impl>::removeAllInsts() +{ + instList.clear(); +} +*/ +template <class Impl> +void +FullO3CPU<Impl>::dumpInsts() +{ + int num = 0; + + ListIt inst_list_it = instList.begin(); + + cprintf("Dumping Instruction List\n"); + + while (inst_list_it != instList.end()) { + cprintf("Instruction:%i\nPC:%#x\n[tid:%i]\n[sn:%lli]\nIssued:%i\n" + "Squashed:%i\n\n", + num, (*inst_list_it)->readPC(), (*inst_list_it)->threadNumber, + (*inst_list_it)->seqNum, (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + inst_list_it++; + ++num; + } +} +/* +template <class Impl> +void +FullO3CPU<Impl>::wakeDependents(DynInstPtr &inst) +{ + iew.wakeDependents(inst); +} +*/ +template <class Impl> +void +FullO3CPU<Impl>::wakeCPU() +{ + if (activityRec.active() || tickEvent.scheduled()) { + DPRINTF(Activity, "CPU already running.\n"); + return; + } + + DPRINTF(Activity, "Waking up CPU\n"); + + idleCycles += (curTick - 1) - lastRunningCycle; + + tickEvent.schedule(curTick); +} + +template <class Impl> +int +FullO3CPU<Impl>::getFreeTid() +{ + for (int i=0; i < numThreads; i++) { + if (!tids[i]) { + tids[i] = true; + return i; + } + } + + return -1; +} + +template <class Impl> +void +FullO3CPU<Impl>::doContextSwitch() +{ + if (contextSwitch) { + + //ADD CODE TO DEACTIVE THREAD HERE (???) + + for (int tid=0; tid < cpuWaitList.size(); tid++) { + activateWhenReady(tid); + } + + if (cpuWaitList.size() == 0) + contextSwitch = true; + } +} + +template <class Impl> +void +FullO3CPU<Impl>::updateThreadPriority() +{ + if (activeThreads.size() > 1) + { + //DEFAULT TO ROUND ROBIN SCHEME + //e.g. Move highest priority to end of thread list + list<unsigned>::iterator list_begin = activeThreads.begin(); + list<unsigned>::iterator list_end = activeThreads.end(); + + unsigned high_thread = *list_begin; + + activeThreads.erase(list_begin); + + activeThreads.push_back(high_thread); + } +} + +// Forward declaration of FullO3CPU. +template class FullO3CPU<O3CPUImpl>; diff --git a/src/cpu/o3/cpu.hh b/src/cpu/o3/cpu.hh new file mode 100644 index 000000000..dcdcd1fe6 --- /dev/null +++ b/src/cpu/o3/cpu.hh @@ -0,0 +1,676 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#ifndef __CPU_O3_CPU_HH__ +#define __CPU_O3_CPU_HH__ + +#include <iostream> +#include <list> +#include <queue> +#include <set> +#include <vector> + +#include "arch/types.hh" +#include "base/statistics.hh" +#include "base/timebuf.hh" +#include "config/full_system.hh" +#include "cpu/activity.hh" +#include "cpu/base.hh" +#include "cpu/simple_thread.hh" +#include "cpu/o3/comm.hh" +#include "cpu/o3/cpu_policy.hh" +#include "cpu/o3/scoreboard.hh" +#include "cpu/o3/thread_state.hh" +//#include "cpu/o3/thread_context.hh" +#include "sim/process.hh" + +template <class> +class Checker; +class ThreadContext; +template <class> +class O3ThreadContext; + +class Checkpoint; +class MemObject; +class Process; + +class BaseO3CPU : public BaseCPU +{ + //Stuff that's pretty ISA independent will go here. + public: + typedef BaseCPU::Params Params; + + BaseO3CPU(Params *params); + + void regStats(); + + /** Sets this CPU's ID. */ + void setCpuId(int id) { cpu_id = id; } + + /** Reads this CPU's ID. */ + int readCpuId() { return cpu_id; } + + protected: + int cpu_id; +}; + +/** + * FullO3CPU class, has each of the stages (fetch through commit) + * within it, as well as all of the time buffers between stages. The + * tick() function for the CPU is defined here. + */ +template <class Impl> +class FullO3CPU : public BaseO3CPU +{ + public: + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + + // Typedefs from the Impl here. + typedef typename Impl::CPUPol CPUPolicy; + typedef typename Impl::Params Params; + typedef typename Impl::DynInstPtr DynInstPtr; + + typedef O3ThreadState<Impl> Thread; + + typedef typename std::list<DynInstPtr>::iterator ListIt; + + friend class O3ThreadContext<Impl>; + + public: + enum Status { + Running, + Idle, + Halted, + Blocked, + SwitchedOut + }; + + /** Overall CPU status. */ + Status _status; + + /** Per-thread status in CPU, used for SMT. */ + Status _threadStatus[Impl::MaxThreads]; + + private: + class TickEvent : public Event + { + private: + /** Pointer to the CPU. */ + FullO3CPU<Impl> *cpu; + + public: + /** Constructs a tick event. */ + TickEvent(FullO3CPU<Impl> *c); + + /** Processes a tick event, calling tick() on the CPU. */ + void process(); + /** Returns the description of the tick event. */ + const char *description(); + }; + + /** The tick event used for scheduling CPU ticks. */ + TickEvent tickEvent; + + /** Schedule tick event, regardless of its current state. */ + void scheduleTickEvent(int delay) + { + if (tickEvent.squashed()) + tickEvent.reschedule(curTick + cycles(delay)); + else if (!tickEvent.scheduled()) + tickEvent.schedule(curTick + cycles(delay)); + } + + /** Unschedule tick event, regardless of its current state. */ + void unscheduleTickEvent() + { + if (tickEvent.scheduled()) + tickEvent.squash(); + } + + class ActivateThreadEvent : public Event + { + private: + /** Number of Thread to Activate */ + int tid; + + /** Pointer to the CPU. */ + FullO3CPU<Impl> *cpu; + + public: + /** Constructs the event. */ + ActivateThreadEvent(); + + /** Initialize Event */ + void init(int thread_num, FullO3CPU<Impl> *thread_cpu); + + /** Processes the event, calling activateThread() on the CPU. */ + void process(); + + /** Returns the description of the event. */ + const char *description(); + }; + + /** Schedule thread to activate , regardless of its current state. */ + void scheduleActivateThreadEvent(int tid, int delay) + { + // Schedule thread to activate, regardless of its current state. + if (activateThreadEvent[tid].squashed()) + activateThreadEvent[tid].reschedule(curTick + cycles(delay)); + else if (!activateThreadEvent[tid].scheduled()) + activateThreadEvent[tid].schedule(curTick + cycles(delay)); + } + + /** Unschedule actiavte thread event, regardless of its current state. */ + void unscheduleActivateThreadEvent(int tid) + { + if (activateThreadEvent[tid].scheduled()) + activateThreadEvent[tid].squash(); + } + + /** The tick event used for scheduling CPU ticks. */ + ActivateThreadEvent activateThreadEvent[Impl::MaxThreads]; + + class DeallocateContextEvent : public Event + { + private: + /** Number of Thread to Activate */ + int tid; + + /** Pointer to the CPU. */ + FullO3CPU<Impl> *cpu; + + public: + /** Constructs the event. */ + DeallocateContextEvent(); + + /** Initialize Event */ + void init(int thread_num, FullO3CPU<Impl> *thread_cpu); + + /** Processes the event, calling activateThread() on the CPU. */ + void process(); + + /** Returns the description of the event. */ + const char *description(); + }; + + /** Schedule cpu to deallocate thread context.*/ + void scheduleDeallocateContextEvent(int tid, int delay) + { + // Schedule thread to activate, regardless of its current state. + if (deallocateContextEvent[tid].squashed()) + deallocateContextEvent[tid].reschedule(curTick + cycles(delay)); + else if (!deallocateContextEvent[tid].scheduled()) + deallocateContextEvent[tid].schedule(curTick + cycles(delay)); + } + + /** Unschedule thread deallocation in CPU */ + void unscheduleDeallocateContextEvent(int tid) + { + if (deallocateContextEvent[tid].scheduled()) + deallocateContextEvent[tid].squash(); + } + + /** The tick event used for scheduling CPU ticks. */ + DeallocateContextEvent deallocateContextEvent[Impl::MaxThreads]; + + public: + /** Constructs a CPU with the given parameters. */ + FullO3CPU(Params *params); + /** Destructor. */ + ~FullO3CPU(); + + /** Registers statistics. */ + void fullCPURegStats(); + + /** Returns a specific port. */ + Port *getPort(const std::string &if_name, int idx); + + /** Ticks CPU, calling tick() on each stage, and checking the overall + * activity to see if the CPU should deschedule itself. + */ + void tick(); + + /** Initialize the CPU */ + void init(); + + /** Returns the Number of Active Threads in the CPU */ + int numActiveThreads() + { return activeThreads.size(); } + + /** Add Thread to Active Threads List */ + void activateThread(unsigned tid); + + /** Remove Thread from Active Threads List */ + void deactivateThread(unsigned tid); + + /** Setup CPU to insert a thread's context */ + void insertThread(unsigned tid); + + /** Remove all of a thread's context from CPU */ + void removeThread(unsigned tid); + + /** Count the Total Instructions Committed in the CPU. */ + virtual Counter totalInstructions() const + { + Counter total(0); + + for (int i=0; i < thread.size(); i++) + total += thread[i]->numInst; + + return total; + } + + /** Add Thread to Active Threads List. */ + void activateContext(int tid, int delay); + + /** Remove Thread from Active Threads List */ + void suspendContext(int tid); + + /** Remove Thread from Active Threads List && + * Remove Thread Context from CPU. + */ + void deallocateContext(int tid, int delay = 1); + + /** Remove Thread from Active Threads List && + * Remove Thread Context from CPU. + */ + void haltContext(int tid); + + /** Activate a Thread When CPU Resources are Available. */ + void activateWhenReady(int tid); + + /** Add or Remove a Thread Context in the CPU. */ + void doContextSwitch(); + + /** Update The Order In Which We Process Threads. */ + void updateThreadPriority(); + + /** Serialize state. */ + virtual void serialize(std::ostream &os); + + /** Unserialize from a checkpoint. */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + public: + /** Executes a syscall on this cycle. + * --------------------------------------- + * Note: this is a virtual function. CPU-Specific + * functionality defined in derived classes + */ + virtual void syscall(int tid) { panic("Unimplemented!"); } + + /** Starts draining the CPU's pipeline of all instructions in + * order to stop all memory accesses. */ + virtual unsigned int drain(Event *drain_event); + + /** Resumes execution after a drain. */ + virtual void resume(); + + /** Signals to this CPU that a stage has completed switching out. */ + void signalDrained(); + + /** Switches out this CPU. */ + virtual void switchOut(); + + /** Takes over from another CPU. */ + virtual void takeOverFrom(BaseCPU *oldCPU); + + /** Get the current instruction sequence number, and increment it. */ + InstSeqNum getAndIncrementInstSeq() + { return globalSeqNum++; } + +#if FULL_SYSTEM + /** Check if this address is a valid instruction address. */ + bool validInstAddr(Addr addr) { return true; } + + /** Check if this address is a valid data address. */ + bool validDataAddr(Addr addr) { return true; } + + /** Get instruction asid. */ + int getInstAsid(unsigned tid) + { return regFile.miscRegs[tid].getInstAsid(); } + + /** Get data asid. */ + int getDataAsid(unsigned tid) + { return regFile.miscRegs[tid].getDataAsid(); } +#else + /** Get instruction asid. */ + int getInstAsid(unsigned tid) + { return thread[tid]->getInstAsid(); } + + /** Get data asid. */ + int getDataAsid(unsigned tid) + { return thread[tid]->getDataAsid(); } + +#endif + + /** Register accessors. Index refers to the physical register index. */ + uint64_t readIntReg(int reg_idx); + + FloatReg readFloatReg(int reg_idx); + + FloatReg readFloatReg(int reg_idx, int width); + + FloatRegBits readFloatRegBits(int reg_idx); + + FloatRegBits readFloatRegBits(int reg_idx, int width); + + void setIntReg(int reg_idx, uint64_t val); + + void setFloatReg(int reg_idx, FloatReg val); + + void setFloatReg(int reg_idx, FloatReg val, int width); + + void setFloatRegBits(int reg_idx, FloatRegBits val); + + void setFloatRegBits(int reg_idx, FloatRegBits val, int width); + + uint64_t readArchIntReg(int reg_idx, unsigned tid); + + float readArchFloatRegSingle(int reg_idx, unsigned tid); + + double readArchFloatRegDouble(int reg_idx, unsigned tid); + + uint64_t readArchFloatRegInt(int reg_idx, unsigned tid); + + /** Architectural register accessors. Looks up in the commit + * rename table to obtain the true physical index of the + * architected register first, then accesses that physical + * register. + */ + void setArchIntReg(int reg_idx, uint64_t val, unsigned tid); + + void setArchFloatRegSingle(int reg_idx, float val, unsigned tid); + + void setArchFloatRegDouble(int reg_idx, double val, unsigned tid); + + void setArchFloatRegInt(int reg_idx, uint64_t val, unsigned tid); + + /** Reads the commit PC of a specific thread. */ + uint64_t readPC(unsigned tid); + + /** Sets the commit PC of a specific thread. */ + void setPC(Addr new_PC, unsigned tid); + + /** Reads the next PC of a specific thread. */ + uint64_t readNextPC(unsigned tid); + + /** Sets the next PC of a specific thread. */ + void setNextPC(uint64_t val, unsigned tid); + + /** Reads the next NPC of a specific thread. */ + uint64_t readNextNPC(unsigned tid); + + /** Sets the next NPC of a specific thread. */ + void setNextNPC(uint64_t val, unsigned tid); + + /** Function to add instruction onto the head of the list of the + * instructions. Used when new instructions are fetched. + */ + ListIt addInst(DynInstPtr &inst); + + /** Function to tell the CPU that an instruction has completed. */ + void instDone(unsigned tid); + + /** Add Instructions to the CPU Remove List*/ + void addToRemoveList(DynInstPtr &inst); + + /** Remove an instruction from the front end of the list. There's + * no restriction on location of the instruction. + */ + void removeFrontInst(DynInstPtr &inst); + + /** Remove all instructions that are not currently in the ROB. + * There's also an option to not squash delay slot instructions.*/ + void removeInstsNotInROB(unsigned tid, bool squash_delay_slot, + const InstSeqNum &delay_slot_seq_num); + + /** Remove all instructions younger than the given sequence number. */ + void removeInstsUntil(const InstSeqNum &seq_num,unsigned tid); + + /** Removes the instruction pointed to by the iterator. */ + inline void squashInstIt(const ListIt &instIt, const unsigned &tid); + + /** Cleans up all instructions on the remove list. */ + void cleanUpRemovedInsts(); + + /** Debug function to print all instructions on the list. */ + void dumpInsts(); + + public: + /** List of all the instructions in flight. */ + std::list<DynInstPtr> instList; + + /** List of all the instructions that will be removed at the end of this + * cycle. + */ + std::queue<ListIt> removeList; + +#ifdef DEBUG + /** Debug structure to keep track of the sequence numbers still in + * flight. + */ + std::set<InstSeqNum> snList; +#endif + + /** Records if instructions need to be removed this cycle due to + * being retired or squashed. + */ + bool removeInstsThisCycle; + + protected: + /** The fetch stage. */ + typename CPUPolicy::Fetch fetch; + + /** The decode stage. */ + typename CPUPolicy::Decode decode; + + /** The dispatch stage. */ + typename CPUPolicy::Rename rename; + + /** The issue/execute/writeback stages. */ + typename CPUPolicy::IEW iew; + + /** The commit stage. */ + typename CPUPolicy::Commit commit; + + /** The register file. */ + typename CPUPolicy::RegFile regFile; + + /** The free list. */ + typename CPUPolicy::FreeList freeList; + + /** The rename map. */ + typename CPUPolicy::RenameMap renameMap[Impl::MaxThreads]; + + /** The commit rename map. */ + typename CPUPolicy::RenameMap commitRenameMap[Impl::MaxThreads]; + + /** The re-order buffer. */ + typename CPUPolicy::ROB rob; + + /** Active Threads List */ + std::list<unsigned> activeThreads; + + /** Integer Register Scoreboard */ + Scoreboard scoreboard; + + public: + /** Enum to give each stage a specific index, so when calling + * activateStage() or deactivateStage(), they can specify which stage + * is being activated/deactivated. + */ + enum StageIdx { + FetchIdx, + DecodeIdx, + RenameIdx, + IEWIdx, + CommitIdx, + NumStages }; + + /** Typedefs from the Impl to get the structs that each of the + * time buffers should use. + */ + typedef typename CPUPolicy::TimeStruct TimeStruct; + + typedef typename CPUPolicy::FetchStruct FetchStruct; + + typedef typename CPUPolicy::DecodeStruct DecodeStruct; + + typedef typename CPUPolicy::RenameStruct RenameStruct; + + typedef typename CPUPolicy::IEWStruct IEWStruct; + + /** The main time buffer to do backwards communication. */ + TimeBuffer<TimeStruct> timeBuffer; + + /** The fetch stage's instruction queue. */ + TimeBuffer<FetchStruct> fetchQueue; + + /** The decode stage's instruction queue. */ + TimeBuffer<DecodeStruct> decodeQueue; + + /** The rename stage's instruction queue. */ + TimeBuffer<RenameStruct> renameQueue; + + /** The IEW stage's instruction queue. */ + TimeBuffer<IEWStruct> iewQueue; + + private: + /** The activity recorder; used to tell if the CPU has any + * activity remaining or if it can go to idle and deschedule + * itself. + */ + ActivityRecorder activityRec; + + public: + /** Records that there was time buffer activity this cycle. */ + void activityThisCycle() { activityRec.activity(); } + + /** Changes a stage's status to active within the activity recorder. */ + void activateStage(const StageIdx idx) + { activityRec.activateStage(idx); } + + /** Changes a stage's status to inactive within the activity recorder. */ + void deactivateStage(const StageIdx idx) + { activityRec.deactivateStage(idx); } + + /** Wakes the CPU, rescheduling the CPU if it's not already active. */ + void wakeCPU(); + + /** Gets a free thread id. Use if thread ids change across system. */ + int getFreeTid(); + + public: + /** Returns a pointer to a thread context. */ + ThreadContext *tcBase(unsigned tid) + { + return thread[tid]->getTC(); + } + + /** The global sequence number counter. */ + InstSeqNum globalSeqNum;//[Impl::MaxThreads]; + + /** Pointer to the checker, which can dynamically verify + * instruction results at run time. This can be set to NULL if it + * is not being used. + */ + Checker<DynInstPtr> *checker; + +#if FULL_SYSTEM + /** Pointer to the system. */ + System *system; + + /** Pointer to physical memory. */ + PhysicalMemory *physmem; +#endif + + /** Pointer to memory. */ + MemObject *mem; + + /** Event to call process() on once draining has completed. */ + Event *drainEvent; + + /** Counter of how many stages have completed draining. */ + int drainCount; + + /** Pointers to all of the threads in the CPU. */ + std::vector<Thread *> thread; + + /** Pointer to the icache interface. */ + MemInterface *icacheInterface; + /** Pointer to the dcache interface. */ + MemInterface *dcacheInterface; + + /** Whether or not the CPU should defer its registration. */ + bool deferRegistration; + + /** Is there a context switch pending? */ + bool contextSwitch; + + /** Threads Scheduled to Enter CPU */ + std::list<int> cpuWaitList; + + /** The cycle that the CPU was last running, used for statistics. */ + Tick lastRunningCycle; + + /** The cycle that the CPU was last activated by a new thread*/ + Tick lastActivatedCycle; + + /** Number of Threads CPU can process */ + unsigned numThreads; + + /** Mapping for system thread id to cpu id */ + std::map<unsigned,unsigned> threadMap; + + /** Available thread ids in the cpu*/ + std::vector<unsigned> tids; + + /** Stat for total number of times the CPU is descheduled. */ + Stats::Scalar<> timesIdled; + /** Stat for total number of cycles the CPU spends descheduled. */ + Stats::Scalar<> idleCycles; + /** Stat for the number of committed instructions per thread. */ + Stats::Vector<> committedInsts; + /** Stat for the total number of committed instructions. */ + Stats::Scalar<> totalCommittedInsts; + /** Stat for the CPI per thread. */ + Stats::Formula cpi; + /** Stat for the total CPI. */ + Stats::Formula totalCpi; + /** Stat for the IPC per thread. */ + Stats::Formula ipc; + /** Stat for the total IPC. */ + Stats::Formula totalIpc; +}; + +#endif // __CPU_O3_CPU_HH__ diff --git a/src/cpu/o3/cpu_policy.hh b/src/cpu/o3/cpu_policy.hh new file mode 100644 index 000000000..32a0adcf1 --- /dev/null +++ b/src/cpu/o3/cpu_policy.hh @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_CPU_POLICY_HH__ +#define __CPU_O3_CPU_POLICY_HH__ + +#include "cpu/o3/bpred_unit.hh" +#include "cpu/o3/free_list.hh" +#include "cpu/o3/inst_queue.hh" +#include "cpu/o3/lsq.hh" +#include "cpu/o3/lsq_unit.hh" +#include "cpu/o3/mem_dep_unit.hh" +#include "cpu/o3/regfile.hh" +#include "cpu/o3/rename_map.hh" +#include "cpu/o3/rob.hh" +#include "cpu/o3/store_set.hh" + +#include "cpu/o3/commit.hh" +#include "cpu/o3/decode.hh" +#include "cpu/o3/fetch.hh" +#include "cpu/o3/iew.hh" +#include "cpu/o3/rename.hh" + +#include "cpu/o3/comm.hh" + +/** + * Struct that defines the key classes to be used by the CPU. All + * classes use the typedefs defined here to determine what are the + * classes of the other stages and communication buffers. In order to + * change a structure such as the IQ, simply change the typedef here + * to use the desired class instead, and recompile. In order to + * create a different CPU to be used simultaneously with this one, see + * the alpha_impl.hh file for instructions. + */ +template<class Impl> +struct SimpleCPUPolicy +{ + /** Typedef for the branch prediction unit (which includes the BP, + * RAS, and BTB). + */ + typedef BPredUnit<Impl> BPredUnit; + /** Typedef for the register file. Most classes assume a unified + * physical register file. + */ + typedef PhysRegFile<Impl> RegFile; + /** Typedef for the freelist of registers. */ + typedef SimpleFreeList FreeList; + /** Typedef for the rename map. */ + typedef SimpleRenameMap RenameMap; + /** Typedef for the ROB. */ + typedef ROB<Impl> ROB; + /** Typedef for the instruction queue/scheduler. */ + typedef InstructionQueue<Impl> IQ; + /** Typedef for the memory dependence unit. */ + typedef MemDepUnit<StoreSet, Impl> MemDepUnit; + /** Typedef for the LSQ. */ + typedef LSQ<Impl> LSQ; + /** Typedef for the thread-specific LSQ units. */ + typedef LSQUnit<Impl> LSQUnit; + + /** Typedef for fetch. */ + typedef DefaultFetch<Impl> Fetch; + /** Typedef for decode. */ + typedef DefaultDecode<Impl> Decode; + /** Typedef for rename. */ + typedef DefaultRename<Impl> Rename; + /** Typedef for Issue/Execute/Writeback. */ + typedef DefaultIEW<Impl> IEW; + /** Typedef for commit. */ + typedef DefaultCommit<Impl> Commit; + + /** The struct for communication between fetch and decode. */ + typedef DefaultFetchDefaultDecode<Impl> FetchStruct; + + /** The struct for communication between decode and rename. */ + typedef DefaultDecodeDefaultRename<Impl> DecodeStruct; + + /** The struct for communication between rename and IEW. */ + typedef DefaultRenameDefaultIEW<Impl> RenameStruct; + + /** The struct for communication between IEW and commit. */ + typedef DefaultIEWDefaultCommit<Impl> IEWStruct; + + /** The struct for communication within the IEW stage. */ + typedef IssueStruct<Impl> IssueStruct; + + /** The struct for all backwards communication. */ + typedef TimeBufStruct<Impl> TimeStruct; + +}; + +#endif //__CPU_O3_CPU_POLICY_HH__ diff --git a/src/cpu/o3/decode.cc b/src/cpu/o3/decode.cc new file mode 100644 index 000000000..896e38331 --- /dev/null +++ b/src/cpu/o3/decode.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/decode_impl.hh" + +template class DefaultDecode<O3CPUImpl>; diff --git a/src/cpu/o3/decode.hh b/src/cpu/o3/decode.hh new file mode 100644 index 000000000..4a845e670 --- /dev/null +++ b/src/cpu/o3/decode.hh @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_DECODE_HH__ +#define __CPU_O3_DECODE_HH__ + +#include <queue> + +#include "base/statistics.hh" +#include "base/timebuf.hh" + +/** + * DefaultDecode class handles both single threaded and SMT + * decode. Its width is specified by the parameters; each cycles it + * tries to decode that many instructions. Because instructions are + * actually decoded when the StaticInst is created, this stage does + * not do much other than check any PC-relative branches. + */ +template<class Impl> +class DefaultDecode +{ + private: + // Typedefs from the Impl. + typedef typename Impl::O3CPU O3CPU; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::Params Params; + typedef typename Impl::CPUPol CPUPol; + + // Typedefs from the CPU policy. + typedef typename CPUPol::FetchStruct FetchStruct; + typedef typename CPUPol::DecodeStruct DecodeStruct; + typedef typename CPUPol::TimeStruct TimeStruct; + + public: + /** Overall decode stage status. Used to determine if the CPU can + * deschedule itself due to a lack of activity. + */ + enum DecodeStatus { + Active, + Inactive + }; + + /** Individual thread status. */ + enum ThreadStatus { + Running, + Idle, + StartSquash, + Squashing, + Blocked, + Unblocking + }; + + private: + /** Decode status. */ + DecodeStatus _status; + + /** Per-thread status. */ + ThreadStatus decodeStatus[Impl::MaxThreads]; + + public: + /** DefaultDecode constructor. */ + DefaultDecode(Params *params); + + /** Returns the name of decode. */ + std::string name() const; + + /** Registers statistics. */ + void regStats(); + + /** Sets CPU pointer. */ + void setCPU(O3CPU *cpu_ptr); + + /** Sets the main backwards communication time buffer pointer. */ + void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + + /** Sets pointer to time buffer used to communicate to the next stage. */ + void setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr); + + /** Sets pointer to time buffer coming from fetch. */ + void setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr); + + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Drains the decode stage. */ + bool drain(); + + /** Resumes execution after a drain. */ + void resume() { } + + /** Switches out the decode stage. */ + void switchOut() { } + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Ticks decode, processing all input signals and decoding as many + * instructions as possible. + */ + void tick(); + + /** Determines what to do based on decode's current status. + * @param status_change decode() sets this variable if there was a status + * change (ie switching from from blocking to unblocking). + * @param tid Thread id to decode instructions from. + */ + void decode(bool &status_change, unsigned tid); + + /** Processes instructions from fetch and passes them on to rename. + * Decoding of instructions actually happens when they are created in + * fetch, so this function mostly checks if PC-relative branches are + * correct. + */ + void decodeInsts(unsigned tid); + + private: + /** Inserts a thread's instructions into the skid buffer, to be decoded + * once decode unblocks. + */ + void skidInsert(unsigned tid); + + /** Returns if all of the skid buffers are empty. */ + bool skidsEmpty(); + + /** Updates overall decode status based on all of the threads' statuses. */ + void updateStatus(); + + /** Separates instructions from fetch into individual lists of instructions + * sorted by thread. + */ + void sortInsts(); + + /** Reads all stall signals from the backwards communication timebuffer. */ + void readStallSignals(unsigned tid); + + /** Checks all input signals and updates decode's status appropriately. */ + bool checkSignalsAndUpdate(unsigned tid); + + /** Checks all stall signals, and returns if any are true. */ + bool checkStall(unsigned tid) const; + + /** Returns if there any instructions from fetch on this cycle. */ + inline bool fetchInstsValid(); + + /** Switches decode to blocking, and signals back that decode has + * become blocked. + * @return Returns true if there is a status change. + */ + bool block(unsigned tid); + + /** Switches decode to unblocking if the skid buffer is empty, and + * signals back that decode has unblocked. + * @return Returns true if there is a status change. + */ + bool unblock(unsigned tid); + + /** Squashes if there is a PC-relative branch that was predicted + * incorrectly. Sends squash information back to fetch. + */ + void squash(DynInstPtr &inst, unsigned tid); + + public: + /** Squashes due to commit signalling a squash. Changes status to + * squashing and clears block/unblock signals as needed. + */ + unsigned squash(unsigned tid); + + private: + // Interfaces to objects outside of decode. + /** CPU interface. */ + O3CPU *cpu; + + /** Time buffer interface. */ + TimeBuffer<TimeStruct> *timeBuffer; + + /** Wire to get rename's output from backwards time buffer. */ + typename TimeBuffer<TimeStruct>::wire fromRename; + + /** Wire to get iew's information from backwards time buffer. */ + typename TimeBuffer<TimeStruct>::wire fromIEW; + + /** Wire to get commit's information from backwards time buffer. */ + typename TimeBuffer<TimeStruct>::wire fromCommit; + + /** Wire to write information heading to previous stages. */ + // Might not be the best name as not only fetch will read it. + typename TimeBuffer<TimeStruct>::wire toFetch; + + /** Decode instruction queue. */ + TimeBuffer<DecodeStruct> *decodeQueue; + + /** Wire used to write any information heading to rename. */ + typename TimeBuffer<DecodeStruct>::wire toRename; + + /** Fetch instruction queue interface. */ + TimeBuffer<FetchStruct> *fetchQueue; + + /** Wire to get fetch's output from fetch queue. */ + typename TimeBuffer<FetchStruct>::wire fromFetch; + + /** Queue of all instructions coming from fetch this cycle. */ + std::queue<DynInstPtr> insts[Impl::MaxThreads]; + + /** Skid buffer between fetch and decode. */ + std::queue<DynInstPtr> skidBuffer[Impl::MaxThreads]; + + /** Variable that tracks if decode has written to the time buffer this + * cycle. Used to tell CPU if there is activity this cycle. + */ + bool wroteToTimeBuffer; + + /** Source of possible stalls. */ + struct Stalls { + bool rename; + bool iew; + bool commit; + }; + + /** Tracks which stages are telling decode to stall. */ + Stalls stalls[Impl::MaxThreads]; + + /** Rename to decode delay, in ticks. */ + unsigned renameToDecodeDelay; + + /** IEW to decode delay, in ticks. */ + unsigned iewToDecodeDelay; + + /** Commit to decode delay, in ticks. */ + unsigned commitToDecodeDelay; + + /** Fetch to decode delay, in ticks. */ + unsigned fetchToDecodeDelay; + + /** The width of decode, in instructions. */ + unsigned decodeWidth; + + /** Index of instructions being sent to rename. */ + unsigned toRenameIndex; + + /** number of Active Threads*/ + unsigned numThreads; + + /** List of active thread ids */ + std::list<unsigned> *activeThreads; + + /** Number of branches in flight. */ + unsigned branchCount[Impl::MaxThreads]; + + /** Maximum size of the skid buffer. */ + unsigned skidBufferMax; + + /** SeqNum of Squashing Branch Delay Instruction (used for MIPS)*/ + Addr bdelayDoneSeqNum[Impl::MaxThreads]; + + /** Instruction used for squashing branch (used for MIPS)*/ + DynInstPtr squashInst[Impl::MaxThreads]; + + /** Tells when their is a pending delay slot inst. to send + * to rename. If there is, then wait squash after the next + * instruction (used for MIPS). + */ + bool squashAfterDelaySlot[Impl::MaxThreads]; + + + /** Stat for total number of idle cycles. */ + Stats::Scalar<> decodeIdleCycles; + /** Stat for total number of blocked cycles. */ + Stats::Scalar<> decodeBlockedCycles; + /** Stat for total number of normal running cycles. */ + Stats::Scalar<> decodeRunCycles; + /** Stat for total number of unblocking cycles. */ + Stats::Scalar<> decodeUnblockCycles; + /** Stat for total number of squashing cycles. */ + Stats::Scalar<> decodeSquashCycles; + /** Stat for number of times a branch is resolved at decode. */ + Stats::Scalar<> decodeBranchResolved; + /** Stat for number of times a branch mispredict is detected. */ + Stats::Scalar<> decodeBranchMispred; + /** Stat for number of times decode detected a non-control instruction + * incorrectly predicted as a branch. + */ + Stats::Scalar<> decodeControlMispred; + /** Stat for total number of decoded instructions. */ + Stats::Scalar<> decodeDecodedInsts; + /** Stat for total number of squashed instructions. */ + Stats::Scalar<> decodeSquashedInsts; +}; + +#endif // __CPU_O3_DECODE_HH__ diff --git a/src/cpu/o3/decode_impl.hh b/src/cpu/o3/decode_impl.hh new file mode 100644 index 000000000..80b6cc4c9 --- /dev/null +++ b/src/cpu/o3/decode_impl.hh @@ -0,0 +1,818 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/decode.hh" + +template<class Impl> +DefaultDecode<Impl>::DefaultDecode(Params *params) + : renameToDecodeDelay(params->renameToDecodeDelay), + iewToDecodeDelay(params->iewToDecodeDelay), + commitToDecodeDelay(params->commitToDecodeDelay), + fetchToDecodeDelay(params->fetchToDecodeDelay), + decodeWidth(params->decodeWidth), + numThreads(params->numberOfThreads) +{ + _status = Inactive; + + // Setup status, make sure stall signals are clear. + for (int i = 0; i < numThreads; ++i) { + decodeStatus[i] = Idle; + + stalls[i].rename = false; + stalls[i].iew = false; + stalls[i].commit = false; + + squashAfterDelaySlot[i] = false; + } + + // @todo: Make into a parameter + skidBufferMax = (fetchToDecodeDelay * params->fetchWidth) + decodeWidth; +} + +template <class Impl> +std::string +DefaultDecode<Impl>::name() const +{ + return cpu->name() + ".decode"; +} + +template <class Impl> +void +DefaultDecode<Impl>::regStats() +{ + decodeIdleCycles + .name(name() + ".DECODE:IdleCycles") + .desc("Number of cycles decode is idle") + .prereq(decodeIdleCycles); + decodeBlockedCycles + .name(name() + ".DECODE:BlockedCycles") + .desc("Number of cycles decode is blocked") + .prereq(decodeBlockedCycles); + decodeRunCycles + .name(name() + ".DECODE:RunCycles") + .desc("Number of cycles decode is running") + .prereq(decodeRunCycles); + decodeUnblockCycles + .name(name() + ".DECODE:UnblockCycles") + .desc("Number of cycles decode is unblocking") + .prereq(decodeUnblockCycles); + decodeSquashCycles + .name(name() + ".DECODE:SquashCycles") + .desc("Number of cycles decode is squashing") + .prereq(decodeSquashCycles); + decodeBranchResolved + .name(name() + ".DECODE:BranchResolved") + .desc("Number of times decode resolved a branch") + .prereq(decodeBranchResolved); + decodeBranchMispred + .name(name() + ".DECODE:BranchMispred") + .desc("Number of times decode detected a branch misprediction") + .prereq(decodeBranchMispred); + decodeControlMispred + .name(name() + ".DECODE:ControlMispred") + .desc("Number of times decode detected an instruction incorrectly" + " predicted as a control") + .prereq(decodeControlMispred); + decodeDecodedInsts + .name(name() + ".DECODE:DecodedInsts") + .desc("Number of instructions handled by decode") + .prereq(decodeDecodedInsts); + decodeSquashedInsts + .name(name() + ".DECODE:SquashedInsts") + .desc("Number of squashed instructions handled by decode") + .prereq(decodeSquashedInsts); +} + +template<class Impl> +void +DefaultDecode<Impl>::setCPU(O3CPU *cpu_ptr) +{ + DPRINTF(Decode, "Setting CPU pointer.\n"); + cpu = cpu_ptr; +} + +template<class Impl> +void +DefaultDecode<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +{ + DPRINTF(Decode, "Setting time buffer pointer.\n"); + timeBuffer = tb_ptr; + + // Setup wire to write information back to fetch. + toFetch = timeBuffer->getWire(0); + + // Create wires to get information from proper places in time buffer. + fromRename = timeBuffer->getWire(-renameToDecodeDelay); + fromIEW = timeBuffer->getWire(-iewToDecodeDelay); + fromCommit = timeBuffer->getWire(-commitToDecodeDelay); +} + +template<class Impl> +void +DefaultDecode<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr) +{ + DPRINTF(Decode, "Setting decode queue pointer.\n"); + decodeQueue = dq_ptr; + + // Setup wire to write information to proper place in decode queue. + toRename = decodeQueue->getWire(0); +} + +template<class Impl> +void +DefaultDecode<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) +{ + DPRINTF(Decode, "Setting fetch queue pointer.\n"); + fetchQueue = fq_ptr; + + // Setup wire to read information from fetch queue. + fromFetch = fetchQueue->getWire(-fetchToDecodeDelay); +} + +template<class Impl> +void +DefaultDecode<Impl>::setActiveThreads(std::list<unsigned> *at_ptr) +{ + DPRINTF(Decode, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template <class Impl> +bool +DefaultDecode<Impl>::drain() +{ + // Decode is done draining at any time. + cpu->signalDrained(); + return true; +} + +template <class Impl> +void +DefaultDecode<Impl>::takeOverFrom() +{ + _status = Inactive; + + // Be sure to reset state and clear out any old instructions. + for (int i = 0; i < numThreads; ++i) { + decodeStatus[i] = Idle; + + stalls[i].rename = false; + stalls[i].iew = false; + stalls[i].commit = false; + while (!insts[i].empty()) + insts[i].pop(); + while (!skidBuffer[i].empty()) + skidBuffer[i].pop(); + branchCount[i] = 0; + } + wroteToTimeBuffer = false; +} + +template<class Impl> +bool +DefaultDecode<Impl>::checkStall(unsigned tid) const +{ + bool ret_val = false; + + if (stalls[tid].rename) { + DPRINTF(Decode,"[tid:%i]: Stall fom Rename stage detected.\n", tid); + ret_val = true; + } else if (stalls[tid].iew) { + DPRINTF(Decode,"[tid:%i]: Stall fom IEW stage detected.\n", tid); + ret_val = true; + } else if (stalls[tid].commit) { + DPRINTF(Decode,"[tid:%i]: Stall fom Commit stage detected.\n", tid); + ret_val = true; + } + + return ret_val; +} + +template<class Impl> +inline bool +DefaultDecode<Impl>::fetchInstsValid() +{ + return fromFetch->size > 0; +} + +template<class Impl> +bool +DefaultDecode<Impl>::block(unsigned tid) +{ + DPRINTF(Decode, "[tid:%u]: Blocking.\n", tid); + + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + + // If the decode status is blocked or unblocking then decode has not yet + // signalled fetch to unblock. In that case, there is no need to tell + // fetch to block. + if (decodeStatus[tid] != Blocked) { + // Set the status to Blocked. + decodeStatus[tid] = Blocked; + + if (decodeStatus[tid] != Unblocking) { + toFetch->decodeBlock[tid] = true; + wroteToTimeBuffer = true; + } + + return true; + } + + return false; +} + +template<class Impl> +bool +DefaultDecode<Impl>::unblock(unsigned tid) +{ + // Decode is done unblocking only if the skid buffer is empty. + if (skidBuffer[tid].empty()) { + DPRINTF(Decode, "[tid:%u]: Done unblocking.\n", tid); + toFetch->decodeUnblock[tid] = true; + wroteToTimeBuffer = true; + + decodeStatus[tid] = Running; + return true; + } + + DPRINTF(Decode, "[tid:%u]: Currently unblocking.\n", tid); + + return false; +} + +template<class Impl> +void +DefaultDecode<Impl>::squash(DynInstPtr &inst, unsigned tid) +{ + DPRINTF(Decode, "[tid:%i]: Squashing due to incorrect branch prediction " + "detected at decode.\n", tid); + + // Send back mispredict information. + toFetch->decodeInfo[tid].branchMispredict = true; + toFetch->decodeInfo[tid].predIncorrect = true; + toFetch->decodeInfo[tid].doneSeqNum = inst->seqNum; + toFetch->decodeInfo[tid].squash = true; + toFetch->decodeInfo[tid].nextPC = inst->branchTarget(); +#if ISA_HAS_DELAY_SLOT + toFetch->decodeInfo[tid].branchTaken = inst->readNextNPC() != + (inst->readNextPC() + sizeof(TheISA::MachInst)); + + toFetch->decodeInfo[tid].bdelayDoneSeqNum = bdelayDoneSeqNum[tid]; + squashAfterDelaySlot[tid] = false; + + InstSeqNum squash_seq_num = bdelayDoneSeqNum[tid]; +#else + toFetch->decodeInfo[tid].branchTaken = + inst->readNextPC() != (inst->readPC() + sizeof(TheISA::MachInst)); + + InstSeqNum squash_seq_num = inst->seqNum; +#endif + + // Might have to tell fetch to unblock. + if (decodeStatus[tid] == Blocked || + decodeStatus[tid] == Unblocking) { + toFetch->decodeUnblock[tid] = 1; + } + + // Set status to squashing. + decodeStatus[tid] = Squashing; + + for (int i=0; i<fromFetch->size; i++) { + if (fromFetch->insts[i]->threadNumber == tid && + fromFetch->insts[i]->seqNum > squash_seq_num) { + fromFetch->insts[i]->setSquashed(); + } + } + + // Clear the instruction list and skid buffer in case they have any + // insts in them. + while (!insts[tid].empty()) { + +#if ISA_HAS_DELAY_SLOT + if (insts[tid].front()->seqNum <= squash_seq_num) { + DPRINTF(Decode, "[tid:%i]: Cannot remove incoming decode " + "instructions before delay slot [sn:%i]. %i insts" + "left in decode.\n", tid, squash_seq_num, + insts[tid].size()); + break; + } +#endif + insts[tid].pop(); + } + + while (!skidBuffer[tid].empty()) { + +#if ISA_HAS_DELAY_SLOT + if (skidBuffer[tid].front()->seqNum <= squash_seq_num) { + DPRINTF(Decode, "[tid:%i]: Cannot remove skidBuffer " + "instructions before delay slot [sn:%i]. %i insts" + "left in decode.\n", tid, squash_seq_num, + insts[tid].size()); + break; + } +#endif + skidBuffer[tid].pop(); + } + + // Squash instructions up until this one + cpu->removeInstsUntil(squash_seq_num, tid); +} + +template<class Impl> +unsigned +DefaultDecode<Impl>::squash(unsigned tid) +{ + DPRINTF(Decode, "[tid:%i]: Squashing.\n",tid); + + if (decodeStatus[tid] == Blocked || + decodeStatus[tid] == Unblocking) { +#if !FULL_SYSTEM + // In syscall emulation, we can have both a block and a squash due + // to a syscall in the same cycle. This would cause both signals to + // be high. This shouldn't happen in full system. + // @todo: Determine if this still happens. + if (toFetch->decodeBlock[tid]) { + toFetch->decodeBlock[tid] = 0; + } else { + toFetch->decodeUnblock[tid] = 1; + } +#else + toFetch->decodeUnblock[tid] = 1; +#endif + } + + // Set status to squashing. + decodeStatus[tid] = Squashing; + + // Go through incoming instructions from fetch and squash them. + unsigned squash_count = 0; + + for (int i=0; i<fromFetch->size; i++) { + if (fromFetch->insts[i]->threadNumber == tid) { + fromFetch->insts[i]->setSquashed(); + squash_count++; + } + } + + // Clear the instruction list and skid buffer in case they have any + // insts in them. + while (!insts[tid].empty()) { + insts[tid].pop(); + } + + while (!skidBuffer[tid].empty()) { + skidBuffer[tid].pop(); + } + + return squash_count; +} + +template<class Impl> +void +DefaultDecode<Impl>::skidInsert(unsigned tid) +{ + DynInstPtr inst = NULL; + + while (!insts[tid].empty()) { + inst = insts[tid].front(); + + insts[tid].pop(); + + assert(tid == inst->threadNumber); + + DPRINTF(Decode,"Inserting [sn:%lli] PC:%#x into decode skidBuffer %i\n", + inst->seqNum, inst->readPC(), inst->threadNumber); + + skidBuffer[tid].push(inst); + } + + // @todo: Eventually need to enforce this by not letting a thread + // fetch past its skidbuffer + assert(skidBuffer[tid].size() <= skidBufferMax); +} + +template<class Impl> +bool +DefaultDecode<Impl>::skidsEmpty() +{ + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + if (!skidBuffer[*threads++].empty()) + return false; + } + + return true; +} + +template<class Impl> +void +DefaultDecode<Impl>::updateStatus() +{ + bool any_unblocking = false; + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (decodeStatus[tid] == Unblocking) { + any_unblocking = true; + break; + } + } + + // Decode will have activity if it's unblocking. + if (any_unblocking) { + if (_status == Inactive) { + _status = Active; + + DPRINTF(Activity, "Activating stage.\n"); + + cpu->activateStage(O3CPU::DecodeIdx); + } + } else { + // If it's not unblocking, then decode will not have any internal + // activity. Switch it to inactive. + if (_status == Active) { + _status = Inactive; + DPRINTF(Activity, "Deactivating stage.\n"); + + cpu->deactivateStage(O3CPU::DecodeIdx); + } + } +} + +template <class Impl> +void +DefaultDecode<Impl>::sortInsts() +{ + int insts_from_fetch = fromFetch->size; +#ifdef DEBUG + for (int i=0; i < numThreads; i++) + assert(insts[i].empty()); +#endif + for (int i = 0; i < insts_from_fetch; ++i) { + insts[fromFetch->insts[i]->threadNumber].push(fromFetch->insts[i]); + } +} + +template<class Impl> +void +DefaultDecode<Impl>::readStallSignals(unsigned tid) +{ + if (fromRename->renameBlock[tid]) { + stalls[tid].rename = true; + } + + if (fromRename->renameUnblock[tid]) { + assert(stalls[tid].rename); + stalls[tid].rename = false; + } + + if (fromIEW->iewBlock[tid]) { + stalls[tid].iew = true; + } + + if (fromIEW->iewUnblock[tid]) { + assert(stalls[tid].iew); + stalls[tid].iew = false; + } + + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } + + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + stalls[tid].commit = false; + } +} + +template <class Impl> +bool +DefaultDecode<Impl>::checkSignalsAndUpdate(unsigned tid) +{ + // Check if there's a squash signal, squash if there is. + // Check stall signals, block if necessary. + // If status was blocked + // Check if stall conditions have passed + // if so then go to unblocking + // If status was Squashing + // check if squashing is not high. Switch to running this cycle. + + // Update the per thread stall statuses. + readStallSignals(tid); + + // Check squash signals from commit. + if (fromCommit->commitInfo[tid].squash) { + + DPRINTF(Decode, "[tid:%u]: Squashing instructions due to squash " + "from commit.\n", tid); + + squash(tid); + + return true; + } + + // Check ROB squash signals from commit. + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(Decode, "[tid:%u]: ROB is still squashing.\n", tid); + + // Continue to squash. + decodeStatus[tid] = Squashing; + + return true; + } + + if (checkStall(tid)) { + return block(tid); + } + + if (decodeStatus[tid] == Blocked) { + DPRINTF(Decode, "[tid:%u]: Done blocking, switching to unblocking.\n", + tid); + + decodeStatus[tid] = Unblocking; + + unblock(tid); + + return true; + } + + if (decodeStatus[tid] == Squashing) { + // Switch status to running if decode isn't being told to block or + // squash this cycle. + DPRINTF(Decode, "[tid:%u]: Done squashing, switching to running.\n", + tid); + + decodeStatus[tid] = Running; + + return false; + } + + // If we've reached this point, we have not gotten any signals that + // cause decode to change its status. Decode remains the same as before. + return false; +} + +template<class Impl> +void +DefaultDecode<Impl>::tick() +{ + wroteToTimeBuffer = false; + + bool status_change = false; + + toRenameIndex = 0; + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + sortInsts(); + + //Check stall and squash signals. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + DPRINTF(Decode,"Processing [tid:%i]\n",tid); + status_change = checkSignalsAndUpdate(tid) || status_change; + + decode(status_change, tid); + } + + if (status_change) { + updateStatus(); + } + + if (wroteToTimeBuffer) { + DPRINTF(Activity, "Activity this cycle.\n"); + + cpu->activityThisCycle(); + } +} + +template<class Impl> +void +DefaultDecode<Impl>::decode(bool &status_change, unsigned tid) +{ + // If status is Running or idle, + // call decodeInsts() + // If status is Unblocking, + // buffer any instructions coming from fetch + // continue trying to empty skid buffer + // check if stall conditions have passed + + if (decodeStatus[tid] == Blocked) { + ++decodeBlockedCycles; + } else if (decodeStatus[tid] == Squashing) { + ++decodeSquashCycles; + } + + // Decode should try to decode as many instructions as its bandwidth + // will allow, as long as it is not currently blocked. + if (decodeStatus[tid] == Running || + decodeStatus[tid] == Idle) { + DPRINTF(Decode, "[tid:%u]: Not blocked, so attempting to run " + "stage.\n",tid); + + decodeInsts(tid); + } else if (decodeStatus[tid] == Unblocking) { + // Make sure that the skid buffer has something in it if the + // status is unblocking. + assert(!skidsEmpty()); + + // If the status was unblocking, then instructions from the skid + // buffer were used. Remove those instructions and handle + // the rest of unblocking. + decodeInsts(tid); + + if (fetchInstsValid()) { + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + } + + status_change = unblock(tid) || status_change; + } +} + +template <class Impl> +void +DefaultDecode<Impl>::decodeInsts(unsigned tid) +{ + // Instructions can come either from the skid buffer or the list of + // instructions coming from fetch, depending on decode's status. + int insts_available = decodeStatus[tid] == Unblocking ? + skidBuffer[tid].size() : insts[tid].size(); + + if (insts_available == 0) { + DPRINTF(Decode, "[tid:%u] Nothing to do, breaking out" + " early.\n",tid); + // Should I change the status to idle? + ++decodeIdleCycles; + return; + } else if (decodeStatus[tid] == Unblocking) { + DPRINTF(Decode, "[tid:%u] Unblocking, removing insts from skid " + "buffer.\n",tid); + ++decodeUnblockCycles; + } else if (decodeStatus[tid] == Running) { + ++decodeRunCycles; + } + + DynInstPtr inst; + + std::queue<DynInstPtr> + &insts_to_decode = decodeStatus[tid] == Unblocking ? + skidBuffer[tid] : insts[tid]; + + DPRINTF(Decode, "[tid:%u]: Sending instruction to rename.\n",tid); + + while (insts_available > 0 && toRenameIndex < decodeWidth) { + assert(!insts_to_decode.empty()); + + inst = insts_to_decode.front(); + + insts_to_decode.pop(); + + DPRINTF(Decode, "[tid:%u]: Processing instruction [sn:%lli] with " + "PC %#x\n", + tid, inst->seqNum, inst->readPC()); + + if (inst->isSquashed()) { + DPRINTF(Decode, "[tid:%u]: Instruction %i with PC %#x is " + "squashed, skipping.\n", + tid, inst->seqNum, inst->readPC()); + + ++decodeSquashedInsts; + + --insts_available; + + continue; + } + + // Also check if instructions have no source registers. Mark + // them as ready to issue at any time. Not sure if this check + // should exist here or at a later stage; however it doesn't matter + // too much for function correctness. + if (inst->numSrcRegs() == 0) { + inst->setCanIssue(); + } + + // This current instruction is valid, so add it into the decode + // queue. The next instruction may not be valid, so check to + // see if branches were predicted correctly. + toRename->insts[toRenameIndex] = inst; + + ++(toRename->size); + ++toRenameIndex; + ++decodeDecodedInsts; + --insts_available; + + // Ensure that if it was predicted as a branch, it really is a + // branch. + if (inst->predTaken() && !inst->isControl()) { + DPRINTF(Decode, "PredPC : %#x != NextPC: %#x\n",inst->predPC, + inst->nextPC + 4); + + panic("Instruction predicted as a branch!"); + + ++decodeControlMispred; + + // Might want to set some sort of boolean and just do + // a check at the end + squash(inst, inst->threadNumber); + + break; + } + + // Go ahead and compute any PC-relative branches. + if (inst->isDirectCtrl() && inst->isUncondCtrl()) { + ++decodeBranchResolved; + + if (inst->branchTarget() != inst->readPredTarg()) { + ++decodeBranchMispred; + + // Might want to set some sort of boolean and just do + // a check at the end +#if !ISA_HAS_DELAY_SLOT + squash(inst, inst->threadNumber); + inst->setPredTarg(inst->branchTarget()); + break; +#else + // If mispredicted as taken, then ignore delay slot + // instruction... else keep delay slot and squash + // after it is sent to rename + if (inst->predTaken() && inst->isCondDelaySlot()) { + DPRINTF(Decode, "[tid:%i]: Conditional delay slot inst." + "[sn:%i] PC %#x mispredicted as taken.\n", tid, + inst->seqNum, inst->PC); + bdelayDoneSeqNum[tid] = inst->seqNum; + squash(inst, inst->threadNumber); + inst->setPredTarg(inst->branchTarget()); + break; + } else { + DPRINTF(Decode, "[tid:%i]: Misprediction detected at " + "[sn:%i] PC %#x, will squash after delay slot " + "inst. is sent to Rename\n", + tid, inst->seqNum, inst->PC); + bdelayDoneSeqNum[tid] = inst->seqNum + 1; + squashAfterDelaySlot[tid] = true; + squashInst[tid] = inst; + continue; + } +#endif + } + } + + if (squashAfterDelaySlot[tid]) { + assert(!inst->isSquashed()); + squash(squashInst[tid], squashInst[tid]->threadNumber); + squashInst[tid]->setPredTarg(squashInst[tid]->branchTarget()); + assert(!inst->isSquashed()); + break; + } + } + + // If we didn't process all instructions, then we will need to block + // and put all those instructions into the skid buffer. + if (!insts_to_decode.empty()) { + block(tid); + } + + // Record that decode has written to the time buffer for activity + // tracking. + if (toRenameIndex) { + wroteToTimeBuffer = true; + } +} diff --git a/src/cpu/o3/dep_graph.hh b/src/cpu/o3/dep_graph.hh new file mode 100644 index 000000000..c19fd0abf --- /dev/null +++ b/src/cpu/o3/dep_graph.hh @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_DEP_GRAPH_HH__ +#define __CPU_O3_DEP_GRAPH_HH__ + +#include "cpu/o3/comm.hh" + +/** Node in a linked list. */ +template <class DynInstPtr> +class DependencyEntry +{ + public: + DependencyEntry() + : inst(NULL), next(NULL) + { } + + DynInstPtr inst; + //Might want to include data about what arch. register the + //dependence is waiting on. + DependencyEntry<DynInstPtr> *next; +}; + +/** Array of linked list that maintains the dependencies between + * producing instructions and consuming instructions. Each linked + * list represents a single physical register, having the future + * producer of the register's value, and all consumers waiting on that + * value on the list. The head node of each linked list represents + * the producing instruction of that register. Instructions are put + * on the list upon reaching the IQ, and are removed from the list + * either when the producer completes, or the instruction is squashed. +*/ +template <class DynInstPtr> +class DependencyGraph +{ + public: + typedef DependencyEntry<DynInstPtr> DepEntry; + + /** Default construction. Must call resize() prior to use. */ + DependencyGraph() + : numEntries(0), memAllocCounter(0), nodesTraversed(0), nodesRemoved(0) + { } + + ~DependencyGraph(); + + /** Resize the dependency graph to have num_entries registers. */ + void resize(int num_entries); + + /** Clears all of the linked lists. */ + void reset(); + + /** Inserts an instruction to be dependent on the given index. */ + void insert(PhysRegIndex idx, DynInstPtr &new_inst); + + /** Sets the producing instruction of a given register. */ + void setInst(PhysRegIndex idx, DynInstPtr &new_inst) + { dependGraph[idx].inst = new_inst; } + + /** Clears the producing instruction. */ + void clearInst(PhysRegIndex idx) + { dependGraph[idx].inst = NULL; } + + /** Removes an instruction from a single linked list. */ + void remove(PhysRegIndex idx, DynInstPtr &inst_to_remove); + + /** Removes and returns the newest dependent of a specific register. */ + DynInstPtr pop(PhysRegIndex idx); + + /** Checks if there are any dependents on a specific register. */ + bool empty(PhysRegIndex idx) { return !dependGraph[idx].next; } + + /** Debugging function to dump out the dependency graph. + */ + void dump(); + + private: + /** Array of linked lists. Each linked list is a list of all the + * instructions that depend upon a given register. The actual + * register's index is used to index into the graph; ie all + * instructions in flight that are dependent upon r34 will be + * in the linked list of dependGraph[34]. + */ + DepEntry *dependGraph; + + /** Number of linked lists; identical to the number of registers. */ + int numEntries; + + // Debug variable, remove when done testing. + unsigned memAllocCounter; + + public: + // Debug variable, remove when done testing. + uint64_t nodesTraversed; + // Debug variable, remove when done testing. + uint64_t nodesRemoved; +}; + +template <class DynInstPtr> +DependencyGraph<DynInstPtr>::~DependencyGraph() +{ + delete [] dependGraph; +} + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::resize(int num_entries) +{ + numEntries = num_entries; + dependGraph = new DepEntry[numEntries]; +} + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::reset() +{ + // Clear the dependency graph + DepEntry *curr; + DepEntry *prev; + + for (int i = 0; i < numEntries; ++i) { + curr = dependGraph[i].next; + + while (curr) { + memAllocCounter--; + + prev = curr; + curr = prev->next; + prev->inst = NULL; + + delete prev; + } + + if (dependGraph[i].inst) { + dependGraph[i].inst = NULL; + } + + dependGraph[i].next = NULL; + } +} + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::insert(PhysRegIndex idx, DynInstPtr &new_inst) +{ + //Add this new, dependent instruction at the head of the dependency + //chain. + + // First create the entry that will be added to the head of the + // dependency chain. + DepEntry *new_entry = new DepEntry; + new_entry->next = dependGraph[idx].next; + new_entry->inst = new_inst; + + // Then actually add it to the chain. + dependGraph[idx].next = new_entry; + + ++memAllocCounter; +} + + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::remove(PhysRegIndex idx, + DynInstPtr &inst_to_remove) +{ + DepEntry *prev = &dependGraph[idx]; + DepEntry *curr = dependGraph[idx].next; + + // Make sure curr isn't NULL. Because this instruction is being + // removed from a dependency list, it must have been placed there at + // an earlier time. The dependency chain should not be empty, + // unless the instruction dependent upon it is already ready. + if (curr == NULL) { + return; + } + + nodesRemoved++; + + // Find the instruction to remove within the dependency linked list. + while (curr->inst != inst_to_remove) { + prev = curr; + curr = curr->next; + nodesTraversed++; + + assert(curr != NULL); + } + + // Now remove this instruction from the list. + prev->next = curr->next; + + --memAllocCounter; + + // Could push this off to the destructor of DependencyEntry + curr->inst = NULL; + + delete curr; +} + +template <class DynInstPtr> +DynInstPtr +DependencyGraph<DynInstPtr>::pop(PhysRegIndex idx) +{ + DepEntry *node; + node = dependGraph[idx].next; + DynInstPtr inst = NULL; + if (node) { + inst = node->inst; + dependGraph[idx].next = node->next; + node->inst = NULL; + memAllocCounter--; + delete node; + } + return inst; +} + +template <class DynInstPtr> +void +DependencyGraph<DynInstPtr>::dump() +{ + DepEntry *curr; + + for (int i = 0; i < numEntries; ++i) + { + curr = &dependGraph[i]; + + if (curr->inst) { + cprintf("dependGraph[%i]: producer: %#x [sn:%lli] consumer: ", + i, curr->inst->readPC(), curr->inst->seqNum); + } else { + cprintf("dependGraph[%i]: No producer. consumer: ", i); + } + + while (curr->next != NULL) { + curr = curr->next; + + cprintf("%#x [sn:%lli] ", + curr->inst->readPC(), curr->inst->seqNum); + } + + cprintf("\n"); + } + cprintf("memAllocCounter: %i\n", memAllocCounter); +} + +#endif // __CPU_O3_DEP_GRAPH_HH__ diff --git a/src/cpu/o3/dyn_inst.hh b/src/cpu/o3/dyn_inst.hh new file mode 100644 index 000000000..279513493 --- /dev/null +++ b/src/cpu/o3/dyn_inst.hh @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#ifndef __CPU_O3_DYN_INST_HH__ +#define __CPU_O3_DYN_INST_HH__ + +#include "arch/isa_specific.hh" + +#if THE_ISA == ALPHA_ISA + template <class Impl> class AlphaDynInst; + struct AlphaSimpleImpl; + typedef AlphaDynInst<AlphaSimpleImpl> O3DynInst; +#elif THE_ISA == MIPS_ISA + template <class Impl> class MipsDynInst; + struct MipsSimpleImpl; + typedef MipsDynInst<MipsSimpleImpl> O3DynInst; +#elif THE_ISA == SPARC_ISA + template <class Impl> class SparcDynInst; + struct SparcSimpleImpl; + typedef SparcDynInst<SparcSimpleImpl> O3DynInst; +#else + #error "O3DynInst not defined for this ISA" +#endif + +#endif // __CPU_O3_DYN_INST_HH__ diff --git a/src/cpu/o3/fetch.cc b/src/cpu/o3/fetch.cc new file mode 100644 index 000000000..d809b07e4 --- /dev/null +++ b/src/cpu/o3/fetch.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/fetch_impl.hh" + +template class DefaultFetch<O3CPUImpl>; diff --git a/src/cpu/o3/fetch.hh b/src/cpu/o3/fetch.hh new file mode 100644 index 000000000..1a2ca32a4 --- /dev/null +++ b/src/cpu/o3/fetch.hh @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#ifndef __CPU_O3_FETCH_HH__ +#define __CPU_O3_FETCH_HH__ + +#include "arch/utility.hh" +#include "base/statistics.hh" +#include "base/timebuf.hh" +#include "cpu/pc_event.hh" +#include "mem/packet_impl.hh" +#include "mem/port.hh" +#include "sim/eventq.hh" + +/** + * DefaultFetch class handles both single threaded and SMT fetch. Its + * width is specified by the parameters; each cycle it tries to fetch + * that many instructions. It supports using a branch predictor to + * predict direction and targets. + * It supports the idling functionality of the CPU by indicating to + * the CPU when it is active and inactive. + */ +template <class Impl> +class DefaultFetch +{ + public: + /** Typedefs from Impl. */ + typedef typename Impl::CPUPol CPUPol; + typedef typename Impl::DynInst DynInst; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::O3CPU O3CPU; + typedef typename Impl::Params Params; + + /** Typedefs from the CPU policy. */ + typedef typename CPUPol::BPredUnit BPredUnit; + typedef typename CPUPol::FetchStruct FetchStruct; + typedef typename CPUPol::TimeStruct TimeStruct; + + /** Typedefs from ISA. */ + typedef TheISA::MachInst MachInst; + typedef TheISA::ExtMachInst ExtMachInst; + + /** IcachePort class for DefaultFetch. Handles doing the + * communication with the cache/memory. + */ + class IcachePort : public Port + { + protected: + /** Pointer to fetch. */ + DefaultFetch<Impl> *fetch; + + public: + /** Default constructor. */ + IcachePort(DefaultFetch<Impl> *_fetch) + : Port(_fetch->name() + "-iport"), fetch(_fetch) + { } + + protected: + /** Atomic version of receive. Panics. */ + virtual Tick recvAtomic(PacketPtr pkt); + + /** Functional version of receive. Panics. */ + virtual void recvFunctional(PacketPtr pkt); + + /** Receives status change. Other than range changing, panics. */ + virtual void recvStatusChange(Status status); + + /** Returns the address ranges of this device. */ + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + /** Timing version of receive. Handles setting fetch to the + * proper status to start fetching. */ + virtual bool recvTiming(PacketPtr pkt); + + /** Handles doing a retry of a failed fetch. */ + virtual void recvRetry(); + }; + + + public: + /** Overall fetch status. Used to determine if the CPU can + * deschedule itsef due to a lack of activity. + */ + enum FetchStatus { + Active, + Inactive + }; + + /** Individual thread status. */ + enum ThreadStatus { + Running, + Idle, + Squashing, + Blocked, + Fetching, + TrapPending, + QuiescePending, + SwitchOut, + IcacheWaitResponse, + IcacheWaitRetry, + IcacheAccessComplete + }; + + /** Fetching Policy, Add new policies here.*/ + enum FetchPriority { + SingleThread, + RoundRobin, + Branch, + IQ, + LSQ + }; + + private: + /** Fetch status. */ + FetchStatus _status; + + /** Per-thread status. */ + ThreadStatus fetchStatus[Impl::MaxThreads]; + + /** Fetch policy. */ + FetchPriority fetchPolicy; + + /** List that has the threads organized by priority. */ + std::list<unsigned> priorityList; + + public: + /** DefaultFetch constructor. */ + DefaultFetch(Params *params); + + /** Returns the name of fetch. */ + std::string name() const; + + /** Registers statistics. */ + void regStats(); + + /** Returns the icache port. */ + Port *getIcachePort() { return icachePort; } + + /** Sets CPU pointer. */ + void setCPU(O3CPU *cpu_ptr); + + /** Sets the main backwards communication time buffer pointer. */ + void setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer); + + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Sets pointer to time buffer used to communicate to the next stage. */ + void setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr); + + /** Initialize stage. */ + void initStage(); + + /** Processes cache completion event. */ + void processCacheCompletion(PacketPtr pkt); + + /** Begins the drain of the fetch stage. */ + bool drain(); + + /** Resumes execution after a drain. */ + void resume(); + + /** Tells fetch stage to prepare to be switched out. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Checks if the fetch stage is switched out. */ + bool isSwitchedOut() { return switchedOut; } + + /** Tells fetch to wake up from a quiesce instruction. */ + void wakeFromQuiesce(); + + private: + /** Changes the status of this stage to active, and indicates this + * to the CPU. + */ + inline void switchToActive(); + + /** Changes the status of this stage to inactive, and indicates + * this to the CPU. + */ + inline void switchToInactive(); + + /** + * Looks up in the branch predictor to see if the next PC should be + * either next PC+=MachInst or a branch target. + * @param next_PC Next PC variable passed in by reference. It is + * expected to be set to the current PC; it will be updated with what + * the next PC will be. + * @param next_NPC Used for ISAs which use delay slots. + * @return Whether or not a branch was predicted as taken. + */ + bool lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC, Addr &next_NPC); + + /** + * Fetches the cache line that contains fetch_PC. Returns any + * fault that happened. Puts the data into the class variable + * cacheData. + * @param fetch_PC The PC address that is being fetched from. + * @param ret_fault The fault reference that will be set to the result of + * the icache access. + * @param tid Thread id. + * @return Any fault that occured. + */ + bool fetchCacheLine(Addr fetch_PC, Fault &ret_fault, unsigned tid); + + /** Squashes a specific thread and resets the PC. */ + inline void doSquash(const Addr &new_PC, unsigned tid); + + /** Squashes a specific thread and resets the PC. Also tells the CPU to + * remove any instructions between fetch and decode that should be sqaushed. + */ + void squashFromDecode(const Addr &new_PC, const InstSeqNum &seq_num, + unsigned tid); + + /** Checks if a thread is stalled. */ + bool checkStall(unsigned tid) const; + + /** Updates overall fetch stage status; to be called at the end of each + * cycle. */ + FetchStatus updateFetchStatus(); + + public: + /** Squashes a specific thread and resets the PC. Also tells the CPU to + * remove any instructions that are not in the ROB. The source of this + * squash should be the commit stage. + */ + void squash(const Addr &new_PC, const InstSeqNum &seq_num, + bool squash_delay_slot, unsigned tid); + + /** Ticks the fetch stage, processing all inputs signals and fetching + * as many instructions as possible. + */ + void tick(); + + /** Checks all input signals and updates the status as necessary. + * @return: Returns if the status has changed due to input signals. + */ + bool checkSignalsAndUpdate(unsigned tid); + + /** Does the actual fetching of instructions and passing them on to the + * next stage. + * @param status_change fetch() sets this variable if there was a status + * change (ie switching to IcacheMissStall). + */ + void fetch(bool &status_change); + + /** Align a PC to the start of an I-cache block. */ + Addr icacheBlockAlignPC(Addr addr) + { + addr = TheISA::realPCToFetchPC(addr); + return (addr & ~(cacheBlkMask)); + } + + private: + /** Handles retrying the fetch access. */ + void recvRetry(); + + /** Returns the appropriate thread to fetch, given the fetch policy. */ + int getFetchingThread(FetchPriority &fetch_priority); + + /** Returns the appropriate thread to fetch using a round robin policy. */ + int roundRobin(); + + /** Returns the appropriate thread to fetch using the IQ count policy. */ + int iqCount(); + + /** Returns the appropriate thread to fetch using the LSQ count policy. */ + int lsqCount(); + + /** Returns the appropriate thread to fetch using the branch count policy. */ + int branchCount(); + + private: + /** Pointer to the O3CPU. */ + O3CPU *cpu; + + /** Time buffer interface. */ + TimeBuffer<TimeStruct> *timeBuffer; + + /** Wire to get decode's information from backwards time buffer. */ + typename TimeBuffer<TimeStruct>::wire fromDecode; + + /** Wire to get rename's information from backwards time buffer. */ + typename TimeBuffer<TimeStruct>::wire fromRename; + + /** Wire to get iew's information from backwards time buffer. */ + typename TimeBuffer<TimeStruct>::wire fromIEW; + + /** Wire to get commit's information from backwards time buffer. */ + typename TimeBuffer<TimeStruct>::wire fromCommit; + + /** Internal fetch instruction queue. */ + TimeBuffer<FetchStruct> *fetchQueue; + + //Might be annoying how this name is different than the queue. + /** Wire used to write any information heading to decode. */ + typename TimeBuffer<FetchStruct>::wire toDecode; + + MemObject *mem; + + /** Icache interface. */ + IcachePort *icachePort; + + /** BPredUnit. */ + BPredUnit branchPred; + + /** Per-thread fetch PC. */ + Addr PC[Impl::MaxThreads]; + + /** Per-thread next PC. */ + Addr nextPC[Impl::MaxThreads]; + + /** Per-thread next Next PC. + * This is not a real register but is used for + * architectures that use a branch-delay slot. + * (such as MIPS or Sparc) + */ + Addr nextNPC[Impl::MaxThreads]; + + /** Memory request used to access cache. */ + RequestPtr memReq[Impl::MaxThreads]; + + /** Variable that tracks if fetch has written to the time buffer this + * cycle. Used to tell CPU if there is activity this cycle. + */ + bool wroteToTimeBuffer; + + /** Tracks how many instructions has been fetched this cycle. */ + int numInst; + + /** Tracks delay slot information for threads in ISAs which use + * delay slots; + */ + struct DelaySlotInfo { + InstSeqNum delaySlotSeqNum; + InstSeqNum branchSeqNum; + int numInsts; + Addr targetAddr; + bool targetReady; + }; + + DelaySlotInfo delaySlotInfo[Impl::MaxThreads]; + + /** Source of possible stalls. */ + struct Stalls { + bool decode; + bool rename; + bool iew; + bool commit; + }; + + /** Tracks which stages are telling fetch to stall. */ + Stalls stalls[Impl::MaxThreads]; + + /** Decode to fetch delay, in ticks. */ + unsigned decodeToFetchDelay; + + /** Rename to fetch delay, in ticks. */ + unsigned renameToFetchDelay; + + /** IEW to fetch delay, in ticks. */ + unsigned iewToFetchDelay; + + /** Commit to fetch delay, in ticks. */ + unsigned commitToFetchDelay; + + /** The width of fetch in instructions. */ + unsigned fetchWidth; + + /** Is the cache blocked? If so no threads can access it. */ + bool cacheBlocked; + + /** The packet that is waiting to be retried. */ + PacketPtr retryPkt; + + /** The thread that is waiting on the cache to tell fetch to retry. */ + int retryTid; + + /** Cache block size. */ + int cacheBlkSize; + + /** Mask to get a cache block's address. */ + Addr cacheBlkMask; + + /** The cache line being fetched. */ + uint8_t *cacheData[Impl::MaxThreads]; + + /** The PC of the cacheline that has been loaded. */ + Addr cacheDataPC[Impl::MaxThreads]; + + /** Whether or not the cache data is valid. */ + bool cacheDataValid[Impl::MaxThreads]; + + /** Size of instructions. */ + int instSize; + + /** Icache stall statistics. */ + Counter lastIcacheStall[Impl::MaxThreads]; + + /** List of Active Threads */ + std::list<unsigned> *activeThreads; + + /** Number of threads. */ + unsigned numThreads; + + /** Number of threads that are actively fetching. */ + unsigned numFetchingThreads; + + /** Thread ID being fetched. */ + int threadFetched; + + /** Checks if there is an interrupt pending. If there is, fetch + * must stop once it is not fetching PAL instructions. + */ + bool interruptPending; + + /** Is there a drain pending. */ + bool drainPending; + + /** Records if fetch is switched out. */ + bool switchedOut; + + // @todo: Consider making these vectors and tracking on a per thread basis. + /** Stat for total number of cycles stalled due to an icache miss. */ + Stats::Scalar<> icacheStallCycles; + /** Stat for total number of fetched instructions. */ + Stats::Scalar<> fetchedInsts; + /** Total number of fetched branches. */ + Stats::Scalar<> fetchedBranches; + /** Stat for total number of predicted branches. */ + Stats::Scalar<> predictedBranches; + /** Stat for total number of cycles spent fetching. */ + Stats::Scalar<> fetchCycles; + /** Stat for total number of cycles spent squashing. */ + Stats::Scalar<> fetchSquashCycles; + /** Stat for total number of cycles spent blocked due to other stages in + * the pipeline. + */ + Stats::Scalar<> fetchIdleCycles; + /** Total number of cycles spent blocked. */ + Stats::Scalar<> fetchBlockedCycles; + /** Total number of cycles spent in any other state. */ + Stats::Scalar<> fetchMiscStallCycles; + /** Stat for total number of fetched cache lines. */ + Stats::Scalar<> fetchedCacheLines; + /** Total number of outstanding icache accesses that were dropped + * due to a squash. + */ + Stats::Scalar<> fetchIcacheSquashes; + /** Distribution of number of instructions fetched each cycle. */ + Stats::Distribution<> fetchNisnDist; + /** Rate of how often fetch was idle. */ + Stats::Formula idleRate; + /** Number of branch fetches per cycle. */ + Stats::Formula branchRate; + /** Number of instruction fetched per cycle. */ + Stats::Formula fetchRate; +}; + +#endif //__CPU_O3_FETCH_HH__ diff --git a/src/cpu/o3/fetch_impl.hh b/src/cpu/o3/fetch_impl.hh new file mode 100644 index 000000000..2d447bfe5 --- /dev/null +++ b/src/cpu/o3/fetch_impl.hh @@ -0,0 +1,1445 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "config/use_checker.hh" + +#include "arch/isa_traits.hh" +#include "arch/utility.hh" +#include "cpu/checker/cpu.hh" +#include "cpu/exetrace.hh" +#include "cpu/o3/fetch.hh" +#include "mem/packet.hh" +#include "mem/request.hh" +#include "sim/byteswap.hh" +#include "sim/host.hh" +#include "sim/root.hh" + +#if FULL_SYSTEM +#include "arch/tlb.hh" +#include "arch/vtophys.hh" +#include "base/remote_gdb.hh" +#include "sim/system.hh" +#endif // FULL_SYSTEM + +#include <algorithm> + +template<class Impl> +Tick +DefaultFetch<Impl>::IcachePort::recvAtomic(PacketPtr pkt) +{ + panic("DefaultFetch doesn't expect recvAtomic callback!"); + return curTick; +} + +template<class Impl> +void +DefaultFetch<Impl>::IcachePort::recvFunctional(PacketPtr pkt) +{ + panic("DefaultFetch doesn't expect recvFunctional callback!"); +} + +template<class Impl> +void +DefaultFetch<Impl>::IcachePort::recvStatusChange(Status status) +{ + if (status == RangeChange) + return; + + panic("DefaultFetch doesn't expect recvStatusChange callback!"); +} + +template<class Impl> +bool +DefaultFetch<Impl>::IcachePort::recvTiming(Packet *pkt) +{ + fetch->processCacheCompletion(pkt); + return true; +} + +template<class Impl> +void +DefaultFetch<Impl>::IcachePort::recvRetry() +{ + fetch->recvRetry(); +} + +template<class Impl> +DefaultFetch<Impl>::DefaultFetch(Params *params) + : mem(params->mem), + branchPred(params), + decodeToFetchDelay(params->decodeToFetchDelay), + renameToFetchDelay(params->renameToFetchDelay), + iewToFetchDelay(params->iewToFetchDelay), + commitToFetchDelay(params->commitToFetchDelay), + fetchWidth(params->fetchWidth), + cacheBlocked(false), + retryPkt(NULL), + retryTid(-1), + numThreads(params->numberOfThreads), + numFetchingThreads(params->smtNumFetchingThreads), + interruptPending(false), + drainPending(false), + switchedOut(false) +{ + if (numThreads > Impl::MaxThreads) + fatal("numThreads is not a valid value\n"); + + // Set fetch stage's status to inactive. + _status = Inactive; + + std::string policy = params->smtFetchPolicy; + + // Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + // Figure out fetch policy + if (policy == "singlethread") { + fetchPolicy = SingleThread; + if (numThreads > 1) + panic("Invalid Fetch Policy for a SMT workload."); + } else if (policy == "roundrobin") { + fetchPolicy = RoundRobin; + DPRINTF(Fetch, "Fetch policy set to Round Robin\n"); + } else if (policy == "branch") { + fetchPolicy = Branch; + DPRINTF(Fetch, "Fetch policy set to Branch Count\n"); + } else if (policy == "iqcount") { + fetchPolicy = IQ; + DPRINTF(Fetch, "Fetch policy set to IQ count\n"); + } else if (policy == "lsqcount") { + fetchPolicy = LSQ; + DPRINTF(Fetch, "Fetch policy set to LSQ count\n"); + } else { + fatal("Invalid Fetch Policy. Options Are: {SingleThread," + " RoundRobin,LSQcount,IQcount}\n"); + } + + // Size of cache block. + cacheBlkSize = 64; + + // Create mask to get rid of offset bits. + cacheBlkMask = (cacheBlkSize - 1); + + for (int tid=0; tid < numThreads; tid++) { + + fetchStatus[tid] = Running; + + priorityList.push_back(tid); + + memReq[tid] = NULL; + + // Create space to store a cache line. + cacheData[tid] = new uint8_t[cacheBlkSize]; + cacheDataPC[tid] = 0; + cacheDataValid[tid] = false; + + delaySlotInfo[tid].branchSeqNum = -1; + delaySlotInfo[tid].numInsts = 0; + delaySlotInfo[tid].targetAddr = 0; + delaySlotInfo[tid].targetReady = false; + + stalls[tid].decode = false; + stalls[tid].rename = false; + stalls[tid].iew = false; + stalls[tid].commit = false; + } + + // Get the size of an instruction. + instSize = sizeof(TheISA::MachInst); +} + +template <class Impl> +std::string +DefaultFetch<Impl>::name() const +{ + return cpu->name() + ".fetch"; +} + +template <class Impl> +void +DefaultFetch<Impl>::regStats() +{ + icacheStallCycles + .name(name() + ".icacheStallCycles") + .desc("Number of cycles fetch is stalled on an Icache miss") + .prereq(icacheStallCycles); + + fetchedInsts + .name(name() + ".Insts") + .desc("Number of instructions fetch has processed") + .prereq(fetchedInsts); + + fetchedBranches + .name(name() + ".Branches") + .desc("Number of branches that fetch encountered") + .prereq(fetchedBranches); + + predictedBranches + .name(name() + ".predictedBranches") + .desc("Number of branches that fetch has predicted taken") + .prereq(predictedBranches); + + fetchCycles + .name(name() + ".Cycles") + .desc("Number of cycles fetch has run and was not squashing or" + " blocked") + .prereq(fetchCycles); + + fetchSquashCycles + .name(name() + ".SquashCycles") + .desc("Number of cycles fetch has spent squashing") + .prereq(fetchSquashCycles); + + fetchIdleCycles + .name(name() + ".IdleCycles") + .desc("Number of cycles fetch was idle") + .prereq(fetchIdleCycles); + + fetchBlockedCycles + .name(name() + ".BlockedCycles") + .desc("Number of cycles fetch has spent blocked") + .prereq(fetchBlockedCycles); + + fetchedCacheLines + .name(name() + ".CacheLines") + .desc("Number of cache lines fetched") + .prereq(fetchedCacheLines); + + fetchMiscStallCycles + .name(name() + ".MiscStallCycles") + .desc("Number of cycles fetch has spent waiting on interrupts, or " + "bad addresses, or out of MSHRs") + .prereq(fetchMiscStallCycles); + + fetchIcacheSquashes + .name(name() + ".IcacheSquashes") + .desc("Number of outstanding Icache misses that were squashed") + .prereq(fetchIcacheSquashes); + + fetchNisnDist + .init(/* base value */ 0, + /* last value */ fetchWidth, + /* bucket size */ 1) + .name(name() + ".rateDist") + .desc("Number of instructions fetched each cycle (Total)") + .flags(Stats::pdf); + + idleRate + .name(name() + ".idleRate") + .desc("Percent of cycles fetch was idle") + .prereq(idleRate); + idleRate = fetchIdleCycles * 100 / cpu->numCycles; + + branchRate + .name(name() + ".branchRate") + .desc("Number of branch fetches per cycle") + .flags(Stats::total); + branchRate = fetchedBranches / cpu->numCycles; + + fetchRate + .name(name() + ".rate") + .desc("Number of inst fetches per cycle") + .flags(Stats::total); + fetchRate = fetchedInsts / cpu->numCycles; + + branchPred.regStats(); +} + +template<class Impl> +void +DefaultFetch<Impl>::setCPU(O3CPU *cpu_ptr) +{ + DPRINTF(Fetch, "Setting the CPU pointer.\n"); + cpu = cpu_ptr; + + // Name is finally available, so create the port. + icachePort = new IcachePort(this); + +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->setIcachePort(icachePort); + } +#endif + + // Schedule fetch to get the correct PC from the CPU + // scheduleFetchStartupEvent(1); + + // Fetch needs to start fetching instructions at the very beginning, + // so it must start up in active state. + switchToActive(); +} + +template<class Impl> +void +DefaultFetch<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *time_buffer) +{ + DPRINTF(Fetch, "Setting the time buffer pointer.\n"); + timeBuffer = time_buffer; + + // Create wires to get information from proper places in time buffer. + fromDecode = timeBuffer->getWire(-decodeToFetchDelay); + fromRename = timeBuffer->getWire(-renameToFetchDelay); + fromIEW = timeBuffer->getWire(-iewToFetchDelay); + fromCommit = timeBuffer->getWire(-commitToFetchDelay); +} + +template<class Impl> +void +DefaultFetch<Impl>::setActiveThreads(std::list<unsigned> *at_ptr) +{ + DPRINTF(Fetch, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template<class Impl> +void +DefaultFetch<Impl>::setFetchQueue(TimeBuffer<FetchStruct> *fq_ptr) +{ + DPRINTF(Fetch, "Setting the fetch queue pointer.\n"); + fetchQueue = fq_ptr; + + // Create wire to write information to proper place in fetch queue. + toDecode = fetchQueue->getWire(0); +} + +template<class Impl> +void +DefaultFetch<Impl>::initStage() +{ + // Setup PC and nextPC with initial state. + for (int tid = 0; tid < numThreads; tid++) { + PC[tid] = cpu->readPC(tid); + nextPC[tid] = cpu->readNextPC(tid); +#if ISA_HAS_DELAY_SLOT + nextNPC[tid] = cpu->readNextNPC(tid); +#endif + } +} + +template<class Impl> +void +DefaultFetch<Impl>::processCacheCompletion(PacketPtr pkt) +{ + unsigned tid = pkt->req->getThreadNum(); + + DPRINTF(Fetch, "[tid:%u] Waking up from cache miss.\n",tid); + + // Only change the status if it's still waiting on the icache access + // to return. + if (fetchStatus[tid] != IcacheWaitResponse || + pkt->req != memReq[tid] || + isSwitchedOut()) { + ++fetchIcacheSquashes; + delete pkt->req; + delete pkt; + return; + } + + memcpy(cacheData[tid], pkt->getPtr<uint8_t *>(), cacheBlkSize); + cacheDataValid[tid] = true; + + if (!drainPending) { + // Wake up the CPU (if it went to sleep and was waiting on + // this completion event). + cpu->wakeCPU(); + + DPRINTF(Activity, "[tid:%u] Activating fetch due to cache completion\n", + tid); + + switchToActive(); + } + + // Only switch to IcacheAccessComplete if we're not stalled as well. + if (checkStall(tid)) { + fetchStatus[tid] = Blocked; + } else { + fetchStatus[tid] = IcacheAccessComplete; + } + + // Reset the mem req to NULL. + delete pkt->req; + delete pkt; + memReq[tid] = NULL; +} + +template <class Impl> +bool +DefaultFetch<Impl>::drain() +{ + // Fetch is ready to drain at any time. + cpu->signalDrained(); + drainPending = true; + return true; +} + +template <class Impl> +void +DefaultFetch<Impl>::resume() +{ + drainPending = false; +} + +template <class Impl> +void +DefaultFetch<Impl>::switchOut() +{ + switchedOut = true; + // Branch predictor needs to have its state cleared. + branchPred.switchOut(); +} + +template <class Impl> +void +DefaultFetch<Impl>::takeOverFrom() +{ + // Reset all state + for (int i = 0; i < Impl::MaxThreads; ++i) { + stalls[i].decode = 0; + stalls[i].rename = 0; + stalls[i].iew = 0; + stalls[i].commit = 0; + PC[i] = cpu->readPC(i); + nextPC[i] = cpu->readNextPC(i); +#if ISA_HAS_DELAY_SLOT + nextNPC[i] = cpu->readNextNPC(i); + delaySlotInfo[i].branchSeqNum = -1; + delaySlotInfo[i].numInsts = 0; + delaySlotInfo[i].targetAddr = 0; + delaySlotInfo[i].targetReady = false; +#endif + fetchStatus[i] = Running; + } + numInst = 0; + wroteToTimeBuffer = false; + _status = Inactive; + switchedOut = false; + interruptPending = false; + branchPred.takeOverFrom(); +} + +template <class Impl> +void +DefaultFetch<Impl>::wakeFromQuiesce() +{ + DPRINTF(Fetch, "Waking up from quiesce\n"); + // Hopefully this is safe + // @todo: Allow other threads to wake from quiesce. + fetchStatus[0] = Running; +} + +template <class Impl> +inline void +DefaultFetch<Impl>::switchToActive() +{ + if (_status == Inactive) { + DPRINTF(Activity, "Activating stage.\n"); + + cpu->activateStage(O3CPU::FetchIdx); + + _status = Active; + } +} + +template <class Impl> +inline void +DefaultFetch<Impl>::switchToInactive() +{ + if (_status == Active) { + DPRINTF(Activity, "Deactivating stage.\n"); + + cpu->deactivateStage(O3CPU::FetchIdx); + + _status = Inactive; + } +} + +template <class Impl> +bool +DefaultFetch<Impl>::lookupAndUpdateNextPC(DynInstPtr &inst, Addr &next_PC, + Addr &next_NPC) +{ + // Do branch prediction check here. + // A bit of a misnomer...next_PC is actually the current PC until + // this function updates it. + bool predict_taken; + + if (!inst->isControl()) { +#if ISA_HAS_DELAY_SLOT + Addr cur_PC = next_PC; + next_PC = cur_PC + instSize; //next_NPC; + next_NPC = cur_PC + (2 * instSize);//next_NPC + instSize; + inst->setPredTarg(next_NPC); +#else + next_PC = next_PC + instSize; + inst->setPredTarg(next_PC); +#endif + return false; + } + + int tid = inst->threadNumber; +#if ISA_HAS_DELAY_SLOT + Addr pred_PC = next_PC; + predict_taken = branchPred.predict(inst, pred_PC, tid); + + if (predict_taken) { + DPRINTF(Fetch, "[tid:%i]: Branch predicted to be true.\n", tid); + } else { + DPRINTF(Fetch, "[tid:%i]: Branch predicted to be false.\n", tid); + } + + if (predict_taken) { + next_PC = next_NPC; + next_NPC = pred_PC; + + // Update delay slot info + ++delaySlotInfo[tid].numInsts; + delaySlotInfo[tid].targetAddr = pred_PC; + DPRINTF(Fetch, "[tid:%i]: %i delay slot inst(s) to process.\n", tid, + delaySlotInfo[tid].numInsts); + } else { // !predict_taken + if (inst->isCondDelaySlot()) { + next_PC = pred_PC; + // The delay slot is skipped here if there is on + // prediction + } else { + next_PC = next_NPC; + // No need to declare a delay slot here since + // there is no for the pred. target to jump + } + + next_NPC = next_NPC + instSize; + } +#else + predict_taken = branchPred.predict(inst, next_PC, tid); +#endif + + ++fetchedBranches; + + if (predict_taken) { + ++predictedBranches; + } + + return predict_taken; +} + +template <class Impl> +bool +DefaultFetch<Impl>::fetchCacheLine(Addr fetch_PC, Fault &ret_fault, unsigned tid) +{ + Fault fault = NoFault; + +#if FULL_SYSTEM + // Flag to say whether or not address is physical addr. + unsigned flags = cpu->inPalMode(fetch_PC) ? PHYSICAL : 0; +#else + unsigned flags = 0; +#endif // FULL_SYSTEM + + if (cacheBlocked || isSwitchedOut() || (interruptPending && flags == 0)) { + // Hold off fetch from getting new instructions when: + // Cache is blocked, or + // while an interrupt is pending and we're not in PAL mode, or + // fetch is switched out. + return false; + } + + // Align the fetch PC so it's at the start of a cache block. + fetch_PC = icacheBlockAlignPC(fetch_PC); + + // If we've already got the block, no need to try to fetch it again. + if (cacheDataValid[tid] && fetch_PC == cacheDataPC[tid]) { + return true; + } + + // Setup the memReq to do a read of the first instruction's address. + // Set the appropriate read size and flags as well. + // Build request here. + RequestPtr mem_req = new Request(tid, fetch_PC, cacheBlkSize, flags, + fetch_PC, cpu->readCpuId(), tid); + + memReq[tid] = mem_req; + + // Translate the instruction request. + fault = cpu->translateInstReq(mem_req, cpu->thread[tid]); + + // In the case of faults, the fetch stage may need to stall and wait + // for the ITB miss to be handled. + + // If translation was successful, attempt to read the first + // instruction. + if (fault == NoFault) { +#if 0 + if (cpu->system->memctrl->badaddr(memReq[tid]->paddr) || + memReq[tid]->flags & UNCACHEABLE) { + DPRINTF(Fetch, "Fetch: Bad address %#x (hopefully on a " + "misspeculating path)!", + memReq[tid]->paddr); + ret_fault = TheISA::genMachineCheckFault(); + return false; + } +#endif + + // Build packet here. + PacketPtr data_pkt = new Packet(mem_req, + Packet::ReadReq, Packet::Broadcast); + data_pkt->dataDynamicArray(new uint8_t[cacheBlkSize]); + + cacheDataPC[tid] = fetch_PC; + cacheDataValid[tid] = false; + + DPRINTF(Fetch, "Fetch: Doing instruction read.\n"); + + fetchedCacheLines++; + + // Now do the timing access to see whether or not the instruction + // exists within the cache. + if (!icachePort->sendTiming(data_pkt)) { + assert(retryPkt == NULL); + assert(retryTid == -1); + DPRINTF(Fetch, "[tid:%i] Out of MSHRs!\n", tid); + fetchStatus[tid] = IcacheWaitRetry; + retryPkt = data_pkt; + retryTid = tid; + cacheBlocked = true; + return false; + } + + DPRINTF(Fetch, "[tid:%i]: Doing cache access.\n", tid); + + lastIcacheStall[tid] = curTick; + + DPRINTF(Activity, "[tid:%i]: Activity: Waiting on I-cache " + "response.\n", tid); + + fetchStatus[tid] = IcacheWaitResponse; + } else { + delete mem_req; + memReq[tid] = NULL; + } + + ret_fault = fault; + return true; +} + +template <class Impl> +inline void +DefaultFetch<Impl>::doSquash(const Addr &new_PC, unsigned tid) +{ + DPRINTF(Fetch, "[tid:%i]: Squashing, setting PC to: %#x.\n", + tid, new_PC); + + PC[tid] = new_PC; + nextPC[tid] = new_PC + instSize; + nextNPC[tid] = new_PC + (2 * instSize); + + // Clear the icache miss if it's outstanding. + if (fetchStatus[tid] == IcacheWaitResponse) { + DPRINTF(Fetch, "[tid:%i]: Squashing outstanding Icache miss.\n", + tid); + memReq[tid] = NULL; + } + + // Get rid of the retrying packet if it was from this thread. + if (retryTid == tid) { + assert(cacheBlocked); + cacheBlocked = false; + retryTid = -1; + delete retryPkt->req; + delete retryPkt; + retryPkt = NULL; + } + + fetchStatus[tid] = Squashing; + + ++fetchSquashCycles; +} + +template<class Impl> +void +DefaultFetch<Impl>::squashFromDecode(const Addr &new_PC, + const InstSeqNum &seq_num, + unsigned tid) +{ + DPRINTF(Fetch, "[tid:%i]: Squashing from decode.\n",tid); + + doSquash(new_PC, tid); + +#if ISA_HAS_DELAY_SLOT + if (seq_num <= delaySlotInfo[tid].branchSeqNum) { + delaySlotInfo[tid].numInsts = 0; + delaySlotInfo[tid].targetAddr = 0; + delaySlotInfo[tid].targetReady = false; + } +#endif + + // Tell the CPU to remove any instructions that are in flight between + // fetch and decode. + cpu->removeInstsUntil(seq_num, tid); +} + +template<class Impl> +bool +DefaultFetch<Impl>::checkStall(unsigned tid) const +{ + bool ret_val = false; + + if (cpu->contextSwitch) { + DPRINTF(Fetch,"[tid:%i]: Stalling for a context switch.\n",tid); + ret_val = true; + } else if (stalls[tid].decode) { + DPRINTF(Fetch,"[tid:%i]: Stall from Decode stage detected.\n",tid); + ret_val = true; + } else if (stalls[tid].rename) { + DPRINTF(Fetch,"[tid:%i]: Stall from Rename stage detected.\n",tid); + ret_val = true; + } else if (stalls[tid].iew) { + DPRINTF(Fetch,"[tid:%i]: Stall from IEW stage detected.\n",tid); + ret_val = true; + } else if (stalls[tid].commit) { + DPRINTF(Fetch,"[tid:%i]: Stall from Commit stage detected.\n",tid); + ret_val = true; + } + + return ret_val; +} + +template<class Impl> +typename DefaultFetch<Impl>::FetchStatus +DefaultFetch<Impl>::updateFetchStatus() +{ + //Check Running + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + + unsigned tid = *threads++; + + if (fetchStatus[tid] == Running || + fetchStatus[tid] == Squashing || + fetchStatus[tid] == IcacheAccessComplete) { + + if (_status == Inactive) { + DPRINTF(Activity, "[tid:%i]: Activating stage.\n",tid); + + if (fetchStatus[tid] == IcacheAccessComplete) { + DPRINTF(Activity, "[tid:%i]: Activating fetch due to cache" + "completion\n",tid); + } + + cpu->activateStage(O3CPU::FetchIdx); + } + + return Active; + } + } + + // Stage is switching from active to inactive, notify CPU of it. + if (_status == Active) { + DPRINTF(Activity, "Deactivating stage.\n"); + + cpu->deactivateStage(O3CPU::FetchIdx); + } + + return Inactive; +} + +template <class Impl> +void +DefaultFetch<Impl>::squash(const Addr &new_PC, const InstSeqNum &seq_num, + bool squash_delay_slot, unsigned tid) +{ + DPRINTF(Fetch, "[tid:%u]: Squash from commit.\n",tid); + + doSquash(new_PC, tid); + +#if ISA_HAS_DELAY_SLOT + if (seq_num <= delaySlotInfo[tid].branchSeqNum) { + delaySlotInfo[tid].numInsts = 0; + delaySlotInfo[tid].targetAddr = 0; + delaySlotInfo[tid].targetReady = false; + } + + // Tell the CPU to remove any instructions that are not in the ROB. + cpu->removeInstsNotInROB(tid, squash_delay_slot, seq_num); +#else + // Tell the CPU to remove any instructions that are not in the ROB. + cpu->removeInstsNotInROB(tid, true, 0); +#endif +} + +template <class Impl> +void +DefaultFetch<Impl>::tick() +{ + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + bool status_change = false; + + wroteToTimeBuffer = false; + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + // Check the signals for each thread to determine the proper status + // for each thread. + bool updated_status = checkSignalsAndUpdate(tid); + status_change = status_change || updated_status; + } + + DPRINTF(Fetch, "Running stage.\n"); + + // Reset the number of the instruction we're fetching. + numInst = 0; + +#if FULL_SYSTEM + if (fromCommit->commitInfo[0].interruptPending) { + interruptPending = true; + } + + if (fromCommit->commitInfo[0].clearInterrupt) { + interruptPending = false; + } +#endif + + for (threadFetched = 0; threadFetched < numFetchingThreads; + threadFetched++) { + // Fetch each of the actively fetching threads. + fetch(status_change); + } + + // Record number of instructions fetched this cycle for distribution. + fetchNisnDist.sample(numInst); + + if (status_change) { + // Change the fetch stage status if there was a status change. + _status = updateFetchStatus(); + } + + // If there was activity this cycle, inform the CPU of it. + if (wroteToTimeBuffer || cpu->contextSwitch) { + DPRINTF(Activity, "Activity this cycle.\n"); + + cpu->activityThisCycle(); + } +} + +template <class Impl> +bool +DefaultFetch<Impl>::checkSignalsAndUpdate(unsigned tid) +{ + // Update the per thread stall statuses. + if (fromDecode->decodeBlock[tid]) { + stalls[tid].decode = true; + } + + if (fromDecode->decodeUnblock[tid]) { + assert(stalls[tid].decode); + assert(!fromDecode->decodeBlock[tid]); + stalls[tid].decode = false; + } + + if (fromRename->renameBlock[tid]) { + stalls[tid].rename = true; + } + + if (fromRename->renameUnblock[tid]) { + assert(stalls[tid].rename); + assert(!fromRename->renameBlock[tid]); + stalls[tid].rename = false; + } + + if (fromIEW->iewBlock[tid]) { + stalls[tid].iew = true; + } + + if (fromIEW->iewUnblock[tid]) { + assert(stalls[tid].iew); + assert(!fromIEW->iewBlock[tid]); + stalls[tid].iew = false; + } + + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } + + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + assert(!fromCommit->commitBlock[tid]); + stalls[tid].commit = false; + } + + // Check squash signals from commit. + if (fromCommit->commitInfo[tid].squash) { + + DPRINTF(Fetch, "[tid:%u]: Squashing instructions due to squash " + "from commit.\n",tid); + +#if ISA_HAS_DELAY_SLOT + InstSeqNum doneSeqNum = fromCommit->commitInfo[tid].bdelayDoneSeqNum; +#else + InstSeqNum doneSeqNum = fromCommit->commitInfo[tid].doneSeqNum; +#endif + // In any case, squash. + squash(fromCommit->commitInfo[tid].nextPC, + doneSeqNum, + fromCommit->commitInfo[tid].squashDelaySlot, + tid); + + // Also check if there's a mispredict that happened. + if (fromCommit->commitInfo[tid].branchMispredict) { + branchPred.squash(fromCommit->commitInfo[tid].doneSeqNum, + fromCommit->commitInfo[tid].nextPC, + fromCommit->commitInfo[tid].branchTaken, + tid); + } else { + branchPred.squash(fromCommit->commitInfo[tid].doneSeqNum, + tid); + } + + return true; + } else if (fromCommit->commitInfo[tid].doneSeqNum) { + // Update the branch predictor if it wasn't a squashed instruction + // that was broadcasted. + branchPred.update(fromCommit->commitInfo[tid].doneSeqNum, tid); + } + + // Check ROB squash signals from commit. + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(Fetch, "[tid:%u]: ROB is still squashing.\n", tid); + + // Continue to squash. + fetchStatus[tid] = Squashing; + + return true; + } + + // Check squash signals from decode. + if (fromDecode->decodeInfo[tid].squash) { + DPRINTF(Fetch, "[tid:%u]: Squashing instructions due to squash " + "from decode.\n",tid); + + // Update the branch predictor. + if (fromDecode->decodeInfo[tid].branchMispredict) { + branchPred.squash(fromDecode->decodeInfo[tid].doneSeqNum, + fromDecode->decodeInfo[tid].nextPC, + fromDecode->decodeInfo[tid].branchTaken, + tid); + } else { + branchPred.squash(fromDecode->decodeInfo[tid].doneSeqNum, + tid); + } + + if (fetchStatus[tid] != Squashing) { + +#if ISA_HAS_DELAY_SLOT + InstSeqNum doneSeqNum = fromDecode->decodeInfo[tid].bdelayDoneSeqNum; +#else + InstSeqNum doneSeqNum = fromDecode->decodeInfo[tid].doneSeqNum; +#endif + // Squash unless we're already squashing + squashFromDecode(fromDecode->decodeInfo[tid].nextPC, + doneSeqNum, + tid); + + return true; + } + } + + if (checkStall(tid) && fetchStatus[tid] != IcacheWaitResponse) { + DPRINTF(Fetch, "[tid:%i]: Setting to blocked\n",tid); + + fetchStatus[tid] = Blocked; + + return true; + } + + if (fetchStatus[tid] == Blocked || + fetchStatus[tid] == Squashing) { + // Switch status to running if fetch isn't being told to block or + // squash this cycle. + DPRINTF(Fetch, "[tid:%i]: Done squashing, switching to running.\n", + tid); + + fetchStatus[tid] = Running; + + return true; + } + + // If we've reached this point, we have not gotten any signals that + // cause fetch to change its status. Fetch remains the same as before. + return false; +} + +template<class Impl> +void +DefaultFetch<Impl>::fetch(bool &status_change) +{ + ////////////////////////////////////////// + // Start actual fetch + ////////////////////////////////////////// + int tid = getFetchingThread(fetchPolicy); + + if (tid == -1 || drainPending) { + DPRINTF(Fetch,"There are no more threads available to fetch from.\n"); + + // Breaks looping condition in tick() + threadFetched = numFetchingThreads; + return; + } + + DPRINTF(Fetch, "Attempting to fetch from [tid:%i]\n", tid); + + // The current PC. + Addr &fetch_PC = PC[tid]; + + // Fault code for memory access. + Fault fault = NoFault; + + // If returning from the delay of a cache miss, then update the status + // to running, otherwise do the cache access. Possibly move this up + // to tick() function. + if (fetchStatus[tid] == IcacheAccessComplete) { + DPRINTF(Fetch, "[tid:%i]: Icache miss is complete.\n", + tid); + + fetchStatus[tid] = Running; + status_change = true; + } else if (fetchStatus[tid] == Running) { + DPRINTF(Fetch, "[tid:%i]: Attempting to translate and read " + "instruction, starting at PC %08p.\n", + tid, fetch_PC); + + bool fetch_success = fetchCacheLine(fetch_PC, fault, tid); + if (!fetch_success) { + if (cacheBlocked) { + ++icacheStallCycles; + } else { + ++fetchMiscStallCycles; + } + return; + } + } else { + if (fetchStatus[tid] == Idle) { + ++fetchIdleCycles; + } else if (fetchStatus[tid] == Blocked) { + ++fetchBlockedCycles; + } else if (fetchStatus[tid] == Squashing) { + ++fetchSquashCycles; + } else if (fetchStatus[tid] == IcacheWaitResponse) { + ++icacheStallCycles; + } + + // Status is Idle, Squashing, Blocked, or IcacheWaitResponse, so + // fetch should do nothing. + return; + } + + ++fetchCycles; + + // If we had a stall due to an icache miss, then return. + if (fetchStatus[tid] == IcacheWaitResponse) { + ++icacheStallCycles; + status_change = true; + return; + } + + Addr next_PC = fetch_PC; + Addr next_NPC = next_PC + instSize; + InstSeqNum inst_seq; + MachInst inst; + ExtMachInst ext_inst; + // @todo: Fix this hack. + unsigned offset = (fetch_PC & cacheBlkMask) & ~3; + + if (fault == NoFault) { + // If the read of the first instruction was successful, then grab the + // instructions from the rest of the cache line and put them into the + // queue heading to decode. + + DPRINTF(Fetch, "[tid:%i]: Adding instructions to queue to " + "decode.\n",tid); + + // Need to keep track of whether or not a predicted branch + // ended this fetch block. + bool predicted_branch = false; + + // Need to keep track of whether or not a delay slot + // instruction has been fetched + + for (; + offset < cacheBlkSize && + numInst < fetchWidth && + (!predicted_branch || delaySlotInfo[tid].numInsts > 0); + ++numInst) { + + // Get a sequence number. + inst_seq = cpu->getAndIncrementInstSeq(); + + // Make sure this is a valid index. + assert(offset <= cacheBlkSize - instSize); + + // Get the instruction from the array of the cache line. + inst = TheISA::gtoh(*reinterpret_cast<TheISA::MachInst *> + (&cacheData[tid][offset])); + + ext_inst = TheISA::makeExtMI(inst, fetch_PC); + + // Create a new DynInst from the instruction fetched. + DynInstPtr instruction = new DynInst(ext_inst, fetch_PC, + next_PC, + inst_seq, cpu); + instruction->setTid(tid); + + instruction->setASID(tid); + + instruction->setThreadState(cpu->thread[tid]); + + DPRINTF(Fetch, "[tid:%i]: Instruction PC %#x created " + "[sn:%lli]\n", + tid, instruction->readPC(), inst_seq); + + DPRINTF(Fetch, "[tid:%i]: Instruction is: %s\n", + tid, instruction->staticInst->disassemble(fetch_PC)); + + instruction->traceData = + Trace::getInstRecord(curTick, cpu->tcBase(tid), + instruction->staticInst, + instruction->readPC()); + + predicted_branch = lookupAndUpdateNextPC(instruction, next_PC, + next_NPC); + + // Add instruction to the CPU's list of instructions. + instruction->setInstListIt(cpu->addInst(instruction)); + + // Write the instruction to the first slot in the queue + // that heads to decode. + toDecode->insts[numInst] = instruction; + + toDecode->size++; + + // Increment stat of fetched instructions. + ++fetchedInsts; + + // Move to the next instruction, unless we have a branch. + fetch_PC = next_PC; + + if (instruction->isQuiesce()) { +// warn("%lli: Quiesce instruction encountered, halting fetch!", +// curTick); + fetchStatus[tid] = QuiescePending; + ++numInst; + status_change = true; + break; + } + + offset += instSize; + +#if ISA_HAS_DELAY_SLOT + if (predicted_branch) { + delaySlotInfo[tid].branchSeqNum = inst_seq; + + DPRINTF(Fetch, "[tid:%i]: Delay slot branch set to [sn:%i]\n", + tid, inst_seq); + continue; + } else if (delaySlotInfo[tid].numInsts > 0) { + --delaySlotInfo[tid].numInsts; + + // It's OK to set PC to target of branch + if (delaySlotInfo[tid].numInsts == 0) { + delaySlotInfo[tid].targetReady = true; + + // Break the looping condition + predicted_branch = true; + } + + DPRINTF(Fetch, "[tid:%i]: %i delay slot inst(s) left to" + " process.\n", tid, delaySlotInfo[tid].numInsts); + } +#endif + } + + if (offset >= cacheBlkSize) { + DPRINTF(Fetch, "[tid:%i]: Done fetching, reached the end of cache " + "block.\n", tid); + } else if (numInst >= fetchWidth) { + DPRINTF(Fetch, "[tid:%i]: Done fetching, reached fetch bandwidth " + "for this cycle.\n", tid); + } else if (predicted_branch && delaySlotInfo[tid].numInsts <= 0) { + DPRINTF(Fetch, "[tid:%i]: Done fetching, predicted branch " + "instruction encountered.\n", tid); + } + } + + if (numInst > 0) { + wroteToTimeBuffer = true; + } + + // Now that fetching is completed, update the PC to signify what the next + // cycle will be. + if (fault == NoFault) { +#if ISA_HAS_DELAY_SLOT + if (delaySlotInfo[tid].targetReady && + delaySlotInfo[tid].numInsts == 0) { + // Set PC to target + PC[tid] = delaySlotInfo[tid].targetAddr; //next_PC + nextPC[tid] = next_PC + instSize; //next_NPC + nextNPC[tid] = next_PC + (2 * instSize); + + delaySlotInfo[tid].targetReady = false; + } else { + PC[tid] = next_PC; + nextPC[tid] = next_NPC; + nextNPC[tid] = next_NPC + instSize; + } + + DPRINTF(Fetch, "[tid:%i]: Setting PC to %08p.\n", tid, PC[tid]); +#else + DPRINTF(Fetch, "[tid:%i]: Setting PC to %08p.\n",tid, next_PC); + PC[tid] = next_PC; + nextPC[tid] = next_PC + instSize; +#endif + } else { + // We shouldn't be in an icache miss and also have a fault (an ITB + // miss) + if (fetchStatus[tid] == IcacheWaitResponse) { + panic("Fetch should have exited prior to this!"); + } + + // Send the fault to commit. This thread will not do anything + // until commit handles the fault. The only other way it can + // wake up is if a squash comes along and changes the PC. +#if FULL_SYSTEM + assert(numInst != fetchWidth); + // Get a sequence number. + inst_seq = cpu->getAndIncrementInstSeq(); + // We will use a nop in order to carry the fault. + ext_inst = TheISA::NoopMachInst; + + // Create a new DynInst from the dummy nop. + DynInstPtr instruction = new DynInst(ext_inst, fetch_PC, + next_PC, + inst_seq, cpu); + instruction->setPredTarg(next_PC + instSize); + instruction->setTid(tid); + + instruction->setASID(tid); + + instruction->setThreadState(cpu->thread[tid]); + + instruction->traceData = NULL; + + instruction->setInstListIt(cpu->addInst(instruction)); + + instruction->fault = fault; + + toDecode->insts[numInst] = instruction; + toDecode->size++; + + DPRINTF(Fetch, "[tid:%i]: Blocked, need to handle the trap.\n",tid); + + fetchStatus[tid] = TrapPending; + status_change = true; + +// warn("%lli fault (%d) detected @ PC %08p", curTick, fault, PC[tid]); +#else // !FULL_SYSTEM + warn("cycle %lli: fault (%s) detected @ PC %08p", curTick, fault->name(), PC[tid]); +#endif // FULL_SYSTEM + } +} + +template<class Impl> +void +DefaultFetch<Impl>::recvRetry() +{ + assert(cacheBlocked); + if (retryPkt != NULL) { + assert(retryTid != -1); + assert(fetchStatus[retryTid] == IcacheWaitRetry); + + if (icachePort->sendTiming(retryPkt)) { + fetchStatus[retryTid] = IcacheWaitResponse; + retryPkt = NULL; + retryTid = -1; + cacheBlocked = false; + } + } else { + assert(retryTid == -1); + // Access has been squashed since it was sent out. Just clear + // the cache being blocked. + cacheBlocked = false; + } +} + +/////////////////////////////////////// +// // +// SMT FETCH POLICY MAINTAINED HERE // +// // +/////////////////////////////////////// +template<class Impl> +int +DefaultFetch<Impl>::getFetchingThread(FetchPriority &fetch_priority) +{ + if (numThreads > 1) { + switch (fetch_priority) { + + case SingleThread: + return 0; + + case RoundRobin: + return roundRobin(); + + case IQ: + return iqCount(); + + case LSQ: + return lsqCount(); + + case Branch: + return branchCount(); + + default: + return -1; + } + } else { + int tid = *((*activeThreads).begin()); + + if (fetchStatus[tid] == Running || + fetchStatus[tid] == IcacheAccessComplete || + fetchStatus[tid] == Idle) { + return tid; + } else { + return -1; + } + } + +} + + +template<class Impl> +int +DefaultFetch<Impl>::roundRobin() +{ + std::list<unsigned>::iterator pri_iter = priorityList.begin(); + std::list<unsigned>::iterator end = priorityList.end(); + + int high_pri; + + while (pri_iter != end) { + high_pri = *pri_iter; + + assert(high_pri <= numThreads); + + if (fetchStatus[high_pri] == Running || + fetchStatus[high_pri] == IcacheAccessComplete || + fetchStatus[high_pri] == Idle) { + + priorityList.erase(pri_iter); + priorityList.push_back(high_pri); + + return high_pri; + } + + pri_iter++; + } + + return -1; +} + +template<class Impl> +int +DefaultFetch<Impl>::iqCount() +{ + std::priority_queue<unsigned> PQ; + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + PQ.push(fromIEW->iewInfo[tid].iqCount); + } + + while (!PQ.empty()) { + + unsigned high_pri = PQ.top(); + + if (fetchStatus[high_pri] == Running || + fetchStatus[high_pri] == IcacheAccessComplete || + fetchStatus[high_pri] == Idle) + return high_pri; + else + PQ.pop(); + + } + + return -1; +} + +template<class Impl> +int +DefaultFetch<Impl>::lsqCount() +{ + std::priority_queue<unsigned> PQ; + + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + PQ.push(fromIEW->iewInfo[tid].ldstqCount); + } + + while (!PQ.empty()) { + + unsigned high_pri = PQ.top(); + + if (fetchStatus[high_pri] == Running || + fetchStatus[high_pri] == IcacheAccessComplete || + fetchStatus[high_pri] == Idle) + return high_pri; + else + PQ.pop(); + + } + + return -1; +} + +template<class Impl> +int +DefaultFetch<Impl>::branchCount() +{ + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + panic("Branch Count Fetch policy unimplemented\n"); + return *threads; +} diff --git a/src/cpu/o3/free_list.cc b/src/cpu/o3/free_list.cc new file mode 100644 index 000000000..ae651398b --- /dev/null +++ b/src/cpu/o3/free_list.cc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "base/trace.hh" + +#include "cpu/o3/free_list.hh" + +SimpleFreeList::SimpleFreeList(unsigned activeThreads, + unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs) + : numLogicalIntRegs(_numLogicalIntRegs), + numPhysicalIntRegs(_numPhysicalIntRegs), + numLogicalFloatRegs(_numLogicalFloatRegs), + numPhysicalFloatRegs(_numPhysicalFloatRegs), + numPhysicalRegs(numPhysicalIntRegs + numPhysicalFloatRegs) +{ + DPRINTF(FreeList, "Creating new free list object.\n"); + + // Put all of the extra physical registers onto the free list. This + // means excluding all of the base logical registers. + for (PhysRegIndex i = numLogicalIntRegs * activeThreads; + i < numPhysicalIntRegs; ++i) + { + freeIntRegs.push(i); + } + + // Put all of the extra physical registers onto the free list. This + // means excluding all of the base logical registers. Because the + // float registers' indices start where the physical registers end, + // some math must be done to determine where the free registers start. + PhysRegIndex i = numPhysicalIntRegs + (numLogicalFloatRegs * activeThreads); + + for ( ; i < numPhysicalRegs; ++i) + { + freeFloatRegs.push(i); + } +} + +std::string +SimpleFreeList::name() const +{ + return "cpu.freelist"; +} diff --git a/src/cpu/o3/free_list.hh b/src/cpu/o3/free_list.hh new file mode 100644 index 000000000..c669b0b34 --- /dev/null +++ b/src/cpu/o3/free_list.hh @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_FREE_LIST_HH__ +#define __CPU_O3_FREE_LIST_HH__ + +#include <iostream> +#include <queue> + +#include "arch/isa_traits.hh" +#include "base/misc.hh" +#include "base/trace.hh" +#include "base/traceflags.hh" +#include "cpu/o3/comm.hh" + +/** + * FreeList class that simply holds the list of free integer and floating + * point registers. Can request for a free register of either type, and + * also send back free registers of either type. This is a very simple + * class, but it should be sufficient for most implementations. Like all + * other classes, it assumes that the indices for the floating point + * registers starts after the integer registers end. Hence the variable + * numPhysicalIntRegs is logically equivalent to the baseFP dependency. + * Note that while this most likely should be called FreeList, the name + * "FreeList" is used in a typedef within the CPU Policy, and therefore no + * class can be named simply "FreeList". + * @todo: Give a better name to the base FP dependency. + */ +class SimpleFreeList +{ + private: + /** The list of free integer registers. */ + std::queue<PhysRegIndex> freeIntRegs; + + /** The list of free floating point registers. */ + std::queue<PhysRegIndex> freeFloatRegs; + + /** Number of logical integer registers. */ + int numLogicalIntRegs; + + /** Number of physical integer registers. */ + int numPhysicalIntRegs; + + /** Number of logical floating point registers. */ + int numLogicalFloatRegs; + + /** Number of physical floating point registers. */ + int numPhysicalFloatRegs; + + /** Total number of physical registers. */ + int numPhysicalRegs; + + public: + /** Constructs a free list. + * @param activeThreads Number of active threads. + * @param _numLogicalIntRegs Number of logical integer registers. + * @param _numPhysicalIntRegs Number of physical integer registers. + * @param _numLogicalFloatRegs Number of logical fp registers. + * @param _numPhysicalFloatRegs Number of physical fp registers. + */ + SimpleFreeList(unsigned activeThreads, + unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs); + + /** Gives the name of the freelist. */ + std::string name() const; + + /** Gets a free integer register. */ + inline PhysRegIndex getIntReg(); + + /** Gets a free fp register. */ + inline PhysRegIndex getFloatReg(); + + /** Adds a register back to the free list. */ + inline void addReg(PhysRegIndex freed_reg); + + /** Adds an integer register back to the free list. */ + inline void addIntReg(PhysRegIndex freed_reg); + + /** Adds a fp register back to the free list. */ + inline void addFloatReg(PhysRegIndex freed_reg); + + /** Checks if there are any free integer registers. */ + bool hasFreeIntRegs() + { return !freeIntRegs.empty(); } + + /** Checks if there are any free fp registers. */ + bool hasFreeFloatRegs() + { return !freeFloatRegs.empty(); } + + /** Returns the number of free integer registers. */ + int numFreeIntRegs() + { return freeIntRegs.size(); } + + /** Returns the number of free fp registers. */ + int numFreeFloatRegs() + { return freeFloatRegs.size(); } +}; + +inline PhysRegIndex +SimpleFreeList::getIntReg() +{ + DPRINTF(FreeList, "Trying to get free integer register.\n"); + + if (freeIntRegs.empty()) { + panic("No free integer registers!"); + } + + PhysRegIndex free_reg = freeIntRegs.front(); + + freeIntRegs.pop(); + + return(free_reg); +} + +inline PhysRegIndex +SimpleFreeList::getFloatReg() +{ + DPRINTF(FreeList, "Trying to get free float register.\n"); + + if (freeFloatRegs.empty()) { + panic("No free integer registers!"); + } + + PhysRegIndex free_reg = freeFloatRegs.front(); + + freeFloatRegs.pop(); + + return(free_reg); +} + +inline void +SimpleFreeList::addReg(PhysRegIndex freed_reg) +{ + DPRINTF(FreeList,"Freeing register %i.\n", freed_reg); + //Might want to add in a check for whether or not this register is + //already in there. A bit vector or something similar would be useful. + if (freed_reg < numPhysicalIntRegs) { + if (freed_reg != TheISA::ZeroReg) + freeIntRegs.push(freed_reg); + } else if (freed_reg < numPhysicalRegs) { + if (freed_reg != (TheISA::ZeroReg + numPhysicalIntRegs)) + freeFloatRegs.push(freed_reg); + } +} + +inline void +SimpleFreeList::addIntReg(PhysRegIndex freed_reg) +{ + DPRINTF(FreeList,"Freeing int register %i.\n", freed_reg); + + freeIntRegs.push(freed_reg); +} + +inline void +SimpleFreeList::addFloatReg(PhysRegIndex freed_reg) +{ + DPRINTF(FreeList,"Freeing float register %i.\n", freed_reg); + + freeFloatRegs.push(freed_reg); +} + +#endif // __CPU_O3_FREE_LIST_HH__ diff --git a/src/cpu/o3/fu_pool.cc b/src/cpu/o3/fu_pool.cc new file mode 100644 index 000000000..42e329aca --- /dev/null +++ b/src/cpu/o3/fu_pool.cc @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include <sstream> + +#include "cpu/o3/fu_pool.hh" +#include "cpu/func_unit.hh" +#include "sim/builder.hh" + +using namespace std; + +//////////////////////////////////////////////////////////////////////////// +// +// A pool of function units +// + +inline void +FUPool::FUIdxQueue::addFU(int fu_idx) +{ + funcUnitsIdx.push_back(fu_idx); + ++size; +} + +inline int +FUPool::FUIdxQueue::getFU() +{ + int retval = funcUnitsIdx[idx++]; + + if (idx == size) + idx = 0; + + return retval; +} + +FUPool::~FUPool() +{ + fuListIterator i = funcUnits.begin(); + fuListIterator end = funcUnits.end(); + for (; i != end; ++i) + delete *i; +} + + +// Constructor +FUPool::FUPool(string name, vector<FUDesc *> paramList) + : SimObject(name) +{ + numFU = 0; + + funcUnits.clear(); + + for (int i = 0; i < Num_OpClasses; ++i) { + maxOpLatencies[i] = 0; + maxIssueLatencies[i] = 0; + } + + // + // Iterate through the list of FUDescData structures + // + for (FUDDiterator i = paramList.begin(); i != paramList.end(); ++i) { + + // + // Don't bother with this if we're not going to create any FU's + // + if ((*i)->number) { + // + // Create the FuncUnit object from this structure + // - add the capabilities listed in the FU's operation + // description + // + // We create the first unit, then duplicate it as needed + // + FuncUnit *fu = new FuncUnit; + + OPDDiterator j = (*i)->opDescList.begin(); + OPDDiterator end = (*i)->opDescList.end(); + for (; j != end; ++j) { + // indicate that this pool has this capability + capabilityList.set((*j)->opClass); + + // Add each of the FU's that will have this capability to the + // appropriate queue. + for (int k = 0; k < (*i)->number; ++k) + fuPerCapList[(*j)->opClass].addFU(numFU + k); + + // indicate that this FU has the capability + fu->addCapability((*j)->opClass, (*j)->opLat, (*j)->issueLat); + + if ((*j)->opLat > maxOpLatencies[(*j)->opClass]) + maxOpLatencies[(*j)->opClass] = (*j)->opLat; + + if ((*j)->issueLat > maxIssueLatencies[(*j)->opClass]) + maxIssueLatencies[(*j)->opClass] = (*j)->issueLat; + } + + numFU++; + + // Add the appropriate number of copies of this FU to the list + ostringstream s; + + s << (*i)->name() << "(0)"; + fu->name = s.str(); + funcUnits.push_back(fu); + + for (int c = 1; c < (*i)->number; ++c) { + ostringstream s; + numFU++; + FuncUnit *fu2 = new FuncUnit(*fu); + + s << (*i)->name() << "(" << c << ")"; + fu2->name = s.str(); + funcUnits.push_back(fu2); + } + } + } + + unitBusy.resize(numFU); + + for (int i = 0; i < numFU; i++) { + unitBusy[i] = false; + } +} + +void +FUPool::annotateMemoryUnits(unsigned hit_latency) +{ + maxOpLatencies[MemReadOp] = hit_latency; + + fuListIterator i = funcUnits.begin(); + fuListIterator iend = funcUnits.end(); + for (; i != iend; ++i) { + if ((*i)->provides(MemReadOp)) + (*i)->opLatency(MemReadOp) = hit_latency; + + if ((*i)->provides(MemWriteOp)) + (*i)->opLatency(MemWriteOp) = hit_latency; + } +} + +int +FUPool::getUnit(OpClass capability) +{ + // If this pool doesn't have the specified capability, + // return this information to the caller + if (!capabilityList[capability]) + return -2; + + int fu_idx = fuPerCapList[capability].getFU(); + int start_idx = fu_idx; + + // Iterate through the circular queue if needed, stopping if we've reached + // the first element again. + while (unitBusy[fu_idx]) { + fu_idx = fuPerCapList[capability].getFU(); + if (fu_idx == start_idx) { + // No FU available + return -1; + } + } + + assert(fu_idx < numFU); + + unitBusy[fu_idx] = true; + + return fu_idx; +} + +void +FUPool::freeUnitNextCycle(int fu_idx) +{ + assert(unitBusy[fu_idx]); + unitsToBeFreed.push_back(fu_idx); +} + +void +FUPool::processFreeUnits() +{ + while (!unitsToBeFreed.empty()) { + int fu_idx = unitsToBeFreed.back(); + unitsToBeFreed.pop_back(); + + assert(unitBusy[fu_idx]); + + unitBusy[fu_idx] = false; + } +} + +void +FUPool::dump() +{ + cout << "Function Unit Pool (" << name() << ")\n"; + cout << "======================================\n"; + cout << "Free List:\n"; + + for (int i = 0; i < numFU; ++i) { + if (unitBusy[i]) { + continue; + } + + cout << " [" << i << "] : "; + + cout << funcUnits[i]->name << " "; + + cout << "\n"; + } + + cout << "======================================\n"; + cout << "Busy List:\n"; + for (int i = 0; i < numFU; ++i) { + if (!unitBusy[i]) { + continue; + } + + cout << " [" << i << "] : "; + + cout << funcUnits[i]->name << " "; + + cout << "\n"; + } +} + +void +FUPool::switchOut() +{ +} + +void +FUPool::takeOverFrom() +{ + for (int i = 0; i < numFU; i++) { + unitBusy[i] = false; + } + unitsToBeFreed.clear(); +} + +// + +//////////////////////////////////////////////////////////////////////////// +// +// The SimObjects we use to get the FU information into the simulator +// +//////////////////////////////////////////////////////////////////////////// + +// +// FUPool - Contails a list of FUDesc objects to make available +// + +// +// The FuPool object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(FUPool) + + SimObjectVectorParam<FUDesc *> FUList; + +END_DECLARE_SIM_OBJECT_PARAMS(FUPool) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(FUPool) + + INIT_PARAM(FUList, "list of FU's for this pool") + +END_INIT_SIM_OBJECT_PARAMS(FUPool) + + +CREATE_SIM_OBJECT(FUPool) +{ + return new FUPool(getInstanceName(), FUList); +} + +REGISTER_SIM_OBJECT("FUPool", FUPool) + diff --git a/src/cpu/o3/fu_pool.hh b/src/cpu/o3/fu_pool.hh new file mode 100644 index 000000000..52d83f056 --- /dev/null +++ b/src/cpu/o3/fu_pool.hh @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_FU_POOL_HH__ +#define __CPU_O3_FU_POOL_HH__ + +#include <bitset> +#include <list> +#include <string> +#include <vector> + +#include "base/sched_list.hh" +#include "cpu/op_class.hh" +#include "sim/sim_object.hh" + +class FUDesc; +class FuncUnit; + +/** + * Pool of FU's, specific to the new CPU model. The old FU pool had lists of + * free units and busy units, and whenever a FU was needed it would iterate + * through the free units to find a FU that provided the capability. This pool + * has lists of units specific to each of the capabilities, and whenever a FU + * is needed, it iterates through that list to find a free unit. The previous + * FU pool would have to be ticked each cycle to update which units became + * free. This FU pool lets the IEW stage handle freeing units, which frees + * them as their scheduled execution events complete. This limits units in this + * model to either have identical issue and op latencies, or 1 cycle issue + * latencies. + */ +class FUPool : public SimObject +{ + private: + /** Maximum op execution latencies, per op class. */ + unsigned maxOpLatencies[Num_OpClasses]; + /** Maximum issue latencies, per op class. */ + unsigned maxIssueLatencies[Num_OpClasses]; + + /** Bitvector listing capabilities of this FU pool. */ + std::bitset<Num_OpClasses> capabilityList; + + /** Bitvector listing which FUs are busy. */ + std::vector<bool> unitBusy; + + /** List of units to be freed at the end of this cycle. */ + std::vector<int> unitsToBeFreed; + + /** + * Class that implements a circular queue to hold FU indices. The hope is + * that FUs that have been just used will be moved to the end of the queue + * by iterating through it, thus leaving free units at the head of the + * queue. + */ + class FUIdxQueue { + public: + /** Constructs a circular queue of FU indices. */ + FUIdxQueue() + : idx(0), size(0) + { } + + /** Adds a FU to the queue. */ + inline void addFU(int fu_idx); + + /** Returns the index of the FU at the head of the queue, and changes + * the index to the next element. + */ + inline int getFU(); + + private: + /** Circular queue index. */ + int idx; + + /** Size of the queue. */ + int size; + + /** Queue of FU indices. */ + std::vector<int> funcUnitsIdx; + }; + + /** Per op class queues of FUs that provide that capability. */ + FUIdxQueue fuPerCapList[Num_OpClasses]; + + /** Number of FUs. */ + int numFU; + + /** Functional units. */ + std::vector<FuncUnit *> funcUnits; + + typedef std::vector<FuncUnit *>::iterator fuListIterator; + + public: + + /** Constructs a FU pool. */ + FUPool(std::string name, std::vector<FUDesc *> l); + ~FUPool(); + + /** Annotates units that provide memory operations. Included only because + * old FU pool provided this function. + */ + void annotateMemoryUnits(unsigned hit_latency); + + /** + * Gets a FU providing the requested capability. Will mark the unit as busy, + * but leaves the freeing of the unit up to the IEW stage. + * @param capability The capability requested. + * @return Returns -2 if the FU pool does not have the capability, -1 if + * there is no free FU, and the FU's index otherwise. + */ + int getUnit(OpClass capability); + + /** Frees a FU at the end of this cycle. */ + void freeUnitNextCycle(int fu_idx); + + /** Frees all FUs on the list. */ + void processFreeUnits(); + + /** Returns the total number of FUs. */ + int size() { return numFU; } + + /** Debugging function used to dump FU information. */ + void dump(); + + /** Returns the operation execution latency of the given capability. */ + unsigned getOpLatency(OpClass capability) { + return maxOpLatencies[capability]; + } + + /** Returns the issue latency of the given capability. */ + unsigned getIssueLatency(OpClass capability) { + return maxIssueLatencies[capability]; + } + + /** Switches out functional unit pool. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); +}; + +#endif // __CPU_O3_FU_POOL_HH__ diff --git a/src/cpu/o3/iew.cc b/src/cpu/o3/iew.cc new file mode 100644 index 000000000..f99be7fe0 --- /dev/null +++ b/src/cpu/o3/iew.cc @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/iew_impl.hh" +#include "cpu/o3/inst_queue.hh" + +template class DefaultIEW<O3CPUImpl>; diff --git a/src/cpu/o3/iew.hh b/src/cpu/o3/iew.hh new file mode 100644 index 000000000..a400c9fa8 --- /dev/null +++ b/src/cpu/o3/iew.hh @@ -0,0 +1,551 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_IEW_HH__ +#define __CPU_O3_IEW_HH__ + +#include "config/full_system.hh" + +#include <queue> + +#include "base/statistics.hh" +#include "base/timebuf.hh" +#include "cpu/o3/comm.hh" +#include "cpu/o3/scoreboard.hh" +#include "cpu/o3/lsq.hh" + +class FUPool; + +/** + * DefaultIEW handles both single threaded and SMT IEW + * (issue/execute/writeback). It handles the dispatching of + * instructions to the LSQ/IQ as part of the issue stage, and has the + * IQ try to issue instructions each cycle. The execute latency is + * actually tied into the issue latency to allow the IQ to be able to + * do back-to-back scheduling without having to speculatively schedule + * instructions. This happens by having the IQ have access to the + * functional units, and the IQ gets the execution latencies from the + * FUs when it issues instructions. Instructions reach the execute + * stage on the last cycle of their execution, which is when the IQ + * knows to wake up any dependent instructions, allowing back to back + * scheduling. The execute portion of IEW separates memory + * instructions from non-memory instructions, either telling the LSQ + * to execute the instruction, or executing the instruction directly. + * The writeback portion of IEW completes the instructions by waking + * up any dependents, and marking the register ready on the + * scoreboard. + */ +template<class Impl> +class DefaultIEW +{ + private: + //Typedefs from Impl + typedef typename Impl::CPUPol CPUPol; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::O3CPU O3CPU; + typedef typename Impl::Params Params; + + typedef typename CPUPol::IQ IQ; + typedef typename CPUPol::RenameMap RenameMap; + typedef typename CPUPol::LSQ LSQ; + + typedef typename CPUPol::TimeStruct TimeStruct; + typedef typename CPUPol::IEWStruct IEWStruct; + typedef typename CPUPol::RenameStruct RenameStruct; + typedef typename CPUPol::IssueStruct IssueStruct; + + friend class Impl::O3CPU; + friend class CPUPol::IQ; + + public: + /** Overall IEW stage status. Used to determine if the CPU can + * deschedule itself due to a lack of activity. + */ + enum Status { + Active, + Inactive + }; + + /** Status for Issue, Execute, and Writeback stages. */ + enum StageStatus { + Running, + Blocked, + Idle, + StartSquash, + Squashing, + Unblocking + }; + + private: + /** Overall stage status. */ + Status _status; + /** Dispatch status. */ + StageStatus dispatchStatus[Impl::MaxThreads]; + /** Execute status. */ + StageStatus exeStatus; + /** Writeback status. */ + StageStatus wbStatus; + + public: + /** Constructs a DefaultIEW with the given parameters. */ + DefaultIEW(Params *params); + + /** Returns the name of the DefaultIEW stage. */ + std::string name() const; + + /** Registers statistics. */ + void regStats(); + + /** Initializes stage; sends back the number of free IQ and LSQ entries. */ + void initStage(); + + /** Returns the dcache port. */ + Port *getDcachePort() { return ldstQueue.getDcachePort(); } + + /** Sets CPU pointer for IEW, IQ, and LSQ. */ + void setCPU(O3CPU *cpu_ptr); + + /** Sets main time buffer used for backwards communication. */ + void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + + /** Sets time buffer for getting instructions coming from rename. */ + void setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr); + + /** Sets time buffer to pass on instructions to commit. */ + void setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr); + + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Sets pointer to the scoreboard. */ + void setScoreboard(Scoreboard *sb_ptr); + + /** Drains IEW stage. */ + bool drain(); + + /** Resumes execution after a drain. */ + void resume(); + + /** Completes switch out of IEW stage. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Returns if IEW is switched out. */ + bool isSwitchedOut() { return switchedOut; } + + /** Squashes instructions in IEW for a specific thread. */ + void squash(unsigned tid); + + /** Wakes all dependents of a completed instruction. */ + void wakeDependents(DynInstPtr &inst); + + /** Tells memory dependence unit that a memory instruction needs to be + * rescheduled. It will re-execute once replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &inst); + + /** Re-executes all rescheduled memory instructions. */ + void replayMemInst(DynInstPtr &inst); + + /** Sends an instruction to commit through the time buffer. */ + void instToCommit(DynInstPtr &inst); + + /** Inserts unused instructions of a thread into the skid buffer. */ + void skidInsert(unsigned tid); + + /** Returns the max of the number of entries in all of the skid buffers. */ + int skidCount(); + + /** Returns if all of the skid buffers are empty. */ + bool skidsEmpty(); + + /** Updates overall IEW status based on all of the stages' statuses. */ + void updateStatus(); + + /** Resets entries of the IQ and the LSQ. */ + void resetEntries(); + + /** Tells the CPU to wakeup if it has descheduled itself due to no + * activity. Used mainly by the LdWritebackEvent. + */ + void wakeCPU(); + + /** Reports to the CPU that there is activity this cycle. */ + void activityThisCycle(); + + /** Tells CPU that the IEW stage is active and running. */ + inline void activateStage(); + + /** Tells CPU that the IEW stage is inactive and idle. */ + inline void deactivateStage(); + + /** Returns if the LSQ has any stores to writeback. */ + bool hasStoresToWB() { return ldstQueue.hasStoresToWB(); } + + void incrWb(InstSeqNum &sn) + { + if (++wbOutstanding == wbMax) + ableToIssue = false; + DPRINTF(IEW, "wbOutstanding: %i\n", wbOutstanding); + assert(wbOutstanding <= wbMax); +#ifdef DEBUG + wbList.insert(sn); +#endif + } + + void decrWb(InstSeqNum &sn) + { + if (wbOutstanding-- == wbMax) + ableToIssue = true; + DPRINTF(IEW, "wbOutstanding: %i\n", wbOutstanding); + assert(wbOutstanding >= 0); +#ifdef DEBUG + assert(wbList.find(sn) != wbList.end()); + wbList.erase(sn); +#endif + } + +#ifdef DEBUG + std::set<InstSeqNum> wbList; + + void dumpWb() + { + std::set<InstSeqNum>::iterator wb_it = wbList.begin(); + while (wb_it != wbList.end()) { + cprintf("[sn:%lli]\n", + (*wb_it)); + wb_it++; + } + } +#endif + + bool canIssue() { return ableToIssue; } + + bool ableToIssue; + + private: + /** Sends commit proper information for a squash due to a branch + * mispredict. + */ + void squashDueToBranch(DynInstPtr &inst, unsigned thread_id); + + /** Sends commit proper information for a squash due to a memory order + * violation. + */ + void squashDueToMemOrder(DynInstPtr &inst, unsigned thread_id); + + /** Sends commit proper information for a squash due to memory becoming + * blocked (younger issued instructions must be retried). + */ + void squashDueToMemBlocked(DynInstPtr &inst, unsigned thread_id); + + /** Sets Dispatch to blocked, and signals back to other stages to block. */ + void block(unsigned thread_id); + + /** Unblocks Dispatch if the skid buffer is empty, and signals back to + * other stages to unblock. + */ + void unblock(unsigned thread_id); + + /** Determines proper actions to take given Dispatch's status. */ + void dispatch(unsigned tid); + + /** Dispatches instructions to IQ and LSQ. */ + void dispatchInsts(unsigned tid); + + /** Executes instructions. In the case of memory operations, it informs the + * LSQ to execute the instructions. Also handles any redirects that occur + * due to the executed instructions. + */ + void executeInsts(); + + /** Writebacks instructions. In our model, the instruction's execute() + * function atomically reads registers, executes, and writes registers. + * Thus this writeback only wakes up dependent instructions, and informs + * the scoreboard of registers becoming ready. + */ + void writebackInsts(); + + /** Returns the number of valid, non-squashed instructions coming from + * rename to dispatch. + */ + unsigned validInstsFromRename(); + + /** Reads the stall signals. */ + void readStallSignals(unsigned tid); + + /** Checks if any of the stall conditions are currently true. */ + bool checkStall(unsigned tid); + + /** Processes inputs and changes state accordingly. */ + void checkSignalsAndUpdate(unsigned tid); + + /** Removes instructions from rename from a thread's instruction list. */ + void emptyRenameInsts(unsigned tid); + + /** Sorts instructions coming from rename into lists separated by thread. */ + void sortInsts(); + + public: + /** Ticks IEW stage, causing Dispatch, the IQ, the LSQ, Execute, and + * Writeback to run for one cycle. + */ + void tick(); + + private: + /** Updates execution stats based on the instruction. */ + void updateExeInstStats(DynInstPtr &inst); + + /** Pointer to main time buffer used for backwards communication. */ + TimeBuffer<TimeStruct> *timeBuffer; + + /** Wire to write information heading to previous stages. */ + typename TimeBuffer<TimeStruct>::wire toFetch; + + /** Wire to get commit's output from backwards time buffer. */ + typename TimeBuffer<TimeStruct>::wire fromCommit; + + /** Wire to write information heading to previous stages. */ + typename TimeBuffer<TimeStruct>::wire toRename; + + /** Rename instruction queue interface. */ + TimeBuffer<RenameStruct> *renameQueue; + + /** Wire to get rename's output from rename queue. */ + typename TimeBuffer<RenameStruct>::wire fromRename; + + /** Issue stage queue. */ + TimeBuffer<IssueStruct> issueToExecQueue; + + /** Wire to read information from the issue stage time queue. */ + typename TimeBuffer<IssueStruct>::wire fromIssue; + + /** + * IEW stage time buffer. Holds ROB indices of instructions that + * can be marked as completed. + */ + TimeBuffer<IEWStruct> *iewQueue; + + /** Wire to write infromation heading to commit. */ + typename TimeBuffer<IEWStruct>::wire toCommit; + + /** Queue of all instructions coming from rename this cycle. */ + std::queue<DynInstPtr> insts[Impl::MaxThreads]; + + /** Skid buffer between rename and IEW. */ + std::queue<DynInstPtr> skidBuffer[Impl::MaxThreads]; + + /** Scoreboard pointer. */ + Scoreboard* scoreboard; + + public: + /** Instruction queue. */ + IQ instQueue; + + /** Load / store queue. */ + LSQ ldstQueue; + + /** Pointer to the functional unit pool. */ + FUPool *fuPool; + + private: + /** CPU pointer. */ + O3CPU *cpu; + + /** Records if IEW has written to the time buffer this cycle, so that the + * CPU can deschedule itself if there is no activity. + */ + bool wroteToTimeBuffer; + + /** Source of possible stalls. */ + struct Stalls { + bool commit; + }; + + /** Stages that are telling IEW to stall. */ + Stalls stalls[Impl::MaxThreads]; + + /** Debug function to print instructions that are issued this cycle. */ + void printAvailableInsts(); + + public: + /** Records if the LSQ needs to be updated on the next cycle, so that + * IEW knows if there will be activity on the next cycle. + */ + bool updateLSQNextCycle; + + private: + /** Records if there is a fetch redirect on this cycle for each thread. */ + bool fetchRedirect[Impl::MaxThreads]; + + /** Keeps track of the last valid branch delay slot instss for threads */ + InstSeqNum bdelayDoneSeqNum[Impl::MaxThreads]; + + /** Used to track if all instructions have been dispatched this cycle. + * If they have not, then blocking must have occurred, and the instructions + * would already be added to the skid buffer. + * @todo: Fix this hack. + */ + bool dispatchedAllInsts; + + /** Records if the queues have been changed (inserted or issued insts), + * so that IEW knows to broadcast the updated amount of free entries. + */ + bool updatedQueues; + + /** Commit to IEW delay, in ticks. */ + unsigned commitToIEWDelay; + + /** Rename to IEW delay, in ticks. */ + unsigned renameToIEWDelay; + + /** + * Issue to execute delay, in ticks. What this actually represents is + * the amount of time it takes for an instruction to wake up, be + * scheduled, and sent to a FU for execution. + */ + unsigned issueToExecuteDelay; + + /** Width of dispatch, in instructions. */ + unsigned dispatchWidth; + + /** Width of issue, in instructions. */ + unsigned issueWidth; + + /** Index into queue of instructions being written back. */ + unsigned wbNumInst; + + /** Cycle number within the queue of instructions being written back. + * Used in case there are too many instructions writing back at the current + * cycle and writesbacks need to be scheduled for the future. See comments + * in instToCommit(). + */ + unsigned wbCycle; + + /** Number of instructions in flight that will writeback. */ + + /** Number of instructions in flight that will writeback. */ + int wbOutstanding; + + /** Writeback width. */ + unsigned wbWidth; + + /** Writeback width * writeback depth, where writeback depth is + * the number of cycles of writing back instructions that can be + * buffered. */ + unsigned wbMax; + + /** Number of active threads. */ + unsigned numThreads; + + /** Pointer to list of active threads. */ + std::list<unsigned> *activeThreads; + + /** Maximum size of the skid buffer. */ + unsigned skidBufferMax; + + /** Is this stage switched out. */ + bool switchedOut; + + /** Stat for total number of idle cycles. */ + Stats::Scalar<> iewIdleCycles; + /** Stat for total number of squashing cycles. */ + Stats::Scalar<> iewSquashCycles; + /** Stat for total number of blocking cycles. */ + Stats::Scalar<> iewBlockCycles; + /** Stat for total number of unblocking cycles. */ + Stats::Scalar<> iewUnblockCycles; + /** Stat for total number of instructions dispatched. */ + Stats::Scalar<> iewDispatchedInsts; + /** Stat for total number of squashed instructions dispatch skips. */ + Stats::Scalar<> iewDispSquashedInsts; + /** Stat for total number of dispatched load instructions. */ + Stats::Scalar<> iewDispLoadInsts; + /** Stat for total number of dispatched store instructions. */ + Stats::Scalar<> iewDispStoreInsts; + /** Stat for total number of dispatched non speculative instructions. */ + Stats::Scalar<> iewDispNonSpecInsts; + /** Stat for number of times the IQ becomes full. */ + Stats::Scalar<> iewIQFullEvents; + /** Stat for number of times the LSQ becomes full. */ + Stats::Scalar<> iewLSQFullEvents; + /** Stat for total number of memory ordering violation events. */ + Stats::Scalar<> memOrderViolationEvents; + /** Stat for total number of incorrect predicted taken branches. */ + Stats::Scalar<> predictedTakenIncorrect; + /** Stat for total number of incorrect predicted not taken branches. */ + Stats::Scalar<> predictedNotTakenIncorrect; + /** Stat for total number of mispredicted branches detected at execute. */ + Stats::Formula branchMispredicts; + + /** Stat for total number of executed instructions. */ + Stats::Scalar<> iewExecutedInsts; + /** Stat for total number of executed load instructions. */ + Stats::Vector<> iewExecLoadInsts; + /** Stat for total number of executed store instructions. */ +// Stats::Scalar<> iewExecStoreInsts; + /** Stat for total number of squashed instructions skipped at execute. */ + Stats::Scalar<> iewExecSquashedInsts; + /** Number of executed software prefetches. */ + Stats::Vector<> iewExecutedSwp; + /** Number of executed nops. */ + Stats::Vector<> iewExecutedNop; + /** Number of executed meomory references. */ + Stats::Vector<> iewExecutedRefs; + /** Number of executed branches. */ + Stats::Vector<> iewExecutedBranches; + /** Number of executed store instructions. */ + Stats::Formula iewExecStoreInsts; + /** Number of instructions executed per cycle. */ + Stats::Formula iewExecRate; + + /** Number of instructions sent to commit. */ + Stats::Vector<> iewInstsToCommit; + /** Number of instructions that writeback. */ + Stats::Vector<> writebackCount; + /** Number of instructions that wake consumers. */ + Stats::Vector<> producerInst; + /** Number of instructions that wake up from producers. */ + Stats::Vector<> consumerInst; + /** Number of instructions that were delayed in writing back due + * to resource contention. + */ + Stats::Vector<> wbPenalized; + /** Number of instructions per cycle written back. */ + Stats::Formula wbRate; + /** Average number of woken instructions per writeback. */ + Stats::Formula wbFanout; + /** Number of instructions per cycle delayed in writing back . */ + Stats::Formula wbPenalizedRate; +}; + +#endif // __CPU_O3_IEW_HH__ diff --git a/src/cpu/o3/iew_impl.hh b/src/cpu/o3/iew_impl.hh new file mode 100644 index 000000000..c82f6dd21 --- /dev/null +++ b/src/cpu/o3/iew_impl.hh @@ -0,0 +1,1613 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +// @todo: Fix the instantaneous communication among all the stages within +// iew. There's a clear delay between issue and execute, yet backwards +// communication happens simultaneously. + +#include <queue> + +#include "base/timebuf.hh" +#include "cpu/o3/fu_pool.hh" +#include "cpu/o3/iew.hh" + +template<class Impl> +DefaultIEW<Impl>::DefaultIEW(Params *params) + : issueToExecQueue(params->backComSize, params->forwardComSize), + instQueue(params), + ldstQueue(params), + fuPool(params->fuPool), + commitToIEWDelay(params->commitToIEWDelay), + renameToIEWDelay(params->renameToIEWDelay), + issueToExecuteDelay(params->issueToExecuteDelay), + dispatchWidth(params->dispatchWidth), + issueWidth(params->issueWidth), + wbOutstanding(0), + wbWidth(params->wbWidth), + numThreads(params->numberOfThreads), + switchedOut(false) +{ + _status = Active; + exeStatus = Running; + wbStatus = Idle; + + // Setup wire to read instructions coming from issue. + fromIssue = issueToExecQueue.getWire(-issueToExecuteDelay); + + // Instruction queue needs the queue between issue and execute. + instQueue.setIssueToExecuteQueue(&issueToExecQueue); + + instQueue.setIEW(this); + ldstQueue.setIEW(this); + + for (int i=0; i < numThreads; i++) { + dispatchStatus[i] = Running; + stalls[i].commit = false; + fetchRedirect[i] = false; + bdelayDoneSeqNum[i] = 0; + } + + wbMax = wbWidth * params->wbDepth; + + updateLSQNextCycle = false; + + ableToIssue = true; + + skidBufferMax = (3 * (renameToIEWDelay * params->renameWidth)) + issueWidth; +} + +template <class Impl> +std::string +DefaultIEW<Impl>::name() const +{ + return cpu->name() + ".iew"; +} + +template <class Impl> +void +DefaultIEW<Impl>::regStats() +{ + using namespace Stats; + + instQueue.regStats(); + ldstQueue.regStats(); + + iewIdleCycles + .name(name() + ".iewIdleCycles") + .desc("Number of cycles IEW is idle"); + + iewSquashCycles + .name(name() + ".iewSquashCycles") + .desc("Number of cycles IEW is squashing"); + + iewBlockCycles + .name(name() + ".iewBlockCycles") + .desc("Number of cycles IEW is blocking"); + + iewUnblockCycles + .name(name() + ".iewUnblockCycles") + .desc("Number of cycles IEW is unblocking"); + + iewDispatchedInsts + .name(name() + ".iewDispatchedInsts") + .desc("Number of instructions dispatched to IQ"); + + iewDispSquashedInsts + .name(name() + ".iewDispSquashedInsts") + .desc("Number of squashed instructions skipped by dispatch"); + + iewDispLoadInsts + .name(name() + ".iewDispLoadInsts") + .desc("Number of dispatched load instructions"); + + iewDispStoreInsts + .name(name() + ".iewDispStoreInsts") + .desc("Number of dispatched store instructions"); + + iewDispNonSpecInsts + .name(name() + ".iewDispNonSpecInsts") + .desc("Number of dispatched non-speculative instructions"); + + iewIQFullEvents + .name(name() + ".iewIQFullEvents") + .desc("Number of times the IQ has become full, causing a stall"); + + iewLSQFullEvents + .name(name() + ".iewLSQFullEvents") + .desc("Number of times the LSQ has become full, causing a stall"); + + memOrderViolationEvents + .name(name() + ".memOrderViolationEvents") + .desc("Number of memory order violations"); + + predictedTakenIncorrect + .name(name() + ".predictedTakenIncorrect") + .desc("Number of branches that were predicted taken incorrectly"); + + predictedNotTakenIncorrect + .name(name() + ".predictedNotTakenIncorrect") + .desc("Number of branches that were predicted not taken incorrectly"); + + branchMispredicts + .name(name() + ".branchMispredicts") + .desc("Number of branch mispredicts detected at execute"); + + branchMispredicts = predictedTakenIncorrect + predictedNotTakenIncorrect; + + iewExecutedInsts + .name(name() + ".iewExecutedInsts") + .desc("Number of executed instructions"); + + iewExecLoadInsts + .init(cpu->number_of_threads) + .name(name() + ".iewExecLoadInsts") + .desc("Number of load instructions executed") + .flags(total); + + iewExecSquashedInsts + .name(name() + ".iewExecSquashedInsts") + .desc("Number of squashed instructions skipped in execute"); + + iewExecutedSwp + .init(cpu->number_of_threads) + .name(name() + ".EXEC:swp") + .desc("number of swp insts executed") + .flags(total); + + iewExecutedNop + .init(cpu->number_of_threads) + .name(name() + ".EXEC:nop") + .desc("number of nop insts executed") + .flags(total); + + iewExecutedRefs + .init(cpu->number_of_threads) + .name(name() + ".EXEC:refs") + .desc("number of memory reference insts executed") + .flags(total); + + iewExecutedBranches + .init(cpu->number_of_threads) + .name(name() + ".EXEC:branches") + .desc("Number of branches executed") + .flags(total); + + iewExecStoreInsts + .name(name() + ".EXEC:stores") + .desc("Number of stores executed") + .flags(total); + iewExecStoreInsts = iewExecutedRefs - iewExecLoadInsts; + + iewExecRate + .name(name() + ".EXEC:rate") + .desc("Inst execution rate") + .flags(total); + + iewExecRate = iewExecutedInsts / cpu->numCycles; + + iewInstsToCommit + .init(cpu->number_of_threads) + .name(name() + ".WB:sent") + .desc("cumulative count of insts sent to commit") + .flags(total); + + writebackCount + .init(cpu->number_of_threads) + .name(name() + ".WB:count") + .desc("cumulative count of insts written-back") + .flags(total); + + producerInst + .init(cpu->number_of_threads) + .name(name() + ".WB:producers") + .desc("num instructions producing a value") + .flags(total); + + consumerInst + .init(cpu->number_of_threads) + .name(name() + ".WB:consumers") + .desc("num instructions consuming a value") + .flags(total); + + wbPenalized + .init(cpu->number_of_threads) + .name(name() + ".WB:penalized") + .desc("number of instrctions required to write to 'other' IQ") + .flags(total); + + wbPenalizedRate + .name(name() + ".WB:penalized_rate") + .desc ("fraction of instructions written-back that wrote to 'other' IQ") + .flags(total); + + wbPenalizedRate = wbPenalized / writebackCount; + + wbFanout + .name(name() + ".WB:fanout") + .desc("average fanout of values written-back") + .flags(total); + + wbFanout = producerInst / consumerInst; + + wbRate + .name(name() + ".WB:rate") + .desc("insts written-back per cycle") + .flags(total); + wbRate = writebackCount / cpu->numCycles; +} + +template<class Impl> +void +DefaultIEW<Impl>::initStage() +{ + for (int tid=0; tid < numThreads; tid++) { + toRename->iewInfo[tid].usedIQ = true; + toRename->iewInfo[tid].freeIQEntries = + instQueue.numFreeEntries(tid); + + toRename->iewInfo[tid].usedLSQ = true; + toRename->iewInfo[tid].freeLSQEntries = + ldstQueue.numFreeEntries(tid); + } +} + +template<class Impl> +void +DefaultIEW<Impl>::setCPU(O3CPU *cpu_ptr) +{ + DPRINTF(IEW, "Setting CPU pointer.\n"); + cpu = cpu_ptr; + + instQueue.setCPU(cpu_ptr); + ldstQueue.setCPU(cpu_ptr); + + cpu->activateStage(O3CPU::IEWIdx); +} + +template<class Impl> +void +DefaultIEW<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +{ + DPRINTF(IEW, "Setting time buffer pointer.\n"); + timeBuffer = tb_ptr; + + // Setup wire to read information from time buffer, from commit. + fromCommit = timeBuffer->getWire(-commitToIEWDelay); + + // Setup wire to write information back to previous stages. + toRename = timeBuffer->getWire(0); + + toFetch = timeBuffer->getWire(0); + + // Instruction queue also needs main time buffer. + instQueue.setTimeBuffer(tb_ptr); +} + +template<class Impl> +void +DefaultIEW<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) +{ + DPRINTF(IEW, "Setting rename queue pointer.\n"); + renameQueue = rq_ptr; + + // Setup wire to read information from rename queue. + fromRename = renameQueue->getWire(-renameToIEWDelay); +} + +template<class Impl> +void +DefaultIEW<Impl>::setIEWQueue(TimeBuffer<IEWStruct> *iq_ptr) +{ + DPRINTF(IEW, "Setting IEW queue pointer.\n"); + iewQueue = iq_ptr; + + // Setup wire to write instructions to commit. + toCommit = iewQueue->getWire(0); +} + +template<class Impl> +void +DefaultIEW<Impl>::setActiveThreads(std::list<unsigned> *at_ptr) +{ + DPRINTF(IEW, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; + + ldstQueue.setActiveThreads(at_ptr); + instQueue.setActiveThreads(at_ptr); +} + +template<class Impl> +void +DefaultIEW<Impl>::setScoreboard(Scoreboard *sb_ptr) +{ + DPRINTF(IEW, "Setting scoreboard pointer.\n"); + scoreboard = sb_ptr; +} + +template <class Impl> +bool +DefaultIEW<Impl>::drain() +{ + // IEW is ready to drain at any time. + cpu->signalDrained(); + return true; +} + +template <class Impl> +void +DefaultIEW<Impl>::resume() +{ +} + +template <class Impl> +void +DefaultIEW<Impl>::switchOut() +{ + // Clear any state. + switchedOut = true; + assert(insts[0].empty()); + assert(skidBuffer[0].empty()); + + instQueue.switchOut(); + ldstQueue.switchOut(); + fuPool->switchOut(); + + for (int i = 0; i < numThreads; i++) { + while (!insts[i].empty()) + insts[i].pop(); + while (!skidBuffer[i].empty()) + skidBuffer[i].pop(); + } +} + +template <class Impl> +void +DefaultIEW<Impl>::takeOverFrom() +{ + // Reset all state. + _status = Active; + exeStatus = Running; + wbStatus = Idle; + switchedOut = false; + + instQueue.takeOverFrom(); + ldstQueue.takeOverFrom(); + fuPool->takeOverFrom(); + + initStage(); + cpu->activityThisCycle(); + + for (int i=0; i < numThreads; i++) { + dispatchStatus[i] = Running; + stalls[i].commit = false; + fetchRedirect[i] = false; + } + + updateLSQNextCycle = false; + + for (int i = 0; i < issueToExecQueue.getSize(); ++i) { + issueToExecQueue.advance(); + } +} + +template<class Impl> +void +DefaultIEW<Impl>::squash(unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Squashing all instructions.\n", + tid); + + // Tell the IQ to start squashing. + instQueue.squash(tid); + + // Tell the LDSTQ to start squashing. +#if ISA_HAS_DELAY_SLOT + ldstQueue.squash(fromCommit->commitInfo[tid].bdelayDoneSeqNum, tid); +#else + ldstQueue.squash(fromCommit->commitInfo[tid].doneSeqNum, tid); +#endif + updatedQueues = true; + + // Clear the skid buffer in case it has any data in it. + DPRINTF(IEW, "[tid:%i]: Removing skidbuffer instructions until [sn:%i].\n", + tid, fromCommit->commitInfo[tid].bdelayDoneSeqNum); + + while (!skidBuffer[tid].empty()) { +#if ISA_HAS_DELAY_SLOT + if (skidBuffer[tid].front()->seqNum <= + fromCommit->commitInfo[tid].bdelayDoneSeqNum) { + DPRINTF(IEW, "[tid:%i]: Cannot remove skidbuffer instructions " + "that occur before delay slot [sn:%i].\n", + fromCommit->commitInfo[tid].bdelayDoneSeqNum, + tid); + break; + } else { + DPRINTF(IEW, "[tid:%i]: Removing instruction [sn:%i] from " + "skidBuffer.\n", tid, skidBuffer[tid].front()->seqNum); + } +#endif + if (skidBuffer[tid].front()->isLoad() || + skidBuffer[tid].front()->isStore() ) { + toRename->iewInfo[tid].dispatchedToLSQ++; + } + + toRename->iewInfo[tid].dispatched++; + + skidBuffer[tid].pop(); + } + + bdelayDoneSeqNum[tid] = fromCommit->commitInfo[tid].bdelayDoneSeqNum; + + emptyRenameInsts(tid); +} + +template<class Impl> +void +DefaultIEW<Impl>::squashDueToBranch(DynInstPtr &inst, unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Squashing from a specific instruction, PC: %#x " + "[sn:%i].\n", tid, inst->readPC(), inst->seqNum); + + toCommit->squash[tid] = true; + toCommit->squashedSeqNum[tid] = inst->seqNum; + toCommit->mispredPC[tid] = inst->readPC(); + toCommit->branchMispredict[tid] = true; + +#if ISA_HAS_DELAY_SLOT + bool branch_taken = inst->readNextNPC() != + (inst->readNextPC() + sizeof(TheISA::MachInst)); + + toCommit->branchTaken[tid] = branch_taken; + + toCommit->condDelaySlotBranch[tid] = inst->isCondDelaySlot(); + + if (inst->isCondDelaySlot() && branch_taken) { + toCommit->nextPC[tid] = inst->readNextPC(); + } else { + toCommit->nextPC[tid] = inst->readNextNPC(); + } +#else + toCommit->branchTaken[tid] = inst->readNextPC() != + (inst->readPC() + sizeof(TheISA::MachInst)); + toCommit->nextPC[tid] = inst->readNextPC(); +#endif + + toCommit->includeSquashInst[tid] = false; + + wroteToTimeBuffer = true; +} + +template<class Impl> +void +DefaultIEW<Impl>::squashDueToMemOrder(DynInstPtr &inst, unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Squashing from a specific instruction, " + "PC: %#x [sn:%i].\n", tid, inst->readPC(), inst->seqNum); + + toCommit->squash[tid] = true; + toCommit->squashedSeqNum[tid] = inst->seqNum; + toCommit->nextPC[tid] = inst->readNextPC(); + + toCommit->includeSquashInst[tid] = false; + + wroteToTimeBuffer = true; +} + +template<class Impl> +void +DefaultIEW<Impl>::squashDueToMemBlocked(DynInstPtr &inst, unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Memory blocked, squashing load and younger insts, " + "PC: %#x [sn:%i].\n", tid, inst->readPC(), inst->seqNum); + + toCommit->squash[tid] = true; + toCommit->squashedSeqNum[tid] = inst->seqNum; + toCommit->nextPC[tid] = inst->readPC(); + + // Must include the broadcasted SN in the squash. + toCommit->includeSquashInst[tid] = true; + + ldstQueue.setLoadBlockedHandled(tid); + + wroteToTimeBuffer = true; +} + +template<class Impl> +void +DefaultIEW<Impl>::block(unsigned tid) +{ + DPRINTF(IEW, "[tid:%u]: Blocking.\n", tid); + + if (dispatchStatus[tid] != Blocked && + dispatchStatus[tid] != Unblocking) { + toRename->iewBlock[tid] = true; + wroteToTimeBuffer = true; + } + + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + + dispatchStatus[tid] = Blocked; +} + +template<class Impl> +void +DefaultIEW<Impl>::unblock(unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Reading instructions out of the skid " + "buffer %u.\n",tid, tid); + + // If the skid bufffer is empty, signal back to previous stages to unblock. + // Also switch status to running. + if (skidBuffer[tid].empty()) { + toRename->iewUnblock[tid] = true; + wroteToTimeBuffer = true; + DPRINTF(IEW, "[tid:%i]: Done unblocking.\n",tid); + dispatchStatus[tid] = Running; + } +} + +template<class Impl> +void +DefaultIEW<Impl>::wakeDependents(DynInstPtr &inst) +{ + instQueue.wakeDependents(inst); +} + +template<class Impl> +void +DefaultIEW<Impl>::rescheduleMemInst(DynInstPtr &inst) +{ + instQueue.rescheduleMemInst(inst); +} + +template<class Impl> +void +DefaultIEW<Impl>::replayMemInst(DynInstPtr &inst) +{ + instQueue.replayMemInst(inst); +} + +template<class Impl> +void +DefaultIEW<Impl>::instToCommit(DynInstPtr &inst) +{ + // First check the time slot that this instruction will write + // to. If there are free write ports at the time, then go ahead + // and write the instruction to that time. If there are not, + // keep looking back to see where's the first time there's a + // free slot. + while ((*iewQueue)[wbCycle].insts[wbNumInst]) { + ++wbNumInst; + if (wbNumInst == wbWidth) { + ++wbCycle; + wbNumInst = 0; + } + + assert((wbCycle * wbWidth + wbNumInst) <= wbMax); + } + + DPRINTF(IEW, "Current wb cycle: %i, width: %i, numInst: %i\nwbActual:%i\n", + wbCycle, wbWidth, wbNumInst, wbCycle * wbWidth + wbNumInst); + // Add finished instruction to queue to commit. + (*iewQueue)[wbCycle].insts[wbNumInst] = inst; + (*iewQueue)[wbCycle].size++; +} + +template <class Impl> +unsigned +DefaultIEW<Impl>::validInstsFromRename() +{ + unsigned inst_count = 0; + + for (int i=0; i<fromRename->size; i++) { + if (!fromRename->insts[i]->isSquashed()) + inst_count++; + } + + return inst_count; +} + +template<class Impl> +void +DefaultIEW<Impl>::skidInsert(unsigned tid) +{ + DynInstPtr inst = NULL; + + while (!insts[tid].empty()) { + inst = insts[tid].front(); + + insts[tid].pop(); + + DPRINTF(Decode,"[tid:%i]: Inserting [sn:%lli] PC:%#x into " + "dispatch skidBuffer %i\n",tid, inst->seqNum, + inst->readPC(),tid); + + skidBuffer[tid].push(inst); + } + + assert(skidBuffer[tid].size() <= skidBufferMax && + "Skidbuffer Exceeded Max Size"); +} + +template<class Impl> +int +DefaultIEW<Impl>::skidCount() +{ + int max=0; + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned thread_count = skidBuffer[*threads++].size(); + if (max < thread_count) + max = thread_count; + } + + return max; +} + +template<class Impl> +bool +DefaultIEW<Impl>::skidsEmpty() +{ + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + if (!skidBuffer[*threads++].empty()) + return false; + } + + return true; +} + +template <class Impl> +void +DefaultIEW<Impl>::updateStatus() +{ + bool any_unblocking = false; + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (dispatchStatus[tid] == Unblocking) { + any_unblocking = true; + break; + } + } + + // If there are no ready instructions waiting to be scheduled by the IQ, + // and there's no stores waiting to write back, and dispatch is not + // unblocking, then there is no internal activity for the IEW stage. + if (_status == Active && !instQueue.hasReadyInsts() && + !ldstQueue.willWB() && !any_unblocking) { + DPRINTF(IEW, "IEW switching to idle\n"); + + deactivateStage(); + + _status = Inactive; + } else if (_status == Inactive && (instQueue.hasReadyInsts() || + ldstQueue.willWB() || + any_unblocking)) { + // Otherwise there is internal activity. Set to active. + DPRINTF(IEW, "IEW switching to active\n"); + + activateStage(); + + _status = Active; + } +} + +template <class Impl> +void +DefaultIEW<Impl>::resetEntries() +{ + instQueue.resetEntries(); + ldstQueue.resetEntries(); +} + +template <class Impl> +void +DefaultIEW<Impl>::readStallSignals(unsigned tid) +{ + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } + + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + stalls[tid].commit = false; + } +} + +template <class Impl> +bool +DefaultIEW<Impl>::checkStall(unsigned tid) +{ + bool ret_val(false); + + if (stalls[tid].commit) { + DPRINTF(IEW,"[tid:%i]: Stall from Commit stage detected.\n",tid); + ret_val = true; + } else if (instQueue.isFull(tid)) { + DPRINTF(IEW,"[tid:%i]: Stall: IQ is full.\n",tid); + ret_val = true; + } else if (ldstQueue.isFull(tid)) { + DPRINTF(IEW,"[tid:%i]: Stall: LSQ is full\n",tid); + + if (ldstQueue.numLoads(tid) > 0 ) { + + DPRINTF(IEW,"[tid:%i]: LSQ oldest load: [sn:%i] \n", + tid,ldstQueue.getLoadHeadSeqNum(tid)); + } + + if (ldstQueue.numStores(tid) > 0) { + + DPRINTF(IEW,"[tid:%i]: LSQ oldest store: [sn:%i] \n", + tid,ldstQueue.getStoreHeadSeqNum(tid)); + } + + ret_val = true; + } else if (ldstQueue.isStalled(tid)) { + DPRINTF(IEW,"[tid:%i]: Stall: LSQ stall detected.\n",tid); + ret_val = true; + } + + return ret_val; +} + +template <class Impl> +void +DefaultIEW<Impl>::checkSignalsAndUpdate(unsigned tid) +{ + // Check if there's a squash signal, squash if there is + // Check stall signals, block if there is. + // If status was Blocked + // if so then go to unblocking + // If status was Squashing + // check if squashing is not high. Switch to running this cycle. + + readStallSignals(tid); + + if (fromCommit->commitInfo[tid].squash) { + squash(tid); + + if (dispatchStatus[tid] == Blocked || + dispatchStatus[tid] == Unblocking) { + toRename->iewUnblock[tid] = true; + wroteToTimeBuffer = true; + } + + dispatchStatus[tid] = Squashing; + + fetchRedirect[tid] = false; + return; + } + + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(IEW, "[tid:%i]: ROB is still squashing.\n", tid); + + dispatchStatus[tid] = Squashing; + + emptyRenameInsts(tid); + wroteToTimeBuffer = true; + return; + } + + if (checkStall(tid)) { + block(tid); + dispatchStatus[tid] = Blocked; + return; + } + + if (dispatchStatus[tid] == Blocked) { + // Status from previous cycle was blocked, but there are no more stall + // conditions. Switch over to unblocking. + DPRINTF(IEW, "[tid:%i]: Done blocking, switching to unblocking.\n", + tid); + + dispatchStatus[tid] = Unblocking; + + unblock(tid); + + return; + } + + if (dispatchStatus[tid] == Squashing) { + // Switch status to running if rename isn't being told to block or + // squash this cycle. + DPRINTF(IEW, "[tid:%i]: Done squashing, switching to running.\n", + tid); + + dispatchStatus[tid] = Running; + + return; + } +} + +template <class Impl> +void +DefaultIEW<Impl>::sortInsts() +{ + int insts_from_rename = fromRename->size; +#ifdef DEBUG +#if !ISA_HAS_DELAY_SLOT + for (int i = 0; i < numThreads; i++) + assert(insts[i].empty()); +#endif +#endif + for (int i = 0; i < insts_from_rename; ++i) { + insts[fromRename->insts[i]->threadNumber].push(fromRename->insts[i]); + } +} + +template <class Impl> +void +DefaultIEW<Impl>::emptyRenameInsts(unsigned tid) +{ + DPRINTF(IEW, "[tid:%i]: Removing incoming rename instructions until " + "[sn:%i].\n", tid, bdelayDoneSeqNum[tid]); + + while (!insts[tid].empty()) { +#if ISA_HAS_DELAY_SLOT + if (insts[tid].front()->seqNum <= bdelayDoneSeqNum[tid]) { + DPRINTF(IEW, "[tid:%i]: Done removing, cannot remove instruction" + " that occurs at or before delay slot [sn:%i].\n", + tid, bdelayDoneSeqNum[tid]); + break; + } else { + DPRINTF(IEW, "[tid:%i]: Removing incoming rename instruction " + "[sn:%i].\n", tid, insts[tid].front()->seqNum); + } +#endif + + if (insts[tid].front()->isLoad() || + insts[tid].front()->isStore() ) { + toRename->iewInfo[tid].dispatchedToLSQ++; + } + + toRename->iewInfo[tid].dispatched++; + + insts[tid].pop(); + } +} + +template <class Impl> +void +DefaultIEW<Impl>::emptyRenameInsts(unsigned tid) +{ + while (!insts[tid].empty()) { + if (insts[tid].front()->isLoad() || + insts[tid].front()->isStore() ) { + toRename->iewInfo[tid].dispatchedToLSQ++; + } + + toRename->iewInfo[tid].dispatched++; + + insts[tid].pop(); + } +} + +template <class Impl> +void +DefaultIEW<Impl>::wakeCPU() +{ + cpu->wakeCPU(); +} + +template <class Impl> +void +DefaultIEW<Impl>::activityThisCycle() +{ + DPRINTF(Activity, "Activity this cycle.\n"); + cpu->activityThisCycle(); +} + +template <class Impl> +inline void +DefaultIEW<Impl>::activateStage() +{ + DPRINTF(Activity, "Activating stage.\n"); + cpu->activateStage(O3CPU::IEWIdx); +} + +template <class Impl> +inline void +DefaultIEW<Impl>::deactivateStage() +{ + DPRINTF(Activity, "Deactivating stage.\n"); + cpu->deactivateStage(O3CPU::IEWIdx); +} + +template<class Impl> +void +DefaultIEW<Impl>::dispatch(unsigned tid) +{ + // If status is Running or idle, + // call dispatchInsts() + // If status is Unblocking, + // buffer any instructions coming from rename + // continue trying to empty skid buffer + // check if stall conditions have passed + + if (dispatchStatus[tid] == Blocked) { + ++iewBlockCycles; + + } else if (dispatchStatus[tid] == Squashing) { + ++iewSquashCycles; + } + + // Dispatch should try to dispatch as many instructions as its bandwidth + // will allow, as long as it is not currently blocked. + if (dispatchStatus[tid] == Running || + dispatchStatus[tid] == Idle) { + DPRINTF(IEW, "[tid:%i] Not blocked, so attempting to run " + "dispatch.\n", tid); + + dispatchInsts(tid); + } else if (dispatchStatus[tid] == Unblocking) { + // Make sure that the skid buffer has something in it if the + // status is unblocking. + assert(!skidsEmpty()); + + // If the status was unblocking, then instructions from the skid + // buffer were used. Remove those instructions and handle + // the rest of unblocking. + dispatchInsts(tid); + + ++iewUnblockCycles; + + if (validInstsFromRename() && dispatchedAllInsts) { + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + } + + unblock(tid); + } +} + +template <class Impl> +void +DefaultIEW<Impl>::dispatchInsts(unsigned tid) +{ + dispatchedAllInsts = true; + + // Obtain instructions from skid buffer if unblocking, or queue from rename + // otherwise. + std::queue<DynInstPtr> &insts_to_dispatch = + dispatchStatus[tid] == Unblocking ? + skidBuffer[tid] : insts[tid]; + + int insts_to_add = insts_to_dispatch.size(); + + DynInstPtr inst; + bool add_to_iq = false; + int dis_num_inst = 0; + + // Loop through the instructions, putting them in the instruction + // queue. + for ( ; dis_num_inst < insts_to_add && + dis_num_inst < dispatchWidth; + ++dis_num_inst) + { + inst = insts_to_dispatch.front(); + + if (dispatchStatus[tid] == Unblocking) { + DPRINTF(IEW, "[tid:%i]: Issue: Examining instruction from skid " + "buffer\n", tid); + } + + // Make sure there's a valid instruction there. + assert(inst); + + DPRINTF(IEW, "[tid:%i]: Issue: Adding PC %#x [sn:%lli] [tid:%i] to " + "IQ.\n", + tid, inst->readPC(), inst->seqNum, inst->threadNumber); + + // Be sure to mark these instructions as ready so that the + // commit stage can go ahead and execute them, and mark + // them as issued so the IQ doesn't reprocess them. + + // Check for squashed instructions. + if (inst->isSquashed()) { + DPRINTF(IEW, "[tid:%i]: Issue: Squashed instruction encountered, " + "not adding to IQ.\n", tid); + + ++iewDispSquashedInsts; + + insts_to_dispatch.pop(); + + //Tell Rename That An Instruction has been processed + if (inst->isLoad() || inst->isStore()) { + toRename->iewInfo[tid].dispatchedToLSQ++; + } + toRename->iewInfo[tid].dispatched++; + + continue; + } + + // Check for full conditions. + if (instQueue.isFull(tid)) { + DPRINTF(IEW, "[tid:%i]: Issue: IQ has become full.\n", tid); + + // Call function to start blocking. + block(tid); + + // Set unblock to false. Special case where we are using + // skidbuffer (unblocking) instructions but then we still + // get full in the IQ. + toRename->iewUnblock[tid] = false; + + dispatchedAllInsts = false; + + ++iewIQFullEvents; + break; + } else if (ldstQueue.isFull(tid)) { + DPRINTF(IEW, "[tid:%i]: Issue: LSQ has become full.\n",tid); + + // Call function to start blocking. + block(tid); + + // Set unblock to false. Special case where we are using + // skidbuffer (unblocking) instructions but then we still + // get full in the IQ. + toRename->iewUnblock[tid] = false; + + dispatchedAllInsts = false; + + ++iewLSQFullEvents; + break; + } + + // Otherwise issue the instruction just fine. + if (inst->isLoad()) { + DPRINTF(IEW, "[tid:%i]: Issue: Memory instruction " + "encountered, adding to LSQ.\n", tid); + + // Reserve a spot in the load store queue for this + // memory access. + ldstQueue.insertLoad(inst); + + ++iewDispLoadInsts; + + add_to_iq = true; + + toRename->iewInfo[tid].dispatchedToLSQ++; + } else if (inst->isStore()) { + DPRINTF(IEW, "[tid:%i]: Issue: Memory instruction " + "encountered, adding to LSQ.\n", tid); + + ldstQueue.insertStore(inst); + + ++iewDispStoreInsts; + + if (inst->isStoreConditional()) { + // Store conditionals need to be set as "canCommit()" + // so that commit can process them when they reach the + // head of commit. + // @todo: This is somewhat specific to Alpha. + inst->setCanCommit(); + instQueue.insertNonSpec(inst); + add_to_iq = false; + + ++iewDispNonSpecInsts; + } else { + add_to_iq = true; + } + + toRename->iewInfo[tid].dispatchedToLSQ++; +#if FULL_SYSTEM + } else if (inst->isMemBarrier() || inst->isWriteBarrier()) { + // Same as non-speculative stores. + inst->setCanCommit(); + instQueue.insertBarrier(inst); + add_to_iq = false; +#endif + } else if (inst->isNonSpeculative()) { + DPRINTF(IEW, "[tid:%i]: Issue: Nonspeculative instruction " + "encountered, skipping.\n", tid); + + // Same as non-speculative stores. + inst->setCanCommit(); + + // Specifically insert it as nonspeculative. + instQueue.insertNonSpec(inst); + + ++iewDispNonSpecInsts; + + add_to_iq = false; + } else if (inst->isNop()) { + DPRINTF(IEW, "[tid:%i]: Issue: Nop instruction encountered, " + "skipping.\n", tid); + + inst->setIssued(); + inst->setExecuted(); + inst->setCanCommit(); + + instQueue.recordProducer(inst); + + iewExecutedNop[tid]++; + + add_to_iq = false; + } else if (inst->isExecuted()) { + assert(0 && "Instruction shouldn't be executed.\n"); + DPRINTF(IEW, "Issue: Executed branch encountered, " + "skipping.\n"); + + inst->setIssued(); + inst->setCanCommit(); + + instQueue.recordProducer(inst); + + add_to_iq = false; + } else { + add_to_iq = true; + } + + // If the instruction queue is not full, then add the + // instruction. + if (add_to_iq) { + instQueue.insert(inst); + } + + insts_to_dispatch.pop(); + + toRename->iewInfo[tid].dispatched++; + + ++iewDispatchedInsts; + } + + if (!insts_to_dispatch.empty()) { + DPRINTF(IEW,"[tid:%i]: Issue: Bandwidth Full. Blocking.\n", tid); + block(tid); + toRename->iewUnblock[tid] = false; + } + + if (dispatchStatus[tid] == Idle && dis_num_inst) { + dispatchStatus[tid] = Running; + + updatedQueues = true; + } + + dis_num_inst = 0; +} + +template <class Impl> +void +DefaultIEW<Impl>::printAvailableInsts() +{ + int inst = 0; + + std::cout << "Available Instructions: "; + + while (fromIssue->insts[inst]) { + + if (inst%3==0) std::cout << "\n\t"; + + std::cout << "PC: " << fromIssue->insts[inst]->readPC() + << " TN: " << fromIssue->insts[inst]->threadNumber + << " SN: " << fromIssue->insts[inst]->seqNum << " | "; + + inst++; + + } + + std::cout << "\n"; +} + +template <class Impl> +void +DefaultIEW<Impl>::executeInsts() +{ + wbNumInst = 0; + wbCycle = 0; + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + fetchRedirect[tid] = false; + } + + // Uncomment this if you want to see all available instructions. +// printAvailableInsts(); + + // Execute/writeback any instructions that are available. + int insts_to_execute = fromIssue->size; + int inst_num = 0; + for (; inst_num < insts_to_execute; + ++inst_num) { + + DPRINTF(IEW, "Execute: Executing instructions from IQ.\n"); + + DynInstPtr inst = instQueue.getInstToExecute(); + + DPRINTF(IEW, "Execute: Processing PC %#x, [tid:%i] [sn:%i].\n", + inst->readPC(), inst->threadNumber,inst->seqNum); + + // Check if the instruction is squashed; if so then skip it + if (inst->isSquashed()) { + DPRINTF(IEW, "Execute: Instruction was squashed.\n"); + + // Consider this instruction executed so that commit can go + // ahead and retire the instruction. + inst->setExecuted(); + + // Not sure if I should set this here or just let commit try to + // commit any squashed instructions. I like the latter a bit more. + inst->setCanCommit(); + + ++iewExecSquashedInsts; + + decrWb(inst->seqNum); + continue; + } + + Fault fault = NoFault; + + // Execute instruction. + // Note that if the instruction faults, it will be handled + // at the commit stage. + if (inst->isMemRef() && + (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { + DPRINTF(IEW, "Execute: Calculating address for memory " + "reference.\n"); + + // Tell the LDSTQ to execute this instruction (if it is a load). + if (inst->isLoad()) { + // Loads will mark themselves as executed, and their writeback + // event adds the instruction to the queue to commit + fault = ldstQueue.executeLoad(inst); + } else if (inst->isStore()) { + fault = ldstQueue.executeStore(inst); + + // If the store had a fault then it may not have a mem req + if (!inst->isStoreConditional() && fault == NoFault) { + inst->setExecuted(); + + instToCommit(inst); + } else if (fault != NoFault) { + // If the instruction faulted, then we need to send it along to commit + // without the instruction completing. + + // Send this instruction to commit, also make sure iew stage + // realizes there is activity. + inst->setExecuted(); + + instToCommit(inst); + activityThisCycle(); + } + + // Store conditionals will mark themselves as + // executed, and their writeback event will add the + // instruction to the queue to commit. + } else { + panic("Unexpected memory type!\n"); + } + + } else { + inst->execute(); + + inst->setExecuted(); + + instToCommit(inst); + } + + updateExeInstStats(inst); + + // Check if branch prediction was correct, if not then we need + // to tell commit to squash in flight instructions. Only + // handle this if there hasn't already been something that + // redirects fetch in this group of instructions. + + // This probably needs to prioritize the redirects if a different + // scheduler is used. Currently the scheduler schedules the oldest + // instruction first, so the branch resolution order will be correct. + unsigned tid = inst->threadNumber; + + if (!fetchRedirect[tid]) { + + if (inst->mispredicted()) { + fetchRedirect[tid] = true; + + DPRINTF(IEW, "Execute: Branch mispredict detected.\n"); +#if ISA_HAS_DELAY_SLOT + DPRINTF(IEW, "Execute: Redirecting fetch to PC: %#x.\n", + inst->nextNPC); +#else + DPRINTF(IEW, "Execute: Redirecting fetch to PC: %#x.\n", + inst->nextPC); +#endif + // If incorrect, then signal the ROB that it must be squashed. + squashDueToBranch(inst, tid); + + if (inst->predTaken()) { + predictedTakenIncorrect++; + } else { + predictedNotTakenIncorrect++; + } + } else if (ldstQueue.violation(tid)) { + fetchRedirect[tid] = true; + + // If there was an ordering violation, then get the + // DynInst that caused the violation. Note that this + // clears the violation signal. + DynInstPtr violator; + violator = ldstQueue.getMemDepViolator(tid); + + DPRINTF(IEW, "LDSTQ detected a violation. Violator PC: " + "%#x, inst PC: %#x. Addr is: %#x.\n", + violator->readPC(), inst->readPC(), inst->physEffAddr); + + // Tell the instruction queue that a violation has occured. + instQueue.violation(inst, violator); + + // Squash. + squashDueToMemOrder(inst,tid); + + ++memOrderViolationEvents; + } else if (ldstQueue.loadBlocked(tid) && + !ldstQueue.isLoadBlockedHandled(tid)) { + fetchRedirect[tid] = true; + + DPRINTF(IEW, "Load operation couldn't execute because the " + "memory system is blocked. PC: %#x [sn:%lli]\n", + inst->readPC(), inst->seqNum); + + squashDueToMemBlocked(inst, tid); + } + } + } + + // Update and record activity if we processed any instructions. + if (inst_num) { + if (exeStatus == Idle) { + exeStatus = Running; + } + + updatedQueues = true; + + cpu->activityThisCycle(); + } + + // Need to reset this in case a writeback event needs to write into the + // iew queue. That way the writeback event will write into the correct + // spot in the queue. + wbNumInst = 0; +} + +template <class Impl> +void +DefaultIEW<Impl>::writebackInsts() +{ + // Loop through the head of the time buffer and wake any + // dependents. These instructions are about to write back. Also + // mark scoreboard that this instruction is finally complete. + // Either have IEW have direct access to scoreboard, or have this + // as part of backwards communication. + for (int inst_num = 0; inst_num < issueWidth && + toCommit->insts[inst_num]; inst_num++) { + DynInstPtr inst = toCommit->insts[inst_num]; + int tid = inst->threadNumber; + + DPRINTF(IEW, "Sending instructions to commit, [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + iewInstsToCommit[tid]++; + + // Some instructions will be sent to commit without having + // executed because they need commit to handle them. + // E.g. Uncached loads have not actually executed when they + // are first sent to commit. Instead commit must tell the LSQ + // when it's ready to execute the uncached load. + if (!inst->isSquashed() && inst->isExecuted() && inst->getFault() == NoFault) { + int dependents = instQueue.wakeDependents(inst); + + for (int i = 0; i < inst->numDestRegs(); i++) { + //mark as Ready + DPRINTF(IEW,"Setting Destination Register %i\n", + inst->renamedDestRegIdx(i)); + scoreboard->setReg(inst->renamedDestRegIdx(i)); + } + + if (dependents) { + producerInst[tid]++; + consumerInst[tid]+= dependents; + } + writebackCount[tid]++; + } + + decrWb(inst->seqNum); + } +} + +template<class Impl> +void +DefaultIEW<Impl>::tick() +{ + wbNumInst = 0; + wbCycle = 0; + + wroteToTimeBuffer = false; + updatedQueues = false; + + sortInsts(); + + // Free function units marked as being freed this cycle. + fuPool->processFreeUnits(); + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + // Check stall and squash signals, dispatch any instructions. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + DPRINTF(IEW,"Issue: Processing [tid:%i]\n",tid); + + checkSignalsAndUpdate(tid); + dispatch(tid); + } + + if (exeStatus != Squashing) { + executeInsts(); + + writebackInsts(); + + // Have the instruction queue try to schedule any ready instructions. + // (In actuality, this scheduling is for instructions that will + // be executed next cycle.) + instQueue.scheduleReadyInsts(); + + // Also should advance its own time buffers if the stage ran. + // Not the best place for it, but this works (hopefully). + issueToExecQueue.advance(); + } + + bool broadcast_free_entries = false; + + if (updatedQueues || exeStatus == Running || updateLSQNextCycle) { + exeStatus = Idle; + updateLSQNextCycle = false; + + broadcast_free_entries = true; + } + + // Writeback any stores using any leftover bandwidth. + ldstQueue.writebackStores(); + + // Check the committed load/store signals to see if there's a load + // or store to commit. Also check if it's being told to execute a + // nonspeculative instruction. + // This is pretty inefficient... + + threads = (*activeThreads).begin(); + while (threads != (*activeThreads).end()) { + unsigned tid = (*threads++); + + DPRINTF(IEW,"Processing [tid:%i]\n",tid); + + // Update structures based on instructions committed. + if (fromCommit->commitInfo[tid].doneSeqNum != 0 && + !fromCommit->commitInfo[tid].squash && + !fromCommit->commitInfo[tid].robSquashing) { + + ldstQueue.commitStores(fromCommit->commitInfo[tid].doneSeqNum,tid); + + ldstQueue.commitLoads(fromCommit->commitInfo[tid].doneSeqNum,tid); + + updateLSQNextCycle = true; + instQueue.commit(fromCommit->commitInfo[tid].doneSeqNum,tid); + } + + if (fromCommit->commitInfo[tid].nonSpecSeqNum != 0) { + + //DPRINTF(IEW,"NonspecInst from thread %i",tid); + if (fromCommit->commitInfo[tid].uncached) { + instQueue.replayMemInst(fromCommit->commitInfo[tid].uncachedLoad); + } else { + instQueue.scheduleNonSpec( + fromCommit->commitInfo[tid].nonSpecSeqNum); + } + } + + if (broadcast_free_entries) { + toFetch->iewInfo[tid].iqCount = + instQueue.getCount(tid); + toFetch->iewInfo[tid].ldstqCount = + ldstQueue.getCount(tid); + + toRename->iewInfo[tid].usedIQ = true; + toRename->iewInfo[tid].freeIQEntries = + instQueue.numFreeEntries(); + toRename->iewInfo[tid].usedLSQ = true; + toRename->iewInfo[tid].freeLSQEntries = + ldstQueue.numFreeEntries(tid); + + wroteToTimeBuffer = true; + } + + DPRINTF(IEW, "[tid:%i], Dispatch dispatched %i instructions.\n", + tid, toRename->iewInfo[tid].dispatched); + } + + DPRINTF(IEW, "IQ has %i free entries (Can schedule: %i). " + "LSQ has %i free entries.\n", + instQueue.numFreeEntries(), instQueue.hasReadyInsts(), + ldstQueue.numFreeEntries()); + + updateStatus(); + + if (wroteToTimeBuffer) { + DPRINTF(Activity, "Activity this cycle.\n"); + cpu->activityThisCycle(); + } +} + +template <class Impl> +void +DefaultIEW<Impl>::updateExeInstStats(DynInstPtr &inst) +{ + int thread_number = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) + iewExecutedSwp[thread_number]++; + else + iewIewExecutedcutedInsts++; +#else + iewExecutedInsts++; +#endif + + // + // Control operations + // + if (inst->isControl()) + iewExecutedBranches[thread_number]++; + + // + // Memory operations + // + if (inst->isMemRef()) { + iewExecutedRefs[thread_number]++; + + if (inst->isLoad()) { + iewExecLoadInsts[thread_number]++; + } + } +} diff --git a/src/cpu/o3/inst_queue.cc b/src/cpu/o3/inst_queue.cc new file mode 100644 index 000000000..a539066f9 --- /dev/null +++ b/src/cpu/o3/inst_queue.cc @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/inst_queue_impl.hh" + +// Force instantiation of InstructionQueue. +template class InstructionQueue<O3CPUImpl>; diff --git a/src/cpu/o3/inst_queue.hh b/src/cpu/o3/inst_queue.hh new file mode 100644 index 000000000..3dd4dc658 --- /dev/null +++ b/src/cpu/o3/inst_queue.hh @@ -0,0 +1,507 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_INST_QUEUE_HH__ +#define __CPU_O3_INST_QUEUE_HH__ + +#include <list> +#include <map> +#include <queue> +#include <vector> + +#include "base/statistics.hh" +#include "base/timebuf.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/dep_graph.hh" +#include "cpu/op_class.hh" +#include "sim/host.hh" + +class FUPool; +class MemInterface; + +/** + * A standard instruction queue class. It holds ready instructions, in + * order, in seperate priority queues to facilitate the scheduling of + * instructions. The IQ uses a separate linked list to track dependencies. + * Similar to the rename map and the free list, it expects that + * floating point registers have their indices start after the integer + * registers (ie with 96 int and 96 fp registers, regs 0-95 are integer + * and 96-191 are fp). This remains true even for both logical and + * physical register indices. The IQ depends on the memory dependence unit to + * track when memory operations are ready in terms of ordering; register + * dependencies are tracked normally. Right now the IQ also handles the + * execution timing; this is mainly to allow back-to-back scheduling without + * requiring IEW to be able to peek into the IQ. At the end of the execution + * latency, the instruction is put into the queue to execute, where it will + * have the execute() function called on it. + * @todo: Make IQ able to handle multiple FU pools. + */ +template <class Impl> +class InstructionQueue +{ + public: + //Typedefs from the Impl. + typedef typename Impl::O3CPU O3CPU; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::Params Params; + + typedef typename Impl::CPUPol::IEW IEW; + typedef typename Impl::CPUPol::MemDepUnit MemDepUnit; + typedef typename Impl::CPUPol::IssueStruct IssueStruct; + typedef typename Impl::CPUPol::TimeStruct TimeStruct; + + // Typedef of iterator through the list of instructions. + typedef typename std::list<DynInstPtr>::iterator ListIt; + + friend class Impl::O3CPU; + + /** FU completion event class. */ + class FUCompletion : public Event { + private: + /** Executing instruction. */ + DynInstPtr inst; + + /** Index of the FU used for executing. */ + int fuIdx; + + /** Pointer back to the instruction queue. */ + InstructionQueue<Impl> *iqPtr; + + /** Should the FU be added to the list to be freed upon + * completing this event. + */ + bool freeFU; + + public: + /** Construct a FU completion event. */ + FUCompletion(DynInstPtr &_inst, int fu_idx, + InstructionQueue<Impl> *iq_ptr); + + virtual void process(); + virtual const char *description(); + void setFreeFU() { freeFU = true; } + }; + + /** Constructs an IQ. */ + InstructionQueue(Params *params); + + /** Destructs the IQ. */ + ~InstructionQueue(); + + /** Returns the name of the IQ. */ + std::string name() const; + + /** Registers statistics. */ + void regStats(); + + /** Resets all instruction queue state. */ + void resetState(); + + /** Sets CPU pointer. */ + void setCPU(O3CPU *_cpu) { cpu = _cpu; } + + /** Sets active threads list. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Sets the IEW pointer. */ + void setIEW(IEW *iew_ptr) { iewStage = iew_ptr; } + + /** Sets the timer buffer between issue and execute. */ + void setIssueToExecuteQueue(TimeBuffer<IssueStruct> *i2eQueue); + + /** Sets the global time buffer. */ + void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + + /** Switches out the instruction queue. */ + void switchOut(); + + /** Takes over execution from another CPU's thread. */ + void takeOverFrom(); + + /** Returns if the IQ is switched out. */ + bool isSwitchedOut() { return switchedOut; } + + /** Number of entries needed for given amount of threads. */ + int entryAmount(int num_threads); + + /** Resets max entries for all threads. */ + void resetEntries(); + + /** Returns total number of free entries. */ + unsigned numFreeEntries(); + + /** Returns number of free entries for a thread. */ + unsigned numFreeEntries(unsigned tid); + + /** Returns whether or not the IQ is full. */ + bool isFull(); + + /** Returns whether or not the IQ is full for a specific thread. */ + bool isFull(unsigned tid); + + /** Returns if there are any ready instructions in the IQ. */ + bool hasReadyInsts(); + + /** Inserts a new instruction into the IQ. */ + void insert(DynInstPtr &new_inst); + + /** Inserts a new, non-speculative instruction into the IQ. */ + void insertNonSpec(DynInstPtr &new_inst); + + /** Inserts a memory or write barrier into the IQ to make sure + * loads and stores are ordered properly. + */ + void insertBarrier(DynInstPtr &barr_inst); + + /** Returns the oldest scheduled instruction, and removes it from + * the list of instructions waiting to execute. + */ + DynInstPtr getInstToExecute(); + + /** + * Records the instruction as the producer of a register without + * adding it to the rest of the IQ. + */ + void recordProducer(DynInstPtr &inst) + { addToProducers(inst); } + + /** Process FU completion event. */ + void processFUCompletion(DynInstPtr &inst, int fu_idx); + + /** + * Schedules ready instructions, adding the ready ones (oldest first) to + * the queue to execute. + */ + void scheduleReadyInsts(); + + /** Schedules a single specific non-speculative instruction. */ + void scheduleNonSpec(const InstSeqNum &inst); + + /** + * Commits all instructions up to and including the given sequence number, + * for a specific thread. + */ + void commit(const InstSeqNum &inst, unsigned tid = 0); + + /** Wakes all dependents of a completed instruction. */ + int wakeDependents(DynInstPtr &completed_inst); + + /** Adds a ready memory instruction to the ready list. */ + void addReadyMemInst(DynInstPtr &ready_inst); + + /** + * Reschedules a memory instruction. It will be ready to issue once + * replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &resched_inst); + + /** Replays a memory instruction. It must be rescheduled first. */ + void replayMemInst(DynInstPtr &replay_inst); + + /** Completes a memory operation. */ + void completeMemInst(DynInstPtr &completed_inst); + + /** Indicates an ordering violation between a store and a load. */ + void violation(DynInstPtr &store, DynInstPtr &faulting_load); + + /** + * Squashes instructions for a thread. Squashing information is obtained + * from the time buffer. + */ + void squash(unsigned tid); + + /** Returns the number of used entries for a thread. */ + unsigned getCount(unsigned tid) { return count[tid]; }; + + /** Debug function to print all instructions. */ + void printInsts(); + + private: + /** Does the actual squashing. */ + void doSquash(unsigned tid); + + ///////////////////////// + // Various pointers + ///////////////////////// + + /** Pointer to the CPU. */ + O3CPU *cpu; + + /** Cache interface. */ + MemInterface *dcacheInterface; + + /** Pointer to IEW stage. */ + IEW *iewStage; + + /** The memory dependence unit, which tracks/predicts memory dependences + * between instructions. + */ + MemDepUnit memDepUnit[Impl::MaxThreads]; + + /** The queue to the execute stage. Issued instructions will be written + * into it. + */ + TimeBuffer<IssueStruct> *issueToExecuteQueue; + + /** The backwards time buffer. */ + TimeBuffer<TimeStruct> *timeBuffer; + + /** Wire to read information from timebuffer. */ + typename TimeBuffer<TimeStruct>::wire fromCommit; + + /** Function unit pool. */ + FUPool *fuPool; + + ////////////////////////////////////// + // Instruction lists, ready queues, and ordering + ////////////////////////////////////// + + /** List of all the instructions in the IQ (some of which may be issued). */ + std::list<DynInstPtr> instList[Impl::MaxThreads]; + + /** List of instructions that are ready to be executed. */ + std::list<DynInstPtr> instsToExecute; + + /** + * Struct for comparing entries to be added to the priority queue. + * This gives reverse ordering to the instructions in terms of + * sequence numbers: the instructions with smaller sequence + * numbers (and hence are older) will be at the top of the + * priority queue. + */ + struct pqCompare { + bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const + { + return lhs->seqNum > rhs->seqNum; + } + }; + + typedef std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> + ReadyInstQueue; + + /** List of ready instructions, per op class. They are separated by op + * class to allow for easy mapping to FUs. + */ + ReadyInstQueue readyInsts[Num_OpClasses]; + + /** List of non-speculative instructions that will be scheduled + * once the IQ gets a signal from commit. While it's redundant to + * have the key be a part of the value (the sequence number is stored + * inside of DynInst), when these instructions are woken up only + * the sequence number will be available. Thus it is most efficient to be + * able to search by the sequence number alone. + */ + std::map<InstSeqNum, DynInstPtr> nonSpecInsts; + + typedef typename std::map<InstSeqNum, DynInstPtr>::iterator NonSpecMapIt; + + /** Entry for the list age ordering by op class. */ + struct ListOrderEntry { + OpClass queueType; + InstSeqNum oldestInst; + }; + + /** List that contains the age order of the oldest instruction of each + * ready queue. Used to select the oldest instruction available + * among op classes. + * @todo: Might be better to just move these entries around instead + * of creating new ones every time the position changes due to an + * instruction issuing. Not sure std::list supports this. + */ + std::list<ListOrderEntry> listOrder; + + typedef typename std::list<ListOrderEntry>::iterator ListOrderIt; + + /** Tracks if each ready queue is on the age order list. */ + bool queueOnList[Num_OpClasses]; + + /** Iterators of each ready queue. Points to their spot in the age order + * list. + */ + ListOrderIt readyIt[Num_OpClasses]; + + /** Add an op class to the age order list. */ + void addToOrderList(OpClass op_class); + + /** + * Called when the oldest instruction has been removed from a ready queue; + * this places that ready queue into the proper spot in the age order list. + */ + void moveToYoungerInst(ListOrderIt age_order_it); + + DependencyGraph<DynInstPtr> dependGraph; + + ////////////////////////////////////// + // Various parameters + ////////////////////////////////////// + + /** IQ Resource Sharing Policy */ + enum IQPolicy { + Dynamic, + Partitioned, + Threshold + }; + + /** IQ sharing policy for SMT. */ + IQPolicy iqPolicy; + + /** Number of Total Threads*/ + unsigned numThreads; + + /** Pointer to list of active threads. */ + std::list<unsigned> *activeThreads; + + /** Per Thread IQ count */ + unsigned count[Impl::MaxThreads]; + + /** Max IQ Entries Per Thread */ + unsigned maxEntries[Impl::MaxThreads]; + + /** Number of free IQ entries left. */ + unsigned freeEntries; + + /** The number of entries in the instruction queue. */ + unsigned numEntries; + + /** The total number of instructions that can be issued in one cycle. */ + unsigned totalWidth; + + /** The number of physical registers in the CPU. */ + unsigned numPhysRegs; + + /** The number of physical integer registers in the CPU. */ + unsigned numPhysIntRegs; + + /** The number of floating point registers in the CPU. */ + unsigned numPhysFloatRegs; + + /** Delay between commit stage and the IQ. + * @todo: Make there be a distinction between the delays within IEW. + */ + unsigned commitToIEWDelay; + + /** Is the IQ switched out. */ + bool switchedOut; + + /** The sequence number of the squashed instruction. */ + InstSeqNum squashedSeqNum[Impl::MaxThreads]; + + /** A cache of the recently woken registers. It is 1 if the register + * has been woken up recently, and 0 if the register has been added + * to the dependency graph and has not yet received its value. It + * is basically a secondary scoreboard, and should pretty much mirror + * the scoreboard that exists in the rename map. + */ + std::vector<bool> regScoreboard; + + /** Adds an instruction to the dependency graph, as a consumer. */ + bool addToDependents(DynInstPtr &new_inst); + + /** Adds an instruction to the dependency graph, as a producer. */ + void addToProducers(DynInstPtr &new_inst); + + /** Moves an instruction to the ready queue if it is ready. */ + void addIfReady(DynInstPtr &inst); + + /** Debugging function to count how many entries are in the IQ. It does + * a linear walk through the instructions, so do not call this function + * during normal execution. + */ + int countInsts(); + + /** Debugging function to dump all the list sizes, as well as print + * out the list of nonspeculative instructions. Should not be used + * in any other capacity, but it has no harmful sideaffects. + */ + void dumpLists(); + + /** Debugging function to dump out all instructions that are in the + * IQ. + */ + void dumpInsts(); + + /** Stat for number of instructions added. */ + Stats::Scalar<> iqInstsAdded; + /** Stat for number of non-speculative instructions added. */ + Stats::Scalar<> iqNonSpecInstsAdded; + + Stats::Scalar<> iqInstsIssued; + /** Stat for number of integer instructions issued. */ + Stats::Scalar<> iqIntInstsIssued; + /** Stat for number of floating point instructions issued. */ + Stats::Scalar<> iqFloatInstsIssued; + /** Stat for number of branch instructions issued. */ + Stats::Scalar<> iqBranchInstsIssued; + /** Stat for number of memory instructions issued. */ + Stats::Scalar<> iqMemInstsIssued; + /** Stat for number of miscellaneous instructions issued. */ + Stats::Scalar<> iqMiscInstsIssued; + /** Stat for number of squashed instructions that were ready to issue. */ + Stats::Scalar<> iqSquashedInstsIssued; + /** Stat for number of squashed instructions examined when squashing. */ + Stats::Scalar<> iqSquashedInstsExamined; + /** Stat for number of squashed instruction operands examined when + * squashing. + */ + Stats::Scalar<> iqSquashedOperandsExamined; + /** Stat for number of non-speculative instructions removed due to a squash. + */ + Stats::Scalar<> iqSquashedNonSpecRemoved; + // Also include number of instructions rescheduled and replayed. + + /** Distribution of number of instructions in the queue. + * @todo: Need to create struct to track the entry time for each + * instruction. */ +// Stats::VectorDistribution<> queueResDist; + /** Distribution of the number of instructions issued. */ + Stats::Distribution<> numIssuedDist; + /** Distribution of the cycles it takes to issue an instruction. + * @todo: Need to create struct to track the ready time for each + * instruction. */ +// Stats::VectorDistribution<> issueDelayDist; + + /** Number of times an instruction could not be issued because a + * FU was busy. + */ + Stats::Vector<> statFuBusy; +// Stats::Vector<> dist_unissued; + /** Stat for total number issued for each instruction type. */ + Stats::Vector2d<> statIssuedInstType; + + /** Number of instructions issued per cycle. */ + Stats::Formula issueRate; + + /** Number of times the FU was busy. */ + Stats::Vector<> fuBusy; + /** Number of times the FU was busy per instruction issued. */ + Stats::Formula fuBusyRate; +}; + +#endif //__CPU_O3_INST_QUEUE_HH__ diff --git a/src/cpu/o3/inst_queue_impl.hh b/src/cpu/o3/inst_queue_impl.hh new file mode 100644 index 000000000..6edb528a9 --- /dev/null +++ b/src/cpu/o3/inst_queue_impl.hh @@ -0,0 +1,1405 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include <limits> +#include <vector> + +#include "sim/root.hh" + +#include "cpu/o3/fu_pool.hh" +#include "cpu/o3/inst_queue.hh" + +template <class Impl> +InstructionQueue<Impl>::FUCompletion::FUCompletion(DynInstPtr &_inst, + int fu_idx, + InstructionQueue<Impl> *iq_ptr) + : Event(&mainEventQueue, Stat_Event_Pri), + inst(_inst), fuIdx(fu_idx), iqPtr(iq_ptr), freeFU(false) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +InstructionQueue<Impl>::FUCompletion::process() +{ + iqPtr->processFUCompletion(inst, freeFU ? fuIdx : -1); + inst = NULL; +} + + +template <class Impl> +const char * +InstructionQueue<Impl>::FUCompletion::description() +{ + return "Functional unit completion event"; +} + +template <class Impl> +InstructionQueue<Impl>::InstructionQueue(Params *params) + : fuPool(params->fuPool), + numEntries(params->numIQEntries), + totalWidth(params->issueWidth), + numPhysIntRegs(params->numPhysIntRegs), + numPhysFloatRegs(params->numPhysFloatRegs), + commitToIEWDelay(params->commitToIEWDelay) +{ + assert(fuPool); + + switchedOut = false; + + numThreads = params->numberOfThreads; + + // Set the number of physical registers as the number of int + float + numPhysRegs = numPhysIntRegs + numPhysFloatRegs; + + DPRINTF(IQ, "There are %i physical registers.\n", numPhysRegs); + + //Create an entry for each physical register within the + //dependency graph. + dependGraph.resize(numPhysRegs); + + // Resize the register scoreboard. + regScoreboard.resize(numPhysRegs); + + //Initialize Mem Dependence Units + for (int i = 0; i < numThreads; i++) { + memDepUnit[i].init(params,i); + memDepUnit[i].setIQ(this); + } + + resetState(); + + std::string policy = params->smtIQPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Figure out resource sharing policy + if (policy == "dynamic") { + iqPolicy = Dynamic; + + //Set Max Entries to Total ROB Capacity + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = numEntries; + } + + } else if (policy == "partitioned") { + iqPolicy = Partitioned; + + //@todo:make work if part_amt doesnt divide evenly. + int part_amt = numEntries / numThreads; + + //Divide ROB up evenly + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = part_amt; + } + + DPRINTF(IQ, "IQ sharing policy set to Partitioned:" + "%i entries per thread.\n",part_amt); + + } else if (policy == "threshold") { + iqPolicy = Threshold; + + double threshold = (double)params->smtIQThreshold / 100; + + int thresholdIQ = (int)((double)threshold * numEntries); + + //Divide up by threshold amount + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = thresholdIQ; + } + + DPRINTF(IQ, "IQ sharing policy set to Threshold:" + "%i entries per thread.\n",thresholdIQ); + } else { + assert(0 && "Invalid IQ Sharing Policy.Options Are:{Dynamic," + "Partitioned, Threshold}"); + } +} + +template <class Impl> +InstructionQueue<Impl>::~InstructionQueue() +{ + dependGraph.reset(); +#ifdef DEBUG + cprintf("Nodes traversed: %i, removed: %i\n", + dependGraph.nodesTraversed, dependGraph.nodesRemoved); +#endif +} + +template <class Impl> +std::string +InstructionQueue<Impl>::name() const +{ + return cpu->name() + ".iq"; +} + +template <class Impl> +void +InstructionQueue<Impl>::regStats() +{ + using namespace Stats; + iqInstsAdded + .name(name() + ".iqInstsAdded") + .desc("Number of instructions added to the IQ (excludes non-spec)") + .prereq(iqInstsAdded); + + iqNonSpecInstsAdded + .name(name() + ".iqNonSpecInstsAdded") + .desc("Number of non-speculative instructions added to the IQ") + .prereq(iqNonSpecInstsAdded); + + iqInstsIssued + .name(name() + ".iqInstsIssued") + .desc("Number of instructions issued") + .prereq(iqInstsIssued); + + iqIntInstsIssued + .name(name() + ".iqIntInstsIssued") + .desc("Number of integer instructions issued") + .prereq(iqIntInstsIssued); + + iqFloatInstsIssued + .name(name() + ".iqFloatInstsIssued") + .desc("Number of float instructions issued") + .prereq(iqFloatInstsIssued); + + iqBranchInstsIssued + .name(name() + ".iqBranchInstsIssued") + .desc("Number of branch instructions issued") + .prereq(iqBranchInstsIssued); + + iqMemInstsIssued + .name(name() + ".iqMemInstsIssued") + .desc("Number of memory instructions issued") + .prereq(iqMemInstsIssued); + + iqMiscInstsIssued + .name(name() + ".iqMiscInstsIssued") + .desc("Number of miscellaneous instructions issued") + .prereq(iqMiscInstsIssued); + + iqSquashedInstsIssued + .name(name() + ".iqSquashedInstsIssued") + .desc("Number of squashed instructions issued") + .prereq(iqSquashedInstsIssued); + + iqSquashedInstsExamined + .name(name() + ".iqSquashedInstsExamined") + .desc("Number of squashed instructions iterated over during squash;" + " mainly for profiling") + .prereq(iqSquashedInstsExamined); + + iqSquashedOperandsExamined + .name(name() + ".iqSquashedOperandsExamined") + .desc("Number of squashed operands that are examined and possibly " + "removed from graph") + .prereq(iqSquashedOperandsExamined); + + iqSquashedNonSpecRemoved + .name(name() + ".iqSquashedNonSpecRemoved") + .desc("Number of squashed non-spec instructions that were removed") + .prereq(iqSquashedNonSpecRemoved); +/* + queueResDist + .init(Num_OpClasses, 0, 99, 2) + .name(name() + ".IQ:residence:") + .desc("cycles from dispatch to issue") + .flags(total | pdf | cdf ) + ; + for (int i = 0; i < Num_OpClasses; ++i) { + queueResDist.subname(i, opClassStrings[i]); + } +*/ + numIssuedDist + .init(0,totalWidth,1) + .name(name() + ".ISSUE:issued_per_cycle") + .desc("Number of insts issued each cycle") + .flags(pdf) + ; +/* + dist_unissued + .init(Num_OpClasses+2) + .name(name() + ".ISSUE:unissued_cause") + .desc("Reason ready instruction not issued") + .flags(pdf | dist) + ; + for (int i=0; i < (Num_OpClasses + 2); ++i) { + dist_unissued.subname(i, unissued_names[i]); + } +*/ + statIssuedInstType + .init(numThreads,Num_OpClasses) + .name(name() + ".ISSUE:FU_type") + .desc("Type of FU issued") + .flags(total | pdf | dist) + ; + statIssuedInstType.ysubnames(opClassStrings); + + // + // How long did instructions for a particular FU type wait prior to issue + // +/* + issueDelayDist + .init(Num_OpClasses,0,99,2) + .name(name() + ".ISSUE:") + .desc("cycles from operands ready to issue") + .flags(pdf | cdf) + ; + + for (int i=0; i<Num_OpClasses; ++i) { + std::stringstream subname; + subname << opClassStrings[i] << "_delay"; + issueDelayDist.subname(i, subname.str()); + } +*/ + issueRate + .name(name() + ".ISSUE:rate") + .desc("Inst issue rate") + .flags(total) + ; + issueRate = iqInstsIssued / cpu->numCycles; + + statFuBusy + .init(Num_OpClasses) + .name(name() + ".ISSUE:fu_full") + .desc("attempts to use FU when none available") + .flags(pdf | dist) + ; + for (int i=0; i < Num_OpClasses; ++i) { + statFuBusy.subname(i, opClassStrings[i]); + } + + fuBusy + .init(numThreads) + .name(name() + ".ISSUE:fu_busy_cnt") + .desc("FU busy when requested") + .flags(total) + ; + + fuBusyRate + .name(name() + ".ISSUE:fu_busy_rate") + .desc("FU busy rate (busy events/executed inst)") + .flags(total) + ; + fuBusyRate = fuBusy / iqInstsIssued; + + for ( int i=0; i < numThreads; i++) { + // Tell mem dependence unit to reg stats as well. + memDepUnit[i].regStats(); + } +} + +template <class Impl> +void +InstructionQueue<Impl>::resetState() +{ + //Initialize thread IQ counts + for (int i = 0; i <numThreads; i++) { + count[i] = 0; + instList[i].clear(); + } + + // Initialize the number of free IQ entries. + freeEntries = numEntries; + + // Note that in actuality, the registers corresponding to the logical + // registers start off as ready. However this doesn't matter for the + // IQ as the instruction should have been correctly told if those + // registers are ready in rename. Thus it can all be initialized as + // unready. + for (int i = 0; i < numPhysRegs; ++i) { + regScoreboard[i] = false; + } + + for (int i = 0; i < numThreads; ++i) { + squashedSeqNum[i] = 0; + } + + for (int i = 0; i < Num_OpClasses; ++i) { + while (!readyInsts[i].empty()) + readyInsts[i].pop(); + queueOnList[i] = false; + readyIt[i] = listOrder.end(); + } + nonSpecInsts.clear(); + listOrder.clear(); +} + +template <class Impl> +void +InstructionQueue<Impl>::setActiveThreads(std::list<unsigned> *at_ptr) +{ + DPRINTF(IQ, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template <class Impl> +void +InstructionQueue<Impl>::setIssueToExecuteQueue(TimeBuffer<IssueStruct> *i2e_ptr) +{ + DPRINTF(IQ, "Set the issue to execute queue.\n"); + issueToExecuteQueue = i2e_ptr; +} + +template <class Impl> +void +InstructionQueue<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +{ + DPRINTF(IQ, "Set the time buffer.\n"); + timeBuffer = tb_ptr; + + fromCommit = timeBuffer->getWire(-commitToIEWDelay); +} + +template <class Impl> +void +InstructionQueue<Impl>::switchOut() +{ +/* + if (!instList[0].empty() || (numEntries != freeEntries) || + !readyInsts[0].empty() || !nonSpecInsts.empty() || !listOrder.empty()) { + dumpInsts(); +// assert(0); + } +*/ + resetState(); + dependGraph.reset(); + instsToExecute.clear(); + switchedOut = true; + for (int i = 0; i < numThreads; ++i) { + memDepUnit[i].switchOut(); + } +} + +template <class Impl> +void +InstructionQueue<Impl>::takeOverFrom() +{ + switchedOut = false; +} + +template <class Impl> +int +InstructionQueue<Impl>::entryAmount(int num_threads) +{ + if (iqPolicy == Partitioned) { + return numEntries / num_threads; + } else { + return 0; + } +} + + +template <class Impl> +void +InstructionQueue<Impl>::resetEntries() +{ + if (iqPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + std::list<unsigned>::iterator list_end = (*activeThreads).end(); + + while (threads != list_end) { + if (iqPolicy == Partitioned) { + maxEntries[*threads++] = numEntries / active_threads; + } else if(iqPolicy == Threshold && active_threads == 1) { + maxEntries[*threads++] = numEntries; + } + } + } +} + +template <class Impl> +unsigned +InstructionQueue<Impl>::numFreeEntries() +{ + return freeEntries; +} + +template <class Impl> +unsigned +InstructionQueue<Impl>::numFreeEntries(unsigned tid) +{ + return maxEntries[tid] - count[tid]; +} + +// Might want to do something more complex if it knows how many instructions +// will be issued this cycle. +template <class Impl> +bool +InstructionQueue<Impl>::isFull() +{ + if (freeEntries == 0) { + return(true); + } else { + return(false); + } +} + +template <class Impl> +bool +InstructionQueue<Impl>::isFull(unsigned tid) +{ + if (numFreeEntries(tid) == 0) { + return(true); + } else { + return(false); + } +} + +template <class Impl> +bool +InstructionQueue<Impl>::hasReadyInsts() +{ + if (!listOrder.empty()) { + return true; + } + + for (int i = 0; i < Num_OpClasses; ++i) { + if (!readyInsts[i].empty()) { + return true; + } + } + + return false; +} + +template <class Impl> +void +InstructionQueue<Impl>::insert(DynInstPtr &new_inst) +{ + // Make sure the instruction is valid + assert(new_inst); + + DPRINTF(IQ, "Adding instruction [sn:%lli] PC %#x to the IQ.\n", + new_inst->seqNum, new_inst->readPC()); + + assert(freeEntries != 0); + + instList[new_inst->threadNumber].push_back(new_inst); + + --freeEntries; + + new_inst->setInIQ(); + + // Look through its source registers (physical regs), and mark any + // dependencies. + addToDependents(new_inst); + + // Have this instruction set itself as the producer of its destination + // register(s). + addToProducers(new_inst); + + if (new_inst->isMemRef()) { + memDepUnit[new_inst->threadNumber].insert(new_inst); + } else { + addIfReady(new_inst); + } + + ++iqInstsAdded; + + count[new_inst->threadNumber]++; + + assert(freeEntries == (numEntries - countInsts())); +} + +template <class Impl> +void +InstructionQueue<Impl>::insertNonSpec(DynInstPtr &new_inst) +{ + // @todo: Clean up this code; can do it by setting inst as unable + // to issue, then calling normal insert on the inst. + + assert(new_inst); + + nonSpecInsts[new_inst->seqNum] = new_inst; + + DPRINTF(IQ, "Adding non-speculative instruction [sn:%lli] PC %#x " + "to the IQ.\n", + new_inst->seqNum, new_inst->readPC()); + + assert(freeEntries != 0); + + instList[new_inst->threadNumber].push_back(new_inst); + + --freeEntries; + + new_inst->setInIQ(); + + // Have this instruction set itself as the producer of its destination + // register(s). + addToProducers(new_inst); + + // If it's a memory instruction, add it to the memory dependency + // unit. + if (new_inst->isMemRef()) { + memDepUnit[new_inst->threadNumber].insertNonSpec(new_inst); + } + + ++iqNonSpecInstsAdded; + + count[new_inst->threadNumber]++; + + assert(freeEntries == (numEntries - countInsts())); +} + +template <class Impl> +void +InstructionQueue<Impl>::insertBarrier(DynInstPtr &barr_inst) +{ + memDepUnit[barr_inst->threadNumber].insertBarrier(barr_inst); + + insertNonSpec(barr_inst); +} + +template <class Impl> +typename Impl::DynInstPtr +InstructionQueue<Impl>::getInstToExecute() +{ + assert(!instsToExecute.empty()); + DynInstPtr inst = instsToExecute.front(); + instsToExecute.pop_front(); + return inst; +} + +template <class Impl> +void +InstructionQueue<Impl>::addToOrderList(OpClass op_class) +{ + assert(!readyInsts[op_class].empty()); + + ListOrderEntry queue_entry; + + queue_entry.queueType = op_class; + + queue_entry.oldestInst = readyInsts[op_class].top()->seqNum; + + ListOrderIt list_it = listOrder.begin(); + ListOrderIt list_end_it = listOrder.end(); + + while (list_it != list_end_it) { + if ((*list_it).oldestInst > queue_entry.oldestInst) { + break; + } + + list_it++; + } + + readyIt[op_class] = listOrder.insert(list_it, queue_entry); + queueOnList[op_class] = true; +} + +template <class Impl> +void +InstructionQueue<Impl>::moveToYoungerInst(ListOrderIt list_order_it) +{ + // Get iterator of next item on the list + // Delete the original iterator + // Determine if the next item is either the end of the list or younger + // than the new instruction. If so, then add in a new iterator right here. + // If not, then move along. + ListOrderEntry queue_entry; + OpClass op_class = (*list_order_it).queueType; + ListOrderIt next_it = list_order_it; + + ++next_it; + + queue_entry.queueType = op_class; + queue_entry.oldestInst = readyInsts[op_class].top()->seqNum; + + while (next_it != listOrder.end() && + (*next_it).oldestInst < queue_entry.oldestInst) { + ++next_it; + } + + readyIt[op_class] = listOrder.insert(next_it, queue_entry); +} + +template <class Impl> +void +InstructionQueue<Impl>::processFUCompletion(DynInstPtr &inst, int fu_idx) +{ + DPRINTF(IQ, "Processing FU completion [sn:%lli]\n", inst->seqNum); + // The CPU could have been sleeping until this op completed (*extremely* + // long latency op). Wake it if it was. This may be overkill. + if (isSwitchedOut()) { + DPRINTF(IQ, "FU completion not processed, IQ is switched out [sn:%lli]\n", + inst->seqNum); + return; + } + + iewStage->wakeCPU(); + + if (fu_idx > -1) + fuPool->freeUnitNextCycle(fu_idx); + + // @todo: Ensure that these FU Completions happen at the beginning + // of a cycle, otherwise they could add too many instructions to + // the queue. + issueToExecuteQueue->access(0)->size++; + instsToExecute.push_back(inst); +} + +// @todo: Figure out a better way to remove the squashed items from the +// lists. Checking the top item of each list to see if it's squashed +// wastes time and forces jumps. +template <class Impl> +void +InstructionQueue<Impl>::scheduleReadyInsts() +{ + DPRINTF(IQ, "Attempting to schedule ready instructions from " + "the IQ.\n"); + + IssueStruct *i2e_info = issueToExecuteQueue->access(0); + + // Have iterator to head of the list + // While I haven't exceeded bandwidth or reached the end of the list, + // Try to get a FU that can do what this op needs. + // If successful, change the oldestInst to the new top of the list, put + // the queue in the proper place in the list. + // Increment the iterator. + // This will avoid trying to schedule a certain op class if there are no + // FUs that handle it. + ListOrderIt order_it = listOrder.begin(); + ListOrderIt order_end_it = listOrder.end(); + int total_issued = 0; + + while (total_issued < totalWidth && + iewStage->canIssue() && + order_it != order_end_it) { + OpClass op_class = (*order_it).queueType; + + assert(!readyInsts[op_class].empty()); + + DynInstPtr issuing_inst = readyInsts[op_class].top(); + + assert(issuing_inst->seqNum == (*order_it).oldestInst); + + if (issuing_inst->isSquashed()) { + readyInsts[op_class].pop(); + + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; + } + + listOrder.erase(order_it++); + + ++iqSquashedInstsIssued; + + continue; + } + + int idx = -2; + int op_latency = 1; + int tid = issuing_inst->threadNumber; + + if (op_class != No_OpClass) { + idx = fuPool->getUnit(op_class); + + if (idx > -1) { + op_latency = fuPool->getOpLatency(op_class); + } + } + + // If we have an instruction that doesn't require a FU, or a + // valid FU, then schedule for execution. + if (idx == -2 || idx != -1) { + if (op_latency == 1) { + i2e_info->size++; + instsToExecute.push_back(issuing_inst); + + // Add the FU onto the list of FU's to be freed next + // cycle if we used one. + if (idx >= 0) + fuPool->freeUnitNextCycle(idx); + } else { + int issue_latency = fuPool->getIssueLatency(op_class); + // Generate completion event for the FU + FUCompletion *execution = new FUCompletion(issuing_inst, + idx, this); + + execution->schedule(curTick + cpu->cycles(issue_latency - 1)); + + // @todo: Enforce that issue_latency == 1 or op_latency + if (issue_latency > 1) { + // If FU isn't pipelined, then it must be freed + // upon the execution completing. + execution->setFreeFU(); + } else { + // Add the FU onto the list of FU's to be freed next cycle. + fuPool->freeUnitNextCycle(idx); + } + } + + DPRINTF(IQ, "Thread %i: Issuing instruction PC %#x " + "[sn:%lli]\n", + tid, issuing_inst->readPC(), + issuing_inst->seqNum); + + readyInsts[op_class].pop(); + + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; + } + + issuing_inst->setIssued(); + ++total_issued; + + if (!issuing_inst->isMemRef()) { + // Memory instructions can not be freed from the IQ until they + // complete. + ++freeEntries; + count[tid]--; + issuing_inst->clearInIQ(); + } else { + memDepUnit[tid].issue(issuing_inst); + } + + listOrder.erase(order_it++); + statIssuedInstType[tid][op_class]++; + iewStage->incrWb(issuing_inst->seqNum); + } else { + statFuBusy[op_class]++; + fuBusy[tid]++; + ++order_it; + } + } + + numIssuedDist.sample(total_issued); + iqInstsIssued+= total_issued; + + // If we issued any instructions, tell the CPU we had activity. + if (total_issued) { + cpu->activityThisCycle(); + } else { + DPRINTF(IQ, "Not able to schedule any instructions.\n"); + } +} + +template <class Impl> +void +InstructionQueue<Impl>::scheduleNonSpec(const InstSeqNum &inst) +{ + DPRINTF(IQ, "Marking nonspeculative instruction [sn:%lli] as ready " + "to execute.\n", inst); + + NonSpecMapIt inst_it = nonSpecInsts.find(inst); + + assert(inst_it != nonSpecInsts.end()); + + unsigned tid = (*inst_it).second->threadNumber; + + (*inst_it).second->setCanIssue(); + + if (!(*inst_it).second->isMemRef()) { + addIfReady((*inst_it).second); + } else { + memDepUnit[tid].nonSpecInstReady((*inst_it).second); + } + + (*inst_it).second = NULL; + + nonSpecInsts.erase(inst_it); +} + +template <class Impl> +void +InstructionQueue<Impl>::commit(const InstSeqNum &inst, unsigned tid) +{ + DPRINTF(IQ, "[tid:%i]: Committing instructions older than [sn:%i]\n", + tid,inst); + + ListIt iq_it = instList[tid].begin(); + + while (iq_it != instList[tid].end() && + (*iq_it)->seqNum <= inst) { + ++iq_it; + instList[tid].pop_front(); + } + + assert(freeEntries == (numEntries - countInsts())); +} + +template <class Impl> +int +InstructionQueue<Impl>::wakeDependents(DynInstPtr &completed_inst) +{ + int dependents = 0; + + DPRINTF(IQ, "Waking dependents of completed instruction.\n"); + + assert(!completed_inst->isSquashed()); + + // Tell the memory dependence unit to wake any dependents on this + // instruction if it is a memory instruction. Also complete the memory + // instruction at this point since we know it executed without issues. + // @todo: Might want to rename "completeMemInst" to something that + // indicates that it won't need to be replayed, and call this + // earlier. Might not be a big deal. + if (completed_inst->isMemRef()) { + memDepUnit[completed_inst->threadNumber].wakeDependents(completed_inst); + completeMemInst(completed_inst); + } else if (completed_inst->isMemBarrier() || + completed_inst->isWriteBarrier()) { + memDepUnit[completed_inst->threadNumber].completeBarrier(completed_inst); + } + + for (int dest_reg_idx = 0; + dest_reg_idx < completed_inst->numDestRegs(); + dest_reg_idx++) + { + PhysRegIndex dest_reg = + completed_inst->renamedDestRegIdx(dest_reg_idx); + + // Special case of uniq or control registers. They are not + // handled by the IQ and thus have no dependency graph entry. + // @todo Figure out a cleaner way to handle this. + if (dest_reg >= numPhysRegs) { + continue; + } + + DPRINTF(IQ, "Waking any dependents on register %i.\n", + (int) dest_reg); + + //Go through the dependency chain, marking the registers as + //ready within the waiting instructions. + DynInstPtr dep_inst = dependGraph.pop(dest_reg); + + while (dep_inst) { + DPRINTF(IQ, "Waking up a dependent instruction, PC%#x.\n", + dep_inst->readPC()); + + // Might want to give more information to the instruction + // so that it knows which of its source registers is + // ready. However that would mean that the dependency + // graph entries would need to hold the src_reg_idx. + dep_inst->markSrcRegReady(); + + addIfReady(dep_inst); + + dep_inst = dependGraph.pop(dest_reg); + + ++dependents; + } + + // Reset the head node now that all of its dependents have + // been woken up. + assert(dependGraph.empty(dest_reg)); + dependGraph.clearInst(dest_reg); + + // Mark the scoreboard as having that register ready. + regScoreboard[dest_reg] = true; + } + return dependents; +} + +template <class Impl> +void +InstructionQueue<Impl>::addReadyMemInst(DynInstPtr &ready_inst) +{ + OpClass op_class = ready_inst->opClass(); + + readyInsts[op_class].push(ready_inst); + + // Will need to reorder the list if either a queue is not on the list, + // or it has an older instruction than last time. + if (!queueOnList[op_class]) { + addToOrderList(op_class); + } else if (readyInsts[op_class].top()->seqNum < + (*readyIt[op_class]).oldestInst) { + listOrder.erase(readyIt[op_class]); + addToOrderList(op_class); + } + + DPRINTF(IQ, "Instruction is ready to issue, putting it onto " + "the ready list, PC %#x opclass:%i [sn:%lli].\n", + ready_inst->readPC(), op_class, ready_inst->seqNum); +} + +template <class Impl> +void +InstructionQueue<Impl>::rescheduleMemInst(DynInstPtr &resched_inst) +{ + memDepUnit[resched_inst->threadNumber].reschedule(resched_inst); +} + +template <class Impl> +void +InstructionQueue<Impl>::replayMemInst(DynInstPtr &replay_inst) +{ + memDepUnit[replay_inst->threadNumber].replay(replay_inst); +} + +template <class Impl> +void +InstructionQueue<Impl>::completeMemInst(DynInstPtr &completed_inst) +{ + int tid = completed_inst->threadNumber; + + DPRINTF(IQ, "Completing mem instruction PC:%#x [sn:%lli]\n", + completed_inst->readPC(), completed_inst->seqNum); + + ++freeEntries; + + completed_inst->memOpDone = true; + + memDepUnit[tid].completed(completed_inst); + + count[tid]--; +} + +template <class Impl> +void +InstructionQueue<Impl>::violation(DynInstPtr &store, + DynInstPtr &faulting_load) +{ + memDepUnit[store->threadNumber].violation(store, faulting_load); +} + +template <class Impl> +void +InstructionQueue<Impl>::squash(unsigned tid) +{ + DPRINTF(IQ, "[tid:%i]: Starting to squash instructions in " + "the IQ.\n", tid); + + // Read instruction sequence number of last instruction out of the + // time buffer. +#if ISA_HAS_DELAY_SLOT + squashedSeqNum[tid] = fromCommit->commitInfo[tid].bdelayDoneSeqNum; +#else + squashedSeqNum[tid] = fromCommit->commitInfo[tid].doneSeqNum; +#endif + + // Call doSquash if there are insts in the IQ + if (count[tid] > 0) { + doSquash(tid); + } + + // Also tell the memory dependence unit to squash. + memDepUnit[tid].squash(squashedSeqNum[tid], tid); +} + +template <class Impl> +void +InstructionQueue<Impl>::doSquash(unsigned tid) +{ + // Start at the tail. + ListIt squash_it = instList[tid].end(); + --squash_it; + + DPRINTF(IQ, "[tid:%i]: Squashing until sequence number %i!\n", + tid, squashedSeqNum[tid]); + + // Squash any instructions younger than the squashed sequence number + // given. + while (squash_it != instList[tid].end() && + (*squash_it)->seqNum > squashedSeqNum[tid]) { + + DynInstPtr squashed_inst = (*squash_it); + + // Only handle the instruction if it actually is in the IQ and + // hasn't already been squashed in the IQ. + if (squashed_inst->threadNumber != tid || + squashed_inst->isSquashedInIQ()) { + --squash_it; + continue; + } + + if (!squashed_inst->isIssued() || + (squashed_inst->isMemRef() && + !squashed_inst->memOpDone)) { + + DPRINTF(IQ, "[tid:%i]: Instruction [sn:%lli] PC %#x " + "squashed.\n", + tid, squashed_inst->seqNum, squashed_inst->readPC()); + + // Remove the instruction from the dependency list. + if (!squashed_inst->isNonSpeculative() && + !squashed_inst->isStoreConditional() && + !squashed_inst->isMemBarrier() && + !squashed_inst->isWriteBarrier()) { + + for (int src_reg_idx = 0; + src_reg_idx < squashed_inst->numSrcRegs(); + src_reg_idx++) + { + PhysRegIndex src_reg = + squashed_inst->renamedSrcRegIdx(src_reg_idx); + + // Only remove it from the dependency graph if it + // was placed there in the first place. + + // Instead of doing a linked list traversal, we + // can just remove these squashed instructions + // either at issue time, or when the register is + // overwritten. The only downside to this is it + // leaves more room for error. + + if (!squashed_inst->isReadySrcRegIdx(src_reg_idx) && + src_reg < numPhysRegs) { + dependGraph.remove(src_reg, squashed_inst); + } + + + ++iqSquashedOperandsExamined; + } + } else if (!squashed_inst->isStoreConditional() || !squashed_inst->isCompleted()) { + NonSpecMapIt ns_inst_it = + nonSpecInsts.find(squashed_inst->seqNum); + assert(ns_inst_it != nonSpecInsts.end()); + + (*ns_inst_it).second = NULL; + + nonSpecInsts.erase(ns_inst_it); + + ++iqSquashedNonSpecRemoved; + } + + // Might want to also clear out the head of the dependency graph. + + // Mark it as squashed within the IQ. + squashed_inst->setSquashedInIQ(); + + // @todo: Remove this hack where several statuses are set so the + // inst will flow through the rest of the pipeline. + squashed_inst->setIssued(); + squashed_inst->setCanCommit(); + squashed_inst->clearInIQ(); + + //Update Thread IQ Count + count[squashed_inst->threadNumber]--; + + ++freeEntries; + } + + instList[tid].erase(squash_it--); + ++iqSquashedInstsExamined; + } +} + +template <class Impl> +bool +InstructionQueue<Impl>::addToDependents(DynInstPtr &new_inst) +{ + // Loop through the instruction's source registers, adding + // them to the dependency list if they are not ready. + int8_t total_src_regs = new_inst->numSrcRegs(); + bool return_val = false; + + for (int src_reg_idx = 0; + src_reg_idx < total_src_regs; + src_reg_idx++) + { + // Only add it to the dependency graph if it's not ready. + if (!new_inst->isReadySrcRegIdx(src_reg_idx)) { + PhysRegIndex src_reg = new_inst->renamedSrcRegIdx(src_reg_idx); + + // Check the IQ's scoreboard to make sure the register + // hasn't become ready while the instruction was in flight + // between stages. Only if it really isn't ready should + // it be added to the dependency graph. + if (src_reg >= numPhysRegs) { + continue; + } else if (regScoreboard[src_reg] == false) { + DPRINTF(IQ, "Instruction PC %#x has src reg %i that " + "is being added to the dependency chain.\n", + new_inst->readPC(), src_reg); + + dependGraph.insert(src_reg, new_inst); + + // Change the return value to indicate that something + // was added to the dependency graph. + return_val = true; + } else { + DPRINTF(IQ, "Instruction PC %#x has src reg %i that " + "became ready before it reached the IQ.\n", + new_inst->readPC(), src_reg); + // Mark a register ready within the instruction. + new_inst->markSrcRegReady(src_reg_idx); + } + } + } + + return return_val; +} + +template <class Impl> +void +InstructionQueue<Impl>::addToProducers(DynInstPtr &new_inst) +{ + // Nothing really needs to be marked when an instruction becomes + // the producer of a register's value, but for convenience a ptr + // to the producing instruction will be placed in the head node of + // the dependency links. + int8_t total_dest_regs = new_inst->numDestRegs(); + + for (int dest_reg_idx = 0; + dest_reg_idx < total_dest_regs; + dest_reg_idx++) + { + PhysRegIndex dest_reg = new_inst->renamedDestRegIdx(dest_reg_idx); + + // Instructions that use the misc regs will have a reg number + // higher than the normal physical registers. In this case these + // registers are not renamed, and there is no need to track + // dependencies as these instructions must be executed at commit. + if (dest_reg >= numPhysRegs) { + continue; + } + + if (!dependGraph.empty(dest_reg)) { + dependGraph.dump(); + panic("Dependency graph %i not empty!", dest_reg); + } + + dependGraph.setInst(dest_reg, new_inst); + + // Mark the scoreboard to say it's not yet ready. + regScoreboard[dest_reg] = false; + } +} + +template <class Impl> +void +InstructionQueue<Impl>::addIfReady(DynInstPtr &inst) +{ + // If the instruction now has all of its source registers + // available, then add it to the list of ready instructions. + if (inst->readyToIssue()) { + + //Add the instruction to the proper ready list. + if (inst->isMemRef()) { + + DPRINTF(IQ, "Checking if memory instruction can issue.\n"); + + // Message to the mem dependence unit that this instruction has + // its registers ready. + memDepUnit[inst->threadNumber].regsReady(inst); + + return; + } + + OpClass op_class = inst->opClass(); + + DPRINTF(IQ, "Instruction is ready to issue, putting it onto " + "the ready list, PC %#x opclass:%i [sn:%lli].\n", + inst->readPC(), op_class, inst->seqNum); + + readyInsts[op_class].push(inst); + + // Will need to reorder the list if either a queue is not on the list, + // or it has an older instruction than last time. + if (!queueOnList[op_class]) { + addToOrderList(op_class); + } else if (readyInsts[op_class].top()->seqNum < + (*readyIt[op_class]).oldestInst) { + listOrder.erase(readyIt[op_class]); + addToOrderList(op_class); + } + } +} + +template <class Impl> +int +InstructionQueue<Impl>::countInsts() +{ +#if 0 + //ksewell:This works but definitely could use a cleaner write + //with a more intuitive way of counting. Right now it's + //just brute force .... + // Change the #if if you want to use this method. + int total_insts = 0; + + for (int i = 0; i < numThreads; ++i) { + ListIt count_it = instList[i].begin(); + + while (count_it != instList[i].end()) { + if (!(*count_it)->isSquashed() && !(*count_it)->isSquashedInIQ()) { + if (!(*count_it)->isIssued()) { + ++total_insts; + } else if ((*count_it)->isMemRef() && + !(*count_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++total_insts; + } + } + + ++count_it; + } + } + + return total_insts; +#else + return numEntries - freeEntries; +#endif +} + +template <class Impl> +void +InstructionQueue<Impl>::dumpLists() +{ + for (int i = 0; i < Num_OpClasses; ++i) { + cprintf("Ready list %i size: %i\n", i, readyInsts[i].size()); + + cprintf("\n"); + } + + cprintf("Non speculative list size: %i\n", nonSpecInsts.size()); + + NonSpecMapIt non_spec_it = nonSpecInsts.begin(); + NonSpecMapIt non_spec_end_it = nonSpecInsts.end(); + + cprintf("Non speculative list: "); + + while (non_spec_it != non_spec_end_it) { + cprintf("%#x [sn:%lli]", (*non_spec_it).second->readPC(), + (*non_spec_it).second->seqNum); + ++non_spec_it; + } + + cprintf("\n"); + + ListOrderIt list_order_it = listOrder.begin(); + ListOrderIt list_order_end_it = listOrder.end(); + int i = 1; + + cprintf("List order: "); + + while (list_order_it != list_order_end_it) { + cprintf("%i OpClass:%i [sn:%lli] ", i, (*list_order_it).queueType, + (*list_order_it).oldestInst); + + ++list_order_it; + ++i; + } + + cprintf("\n"); +} + + +template <class Impl> +void +InstructionQueue<Impl>::dumpInsts() +{ + for (int i = 0; i < numThreads; ++i) { + int num = 0; + int valid_num = 0; + ListIt inst_list_it = instList[i].begin(); + + while (inst_list_it != instList[i].end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed + // still count towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } + } + + cprintf("Insts to Execute list:\n"); + + int num = 0; + int valid_num = 0; + ListIt inst_list_it = instsToExecute.begin(); + + while (inst_list_it != instsToExecute.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed + // still count towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } +} diff --git a/src/cpu/o3/isa_specific.hh b/src/cpu/o3/isa_specific.hh new file mode 100755 index 000000000..4937589e3 --- /dev/null +++ b/src/cpu/o3/isa_specific.hh @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#include "cpu/base.hh" + +#if THE_ISA == ALPHA_ISA + #include "cpu/o3/alpha/cpu.hh" + #include "cpu/o3/alpha/impl.hh" + #include "cpu/o3/alpha/params.hh" + #include "cpu/o3/alpha/dyn_inst.hh" +#elif THE_ISA == MIPS_ISA + #include "cpu/o3/mips/cpu.hh" + #include "cpu/o3/mips/impl.hh" + #include "cpu/o3/mips/params.hh" + #include "cpu/o3/mips/dyn_inst.hh" +#else + #error "ISA-specific header files O3CPU not defined ISA" +#endif diff --git a/src/cpu/o3/lsq.cc b/src/cpu/o3/lsq.cc new file mode 100644 index 000000000..527947281 --- /dev/null +++ b/src/cpu/o3/lsq.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/lsq_impl.hh" + +// Force the instantiation of LDSTQ for all the implementations we care about. +template class LSQ<O3CPUImpl>; + diff --git a/src/cpu/o3/lsq.hh b/src/cpu/o3/lsq.hh new file mode 100644 index 000000000..190734dc2 --- /dev/null +++ b/src/cpu/o3/lsq.hh @@ -0,0 +1,383 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#ifndef __CPU_O3_LSQ_HH__ +#define __CPU_O3_LSQ_HH__ + +#include <map> +#include <queue> + +#include "config/full_system.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/lsq_unit.hh" +#include "mem/port.hh" +#include "sim/sim_object.hh" + +template <class Impl> +class LSQ { + public: + typedef typename Impl::Params Params; + typedef typename Impl::O3CPU O3CPU; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::CPUPol::IEW IEW; + typedef typename Impl::CPUPol::LSQUnit LSQUnit; + + /** SMT policy. */ + enum LSQPolicy { + Dynamic, + Partitioned, + Threshold + }; + + /** Constructs an LSQ with the given parameters. */ + LSQ(Params *params); + + /** Returns the name of the LSQ. */ + std::string name() const; + + /** Registers statistics of each LSQ unit. */ + void regStats(); + + /** Returns dcache port. + * @todo: Dcache port needs to be moved up to this level for SMT + * to work. For now it just returns the port from one of the + * threads. + */ + Port *getDcachePort() { return &dcachePort; } + + /** Sets the pointer to the list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + /** Sets the CPU pointer. */ + void setCPU(O3CPU *cpu_ptr); + /** Sets the IEW stage pointer. */ + void setIEW(IEW *iew_ptr); + /** Switches out the LSQ. */ + void switchOut(); + /** Takes over execution from another CPU's thread. */ + void takeOverFrom(); + + /** Number of entries needed for the given amount of threads.*/ + int entryAmount(int num_threads); + void removeEntries(unsigned tid); + /** Reset the max entries for each thread. */ + void resetEntries(); + /** Resize the max entries for a thread. */ + void resizeEntries(unsigned size, unsigned tid); + + /** Ticks the LSQ. */ + void tick(); + /** Ticks a specific LSQ Unit. */ + void tick(unsigned tid) + { thread[tid].tick(); } + + /** Inserts a load into the LSQ. */ + void insertLoad(DynInstPtr &load_inst); + /** Inserts a store into the LSQ. */ + void insertStore(DynInstPtr &store_inst); + + /** Executes a load. */ + Fault executeLoad(DynInstPtr &inst); + + /** Executes a store. */ + Fault executeStore(DynInstPtr &inst); + + /** + * Commits loads up until the given sequence number for a specific thread. + */ + void commitLoads(InstSeqNum &youngest_inst, unsigned tid) + { thread[tid].commitLoads(youngest_inst); } + + /** + * Commits stores up until the given sequence number for a specific thread. + */ + void commitStores(InstSeqNum &youngest_inst, unsigned tid) + { thread[tid].commitStores(youngest_inst); } + + /** + * Attempts to write back stores until all cache ports are used or the + * interface becomes blocked. + */ + void writebackStores(); + /** Same as above, but only for one thread. */ + void writebackStores(unsigned tid); + + /** + * Squash instructions from a thread until the specified sequence number. + */ + void squash(const InstSeqNum &squashed_num, unsigned tid) + { thread[tid].squash(squashed_num); } + + /** Returns whether or not there was a memory ordering violation. */ + bool violation(); + /** + * Returns whether or not there was a memory ordering violation for a + * specific thread. + */ + bool violation(unsigned tid) + { return thread[tid].violation(); } + + /** Returns if a load is blocked due to the memory system for a specific + * thread. + */ + bool loadBlocked(unsigned tid) + { return thread[tid].loadBlocked(); } + + bool isLoadBlockedHandled(unsigned tid) + { return thread[tid].isLoadBlockedHandled(); } + + void setLoadBlockedHandled(unsigned tid) + { thread[tid].setLoadBlockedHandled(); } + + /** Gets the instruction that caused the memory ordering violation. */ + DynInstPtr getMemDepViolator(unsigned tid) + { return thread[tid].getMemDepViolator(); } + + /** Returns the head index of the load queue for a specific thread. */ + int getLoadHead(unsigned tid) + { return thread[tid].getLoadHead(); } + + /** Returns the sequence number of the head of the load queue. */ + InstSeqNum getLoadHeadSeqNum(unsigned tid) + { + return thread[tid].getLoadHeadSeqNum(); + } + + /** Returns the head index of the store queue. */ + int getStoreHead(unsigned tid) + { return thread[tid].getStoreHead(); } + + /** Returns the sequence number of the head of the store queue. */ + InstSeqNum getStoreHeadSeqNum(unsigned tid) + { + return thread[tid].getStoreHeadSeqNum(); + } + + /** Returns the number of instructions in all of the queues. */ + int getCount(); + /** Returns the number of instructions in the queues of one thread. */ + int getCount(unsigned tid) + { return thread[tid].getCount(); } + + /** Returns the total number of loads in the load queue. */ + int numLoads(); + /** Returns the total number of loads for a single thread. */ + int numLoads(unsigned tid) + { return thread[tid].numLoads(); } + + /** Returns the total number of stores in the store queue. */ + int numStores(); + /** Returns the total number of stores for a single thread. */ + int numStores(unsigned tid) + { return thread[tid].numStores(); } + + /** Returns the total number of loads that are ready. */ + int numLoadsReady(); + /** Returns the number of loads that are ready for a single thread. */ + int numLoadsReady(unsigned tid) + { return thread[tid].numLoadsReady(); } + + /** Returns the number of free entries. */ + unsigned numFreeEntries(); + /** Returns the number of free entries for a specific thread. */ + unsigned numFreeEntries(unsigned tid); + + /** Returns if the LSQ is full (either LQ or SQ is full). */ + bool isFull(); + /** + * Returns if the LSQ is full for a specific thread (either LQ or SQ is + * full). + */ + bool isFull(unsigned tid); + + /** Returns if any of the LQs are full. */ + bool lqFull(); + /** Returns if the LQ of a given thread is full. */ + bool lqFull(unsigned tid); + + /** Returns if any of the SQs are full. */ + bool sqFull(); + /** Returns if the SQ of a given thread is full. */ + bool sqFull(unsigned tid); + + /** + * Returns if the LSQ is stalled due to a memory operation that must be + * replayed. + */ + bool isStalled(); + /** + * Returns if the LSQ of a specific thread is stalled due to a memory + * operation that must be replayed. + */ + bool isStalled(unsigned tid); + + /** Returns whether or not there are any stores to write back to memory. */ + bool hasStoresToWB(); + + /** Returns whether or not a specific thread has any stores to write back + * to memory. + */ + bool hasStoresToWB(unsigned tid) + { return thread[tid].hasStoresToWB(); } + + /** Returns the number of stores a specific thread has to write back. */ + int numStoresToWB(unsigned tid) + { return thread[tid].numStoresToWB(); } + + /** Returns if the LSQ will write back to memory this cycle. */ + bool willWB(); + /** Returns if the LSQ of a specific thread will write back to memory this + * cycle. + */ + bool willWB(unsigned tid) + { return thread[tid].willWB(); } + + /** Returns if the cache is currently blocked. */ + bool cacheBlocked() + { return retryTid != -1; } + + /** Sets the retry thread id, indicating that one of the LSQUnits + * tried to access the cache but the cache was blocked. */ + void setRetryTid(int tid) + { retryTid = tid; } + + /** Debugging function to print out all instructions. */ + void dumpInsts(); + /** Debugging function to print out instructions from a specific thread. */ + void dumpInsts(unsigned tid) + { thread[tid].dumpInsts(); } + + /** Executes a read operation, using the load specified at the load index. */ + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + /** Executes a store operation, using the store specified at the store + * index. + */ + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + /** DcachePort class for this LSQ. Handles doing the + * communication with the cache/memory. + */ + class DcachePort : public Port + { + protected: + /** Pointer to LSQ. */ + LSQ *lsq; + + public: + /** Default constructor. */ + DcachePort(LSQ *_lsq) + : lsq(_lsq) + { } + + protected: + /** Atomic version of receive. Panics. */ + virtual Tick recvAtomic(PacketPtr pkt); + + /** Functional version of receive. Panics. */ + virtual void recvFunctional(PacketPtr pkt); + + /** Receives status change. Other than range changing, panics. */ + virtual void recvStatusChange(Status status); + + /** Returns the address ranges of this device. */ + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + /** Timing version of receive. Handles writing back and + * completing the load or store that has returned from + * memory. */ + virtual bool recvTiming(PacketPtr pkt); + + /** Handles doing a retry of the previous send. */ + virtual void recvRetry(); + }; + + /** D-cache port. */ + DcachePort dcachePort; + + protected: + /** The LSQ policy for SMT mode. */ + LSQPolicy lsqPolicy; + + /** The LSQ units for individual threads. */ + LSQUnit thread[Impl::MaxThreads]; + + /** The CPU pointer. */ + O3CPU *cpu; + + /** The IEW stage pointer. */ + IEW *iewStage; + + /** List of Active Threads in System. */ + std::list<unsigned> *activeThreads; + + /** Total Size of LQ Entries. */ + unsigned LQEntries; + /** Total Size of SQ Entries. */ + unsigned SQEntries; + + /** Max LQ Size - Used to Enforce Sharing Policies. */ + unsigned maxLQEntries; + + /** Max SQ Size - Used to Enforce Sharing Policies. */ + unsigned maxSQEntries; + + /** Number of Threads. */ + unsigned numThreads; + + /** The thread id of the LSQ Unit that is currently waiting for a + * retry. */ + int retryTid; +}; + +template <class Impl> +template <class T> +Fault +LSQ<Impl>::read(RequestPtr req, T &data, int load_idx) +{ + unsigned tid = req->getThreadNum(); + + return thread[tid].read(req, data, load_idx); +} + +template <class Impl> +template <class T> +Fault +LSQ<Impl>::write(RequestPtr req, T &data, int store_idx) +{ + unsigned tid = req->getThreadNum(); + + return thread[tid].write(req, data, store_idx); +} + +#endif // __CPU_O3_LSQ_HH__ diff --git a/src/cpu/o3/lsq_impl.hh b/src/cpu/o3/lsq_impl.hh new file mode 100644 index 000000000..a1ac5adb8 --- /dev/null +++ b/src/cpu/o3/lsq_impl.hh @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + */ + +#include <algorithm> +#include <list> +#include <string> + +#include "cpu/o3/lsq.hh" + +template <class Impl> +Tick +LSQ<Impl>::DcachePort::recvAtomic(PacketPtr pkt) +{ + panic("O3CPU model does not work with atomic mode!"); + return curTick; +} + +template <class Impl> +void +LSQ<Impl>::DcachePort::recvFunctional(PacketPtr pkt) +{ + panic("O3CPU doesn't expect recvFunctional callback!"); +} + +template <class Impl> +void +LSQ<Impl>::DcachePort::recvStatusChange(Status status) +{ + if (status == RangeChange) + return; + + panic("O3CPU doesn't expect recvStatusChange callback!"); +} + +template <class Impl> +bool +LSQ<Impl>::DcachePort::recvTiming(PacketPtr pkt) +{ + lsq->thread[pkt->req->getThreadNum()].completeDataAccess(pkt); + return true; +} + +template <class Impl> +void +LSQ<Impl>::DcachePort::recvRetry() +{ + if (lsq->retryTid == -1) + { + //Squashed, so drop it + return; + } + lsq->thread[lsq->retryTid].recvRetry(); + // Speculatively clear the retry Tid. This will get set again if + // the LSQUnit was unable to complete its access. + lsq->retryTid = -1; +} + +template <class Impl> +LSQ<Impl>::LSQ(Params *params) + : dcachePort(this), LQEntries(params->LQEntries), + SQEntries(params->SQEntries), numThreads(params->numberOfThreads), + retryTid(-1) +{ + DPRINTF(LSQ, "Creating LSQ object.\n"); + + //**********************************************/ + //************ Handle SMT Parameters ***********/ + //**********************************************/ + std::string policy = params->smtLSQPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Figure out fetch policy + if (policy == "dynamic") { + lsqPolicy = Dynamic; + + maxLQEntries = LQEntries; + maxSQEntries = SQEntries; + + DPRINTF(LSQ, "LSQ sharing policy set to Dynamic\n"); + + } else if (policy == "partitioned") { + lsqPolicy = Partitioned; + + //@todo:make work if part_amt doesnt divide evenly. + maxLQEntries = LQEntries / numThreads; + maxSQEntries = SQEntries / numThreads; + + DPRINTF(Fetch, "LSQ sharing policy set to Partitioned: " + "%i entries per LQ | %i entries per SQ", + maxLQEntries,maxSQEntries); + + } else if (policy == "threshold") { + lsqPolicy = Threshold; + + assert(params->smtLSQThreshold > LQEntries); + assert(params->smtLSQThreshold > SQEntries); + + //Divide up by threshold amount + //@todo: Should threads check the max and the total + //amount of the LSQ + maxLQEntries = params->smtLSQThreshold; + maxSQEntries = params->smtLSQThreshold; + + DPRINTF(LSQ, "LSQ sharing policy set to Threshold: " + "%i entries per LQ | %i entries per SQ", + maxLQEntries,maxSQEntries); + + } else { + assert(0 && "Invalid LSQ Sharing Policy.Options Are:{Dynamic," + "Partitioned, Threshold}"); + } + + //Initialize LSQs + for (int tid=0; tid < numThreads; tid++) { + thread[tid].init(params, this, maxLQEntries, maxSQEntries, tid); + thread[tid].setDcachePort(&dcachePort); + } +} + + +template<class Impl> +std::string +LSQ<Impl>::name() const +{ + return iewStage->name() + ".lsq"; +} + +template<class Impl> +void +LSQ<Impl>::regStats() +{ + //Initialize LSQs + for (int tid=0; tid < numThreads; tid++) { + thread[tid].regStats(); + } +} + +template<class Impl> +void +LSQ<Impl>::regStats() +{ + //Initialize LSQs + for (int tid=0; tid < numThreads; tid++) { + thread[tid].regStats(); + } +} + +template<class Impl> +void +LSQ<Impl>::setActiveThreads(std::list<unsigned> *at_ptr) +{ + activeThreads = at_ptr; + assert(activeThreads != 0); +} + +template<class Impl> +void +LSQ<Impl>::setCPU(O3CPU *cpu_ptr) +{ + cpu = cpu_ptr; + + dcachePort.setName(name()); + + for (int tid=0; tid < numThreads; tid++) { + thread[tid].setCPU(cpu_ptr); + } +} + +template<class Impl> +void +LSQ<Impl>::setIEW(IEW *iew_ptr) +{ + iewStage = iew_ptr; + + for (int tid=0; tid < numThreads; tid++) { + thread[tid].setIEW(iew_ptr); + } +} + +template <class Impl> +void +LSQ<Impl>::switchOut() +{ + for (int tid = 0; tid < numThreads; tid++) { + thread[tid].switchOut(); + } +} + +template <class Impl> +void +LSQ<Impl>::takeOverFrom() +{ + for (int tid = 0; tid < numThreads; tid++) { + thread[tid].takeOverFrom(); + } +} + +template <class Impl> +int +LSQ<Impl>::entryAmount(int num_threads) +{ + if (lsqPolicy == Partitioned) { + return LQEntries / num_threads; + } else { + return 0; + } +} + +template <class Impl> +void +LSQ<Impl>::resetEntries() +{ + if (lsqPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + std::list<unsigned>::iterator list_end = (*activeThreads).end(); + + int maxEntries; + + if (lsqPolicy == Partitioned) { + maxEntries = LQEntries / active_threads; + } else if (lsqPolicy == Threshold && active_threads == 1) { + maxEntries = LQEntries; + } else { + maxEntries = LQEntries; + } + + while (threads != list_end) { + resizeEntries(maxEntries,*threads++); + } + } +} + +template<class Impl> +void +LSQ<Impl>::removeEntries(unsigned tid) +{ + thread[tid].clearLQ(); + thread[tid].clearSQ(); +} + +template<class Impl> +void +LSQ<Impl>::resizeEntries(unsigned size,unsigned tid) +{ + thread[tid].resizeLQ(size); + thread[tid].resizeSQ(size); +} + +template<class Impl> +void +LSQ<Impl>::tick() +{ + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + + thread[tid].tick(); + } +} + +template<class Impl> +void +LSQ<Impl>::insertLoad(DynInstPtr &load_inst) +{ + unsigned tid = load_inst->threadNumber; + + thread[tid].insertLoad(load_inst); +} + +template<class Impl> +void +LSQ<Impl>::insertStore(DynInstPtr &store_inst) +{ + unsigned tid = store_inst->threadNumber; + + thread[tid].insertStore(store_inst); +} + +template<class Impl> +Fault +LSQ<Impl>::executeLoad(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + return thread[tid].executeLoad(inst); +} + +template<class Impl> +Fault +LSQ<Impl>::executeStore(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + return thread[tid].executeStore(inst); +} + +template<class Impl> +void +LSQ<Impl>::writebackStores() +{ + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + + if (numStoresToWB(tid) > 0) { + DPRINTF(Writeback,"[tid:%i] Writing back stores. %i stores " + "available for Writeback.\n", tid, numStoresToWB(tid)); + } + + thread[tid].writebackStores(); + } +} + +template<class Impl> +bool +LSQ<Impl>::violation() +{ + /* Answers: Does Anybody Have a Violation?*/ + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (thread[tid].violation()) + return true; + } + + return false; +} + +template<class Impl> +int +LSQ<Impl>::getCount() +{ + unsigned total = 0; + + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += getCount(tid); + } + + return total; +} + +template<class Impl> +int +LSQ<Impl>::numLoads() +{ + unsigned total = 0; + + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += numLoads(tid); + } + + return total; +} + +template<class Impl> +int +LSQ<Impl>::numStores() +{ + unsigned total = 0; + + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += thread[tid].numStores(); + } + + return total; +} + +template<class Impl> +int +LSQ<Impl>::numLoadsReady() +{ + unsigned total = 0; + + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += thread[tid].numLoadsReady(); + } + + return total; +} + +template<class Impl> +unsigned +LSQ<Impl>::numFreeEntries() +{ + unsigned total = 0; + + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + total += thread[tid].numFreeEntries(); + } + + return total; +} + +template<class Impl> +unsigned +LSQ<Impl>::numFreeEntries(unsigned tid) +{ + //if( lsqPolicy == Dynamic ) + //return numFreeEntries(); + //else + return thread[tid].numFreeEntries(); +} + +template<class Impl> +bool +LSQ<Impl>::isFull() +{ + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (! (thread[tid].lqFull() || thread[tid].sqFull()) ) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::isFull(unsigned tid) +{ + //@todo: Change to Calculate All Entries for + //Dynamic Policy + if( lsqPolicy == Dynamic ) + return isFull(); + else + return thread[tid].lqFull() || thread[tid].sqFull(); +} + +template<class Impl> +bool +LSQ<Impl>::lqFull() +{ + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!thread[tid].lqFull()) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::lqFull(unsigned tid) +{ + //@todo: Change to Calculate All Entries for + //Dynamic Policy + if( lsqPolicy == Dynamic ) + return lqFull(); + else + return thread[tid].lqFull(); +} + +template<class Impl> +bool +LSQ<Impl>::sqFull() +{ + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!sqFull(tid)) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::sqFull(unsigned tid) +{ + //@todo: Change to Calculate All Entries for + //Dynamic Policy + if( lsqPolicy == Dynamic ) + return sqFull(); + else + return thread[tid].sqFull(); +} + +template<class Impl> +bool +LSQ<Impl>::isStalled() +{ + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!thread[tid].isStalled()) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::isStalled(unsigned tid) +{ + if( lsqPolicy == Dynamic ) + return isStalled(); + else + return thread[tid].isStalled(); +} + +template<class Impl> +bool +LSQ<Impl>::hasStoresToWB() +{ + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + if ((*activeThreads).empty()) + return false; + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!hasStoresToWB(tid)) + return false; + } + + return true; +} + +template<class Impl> +bool +LSQ<Impl>::willWB() +{ + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + if (!willWB(tid)) + return false; + } + + return true; +} + +template<class Impl> +void +LSQ<Impl>::dumpInsts() +{ + std::list<unsigned>::iterator active_threads = (*activeThreads).begin(); + + while (active_threads != (*activeThreads).end()) { + unsigned tid = *active_threads++; + thread[tid].dumpInsts(); + } +} diff --git a/src/cpu/o3/lsq_unit.cc b/src/cpu/o3/lsq_unit.cc new file mode 100644 index 000000000..3ca3fa667 --- /dev/null +++ b/src/cpu/o3/lsq_unit.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/lsq_unit_impl.hh" + +// Force the instantiation of LDSTQ for all the implementations we care about. +template class LSQUnit<O3CPUImpl>; + diff --git a/src/cpu/o3/lsq_unit.hh b/src/cpu/o3/lsq_unit.hh new file mode 100644 index 000000000..8537e9dd7 --- /dev/null +++ b/src/cpu/o3/lsq_unit.hh @@ -0,0 +1,688 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#ifndef __CPU_O3_LSQ_UNIT_HH__ +#define __CPU_O3_LSQ_UNIT_HH__ + +#include <algorithm> +#include <map> +#include <queue> + +#include "arch/faults.hh" +#include "config/full_system.hh" +#include "base/hashmap.hh" +#include "cpu/inst_seq.hh" +#include "mem/packet_impl.hh" +#include "mem/port.hh" + +/** + * Class that implements the actual LQ and SQ for each specific + * thread. Both are circular queues; load entries are freed upon + * committing, while store entries are freed once they writeback. The + * LSQUnit tracks if there are memory ordering violations, and also + * detects partial load to store forwarding cases (a store only has + * part of a load's data) that requires the load to wait until the + * store writes back. In the former case it holds onto the instruction + * until the dependence unit looks at it, and in the latter it stalls + * the LSQ until the store writes back. At that point the load is + * replayed. + */ +template <class Impl> +class LSQUnit { + protected: + typedef TheISA::IntReg IntReg; + public: + typedef typename Impl::Params Params; + typedef typename Impl::O3CPU O3CPU; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::CPUPol::IEW IEW; + typedef typename Impl::CPUPol::LSQ LSQ; + typedef typename Impl::CPUPol::IssueStruct IssueStruct; + + public: + /** Constructs an LSQ unit. init() must be called prior to use. */ + LSQUnit(); + + /** Initializes the LSQ unit with the specified number of entries. */ + void init(Params *params, LSQ *lsq_ptr, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id); + + /** Returns the name of the LSQ unit. */ + std::string name() const; + + /** Registers statistics. */ + void regStats(); + + /** Sets the CPU pointer. */ + void setCPU(O3CPU *cpu_ptr); + + /** Sets the IEW stage pointer. */ + void setIEW(IEW *iew_ptr) + { iewStage = iew_ptr; } + + /** Sets the pointer to the dcache port. */ + void setDcachePort(Port *dcache_port) + { dcachePort = dcache_port; } + + /** Switches out LSQ unit. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Returns if the LSQ is switched out. */ + bool isSwitchedOut() { return switchedOut; } + + /** Ticks the LSQ unit, which in this case only resets the number of + * used cache ports. + * @todo: Move the number of used ports up to the LSQ level so it can + * be shared by all LSQ units. + */ + void tick() { usedPorts = 0; } + + /** Inserts an instruction. */ + void insert(DynInstPtr &inst); + /** Inserts a load instruction. */ + void insertLoad(DynInstPtr &load_inst); + /** Inserts a store instruction. */ + void insertStore(DynInstPtr &store_inst); + + /** Executes a load instruction. */ + Fault executeLoad(DynInstPtr &inst); + + Fault executeLoad(int lq_idx) { panic("Not implemented"); return NoFault; } + /** Executes a store instruction. */ + Fault executeStore(DynInstPtr &inst); + + /** Commits the head load. */ + void commitLoad(); + /** Commits loads older than a specific sequence number. */ + void commitLoads(InstSeqNum &youngest_inst); + + /** Commits stores older than a specific sequence number. */ + void commitStores(InstSeqNum &youngest_inst); + + /** Writes back stores. */ + void writebackStores(); + + /** Completes the data access that has been returned from the + * memory system. */ + void completeDataAccess(PacketPtr pkt); + + /** Clears all the entries in the LQ. */ + void clearLQ(); + + /** Clears all the entries in the SQ. */ + void clearSQ(); + + /** Resizes the LQ to a given size. */ + void resizeLQ(unsigned size); + + /** Resizes the SQ to a given size. */ + void resizeSQ(unsigned size); + + /** Squashes all instructions younger than a specific sequence number. */ + void squash(const InstSeqNum &squashed_num); + + /** Returns if there is a memory ordering violation. Value is reset upon + * call to getMemDepViolator(). + */ + bool violation() { return memDepViolator; } + + /** Returns the memory ordering violator. */ + DynInstPtr getMemDepViolator(); + + /** Returns if a load became blocked due to the memory system. */ + bool loadBlocked() + { return isLoadBlocked; } + + /** Clears the signal that a load became blocked. */ + void clearLoadBlocked() + { isLoadBlocked = false; } + + /** Returns if the blocked load was handled. */ + bool isLoadBlockedHandled() + { return loadBlockedHandled; } + + /** Records the blocked load as being handled. */ + void setLoadBlockedHandled() + { loadBlockedHandled = true; } + + /** Returns the number of free entries (min of free LQ and SQ entries). */ + unsigned numFreeEntries(); + + /** Returns the number of loads ready to execute. */ + int numLoadsReady(); + + /** Returns the number of loads in the LQ. */ + int numLoads() { return loads; } + + /** Returns the number of stores in the SQ. */ + int numStores() { return stores; } + + /** Returns if either the LQ or SQ is full. */ + bool isFull() { return lqFull() || sqFull(); } + + /** Returns if the LQ is full. */ + bool lqFull() { return loads >= (LQEntries - 1); } + + /** Returns if the SQ is full. */ + bool sqFull() { return stores >= (SQEntries - 1); } + + /** Returns the number of instructions in the LSQ. */ + unsigned getCount() { return loads + stores; } + + /** Returns if there are any stores to writeback. */ + bool hasStoresToWB() { return storesToWB; } + + /** Returns the number of stores to writeback. */ + int numStoresToWB() { return storesToWB; } + + /** Returns if the LSQ unit will writeback on this cycle. */ + bool willWB() { return storeQueue[storeWBIdx].canWB && + !storeQueue[storeWBIdx].completed && + !isStoreBlocked; } + + /** Handles doing the retry. */ + void recvRetry(); + + private: + /** Writes back the instruction, sending it to IEW. */ + void writeback(DynInstPtr &inst, PacketPtr pkt); + + /** Handles completing the send of a store to memory. */ + void storePostSend(Packet *pkt); + + /** Completes the store at the specified index. */ + void completeStore(int store_idx); + + /** Increments the given store index (circular queue). */ + inline void incrStIdx(int &store_idx); + /** Decrements the given store index (circular queue). */ + inline void decrStIdx(int &store_idx); + /** Increments the given load index (circular queue). */ + inline void incrLdIdx(int &load_idx); + /** Decrements the given load index (circular queue). */ + inline void decrLdIdx(int &load_idx); + + public: + /** Debugging function to dump instructions in the LSQ. */ + void dumpInsts(); + + private: + /** Pointer to the CPU. */ + O3CPU *cpu; + + /** Pointer to the IEW stage. */ + IEW *iewStage; + + /** Pointer to the LSQ. */ + LSQ *lsq; + + /** Pointer to the dcache port. Used only for sending. */ + Port *dcachePort; + + /** Derived class to hold any sender state the LSQ needs. */ + class LSQSenderState : public Packet::SenderState + { + public: + /** Default constructor. */ + LSQSenderState() + : noWB(false) + { } + + /** Instruction who initiated the access to memory. */ + DynInstPtr inst; + /** Whether or not it is a load. */ + bool isLoad; + /** The LQ/SQ index of the instruction. */ + int idx; + /** Whether or not the instruction will need to writeback. */ + bool noWB; + }; + + /** Writeback event, specifically for when stores forward data to loads. */ + class WritebackEvent : public Event { + public: + /** Constructs a writeback event. */ + WritebackEvent(DynInstPtr &_inst, PacketPtr pkt, LSQUnit *lsq_ptr); + + /** Processes the writeback event. */ + void process(); + + /** Returns the description of this event. */ + const char *description(); + + private: + /** Instruction whose results are being written back. */ + DynInstPtr inst; + + /** The packet that would have been sent to memory. */ + PacketPtr pkt; + + /** The pointer to the LSQ unit that issued the store. */ + LSQUnit<Impl> *lsqPtr; + }; + + public: + struct SQEntry { + /** Constructs an empty store queue entry. */ + SQEntry() + : inst(NULL), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0) + { } + + /** Constructs a store queue entry for a given instruction. */ + SQEntry(DynInstPtr &_inst) + : inst(_inst), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0) + { } + + /** The store instruction. */ + DynInstPtr inst; + /** The request for the store. */ + RequestPtr req; + /** The size of the store. */ + int size; + /** The store data. */ + IntReg data; + /** Whether or not the store can writeback. */ + bool canWB; + /** Whether or not the store is committed. */ + bool committed; + /** Whether or not the store is completed. */ + bool completed; + }; + + private: + /** The LSQUnit thread id. */ + unsigned lsqID; + + /** The store queue. */ + std::vector<SQEntry> storeQueue; + + /** The load queue. */ + std::vector<DynInstPtr> loadQueue; + + /** The number of LQ entries, plus a sentinel entry (circular queue). + * @todo: Consider having var that records the true number of LQ entries. + */ + unsigned LQEntries; + /** The number of SQ entries, plus a sentinel entry (circular queue). + * @todo: Consider having var that records the true number of SQ entries. + */ + unsigned SQEntries; + + /** The number of load instructions in the LQ. */ + int loads; + /** The number of store instructions in the SQ. */ + int stores; + /** The number of store instructions in the SQ waiting to writeback. */ + int storesToWB; + + /** The index of the head instruction in the LQ. */ + int loadHead; + /** The index of the tail instruction in the LQ. */ + int loadTail; + + /** The index of the head instruction in the SQ. */ + int storeHead; + /** The index of the first instruction that may be ready to be + * written back, and has not yet been written back. + */ + int storeWBIdx; + /** The index of the tail instruction in the SQ. */ + int storeTail; + + /// @todo Consider moving to a more advanced model with write vs read ports + /** The number of cache ports available each cycle. */ + int cachePorts; + + /** The number of used cache ports in this cycle. */ + int usedPorts; + + /** Is the LSQ switched out. */ + bool switchedOut; + + //list<InstSeqNum> mshrSeqNums; + + /** Wire to read information from the issue stage time queue. */ + typename TimeBuffer<IssueStruct>::wire fromIssue; + + /** Whether or not the LSQ is stalled. */ + bool stalled; + /** The store that causes the stall due to partial store to load + * forwarding. + */ + InstSeqNum stallingStoreIsn; + /** The index of the above store. */ + int stallingLoadIdx; + + /** The packet that needs to be retried. */ + PacketPtr retryPkt; + + /** Whehter or not a store is blocked due to the memory system. */ + bool isStoreBlocked; + + /** Whether or not a load is blocked due to the memory system. */ + bool isLoadBlocked; + + /** Has the blocked load been handled. */ + bool loadBlockedHandled; + + /** The sequence number of the blocked load. */ + InstSeqNum blockedLoadSeqNum; + + /** The oldest load that caused a memory ordering violation. */ + DynInstPtr memDepViolator; + + // Will also need how many read/write ports the Dcache has. Or keep track + // of that in stage that is one level up, and only call executeLoad/Store + // the appropriate number of times. + /** Total number of loads forwaded from LSQ stores. */ + Stats::Scalar<> lsqForwLoads; + + /** Total number of squashed stores. */ + Stats::Scalar<> lsqSquashedStores; + + /** Total number of software prefetches ignored due to invalid addresses. */ + Stats::Scalar<> invAddrSwpfs; + + /** Ready loads blocked due to partial store-forwarding. */ + Stats::Scalar<> lsqBlockedLoads; + + /** Number of loads that were rescheduled. */ + Stats::Scalar<> lsqRescheduledLoads; + + /** Number of times the LSQ is blocked due to the cache. */ + Stats::Scalar<> lsqCacheBlocked; + + public: + /** Executes the load at the given index. */ + template <class T> + Fault read(Request *req, T &data, int load_idx); + + /** Executes the store at the given index. */ + template <class T> + Fault write(Request *req, T &data, int store_idx); + + /** Returns the index of the head load instruction. */ + int getLoadHead() { return loadHead; } + /** Returns the sequence number of the head load instruction. */ + InstSeqNum getLoadHeadSeqNum() + { + if (loadQueue[loadHead]) { + return loadQueue[loadHead]->seqNum; + } else { + return 0; + } + + } + + /** Returns the index of the head store instruction. */ + int getStoreHead() { return storeHead; } + /** Returns the sequence number of the head store instruction. */ + InstSeqNum getStoreHeadSeqNum() + { + if (storeQueue[storeHead].inst) { + return storeQueue[storeHead].inst->seqNum; + } else { + return 0; + } + + } + + /** Returns whether or not the LSQ unit is stalled. */ + bool isStalled() { return stalled; } +}; + +template <class Impl> +template <class T> +Fault +LSQUnit<Impl>::read(Request *req, T &data, int load_idx) +{ + DynInstPtr load_inst = loadQueue[load_idx]; + + assert(load_inst); + + assert(!load_inst->isExecuted()); + + // Make sure this isn't an uncacheable access + // A bit of a hackish way to get uncached accesses to work only if they're + // at the head of the LSQ and are ready to commit (at the head of the ROB + // too). + if (req->getFlags() & UNCACHEABLE && + (load_idx != loadHead || !load_inst->isAtCommit())) { + iewStage->rescheduleMemInst(load_inst); + ++lsqRescheduledLoads; + return TheISA::genMachineCheckFault(); + } + + // Check the SQ for any previous stores that might lead to forwarding + int store_idx = load_inst->sqIdx; + + int store_size = 0; + + DPRINTF(LSQUnit, "Read called, load idx: %i, store idx: %i, " + "storeHead: %i addr: %#x\n", + load_idx, store_idx, storeHead, req->getPaddr()); + +#if FULL_SYSTEM + if (req->getFlags() & LOCKED) { + cpu->lockAddr = req->getPaddr(); + cpu->lockFlag = true; + } +#endif + + while (store_idx != -1) { + // End once we've reached the top of the LSQ + if (store_idx == storeWBIdx) { + break; + } + + // Move the index to one younger + if (--store_idx < 0) + store_idx += SQEntries; + + assert(storeQueue[store_idx].inst); + + store_size = storeQueue[store_idx].size; + + if (store_size == 0) + continue; + + // Check if the store data is within the lower and upper bounds of + // addresses that the request needs. + bool store_has_lower_limit = + req->getVaddr() >= storeQueue[store_idx].inst->effAddr; + bool store_has_upper_limit = + (req->getVaddr() + req->getSize()) <= + (storeQueue[store_idx].inst->effAddr + store_size); + bool lower_load_has_store_part = + req->getVaddr() < (storeQueue[store_idx].inst->effAddr + + store_size); + bool upper_load_has_store_part = + (req->getVaddr() + req->getSize()) > + storeQueue[store_idx].inst->effAddr; + + // If the store's data has all of the data needed, we can forward. + if (store_has_lower_limit && store_has_upper_limit) { + // Get shift amount for offset into the store's data. + int shift_amt = req->getVaddr() & (store_size - 1); + // @todo: Magic number, assumes byte addressing + shift_amt = shift_amt << 3; + + // Cast this to type T? + data = storeQueue[store_idx].data >> shift_amt; + + assert(!load_inst->memData); + load_inst->memData = new uint8_t[64]; + + memcpy(load_inst->memData, &data, req->getSize()); + + DPRINTF(LSQUnit, "Forwarding from store idx %i to load to " + "addr %#x, data %#x\n", + store_idx, req->getVaddr(), data); + + PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(load_inst->memData); + + WritebackEvent *wb = new WritebackEvent(load_inst, data_pkt, this); + + // We'll say this has a 1 cycle load-store forwarding latency + // for now. + // @todo: Need to make this a parameter. + wb->schedule(curTick); + + ++lsqForwLoads; + return NoFault; + } else if ((store_has_lower_limit && lower_load_has_store_part) || + (store_has_upper_limit && upper_load_has_store_part) || + (lower_load_has_store_part && upper_load_has_store_part)) { + // This is the partial store-load forwarding case where a store + // has only part of the load's data. + + // If it's already been written back, then don't worry about + // stalling on it. + if (storeQueue[store_idx].completed) { + continue; + } + + // Must stall load and force it to retry, so long as it's the oldest + // load that needs to do so. + if (!stalled || + (stalled && + load_inst->seqNum < + loadQueue[stallingLoadIdx]->seqNum)) { + stalled = true; + stallingStoreIsn = storeQueue[store_idx].inst->seqNum; + stallingLoadIdx = load_idx; + } + + // Tell IQ/mem dep unit that this instruction will need to be + // rescheduled eventually + iewStage->rescheduleMemInst(load_inst); + iewStage->decrWb(load_inst->seqNum); + ++lsqRescheduledLoads; + + // Do not generate a writeback event as this instruction is not + // complete. + DPRINTF(LSQUnit, "Load-store forwarding mis-match. " + "Store idx %i to load addr %#x\n", + store_idx, req->getVaddr()); + + ++lsqBlockedLoads; + return NoFault; + } + } + + // If there's no forwarding case, then go access memory + DPRINTF(LSQUnit, "Doing memory access for inst [sn:%lli] PC %#x\n", + load_inst->seqNum, load_inst->readPC()); + + assert(!load_inst->memData); + load_inst->memData = new uint8_t[64]; + + ++usedPorts; + + PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(load_inst->memData); + + LSQSenderState *state = new LSQSenderState; + state->isLoad = true; + state->idx = load_idx; + state->inst = load_inst; + data_pkt->senderState = state; + + // if we the cache is not blocked, do cache access + if (!lsq->cacheBlocked()) { + if (!dcachePort->sendTiming(data_pkt)) { + // If the access didn't succeed, tell the LSQ by setting + // the retry thread id. + lsq->setRetryTid(lsqID); + } + } + + // If the cache was blocked, or has become blocked due to the access, + // handle it. + if (lsq->cacheBlocked()) { + ++lsqCacheBlocked; + + iewStage->decrWb(load_inst->seqNum); + // There's an older load that's already going to squash. + if (isLoadBlocked && blockedLoadSeqNum < load_inst->seqNum) + return NoFault; + + // Record that the load was blocked due to memory. This + // load will squash all instructions after it, be + // refetched, and re-executed. + isLoadBlocked = true; + loadBlockedHandled = false; + blockedLoadSeqNum = load_inst->seqNum; + // No fault occurred, even though the interface is blocked. + return NoFault; + } + + if (data_pkt->result != Packet::Success) { + DPRINTF(LSQUnit, "LSQUnit: D-cache miss!\n"); + DPRINTF(Activity, "Activity: ld accessing mem miss [sn:%lli]\n", + load_inst->seqNum); + } else { + DPRINTF(LSQUnit, "LSQUnit: D-cache hit!\n"); + DPRINTF(Activity, "Activity: ld accessing mem hit [sn:%lli]\n", + load_inst->seqNum); + } + + return NoFault; +} + +template <class Impl> +template <class T> +Fault +LSQUnit<Impl>::write(Request *req, T &data, int store_idx) +{ + assert(storeQueue[store_idx].inst); + + DPRINTF(LSQUnit, "Doing write to store idx %i, addr %#x data %#x" + " | storeHead:%i [sn:%i]\n", + store_idx, req->getPaddr(), data, storeHead, + storeQueue[store_idx].inst->seqNum); + + storeQueue[store_idx].req = req; + storeQueue[store_idx].size = sizeof(T); + storeQueue[store_idx].data = data; + + // This function only writes the data to the store queue, so no fault + // can happen here. + return NoFault; +} + +#endif // __CPU_O3_LSQ_UNIT_HH__ diff --git a/src/cpu/o3/lsq_unit_impl.hh b/src/cpu/o3/lsq_unit_impl.hh new file mode 100644 index 000000000..2922b81bd --- /dev/null +++ b/src/cpu/o3/lsq_unit_impl.hh @@ -0,0 +1,959 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "config/use_checker.hh" + +#include "cpu/o3/lsq.hh" +#include "cpu/o3/lsq_unit.hh" +#include "base/str.hh" +#include "mem/packet.hh" +#include "mem/request.hh" + +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + +template<class Impl> +LSQUnit<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst, PacketPtr _pkt, + LSQUnit *lsq_ptr) + : Event(&mainEventQueue), inst(_inst), pkt(_pkt), lsqPtr(lsq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template<class Impl> +void +LSQUnit<Impl>::WritebackEvent::process() +{ + if (!lsqPtr->isSwitchedOut()) { + lsqPtr->writeback(inst, pkt); + } + delete pkt; +} + +template<class Impl> +const char * +LSQUnit<Impl>::WritebackEvent::description() +{ + return "Store writeback event"; +} + +template<class Impl> +void +LSQUnit<Impl>::completeDataAccess(PacketPtr pkt) +{ + LSQSenderState *state = dynamic_cast<LSQSenderState *>(pkt->senderState); + DynInstPtr inst = state->inst; + DPRINTF(IEW, "Writeback event [sn:%lli]\n", inst->seqNum); + DPRINTF(Activity, "Activity: Writeback event [sn:%lli]\n", inst->seqNum); + + //iewStage->ldstQueue.removeMSHR(inst->threadNumber,inst->seqNum); + + if (isSwitchedOut() || inst->isSquashed()) { + iewStage->decrWb(inst->seqNum); + delete state; + delete pkt; + return; + } else { + if (!state->noWB) { + writeback(inst, pkt); + } + + if (inst->isStore()) { + completeStore(state->idx); + } + } + + delete state; + delete pkt; +} + +template <class Impl> +LSQUnit<Impl>::LSQUnit() + : loads(0), stores(0), storesToWB(0), stalled(false), + isStoreBlocked(false), isLoadBlocked(false), + loadBlockedHandled(false) +{ +} + +template<class Impl> +void +LSQUnit<Impl>::init(Params *params, LSQ *lsq_ptr, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id) +{ + DPRINTF(LSQUnit, "Creating LSQUnit%i object.\n",id); + + switchedOut = false; + + lsq = lsq_ptr; + + lsqID = id; + + // Add 1 for the sentinel entry (they are circular queues). + LQEntries = maxLQEntries + 1; + SQEntries = maxSQEntries + 1; + + loadQueue.resize(LQEntries); + storeQueue.resize(SQEntries); + + loadHead = loadTail = 0; + + storeHead = storeWBIdx = storeTail = 0; + + usedPorts = 0; + cachePorts = params->cachePorts; + + memDepViolator = NULL; + + blockedLoadSeqNum = 0; +} + +template<class Impl> +void +LSQUnit<Impl>::setCPU(O3CPU *cpu_ptr) +{ + cpu = cpu_ptr; + +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->setDcachePort(dcachePort); + } +#endif +} + +template<class Impl> +std::string +LSQUnit<Impl>::name() const +{ + if (Impl::MaxThreads == 1) { + return iewStage->name() + ".lsq"; + } else { + return iewStage->name() + ".lsq.thread." + to_string(lsqID); + } +} + +template<class Impl> +void +LSQUnit<Impl>::regStats() +{ + lsqForwLoads + .name(name() + ".forwLoads") + .desc("Number of loads that had data forwarded from stores"); + + invAddrLoads + .name(name() + ".invAddrLoads") + .desc("Number of loads ignored due to an invalid address"); + + lsqSquashedLoads + .name(name() + ".squashedLoads") + .desc("Number of loads squashed"); + + lsqIgnoredResponses + .name(name() + ".ignoredResponses") + .desc("Number of memory responses ignored because the instruction is squashed"); + + lsqMemOrderViolation + .name(name() + ".memOrderViolation") + .desc("Number of memory ordering violations"); + + lsqSquashedStores + .name(name() + ".squashedStores") + .desc("Number of stores squashed"); + + invAddrSwpfs + .name(name() + ".invAddrSwpfs") + .desc("Number of software prefetches ignored due to an invalid address"); + + lsqBlockedLoads + .name(name() + ".blockedLoads") + .desc("Number of blocked loads due to partial load-store forwarding"); + + lsqRescheduledLoads + .name(name() + ".rescheduledLoads") + .desc("Number of loads that were rescheduled"); + + lsqCacheBlocked + .name(name() + ".cacheBlocked") + .desc("Number of times an access to memory failed due to the cache being blocked"); +} + +template<class Impl> +void +LSQUnit<Impl>::clearLQ() +{ + loadQueue.clear(); +} + +template<class Impl> +void +LSQUnit<Impl>::clearSQ() +{ + storeQueue.clear(); +} + +template<class Impl> +void +LSQUnit<Impl>::switchOut() +{ + switchedOut = true; + for (int i = 0; i < loadQueue.size(); ++i) { + assert(!loadQueue[i]); + loadQueue[i] = NULL; + } + + assert(storesToWB == 0); +} + +template<class Impl> +void +LSQUnit<Impl>::takeOverFrom() +{ + switchedOut = false; + loads = stores = storesToWB = 0; + + loadHead = loadTail = 0; + + storeHead = storeWBIdx = storeTail = 0; + + usedPorts = 0; + + memDepViolator = NULL; + + blockedLoadSeqNum = 0; + + stalled = false; + isLoadBlocked = false; + loadBlockedHandled = false; +} + +template<class Impl> +void +LSQUnit<Impl>::resizeLQ(unsigned size) +{ + unsigned size_plus_sentinel = size + 1; + assert(size_plus_sentinel >= LQEntries); + + if (size_plus_sentinel > LQEntries) { + while (size_plus_sentinel > loadQueue.size()) { + DynInstPtr dummy; + loadQueue.push_back(dummy); + LQEntries++; + } + } else { + LQEntries = size_plus_sentinel; + } + +} + +template<class Impl> +void +LSQUnit<Impl>::resizeSQ(unsigned size) +{ + unsigned size_plus_sentinel = size + 1; + if (size_plus_sentinel > SQEntries) { + while (size_plus_sentinel > storeQueue.size()) { + SQEntry dummy; + storeQueue.push_back(dummy); + SQEntries++; + } + } else { + SQEntries = size_plus_sentinel; + } +} + +template <class Impl> +void +LSQUnit<Impl>::insert(DynInstPtr &inst) +{ + assert(inst->isMemRef()); + + assert(inst->isLoad() || inst->isStore()); + + if (inst->isLoad()) { + insertLoad(inst); + } else { + insertStore(inst); + } + + inst->setInLSQ(); +} + +template <class Impl> +void +LSQUnit<Impl>::insertLoad(DynInstPtr &load_inst) +{ + assert((loadTail + 1) % LQEntries != loadHead); + assert(loads < LQEntries); + + DPRINTF(LSQUnit, "Inserting load PC %#x, idx:%i [sn:%lli]\n", + load_inst->readPC(), loadTail, load_inst->seqNum); + + load_inst->lqIdx = loadTail; + + if (stores == 0) { + load_inst->sqIdx = -1; + } else { + load_inst->sqIdx = storeTail; + } + + loadQueue[loadTail] = load_inst; + + incrLdIdx(loadTail); + + ++loads; +} + +template <class Impl> +void +LSQUnit<Impl>::insertStore(DynInstPtr &store_inst) +{ + // Make sure it is not full before inserting an instruction. + assert((storeTail + 1) % SQEntries != storeHead); + assert(stores < SQEntries); + + DPRINTF(LSQUnit, "Inserting store PC %#x, idx:%i [sn:%lli]\n", + store_inst->readPC(), storeTail, store_inst->seqNum); + + store_inst->sqIdx = storeTail; + store_inst->lqIdx = loadTail; + + storeQueue[storeTail] = SQEntry(store_inst); + + incrStIdx(storeTail); + + ++stores; +} + +template <class Impl> +typename Impl::DynInstPtr +LSQUnit<Impl>::getMemDepViolator() +{ + DynInstPtr temp = memDepViolator; + + memDepViolator = NULL; + + return temp; +} + +template <class Impl> +unsigned +LSQUnit<Impl>::numFreeEntries() +{ + unsigned free_lq_entries = LQEntries - loads; + unsigned free_sq_entries = SQEntries - stores; + + // Both the LQ and SQ entries have an extra dummy entry to differentiate + // empty/full conditions. Subtract 1 from the free entries. + if (free_lq_entries < free_sq_entries) { + return free_lq_entries - 1; + } else { + return free_sq_entries - 1; + } +} + +template <class Impl> +int +LSQUnit<Impl>::numLoadsReady() +{ + int load_idx = loadHead; + int retval = 0; + + while (load_idx != loadTail) { + assert(loadQueue[load_idx]); + + if (loadQueue[load_idx]->readyToIssue()) { + ++retval; + } + } + + return retval; +} + +template <class Impl> +Fault +LSQUnit<Impl>::executeLoad(DynInstPtr &inst) +{ + // Execute a specific load. + Fault load_fault = NoFault; + + DPRINTF(LSQUnit, "Executing load PC %#x, [sn:%lli]\n", + inst->readPC(),inst->seqNum); + + load_fault = inst->initiateAcc(); + + // If the instruction faulted, then we need to send it along to commit + // without the instruction completing. + if (load_fault != NoFault) { + // Send this instruction to commit, also make sure iew stage + // realizes there is activity. + // Mark it as executed unless it is an uncached load that + // needs to hit the head of commit. + if (!(inst->req->flags & UNCACHEABLE) || inst->isAtCommit()) { + inst->setExecuted(); + } + iewStage->instToCommit(inst); + iewStage->activityThisCycle(); + } + + return load_fault; +} + +template <class Impl> +Fault +LSQUnit<Impl>::executeStore(DynInstPtr &store_inst) +{ + using namespace TheISA; + // Make sure that a store exists. + assert(stores != 0); + + int store_idx = store_inst->sqIdx; + + DPRINTF(LSQUnit, "Executing store PC %#x [sn:%lli]\n", + store_inst->readPC(), store_inst->seqNum); + + // Check the recently completed loads to see if any match this store's + // address. If so, then we have a memory ordering violation. + int load_idx = store_inst->lqIdx; + + Fault store_fault = store_inst->initiateAcc(); + + if (storeQueue[store_idx].size == 0) { + DPRINTF(LSQUnit,"Fault on Store PC %#x, [sn:%lli],Size = 0\n", + store_inst->readPC(),store_inst->seqNum); + + return store_fault; + } + + assert(store_fault == NoFault); + + if (store_inst->isStoreConditional()) { + // Store conditionals need to set themselves as able to + // writeback if we haven't had a fault by here. + storeQueue[store_idx].canWB = true; + + ++storesToWB; + } + + if (!memDepViolator) { + while (load_idx != loadTail) { + // Really only need to check loads that have actually executed + // It's safe to check all loads because effAddr is set to + // InvalAddr when the dyn inst is created. + + // @todo: For now this is extra conservative, detecting a + // violation if the addresses match assuming all accesses + // are quad word accesses. + + // @todo: Fix this, magic number being used here + if ((loadQueue[load_idx]->effAddr >> 8) == + (store_inst->effAddr >> 8)) { + // A load incorrectly passed this store. Squash and refetch. + // For now return a fault to show that it was unsuccessful. + memDepViolator = loadQueue[load_idx]; + ++lsqMemOrderViolation; + + return genMachineCheckFault(); + } + + incrLdIdx(load_idx); + } + + // If we've reached this point, there was no violation. + memDepViolator = NULL; + } + + return store_fault; +} + +template <class Impl> +void +LSQUnit<Impl>::commitLoad() +{ + assert(loadQueue[loadHead]); + + DPRINTF(LSQUnit, "Committing head load instruction, PC %#x\n", + loadQueue[loadHead]->readPC()); + + loadQueue[loadHead] = NULL; + + incrLdIdx(loadHead); + + --loads; +} + +template <class Impl> +void +LSQUnit<Impl>::commitLoads(InstSeqNum &youngest_inst) +{ + assert(loads == 0 || loadQueue[loadHead]); + + while (loads != 0 && loadQueue[loadHead]->seqNum <= youngest_inst) { + commitLoad(); + } +} + +template <class Impl> +void +LSQUnit<Impl>::commitStores(InstSeqNum &youngest_inst) +{ + assert(stores == 0 || storeQueue[storeHead].inst); + + int store_idx = storeHead; + + while (store_idx != storeTail) { + assert(storeQueue[store_idx].inst); + // Mark any stores that are now committed and have not yet + // been marked as able to write back. + if (!storeQueue[store_idx].canWB) { + if (storeQueue[store_idx].inst->seqNum > youngest_inst) { + break; + } + DPRINTF(LSQUnit, "Marking store as able to write back, PC " + "%#x [sn:%lli]\n", + storeQueue[store_idx].inst->readPC(), + storeQueue[store_idx].inst->seqNum); + + storeQueue[store_idx].canWB = true; + + ++storesToWB; + } + + incrStIdx(store_idx); + } +} + +template <class Impl> +void +LSQUnit<Impl>::writebackStores() +{ + while (storesToWB > 0 && + storeWBIdx != storeTail && + storeQueue[storeWBIdx].inst && + storeQueue[storeWBIdx].canWB && + usedPorts < cachePorts) { + + if (isStoreBlocked || lsq->cacheBlocked()) { + DPRINTF(LSQUnit, "Unable to write back any more stores, cache" + " is blocked!\n"); + break; + } + + // Store didn't write any data so no need to write it back to + // memory. + if (storeQueue[storeWBIdx].size == 0) { + completeStore(storeWBIdx); + + incrStIdx(storeWBIdx); + + continue; + } + + ++usedPorts; + + if (storeQueue[storeWBIdx].inst->isDataPrefetch()) { + incrStIdx(storeWBIdx); + + continue; + } + + assert(storeQueue[storeWBIdx].req); + assert(!storeQueue[storeWBIdx].committed); + + DynInstPtr inst = storeQueue[storeWBIdx].inst; + + Request *req = storeQueue[storeWBIdx].req; + storeQueue[storeWBIdx].committed = true; + + assert(!inst->memData); + inst->memData = new uint8_t[64]; + memcpy(inst->memData, (uint8_t *)&storeQueue[storeWBIdx].data, + req->getSize()); + + PacketPtr data_pkt = new Packet(req, Packet::WriteReq, Packet::Broadcast); + data_pkt->dataStatic(inst->memData); + + LSQSenderState *state = new LSQSenderState; + state->isLoad = false; + state->idx = storeWBIdx; + state->inst = inst; + data_pkt->senderState = state; + + DPRINTF(LSQUnit, "D-Cache: Writing back store idx:%i PC:%#x " + "to Addr:%#x, data:%#x [sn:%lli]\n", + storeWBIdx, storeQueue[storeWBIdx].inst->readPC(), + req->getPaddr(), *(inst->memData), + storeQueue[storeWBIdx].inst->seqNum); + + // @todo: Remove this SC hack once the memory system handles it. + if (req->getFlags() & LOCKED) { + if (req->getFlags() & UNCACHEABLE) { + req->setScResult(2); + } else { + if (cpu->lockFlag) { + req->setScResult(1); + } else { + req->setScResult(0); + // Hack: Instantly complete this store. + completeDataAccess(data_pkt); + incrStIdx(storeWBIdx); + continue; + } + } + } else { + // Non-store conditionals do not need a writeback. + state->noWB = true; + } + + if (!dcachePort->sendTiming(data_pkt)) { + // Need to handle becoming blocked on a store. + isStoreBlocked = true; + ++lsqCacheBlocked; + assert(retryPkt == NULL); + retryPkt = data_pkt; + lsq->setRetryTid(lsqID); + } else { + storePostSend(data_pkt); + } + } + + // Not sure this should set it to 0. + usedPorts = 0; + + assert(stores >= 0 && storesToWB >= 0); +} + +/*template <class Impl> +void +LSQUnit<Impl>::removeMSHR(InstSeqNum seqNum) +{ + list<InstSeqNum>::iterator mshr_it = find(mshrSeqNums.begin(), + mshrSeqNums.end(), + seqNum); + + if (mshr_it != mshrSeqNums.end()) { + mshrSeqNums.erase(mshr_it); + DPRINTF(LSQUnit, "Removing MSHR. count = %i\n",mshrSeqNums.size()); + } +}*/ + +template <class Impl> +void +LSQUnit<Impl>::squash(const InstSeqNum &squashed_num) +{ + DPRINTF(LSQUnit, "Squashing until [sn:%lli]!" + "(Loads:%i Stores:%i)\n", squashed_num, loads, stores); + + int load_idx = loadTail; + decrLdIdx(load_idx); + + while (loads != 0 && loadQueue[load_idx]->seqNum > squashed_num) { + DPRINTF(LSQUnit,"Load Instruction PC %#x squashed, " + "[sn:%lli]\n", + loadQueue[load_idx]->readPC(), + loadQueue[load_idx]->seqNum); + + if (isStalled() && load_idx == stallingLoadIdx) { + stalled = false; + stallingStoreIsn = 0; + stallingLoadIdx = 0; + } + + // Clear the smart pointer to make sure it is decremented. + loadQueue[load_idx]->setSquashed(); + loadQueue[load_idx] = NULL; + --loads; + + // Inefficient! + loadTail = load_idx; + + decrLdIdx(load_idx); + ++lsqSquashedLoads; + } + + if (isLoadBlocked) { + if (squashed_num < blockedLoadSeqNum) { + isLoadBlocked = false; + loadBlockedHandled = false; + blockedLoadSeqNum = 0; + } + } + + int store_idx = storeTail; + decrStIdx(store_idx); + + while (stores != 0 && + storeQueue[store_idx].inst->seqNum > squashed_num) { + // Instructions marked as can WB are already committed. + if (storeQueue[store_idx].canWB) { + break; + } + + DPRINTF(LSQUnit,"Store Instruction PC %#x squashed, " + "idx:%i [sn:%lli]\n", + storeQueue[store_idx].inst->readPC(), + store_idx, storeQueue[store_idx].inst->seqNum); + + // I don't think this can happen. It should have been cleared + // by the stalling load. + if (isStalled() && + storeQueue[store_idx].inst->seqNum == stallingStoreIsn) { + panic("Is stalled should have been cleared by stalling load!\n"); + stalled = false; + stallingStoreIsn = 0; + } + + // Clear the smart pointer to make sure it is decremented. + storeQueue[store_idx].inst->setSquashed(); + storeQueue[store_idx].inst = NULL; + storeQueue[store_idx].canWB = 0; + + storeQueue[store_idx].req = NULL; + --stores; + + // Inefficient! + storeTail = store_idx; + + decrStIdx(store_idx); + ++lsqSquashedStores; + } +} + +template <class Impl> +void +LSQUnit<Impl>::storePostSend(Packet *pkt) +{ + if (isStalled() && + storeQueue[storeWBIdx].inst->seqNum == stallingStoreIsn) { + DPRINTF(LSQUnit, "Unstalling, stalling store [sn:%lli] " + "load idx:%i\n", + stallingStoreIsn, stallingLoadIdx); + stalled = false; + stallingStoreIsn = 0; + iewStage->replayMemInst(loadQueue[stallingLoadIdx]); + } + + if (!storeQueue[storeWBIdx].inst->isStoreConditional()) { + // The store is basically completed at this time. This + // only works so long as the checker doesn't try to + // verify the value in memory for stores. + storeQueue[storeWBIdx].inst->setCompleted(); +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->verify(storeQueue[storeWBIdx].inst); + } +#endif + } + + if (pkt->result != Packet::Success) { + DPRINTF(LSQUnit,"D-Cache Write Miss on idx:%i!\n", + storeWBIdx); + + DPRINTF(Activity, "Active st accessing mem miss [sn:%lli]\n", + storeQueue[storeWBIdx].inst->seqNum); + + //mshrSeqNums.push_back(storeQueue[storeWBIdx].inst->seqNum); + + //DPRINTF(LSQUnit, "Added MSHR. count = %i\n",mshrSeqNums.size()); + + // @todo: Increment stat here. + } else { + DPRINTF(LSQUnit,"D-Cache: Write Hit on idx:%i !\n", + storeWBIdx); + + DPRINTF(Activity, "Active st accessing mem hit [sn:%lli]\n", + storeQueue[storeWBIdx].inst->seqNum); + } + + incrStIdx(storeWBIdx); +} + +template <class Impl> +void +LSQUnit<Impl>::writeback(DynInstPtr &inst, PacketPtr pkt) +{ + iewStage->wakeCPU(); + + // Squashed instructions do not need to complete their access. + if (inst->isSquashed()) { + iewStage->decrWb(inst->seqNum); + assert(!inst->isStore()); + ++lsqIgnoredResponses; + return; + } + + if (!inst->isExecuted()) { + inst->setExecuted(); + + // Complete access to copy data to proper place. + inst->completeAcc(pkt); + } + + // Need to insert instruction into queue to commit + iewStage->instToCommit(inst); + + iewStage->activityThisCycle(); +} + +template <class Impl> +void +LSQUnit<Impl>::completeStore(int store_idx) +{ + assert(storeQueue[store_idx].inst); + storeQueue[store_idx].completed = true; + --storesToWB; + // A bit conservative because a store completion may not free up entries, + // but hopefully avoids two store completions in one cycle from making + // the CPU tick twice. + cpu->activityThisCycle(); + + if (store_idx == storeHead) { + do { + incrStIdx(storeHead); + + --stores; + } while (storeQueue[storeHead].completed && + storeHead != storeTail); + + iewStage->updateLSQNextCycle = true; + } + + DPRINTF(LSQUnit, "Completing store [sn:%lli], idx:%i, store head " + "idx:%i\n", + storeQueue[store_idx].inst->seqNum, store_idx, storeHead); + + if (isStalled() && + storeQueue[store_idx].inst->seqNum == stallingStoreIsn) { + DPRINTF(LSQUnit, "Unstalling, stalling store [sn:%lli] " + "load idx:%i\n", + stallingStoreIsn, stallingLoadIdx); + stalled = false; + stallingStoreIsn = 0; + iewStage->replayMemInst(loadQueue[stallingLoadIdx]); + } + + storeQueue[store_idx].inst->setCompleted(); + + // Tell the checker we've completed this instruction. Some stores + // may get reported twice to the checker, but the checker can + // handle that case. +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->verify(storeQueue[store_idx].inst); + } +#endif +} + +template <class Impl> +void +LSQUnit<Impl>::recvRetry() +{ + if (isStoreBlocked) { + assert(retryPkt != NULL); + + if (dcachePort->sendTiming(retryPkt)) { + storePostSend(retryPkt); + retryPkt = NULL; + isStoreBlocked = false; + lsq->setRetryTid(-1); + } else { + // Still blocked! + ++lsqCacheBlocked; + lsq->setRetryTid(lsqID); + } + } else if (isLoadBlocked) { + DPRINTF(LSQUnit, "Loads squash themselves and all younger insts, " + "no need to resend packet.\n"); + } else { + DPRINTF(LSQUnit, "Retry received but LSQ is no longer blocked.\n"); + } +} + +template <class Impl> +inline void +LSQUnit<Impl>::incrStIdx(int &store_idx) +{ + if (++store_idx >= SQEntries) + store_idx = 0; +} + +template <class Impl> +inline void +LSQUnit<Impl>::decrStIdx(int &store_idx) +{ + if (--store_idx < 0) + store_idx += SQEntries; +} + +template <class Impl> +inline void +LSQUnit<Impl>::incrLdIdx(int &load_idx) +{ + if (++load_idx >= LQEntries) + load_idx = 0; +} + +template <class Impl> +inline void +LSQUnit<Impl>::decrLdIdx(int &load_idx) +{ + if (--load_idx < 0) + load_idx += LQEntries; +} + +template <class Impl> +void +LSQUnit<Impl>::dumpInsts() +{ + cprintf("Load store queue: Dumping instructions.\n"); + cprintf("Load queue size: %i\n", loads); + cprintf("Load queue: "); + + int load_idx = loadHead; + + while (load_idx != loadTail && loadQueue[load_idx]) { + cprintf("%#x ", loadQueue[load_idx]->readPC()); + + incrLdIdx(load_idx); + } + + cprintf("Store queue size: %i\n", stores); + cprintf("Store queue: "); + + int store_idx = storeHead; + + while (store_idx != storeTail && storeQueue[store_idx].inst) { + cprintf("%#x ", storeQueue[store_idx].inst->readPC()); + + incrStIdx(store_idx); + } + + cprintf("\n"); +} diff --git a/src/cpu/o3/mem_dep_unit.cc b/src/cpu/o3/mem_dep_unit.cc new file mode 100644 index 000000000..6a14dcbff --- /dev/null +++ b/src/cpu/o3/mem_dep_unit.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/store_set.hh" +#include "cpu/o3/mem_dep_unit_impl.hh" + +// Force instantation of memory dependency unit using store sets and +// O3CPUImpl. +template class MemDepUnit<StoreSet, O3CPUImpl>; + +#ifdef DEBUG +template <> +int +MemDepUnit<StoreSet, O3CPUImpl>::MemDepEntry::memdep_count = 0; +template <> +int +MemDepUnit<StoreSet, O3CPUImpl>::MemDepEntry::memdep_insert = 0; +template <> +int +MemDepUnit<StoreSet, O3CPUImpl>::MemDepEntry::memdep_erase = 0; +#endif diff --git a/src/cpu/o3/mem_dep_unit.hh b/src/cpu/o3/mem_dep_unit.hh new file mode 100644 index 000000000..e399f0133 --- /dev/null +++ b/src/cpu/o3/mem_dep_unit.hh @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_MEM_DEP_UNIT_HH__ +#define __CPU_O3_MEM_DEP_UNIT_HH__ + +#include <list> +#include <set> + +#include "base/hashmap.hh" +#include "base/refcnt.hh" +#include "base/statistics.hh" +#include "cpu/inst_seq.hh" + +struct SNHash { + size_t operator() (const InstSeqNum &seq_num) const { + unsigned a = (unsigned)seq_num; + unsigned hash = (((a >> 14) ^ ((a >> 2) & 0xffff))) & 0x7FFFFFFF; + + return hash; + } +}; + +template <class Impl> +class InstructionQueue; + +/** + * Memory dependency unit class. This holds the memory dependence predictor. + * As memory operations are issued to the IQ, they are also issued to this + * unit, which then looks up the prediction as to what they are dependent + * upon. This unit must be checked prior to a memory operation being able + * to issue. Although this is templated, it's somewhat hard to make a generic + * memory dependence unit. This one is mostly for store sets; it will be + * quite limited in what other memory dependence predictions it can also + * utilize. Thus this class should be most likely be rewritten for other + * dependence prediction schemes. + */ +template <class MemDepPred, class Impl> +class MemDepUnit { + public: + typedef typename Impl::Params Params; + typedef typename Impl::DynInstPtr DynInstPtr; + + /** Empty constructor. Must call init() prior to using in this case. */ + MemDepUnit() {} + + /** Constructs a MemDepUnit with given parameters. */ + MemDepUnit(Params *params); + + /** Frees up any memory allocated. */ + ~MemDepUnit(); + + /** Returns the name of the memory dependence unit. */ + std::string name() const; + + /** Initializes the unit with parameters and a thread id. */ + void init(Params *params, int tid); + + /** Registers statistics. */ + void regStats(); + + /** Switches out the memory dependence predictor. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Sets the pointer to the IQ. */ + void setIQ(InstructionQueue<Impl> *iq_ptr); + + /** Inserts a memory instruction. */ + void insert(DynInstPtr &inst); + + /** Inserts a non-speculative memory instruction. */ + void insertNonSpec(DynInstPtr &inst); + + /** Inserts a barrier instruction. */ + void insertBarrier(DynInstPtr &barr_inst); + + /** Indicate that an instruction has its registers ready. */ + void regsReady(DynInstPtr &inst); + + /** Indicate that a non-speculative instruction is ready. */ + void nonSpecInstReady(DynInstPtr &inst); + + /** Reschedules an instruction to be re-executed. */ + void reschedule(DynInstPtr &inst); + + /** Replays all instructions that have been rescheduled by moving them to + * the ready list. + */ + void replay(DynInstPtr &inst); + + /** Completes a memory instruction. */ + void completed(DynInstPtr &inst); + + /** Completes a barrier instruction. */ + void completeBarrier(DynInstPtr &inst); + + /** Wakes any dependents of a memory instruction. */ + void wakeDependents(DynInstPtr &inst); + + /** Squashes all instructions up until a given sequence number for a + * specific thread. + */ + void squash(const InstSeqNum &squashed_num, unsigned tid); + + /** Indicates an ordering violation between a store and a younger load. */ + void violation(DynInstPtr &store_inst, DynInstPtr &violating_load); + + /** Issues the given instruction */ + void issue(DynInstPtr &inst); + + /** Debugging function to dump the lists of instructions. */ + void dumpLists(); + + private: + typedef typename std::list<DynInstPtr>::iterator ListIt; + + class MemDepEntry; + + typedef RefCountingPtr<MemDepEntry> MemDepEntryPtr; + + /** Memory dependence entries that track memory operations, marking + * when the instruction is ready to execute and what instructions depend + * upon it. + */ + class MemDepEntry : public RefCounted { + public: + /** Constructs a memory dependence entry. */ + MemDepEntry(DynInstPtr &new_inst) + : inst(new_inst), regsReady(false), memDepReady(false), + completed(false), squashed(false) + { +#ifdef DEBUG + ++memdep_count; + + DPRINTF(MemDepUnit, "Memory dependency entry created. " + "memdep_count=%i\n", memdep_count); +#endif + } + + /** Frees any pointers. */ + ~MemDepEntry() + { + for (int i = 0; i < dependInsts.size(); ++i) { + dependInsts[i] = NULL; + } +#ifdef DEBUG + --memdep_count; + + DPRINTF(MemDepUnit, "Memory dependency entry deleted. " + "memdep_count=%i\n", memdep_count); +#endif + } + + /** Returns the name of the memory dependence entry. */ + std::string name() const { return "memdepentry"; } + + /** The instruction being tracked. */ + DynInstPtr inst; + + /** The iterator to the instruction's location inside the list. */ + ListIt listIt; + + /** A vector of any dependent instructions. */ + std::vector<MemDepEntryPtr> dependInsts; + + /** If the registers are ready or not. */ + bool regsReady; + /** If all memory dependencies have been satisfied. */ + bool memDepReady; + /** If the instruction is completed. */ + bool completed; + /** If the instruction is squashed. */ + bool squashed; + + /** For debugging. */ +#ifdef DEBUG + static int memdep_count; + static int memdep_insert; + static int memdep_erase; +#endif + }; + + /** Finds the memory dependence entry in the hash map. */ + inline MemDepEntryPtr &findInHash(const DynInstPtr &inst); + + /** Moves an entry to the ready list. */ + inline void moveToReady(MemDepEntryPtr &ready_inst_entry); + + typedef m5::hash_map<InstSeqNum, MemDepEntryPtr, SNHash> MemDepHash; + + typedef typename MemDepHash::iterator MemDepHashIt; + + /** A hash map of all memory dependence entries. */ + MemDepHash memDepHash; + + /** A list of all instructions in the memory dependence unit. */ + std::list<DynInstPtr> instList[Impl::MaxThreads]; + + /** A list of all instructions that are going to be replayed. */ + std::list<DynInstPtr> instsToReplay; + + /** The memory dependence predictor. It is accessed upon new + * instructions being added to the IQ, and responds by telling + * this unit what instruction the newly added instruction is dependent + * upon. + */ + MemDepPred depPred; + + /** Is there an outstanding load barrier that loads must wait on. */ + bool loadBarrier; + /** The sequence number of the load barrier. */ + InstSeqNum loadBarrierSN; + /** Is there an outstanding store barrier that loads must wait on. */ + bool storeBarrier; + /** The sequence number of the store barrier. */ + InstSeqNum storeBarrierSN; + + /** Pointer to the IQ. */ + InstructionQueue<Impl> *iqPtr; + + /** The thread id of this memory dependence unit. */ + int id; + + /** Stat for number of inserted loads. */ + Stats::Scalar<> insertedLoads; + /** Stat for number of inserted stores. */ + Stats::Scalar<> insertedStores; + /** Stat for number of conflicting loads that had to wait for a store. */ + Stats::Scalar<> conflictingLoads; + /** Stat for number of conflicting stores that had to wait for a store. */ + Stats::Scalar<> conflictingStores; +}; + +#endif // __CPU_O3_MEM_DEP_UNIT_HH__ diff --git a/src/cpu/o3/mem_dep_unit_impl.hh b/src/cpu/o3/mem_dep_unit_impl.hh new file mode 100644 index 000000000..c649ca385 --- /dev/null +++ b/src/cpu/o3/mem_dep_unit_impl.hh @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include <map> + +#include "cpu/o3/inst_queue.hh" +#include "cpu/o3/mem_dep_unit.hh" + +template <class MemDepPred, class Impl> +MemDepUnit<MemDepPred, Impl>::MemDepUnit(Params *params) + : depPred(params->SSITSize, params->LFSTSize), loadBarrier(false), + loadBarrierSN(0), storeBarrier(false), storeBarrierSN(0), iqPtr(NULL) +{ + DPRINTF(MemDepUnit, "Creating MemDepUnit object.\n"); +} + +template <class MemDepPred, class Impl> +MemDepUnit<MemDepPred, Impl>::~MemDepUnit() +{ + for (int tid=0; tid < Impl::MaxThreads; tid++) { + + ListIt inst_list_it = instList[tid].begin(); + + MemDepHashIt hash_it; + + while (!instList[tid].empty()) { + hash_it = memDepHash.find((*inst_list_it)->seqNum); + + assert(hash_it != memDepHash.end()); + + memDepHash.erase(hash_it); + + instList[tid].erase(inst_list_it++); + } + } + +#ifdef DEBUG + assert(MemDepEntry::memdep_count == 0); +#endif +} + +template <class MemDepPred, class Impl> +std::string +MemDepUnit<MemDepPred, Impl>::name() const +{ + return "memdepunit"; +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::init(Params *params, int tid) +{ + DPRINTF(MemDepUnit, "Creating MemDepUnit %i object.\n",tid); + + id = tid; + + depPred.init(params->SSITSize, params->LFSTSize); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::regStats() +{ + insertedLoads + .name(name() + ".memDep.insertedLoads") + .desc("Number of loads inserted to the mem dependence unit."); + + insertedStores + .name(name() + ".memDep.insertedStores") + .desc("Number of stores inserted to the mem dependence unit."); + + conflictingLoads + .name(name() + ".memDep.conflictingLoads") + .desc("Number of conflicting loads."); + + conflictingStores + .name(name() + ".memDep.conflictingStores") + .desc("Number of conflicting stores."); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::switchOut() +{ + assert(instList[0].empty()); + assert(instsToReplay.empty()); + assert(memDepHash.empty()); + // Clear any state. + for (int i = 0; i < Impl::MaxThreads; ++i) { + instList[i].clear(); + } + instsToReplay.clear(); + memDepHash.clear(); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::takeOverFrom() +{ + // Be sure to reset all state. + loadBarrier = storeBarrier = false; + loadBarrierSN = storeBarrierSN = 0; + depPred.clear(); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::setIQ(InstructionQueue<Impl> *iq_ptr) +{ + iqPtr = iq_ptr; +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::insert(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + MemDepEntryPtr inst_entry = new MemDepEntry(inst); + + // Add the MemDepEntry to the hash. + memDepHash.insert( + std::pair<InstSeqNum, MemDepEntryPtr>(inst->seqNum, inst_entry)); +#ifdef DEBUG + MemDepEntry::memdep_insert++; +#endif + + instList[tid].push_back(inst); + + inst_entry->listIt = --(instList[tid].end()); + + // Check any barriers and the dependence predictor for any + // producing memrefs/stores. + InstSeqNum producing_store; + if (inst->isLoad() && loadBarrier) { + producing_store = loadBarrierSN; + } else if (inst->isStore() && storeBarrier) { + producing_store = storeBarrierSN; + } else { + producing_store = depPred.checkInst(inst->readPC()); + } + + MemDepEntryPtr store_entry = NULL; + + // If there is a producing store, try to find the entry. + if (producing_store != 0) { + MemDepHashIt hash_it = memDepHash.find(producing_store); + + if (hash_it != memDepHash.end()) { + store_entry = (*hash_it).second; + } + } + + // If no store entry, then instruction can issue as soon as the registers + // are ready. + if (!store_entry) { + DPRINTF(MemDepUnit, "No dependency for inst PC " + "%#x [sn:%lli].\n", inst->readPC(), inst->seqNum); + + inst_entry->memDepReady = true; + + if (inst->readyToIssue()) { + inst_entry->regsReady = true; + + moveToReady(inst_entry); + } + } else { + // Otherwise make the instruction dependent on the store/barrier. + DPRINTF(MemDepUnit, "Adding to dependency list; " + "inst PC %#x is dependent on [sn:%lli].\n", + inst->readPC(), producing_store); + + if (inst->readyToIssue()) { + inst_entry->regsReady = true; + } + + // Add this instruction to the list of dependents. + store_entry->dependInsts.push_back(inst_entry); + + if (inst->isLoad()) { + ++conflictingLoads; + } else { + ++conflictingStores; + } + } + + if (inst->isStore()) { + DPRINTF(MemDepUnit, "Inserting store PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); + + depPred.insertStore(inst->readPC(), inst->seqNum, inst->threadNumber); + + ++insertedStores; + } else if (inst->isLoad()) { + ++insertedLoads; + } else { + panic("Unknown type! (most likely a barrier)."); + } +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::insertNonSpec(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + MemDepEntryPtr inst_entry = new MemDepEntry(inst); + + // Insert the MemDepEntry into the hash. + memDepHash.insert( + std::pair<InstSeqNum, MemDepEntryPtr>(inst->seqNum, inst_entry)); +#ifdef DEBUG + MemDepEntry::memdep_insert++; +#endif + + // Add the instruction to the list. + instList[tid].push_back(inst); + + inst_entry->listIt = --(instList[tid].end()); + + // Might want to turn this part into an inline function or something. + // It's shared between both insert functions. + if (inst->isStore()) { + DPRINTF(MemDepUnit, "Inserting store PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); + + depPred.insertStore(inst->readPC(), inst->seqNum, inst->threadNumber); + + ++insertedStores; + } else if (inst->isLoad()) { + ++insertedLoads; + } else { + panic("Unknown type! (most likely a barrier)."); + } +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::insertBarrier(DynInstPtr &barr_inst) +{ + InstSeqNum barr_sn = barr_inst->seqNum; + // Memory barriers block loads and stores, write barriers only stores. + if (barr_inst->isMemBarrier()) { + loadBarrier = true; + loadBarrierSN = barr_sn; + storeBarrier = true; + storeBarrierSN = barr_sn; + DPRINTF(MemDepUnit, "Inserted a memory barrier\n"); + } else if (barr_inst->isWriteBarrier()) { + storeBarrier = true; + storeBarrierSN = barr_sn; + DPRINTF(MemDepUnit, "Inserted a write barrier\n"); + } + + unsigned tid = barr_inst->threadNumber; + + MemDepEntryPtr inst_entry = new MemDepEntry(barr_inst); + + // Add the MemDepEntry to the hash. + memDepHash.insert( + std::pair<InstSeqNum, MemDepEntryPtr>(barr_sn, inst_entry)); +#ifdef DEBUG + MemDepEntry::memdep_insert++; +#endif + + // Add the instruction to the instruction list. + instList[tid].push_back(barr_inst); + + inst_entry->listIt = --(instList[tid].end()); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::regsReady(DynInstPtr &inst) +{ + DPRINTF(MemDepUnit, "Marking registers as ready for " + "instruction PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); + + MemDepEntryPtr inst_entry = findInHash(inst); + + inst_entry->regsReady = true; + + if (inst_entry->memDepReady) { + DPRINTF(MemDepUnit, "Instruction has its memory " + "dependencies resolved, adding it to the ready list.\n"); + + moveToReady(inst_entry); + } else { + DPRINTF(MemDepUnit, "Instruction still waiting on " + "memory dependency.\n"); + } +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::nonSpecInstReady(DynInstPtr &inst) +{ + DPRINTF(MemDepUnit, "Marking non speculative " + "instruction PC %#x as ready [sn:%lli].\n", + inst->readPC(), inst->seqNum); + + MemDepEntryPtr inst_entry = findInHash(inst); + + moveToReady(inst_entry); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::reschedule(DynInstPtr &inst) +{ + instsToReplay.push_back(inst); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::replay(DynInstPtr &inst) +{ + DynInstPtr temp_inst; + bool found_inst = false; + + // For now this replay function replays all waiting memory ops. + while (!instsToReplay.empty()) { + temp_inst = instsToReplay.front(); + + MemDepEntryPtr inst_entry = findInHash(temp_inst); + + DPRINTF(MemDepUnit, "Replaying mem instruction PC %#x " + "[sn:%lli].\n", + temp_inst->readPC(), temp_inst->seqNum); + + moveToReady(inst_entry); + + if (temp_inst == inst) { + found_inst = true; + } + + instsToReplay.pop_front(); + } + + assert(found_inst); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::completed(DynInstPtr &inst) +{ + DPRINTF(MemDepUnit, "Completed mem instruction PC %#x " + "[sn:%lli].\n", + inst->readPC(), inst->seqNum); + + unsigned tid = inst->threadNumber; + + // Remove the instruction from the hash and the list. + MemDepHashIt hash_it = memDepHash.find(inst->seqNum); + + assert(hash_it != memDepHash.end()); + + instList[tid].erase((*hash_it).second->listIt); + + (*hash_it).second = NULL; + + memDepHash.erase(hash_it); +#ifdef DEBUG + MemDepEntry::memdep_erase++; +#endif +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::completeBarrier(DynInstPtr &inst) +{ + wakeDependents(inst); + completed(inst); + + InstSeqNum barr_sn = inst->seqNum; + + if (inst->isMemBarrier()) { + assert(loadBarrier && storeBarrier); + if (loadBarrierSN == barr_sn) + loadBarrier = false; + if (storeBarrierSN == barr_sn) + storeBarrier = false; + } else if (inst->isWriteBarrier()) { + assert(storeBarrier); + if (storeBarrierSN == barr_sn) + storeBarrier = false; + } +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::wakeDependents(DynInstPtr &inst) +{ + // Only stores and barriers have dependents. + if (!inst->isStore() && !inst->isMemBarrier() && !inst->isWriteBarrier()) { + return; + } + + MemDepEntryPtr inst_entry = findInHash(inst); + + for (int i = 0; i < inst_entry->dependInsts.size(); ++i ) { + MemDepEntryPtr woken_inst = inst_entry->dependInsts[i]; + + if (!woken_inst->inst) { + // Potentially removed mem dep entries could be on this list + continue; + } + + DPRINTF(MemDepUnit, "Waking up a dependent inst, " + "[sn:%lli].\n", + woken_inst->inst->seqNum); + + if (woken_inst->regsReady && !woken_inst->squashed) { + moveToReady(woken_inst); + } else { + woken_inst->memDepReady = true; + } + } + + inst_entry->dependInsts.clear(); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::squash(const InstSeqNum &squashed_num, + unsigned tid) +{ + if (!instsToReplay.empty()) { + ListIt replay_it = instsToReplay.begin(); + while (replay_it != instsToReplay.end()) { + if ((*replay_it)->threadNumber == tid && + (*replay_it)->seqNum > squashed_num) { + instsToReplay.erase(replay_it++); + } else { + ++replay_it; + } + } + } + + ListIt squash_it = instList[tid].end(); + --squash_it; + + MemDepHashIt hash_it; + + while (!instList[tid].empty() && + (*squash_it)->seqNum > squashed_num) { + + DPRINTF(MemDepUnit, "Squashing inst [sn:%lli]\n", + (*squash_it)->seqNum); + + hash_it = memDepHash.find((*squash_it)->seqNum); + + assert(hash_it != memDepHash.end()); + + (*hash_it).second->squashed = true; + + (*hash_it).second = NULL; + + memDepHash.erase(hash_it); +#ifdef DEBUG + MemDepEntry::memdep_erase++; +#endif + + instList[tid].erase(squash_it--); + } + + // Tell the dependency predictor to squash as well. + depPred.squash(squashed_num, tid); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::violation(DynInstPtr &store_inst, + DynInstPtr &violating_load) +{ + DPRINTF(MemDepUnit, "Passing violating PCs to store sets," + " load: %#x, store: %#x\n", violating_load->readPC(), + store_inst->readPC()); + // Tell the memory dependence unit of the violation. + depPred.violation(violating_load->readPC(), store_inst->readPC()); +} + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::issue(DynInstPtr &inst) +{ + DPRINTF(MemDepUnit, "Issuing instruction PC %#x [sn:%lli].\n", + inst->readPC(), inst->seqNum); + + depPred.issued(inst->readPC(), inst->seqNum, inst->isStore()); +} + +template <class MemDepPred, class Impl> +inline typename MemDepUnit<MemDepPred,Impl>::MemDepEntryPtr & +MemDepUnit<MemDepPred, Impl>::findInHash(const DynInstPtr &inst) +{ + MemDepHashIt hash_it = memDepHash.find(inst->seqNum); + + assert(hash_it != memDepHash.end()); + + return (*hash_it).second; +} + +template <class MemDepPred, class Impl> +inline void +MemDepUnit<MemDepPred, Impl>::moveToReady(MemDepEntryPtr &woken_inst_entry) +{ + DPRINTF(MemDepUnit, "Adding instruction [sn:%lli] " + "to the ready list.\n", woken_inst_entry->inst->seqNum); + + assert(!woken_inst_entry->squashed); + + iqPtr->addReadyMemInst(woken_inst_entry->inst); +} + + +template <class MemDepPred, class Impl> +void +MemDepUnit<MemDepPred, Impl>::dumpLists() +{ + for (unsigned tid=0; tid < Impl::MaxThreads; tid++) { + cprintf("Instruction list %i size: %i\n", + tid, instList[tid].size()); + + ListIt inst_list_it = instList[tid].begin(); + int num = 0; + + while (inst_list_it != instList[tid].end()) { + cprintf("Instruction:%i\nPC:%#x\n[sn:%i]\n[tid:%i]\nIssued:%i\n" + "Squashed:%i\n\n", + num, (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + inst_list_it++; + ++num; + } + } + + cprintf("Memory dependence hash size: %i\n", memDepHash.size()); + +#ifdef DEBUG + cprintf("Memory dependence entries: %i\n", MemDepEntry::memdep_count); +#endif +} diff --git a/src/cpu/o3/mips/cpu.cc b/src/cpu/o3/mips/cpu.cc new file mode 100755 index 000000000..420f460b2 --- /dev/null +++ b/src/cpu/o3/mips/cpu.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "cpu/o3/mips/impl.hh" +#include "cpu/o3/mips/cpu_impl.hh" +#include "cpu/o3/mips/dyn_inst.hh" + +// Force instantiation of MipsO3CPU for all the implemntations that are +// needed. Consider merging this and mips_dyn_inst.cc, and maybe all +// classes that depend on a certain impl, into one file (mips_impl.cc?). +template class MipsO3CPU<MipsSimpleImpl>; diff --git a/src/cpu/o3/mips/cpu.hh b/src/cpu/o3/mips/cpu.hh new file mode 100755 index 000000000..bf04b9f69 --- /dev/null +++ b/src/cpu/o3/mips/cpu.hh @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#ifndef __CPU_O3_MIPS_CPU_HH__ +#define __CPU_O3_MIPS_CPU_HH__ + +#include "arch/mips/regfile.hh" +#include "arch/mips/syscallreturn.hh" +#include "cpu/thread_context.hh" +#include "cpu/o3/cpu.hh" +#include "sim/byteswap.hh" +#include "sim/faults.hh" + +class EndQuiesceEvent; +namespace Kernel { + class Statistics; +}; + +class TranslatingPort; + +/** + * MipsO3CPU class. Derives from the FullO3CPU class, and + * implements all ISA and implementation specific functions of the + * CPU. This is the CPU class that is used for the SimObjects, and is + * what is given to the DynInsts. Most of its state exists in the + * FullO3CPU; the state is has is mainly for ISA specific + * functionality. + */ +template <class Impl> +class MipsO3CPU : public FullO3CPU<Impl> +{ + public: + typedef O3ThreadState<Impl> ImplState; + typedef O3ThreadState<Impl> Thread; + typedef typename Impl::Params Params; + + /** Constructs an MipsO3CPU with the given parameters. */ + MipsO3CPU(Params *params); + + /** Registers statistics. */ + void regStats(); + + /** Translates instruction requestion in syscall emulation mode. */ + Fault translateInstReq(RequestPtr &req, Thread *thread) + { + return thread->getProcessPtr()->pTable->translate(req); + } + + /** Translates data read request in syscall emulation mode. */ + Fault translateDataReadReq(RequestPtr &req, Thread *thread) + { + return thread->getProcessPtr()->pTable->translate(req); + } + + /** Translates data write request in syscall emulation mode. */ + Fault translateDataWriteReq(RequestPtr &req, Thread *thread) + { + return thread->getProcessPtr()->pTable->translate(req); + } + + /** Reads a miscellaneous register. */ + TheISA::MiscReg readMiscReg(int misc_reg, unsigned tid); + + /** Reads a misc. register, including any side effects the read + * might have as defined by the architecture. + */ + TheISA::MiscReg readMiscRegWithEffect(int misc_reg, + Fault &fault, unsigned tid); + + /** Sets a miscellaneous register. */ + Fault setMiscReg(int misc_reg, const TheISA::MiscReg &val, unsigned tid); + + /** Sets a misc. register, including any side effects the write + * might have as defined by the architecture. + */ + Fault setMiscRegWithEffect(int misc_reg, + const TheISA::MiscReg &val, unsigned tid); + + /** Initiates a squash of all in-flight instructions for a given + * thread. The source of the squash is an external update of + * state through the TC. + */ + void squashFromTC(unsigned tid); + + /** Traps to handle given fault. */ + void trap(Fault fault, unsigned tid); + + /** Executes a syscall. + * @todo: Determine if this needs to be virtual. + */ + void syscall(int64_t callnum, int tid); + /** Gets a syscall argument. */ + TheISA::IntReg getSyscallArg(int i, int tid); + + /** Used to shift args for indirect syscall. */ + void setSyscallArg(int i, TheISA::IntReg val, int tid); + + /** Sets the return value of a syscall. */ + void setSyscallReturn(SyscallReturn return_value, int tid); + + /** CPU read function, forwards read to LSQ. */ + template <class T> + Fault read(RequestPtr &req, T &data, int load_idx) + { + return this->iew.ldstQueue.read(req, data, load_idx); + } + + /** CPU write function, forwards write to LSQ. */ + template <class T> + Fault write(RequestPtr &req, T &data, int store_idx) + { + return this->iew.ldstQueue.write(req, data, store_idx); + } + + Addr lockAddr; + + /** Temporary fix for the lock flag, works in the UP case. */ + bool lockFlag; +}; + +#endif // __CPU_O3_MIPS_CPU_HH__ diff --git a/src/cpu/o3/mips/cpu_builder.cc b/src/cpu/o3/mips/cpu_builder.cc new file mode 100644 index 000000000..f1c3b33a5 --- /dev/null +++ b/src/cpu/o3/mips/cpu_builder.cc @@ -0,0 +1,394 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include <string> + +#include "cpu/base.hh" +#include "cpu/o3/mips/cpu.hh" +#include "cpu/o3/mips/impl.hh" +#include "cpu/o3/mips/params.hh" +#include "cpu/o3/fu_pool.hh" +#include "sim/builder.hh" + +class DerivO3CPU : public MipsO3CPU<MipsSimpleImpl> +{ + public: + DerivO3CPU(MipsSimpleParams *p) + : MipsO3CPU<MipsSimpleImpl>(p) + { } +}; + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(DerivO3CPU) + +Param<int> clock; +Param<int> numThreads; +Param<int> activity; + +SimObjectVectorParam<Process *> workload; + +SimObjectParam<MemObject *> mem; + +SimObjectParam<BaseCPU *> checker; + +Param<Counter> max_insts_any_thread; +Param<Counter> max_insts_all_threads; +Param<Counter> max_loads_any_thread; +Param<Counter> max_loads_all_threads; + +Param<unsigned> cachePorts; + +Param<unsigned> decodeToFetchDelay; +Param<unsigned> renameToFetchDelay; +Param<unsigned> iewToFetchDelay; +Param<unsigned> commitToFetchDelay; +Param<unsigned> fetchWidth; + +Param<unsigned> renameToDecodeDelay; +Param<unsigned> iewToDecodeDelay; +Param<unsigned> commitToDecodeDelay; +Param<unsigned> fetchToDecodeDelay; +Param<unsigned> decodeWidth; + +Param<unsigned> iewToRenameDelay; +Param<unsigned> commitToRenameDelay; +Param<unsigned> decodeToRenameDelay; +Param<unsigned> renameWidth; + +Param<unsigned> commitToIEWDelay; +Param<unsigned> renameToIEWDelay; +Param<unsigned> issueToExecuteDelay; +Param<unsigned> dispatchWidth; +Param<unsigned> issueWidth; +Param<unsigned> wbWidth; +Param<unsigned> wbDepth; +SimObjectParam<FUPool *> fuPool; + +Param<unsigned> iewToCommitDelay; +Param<unsigned> renameToROBDelay; +Param<unsigned> commitWidth; +Param<unsigned> squashWidth; +Param<Tick> trapLatency; + +Param<unsigned> backComSize; +Param<unsigned> forwardComSize; + +Param<std::string> predType; +Param<unsigned> localPredictorSize; +Param<unsigned> localCtrBits; +Param<unsigned> localHistoryTableSize; +Param<unsigned> localHistoryBits; +Param<unsigned> globalPredictorSize; +Param<unsigned> globalCtrBits; +Param<unsigned> globalHistoryBits; +Param<unsigned> choicePredictorSize; +Param<unsigned> choiceCtrBits; + +Param<unsigned> BTBEntries; +Param<unsigned> BTBTagSize; + +Param<unsigned> RASSize; + +Param<unsigned> LQEntries; +Param<unsigned> SQEntries; +Param<unsigned> LFSTSize; +Param<unsigned> SSITSize; + +Param<unsigned> numPhysIntRegs; +Param<unsigned> numPhysFloatRegs; +Param<unsigned> numIQEntries; +Param<unsigned> numROBEntries; + +Param<unsigned> smtNumFetchingThreads; +Param<std::string> smtFetchPolicy; +Param<std::string> smtLSQPolicy; +Param<unsigned> smtLSQThreshold; +Param<std::string> smtIQPolicy; +Param<unsigned> smtIQThreshold; +Param<std::string> smtROBPolicy; +Param<unsigned> smtROBThreshold; +Param<std::string> smtCommitPolicy; + +Param<unsigned> instShiftAmt; + +Param<bool> defer_registration; + +Param<bool> function_trace; +Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(DerivO3CPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(DerivO3CPU) + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(numThreads, "number of HW thread contexts"), + INIT_PARAM_DFLT(activity, "Initial activity count", 0), + + INIT_PARAM(workload, "Processes to run"), + + INIT_PARAM(mem, "Memory"), + + INIT_PARAM_DFLT(checker, "Checker CPU", NULL), + + INIT_PARAM_DFLT(max_insts_any_thread, + "Terminate when any thread reaches this inst count", + 0), + INIT_PARAM_DFLT(max_insts_all_threads, + "Terminate when all threads have reached" + "this inst count", + 0), + INIT_PARAM_DFLT(max_loads_any_thread, + "Terminate when any thread reaches this load count", + 0), + INIT_PARAM_DFLT(max_loads_all_threads, + "Terminate when all threads have reached this load" + "count", + 0), + + INIT_PARAM_DFLT(cachePorts, "Cache Ports", 200), + + INIT_PARAM(decodeToFetchDelay, "Decode to fetch delay"), + INIT_PARAM(renameToFetchDelay, "Rename to fetch delay"), + INIT_PARAM(iewToFetchDelay, "Issue/Execute/Writeback to fetch" + "delay"), + INIT_PARAM(commitToFetchDelay, "Commit to fetch delay"), + INIT_PARAM(fetchWidth, "Fetch width"), + INIT_PARAM(renameToDecodeDelay, "Rename to decode delay"), + INIT_PARAM(iewToDecodeDelay, "Issue/Execute/Writeback to decode" + "delay"), + INIT_PARAM(commitToDecodeDelay, "Commit to decode delay"), + INIT_PARAM(fetchToDecodeDelay, "Fetch to decode delay"), + INIT_PARAM(decodeWidth, "Decode width"), + + INIT_PARAM(iewToRenameDelay, "Issue/Execute/Writeback to rename" + "delay"), + INIT_PARAM(commitToRenameDelay, "Commit to rename delay"), + INIT_PARAM(decodeToRenameDelay, "Decode to rename delay"), + INIT_PARAM(renameWidth, "Rename width"), + + INIT_PARAM(commitToIEWDelay, "Commit to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(renameToIEWDelay, "Rename to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(issueToExecuteDelay, "Issue to execute delay (internal" + "to the IEW stage)"), + INIT_PARAM(dispatchWidth, "Dispatch width"), + INIT_PARAM(issueWidth, "Issue width"), + INIT_PARAM(wbWidth, "Writeback width"), + INIT_PARAM(wbDepth, "Writeback depth (number of cycles it can buffer)"), + INIT_PARAM_DFLT(fuPool, "Functional unit pool", NULL), + + INIT_PARAM(iewToCommitDelay, "Issue/Execute/Writeback to commit " + "delay"), + INIT_PARAM(renameToROBDelay, "Rename to reorder buffer delay"), + INIT_PARAM(commitWidth, "Commit width"), + INIT_PARAM(squashWidth, "Squash width"), + INIT_PARAM_DFLT(trapLatency, "Number of cycles before the trap is handled", 6), + + INIT_PARAM(backComSize, "Time buffer size for backwards communication"), + INIT_PARAM(forwardComSize, "Time buffer size for forward communication"), + + INIT_PARAM(predType, "Type of branch predictor ('local', 'tournament')"), + INIT_PARAM(localPredictorSize, "Size of local predictor"), + INIT_PARAM(localCtrBits, "Bits per counter"), + INIT_PARAM(localHistoryTableSize, "Size of local history table"), + INIT_PARAM(localHistoryBits, "Bits for the local history"), + INIT_PARAM(globalPredictorSize, "Size of global predictor"), + INIT_PARAM(globalCtrBits, "Bits per counter"), + INIT_PARAM(globalHistoryBits, "Bits of history"), + INIT_PARAM(choicePredictorSize, "Size of choice predictor"), + INIT_PARAM(choiceCtrBits, "Bits of choice counters"), + + INIT_PARAM(BTBEntries, "Number of BTB entries"), + INIT_PARAM(BTBTagSize, "Size of the BTB tags, in bits"), + + INIT_PARAM(RASSize, "RAS size"), + + INIT_PARAM(LQEntries, "Number of load queue entries"), + INIT_PARAM(SQEntries, "Number of store queue entries"), + INIT_PARAM(LFSTSize, "Last fetched store table size"), + INIT_PARAM(SSITSize, "Store set ID table size"), + + INIT_PARAM(numPhysIntRegs, "Number of physical integer registers"), + INIT_PARAM(numPhysFloatRegs, "Number of physical floating point " + "registers"), + INIT_PARAM(numIQEntries, "Number of instruction queue entries"), + INIT_PARAM(numROBEntries, "Number of reorder buffer entries"), + + INIT_PARAM_DFLT(smtNumFetchingThreads, "SMT Number of Fetching Threads", 1), + INIT_PARAM_DFLT(smtFetchPolicy, "SMT Fetch Policy", "SingleThread"), + INIT_PARAM_DFLT(smtLSQPolicy, "SMT LSQ Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtLSQThreshold,"SMT LSQ Threshold", 100), + INIT_PARAM_DFLT(smtIQPolicy, "SMT IQ Policy", "Partitioned"), + INIT_PARAM_DFLT(smtIQThreshold, "SMT IQ Threshold", 100), + INIT_PARAM_DFLT(smtROBPolicy, "SMT ROB Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtROBThreshold,"SMT ROB Threshold", 100), + INIT_PARAM_DFLT(smtCommitPolicy,"SMT Commit Fetch Policy", "RoundRobin"), + + INIT_PARAM(instShiftAmt, "Number of bits to shift instructions by"), + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(DerivO3CPU) + +CREATE_SIM_OBJECT(DerivO3CPU) +{ + DerivO3CPU *cpu; + + // In non-full-system mode, we infer the number of threads from + // the workload if it's not explicitly specified. + int actual_num_threads = + (numThreads.isValid() && numThreads >= workload.size()) ? + numThreads : workload.size(); + + if (workload.size() == 0) { + fatal("Must specify at least one workload!"); + } + + MipsSimpleParams *params = new MipsSimpleParams; + + params->clock = clock; + + params->name = getInstanceName(); + params->numberOfThreads = actual_num_threads; + params->activity = activity; + + params->workload = workload; + + params->mem = mem; + + params->checker = checker; + + params->max_insts_any_thread = max_insts_any_thread; + params->max_insts_all_threads = max_insts_all_threads; + params->max_loads_any_thread = max_loads_any_thread; + params->max_loads_all_threads = max_loads_all_threads; + + // + // Caches + // + params->cachePorts = cachePorts; + + params->decodeToFetchDelay = decodeToFetchDelay; + params->renameToFetchDelay = renameToFetchDelay; + params->iewToFetchDelay = iewToFetchDelay; + params->commitToFetchDelay = commitToFetchDelay; + params->fetchWidth = fetchWidth; + + params->renameToDecodeDelay = renameToDecodeDelay; + params->iewToDecodeDelay = iewToDecodeDelay; + params->commitToDecodeDelay = commitToDecodeDelay; + params->fetchToDecodeDelay = fetchToDecodeDelay; + params->decodeWidth = decodeWidth; + + params->iewToRenameDelay = iewToRenameDelay; + params->commitToRenameDelay = commitToRenameDelay; + params->decodeToRenameDelay = decodeToRenameDelay; + params->renameWidth = renameWidth; + + params->commitToIEWDelay = commitToIEWDelay; + params->renameToIEWDelay = renameToIEWDelay; + params->issueToExecuteDelay = issueToExecuteDelay; + params->dispatchWidth = dispatchWidth; + params->issueWidth = issueWidth; + params->wbWidth = wbWidth; + params->wbDepth = wbDepth; + params->fuPool = fuPool; + + params->iewToCommitDelay = iewToCommitDelay; + params->renameToROBDelay = renameToROBDelay; + params->commitWidth = commitWidth; + params->squashWidth = squashWidth; + params->trapLatency = trapLatency; + + params->backComSize = backComSize; + params->forwardComSize = forwardComSize; + + params->predType = predType; + params->localPredictorSize = localPredictorSize; + params->localCtrBits = localCtrBits; + params->localHistoryTableSize = localHistoryTableSize; + params->localHistoryBits = localHistoryBits; + params->globalPredictorSize = globalPredictorSize; + params->globalCtrBits = globalCtrBits; + params->globalHistoryBits = globalHistoryBits; + params->choicePredictorSize = choicePredictorSize; + params->choiceCtrBits = choiceCtrBits; + + params->BTBEntries = BTBEntries; + params->BTBTagSize = BTBTagSize; + + params->RASSize = RASSize; + + params->LQEntries = LQEntries; + params->SQEntries = SQEntries; + + params->SSITSize = SSITSize; + params->LFSTSize = LFSTSize; + + params->numPhysIntRegs = numPhysIntRegs; + params->numPhysFloatRegs = numPhysFloatRegs; + params->numIQEntries = numIQEntries; + params->numROBEntries = numROBEntries; + + params->smtNumFetchingThreads = smtNumFetchingThreads; + + // Default smtFetchPolicy to "RoundRobin", if necessary. + std::string round_robin_policy = "RoundRobin"; + std::string single_thread = "SingleThread"; + + if (actual_num_threads > 1 && single_thread.compare(smtFetchPolicy) == 0) + params->smtFetchPolicy = round_robin_policy; + else + params->smtFetchPolicy = smtFetchPolicy; + + params->smtIQPolicy = smtIQPolicy; + params->smtLSQPolicy = smtLSQPolicy; + params->smtLSQThreshold = smtLSQThreshold; + params->smtROBPolicy = smtROBPolicy; + params->smtROBThreshold = smtROBThreshold; + params->smtCommitPolicy = smtCommitPolicy; + + params->instShiftAmt = 2; + + params->deferRegistration = defer_registration; + + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + + cpu = new DerivO3CPU(params); + + return cpu; +} + +REGISTER_SIM_OBJECT("DerivO3CPU", DerivO3CPU) + diff --git a/src/cpu/o3/mips/cpu_impl.hh b/src/cpu/o3/mips/cpu_impl.hh new file mode 100644 index 000000000..e08741626 --- /dev/null +++ b/src/cpu/o3/mips/cpu_impl.hh @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "config/use_checker.hh" + +#include "arch/mips/faults.hh" +#include "base/cprintf.hh" +#include "base/statistics.hh" +#include "base/timebuf.hh" +#include "cpu/checker/thread_context.hh" +#include "sim/sim_events.hh" +#include "sim/stats.hh" + +#include "cpu/o3/mips/cpu.hh" +#include "cpu/o3/mips/params.hh" +#include "cpu/o3/mips/thread_context.hh" +#include "cpu/o3/comm.hh" +#include "cpu/o3/thread_state.hh" + +template <class Impl> +MipsO3CPU<Impl>::MipsO3CPU(Params *params) + : FullO3CPU<Impl>(params) +{ + DPRINTF(O3CPU, "Creating MipsO3CPU object.\n"); + + // Setup any thread state. + this->thread.resize(this->numThreads); + + for (int i = 0; i < this->numThreads; ++i) { + if (i < params->workload.size()) { + DPRINTF(O3CPU, "Workload[%i] process is %#x", + i, this->thread[i]); + this->thread[i] = new Thread(this, i, params->workload[i], + i, params->mem); + + this->thread[i]->setStatus(ThreadContext::Suspended); + + + /* Use this port to for syscall emulation writes to memory. */ + Port *mem_port; + TranslatingPort *trans_port; + trans_port = new TranslatingPort(csprintf("%s-%d-funcport", + name(), i), + params->workload[i]->pTable, + false); + mem_port = params->mem->getPort("functional"); + mem_port->setPeer(trans_port); + trans_port->setPeer(mem_port); + this->thread[i]->setMemPort(trans_port); + + //usedTids[i] = true; + //threadMap[i] = i; + } else { + //Allocate Empty thread so M5 can use later + //when scheduling threads to CPU + Process* dummy_proc = NULL; + + this->thread[i] = new Thread(this, i, dummy_proc, i, params->mem); + //usedTids[i] = false; + } + + ThreadContext *tc; + + // Setup the TC that will serve as the interface to the threads/CPU. + MipsTC<Impl> *mips_tc = + new MipsTC<Impl>; + + tc = mips_tc; + + // If we're using a checker, then the TC should be the + // CheckerThreadContext. +#if USE_CHECKER + if (params->checker) { + tc = new CheckerThreadContext<MipsTC<Impl> >( + mips_tc, this->checker); + } +#endif + + mips_tc->cpu = this; + mips_tc->thread = this->thread[i]; + + // Give the thread the TC. + this->thread[i]->tc = tc; + + // Add the TC to the CPU's list of TC's. + this->threadContexts.push_back(tc); + } + + for (int i=0; i < this->numThreads; i++) { + this->thread[i]->setFuncExeInst(0); + } + + // Sets CPU pointers. These must be set at this level because the CPU + // pointers are defined to be the highest level of CPU class. + this->fetch.setCPU(this); + this->decode.setCPU(this); + this->rename.setCPU(this); + this->iew.setCPU(this); + this->commit.setCPU(this); + + this->rob.setCPU(this); + this->regFile.setCPU(this); + + lockAddr = 0; + lockFlag = false; +} + +template <class Impl> +void +MipsO3CPU<Impl>::regStats() +{ + // Register stats for everything that has stats. + this->fullCPURegStats(); + this->fetch.regStats(); + this->decode.regStats(); + this->rename.regStats(); + this->iew.regStats(); + this->commit.regStats(); +} + + +template <class Impl> +MiscReg +MipsO3CPU<Impl>::readMiscReg(int misc_reg, unsigned tid) +{ + return this->regFile.readMiscReg(misc_reg, tid); +} + +template <class Impl> +MiscReg +MipsO3CPU<Impl>::readMiscRegWithEffect(int misc_reg, Fault &fault, + unsigned tid) +{ + return this->regFile.readMiscRegWithEffect(misc_reg, fault, tid); +} + +template <class Impl> +Fault +MipsO3CPU<Impl>::setMiscReg(int misc_reg, const MiscReg &val, unsigned tid) +{ + return this->regFile.setMiscReg(misc_reg, val, tid); +} + +template <class Impl> +Fault +MipsO3CPU<Impl>::setMiscRegWithEffect(int misc_reg, const MiscReg &val, + unsigned tid) +{ + return this->regFile.setMiscRegWithEffect(misc_reg, val, tid); +} + +template <class Impl> +void +MipsO3CPU<Impl>::squashFromTC(unsigned tid) +{ + this->thread[tid]->inSyscall = true; + this->commit.generateTCEvent(tid); +} + +template <class Impl> +void +MipsO3CPU<Impl>::trap(Fault fault, unsigned tid) +{ + // Pass the thread's TC into the invoke method. + fault->invoke(this->threadContexts[tid]); +} + +#if !FULL_SYSTEM + +template <class Impl> +void +MipsO3CPU<Impl>::syscall(int64_t callnum, int tid) +{ + DPRINTF(O3CPU, "[tid:%i] Executing syscall().\n\n", tid); + + DPRINTF(Activity,"Activity: syscall() called.\n"); + + // Temporarily increase this by one to account for the syscall + // instruction. + ++(this->thread[tid]->funcExeInst); + + // Execute the actual syscall. + this->thread[tid]->syscall(callnum); + + // Decrease funcExeInst by one as the normal commit will handle + // incrementing it. + --(this->thread[tid]->funcExeInst); + + DPRINTF(O3CPU, "[tid:%i] Register 2 is %i ", tid, this->readIntReg(2)); +} + +template <class Impl> +TheISA::IntReg +MipsO3CPU<Impl>::getSyscallArg(int i, int tid) +{ + return this->readArchIntReg(MipsISA::ArgumentReg0 + i, tid); +} + +template <class Impl> +void +MipsO3CPU<Impl>::setSyscallArg(int i, IntReg val, int tid) +{ + this->setArchIntReg(MipsISA::ArgumentReg0 + i, val, tid); +} + +template <class Impl> +void +MipsO3CPU<Impl>::setSyscallReturn(SyscallReturn return_value, int tid) +{ + // check for error condition. + if (return_value.successful()) { + // no error + this->setArchIntReg(TheISA::SyscallSuccessReg, 0, tid); + this->setArchIntReg(TheISA::ReturnValueReg, return_value.value(), tid); + } else { + // got an error, return details + this->setArchIntReg(TheISA::SyscallSuccessReg, + (TheISA::IntReg) -1, tid); + this->setArchIntReg(TheISA::ReturnValueReg, -return_value.value(), tid); + } +} +#endif diff --git a/src/cpu/o3/mips/dyn_inst.cc b/src/cpu/o3/mips/dyn_inst.cc new file mode 100755 index 000000000..216aa7d2c --- /dev/null +++ b/src/cpu/o3/mips/dyn_inst.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "cpu/o3/mips/dyn_inst_impl.hh" +#include "cpu/o3/mips/impl.hh" + +// Force instantiation of MipsDynInst for all the implementations that +// are needed. +template class MipsDynInst<MipsSimpleImpl>; diff --git a/src/cpu/o3/mips/dyn_inst.hh b/src/cpu/o3/mips/dyn_inst.hh new file mode 100755 index 000000000..06bdfcec4 --- /dev/null +++ b/src/cpu/o3/mips/dyn_inst.hh @@ -0,0 +1,282 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#ifndef __CPU_O3_MIPS_DYN_INST_HH__ +#define __CPU_O3_MIPS_DYN_INST_HH__ + +#include "arch/isa_traits.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/mips/cpu.hh" +#include "cpu/o3/mips/impl.hh" + +class Packet; + +/** + * Mostly implementation & ISA specific MipsDynInst. As with most + * other classes in the new CPU model, it is templated on the Impl to + * allow for passing in of all types, such as the CPU type and the ISA + * type. The MipsDynInst serves as the primary interface to the CPU + * for instructions that are executing. + */ +template <class Impl> +class MipsDynInst : public BaseDynInst<Impl> +{ + public: + /** Typedef for the CPU. */ + typedef typename Impl::O3CPU O3CPU; + + /** Binary machine instruction type. */ + typedef TheISA::MachInst MachInst; + /** Extended machine instruction type. */ + typedef TheISA::ExtMachInst ExtMachInst; + /** Logical register index type. */ + typedef TheISA::RegIndex RegIndex; + /** Integer register index type. */ + typedef TheISA::IntReg IntReg; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + /** Misc register index type. */ + typedef TheISA::MiscReg MiscReg; + + enum { + MaxInstSrcRegs = TheISA::MaxInstSrcRegs, //< Max source regs + MaxInstDestRegs = TheISA::MaxInstDestRegs, //< Max dest regs + }; + + public: + /** BaseDynInst constructor given a binary instruction. */ + MipsDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, InstSeqNum seq_num, + O3CPU *cpu); + + /** BaseDynInst constructor given a static inst pointer. */ + MipsDynInst(StaticInstPtr &_staticInst); + + /** Executes the instruction.*/ + Fault execute(); + + /** Initiates the access. Only valid for memory operations. */ + Fault initiateAcc(); + + /** Completes the access. Only valid for memory operations. */ + Fault completeAcc(Packet *pkt); + + private: + /** Initializes variables. */ + void initVars(); + + public: + /** Reads a miscellaneous register. */ + MiscReg readMiscReg(int misc_reg) + { + return this->cpu->readMiscReg(misc_reg, this->threadNumber); + } + + /** Reads a misc. register, including any side-effects the read + * might have as defined by the architecture. + */ + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { + return this->cpu->readMiscRegWithEffect(misc_reg, fault, + this->threadNumber); + } + + /** Sets a misc. register. */ + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + this->instResult.integer = val; + return this->cpu->setMiscReg(misc_reg, val, this->threadNumber); + } + + /** Sets a misc. register, including any side-effects the write + * might have as defined by the architecture. + */ + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + return this->cpu->setMiscRegWithEffect(misc_reg, val, + this->threadNumber); + } + + /** Calls a syscall. */ + void syscall(int64_t callnum); + + private: + /** Physical register index of the destination registers of this + * instruction. + */ + PhysRegIndex _destRegIdx[MaxInstDestRegs]; + + /** Physical register index of the source registers of this + * instruction. + */ + PhysRegIndex _srcRegIdx[MaxInstSrcRegs]; + + /** Physical register index of the previous producers of the + * architected destinations. + */ + PhysRegIndex _prevDestRegIdx[MaxInstDestRegs]; + + public: + + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to redice overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + uint64_t readIntReg(const StaticInst *si, int idx) + { + return this->cpu->readIntReg(_srcRegIdx[idx]); + } + + FloatReg readFloatReg(const StaticInst *si, int idx, int width) + { + return this->cpu->readFloatReg(_srcRegIdx[idx], width); + } + + FloatReg readFloatReg(const StaticInst *si, int idx) + { + return this->cpu->readFloatReg(_srcRegIdx[idx]); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width) + { + return this->cpu->readFloatRegBits(_srcRegIdx[idx], width); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx) + { + return this->cpu->readFloatRegBits(_srcRegIdx[idx]); + } + + /** @todo: Make results into arrays so they can handle multiple dest + * registers. + */ + void setIntReg(const StaticInst *si, int idx, uint64_t val) + { + this->cpu->setIntReg(_destRegIdx[idx], val); + BaseDynInst<Impl>::setIntReg(si, idx, val); + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) + { + this->cpu->setFloatReg(_destRegIdx[idx], val, width); + BaseDynInst<Impl>::setFloatReg(si, idx, val, width); + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val) + { + this->cpu->setFloatReg(_destRegIdx[idx], val); + BaseDynInst<Impl>::setFloatReg(si, idx, val); + } + + void setFloatRegBits(const StaticInst *si, int idx, + FloatRegBits val, int width) + { + this->cpu->setFloatRegBits(_destRegIdx[idx], val, width); + BaseDynInst<Impl>::setFloatRegBits(si, idx, val); + } + + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val) + { + this->cpu->setFloatRegBits(_destRegIdx[idx], val); + BaseDynInst<Impl>::setFloatRegBits(si, idx, val); + } + + /** Returns the physical register index of the i'th destination + * register. + */ + PhysRegIndex renamedDestRegIdx(int idx) const + { + return _destRegIdx[idx]; + } + + /** Returns the physical register index of the i'th source register. */ + PhysRegIndex renamedSrcRegIdx(int idx) const + { + return _srcRegIdx[idx]; + } + + /** Returns the physical register index of the previous physical register + * that remapped to the same logical register index. + */ + PhysRegIndex prevDestRegIdx(int idx) const + { + return _prevDestRegIdx[idx]; + } + + /** Renames a destination register to a physical register. Also records + * the previous physical register that the logical register mapped to. + */ + void renameDestReg(int idx, + PhysRegIndex renamed_dest, + PhysRegIndex previous_rename) + { + _destRegIdx[idx] = renamed_dest; + _prevDestRegIdx[idx] = previous_rename; + } + + /** Renames a source logical register to the physical register which + * has/will produce that logical register's result. + * @todo: add in whether or not the source register is ready. + */ + void renameSrcReg(int idx, PhysRegIndex renamed_src) + { + _srcRegIdx[idx] = renamed_src; + } + + public: + /** Calculates EA part of a memory instruction. Currently unused, + * though it may be useful in the future if we want to split + * memory operations into EA calculation and memory access parts. + */ + Fault calcEA() + { + return this->staticInst->eaCompInst()->execute(this, this->traceData); + } + + /** Does the memory access part of a memory instruction. Currently unused, + * though it may be useful in the future if we want to split + * memory operations into EA calculation and memory access parts. + */ + Fault memAccess() + { + return this->staticInst->memAccInst()->execute(this, this->traceData); + } +}; + +#endif // __CPU_O3_MIPS_DYN_INST_HH__ + diff --git a/src/cpu/o3/mips/dyn_inst_impl.hh b/src/cpu/o3/mips/dyn_inst_impl.hh new file mode 100755 index 000000000..57dec1ccf --- /dev/null +++ b/src/cpu/o3/mips/dyn_inst_impl.hh @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/mips/dyn_inst.hh" + +template <class Impl> +MipsDynInst<Impl>::MipsDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, + InstSeqNum seq_num, O3CPU *cpu) + : BaseDynInst<Impl>(inst, PC, Pred_PC, seq_num, cpu) +{ + initVars(); +} + +template <class Impl> +MipsDynInst<Impl>::MipsDynInst(StaticInstPtr &_staticInst) + : BaseDynInst<Impl>(_staticInst) +{ + initVars(); +} + +template <class Impl> +void +MipsDynInst<Impl>::initVars() +{ + // Make sure to have the renamed register entries set to the same + // as the normal register entries. It will allow the IQ to work + // without any modifications. + for (int i = 0; i < this->staticInst->numDestRegs(); i++) { + _destRegIdx[i] = this->staticInst->destRegIdx(i); + } + + for (int i = 0; i < this->staticInst->numSrcRegs(); i++) { + _srcRegIdx[i] = this->staticInst->srcRegIdx(i); + this->_readySrcRegIdx[i] = 0; + } +} + +template <class Impl> +Fault +MipsDynInst<Impl>::execute() +{ + // @todo: Pretty convoluted way to avoid squashing from happening + // when using the TC during an instruction's execution + // (specifically for instructions that have side-effects that use + // the TC). Fix this. + bool in_syscall = this->thread->inSyscall; + this->thread->inSyscall = true; + + this->fault = this->staticInst->execute(this, this->traceData); + + this->thread->inSyscall = in_syscall; + + return this->fault; +} + +template <class Impl> +Fault +MipsDynInst<Impl>::initiateAcc() +{ + // @todo: Pretty convoluted way to avoid squashing from happening + // when using the TC during an instruction's execution + // (specifically for instructions that have side-effects that use + // the TC). Fix this. + bool in_syscall = this->thread->inSyscall; + this->thread->inSyscall = true; + + this->fault = this->staticInst->initiateAcc(this, this->traceData); + + this->thread->inSyscall = in_syscall; + + return this->fault; +} + +template <class Impl> +Fault +MipsDynInst<Impl>::completeAcc(Packet *pkt) +{ + this->fault = this->staticInst->completeAcc(pkt, this, this->traceData); + + return this->fault; +} + +template <class Impl> +void +MipsDynInst<Impl>::syscall(int64_t callnum) +{ + this->cpu->syscall(callnum, this->threadNumber); +} + diff --git a/src/cpu/o3/mips/impl.hh b/src/cpu/o3/mips/impl.hh new file mode 100644 index 000000000..ac7181a19 --- /dev/null +++ b/src/cpu/o3/mips/impl.hh @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#ifndef __CPU_O3_MIPS_IMPL_HH__ +#define __CPU_O3_MIPS_IMPL_HH__ + +#include "arch/mips/isa_traits.hh" + +#include "cpu/o3/mips/params.hh" +#include "cpu/o3/cpu_policy.hh" + + +// Forward declarations. +template <class Impl> +class MipsDynInst; + +template <class Impl> +class MipsO3CPU; + +/** Implementation specific struct that defines several key types to the + * CPU, the stages within the CPU, the time buffers, and the DynInst. + * The struct defines the ISA, the CPU policy, the specific DynInst, the + * specific O3CPU, and all of the structs from the time buffers to do + * communication. + * This is one of the key things that must be defined for each hardware + * specific CPU implementation. + */ +struct MipsSimpleImpl +{ + /** The type of MachInst. */ + typedef TheISA::MachInst MachInst; + + /** The CPU policy to be used, which defines all of the CPU stages. */ + typedef SimpleCPUPolicy<MipsSimpleImpl> CPUPol; + + /** The DynInst type to be used. */ + typedef MipsDynInst<MipsSimpleImpl> DynInst; + + /** The refcounted DynInst pointer to be used. In most cases this is + * what should be used, and not DynInst *. + */ + typedef RefCountingPtr<DynInst> DynInstPtr; + + /** The O3CPU type to be used. */ + typedef MipsO3CPU<MipsSimpleImpl> O3CPU; + + /** Same typedef, but for CPUType. BaseDynInst may not always use + * an O3 CPU, so it's clearer to call it CPUType instead in that + * case. + */ + typedef O3CPU CPUType; + + /** The Params to be passed to each stage. */ + typedef MipsSimpleParams Params; + + enum { + MaxWidth = 8, + MaxThreads = 4 + }; +}; + +/** The O3Impl to be used. */ +typedef MipsSimpleImpl O3CPUImpl; + +#endif // __CPU_O3_MIPS_IMPL_HH__ diff --git a/src/cpu/o3/mips/params.hh b/src/cpu/o3/mips/params.hh new file mode 100644 index 000000000..d1ac62e21 --- /dev/null +++ b/src/cpu/o3/mips/params.hh @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#ifndef __CPU_O3_MIPS_PARAMS_HH__ +#define __CPU_O3_MIPS_PARAMS_HH__ + +#include "cpu/o3/cpu.hh" +#include "cpu/o3/params.hh" + +//Forward declarations +//class MipsDTB; +//class MipsITB; +class MemObject; +class Process; +class System; + +/** + * This file defines the parameters that will be used for the MipsO3CPU. + * This must be defined externally so that the Impl can have a params class + * defined that it can pass to all of the individual stages. + */ + +class MipsSimpleParams : public O3Params +{ + public: + MipsSimpleParams() {} + +#if FULL_SYSTEM + //Full System Paramater Objects place here + MipsITB *itb; + MipsDTB *dtb; +#endif +}; + +#endif // __CPU_O3_MIPS_PARAMS_HH__ diff --git a/src/cpu/o3/mips/thread_context.cc b/src/cpu/o3/mips/thread_context.cc new file mode 100755 index 000000000..0061a2a63 --- /dev/null +++ b/src/cpu/o3/mips/thread_context.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "cpu/o3/thread_context.hh" +#include "cpu/o3/thread_context_impl.hh" + +template class O3ThreadContext<MipsSimpleImpl>; + diff --git a/src/cpu/o3/mips/thread_context.hh b/src/cpu/o3/mips/thread_context.hh new file mode 100644 index 000000000..26b1e2e7f --- /dev/null +++ b/src/cpu/o3/mips/thread_context.hh @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "arch/mips/types.hh" +#include "cpu/o3/thread_context.hh" + +template <class Impl> +class MipsTC : public O3ThreadContext<Impl> +{ + public: + virtual uint64_t readNextNPC() + { + return this->cpu->readNextNPC(this->thread->readTid()); + } + + virtual void setNextNPC(uint64_t val) + { + this->cpu->setNextNPC(val, this->thread->readTid()); + } + + virtual void changeRegFileContext(TheISA::RegContextParam param, + TheISA::RegContextVal val) + { panic("Not supported on Mips!"); } + + /** This function exits the thread context in the CPU and returns + * 1 if the CPU has no more active threads (meaning it's OK to exit); + * Used in syscall-emulation mode when a thread executes the 'exit' + * syscall. + */ + virtual int exit() + { + this->deallocate(); + + // If there are still threads executing in the system + if (this->cpu->numActiveThreads()) + return 0; // don't exit simulation + else + return 1; // exit simulation + } +}; diff --git a/src/cpu/o3/params.hh b/src/cpu/o3/params.hh new file mode 100755 index 000000000..1c234bcd7 --- /dev/null +++ b/src/cpu/o3/params.hh @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_PARAMS_HH__ +#define __CPU_O3_PARAMS_HH__ + +#include "cpu/o3/cpu.hh" + +//Forward declarations +class FUPool; + +/** + * This file defines the parameters that will be used for the O3CPU. + * This must be defined externally so that the Impl can have a params class + * defined that it can pass to all of the individual stages. + */ +class O3Params : public BaseO3CPU::Params +{ + public: + unsigned activity; + + // + // Pointers to key objects + // +#if !FULL_SYSTEM + std::vector<Process *> workload; + Process *process; +#endif // FULL_SYSTEM + + MemObject *mem; + + BaseCPU *checker; + + // + // Caches + // + // MemInterface *icacheInterface; + // MemInterface *dcacheInterface; + + unsigned cachePorts; + + // + // Fetch + // + unsigned decodeToFetchDelay; + unsigned renameToFetchDelay; + unsigned iewToFetchDelay; + unsigned commitToFetchDelay; + unsigned fetchWidth; + + // + // Decode + // + unsigned renameToDecodeDelay; + unsigned iewToDecodeDelay; + unsigned commitToDecodeDelay; + unsigned fetchToDecodeDelay; + unsigned decodeWidth; + + // + // Rename + // + unsigned iewToRenameDelay; + unsigned commitToRenameDelay; + unsigned decodeToRenameDelay; + unsigned renameWidth; + + // + // IEW + // + unsigned commitToIEWDelay; + unsigned renameToIEWDelay; + unsigned issueToExecuteDelay; + unsigned dispatchWidth; + unsigned issueWidth; + unsigned wbWidth; + unsigned wbDepth; + FUPool *fuPool; + + // + // Commit + // + unsigned iewToCommitDelay; + unsigned renameToROBDelay; + unsigned commitWidth; + unsigned squashWidth; + Tick trapLatency; + Tick fetchTrapLatency; + + // + // Timebuffer sizes + // + unsigned backComSize; + unsigned forwardComSize; + + // + // Branch predictor (BP, BTB, RAS) + // + std::string predType; + unsigned localPredictorSize; + unsigned localCtrBits; + unsigned localHistoryTableSize; + unsigned localHistoryBits; + unsigned globalPredictorSize; + unsigned globalCtrBits; + unsigned globalHistoryBits; + unsigned choicePredictorSize; + unsigned choiceCtrBits; + + unsigned BTBEntries; + unsigned BTBTagSize; + + unsigned RASSize; + + // + // Load store queue + // + unsigned LQEntries; + unsigned SQEntries; + + // + // Memory dependence + // + unsigned SSITSize; + unsigned LFSTSize; + + // + // Miscellaneous + // + unsigned numPhysIntRegs; + unsigned numPhysFloatRegs; + unsigned numIQEntries; + unsigned numROBEntries; + + //SMT Parameters + unsigned smtNumFetchingThreads; + + std::string smtFetchPolicy; + + std::string smtIQPolicy; + unsigned smtIQThreshold; + + std::string smtLSQPolicy; + unsigned smtLSQThreshold; + + std::string smtCommitPolicy; + + std::string smtROBPolicy; + unsigned smtROBThreshold; + + // Probably can get this from somewhere. + unsigned instShiftAmt; +}; + +#endif // __CPU_O3_ALPHA_PARAMS_HH__ diff --git a/src/cpu/o3/ras.cc b/src/cpu/o3/ras.cc new file mode 100644 index 000000000..f9939259a --- /dev/null +++ b/src/cpu/o3/ras.cc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/ras.hh" + +void +ReturnAddrStack::init(unsigned _numEntries) +{ + numEntries = _numEntries; + usedEntries = 0; + tos = 0; + + addrStack.resize(numEntries); + + for (int i = 0; i < numEntries; ++i) + addrStack[i] = 0; +} + +void +ReturnAddrStack::reset() +{ + usedEntries = 0; + tos = 0; + for (int i = 0; i < numEntries; ++i) + addrStack[i] = 0; +} + +void +ReturnAddrStack::push(const Addr &return_addr) +{ + incrTos(); + + addrStack[tos] = return_addr; + + if (usedEntries != numEntries) { + ++usedEntries; + } +} + +void +ReturnAddrStack::pop() +{ + if (usedEntries > 0) { + --usedEntries; + } + + decrTos(); +} + +void +ReturnAddrStack::restore(unsigned top_entry_idx, + const Addr &restored_target) +{ + tos = top_entry_idx; + + addrStack[tos] = restored_target; +} diff --git a/src/cpu/o3/ras.hh b/src/cpu/o3/ras.hh new file mode 100644 index 000000000..97846ed16 --- /dev/null +++ b/src/cpu/o3/ras.hh @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_RAS_HH__ +#define __CPU_O3_RAS_HH__ + +#include "sim/host.hh" +#include <vector> + +/** Return address stack class, implements a simple RAS. */ +class ReturnAddrStack +{ + public: + /** Creates a return address stack, but init() must be called prior to + * use. + */ + ReturnAddrStack() {} + + /** Initializes RAS with a specified number of entries. + * @param numEntries Number of entries in the RAS. + */ + void init(unsigned numEntries); + + void reset(); + + /** Returns the top address on the RAS. */ + Addr top() + { return addrStack[tos]; } + + /** Returns the index of the top of the RAS. */ + unsigned topIdx() + { return tos; } + + /** Pushes an address onto the RAS. */ + void push(const Addr &return_addr); + + /** Pops the top address from the RAS. */ + void pop(); + + /** Changes index to the top of the RAS, and replaces the top address with + * a new target. + * @param top_entry_idx The index of the RAS that will now be the top. + * @param restored_target The new target address of the new top of the RAS. + */ + void restore(unsigned top_entry_idx, const Addr &restored_target); + + private: + /** Increments the top of stack index. */ + inline void incrTos() + { if (++tos == numEntries) tos = 0; } + + /** Decrements the top of stack index. */ + inline void decrTos() + { tos = (tos == 0 ? numEntries - 1 : tos - 1); } + + /** The RAS itself. */ + std::vector<Addr> addrStack; + + /** The number of entries in the RAS. */ + unsigned numEntries; + + /** The number of used entries in the RAS. */ + unsigned usedEntries; + + /** The top of stack index. */ + unsigned tos; +}; + +#endif // __CPU_O3_RAS_HH__ diff --git a/src/cpu/o3/regfile.hh b/src/cpu/o3/regfile.hh new file mode 100644 index 000000000..512cf0721 --- /dev/null +++ b/src/cpu/o3/regfile.hh @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Gabe Black + */ + +#ifndef __CPU_O3_REGFILE_HH__ +#define __CPU_O3_REGFILE_HH__ + +#include "arch/isa_traits.hh" +#include "arch/types.hh" +#include "base/trace.hh" +#include "config/full_system.hh" +#include "cpu/o3/comm.hh" +#include "sim/faults.hh" + +#if FULL_SYSTEM +#include "kern/kernel_stats.hh" + +#endif + +#include <vector> + +/** + * Simple physical register file class. + * Right now this is specific to Alpha until we decide if/how to make things + * generic enough to support other ISAs. + */ +template <class Impl> +class PhysRegFile +{ + protected: + typedef TheISA::IntReg IntReg; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscRegFile MiscRegFile; + typedef TheISA::MiscReg MiscReg; + + typedef union { + FloatReg d; + FloatRegBits q; + } PhysFloatReg; + + // Note that most of the definitions of the IntReg, FloatReg, etc. exist + // within the Impl/ISA class and not within this PhysRegFile class. + + // Will make these registers public for now, but they probably should + // be private eventually with some accessor functions. + public: + typedef typename Impl::O3CPU O3CPU; + + /** + * Constructs a physical register file with the specified amount of + * integer and floating point registers. + */ + PhysRegFile(unsigned _numPhysicalIntRegs, + unsigned _numPhysicalFloatRegs); + + //Everything below should be pretty well identical to the normal + //register file that exists within AlphaISA class. + //The duplication is unfortunate but it's better than having + //different ways to access certain registers. + + /** Reads an integer register. */ + uint64_t readIntReg(PhysRegIndex reg_idx) + { + assert(reg_idx < numPhysicalIntRegs); + + DPRINTF(IEW, "RegFile: Access to int register %i, has data " + "%#x\n", int(reg_idx), intRegFile[reg_idx]); + return intRegFile[reg_idx]; + } + + FloatReg readFloatReg(PhysRegIndex reg_idx, int width) + { + // Remove the base Float reg dependency. + reg_idx = reg_idx - numPhysicalIntRegs; + + assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); + + FloatReg floatReg = floatRegFile[reg_idx].d; + + DPRINTF(IEW, "RegFile: Access to %d byte float register %i, has " + "data %#x\n", int(reg_idx), floatRegFile[reg_idx].q); + + return floatReg; + } + + /** Reads a floating point register (double precision). */ + FloatReg readFloatReg(PhysRegIndex reg_idx) + { + // Remove the base Float reg dependency. + reg_idx = reg_idx - numPhysicalIntRegs; + + assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); + + FloatReg floatReg = floatRegFile[reg_idx].d; + + DPRINTF(IEW, "RegFile: Access to float register %i, has " + "data %#x\n", int(reg_idx), floatRegFile[reg_idx].q); + + return floatReg; + } + + /** Reads a floating point register as an integer. */ + FloatRegBits readFloatRegBits(PhysRegIndex reg_idx, int width) + { + // Remove the base Float reg dependency. + reg_idx = reg_idx - numPhysicalIntRegs; + + assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); + + FloatRegBits floatRegBits = floatRegFile[reg_idx].q; + + DPRINTF(IEW, "RegFile: Access to float register %i as int, " + "has data %#x\n", int(reg_idx), (uint64_t)floatRegBits); + + return floatRegBits; + } + + FloatRegBits readFloatRegBits(PhysRegIndex reg_idx) + { + // Remove the base Float reg dependency. + reg_idx = reg_idx - numPhysicalIntRegs; + + assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); + + FloatRegBits floatRegBits = floatRegFile[reg_idx].q; + + DPRINTF(IEW, "RegFile: Access to float register %i as int, " + "has data %#x\n", int(reg_idx), (uint64_t)floatRegBits); + + return floatRegBits; + } + + /** Sets an integer register to the given value. */ + void setIntReg(PhysRegIndex reg_idx, uint64_t val) + { + assert(reg_idx < numPhysicalIntRegs); + + DPRINTF(IEW, "RegFile: Setting int register %i to %#x\n", + int(reg_idx), val); + + if (reg_idx != TheISA::ZeroReg) + intRegFile[reg_idx] = val; + } + + /** Sets a single precision floating point register to the given value. */ + void setFloatReg(PhysRegIndex reg_idx, FloatReg val, int width) + { + // Remove the base Float reg dependency. + reg_idx = reg_idx - numPhysicalIntRegs; + + assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); + + DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n", + int(reg_idx), (uint64_t)val); + + if (reg_idx != TheISA::ZeroReg) + floatRegFile[reg_idx].d = val; + } + + /** Sets a double precision floating point register to the given value. */ + void setFloatReg(PhysRegIndex reg_idx, FloatReg val) + { + // Remove the base Float reg dependency. + reg_idx = reg_idx - numPhysicalIntRegs; + + assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); + + DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n", + int(reg_idx), (uint64_t)val); + + if (reg_idx != TheISA::ZeroReg) + floatRegFile[reg_idx].d = val; + } + + /** Sets a floating point register to the given integer value. */ + void setFloatRegBits(PhysRegIndex reg_idx, FloatRegBits val, int width) + { + // Remove the base Float reg dependency. + reg_idx = reg_idx - numPhysicalIntRegs; + + assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); + + DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n", + int(reg_idx), (uint64_t)val); + + floatRegFile[reg_idx].q = val; + } + + void setFloatRegBits(PhysRegIndex reg_idx, FloatRegBits val) + { + // Remove the base Float reg dependency. + reg_idx = reg_idx - numPhysicalIntRegs; + + assert(reg_idx < numPhysicalFloatRegs + numPhysicalIntRegs); + + DPRINTF(IEW, "RegFile: Setting float register %i to %#x\n", + int(reg_idx), (uint64_t)val); + + floatRegFile[reg_idx].q = val; + } + + MiscReg readMiscReg(int misc_reg, unsigned thread_id) + { + return miscRegs[thread_id].readReg(misc_reg); + } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault, + unsigned thread_id) + { + return miscRegs[thread_id].readRegWithEffect(misc_reg, fault, + cpu->tcBase(thread_id)); + } + + Fault setMiscReg(int misc_reg, const MiscReg &val, unsigned thread_id) + { + return miscRegs[thread_id].setReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val, + unsigned thread_id) + { + return miscRegs[thread_id].setRegWithEffect(misc_reg, val, + cpu->tcBase(thread_id)); + } + +#if FULL_SYSTEM + int readIntrFlag() { return intrflag; } + /** Sets an interrupt flag. */ + void setIntrFlag(int val) { intrflag = val; } +#endif + + public: + /** (signed) integer register file. */ + IntReg *intRegFile; + + /** Floating point register file. */ + PhysFloatReg *floatRegFile; + + /** Miscellaneous register file. */ + MiscRegFile miscRegs[Impl::MaxThreads]; + +#if FULL_SYSTEM + private: + int intrflag; // interrupt flag +#endif + + private: + /** CPU pointer. */ + O3CPU *cpu; + + public: + /** Sets the CPU pointer. */ + void setCPU(O3CPU *cpu_ptr) { cpu = cpu_ptr; } + + /** Number of physical integer registers. */ + unsigned numPhysicalIntRegs; + /** Number of physical floating point registers. */ + unsigned numPhysicalFloatRegs; +}; + +template <class Impl> +PhysRegFile<Impl>::PhysRegFile(unsigned _numPhysicalIntRegs, + unsigned _numPhysicalFloatRegs) + : numPhysicalIntRegs(_numPhysicalIntRegs), + numPhysicalFloatRegs(_numPhysicalFloatRegs) +{ + intRegFile = new IntReg[numPhysicalIntRegs]; + floatRegFile = new PhysFloatReg[numPhysicalFloatRegs]; + + for (int i = 0; i < Impl::MaxThreads; ++i) { + miscRegs[i].clear(); + } + + memset(intRegFile, 0, sizeof(IntReg) * numPhysicalIntRegs); + memset(floatRegFile, 0, sizeof(PhysFloatReg) * numPhysicalFloatRegs); +} + +#endif diff --git a/src/cpu/o3/rename.cc b/src/cpu/o3/rename.cc new file mode 100644 index 000000000..443ada0cb --- /dev/null +++ b/src/cpu/o3/rename.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/rename_impl.hh" + +template class DefaultRename<O3CPUImpl>; diff --git a/src/cpu/o3/rename.hh b/src/cpu/o3/rename.hh new file mode 100644 index 000000000..177b9cb87 --- /dev/null +++ b/src/cpu/o3/rename.hh @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_RENAME_HH__ +#define __CPU_O3_RENAME_HH__ + +#include <list> + +#include "base/statistics.hh" +#include "base/timebuf.hh" + +/** + * DefaultRename handles both single threaded and SMT rename. Its + * width is specified by the parameters; each cycle it tries to rename + * that many instructions. It holds onto the rename history of all + * instructions with destination registers, storing the + * arch. register, the new physical register, and the old physical + * register, to allow for undoing of mappings if squashing happens, or + * freeing up registers upon commit. Rename handles blocking if the + * ROB, IQ, or LSQ is going to be full. Rename also handles barriers, + * and does so by stalling on the instruction until the ROB is empty + * and there are no instructions in flight to the ROB. + */ +template<class Impl> +class DefaultRename +{ + public: + // Typedefs from the Impl. + typedef typename Impl::CPUPol CPUPol; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::O3CPU O3CPU; + typedef typename Impl::Params Params; + + // Typedefs from the CPUPol + typedef typename CPUPol::DecodeStruct DecodeStruct; + typedef typename CPUPol::RenameStruct RenameStruct; + typedef typename CPUPol::TimeStruct TimeStruct; + typedef typename CPUPol::FreeList FreeList; + typedef typename CPUPol::RenameMap RenameMap; + // These are used only for initialization. + typedef typename CPUPol::IEW IEW; + typedef typename CPUPol::Commit Commit; + + // Typedefs from the ISA. + typedef TheISA::RegIndex RegIndex; + + // A list is used to queue the instructions. Barrier insts must + // be added to the front of the list, which is the only reason for + // using a list instead of a queue. (Most other stages use a + // queue) + typedef std::list<DynInstPtr> InstQueue; + typedef typename std::list<DynInstPtr>::iterator ListIt; + + public: + /** Overall rename status. Used to determine if the CPU can + * deschedule itself due to a lack of activity. + */ + enum RenameStatus { + Active, + Inactive + }; + + /** Individual thread status. */ + enum ThreadStatus { + Running, + Idle, + StartSquash, + Squashing, + Blocked, + Unblocking, + SerializeStall + }; + + private: + /** Rename status. */ + RenameStatus _status; + + /** Per-thread status. */ + ThreadStatus renameStatus[Impl::MaxThreads]; + + public: + /** DefaultRename constructor. */ + DefaultRename(Params *params); + + /** Returns the name of rename. */ + std::string name() const; + + /** Registers statistics. */ + void regStats(); + + /** Sets CPU pointer. */ + void setCPU(O3CPU *cpu_ptr); + + /** Sets the main backwards communication time buffer pointer. */ + void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + + /** Sets pointer to time buffer used to communicate to the next stage. */ + void setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr); + + /** Sets pointer to time buffer coming from decode. */ + void setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr); + + /** Sets pointer to IEW stage. Used only for initialization. */ + void setIEWStage(IEW *iew_stage) + { iew_ptr = iew_stage; } + + /** Sets pointer to commit stage. Used only for initialization. */ + void setCommitStage(Commit *commit_stage) + { commit_ptr = commit_stage; } + + private: + /** Pointer to IEW stage. Used only for initialization. */ + IEW *iew_ptr; + + /** Pointer to commit stage. Used only for initialization. */ + Commit *commit_ptr; + + public: + /** Initializes variables for the stage. */ + void initStage(); + + /** Sets pointer to list of active threads. */ + void setActiveThreads(std::list<unsigned> *at_ptr); + + /** Sets pointer to rename maps (per-thread structures). */ + void setRenameMap(RenameMap rm_ptr[Impl::MaxThreads]); + + /** Sets pointer to the free list. */ + void setFreeList(FreeList *fl_ptr); + + /** Sets pointer to the scoreboard. */ + void setScoreboard(Scoreboard *_scoreboard); + + /** Drains the rename stage. */ + bool drain(); + + /** Resumes execution after a drain. */ + void resume() { } + + /** Switches out the rename stage. */ + void switchOut(); + + /** Takes over from another CPU's thread. */ + void takeOverFrom(); + + /** Squashes all instructions in a thread. */ + void squash(const InstSeqNum &squash_seq_num, unsigned tid); + + /** Ticks rename, which processes all input signals and attempts to rename + * as many instructions as possible. + */ + void tick(); + + /** Debugging function used to dump history buffer of renamings. */ + void dumpHistory(); + + private: + /** Determines what to do based on rename's current status. + * @param status_change rename() sets this variable if there was a status + * change (ie switching from blocking to unblocking). + * @param tid Thread id to rename instructions from. + */ + void rename(bool &status_change, unsigned tid); + + /** Renames instructions for the given thread. Also handles serializing + * instructions. + */ + void renameInsts(unsigned tid); + + /** Inserts unused instructions from a given thread into the skid buffer, + * to be renamed once rename unblocks. + */ + void skidInsert(unsigned tid); + + /** Separates instructions from decode into individual lists of instructions + * sorted by thread. + */ + void sortInsts(); + + /** Returns if all of the skid buffers are empty. */ + bool skidsEmpty(); + + /** Updates overall rename status based on all of the threads' statuses. */ + void updateStatus(); + + /** Switches rename to blocking, and signals back that rename has become + * blocked. + * @return Returns true if there is a status change. + */ + bool block(unsigned tid); + + /** Switches rename to unblocking if the skid buffer is empty, and signals + * back that rename has unblocked. + * @return Returns true if there is a status change. + */ + bool unblock(unsigned tid); + + /** Executes actual squash, removing squashed instructions. */ + void doSquash(const InstSeqNum &squash_seq_num, unsigned tid); + + /** Removes a committed instruction's rename history. */ + void removeFromHistory(InstSeqNum inst_seq_num, unsigned tid); + + /** Renames the source registers of an instruction. */ + inline void renameSrcRegs(DynInstPtr &inst, unsigned tid); + + /** Renames the destination registers of an instruction. */ + inline void renameDestRegs(DynInstPtr &inst, unsigned tid); + + /** Calculates the number of free ROB entries for a specific thread. */ + inline int calcFreeROBEntries(unsigned tid); + + /** Calculates the number of free IQ entries for a specific thread. */ + inline int calcFreeIQEntries(unsigned tid); + + /** Calculates the number of free LSQ entries for a specific thread. */ + inline int calcFreeLSQEntries(unsigned tid); + + /** Returns the number of valid instructions coming from decode. */ + unsigned validInsts(); + + /** Reads signals telling rename to block/unblock. */ + void readStallSignals(unsigned tid); + + /** Checks if any stages are telling rename to block. */ + bool checkStall(unsigned tid); + + /** Gets the number of free entries for a specific thread. */ + void readFreeEntries(unsigned tid); + + /** Checks the signals and updates the status. */ + bool checkSignalsAndUpdate(unsigned tid); + + /** Either serializes on the next instruction available in the InstQueue, + * or records that it must serialize on the next instruction to enter + * rename. + * @param inst_list The list of younger, unprocessed instructions for the + * thread that has the serializeAfter instruction. + * @param tid The thread id. + */ + void serializeAfter(InstQueue &inst_list, unsigned tid); + + /** Holds the information for each destination register rename. It holds + * the instruction's sequence number, the arch register, the old physical + * register for that arch. register, and the new physical register. + */ + struct RenameHistory { + RenameHistory(InstSeqNum _instSeqNum, RegIndex _archReg, + PhysRegIndex _newPhysReg, PhysRegIndex _prevPhysReg) + : instSeqNum(_instSeqNum), archReg(_archReg), + newPhysReg(_newPhysReg), prevPhysReg(_prevPhysReg) + { + } + + /** The sequence number of the instruction that renamed. */ + InstSeqNum instSeqNum; + /** The architectural register index that was renamed. */ + RegIndex archReg; + /** The new physical register that the arch. register is renamed to. */ + PhysRegIndex newPhysReg; + /** The old physical register that the arch. register was renamed to. */ + PhysRegIndex prevPhysReg; + }; + + /** A per-thread list of all destination register renames, used to either + * undo rename mappings or free old physical registers. + */ + std::list<RenameHistory> historyBuffer[Impl::MaxThreads]; + + /** Pointer to CPU. */ + O3CPU *cpu; + + /** Pointer to main time buffer used for backwards communication. */ + TimeBuffer<TimeStruct> *timeBuffer; + + /** Wire to get IEW's output from backwards time buffer. */ + typename TimeBuffer<TimeStruct>::wire fromIEW; + + /** Wire to get commit's output from backwards time buffer. */ + typename TimeBuffer<TimeStruct>::wire fromCommit; + + /** Wire to write infromation heading to previous stages. */ + typename TimeBuffer<TimeStruct>::wire toDecode; + + /** Rename instruction queue. */ + TimeBuffer<RenameStruct> *renameQueue; + + /** Wire to write any information heading to IEW. */ + typename TimeBuffer<RenameStruct>::wire toIEW; + + /** Decode instruction queue interface. */ + TimeBuffer<DecodeStruct> *decodeQueue; + + /** Wire to get decode's output from decode queue. */ + typename TimeBuffer<DecodeStruct>::wire fromDecode; + + /** Queue of all instructions coming from decode this cycle. */ + InstQueue insts[Impl::MaxThreads]; + + /** Skid buffer between rename and decode. */ + InstQueue skidBuffer[Impl::MaxThreads]; + + /** Rename map interface. */ + RenameMap *renameMap[Impl::MaxThreads]; + + /** Free list interface. */ + FreeList *freeList; + + /** Pointer to the list of active threads. */ + std::list<unsigned> *activeThreads; + + /** Pointer to the scoreboard. */ + Scoreboard *scoreboard; + + /** Count of instructions in progress that have been sent off to the IQ + * and ROB, but are not yet included in their occupancy counts. + */ + int instsInProgress[Impl::MaxThreads]; + + /** Variable that tracks if decode has written to the time buffer this + * cycle. Used to tell CPU if there is activity this cycle. + */ + bool wroteToTimeBuffer; + + /** Structures whose free entries impact the amount of instructions that + * can be renamed. + */ + struct FreeEntries { + unsigned iqEntries; + unsigned lsqEntries; + unsigned robEntries; + }; + + /** Per-thread tracking of the number of free entries of back-end + * structures. + */ + FreeEntries freeEntries[Impl::MaxThreads]; + + /** Records if the ROB is empty. In SMT mode the ROB may be dynamically + * partitioned between threads, so the ROB must tell rename when it is + * empty. + */ + bool emptyROB[Impl::MaxThreads]; + + /** Source of possible stalls. */ + struct Stalls { + bool iew; + bool commit; + }; + + /** Tracks which stages are telling decode to stall. */ + Stalls stalls[Impl::MaxThreads]; + + /** The serialize instruction that rename has stalled on. */ + DynInstPtr serializeInst[Impl::MaxThreads]; + + /** Records if rename needs to serialize on the next instruction for any + * thread. + */ + bool serializeOnNextInst[Impl::MaxThreads]; + + /** Delay between iew and rename, in ticks. */ + int iewToRenameDelay; + + /** Delay between decode and rename, in ticks. */ + int decodeToRenameDelay; + + /** Delay between commit and rename, in ticks. */ + unsigned commitToRenameDelay; + + /** Rename width, in instructions. */ + unsigned renameWidth; + + /** Commit width, in instructions. Used so rename knows how many + * instructions might have freed registers in the previous cycle. + */ + unsigned commitWidth; + + /** The index of the instruction in the time buffer to IEW that rename is + * currently using. + */ + unsigned toIEWIndex; + + /** Whether or not rename needs to block this cycle. */ + bool blockThisCycle; + + /** The number of threads active in rename. */ + unsigned numThreads; + + /** The maximum skid buffer size. */ + unsigned skidBufferMax; + + PhysRegIndex maxPhysicalRegs; + + /** Enum to record the source of a structure full stall. Can come from + * either ROB, IQ, LSQ, and it is priortized in that order. + */ + enum FullSource { + ROB, + IQ, + LSQ, + NONE + }; + + /** Function used to increment the stat that corresponds to the source of + * the stall. + */ + inline void incrFullStat(const FullSource &source); + + /** Stat for total number of cycles spent squashing. */ + Stats::Scalar<> renameSquashCycles; + /** Stat for total number of cycles spent idle. */ + Stats::Scalar<> renameIdleCycles; + /** Stat for total number of cycles spent blocking. */ + Stats::Scalar<> renameBlockCycles; + /** Stat for total number of cycles spent stalling for a serializing inst. */ + Stats::Scalar<> renameSerializeStallCycles; + /** Stat for total number of cycles spent running normally. */ + Stats::Scalar<> renameRunCycles; + /** Stat for total number of cycles spent unblocking. */ + Stats::Scalar<> renameUnblockCycles; + /** Stat for total number of renamed instructions. */ + Stats::Scalar<> renameRenamedInsts; + /** Stat for total number of squashed instructions that rename discards. */ + Stats::Scalar<> renameSquashedInsts; + /** Stat for total number of times that the ROB starts a stall in rename. */ + Stats::Scalar<> renameROBFullEvents; + /** Stat for total number of times that the IQ starts a stall in rename. */ + Stats::Scalar<> renameIQFullEvents; + /** Stat for total number of times that the LSQ starts a stall in rename. */ + Stats::Scalar<> renameLSQFullEvents; + /** Stat for total number of times that rename runs out of free registers + * to use to rename. */ + Stats::Scalar<> renameFullRegistersEvents; + /** Stat for total number of renamed destination registers. */ + Stats::Scalar<> renameRenamedOperands; + /** Stat for total number of source register rename lookups. */ + Stats::Scalar<> renameRenameLookups; + /** Stat for total number of committed renaming mappings. */ + Stats::Scalar<> renameCommittedMaps; + /** Stat for total number of mappings that were undone due to a squash. */ + Stats::Scalar<> renameUndoneMaps; + /** Number of serialize instructions handled. */ + Stats::Scalar<> renamedSerializing; + /** Number of instructions marked as temporarily serializing. */ + Stats::Scalar<> renamedTempSerializing; + /** Number of instructions inserted into skid buffers. */ + Stats::Scalar<> renameSkidInsts; +}; + +#endif // __CPU_O3_RENAME_HH__ diff --git a/src/cpu/o3/rename_impl.hh b/src/cpu/o3/rename_impl.hh new file mode 100644 index 000000000..248d7deb6 --- /dev/null +++ b/src/cpu/o3/rename_impl.hh @@ -0,0 +1,1331 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include <list> + +#include "config/full_system.hh" +#include "cpu/o3/rename.hh" + +template <class Impl> +DefaultRename<Impl>::DefaultRename(Params *params) + : iewToRenameDelay(params->iewToRenameDelay), + decodeToRenameDelay(params->decodeToRenameDelay), + commitToRenameDelay(params->commitToRenameDelay), + renameWidth(params->renameWidth), + commitWidth(params->commitWidth), + numThreads(params->numberOfThreads), + maxPhysicalRegs(params->numPhysIntRegs + params->numPhysFloatRegs) +{ + _status = Inactive; + + for (int i=0; i< numThreads; i++) { + renameStatus[i] = Idle; + + freeEntries[i].iqEntries = 0; + freeEntries[i].lsqEntries = 0; + freeEntries[i].robEntries = 0; + + stalls[i].iew = false; + stalls[i].commit = false; + serializeInst[i] = NULL; + + instsInProgress[i] = 0; + + emptyROB[i] = true; + + serializeOnNextInst[i] = false; + } + + // @todo: Make into a parameter. + skidBufferMax = (2 * (iewToRenameDelay * params->decodeWidth)) + renameWidth; +} + +template <class Impl> +std::string +DefaultRename<Impl>::name() const +{ + return cpu->name() + ".rename"; +} + +template <class Impl> +void +DefaultRename<Impl>::regStats() +{ + renameSquashCycles + .name(name() + ".RENAME:SquashCycles") + .desc("Number of cycles rename is squashing") + .prereq(renameSquashCycles); + renameIdleCycles + .name(name() + ".RENAME:IdleCycles") + .desc("Number of cycles rename is idle") + .prereq(renameIdleCycles); + renameBlockCycles + .name(name() + ".RENAME:BlockCycles") + .desc("Number of cycles rename is blocking") + .prereq(renameBlockCycles); + renameSerializeStallCycles + .name(name() + ".RENAME:serializeStallCycles") + .desc("count of cycles rename stalled for serializing inst") + .flags(Stats::total); + renameRunCycles + .name(name() + ".RENAME:RunCycles") + .desc("Number of cycles rename is running") + .prereq(renameIdleCycles); + renameUnblockCycles + .name(name() + ".RENAME:UnblockCycles") + .desc("Number of cycles rename is unblocking") + .prereq(renameUnblockCycles); + renameRenamedInsts + .name(name() + ".RENAME:RenamedInsts") + .desc("Number of instructions processed by rename") + .prereq(renameRenamedInsts); + renameSquashedInsts + .name(name() + ".RENAME:SquashedInsts") + .desc("Number of squashed instructions processed by rename") + .prereq(renameSquashedInsts); + renameROBFullEvents + .name(name() + ".RENAME:ROBFullEvents") + .desc("Number of times rename has blocked due to ROB full") + .prereq(renameROBFullEvents); + renameIQFullEvents + .name(name() + ".RENAME:IQFullEvents") + .desc("Number of times rename has blocked due to IQ full") + .prereq(renameIQFullEvents); + renameLSQFullEvents + .name(name() + ".RENAME:LSQFullEvents") + .desc("Number of times rename has blocked due to LSQ full") + .prereq(renameLSQFullEvents); + renameFullRegistersEvents + .name(name() + ".RENAME:FullRegisterEvents") + .desc("Number of times there has been no free registers") + .prereq(renameFullRegistersEvents); + renameRenamedOperands + .name(name() + ".RENAME:RenamedOperands") + .desc("Number of destination operands rename has renamed") + .prereq(renameRenamedOperands); + renameRenameLookups + .name(name() + ".RENAME:RenameLookups") + .desc("Number of register rename lookups that rename has made") + .prereq(renameRenameLookups); + renameCommittedMaps + .name(name() + ".RENAME:CommittedMaps") + .desc("Number of HB maps that are committed") + .prereq(renameCommittedMaps); + renameUndoneMaps + .name(name() + ".RENAME:UndoneMaps") + .desc("Number of HB maps that are undone due to squashing") + .prereq(renameUndoneMaps); + renamedSerializing + .name(name() + ".RENAME:serializingInsts") + .desc("count of serializing insts renamed") + .flags(Stats::total) + ; + renamedTempSerializing + .name(name() + ".RENAME:tempSerializingInsts") + .desc("count of temporary serializing insts renamed") + .flags(Stats::total) + ; + renameSkidInsts + .name(name() + ".RENAME:skidInsts") + .desc("count of insts added to the skid buffer") + .flags(Stats::total) + ; +} + +template <class Impl> +void +DefaultRename<Impl>::setCPU(O3CPU *cpu_ptr) +{ + DPRINTF(Rename, "Setting CPU pointer.\n"); + cpu = cpu_ptr; +} + +template <class Impl> +void +DefaultRename<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +{ + DPRINTF(Rename, "Setting time buffer pointer.\n"); + timeBuffer = tb_ptr; + + // Setup wire to read information from time buffer, from IEW stage. + fromIEW = timeBuffer->getWire(-iewToRenameDelay); + + // Setup wire to read infromation from time buffer, from commit stage. + fromCommit = timeBuffer->getWire(-commitToRenameDelay); + + // Setup wire to write information to previous stages. + toDecode = timeBuffer->getWire(0); +} + +template <class Impl> +void +DefaultRename<Impl>::setRenameQueue(TimeBuffer<RenameStruct> *rq_ptr) +{ + DPRINTF(Rename, "Setting rename queue pointer.\n"); + renameQueue = rq_ptr; + + // Setup wire to write information to future stages. + toIEW = renameQueue->getWire(0); +} + +template <class Impl> +void +DefaultRename<Impl>::setDecodeQueue(TimeBuffer<DecodeStruct> *dq_ptr) +{ + DPRINTF(Rename, "Setting decode queue pointer.\n"); + decodeQueue = dq_ptr; + + // Setup wire to get information from decode. + fromDecode = decodeQueue->getWire(-decodeToRenameDelay); +} + +template <class Impl> +void +DefaultRename<Impl>::initStage() +{ + // Grab the number of free entries directly from the stages. + for (int tid=0; tid < numThreads; tid++) { + freeEntries[tid].iqEntries = iew_ptr->instQueue.numFreeEntries(tid); + freeEntries[tid].lsqEntries = iew_ptr->ldstQueue.numFreeEntries(tid); + freeEntries[tid].robEntries = commit_ptr->numROBFreeEntries(tid); + emptyROB[tid] = true; + } +} + +template<class Impl> +void +DefaultRename<Impl>::setActiveThreads(std::list<unsigned> *at_ptr) +{ + DPRINTF(Rename, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + + +template <class Impl> +void +DefaultRename<Impl>::setRenameMap(RenameMap rm_ptr[]) +{ + DPRINTF(Rename, "Setting rename map pointers.\n"); + + for (int i=0; i<numThreads; i++) { + renameMap[i] = &rm_ptr[i]; + } +} + +template <class Impl> +void +DefaultRename<Impl>::setFreeList(FreeList *fl_ptr) +{ + DPRINTF(Rename, "Setting free list pointer.\n"); + freeList = fl_ptr; +} + +template<class Impl> +void +DefaultRename<Impl>::setScoreboard(Scoreboard *_scoreboard) +{ + DPRINTF(Rename, "Setting scoreboard pointer.\n"); + scoreboard = _scoreboard; +} + +template <class Impl> +bool +DefaultRename<Impl>::drain() +{ + // Rename is ready to switch out at any time. + cpu->signalDrained(); + return true; +} + +template <class Impl> +void +DefaultRename<Impl>::switchOut() +{ + // Clear any state, fix up the rename map. + for (int i = 0; i < numThreads; i++) { + typename std::list<RenameHistory>::iterator hb_it = + historyBuffer[i].begin(); + + while (!historyBuffer[i].empty()) { + assert(hb_it != historyBuffer[i].end()); + + DPRINTF(Rename, "[tid:%u]: Removing history entry with sequence " + "number %i.\n", i, (*hb_it).instSeqNum); + + // Tell the rename map to set the architected register to the + // previous physical register that it was renamed to. + renameMap[i]->setEntry(hb_it->archReg, hb_it->prevPhysReg); + + // Put the renamed physical register back on the free list. + freeList->addReg(hb_it->newPhysReg); + + // Be sure to mark its register as ready if it's a misc register. + if (hb_it->newPhysReg >= maxPhysicalRegs) { + scoreboard->setReg(hb_it->newPhysReg); + } + + historyBuffer[i].erase(hb_it++); + } + insts[i].clear(); + skidBuffer[i].clear(); + } +} + +template <class Impl> +void +DefaultRename<Impl>::takeOverFrom() +{ + _status = Inactive; + initStage(); + + // Reset all state prior to taking over from the other CPU. + for (int i=0; i< numThreads; i++) { + renameStatus[i] = Idle; + + stalls[i].iew = false; + stalls[i].commit = false; + serializeInst[i] = NULL; + + instsInProgress[i] = 0; + + emptyROB[i] = true; + + serializeOnNextInst[i] = false; + } +} + +template <class Impl> +void +DefaultRename<Impl>::squash(const InstSeqNum &squash_seq_num, unsigned tid) +{ + DPRINTF(Rename, "[tid:%u]: Squashing instructions.\n",tid); + + // Clear the stall signal if rename was blocked or unblocking before. + // If it still needs to block, the blocking should happen the next + // cycle and there should be space to hold everything due to the squash. + if (renameStatus[tid] == Blocked || + renameStatus[tid] == Unblocking || + renameStatus[tid] == SerializeStall) { + + toDecode->renameUnblock[tid] = 1; + + serializeInst[tid] = NULL; + } + + // Set the status to Squashing. + renameStatus[tid] = Squashing; + + // Squash any instructions from decode. + unsigned squashCount = 0; + + for (int i=0; i<fromDecode->size; i++) { + if (fromDecode->insts[i]->threadNumber == tid && + fromDecode->insts[i]->seqNum > squash_seq_num) { + fromDecode->insts[i]->setSquashed(); + wroteToTimeBuffer = true; + squashCount++; + } + + } + + // Clear the instruction list and skid buffer in case they have any + // insts in them. Since we support multiple ISAs, we cant just: + // "insts[tid].clear();" or "skidBuffer[tid].clear()" since there is + // a possible delay slot inst for different architectures + // insts[tid].clear(); +#if ISA_HAS_DELAY_SLOT + DPRINTF(Rename, "[tid:%i] Squashing incoming decode instructions until " + "[sn:%i].\n",tid, squash_seq_num); + ListIt ilist_it = insts[tid].begin(); + while (ilist_it != insts[tid].end()) { + if ((*ilist_it)->seqNum > squash_seq_num) { + (*ilist_it)->setSquashed(); + DPRINTF(Rename, "Squashing incoming decode instruction, " + "[tid:%i] [sn:%i] PC %08p.\n", tid, (*ilist_it)->seqNum, (*ilist_it)->PC); + } + ilist_it++; + } +#else + insts[tid].clear(); +#endif + + // Clear the skid buffer in case it has any data in it. + // See comments above. + // skidBuffer[tid].clear(); +#if ISA_HAS_DELAY_SLOT + DPRINTF(Rename, "[tid:%i] Squashing incoming skidbuffer instructions " + "until [sn:%i].\n", tid, squash_seq_num); + ListIt slist_it = skidBuffer[tid].begin(); + while (slist_it != skidBuffer[tid].end()) { + if ((*slist_it)->seqNum > squash_seq_num) { + (*slist_it)->setSquashed(); + DPRINTF(Rename, "Squashing skidbuffer instruction, [tid:%i] [sn:%i]" + "PC %08p.\n", tid, (*slist_it)->seqNum, (*slist_it)->PC); + } + slist_it++; + } +#else + skidBuffer[tid].clear(); +#endif + doSquash(squash_seq_num, tid); +} + +template <class Impl> +void +DefaultRename<Impl>::tick() +{ + wroteToTimeBuffer = false; + + blockThisCycle = false; + + bool status_change = false; + + toIEWIndex = 0; + + sortInsts(); + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + // Check stall and squash signals. + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + DPRINTF(Rename, "Processing [tid:%i]\n", tid); + + status_change = checkSignalsAndUpdate(tid) || status_change; + + rename(status_change, tid); + } + + if (status_change) { + updateStatus(); + } + + if (wroteToTimeBuffer) { + DPRINTF(Activity, "Activity this cycle.\n"); + cpu->activityThisCycle(); + } + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + // If we committed this cycle then doneSeqNum will be > 0 + if (fromCommit->commitInfo[tid].doneSeqNum != 0 && + !fromCommit->commitInfo[tid].squash && + renameStatus[tid] != Squashing) { + + removeFromHistory(fromCommit->commitInfo[tid].doneSeqNum, + tid); + } + } + + // @todo: make into updateProgress function + for (int tid=0; tid < numThreads; tid++) { + instsInProgress[tid] -= fromIEW->iewInfo[tid].dispatched; + + assert(instsInProgress[tid] >=0); + } + +} + +template<class Impl> +void +DefaultRename<Impl>::rename(bool &status_change, unsigned tid) +{ + // If status is Running or idle, + // call renameInsts() + // If status is Unblocking, + // buffer any instructions coming from decode + // continue trying to empty skid buffer + // check if stall conditions have passed + + if (renameStatus[tid] == Blocked) { + ++renameBlockCycles; + } else if (renameStatus[tid] == Squashing) { + ++renameSquashCycles; + } else if (renameStatus[tid] == SerializeStall) { + ++renameSerializeStallCycles; + } + + if (renameStatus[tid] == Running || + renameStatus[tid] == Idle) { + DPRINTF(Rename, "[tid:%u]: Not blocked, so attempting to run " + "stage.\n", tid); + + renameInsts(tid); + } else if (renameStatus[tid] == Unblocking) { + renameInsts(tid); + + if (validInsts()) { + // Add the current inputs to the skid buffer so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + } + + // If we switched over to blocking, then there's a potential for + // an overall status change. + status_change = unblock(tid) || status_change || blockThisCycle; + } +} + +template <class Impl> +void +DefaultRename<Impl>::renameInsts(unsigned tid) +{ + // Instructions can be either in the skid buffer or the queue of + // instructions coming from decode, depending on the status. + int insts_available = renameStatus[tid] == Unblocking ? + skidBuffer[tid].size() : insts[tid].size(); + + // Check the decode queue to see if instructions are available. + // If there are no available instructions to rename, then do nothing. + if (insts_available == 0) { + DPRINTF(Rename, "[tid:%u]: Nothing to do, breaking out early.\n", + tid); + // Should I change status to idle? + ++renameIdleCycles; + return; + } else if (renameStatus[tid] == Unblocking) { + ++renameUnblockCycles; + } else if (renameStatus[tid] == Running) { + ++renameRunCycles; + } + + DynInstPtr inst; + + // Will have to do a different calculation for the number of free + // entries. + int free_rob_entries = calcFreeROBEntries(tid); + int free_iq_entries = calcFreeIQEntries(tid); + int free_lsq_entries = calcFreeLSQEntries(tid); + int min_free_entries = free_rob_entries; + + FullSource source = ROB; + + if (free_iq_entries < min_free_entries) { + min_free_entries = free_iq_entries; + source = IQ; + } + + if (free_lsq_entries < min_free_entries) { + min_free_entries = free_lsq_entries; + source = LSQ; + } + + // Check if there's any space left. + if (min_free_entries <= 0) { + DPRINTF(Rename, "[tid:%u]: Blocking due to no free ROB/IQ/LSQ " + "entries.\n" + "ROB has %i free entries.\n" + "IQ has %i free entries.\n" + "LSQ has %i free entries.\n", + tid, + free_rob_entries, + free_iq_entries, + free_lsq_entries); + + blockThisCycle = true; + + block(tid); + + incrFullStat(source); + + return; + } else if (min_free_entries < insts_available) { + DPRINTF(Rename, "[tid:%u]: Will have to block this cycle." + "%i insts available, but only %i insts can be " + "renamed due to ROB/IQ/LSQ limits.\n", + tid, insts_available, min_free_entries); + + insts_available = min_free_entries; + + blockThisCycle = true; + + incrFullStat(source); + } + + InstQueue &insts_to_rename = renameStatus[tid] == Unblocking ? + skidBuffer[tid] : insts[tid]; + + DPRINTF(Rename, "[tid:%u]: %i available instructions to " + "send iew.\n", tid, insts_available); + + DPRINTF(Rename, "[tid:%u]: %i insts pipelining from Rename | %i insts " + "dispatched to IQ last cycle.\n", + tid, instsInProgress[tid], fromIEW->iewInfo[tid].dispatched); + + // Handle serializing the next instruction if necessary. + if (serializeOnNextInst[tid]) { + if (emptyROB[tid] && instsInProgress[tid] == 0) { + // ROB already empty; no need to serialize. + serializeOnNextInst[tid] = false; + } else if (!insts_to_rename.empty()) { + insts_to_rename.front()->setSerializeBefore(); + } + } + + int renamed_insts = 0; + + while (insts_available > 0 && toIEWIndex < renameWidth) { + DPRINTF(Rename, "[tid:%u]: Sending instructions to IEW.\n", tid); + + assert(!insts_to_rename.empty()); + + inst = insts_to_rename.front(); + + insts_to_rename.pop_front(); + + if (renameStatus[tid] == Unblocking) { + DPRINTF(Rename,"[tid:%u]: Removing [sn:%lli] PC:%#x from rename " + "skidBuffer\n", + tid, inst->seqNum, inst->readPC()); + } + + if (inst->isSquashed()) { + DPRINTF(Rename, "[tid:%u]: instruction %i with PC %#x is " + "squashed, skipping.\n", + tid, inst->seqNum, inst->readPC()); + + ++renameSquashedInsts; + + // Decrement how many instructions are available. + --insts_available; + + continue; + } + + DPRINTF(Rename, "[tid:%u]: Processing instruction [sn:%lli] with " + "PC %#x.\n", + tid, inst->seqNum, inst->readPC()); + + // Handle serializeAfter/serializeBefore instructions. + // serializeAfter marks the next instruction as serializeBefore. + // serializeBefore makes the instruction wait in rename until the ROB + // is empty. + + // In this model, IPR accesses are serialize before + // instructions, and store conditionals are serialize after + // instructions. This is mainly due to lack of support for + // out-of-order operations of either of those classes of + // instructions. + if ((inst->isIprAccess() || inst->isSerializeBefore()) && + !inst->isSerializeHandled()) { + DPRINTF(Rename, "Serialize before instruction encountered.\n"); + + if (!inst->isTempSerializeBefore()) { + renamedSerializing++; + inst->setSerializeHandled(); + } else { + renamedTempSerializing++; + } + + // Change status over to SerializeStall so that other stages know + // what this is blocked on. + renameStatus[tid] = SerializeStall; + + serializeInst[tid] = inst; + + blockThisCycle = true; + + break; + } else if ((inst->isStoreConditional() || inst->isSerializeAfter()) && + !inst->isSerializeHandled()) { + DPRINTF(Rename, "Serialize after instruction encountered.\n"); + + renamedSerializing++; + + inst->setSerializeHandled(); + + serializeAfter(insts_to_rename, tid); + } + + // Check here to make sure there are enough destination registers + // to rename to. Otherwise block. + if (renameMap[tid]->numFreeEntries() < inst->numDestRegs()) { + DPRINTF(Rename, "Blocking due to lack of free " + "physical registers to rename to.\n"); + blockThisCycle = true; + + ++renameFullRegistersEvents; + + break; + } + + renameSrcRegs(inst, inst->threadNumber); + + renameDestRegs(inst, inst->threadNumber); + + ++renamed_insts; + + // Put instruction in rename queue. + toIEW->insts[toIEWIndex] = inst; + ++(toIEW->size); + + // Increment which instruction we're on. + ++toIEWIndex; + + // Decrement how many instructions are available. + --insts_available; + } + + instsInProgress[tid] += renamed_insts; + renameRenamedInsts += renamed_insts; + + // If we wrote to the time buffer, record this. + if (toIEWIndex) { + wroteToTimeBuffer = true; + } + + // Check if there's any instructions left that haven't yet been renamed. + // If so then block. + if (insts_available) { + blockThisCycle = true; + } + + if (blockThisCycle) { + block(tid); + toDecode->renameUnblock[tid] = false; + } +} + +template<class Impl> +void +DefaultRename<Impl>::skidInsert(unsigned tid) +{ + DynInstPtr inst = NULL; + + while (!insts[tid].empty()) { + inst = insts[tid].front(); + + insts[tid].pop_front(); + + assert(tid == inst->threadNumber); + + DPRINTF(Rename, "[tid:%u]: Inserting [sn:%lli] PC:%#x into Rename " + "skidBuffer\n", tid, inst->seqNum, inst->readPC()); + + ++renameSkidInsts; + + skidBuffer[tid].push_back(inst); + } + + if (skidBuffer[tid].size() > skidBufferMax) + panic("Skidbuffer Exceeded Max Size"); +} + +template <class Impl> +void +DefaultRename<Impl>::sortInsts() +{ + int insts_from_decode = fromDecode->size; +#ifdef DEBUG +#if !ISA_HAS_DELAY_SLOT + for (int i=0; i < numThreads; i++) + assert(insts[i].empty()); +#endif +#endif + for (int i = 0; i < insts_from_decode; ++i) { + DynInstPtr inst = fromDecode->insts[i]; + insts[inst->threadNumber].push_back(inst); + } +} + +template<class Impl> +bool +DefaultRename<Impl>::skidsEmpty() +{ + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + if (!skidBuffer[*threads++].empty()) + return false; + } + + return true; +} + +template<class Impl> +void +DefaultRename<Impl>::updateStatus() +{ + bool any_unblocking = false; + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (renameStatus[tid] == Unblocking) { + any_unblocking = true; + break; + } + } + + // Rename will have activity if it's unblocking. + if (any_unblocking) { + if (_status == Inactive) { + _status = Active; + + DPRINTF(Activity, "Activating stage.\n"); + + cpu->activateStage(O3CPU::RenameIdx); + } + } else { + // If it's not unblocking, then rename will not have any internal + // activity. Switch it to inactive. + if (_status == Active) { + _status = Inactive; + DPRINTF(Activity, "Deactivating stage.\n"); + + cpu->deactivateStage(O3CPU::RenameIdx); + } + } +} + +template <class Impl> +bool +DefaultRename<Impl>::block(unsigned tid) +{ + DPRINTF(Rename, "[tid:%u]: Blocking.\n", tid); + + // Add the current inputs onto the skid buffer, so they can be + // reprocessed when this stage unblocks. + skidInsert(tid); + + // Only signal backwards to block if the previous stages do not think + // rename is already blocked. + if (renameStatus[tid] != Blocked) { + if (renameStatus[tid] != Unblocking) { + toDecode->renameBlock[tid] = true; + toDecode->renameUnblock[tid] = false; + wroteToTimeBuffer = true; + } + + // Rename can not go from SerializeStall to Blocked, otherwise + // it would not know to complete the serialize stall. + if (renameStatus[tid] != SerializeStall) { + // Set status to Blocked. + renameStatus[tid] = Blocked; + return true; + } + } + + return false; +} + +template <class Impl> +bool +DefaultRename<Impl>::unblock(unsigned tid) +{ + DPRINTF(Rename, "[tid:%u]: Trying to unblock.\n", tid); + + // Rename is done unblocking if the skid buffer is empty. + if (skidBuffer[tid].empty() && renameStatus[tid] != SerializeStall) { + + DPRINTF(Rename, "[tid:%u]: Done unblocking.\n", tid); + + toDecode->renameUnblock[tid] = true; + wroteToTimeBuffer = true; + + renameStatus[tid] = Running; + return true; + } + + return false; +} + +template <class Impl> +void +DefaultRename<Impl>::doSquash(const InstSeqNum &squashed_seq_num, unsigned tid) +{ + typename std::list<RenameHistory>::iterator hb_it = + historyBuffer[tid].begin(); + + // After a syscall squashes everything, the history buffer may be empty + // but the ROB may still be squashing instructions. + if (historyBuffer[tid].empty()) { + return; + } + + // Go through the most recent instructions, undoing the mappings + // they did and freeing up the registers. + while (!historyBuffer[tid].empty() && + (*hb_it).instSeqNum > squashed_seq_num) { + assert(hb_it != historyBuffer[tid].end()); + + DPRINTF(Rename, "[tid:%u]: Removing history entry with sequence " + "number %i.\n", tid, (*hb_it).instSeqNum); + + // Tell the rename map to set the architected register to the + // previous physical register that it was renamed to. + renameMap[tid]->setEntry(hb_it->archReg, hb_it->prevPhysReg); + + // Put the renamed physical register back on the free list. + freeList->addReg(hb_it->newPhysReg); + + // Be sure to mark its register as ready if it's a misc register. + if (hb_it->newPhysReg >= maxPhysicalRegs) { + scoreboard->setReg(hb_it->newPhysReg); + } + + historyBuffer[tid].erase(hb_it++); + + ++renameUndoneMaps; + } +} + +template<class Impl> +void +DefaultRename<Impl>::removeFromHistory(InstSeqNum inst_seq_num, unsigned tid) +{ + DPRINTF(Rename, "[tid:%u]: Removing a committed instruction from the " + "history buffer %u (size=%i), until [sn:%lli].\n", + tid, tid, historyBuffer[tid].size(), inst_seq_num); + + typename std::list<RenameHistory>::iterator hb_it = + historyBuffer[tid].end(); + + --hb_it; + + if (historyBuffer[tid].empty()) { + DPRINTF(Rename, "[tid:%u]: History buffer is empty.\n", tid); + return; + } else if (hb_it->instSeqNum > inst_seq_num) { + DPRINTF(Rename, "[tid:%u]: Old sequence number encountered. Ensure " + "that a syscall happened recently.\n", tid); + return; + } + + // Commit all the renames up until (and including) the committed sequence + // number. Some or even all of the committed instructions may not have + // rename histories if they did not have destination registers that were + // renamed. + while (!historyBuffer[tid].empty() && + hb_it != historyBuffer[tid].end() && + (*hb_it).instSeqNum <= inst_seq_num) { + + DPRINTF(Rename, "[tid:%u]: Freeing up older rename of reg %i, " + "[sn:%lli].\n", + tid, (*hb_it).prevPhysReg, (*hb_it).instSeqNum); + + freeList->addReg((*hb_it).prevPhysReg); + ++renameCommittedMaps; + + historyBuffer[tid].erase(hb_it--); + } +} + +template <class Impl> +inline void +DefaultRename<Impl>::renameSrcRegs(DynInstPtr &inst,unsigned tid) +{ + assert(renameMap[tid] != 0); + + unsigned num_src_regs = inst->numSrcRegs(); + + // Get the architectual register numbers from the source and + // destination operands, and redirect them to the right register. + // Will need to mark dependencies though. + for (int src_idx = 0; src_idx < num_src_regs; src_idx++) { + RegIndex src_reg = inst->srcRegIdx(src_idx); + + // Look up the source registers to get the phys. register they've + // been renamed to, and set the sources to those registers. + PhysRegIndex renamed_reg = renameMap[tid]->lookup(src_reg); + + DPRINTF(Rename, "[tid:%u]: Looking up arch reg %i, got " + "physical reg %i.\n", tid, (int)src_reg, + (int)renamed_reg); + + inst->renameSrcReg(src_idx, renamed_reg); + + // See if the register is ready or not. + if (scoreboard->getReg(renamed_reg) == true) { + DPRINTF(Rename, "[tid:%u]: Register is ready.\n", tid); + + inst->markSrcRegReady(src_idx); + } + + ++renameRenameLookups; + } +} + +template <class Impl> +inline void +DefaultRename<Impl>::renameDestRegs(DynInstPtr &inst,unsigned tid) +{ + typename RenameMap::RenameInfo rename_result; + + unsigned num_dest_regs = inst->numDestRegs(); + + // Rename the destination registers. + for (int dest_idx = 0; dest_idx < num_dest_regs; dest_idx++) { + RegIndex dest_reg = inst->destRegIdx(dest_idx); + + // Get the physical register that the destination will be + // renamed to. + rename_result = renameMap[tid]->rename(dest_reg); + + //Mark Scoreboard entry as not ready + scoreboard->unsetReg(rename_result.first); + + DPRINTF(Rename, "[tid:%u]: Renaming arch reg %i to physical " + "reg %i.\n", tid, (int)dest_reg, + (int)rename_result.first); + + // Record the rename information so that a history can be kept. + RenameHistory hb_entry(inst->seqNum, dest_reg, + rename_result.first, + rename_result.second); + + historyBuffer[tid].push_front(hb_entry); + + DPRINTF(Rename, "[tid:%u]: Adding instruction to history buffer " + "(size=%i), [sn:%lli].\n",tid, + historyBuffer[tid].size(), + (*historyBuffer[tid].begin()).instSeqNum); + + // Tell the instruction to rename the appropriate destination + // register (dest_idx) to the new physical register + // (rename_result.first), and record the previous physical + // register that the same logical register was renamed to + // (rename_result.second). + inst->renameDestReg(dest_idx, + rename_result.first, + rename_result.second); + + ++renameRenamedOperands; + } +} + +template <class Impl> +inline int +DefaultRename<Impl>::calcFreeROBEntries(unsigned tid) +{ + int num_free = freeEntries[tid].robEntries - + (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatched); + + //DPRINTF(Rename,"[tid:%i]: %i rob free\n",tid,num_free); + + return num_free; +} + +template <class Impl> +inline int +DefaultRename<Impl>::calcFreeIQEntries(unsigned tid) +{ + int num_free = freeEntries[tid].iqEntries - + (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatched); + + //DPRINTF(Rename,"[tid:%i]: %i iq free\n",tid,num_free); + + return num_free; +} + +template <class Impl> +inline int +DefaultRename<Impl>::calcFreeLSQEntries(unsigned tid) +{ + int num_free = freeEntries[tid].lsqEntries - + (instsInProgress[tid] - fromIEW->iewInfo[tid].dispatchedToLSQ); + + //DPRINTF(Rename,"[tid:%i]: %i lsq free\n",tid,num_free); + + return num_free; +} + +template <class Impl> +unsigned +DefaultRename<Impl>::validInsts() +{ + unsigned inst_count = 0; + + for (int i=0; i<fromDecode->size; i++) { + if (!fromDecode->insts[i]->isSquashed()) + inst_count++; + } + + return inst_count; +} + +template <class Impl> +void +DefaultRename<Impl>::readStallSignals(unsigned tid) +{ + if (fromIEW->iewBlock[tid]) { + stalls[tid].iew = true; + } + + if (fromIEW->iewUnblock[tid]) { + assert(stalls[tid].iew); + stalls[tid].iew = false; + } + + if (fromCommit->commitBlock[tid]) { + stalls[tid].commit = true; + } + + if (fromCommit->commitUnblock[tid]) { + assert(stalls[tid].commit); + stalls[tid].commit = false; + } +} + +template <class Impl> +bool +DefaultRename<Impl>::checkStall(unsigned tid) +{ + bool ret_val = false; + + if (stalls[tid].iew) { + DPRINTF(Rename,"[tid:%i]: Stall from IEW stage detected.\n", tid); + ret_val = true; + } else if (stalls[tid].commit) { + DPRINTF(Rename,"[tid:%i]: Stall from Commit stage detected.\n", tid); + ret_val = true; + } else if (calcFreeROBEntries(tid) <= 0) { + DPRINTF(Rename,"[tid:%i]: Stall: ROB has 0 free entries.\n", tid); + ret_val = true; + } else if (calcFreeIQEntries(tid) <= 0) { + DPRINTF(Rename,"[tid:%i]: Stall: IQ has 0 free entries.\n", tid); + ret_val = true; + } else if (calcFreeLSQEntries(tid) <= 0) { + DPRINTF(Rename,"[tid:%i]: Stall: LSQ has 0 free entries.\n", tid); + ret_val = true; + } else if (renameMap[tid]->numFreeEntries() <= 0) { + DPRINTF(Rename,"[tid:%i]: Stall: RenameMap has 0 free entries.\n", tid); + ret_val = true; + } else if (renameStatus[tid] == SerializeStall && + (!emptyROB[tid] || instsInProgress[tid])) { + DPRINTF(Rename,"[tid:%i]: Stall: Serialize stall and ROB is not " + "empty.\n", + tid); + ret_val = true; + } + + return ret_val; +} + +template <class Impl> +void +DefaultRename<Impl>::readFreeEntries(unsigned tid) +{ + bool updated = false; + if (fromIEW->iewInfo[tid].usedIQ) { + freeEntries[tid].iqEntries = + fromIEW->iewInfo[tid].freeIQEntries; + updated = true; + } + + if (fromIEW->iewInfo[tid].usedLSQ) { + freeEntries[tid].lsqEntries = + fromIEW->iewInfo[tid].freeLSQEntries; + updated = true; + } + + if (fromCommit->commitInfo[tid].usedROB) { + freeEntries[tid].robEntries = + fromCommit->commitInfo[tid].freeROBEntries; + emptyROB[tid] = fromCommit->commitInfo[tid].emptyROB; + updated = true; + } + + DPRINTF(Rename, "[tid:%i]: Free IQ: %i, Free ROB: %i, Free LSQ: %i\n", + tid, + freeEntries[tid].iqEntries, + freeEntries[tid].robEntries, + freeEntries[tid].lsqEntries); + + DPRINTF(Rename, "[tid:%i]: %i instructions not yet in ROB\n", + tid, instsInProgress[tid]); +} + +template <class Impl> +bool +DefaultRename<Impl>::checkSignalsAndUpdate(unsigned tid) +{ + // Check if there's a squash signal, squash if there is + // Check stall signals, block if necessary. + // If status was blocked + // check if stall conditions have passed + // if so then go to unblocking + // If status was Squashing + // check if squashing is not high. Switch to running this cycle. + // If status was serialize stall + // check if ROB is empty and no insts are in flight to the ROB + + readFreeEntries(tid); + readStallSignals(tid); + + if (fromCommit->commitInfo[tid].squash) { + DPRINTF(Rename, "[tid:%u]: Squashing instructions due to squash from " + "commit.\n", tid); + +#if ISA_HAS_DELAY_SLOT + InstSeqNum squashed_seq_num = fromCommit->commitInfo[tid].bdelayDoneSeqNum; +#else + InstSeqNum squashed_seq_num = fromCommit->commitInfo[tid].doneSeqNum; +#endif + + squash(squashed_seq_num, tid); + + return true; + } + + if (fromCommit->commitInfo[tid].robSquashing) { + DPRINTF(Rename, "[tid:%u]: ROB is still squashing.\n", tid); + + renameStatus[tid] = Squashing; + + return true; + } + + if (checkStall(tid)) { + return block(tid); + } + + if (renameStatus[tid] == Blocked) { + DPRINTF(Rename, "[tid:%u]: Done blocking, switching to unblocking.\n", + tid); + + renameStatus[tid] = Unblocking; + + unblock(tid); + + return true; + } + + if (renameStatus[tid] == Squashing) { + // Switch status to running if rename isn't being told to block or + // squash this cycle. + DPRINTF(Rename, "[tid:%u]: Done squashing, switching to running.\n", + tid); + + renameStatus[tid] = Running; + + return false; + } + + if (renameStatus[tid] == SerializeStall) { + // Stall ends once the ROB is free. + DPRINTF(Rename, "[tid:%u]: Done with serialize stall, switching to " + "unblocking.\n", tid); + + DynInstPtr serial_inst = serializeInst[tid]; + + renameStatus[tid] = Unblocking; + + unblock(tid); + + DPRINTF(Rename, "[tid:%u]: Processing instruction [%lli] with " + "PC %#x.\n", + tid, serial_inst->seqNum, serial_inst->readPC()); + + // Put instruction into queue here. + serial_inst->clearSerializeBefore(); + + if (!skidBuffer[tid].empty()) { + skidBuffer[tid].push_front(serial_inst); + } else { + insts[tid].push_front(serial_inst); + } + + DPRINTF(Rename, "[tid:%u]: Instruction must be processed by rename." + " Adding to front of list.\n", tid); + + serializeInst[tid] = NULL; + + return true; + } + + // If we've reached this point, we have not gotten any signals that + // cause rename to change its status. Rename remains the same as before. + return false; +} + +template<class Impl> +void +DefaultRename<Impl>::serializeAfter(InstQueue &inst_list, + unsigned tid) +{ + if (inst_list.empty()) { + // Mark a bit to say that I must serialize on the next instruction. + serializeOnNextInst[tid] = true; + return; + } + + // Set the next instruction as serializing. + inst_list.front()->setSerializeBefore(); +} + +template <class Impl> +inline void +DefaultRename<Impl>::incrFullStat(const FullSource &source) +{ + switch (source) { + case ROB: + ++renameROBFullEvents; + break; + case IQ: + ++renameIQFullEvents; + break; + case LSQ: + ++renameLSQFullEvents; + break; + default: + panic("Rename full stall stat should be incremented for a reason!"); + break; + } +} + +template <class Impl> +void +DefaultRename<Impl>::dumpHistory() +{ + typename std::list<RenameHistory>::iterator buf_it; + + for (int i = 0; i < numThreads; i++) { + + buf_it = historyBuffer[i].begin(); + + while (buf_it != historyBuffer[i].end()) { + cprintf("Seq num: %i\nArch reg: %i New phys reg: %i Old phys " + "reg: %i\n", (*buf_it).instSeqNum, (int)(*buf_it).archReg, + (int)(*buf_it).newPhysReg, (int)(*buf_it).prevPhysReg); + + buf_it++; + } + } +} diff --git a/src/cpu/o3/rename_map.cc b/src/cpu/o3/rename_map.cc new file mode 100644 index 000000000..befbc3e8a --- /dev/null +++ b/src/cpu/o3/rename_map.cc @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include <vector> + +#include "cpu/o3/rename_map.hh" + +using namespace std; + +// @todo: Consider making inline bool functions that determine if the +// register is a logical int, logical fp, physical int, physical fp, +// etc. + +SimpleRenameMap::~SimpleRenameMap() +{ +} + +void +SimpleRenameMap::init(unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + PhysRegIndex &ireg_idx, + + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs, + PhysRegIndex &freg_idx, + + unsigned _numMiscRegs, + + RegIndex _intZeroReg, + RegIndex _floatZeroReg, + + int map_id, + bool bindRegs) +{ + id = map_id; + + numLogicalIntRegs = _numLogicalIntRegs; + + numLogicalFloatRegs = _numLogicalFloatRegs; + + numPhysicalIntRegs = _numPhysicalIntRegs; + + numPhysicalFloatRegs = _numPhysicalFloatRegs; + + numMiscRegs = _numMiscRegs; + + intZeroReg = _intZeroReg; + floatZeroReg = _floatZeroReg; + + DPRINTF(Rename, "Creating rename map %i. Phys: %i / %i, Float: " + "%i / %i.\n", id, numLogicalIntRegs, numPhysicalIntRegs, + numLogicalFloatRegs, numPhysicalFloatRegs); + + numLogicalRegs = numLogicalIntRegs + numLogicalFloatRegs; + + numPhysicalRegs = numPhysicalIntRegs + numPhysicalFloatRegs; + + //Create the rename maps + intRenameMap.resize(numLogicalIntRegs); + floatRenameMap.resize(numLogicalRegs); + + if (bindRegs) { + DPRINTF(Rename, "Binding registers into rename map %i",id); + + // Initialize the entries in the integer rename map to point to the + // physical registers of the same index + for (RegIndex index = 0; index < numLogicalIntRegs; ++index) + { + intRenameMap[index].physical_reg = ireg_idx++; + } + + // Initialize the entries in the floating point rename map to point to + // the physical registers of the same index + // Although the index refers purely to architected registers, because + // the floating reg indices come after the integer reg indices, they + // may exceed the size of a normal RegIndex (short). + for (PhysRegIndex index = numLogicalIntRegs; + index < numLogicalRegs; ++index) + { + floatRenameMap[index].physical_reg = freg_idx++; + } + } else { + DPRINTF(Rename, "Binding registers into rename map %i",id); + + PhysRegIndex temp_ireg = ireg_idx; + + for (RegIndex index = 0; index < numLogicalIntRegs; ++index) + { + intRenameMap[index].physical_reg = temp_ireg++; + } + + PhysRegIndex temp_freg = freg_idx; + + for (PhysRegIndex index = numLogicalIntRegs; + index < numLogicalRegs; ++index) + { + floatRenameMap[index].physical_reg = temp_freg++; + } + } +} + +void +SimpleRenameMap::setFreeList(SimpleFreeList *fl_ptr) +{ + freeList = fl_ptr; +} + + +SimpleRenameMap::RenameInfo +SimpleRenameMap::rename(RegIndex arch_reg) +{ + PhysRegIndex renamed_reg; + PhysRegIndex prev_reg; + + if (arch_reg < numLogicalIntRegs) { + + // Record the current physical register that is renamed to the + // requested architected register. + prev_reg = intRenameMap[arch_reg].physical_reg; + + // If it's not referencing the zero register, then rename the + // register. + if (arch_reg != intZeroReg) { + renamed_reg = freeList->getIntReg(); + + intRenameMap[arch_reg].physical_reg = renamed_reg; + + assert(renamed_reg >= 0 && renamed_reg < numPhysicalIntRegs); + + } else { + // Otherwise return the zero register so nothing bad happens. + renamed_reg = intZeroReg; + } + } else if (arch_reg < numLogicalRegs) { + // Record the current physical register that is renamed to the + // requested architected register. + prev_reg = floatRenameMap[arch_reg].physical_reg; + + // If it's not referencing the zero register, then rename the + // register. + if (arch_reg != floatZeroReg) { + renamed_reg = freeList->getFloatReg(); + + floatRenameMap[arch_reg].physical_reg = renamed_reg; + + assert(renamed_reg < numPhysicalRegs && + renamed_reg >= numPhysicalIntRegs); + } else { + // Otherwise return the zero register so nothing bad happens. + renamed_reg = floatZeroReg; + } + } else { + // Subtract off the base offset for miscellaneous registers. + arch_reg = arch_reg - numLogicalRegs; + + // No renaming happens to the misc. registers. They are + // simply the registers that come after all the physical + // registers; thus take the base architected register and add + // the physical registers to it. + renamed_reg = arch_reg + numPhysicalRegs; + + // Set the previous register to the same register; mainly it must be + // known that the prev reg was outside the range of normal registers + // so the free list can avoid adding it. + prev_reg = renamed_reg; + + assert(renamed_reg < numPhysicalRegs + numMiscRegs); + } + + return RenameInfo(renamed_reg, prev_reg); +} + +PhysRegIndex +SimpleRenameMap::lookup(RegIndex arch_reg) +{ + if (arch_reg < numLogicalIntRegs) { + return intRenameMap[arch_reg].physical_reg; + } else if (arch_reg < numLogicalRegs) { + return floatRenameMap[arch_reg].physical_reg; + } else { + // Subtract off the misc registers offset. + arch_reg = arch_reg - numLogicalRegs; + + // Misc. regs don't rename, so simply add the base arch reg to + // the number of physical registers. + return numPhysicalRegs + arch_reg; + } +} + +void +SimpleRenameMap::setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg) +{ + // In this implementation the miscellaneous registers do not + // actually rename, so this function does not allow you to try to + // change their mappings. + if (arch_reg < numLogicalIntRegs) { + DPRINTF(Rename, "Rename Map: Integer register %i being set to %i.\n", + (int)arch_reg, renamed_reg); + + intRenameMap[arch_reg].physical_reg = renamed_reg; + } else if (arch_reg < numLogicalIntRegs + numLogicalFloatRegs) { + DPRINTF(Rename, "Rename Map: Float register %i being set to %i.\n", + (int)arch_reg - numLogicalIntRegs, renamed_reg); + + floatRenameMap[arch_reg].physical_reg = renamed_reg; + } +} + +int +SimpleRenameMap::numFreeEntries() +{ + int free_int_regs = freeList->numFreeIntRegs(); + int free_float_regs = freeList->numFreeFloatRegs(); + + if (free_int_regs < free_float_regs) { + return free_int_regs; + } else { + return free_float_regs; + } +} diff --git a/src/cpu/o3/rename_map.hh b/src/cpu/o3/rename_map.hh new file mode 100644 index 000000000..896c66f3e --- /dev/null +++ b/src/cpu/o3/rename_map.hh @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +// Todo: Create destructor. +// Have it so that there's a more meaningful name given to the variable +// that marks the beginning of the FP registers. + +#ifndef __CPU_O3_RENAME_MAP_HH__ +#define __CPU_O3_RENAME_MAP_HH__ + +#include <iostream> +#include <utility> +#include <vector> + +#include "cpu/o3/free_list.hh" +#include "arch/types.hh" + +class SimpleRenameMap +{ + protected: + typedef TheISA::RegIndex RegIndex; + public: + /** + * Pair of a logical register and a physical register. Tells the + * previous mapping of a logical register to a physical register. + * Used to roll back the rename map to a previous state. + */ + typedef std::pair<RegIndex, PhysRegIndex> UnmapInfo; + + /** + * Pair of a physical register and a physical register. Used to + * return the physical register that a logical register has been + * renamed to, and the previous physical register that the same + * logical register was previously mapped to. + */ + typedef std::pair<PhysRegIndex, PhysRegIndex> RenameInfo; + + public: + /** Default constructor. init() must be called prior to use. */ + SimpleRenameMap() {}; + + /** Destructor. */ + ~SimpleRenameMap(); + + /** Initializes rename map with given parameters. */ + void init(unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + PhysRegIndex &_int_reg_start, + + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs, + PhysRegIndex &_float_reg_start, + + unsigned _numMiscRegs, + + RegIndex _intZeroReg, + RegIndex _floatZeroReg, + + int id, + bool bindRegs); + + /** Sets the free list used with this rename map. */ + void setFreeList(SimpleFreeList *fl_ptr); + + //Tell rename map to get a free physical register for a given + //architected register. Not sure it should have a return value, + //but perhaps it should have some sort of fault in case there are + //no free registers. + RenameInfo rename(RegIndex arch_reg); + + PhysRegIndex lookup(RegIndex phys_reg); + + /** + * Marks the given register as ready, meaning that its value has been + * calculated and written to the register file. + * @param ready_reg The index of the physical register that is now ready. + */ + void setEntry(RegIndex arch_reg, PhysRegIndex renamed_reg); + + int numFreeEntries(); + + private: + /** Rename Map ID */ + int id; + + /** Number of logical integer registers. */ + int numLogicalIntRegs; + + /** Number of physical integer registers. */ + int numPhysicalIntRegs; + + /** Number of logical floating point registers. */ + int numLogicalFloatRegs; + + /** Number of physical floating point registers. */ + int numPhysicalFloatRegs; + + /** Number of miscellaneous registers. */ + int numMiscRegs; + + /** Number of logical integer + float registers. */ + int numLogicalRegs; + + /** Number of physical integer + float registers. */ + int numPhysicalRegs; + + /** The integer zero register. This implementation assumes it is always + * zero and never can be anything else. + */ + RegIndex intZeroReg; + + /** The floating point zero register. This implementation assumes it is + * always zero and never can be anything else. + */ + RegIndex floatZeroReg; + + class RenameEntry + { + public: + PhysRegIndex physical_reg; + bool valid; + + RenameEntry() + : physical_reg(0), valid(false) + { } + }; + + private: + /** Integer rename map. */ + std::vector<RenameEntry> intRenameMap; + + /** Floating point rename map. */ + std::vector<RenameEntry> floatRenameMap; + + private: + /** Free list interface. */ + SimpleFreeList *freeList; +}; + +#endif //__CPU_O3_RENAME_MAP_HH__ diff --git a/src/cpu/o3/rob.cc b/src/cpu/o3/rob.cc new file mode 100644 index 000000000..9976049cd --- /dev/null +++ b/src/cpu/o3/rob.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Nathan Binkert + */ + +#include "cpu/o3/isa_specific.hh" +#include "cpu/o3/rob_impl.hh" + +// Force instantiation of InstructionQueue. +template class ROB<O3CPUImpl>; diff --git a/src/cpu/o3/rob.hh b/src/cpu/o3/rob.hh new file mode 100644 index 000000000..7cd5a5143 --- /dev/null +++ b/src/cpu/o3/rob.hh @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#ifndef __CPU_O3_ROB_HH__ +#define __CPU_O3_ROB_HH__ + +#include <string> +#include <utility> +#include <vector> + +/** + * ROB class. The ROB is largely what drives squashing. + */ +template <class Impl> +class ROB +{ + protected: + typedef TheISA::RegIndex RegIndex; + public: + //Typedefs from the Impl. + typedef typename Impl::O3CPU O3CPU; + typedef typename Impl::DynInstPtr DynInstPtr; + + typedef std::pair<RegIndex, PhysRegIndex> UnmapInfo; + typedef typename std::list<DynInstPtr>::iterator InstIt; + + /** Possible ROB statuses. */ + enum Status { + Running, + Idle, + ROBSquashing + }; + + /** SMT ROB Sharing Policy */ + enum ROBPolicy{ + Dynamic, + Partitioned, + Threshold + }; + + private: + /** Per-thread ROB status. */ + Status robStatus[Impl::MaxThreads]; + + /** ROB resource sharing policy for SMT mode. */ + ROBPolicy robPolicy; + + public: + /** ROB constructor. + * @param _numEntries Number of entries in ROB. + * @param _squashWidth Number of instructions that can be squashed in a + * single cycle. + * @param _smtROBPolicy ROB Partitioning Scheme for SMT. + * @param _smtROBThreshold Max Resources(by %) a thread can have in the ROB. + * @param _numThreads The number of active threads. + */ + ROB(unsigned _numEntries, unsigned _squashWidth, std::string smtROBPolicy, + unsigned _smtROBThreshold, unsigned _numThreads); + + std::string name() const; + + /** Function to set the CPU pointer, necessary due to which object the ROB + * is created within. + * @param cpu_ptr Pointer to the implementation specific full CPU object. + */ + void setCPU(O3CPU *cpu_ptr); + + /** Sets pointer to the list of active threads. + * @param at_ptr Pointer to the list of active threads. + */ + void setActiveThreads(std::list<unsigned>* at_ptr); + + /** Switches out the ROB. */ + void switchOut(); + + /** Takes over another CPU's thread. */ + void takeOverFrom(); + + /** Function to insert an instruction into the ROB. Note that whatever + * calls this function must ensure that there is enough space within the + * ROB for the new instruction. + * @param inst The instruction being inserted into the ROB. + */ + void insertInst(DynInstPtr &inst); + + /** Returns pointer to the head instruction within the ROB. There is + * no guarantee as to the return value if the ROB is empty. + * @retval Pointer to the DynInst that is at the head of the ROB. + */ +// DynInstPtr readHeadInst(); + + /** Returns a pointer to the head instruction of a specific thread within + * the ROB. + * @return Pointer to the DynInst that is at the head of the ROB. + */ + DynInstPtr readHeadInst(unsigned tid); + + /** Returns pointer to the tail instruction within the ROB. There is + * no guarantee as to the return value if the ROB is empty. + * @retval Pointer to the DynInst that is at the tail of the ROB. + */ +// DynInstPtr readTailInst(); + + /** Returns a pointer to the tail instruction of a specific thread within + * the ROB. + * @return Pointer to the DynInst that is at the tail of the ROB. + */ + DynInstPtr readTailInst(unsigned tid); + + /** Retires the head instruction, removing it from the ROB. */ +// void retireHead(); + + /** Retires the head instruction of a specific thread, removing it from the + * ROB. + */ + void retireHead(unsigned tid); + + /** Is the oldest instruction across all threads ready. */ +// bool isHeadReady(); + + /** Is the oldest instruction across a particular thread ready. */ + bool isHeadReady(unsigned tid); + + /** Is there any commitable head instruction across all threads ready. */ + bool canCommit(); + + /** Re-adjust ROB partitioning. */ + void resetEntries(); + + /** Number of entries needed For 'num_threads' amount of threads. */ + int entryAmount(int num_threads); + + /** Returns the number of total free entries in the ROB. */ + unsigned numFreeEntries(); + + /** Returns the number of free entries in a specific ROB paritition. */ + unsigned numFreeEntries(unsigned tid); + + /** Returns the maximum number of entries for a specific thread. */ + unsigned getMaxEntries(unsigned tid) + { return maxEntries[tid]; } + + /** Returns the number of entries being used by a specific thread. */ + unsigned getThreadEntries(unsigned tid) + { return threadEntries[tid]; } + + /** Returns if the ROB is full. */ + bool isFull() + { return numInstsInROB == numEntries; } + + /** Returns if a specific thread's partition is full. */ + bool isFull(unsigned tid) + { return threadEntries[tid] == numEntries; } + + /** Returns if the ROB is empty. */ + bool isEmpty() + { return numInstsInROB == 0; } + + /** Returns if a specific thread's partition is empty. */ + bool isEmpty(unsigned tid) + { return threadEntries[tid] == 0; } + + /** Executes the squash, marking squashed instructions. */ + void doSquash(unsigned tid); + + /** Squashes all instructions younger than the given sequence number for + * the specific thread. + */ + void squash(InstSeqNum squash_num, unsigned tid); + + /** Updates the head instruction with the new oldest instruction. */ + void updateHead(); + + /** Updates the tail instruction with the new youngest instruction. */ + void updateTail(); + + /** Reads the PC of the oldest head instruction. */ +// uint64_t readHeadPC(); + + /** Reads the PC of the head instruction of a specific thread. */ +// uint64_t readHeadPC(unsigned tid); + + /** Reads the next PC of the oldest head instruction. */ +// uint64_t readHeadNextPC(); + + /** Reads the next PC of the head instruction of a specific thread. */ +// uint64_t readHeadNextPC(unsigned tid); + + /** Reads the sequence number of the oldest head instruction. */ +// InstSeqNum readHeadSeqNum(); + + /** Reads the sequence number of the head instruction of a specific thread. + */ +// InstSeqNum readHeadSeqNum(unsigned tid); + + /** Reads the PC of the youngest tail instruction. */ +// uint64_t readTailPC(); + + /** Reads the PC of the tail instruction of a specific thread. */ +// uint64_t readTailPC(unsigned tid); + + /** Reads the sequence number of the youngest tail instruction. */ +// InstSeqNum readTailSeqNum(); + + /** Reads the sequence number of tail instruction of a specific thread. */ +// InstSeqNum readTailSeqNum(unsigned tid); + + /** Checks if the ROB is still in the process of squashing instructions. + * @retval Whether or not the ROB is done squashing. + */ + bool isDoneSquashing(unsigned tid) const + { return doneSquashing[tid]; } + + /** Checks if the ROB is still in the process of squashing instructions for + * any thread. + */ + bool isDoneSquashing(); + + /** This is more of a debugging function than anything. Use + * numInstsInROB to get the instructions in the ROB unless you are + * double checking that variable. + */ + int countInsts(); + + /** This is more of a debugging function than anything. Use + * threadEntries to get the instructions in the ROB unless you are + * double checking that variable. + */ + int countInsts(unsigned tid); + + private: + /** Pointer to the CPU. */ + O3CPU *cpu; + + /** Active Threads in CPU */ + std::list<unsigned>* activeThreads; + + /** Number of instructions in the ROB. */ + unsigned numEntries; + + /** Entries Per Thread */ + unsigned threadEntries[Impl::MaxThreads]; + + /** Max Insts a Thread Can Have in the ROB */ + unsigned maxEntries[Impl::MaxThreads]; + + /** ROB List of Instructions */ + std::list<DynInstPtr> instList[Impl::MaxThreads]; + + /** Number of instructions that can be squashed in a single cycle. */ + unsigned squashWidth; + + public: + /** Iterator pointing to the instruction which is the last instruction + * in the ROB. This may at times be invalid (ie when the ROB is empty), + * however it should never be incorrect. + */ + InstIt tail; + + /** Iterator pointing to the instruction which is the first instruction in + * in the ROB*/ + InstIt head; + + private: + /** Iterator used for walking through the list of instructions when + * squashing. Used so that there is persistent state between cycles; + * when squashing, the instructions are marked as squashed but not + * immediately removed, meaning the tail iterator remains the same before + * and after a squash. + * This will always be set to cpu->instList.end() if it is invalid. + */ + InstIt squashIt[Impl::MaxThreads]; + + public: + /** Number of instructions in the ROB. */ + int numInstsInROB; + + /** Dummy instruction returned if there are no insts left. */ + DynInstPtr dummyInst; + + private: + /** The sequence number of the squashed instruction. */ + InstSeqNum squashedSeqNum[Impl::MaxThreads]; + + /** Is the ROB done squashing. */ + bool doneSquashing[Impl::MaxThreads]; + + /** Number of active threads. */ + unsigned numThreads; +}; + +#endif //__CPU_O3_ROB_HH__ diff --git a/src/cpu/o3/rob_impl.hh b/src/cpu/o3/rob_impl.hh new file mode 100644 index 000000000..fab114a74 --- /dev/null +++ b/src/cpu/o3/rob_impl.hh @@ -0,0 +1,696 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "config/full_system.hh" +#include "cpu/o3/rob.hh" + +#include <list> + +template <class Impl> +ROB<Impl>::ROB(unsigned _numEntries, unsigned _squashWidth, + std::string _smtROBPolicy, unsigned _smtROBThreshold, + unsigned _numThreads) + : numEntries(_numEntries), + squashWidth(_squashWidth), + numInstsInROB(0), + numThreads(_numThreads) +{ + for (int tid=0; tid < numThreads; tid++) { + squashedSeqNum[tid] = 0; + doneSquashing[tid] = true; + threadEntries[tid] = 0; + } + + std::string policy = _smtROBPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Figure out rob policy + if (policy == "dynamic") { + robPolicy = Dynamic; + + //Set Max Entries to Total ROB Capacity + for (int i = 0; i < numThreads; i++) { + maxEntries[i]=numEntries; + } + + } else if (policy == "partitioned") { + robPolicy = Partitioned; + DPRINTF(Fetch, "ROB sharing policy set to Partitioned\n"); + + //@todo:make work if part_amt doesnt divide evenly. + int part_amt = numEntries / numThreads; + + //Divide ROB up evenly + for (int i = 0; i < numThreads; i++) { + maxEntries[i]=part_amt; + } + + } else if (policy == "threshold") { + robPolicy = Threshold; + DPRINTF(Fetch, "ROB sharing policy set to Threshold\n"); + + int threshold = _smtROBThreshold;; + + //Divide up by threshold amount + for (int i = 0; i < numThreads; i++) { + maxEntries[i]=threshold; + } + } else { + assert(0 && "Invalid ROB Sharing Policy.Options Are:{Dynamic," + "Partitioned, Threshold}"); + } +} + +template <class Impl> +std::string +ROB<Impl>::name() const +{ + return cpu->name() + ".rob"; +} + +template <class Impl> +void +ROB<Impl>::setCPU(O3CPU *cpu_ptr) +{ + cpu = cpu_ptr; + + // Set the per-thread iterators to the end of the instruction list. + for (int i=0; i < numThreads;i++) { + squashIt[i] = instList[i].end(); + } + + // Initialize the "universal" ROB head & tail point to invalid + // pointers + head = instList[0].end(); + tail = instList[0].end(); +} + +template <class Impl> +void +ROB<Impl>::setActiveThreads(std::list<unsigned> *at_ptr) +{ + DPRINTF(ROB, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} + +template <class Impl> +void +ROB<Impl>::switchOut() +{ + for (int tid = 0; tid < numThreads; tid++) { + instList[tid].clear(); + } +} + +template <class Impl> +void +ROB<Impl>::takeOverFrom() +{ + for (int tid=0; tid < numThreads; tid++) { + doneSquashing[tid] = true; + threadEntries[tid] = 0; + squashIt[tid] = instList[tid].end(); + } + numInstsInROB = 0; + + // Initialize the "universal" ROB head & tail point to invalid + // pointers + head = instList[0].end(); + tail = instList[0].end(); +} + +template <class Impl> +void +ROB<Impl>::resetEntries() +{ + if (robPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + std::list<unsigned>::iterator list_end = (*activeThreads).end(); + + while (threads != list_end) { + if (robPolicy == Partitioned) { + maxEntries[*threads++] = numEntries / active_threads; + } else if (robPolicy == Threshold && active_threads == 1) { + maxEntries[*threads++] = numEntries; + } + } + } +} + +template <class Impl> +int +ROB<Impl>::entryAmount(int num_threads) +{ + if (robPolicy == Partitioned) { + return numEntries / num_threads; + } else { + return 0; + } +} + +template <class Impl> +int +ROB<Impl>::countInsts() +{ + int total=0; + + for (int i=0;i < numThreads;i++) + total += countInsts(i); + + return total; +} + +template <class Impl> +int +ROB<Impl>::countInsts(unsigned tid) +{ + return instList[tid].size(); +} + +template <class Impl> +void +ROB<Impl>::insertInst(DynInstPtr &inst) +{ + //assert(numInstsInROB == countInsts()); + assert(inst); + + DPRINTF(ROB, "Adding inst PC %#x to the ROB.\n", inst->readPC()); + + assert(numInstsInROB != numEntries); + + int tid = inst->threadNumber; + + instList[tid].push_back(inst); + + //Set Up head iterator if this is the 1st instruction in the ROB + if (numInstsInROB == 0) { + head = instList[tid].begin(); + assert((*head) == inst); + } + + //Must Decrement for iterator to actually be valid since __.end() + //actually points to 1 after the last inst + tail = instList[tid].end(); + tail--; + + inst->setInROB(); + + ++numInstsInROB; + ++threadEntries[tid]; + + assert((*tail) == inst); + + DPRINTF(ROB, "[tid:%i] Now has %d instructions.\n", tid, threadEntries[tid]); +} + +// Whatever calls this function needs to ensure that it properly frees up +// registers prior to this function. +/* +template <class Impl> +void +ROB<Impl>::retireHead() +{ + //assert(numInstsInROB == countInsts()); + assert(numInstsInROB > 0); + + int tid = (*head)->threadNumber; + + retireHead(tid); + + if (numInstsInROB == 0) { + tail = instList[tid].end(); + } +} +*/ + +template <class Impl> +void +ROB<Impl>::retireHead(unsigned tid) +{ + //assert(numInstsInROB == countInsts()); + assert(numInstsInROB > 0); + + // Get the head ROB instruction. + InstIt head_it = instList[tid].begin(); + + DynInstPtr head_inst = (*head_it); + + assert(head_inst->readyToCommit()); + + DPRINTF(ROB, "[tid:%u]: Retiring head instruction, " + "instruction PC %#x,[sn:%lli]\n", tid, head_inst->readPC(), + head_inst->seqNum); + + --numInstsInROB; + --threadEntries[tid]; + + head_inst->clearInROB(); + head_inst->setCommitted(); + + instList[tid].erase(head_it); + + //Update "Global" Head of ROB + updateHead(); + + // @todo: A special case is needed if the instruction being + // retired is the only instruction in the ROB; otherwise the tail + // iterator will become invalidated. + cpu->removeFrontInst(head_inst); +} +/* +template <class Impl> +bool +ROB<Impl>::isHeadReady() +{ + if (numInstsInROB != 0) { + return (*head)->readyToCommit(); + } + + return false; +} +*/ +template <class Impl> +bool +ROB<Impl>::isHeadReady(unsigned tid) +{ + if (threadEntries[tid] != 0) { + return instList[tid].front()->readyToCommit(); + } + + return false; +} + +template <class Impl> +bool +ROB<Impl>::canCommit() +{ + //@todo: set ActiveThreads through ROB or CPU + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (isHeadReady(tid)) { + return true; + } + } + + return false; +} + +template <class Impl> +unsigned +ROB<Impl>::numFreeEntries() +{ + //assert(numInstsInROB == countInsts()); + + return numEntries - numInstsInROB; +} + +template <class Impl> +unsigned +ROB<Impl>::numFreeEntries(unsigned tid) +{ + return maxEntries[tid] - threadEntries[tid]; +} + +template <class Impl> +void +ROB<Impl>::doSquash(unsigned tid) +{ + DPRINTF(ROB, "[tid:%u]: Squashing instructions until [sn:%i].\n", + tid, squashedSeqNum[tid]); + + assert(squashIt[tid] != instList[tid].end()); + + if ((*squashIt[tid])->seqNum < squashedSeqNum[tid]) { + DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n", + tid); + + squashIt[tid] = instList[tid].end(); + + doneSquashing[tid] = true; + return; + } + + bool robTailUpdate = false; + + for (int numSquashed = 0; + numSquashed < squashWidth && + squashIt[tid] != instList[tid].end() && + (*squashIt[tid])->seqNum > squashedSeqNum[tid]; + ++numSquashed) + { + DPRINTF(ROB, "[tid:%u]: Squashing instruction PC %#x, seq num %i.\n", + (*squashIt[tid])->threadNumber, + (*squashIt[tid])->readPC(), + (*squashIt[tid])->seqNum); + + // Mark the instruction as squashed, and ready to commit so that + // it can drain out of the pipeline. + (*squashIt[tid])->setSquashed(); + + (*squashIt[tid])->setCanCommit(); + + + if (squashIt[tid] == instList[tid].begin()) { + DPRINTF(ROB, "Reached head of instruction list while " + "squashing.\n"); + + squashIt[tid] = instList[tid].end(); + + doneSquashing[tid] = true; + + return; + } + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + if ((*squashIt[tid]) == (*tail_thread)) + robTailUpdate = true; + + squashIt[tid]--; + } + + + // Check if ROB is done squashing. + if ((*squashIt[tid])->seqNum <= squashedSeqNum[tid]) { + DPRINTF(ROB, "[tid:%u]: Done squashing instructions.\n", + tid); + + squashIt[tid] = instList[tid].end(); + + doneSquashing[tid] = true; + } + + if (robTailUpdate) { + updateTail(); + } +} + + +template <class Impl> +void +ROB<Impl>::updateHead() +{ + DynInstPtr head_inst; + InstSeqNum lowest_num = 0; + bool first_valid = true; + + // @todo: set ActiveThreads through ROB or CPU + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned thread_num = *threads++; + + if (instList[thread_num].empty()) + continue; + + if (first_valid) { + head = instList[thread_num].begin(); + lowest_num = (*head)->seqNum; + first_valid = false; + continue; + } + + InstIt head_thread = instList[thread_num].begin(); + + DynInstPtr head_inst = (*head_thread); + + assert(head_inst != 0); + + if (head_inst->seqNum < lowest_num) { + head = head_thread; + lowest_num = head_inst->seqNum; + } + } + + if (first_valid) { + head = instList[0].end(); + } + +} + +template <class Impl> +void +ROB<Impl>::updateTail() +{ + tail = instList[0].end(); + bool first_valid = true; + + std::list<unsigned>::iterator threads = (*activeThreads).begin(); + + while (threads != (*activeThreads).end()) { + unsigned tid = *threads++; + + if (instList[tid].empty()) { + continue; + } + + // If this is the first valid then assign w/out + // comparison + if (first_valid) { + tail = instList[tid].end(); + tail--; + first_valid = false; + continue; + } + + // Assign new tail if this thread's tail is younger + // than our current "tail high" + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + if ((*tail_thread)->seqNum > (*tail)->seqNum) { + tail = tail_thread; + } + } +} + + +template <class Impl> +void +ROB<Impl>::squash(InstSeqNum squash_num,unsigned tid) +{ + if (isEmpty()) { + DPRINTF(ROB, "Does not need to squash due to being empty " + "[sn:%i]\n", + squash_num); + + return; + } + + DPRINTF(ROB, "Starting to squash within the ROB.\n"); + + robStatus[tid] = ROBSquashing; + + doneSquashing[tid] = false; + + squashedSeqNum[tid] = squash_num; + + if (!instList[tid].empty()) { + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + squashIt[tid] = tail_thread; + + doSquash(tid); + } +} +/* +template <class Impl> +typename Impl::DynInstPtr +ROB<Impl>::readHeadInst() +{ + if (numInstsInROB != 0) { + assert((*head)->isInROB()==true); + return *head; + } else { + return dummyInst; + } +} +*/ + +template <class Impl> +typename Impl::DynInstPtr +ROB<Impl>::readHeadInst(unsigned tid) +{ + if (threadEntries[tid] != 0) { + InstIt head_thread = instList[tid].begin(); + + assert((*head_thread)->isInROB()==true); + + return *head_thread; + } else { + return dummyInst; + } +} + +/* +template <class Impl> +uint64_t +ROB<Impl>::readHeadPC() +{ + //assert(numInstsInROB == countInsts()); + + DynInstPtr head_inst = *head; + + return head_inst->readPC(); +} + +template <class Impl> +uint64_t +ROB<Impl>::readHeadPC(unsigned tid) +{ + //assert(numInstsInROB == countInsts()); + InstIt head_thread = instList[tid].begin(); + + return (*head_thread)->readPC(); +} + + +template <class Impl> +uint64_t +ROB<Impl>::readHeadNextPC() +{ + //assert(numInstsInROB == countInsts()); + + DynInstPtr head_inst = *head; + + return head_inst->readNextPC(); +} + +template <class Impl> +uint64_t +ROB<Impl>::readHeadNextPC(unsigned tid) +{ + //assert(numInstsInROB == countInsts()); + InstIt head_thread = instList[tid].begin(); + + return (*head_thread)->readNextPC(); +} + +template <class Impl> +InstSeqNum +ROB<Impl>::readHeadSeqNum() +{ + //assert(numInstsInROB == countInsts()); + DynInstPtr head_inst = *head; + + return head_inst->seqNum; +} + +template <class Impl> +InstSeqNum +ROB<Impl>::readHeadSeqNum(unsigned tid) +{ + InstIt head_thread = instList[tid].begin(); + + return ((*head_thread)->seqNum); +} + +template <class Impl> +typename Impl::DynInstPtr +ROB<Impl>::readTailInst() +{ + //assert(numInstsInROB == countInsts()); + //assert(tail != instList[0].end()); + + return (*tail); +} +*/ +template <class Impl> +typename Impl::DynInstPtr +ROB<Impl>::readTailInst(unsigned tid) +{ + //assert(tail_thread[tid] != instList[tid].end()); + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + return *tail_thread; +} + +/* +template <class Impl> +uint64_t +ROB<Impl>::readTailPC() +{ + //assert(numInstsInROB == countInsts()); + + //assert(tail != instList[0].end()); + + return (*tail)->readPC(); +} + +template <class Impl> +uint64_t +ROB<Impl>::readTailPC(unsigned tid) +{ + //assert(tail_thread[tid] != instList[tid].end()); + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + return (*tail_thread)->readPC(); +} + +template <class Impl> +InstSeqNum +ROB<Impl>::readTailSeqNum() +{ + // Return the last sequence number that has not been squashed. Other + // stages can use it to squash any instructions younger than the current + // tail. + return (*tail)->seqNum; +} + +template <class Impl> +InstSeqNum +ROB<Impl>::readTailSeqNum(unsigned tid) +{ + // Return the last sequence number that has not been squashed. Other + // stages can use it to squash any instructions younger than the current + // tail. + // assert(tail_thread[tid] != instList[tid].end()); + + InstIt tail_thread = instList[tid].end(); + tail_thread--; + + return (*tail_thread)->seqNum; +} +*/ diff --git a/src/cpu/o3/sat_counter.cc b/src/cpu/o3/sat_counter.cc new file mode 100644 index 000000000..68d3ef627 --- /dev/null +++ b/src/cpu/o3/sat_counter.cc @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "base/misc.hh" +#include "cpu/o3/sat_counter.hh" + +SatCounter::SatCounter() + : initialVal(0), counter(0) +{ +} + +SatCounter::SatCounter(unsigned bits) + : initialVal(0), maxVal((1 << bits) - 1), counter(0) +{ +} + +SatCounter::SatCounter(unsigned bits, uint8_t initial_val) + : initialVal(initialVal), maxVal((1 << bits) - 1), counter(initial_val) +{ + // Check to make sure initial value doesn't exceed the max counter value. + if (initial_val > maxVal) { + fatal("BP: Initial counter value exceeds max size."); + } +} + +void +SatCounter::setBits(unsigned bits) +{ + maxVal = (1 << bits) - 1; +} diff --git a/src/cpu/o3/sat_counter.hh b/src/cpu/o3/sat_counter.hh new file mode 100644 index 000000000..7e15119b0 --- /dev/null +++ b/src/cpu/o3/sat_counter.hh @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_SAT_COUNTER_HH__ +#define __CPU_O3_SAT_COUNTER_HH__ + +#include "base/misc.hh" +#include "sim/host.hh" + +/** + * Private counter class for the internal saturating counters. + * Implements an n bit saturating counter and provides methods to + * increment, decrement, and read it. + * @todo Consider making this something that more closely mimics a + * built in class so you can use ++ or --. + */ +class SatCounter +{ + public: + /** + * Constructor for the counter. + */ + SatCounter() + : initialVal(0), counter(0) + { } + + /** + * Constructor for the counter. + * @param bits How many bits the counter will have. + */ + SatCounter(unsigned bits) + : initialVal(0), maxVal((1 << bits) - 1), counter(0) + { } + + /** + * Constructor for the counter. + * @param bits How many bits the counter will have. + * @param initial_val Starting value for each counter. + */ + SatCounter(unsigned bits, uint8_t initial_val) + : initialVal(initialVal), maxVal((1 << bits) - 1), counter(initial_val) + { + // Check to make sure initial value doesn't exceed the max + // counter value. + if (initial_val > maxVal) { + fatal("BP: Initial counter value exceeds max size."); + } + } + + /** + * Sets the number of bits. + */ + void setBits(unsigned bits) { maxVal = (1 << bits) - 1; } + + void reset() { counter = initialVal; } + + /** + * Increments the counter's current value. + */ + void increment() + { + if (counter < maxVal) { + ++counter; + } + } + + /** + * Decrements the counter's current value. + */ + void decrement() + { + if (counter > 0) { + --counter; + } + } + + /** + * Read the counter's value. + */ + const uint8_t read() const + { return counter; } + + private: + uint8_t initialVal; + uint8_t maxVal; + uint8_t counter; +}; + +#endif // __CPU_O3_SAT_COUNTER_HH__ diff --git a/src/cpu/o3/scoreboard.cc b/src/cpu/o3/scoreboard.cc new file mode 100644 index 000000000..1859b35a4 --- /dev/null +++ b/src/cpu/o3/scoreboard.cc @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + * Kevin Lim + */ + +#include "cpu/o3/scoreboard.hh" + +Scoreboard::Scoreboard(unsigned activeThreads, + unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs, + unsigned _numMiscRegs, + unsigned _zeroRegIdx) + : numLogicalIntRegs(_numLogicalIntRegs), + numPhysicalIntRegs(_numPhysicalIntRegs), + numLogicalFloatRegs(_numLogicalFloatRegs), + numPhysicalFloatRegs(_numPhysicalFloatRegs), + numMiscRegs(_numMiscRegs), + zeroRegIdx(_zeroRegIdx) +{ + //Get Register Sizes + numLogicalRegs = numLogicalIntRegs + numLogicalFloatRegs; + numPhysicalRegs = numPhysicalIntRegs + numPhysicalFloatRegs; + + //Resize scoreboard appropriately + regScoreBoard.resize(numPhysicalRegs + (numMiscRegs * activeThreads)); + + //Initialize values + for (int i=0; i < numLogicalIntRegs * activeThreads; i++) { + regScoreBoard[i] = 1; + } + + for (int i= numPhysicalIntRegs; + i < numPhysicalIntRegs + (numLogicalFloatRegs * activeThreads); + i++) { + regScoreBoard[i] = 1; + } + + for (int i = numPhysicalRegs; + i < numPhysicalRegs + (numMiscRegs * activeThreads); + i++) { + regScoreBoard[i] = 1; + } +} + +std::string +Scoreboard::name() const +{ + return "cpu.scoreboard"; +} + +bool +Scoreboard::getReg(PhysRegIndex phys_reg) +{ + // Always ready if int or fp zero reg. + if (phys_reg == zeroRegIdx || + phys_reg == (zeroRegIdx + numPhysicalIntRegs)) { + return 1; + } + + return regScoreBoard[phys_reg]; +} + +void +Scoreboard::setReg(PhysRegIndex phys_reg) +{ + DPRINTF(Scoreboard, "Setting reg %i as ready\n", phys_reg); + + regScoreBoard[phys_reg] = 1; +} + +void +Scoreboard::unsetReg(PhysRegIndex ready_reg) +{ + if (ready_reg == zeroRegIdx || + ready_reg == (zeroRegIdx + numPhysicalIntRegs)) { + // Don't do anything if int or fp zero reg. + return; + } + + regScoreBoard[ready_reg] = 0; +} diff --git a/src/cpu/o3/scoreboard.hh b/src/cpu/o3/scoreboard.hh new file mode 100644 index 000000000..eefff1d8b --- /dev/null +++ b/src/cpu/o3/scoreboard.hh @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Korey Sewell + * Kevin Lim + */ + +#ifndef __CPU_O3_SCOREBOARD_HH__ +#define __CPU_O3_SCOREBOARD_HH__ + +#include <iostream> +#include <utility> +#include <vector> +#include "base/trace.hh" +#include "base/traceflags.hh" +#include "cpu/o3/comm.hh" + +/** + * Implements a simple scoreboard to track which registers are ready. + * This class assumes that the fp registers start, index wise, right after + * the integer registers. The misc. registers start, index wise, right after + * the fp registers. + * @todo: Fix up handling of the zero register in case the decoder does not + * automatically make insts that write the zero register into nops. + */ +class Scoreboard +{ + public: + /** Constructs a scoreboard. + * @param activeThreads The number of active threads. + * @param _numLogicalIntRegs Number of logical integer registers. + * @param _numPhysicalIntRegs Number of physical integer registers. + * @param _numLogicalFloatRegs Number of logical fp registers. + * @param _numPhysicalFloatRegs Number of physical fp registers. + * @param _numMiscRegs Number of miscellaneous registers. + * @param _zeroRegIdx Index of the zero register. + */ + Scoreboard(unsigned activeThreads, + unsigned _numLogicalIntRegs, + unsigned _numPhysicalIntRegs, + unsigned _numLogicalFloatRegs, + unsigned _numPhysicalFloatRegs, + unsigned _numMiscRegs, + unsigned _zeroRegIdx); + + /** Destructor. */ + ~Scoreboard() {} + + /** Returns the name of the scoreboard. */ + std::string name() const; + + /** Checks if the register is ready. */ + bool getReg(PhysRegIndex ready_reg); + + /** Sets the register as ready. */ + void setReg(PhysRegIndex phys_reg); + + /** Sets the register as not ready. */ + void unsetReg(PhysRegIndex ready_reg); + + private: + /** Scoreboard of physical integer registers, saying whether or not they + * are ready. + */ + std::vector<bool> regScoreBoard; + + /** Number of logical integer registers. */ + int numLogicalIntRegs; + + /** Number of physical integer registers. */ + int numPhysicalIntRegs; + + /** Number of logical floating point registers. */ + int numLogicalFloatRegs; + + /** Number of physical floating point registers. */ + int numPhysicalFloatRegs; + + /** Number of miscellaneous registers. */ + int numMiscRegs; + + /** Number of logical integer + float registers. */ + int numLogicalRegs; + + /** Number of physical integer + float registers. */ + int numPhysicalRegs; + + /** The logical index of the zero register. */ + int zeroRegIdx; +}; + +#endif diff --git a/src/cpu/o3/store_set.cc b/src/cpu/o3/store_set.cc new file mode 100644 index 000000000..2d28b617f --- /dev/null +++ b/src/cpu/o3/store_set.cc @@ -0,0 +1,348 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "base/intmath.hh" +#include "base/misc.hh" +#include "base/trace.hh" +#include "cpu/o3/store_set.hh" + +StoreSet::StoreSet(int _SSIT_size, int _LFST_size) + : SSITSize(_SSIT_size), LFSTSize(_LFST_size) +{ + DPRINTF(StoreSet, "StoreSet: Creating store set object.\n"); + DPRINTF(StoreSet, "StoreSet: SSIT size: %i, LFST size: %i.\n", + SSITSize, LFSTSize); + + if (!isPowerOf2(SSITSize)) { + fatal("Invalid SSIT size!\n"); + } + + SSIT.resize(SSITSize); + + validSSIT.resize(SSITSize); + + for (int i = 0; i < SSITSize; ++i) + validSSIT[i] = false; + + if (!isPowerOf2(LFSTSize)) { + fatal("Invalid LFST size!\n"); + } + + LFST.resize(LFSTSize); + + validLFST.resize(LFSTSize); + + for (int i = 0; i < LFSTSize; ++i) { + validLFST[i] = false; + LFST[i] = 0; + } + + indexMask = SSITSize - 1; + + offsetBits = 2; +} + +StoreSet::~StoreSet() +{ +} + +void +StoreSet::init(int _SSIT_size, int _LFST_size) +{ + SSITSize = _SSIT_size; + LFSTSize = _LFST_size; + + DPRINTF(StoreSet, "StoreSet: Creating store set object.\n"); + DPRINTF(StoreSet, "StoreSet: SSIT size: %i, LFST size: %i.\n", + SSITSize, LFSTSize); + + SSIT.resize(SSITSize); + + validSSIT.resize(SSITSize); + + for (int i = 0; i < SSITSize; ++i) + validSSIT[i] = false; + + LFST.resize(LFSTSize); + + validLFST.resize(LFSTSize); + + for (int i = 0; i < LFSTSize; ++i) { + validLFST[i] = false; + LFST[i] = 0; + } + + indexMask = SSITSize - 1; + + offsetBits = 2; +} + + +void +StoreSet::violation(Addr store_PC, Addr load_PC) +{ + int load_index = calcIndex(load_PC); + int store_index = calcIndex(store_PC); + + assert(load_index < SSITSize && store_index < SSITSize); + + bool valid_load_SSID = validSSIT[load_index]; + bool valid_store_SSID = validSSIT[store_index]; + + if (!valid_load_SSID && !valid_store_SSID) { + // Calculate a new SSID here. + SSID new_set = calcSSID(load_PC); + + validSSIT[load_index] = true; + + SSIT[load_index] = new_set; + + validSSIT[store_index] = true; + + SSIT[store_index] = new_set; + + assert(new_set < LFSTSize); + + DPRINTF(StoreSet, "StoreSet: Neither load nor store had a valid " + "storeset, creating a new one: %i for load %#x, store %#x\n", + new_set, load_PC, store_PC); + } else if (valid_load_SSID && !valid_store_SSID) { + SSID load_SSID = SSIT[load_index]; + + validSSIT[store_index] = true; + + SSIT[store_index] = load_SSID; + + assert(load_SSID < LFSTSize); + + DPRINTF(StoreSet, "StoreSet: Load had a valid store set. Adding " + "store to that set: %i for load %#x, store %#x\n", + load_SSID, load_PC, store_PC); + } else if (!valid_load_SSID && valid_store_SSID) { + SSID store_SSID = SSIT[store_index]; + + validSSIT[load_index] = true; + + SSIT[load_index] = store_SSID; + + DPRINTF(StoreSet, "StoreSet: Store had a valid store set: %i for " + "load %#x, store %#x\n", + store_SSID, load_PC, store_PC); + } else { + SSID load_SSID = SSIT[load_index]; + SSID store_SSID = SSIT[store_index]; + + assert(load_SSID < LFSTSize && store_SSID < LFSTSize); + + // The store set with the lower number wins + if (store_SSID > load_SSID) { + SSIT[store_index] = load_SSID; + + DPRINTF(StoreSet, "StoreSet: Load had smaller store set: %i; " + "for load %#x, store %#x\n", + load_SSID, load_PC, store_PC); + } else { + SSIT[load_index] = store_SSID; + + DPRINTF(StoreSet, "StoreSet: Store had smaller store set: %i; " + "for load %#x, store %#x\n", + store_SSID, load_PC, store_PC); + } + } +} + +void +StoreSet::insertLoad(Addr load_PC, InstSeqNum load_seq_num) +{ + // Does nothing. + return; +} + +void +StoreSet::insertStore(Addr store_PC, InstSeqNum store_seq_num, + unsigned tid) +{ + int index = calcIndex(store_PC); + + int store_SSID; + + assert(index < SSITSize); + + if (!validSSIT[index]) { + // Do nothing if there's no valid entry. + return; + } else { + store_SSID = SSIT[index]; + + assert(store_SSID < LFSTSize); + + // Update the last store that was fetched with the current one. + LFST[store_SSID] = store_seq_num; + + validLFST[store_SSID] = 1; + + storeList[store_seq_num] = store_SSID; + + DPRINTF(StoreSet, "Store %#x updated the LFST, SSID: %i\n", + store_PC, store_SSID); + } +} + +InstSeqNum +StoreSet::checkInst(Addr PC) +{ + int index = calcIndex(PC); + + int inst_SSID; + + assert(index < SSITSize); + + if (!validSSIT[index]) { + DPRINTF(StoreSet, "Inst %#x with index %i had no SSID\n", + PC, index); + + // Return 0 if there's no valid entry. + return 0; + } else { + inst_SSID = SSIT[index]; + + assert(inst_SSID < LFSTSize); + + if (!validLFST[inst_SSID]) { + + DPRINTF(StoreSet, "Inst %#x with index %i and SSID %i had no " + "dependency\n", PC, index, inst_SSID); + + return 0; + } else { + DPRINTF(StoreSet, "Inst %#x with index %i and SSID %i had LFST " + "inum of %i\n", PC, index, inst_SSID, LFST[inst_SSID]); + + return LFST[inst_SSID]; + } + } +} + +void +StoreSet::issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store) +{ + // This only is updated upon a store being issued. + if (!is_store) { + return; + } + + int index = calcIndex(issued_PC); + + int store_SSID; + + assert(index < SSITSize); + + SeqNumMapIt store_list_it = storeList.find(issued_seq_num); + + if (store_list_it != storeList.end()) { + storeList.erase(store_list_it); + } + + // Make sure the SSIT still has a valid entry for the issued store. + if (!validSSIT[index]) { + return; + } + + store_SSID = SSIT[index]; + + assert(store_SSID < LFSTSize); + + // If the last fetched store in the store set refers to the store that + // was just issued, then invalidate the entry. + if (validLFST[store_SSID] && LFST[store_SSID] == issued_seq_num) { + DPRINTF(StoreSet, "StoreSet: store invalidated itself in LFST.\n"); + validLFST[store_SSID] = false; + } +} + +void +StoreSet::squash(InstSeqNum squashed_num, unsigned tid) +{ + DPRINTF(StoreSet, "StoreSet: Squashing until inum %i\n", + squashed_num); + + int idx; + SeqNumMapIt store_list_it = storeList.begin(); + + //@todo:Fix to only delete from correct thread + while (!storeList.empty()) { + idx = (*store_list_it).second; + + if ((*store_list_it).first <= squashed_num) { + break; + } + + bool younger = LFST[idx] > squashed_num; + + if (validLFST[idx] && younger) { + DPRINTF(StoreSet, "Squashed [sn:%lli]\n", LFST[idx]); + validLFST[idx] = false; + + storeList.erase(store_list_it++); + } else if (!validLFST[idx] && younger) { + storeList.erase(store_list_it++); + } + } +} + +void +StoreSet::clear() +{ + for (int i = 0; i < SSITSize; ++i) { + validSSIT[i] = false; + } + + for (int i = 0; i < LFSTSize; ++i) { + validLFST[i] = false; + } + + storeList.clear(); +} + +void +StoreSet::dump() +{ + cprintf("storeList.size(): %i\n", storeList.size()); + SeqNumMapIt store_list_it = storeList.begin(); + + int num = 0; + + while (store_list_it != storeList.end()) { + cprintf("%i: [sn:%lli] SSID:%i\n", + num, (*store_list_it).first, (*store_list_it).second); + num++; + store_list_it++; + } +} diff --git a/src/cpu/o3/store_set.hh b/src/cpu/o3/store_set.hh new file mode 100644 index 000000000..f9f7637d0 --- /dev/null +++ b/src/cpu/o3/store_set.hh @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_STORE_SET_HH__ +#define __CPU_O3_STORE_SET_HH__ + +#include <list> +#include <map> +#include <utility> +#include <vector> + +#include "cpu/inst_seq.hh" +#include "sim/host.hh" + +struct ltseqnum { + bool operator()(const InstSeqNum &lhs, const InstSeqNum &rhs) const + { + return lhs > rhs; + } +}; + +/** + * Implements a store set predictor for determining if memory + * instructions are dependent upon each other. See paper "Memory + * Dependence Prediction using Store Sets" by Chrysos and Emer. SSID + * stands for Store Set ID, SSIT stands for Store Set ID Table, and + * LFST is Last Fetched Store Table. + */ +class StoreSet +{ + public: + typedef unsigned SSID; + + public: + /** Default constructor. init() must be called prior to use. */ + StoreSet() { }; + + /** Creates store set predictor with given table sizes. */ + StoreSet(int SSIT_size, int LFST_size); + + /** Default destructor. */ + ~StoreSet(); + + /** Initializes the store set predictor with the given table sizes. */ + void init(int SSIT_size, int LFST_size); + + /** Records a memory ordering violation between the younger load + * and the older store. */ + void violation(Addr store_PC, Addr load_PC); + + /** Inserts a load into the store set predictor. This does nothing but + * is included in case other predictors require a similar function. + */ + void insertLoad(Addr load_PC, InstSeqNum load_seq_num); + + /** Inserts a store into the store set predictor. Updates the + * LFST if the store has a valid SSID. */ + void insertStore(Addr store_PC, InstSeqNum store_seq_num, + unsigned tid); + + /** Checks if the instruction with the given PC is dependent upon + * any store. @return Returns the sequence number of the store + * instruction this PC is dependent upon. Returns 0 if none. + */ + InstSeqNum checkInst(Addr PC); + + /** Records this PC/sequence number as issued. */ + void issued(Addr issued_PC, InstSeqNum issued_seq_num, bool is_store); + + /** Squashes for a specific thread until the given sequence number. */ + void squash(InstSeqNum squashed_num, unsigned tid); + + /** Resets all tables. */ + void clear(); + + /** Debug function to dump the contents of the store list. */ + void dump(); + + private: + /** Calculates the index into the SSIT based on the PC. */ + inline int calcIndex(Addr PC) + { return (PC >> offsetBits) & indexMask; } + + /** Calculates a Store Set ID based on the PC. */ + inline SSID calcSSID(Addr PC) + { return ((PC ^ (PC >> 10)) % LFSTSize); } + + /** The Store Set ID Table. */ + std::vector<SSID> SSIT; + + /** Bit vector to tell if the SSIT has a valid entry. */ + std::vector<bool> validSSIT; + + /** Last Fetched Store Table. */ + std::vector<InstSeqNum> LFST; + + /** Bit vector to tell if the LFST has a valid entry. */ + std::vector<bool> validLFST; + + /** Map of stores that have been inserted into the store set, but + * not yet issued or squashed. + */ + std::map<InstSeqNum, int, ltseqnum> storeList; + + typedef std::map<InstSeqNum, int, ltseqnum>::iterator SeqNumMapIt; + + /** Store Set ID Table size, in entries. */ + int SSITSize; + + /** Last Fetched Store Table size, in entries. */ + int LFSTSize; + + /** Mask to obtain the index. */ + int indexMask; + + // HACK: Hardcoded for now. + int offsetBits; +}; + +#endif // __CPU_O3_STORE_SET_HH__ diff --git a/src/cpu/o3/thread_context.hh b/src/cpu/o3/thread_context.hh new file mode 100755 index 000000000..9ca02b9f3 --- /dev/null +++ b/src/cpu/o3/thread_context.hh @@ -0,0 +1,250 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_THREAD_CONTEXT_HH__ +#define __CPU_O3_THREAD_CONTEXT_HH__ + +#include "cpu/thread_context.hh" +#include "cpu/o3/isa_specific.hh" + +class EndQuiesceEvent; +namespace Kernel { + class Statistics; +}; + +class TranslatingPort; + +/** + * Derived ThreadContext class for use with the O3CPU. It + * provides the interface for any external objects to access a + * single thread's state and some general CPU state. Any time + * external objects try to update state through this interface, + * the CPU will create an event to squash all in-flight + * instructions in order to ensure state is maintained correctly. + * It must be defined specifically for the O3CPU because + * not all architectural state is located within the O3ThreadState + * (such as the commit PC, and registers), and specific actions + * must be taken when using this interface (such as squashing all + * in-flight instructions when doing a write to this interface). + */ +template <class Impl> +class O3ThreadContext : public ThreadContext +{ + public: + typedef typename Impl::O3CPU O3CPU; + + /** Pointer to the CPU. */ + O3CPU *cpu; + + /** Pointer to the thread state that this TC corrseponds to. */ + O3ThreadState<Impl> *thread; + + /** Returns a pointer to this CPU. */ + virtual BaseCPU *getCpuPtr() { return cpu; } + + /** Sets this CPU's ID. */ + virtual void setCpuId(int id) { cpu->setCpuId(id); } + + /** Reads this CPU's ID. */ + virtual int readCpuId() { return cpu->readCpuId(); } + +#if FULL_SYSTEM + /** Returns a pointer to the system. */ + virtual System *getSystemPtr() { return cpu->system; } + + /** Returns a pointer to physical memory. */ + virtual PhysicalMemory *getPhysMemPtr() { return cpu->physmem; } + + /** Returns a pointer to this thread's kernel statistics. */ + virtual Kernel::Statistics *getKernelStats() + { return thread->kernelStats; } + + virtual FunctionalPort *getPhysPort() { return thread->getPhysPort(); } + + virtual VirtualPort *getVirtPort(ThreadContext *src_tc = NULL); + + void delVirtPort(VirtualPort *vp); +#else + virtual TranslatingPort *getMemPort() { return thread->getMemPort(); } + + /** Returns a pointer to this thread's process. */ + virtual Process *getProcessPtr() { return thread->getProcessPtr(); } +#endif + /** Returns this thread's status. */ + virtual Status status() const { return thread->status(); } + + /** Sets this thread's status. */ + virtual void setStatus(Status new_status) + { thread->setStatus(new_status); } + + /** Set the status to Active. Optional delay indicates number of + * cycles to wait before beginning execution. */ + virtual void activate(int delay = 1); + + /** Set the status to Suspended. */ + virtual void suspend(); + + /** Set the status to Unallocated. */ + virtual void deallocate(int delay = 0); + + /** Set the status to Halted. */ + virtual void halt(); + +#if FULL_SYSTEM + /** Dumps the function profiling information. + * @todo: Implement. + */ + virtual void dumpFuncProfile(); +#endif + /** Takes over execution of a thread from another CPU. */ + virtual void takeOverFrom(ThreadContext *old_context); + + /** Registers statistics associated with this TC. */ + virtual void regStats(const std::string &name); + + /** Serializes state. */ + virtual void serialize(std::ostream &os); + /** Unserializes state. */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +#if FULL_SYSTEM + /** Reads the last tick that this thread was activated on. */ + virtual Tick readLastActivate(); + /** Reads the last tick that this thread was suspended on. */ + virtual Tick readLastSuspend(); + + /** Clears the function profiling information. */ + virtual void profileClear(); + /** Samples the function profiling information. */ + virtual void profileSample(); +#endif + /** Returns this thread's ID number. */ + virtual int getThreadNum() { return thread->readTid(); } + + /** Returns the instruction this thread is currently committing. + * Only used when an instruction faults. + */ + virtual TheISA::MachInst getInst(); + + /** Copies the architectural registers from another TC into this TC. */ + virtual void copyArchRegs(ThreadContext *tc); + + /** Resets all architectural registers to 0. */ + virtual void clearArchRegs(); + + /** Reads an integer register. */ + virtual uint64_t readIntReg(int reg_idx); + + virtual FloatReg readFloatReg(int reg_idx, int width); + + virtual FloatReg readFloatReg(int reg_idx); + + virtual FloatRegBits readFloatRegBits(int reg_idx, int width); + + virtual FloatRegBits readFloatRegBits(int reg_idx); + + /** Sets an integer register to a value. */ + virtual void setIntReg(int reg_idx, uint64_t val); + + virtual void setFloatReg(int reg_idx, FloatReg val, int width); + + virtual void setFloatReg(int reg_idx, FloatReg val); + + virtual void setFloatRegBits(int reg_idx, FloatRegBits val, int width); + + virtual void setFloatRegBits(int reg_idx, FloatRegBits val); + + /** Reads this thread's PC. */ + virtual uint64_t readPC() + { return cpu->readPC(thread->readTid()); } + + /** Sets this thread's PC. */ + virtual void setPC(uint64_t val); + + /** Reads this thread's next PC. */ + virtual uint64_t readNextPC() + { return cpu->readNextPC(thread->readTid()); } + + /** Sets this thread's next PC. */ + virtual void setNextPC(uint64_t val); + + /** Reads a miscellaneous register. */ + virtual MiscReg readMiscReg(int misc_reg) + { return cpu->readMiscReg(misc_reg, thread->readTid()); } + + /** Reads a misc. register, including any side-effects the + * read might have as defined by the architecture. */ + virtual MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { return cpu->readMiscRegWithEffect(misc_reg, fault, thread->readTid()); } + + /** Sets a misc. register. */ + virtual Fault setMiscReg(int misc_reg, const MiscReg &val); + + /** Sets a misc. register, including any side-effects the + * write might have as defined by the architecture. */ + virtual Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val); + + /** Returns the number of consecutive store conditional failures. */ + // @todo: Figure out where these store cond failures should go. + virtual unsigned readStCondFailures() + { return thread->storeCondFailures; } + + /** Sets the number of consecutive store conditional failures. */ + virtual void setStCondFailures(unsigned sc_failures) + { thread->storeCondFailures = sc_failures; } + + // Only really makes sense for old CPU model. Lots of code + // outside the CPU still checks this function, so it will + // always return false to keep everything working. + /** Checks if the thread is misspeculating. Because it is + * very difficult to determine if the thread is + * misspeculating, this is set as false. */ + virtual bool misspeculating() { return false; } + +#if !FULL_SYSTEM + /** Gets a syscall argument by index. */ + virtual IntReg getSyscallArg(int i); + + /** Sets a syscall argument. */ + virtual void setSyscallArg(int i, IntReg val); + + /** Sets the syscall return value. */ + virtual void setSyscallReturn(SyscallReturn return_value); + + /** Executes a syscall in SE mode. */ + virtual void syscall(int64_t callnum) + { return cpu->syscall(callnum, thread->readTid()); } + + /** Reads the funcExeInst counter. */ + virtual Counter readFuncExeInst() { return thread->funcExeInst; } +#endif +}; + +#endif diff --git a/src/cpu/o3/thread_context_impl.hh b/src/cpu/o3/thread_context_impl.hh new file mode 100755 index 000000000..a4546e669 --- /dev/null +++ b/src/cpu/o3/thread_context_impl.hh @@ -0,0 +1,491 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Korey Sewell + */ + +#include "cpu/o3/thread_context.hh" +#include "cpu/quiesce_event.hh" + +#if FULL_SYSTEM +template <class Impl> +VirtualPort * +O3ThreadContext<Impl>::getVirtPort(ThreadContext *src_tc) +{ + if (!src_tc) + return thread->getVirtPort(); + + VirtualPort *vp; + Port *mem_port; + + vp = new VirtualPort("tc-vport", src_tc); + mem_port = cpu->system->physmem->getPort("functional"); + mem_port->setPeer(vp); + vp->setPeer(mem_port); + return vp; +} + +template <class Impl> +void +O3ThreadContext<Impl>::dumpFuncProfile() +{ + // Currently not supported +} +#endif + +template <class Impl> +void +O3ThreadContext<Impl>::takeOverFrom(ThreadContext *old_context) +{ + // some things should already be set up +#if FULL_SYSTEM + assert(getSystemPtr() == old_context->getSystemPtr()); +#else + assert(getProcessPtr() == old_context->getProcessPtr()); +#endif + + // copy over functional state + setStatus(old_context->status()); + copyArchRegs(old_context); + setCpuId(old_context->readCpuId()); + +#if !FULL_SYSTEM + thread->funcExeInst = old_context->readFuncExeInst(); +#else + EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent(); + if (other_quiesce) { + // Point the quiesce event's TC at this TC so that it wakes up + // the proper CPU. + other_quiesce->tc = this; + } + if (thread->quiesceEvent) { + thread->quiesceEvent->tc = this; + } + + // Transfer kernel stats from one CPU to the other. + thread->kernelStats = old_context->getKernelStats(); +// storeCondFailures = 0; + cpu->lockFlag = false; +#endif + + old_context->setStatus(ThreadContext::Unallocated); + + thread->inSyscall = false; + thread->trapPending = false; +} + +#if FULL_SYSTEM +template <class Impl> +void +O3ThreadContext<Impl>::delVirtPort(VirtualPort *vp) +{ + delete vp->getPeer(); + delete vp; +} +#endif + +template <class Impl> +void +O3ThreadContext<Impl>::activate(int delay) +{ + DPRINTF(O3CPU, "Calling activate on Thread Context %d\n", + getThreadNum()); + + if (thread->status() == ThreadContext::Active) + return; + +#if FULL_SYSTEM + thread->lastActivate = curTick; +#endif + + if (thread->status() == ThreadContext::Unallocated) { + cpu->activateWhenReady(thread->readTid()); + return; + } + + thread->setStatus(ThreadContext::Active); + + // status() == Suspended + cpu->activateContext(thread->readTid(), delay); +} + +template <class Impl> +void +O3ThreadContext<Impl>::suspend() +{ + DPRINTF(O3CPU, "Calling suspend on Thread Context %d\n", + getThreadNum()); + + if (thread->status() == ThreadContext::Suspended) + return; + +#if FULL_SYSTEM + thread->lastActivate = curTick; + thread->lastSuspend = curTick; +#endif +/* +#if FULL_SYSTEM + // Don't change the status from active if there are pending interrupts + if (cpu->check_interrupts()) { + assert(status() == ThreadContext::Active); + return; + } +#endif +*/ + thread->setStatus(ThreadContext::Suspended); + cpu->suspendContext(thread->readTid()); +} + +template <class Impl> +void +O3ThreadContext<Impl>::deallocate(int delay) +{ + DPRINTF(O3CPU, "Calling deallocate on Thread Context %d\n", + getThreadNum()); + + if (thread->status() == ThreadContext::Unallocated) + return; + + thread->setStatus(ThreadContext::Unallocated); + cpu->deallocateContext(thread->readTid(), delay); +} + +template <class Impl> +void +O3ThreadContext<Impl>::halt() +{ + DPRINTF(O3CPU, "Calling halt on Thread Context %d\n", + getThreadNum()); + + if (thread->status() == ThreadContext::Halted) + return; + + thread->setStatus(ThreadContext::Halted); + cpu->haltContext(thread->readTid()); +} + +template <class Impl> +void +O3ThreadContext<Impl>::regStats(const std::string &name) +{ +#if FULL_SYSTEM + thread->kernelStats = new Kernel::Statistics(cpu->system); + thread->kernelStats->regStats(name + ".kern"); +#endif +} + +template <class Impl> +void +O3ThreadContext<Impl>::serialize(std::ostream &os) +{ +#if FULL_SYSTEM + if (thread->kernelStats) + thread->kernelStats->serialize(os); +#endif + +} + +template <class Impl> +void +O3ThreadContext<Impl>::unserialize(Checkpoint *cp, const std::string §ion) +{ +#if FULL_SYSTEM + if (thread->kernelStats) + thread->kernelStats->unserialize(cp, section); +#endif + +} + +#if FULL_SYSTEM +template <class Impl> +Tick +O3ThreadContext<Impl>::readLastActivate() +{ + return thread->lastActivate; +} + +template <class Impl> +Tick +O3ThreadContext<Impl>::readLastSuspend() +{ + return thread->lastSuspend; +} + +template <class Impl> +void +O3ThreadContext<Impl>::profileClear() +{} + +template <class Impl> +void +O3ThreadContext<Impl>::profileSample() +{} +#endif + +template <class Impl> +TheISA::MachInst +O3ThreadContext<Impl>:: getInst() +{ + return thread->getInst(); +} + +template <class Impl> +void +O3ThreadContext<Impl>::copyArchRegs(ThreadContext *tc) +{ + // This function will mess things up unless the ROB is empty and + // there are no instructions in the pipeline. + unsigned tid = thread->readTid(); + PhysRegIndex renamed_reg; + + // First loop through the integer registers. + for (int i = 0; i < TheISA::NumIntRegs; ++i) { + renamed_reg = cpu->renameMap[tid].lookup(i); + + DPRINTF(O3CPU, "Copying over register %i, had data %lli, " + "now has data %lli.\n", + renamed_reg, cpu->readIntReg(renamed_reg), + tc->readIntReg(i)); + + cpu->setIntReg(renamed_reg, tc->readIntReg(i)); + } + + // Then loop through the floating point registers. + for (int i = 0; i < TheISA::NumFloatRegs; ++i) { + renamed_reg = cpu->renameMap[tid].lookup(i + TheISA::FP_Base_DepTag); + cpu->setFloatRegBits(renamed_reg, + tc->readFloatRegBits(i)); + } + + // Copy the misc regs. + TheISA::copyMiscRegs(tc, this); + + // Then finally set the PC and the next PC. + cpu->setPC(tc->readPC(), tid); + cpu->setNextPC(tc->readNextPC(), tid); +#if !FULL_SYSTEM + this->thread->funcExeInst = tc->readFuncExeInst(); +#endif +} + +template <class Impl> +void +O3ThreadContext<Impl>::clearArchRegs() +{} + +template <class Impl> +uint64_t +O3ThreadContext<Impl>::readIntReg(int reg_idx) +{ + return cpu->readArchIntReg(reg_idx, thread->readTid()); +} + +template <class Impl> +TheISA::FloatReg +O3ThreadContext<Impl>::readFloatReg(int reg_idx, int width) +{ + switch(width) { + case 32: + return cpu->readArchFloatRegSingle(reg_idx, thread->readTid()); + case 64: + return cpu->readArchFloatRegDouble(reg_idx, thread->readTid()); + default: + panic("Unsupported width!"); + return 0; + } +} + +template <class Impl> +TheISA::FloatReg +O3ThreadContext<Impl>::readFloatReg(int reg_idx) +{ + return cpu->readArchFloatRegSingle(reg_idx, thread->readTid()); +} + +template <class Impl> +TheISA::FloatRegBits +O3ThreadContext<Impl>::readFloatRegBits(int reg_idx, int width) +{ + DPRINTF(Fault, "Reading floatint register through the TC!\n"); + return cpu->readArchFloatRegInt(reg_idx, thread->readTid()); +} + +template <class Impl> +TheISA::FloatRegBits +O3ThreadContext<Impl>::readFloatRegBits(int reg_idx) +{ + return cpu->readArchFloatRegInt(reg_idx, thread->readTid()); +} + +template <class Impl> +void +O3ThreadContext<Impl>::setIntReg(int reg_idx, uint64_t val) +{ + cpu->setArchIntReg(reg_idx, val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +O3ThreadContext<Impl>::setFloatReg(int reg_idx, FloatReg val, int width) +{ + switch(width) { + case 32: + cpu->setArchFloatRegSingle(reg_idx, val, thread->readTid()); + break; + case 64: + cpu->setArchFloatRegDouble(reg_idx, val, thread->readTid()); + break; + } + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +O3ThreadContext<Impl>::setFloatReg(int reg_idx, FloatReg val) +{ + cpu->setArchFloatRegSingle(reg_idx, val, thread->readTid()); + + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +O3ThreadContext<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val, + int width) +{ + DPRINTF(Fault, "Setting floatint register through the TC!\n"); + cpu->setArchFloatRegInt(reg_idx, val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +O3ThreadContext<Impl>::setFloatRegBits(int reg_idx, FloatRegBits val) +{ + cpu->setArchFloatRegInt(reg_idx, val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +O3ThreadContext<Impl>::setPC(uint64_t val) +{ + cpu->setPC(val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +void +O3ThreadContext<Impl>::setNextPC(uint64_t val) +{ + cpu->setNextPC(val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } +} + +template <class Impl> +Fault +O3ThreadContext<Impl>::setMiscReg(int misc_reg, const MiscReg &val) +{ + Fault ret_fault = cpu->setMiscReg(misc_reg, val, thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } + + return ret_fault; +} + +template <class Impl> +Fault +O3ThreadContext<Impl>::setMiscRegWithEffect(int misc_reg, + const MiscReg &val) +{ + Fault ret_fault = cpu->setMiscRegWithEffect(misc_reg, val, + thread->readTid()); + + // Squash if we're not already in a state update mode. + if (!thread->trapPending && !thread->inSyscall) { + cpu->squashFromTC(thread->readTid()); + } + + return ret_fault; +} + +#if !FULL_SYSTEM + +template <class Impl> +TheISA::IntReg +O3ThreadContext<Impl>::getSyscallArg(int i) +{ + return cpu->getSyscallArg(i, thread->readTid()); +} + +template <class Impl> +void +O3ThreadContext<Impl>::setSyscallArg(int i, IntReg val) +{ + cpu->setSyscallArg(i, val, thread->readTid()); +} + +template <class Impl> +void +O3ThreadContext<Impl>::setSyscallReturn(SyscallReturn return_value) +{ + cpu->setSyscallReturn(return_value, thread->readTid()); +} + +#endif // FULL_SYSTEM + diff --git a/src/cpu/o3/thread_state.hh b/src/cpu/o3/thread_state.hh new file mode 100644 index 000000000..0247deb52 --- /dev/null +++ b/src/cpu/o3/thread_state.hh @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_THREAD_STATE_HH__ +#define __CPU_O3_THREAD_STATE_HH__ + +#include "base/callback.hh" +#include "base/output.hh" +#include "cpu/thread_context.hh" +#include "cpu/thread_state.hh" +#include "sim/sim_exit.hh" + +class Event; +class Process; + +#if FULL_SYSTEM +class EndQuiesceEvent; +class FunctionProfile; +class ProfileNode; +#else +class FunctionalMemory; +class Process; +#endif + +/** + * Class that has various thread state, such as the status, the + * current instruction being processed, whether or not the thread has + * a trap pending or is being externally updated, the ThreadContext + * pointer, etc. It also handles anything related to a specific + * thread's process, such as syscalls and checking valid addresses. + */ +template <class Impl> +struct O3ThreadState : public ThreadState { + typedef ThreadContext::Status Status; + typedef typename Impl::O3CPU O3CPU; + + private: + /** Pointer to the CPU. */ + O3CPU *cpu; + public: + /** Whether or not the thread is currently in syscall mode, and + * thus able to be externally updated without squashing. + */ + bool inSyscall; + + /** Whether or not the thread is currently waiting on a trap, and + * thus able to be externally updated without squashing. + */ + bool trapPending; + +#if FULL_SYSTEM + O3ThreadState(O3CPU *_cpu, int _thread_num) + : ThreadState(-1, _thread_num), + cpu(_cpu), inSyscall(0), trapPending(0) + { + if (cpu->params->profile) { + profile = new FunctionProfile(cpu->params->system->kernelSymtab); + Callback *cb = + new MakeCallback<O3ThreadState, + &O3ThreadState::dumpFuncProfile>(this); + registerExitCallback(cb); + } + + // let's fill with a dummy node for now so we don't get a segfault + // on the first cycle when there's no node available. + static ProfileNode dummyNode; + profileNode = &dummyNode; + profilePC = 3; + } +#else + O3ThreadState(O3CPU *_cpu, int _thread_num, Process *_process, int _asid, + MemObject *mem) + : ThreadState(-1, _thread_num, _process, _asid, mem), + cpu(_cpu), inSyscall(0), trapPending(0) + { } +#endif + + /** Pointer to the ThreadContext of this thread. */ + ThreadContext *tc; + + /** Returns a pointer to the TC of this thread. */ + ThreadContext *getTC() { return tc; } + +#if !FULL_SYSTEM + /** Handles the syscall. */ + void syscall(int64_t callnum) { process->syscall(callnum, tc); } +#endif + +#if FULL_SYSTEM + void dumpFuncProfile() + { + std::ostream *os = simout.create(csprintf("profile.%s.dat", cpu->name())); + profile->dump(xcProxy, *os); + } +#endif +}; + +#endif // __CPU_O3_THREAD_STATE_HH__ diff --git a/src/cpu/o3/tournament_pred.cc b/src/cpu/o3/tournament_pred.cc new file mode 100644 index 000000000..ffb941c77 --- /dev/null +++ b/src/cpu/o3/tournament_pred.cc @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "base/intmath.hh" +#include "cpu/o3/tournament_pred.hh" + +TournamentBP::TournamentBP(unsigned _localPredictorSize, + unsigned _localCtrBits, + unsigned _localHistoryTableSize, + unsigned _localHistoryBits, + unsigned _globalPredictorSize, + unsigned _globalCtrBits, + unsigned _globalHistoryBits, + unsigned _choicePredictorSize, + unsigned _choiceCtrBits, + unsigned _instShiftAmt) + : localPredictorSize(_localPredictorSize), + localCtrBits(_localCtrBits), + localHistoryTableSize(_localHistoryTableSize), + localHistoryBits(_localHistoryBits), + globalPredictorSize(_globalPredictorSize), + globalCtrBits(_globalCtrBits), + globalHistoryBits(_globalHistoryBits), + choicePredictorSize(_globalPredictorSize), + choiceCtrBits(_choiceCtrBits), + instShiftAmt(_instShiftAmt) +{ + if (!isPowerOf2(localPredictorSize)) { + fatal("Invalid local predictor size!\n"); + } + + //Setup the array of counters for the local predictor + localCtrs.resize(localPredictorSize); + + for (int i = 0; i < localPredictorSize; ++i) + localCtrs[i].setBits(localCtrBits); + + localPredictorMask = floorPow2(localPredictorSize) - 1; + + if (!isPowerOf2(localHistoryTableSize)) { + fatal("Invalid local history table size!\n"); + } + + //Setup the history table for the local table + localHistoryTable.resize(localHistoryTableSize); + + for (int i = 0; i < localHistoryTableSize; ++i) + localHistoryTable[i] = 0; + + // Setup the local history mask + localHistoryMask = (1 << localHistoryBits) - 1; + + if (!isPowerOf2(globalPredictorSize)) { + fatal("Invalid global predictor size!\n"); + } + + //Setup the array of counters for the global predictor + globalCtrs.resize(globalPredictorSize); + + for (int i = 0; i < globalPredictorSize; ++i) + globalCtrs[i].setBits(globalCtrBits); + + //Clear the global history + globalHistory = 0; + // Setup the global history mask + globalHistoryMask = (1 << globalHistoryBits) - 1; + + if (!isPowerOf2(choicePredictorSize)) { + fatal("Invalid choice predictor size!\n"); + } + + //Setup the array of counters for the choice predictor + choiceCtrs.resize(choicePredictorSize); + + for (int i = 0; i < choicePredictorSize; ++i) + choiceCtrs[i].setBits(choiceCtrBits); + + // @todo: Allow for different thresholds between the predictors. + threshold = (1 << (localCtrBits - 1)) - 1; + threshold = threshold / 2; +} + +inline +unsigned +TournamentBP::calcLocHistIdx(Addr &branch_addr) +{ + // Get low order bits after removing instruction offset. + return (branch_addr >> instShiftAmt) & (localHistoryTableSize - 1); +} + +inline +void +TournamentBP::updateGlobalHistTaken() +{ + globalHistory = (globalHistory << 1) | 1; + globalHistory = globalHistory & globalHistoryMask; +} + +inline +void +TournamentBP::updateGlobalHistNotTaken() +{ + globalHistory = (globalHistory << 1); + globalHistory = globalHistory & globalHistoryMask; +} + +inline +void +TournamentBP::updateLocalHistTaken(unsigned local_history_idx) +{ + localHistoryTable[local_history_idx] = + (localHistoryTable[local_history_idx] << 1) | 1; +} + +inline +void +TournamentBP::updateLocalHistNotTaken(unsigned local_history_idx) +{ + localHistoryTable[local_history_idx] = + (localHistoryTable[local_history_idx] << 1); +} + +bool +TournamentBP::lookup(Addr &branch_addr, void * &bp_history) +{ + bool local_prediction; + unsigned local_history_idx; + unsigned local_predictor_idx; + + bool global_prediction; + bool choice_prediction; + + //Lookup in the local predictor to get its branch prediction + local_history_idx = calcLocHistIdx(branch_addr); + local_predictor_idx = localHistoryTable[local_history_idx] + & localPredictorMask; + local_prediction = localCtrs[local_predictor_idx].read() > threshold; + + //Lookup in the global predictor to get its branch prediction + global_prediction = globalCtrs[globalHistory].read() > threshold; + + //Lookup in the choice predictor to see which one to use + choice_prediction = choiceCtrs[globalHistory].read() > threshold; + + // Create BPHistory and pass it back to be recorded. + BPHistory *history = new BPHistory; + history->globalHistory = globalHistory; + history->localPredTaken = local_prediction; + history->globalPredTaken = global_prediction; + history->globalUsed = choice_prediction; + bp_history = (void *)history; + + assert(globalHistory < globalPredictorSize && + local_history_idx < localHistoryTableSize && + local_predictor_idx < localPredictorSize); + + // Commented code is for doing speculative update of counters and + // all histories. + if (choice_prediction) { + if (global_prediction) { +// updateHistoriesTaken(local_history_idx); +// globalCtrs[globalHistory].increment(); +// localCtrs[local_history_idx].increment(); + updateGlobalHistTaken(); + return true; + } else { +// updateHistoriesNotTaken(local_history_idx); +// globalCtrs[globalHistory].decrement(); +// localCtrs[local_history_idx].decrement(); + updateGlobalHistNotTaken(); + return false; + } + } else { + if (local_prediction) { +// updateHistoriesTaken(local_history_idx); +// globalCtrs[globalHistory].increment(); +// localCtrs[local_history_idx].increment(); + updateGlobalHistTaken(); + return true; + } else { +// updateHistoriesNotTaken(local_history_idx); +// globalCtrs[globalHistory].decrement(); +// localCtrs[local_history_idx].decrement(); + updateGlobalHistNotTaken(); + return false; + } + } +} + +void +TournamentBP::uncondBr(void * &bp_history) +{ + // Create BPHistory and pass it back to be recorded. + BPHistory *history = new BPHistory; + history->globalHistory = globalHistory; + history->localPredTaken = true; + history->globalPredTaken = true; + bp_history = static_cast<void *>(history); + + updateGlobalHistTaken(); +} + +void +TournamentBP::update(Addr &branch_addr, bool taken, void *bp_history) +{ + unsigned local_history_idx; + unsigned local_predictor_idx; + unsigned local_predictor_hist; + + // Get the local predictor's current prediction + local_history_idx = calcLocHistIdx(branch_addr); + local_predictor_hist = localHistoryTable[local_history_idx]; + local_predictor_idx = local_predictor_hist & localPredictorMask; + + // Update the choice predictor to tell it which one was correct if + // there was a prediction. + if (bp_history) { + BPHistory *history = static_cast<BPHistory *>(bp_history); + if (history->localPredTaken != history->globalPredTaken) { + // If the local prediction matches the actual outcome, + // decerement the counter. Otherwise increment the + // counter. + if (history->localPredTaken == taken) { + choiceCtrs[globalHistory].decrement(); + } else if (history->globalPredTaken == taken){ + choiceCtrs[globalHistory].increment(); + } + } + + // We're done with this history, now delete it. + delete history; + } + + assert(globalHistory < globalPredictorSize && + local_history_idx < localHistoryTableSize && + local_predictor_idx < localPredictorSize); + + // Update the counters and local history with the proper + // resolution of the branch. Global history is updated + // speculatively and restored upon squash() calls, so it does not + // need to be updated. + if (taken) { + localCtrs[local_predictor_idx].increment(); + globalCtrs[globalHistory].increment(); + + updateLocalHistTaken(local_history_idx); + } else { + localCtrs[local_predictor_idx].decrement(); + globalCtrs[globalHistory].decrement(); + + updateLocalHistNotTaken(local_history_idx); + } +} + +void +TournamentBP::squash(void *bp_history) +{ + BPHistory *history = static_cast<BPHistory *>(bp_history); + + // Restore global history to state prior to this branch. + globalHistory = history->globalHistory; + + // Delete this BPHistory now that we're done with it. + delete history; +} + +#ifdef DEBUG +int +TournamentBP::BPHistory::newCount = 0; +#endif diff --git a/src/cpu/o3/tournament_pred.hh b/src/cpu/o3/tournament_pred.hh new file mode 100644 index 000000000..472944910 --- /dev/null +++ b/src/cpu/o3/tournament_pred.hh @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_O3_TOURNAMENT_PRED_HH__ +#define __CPU_O3_TOURNAMENT_PRED_HH__ + +#include "cpu/o3/sat_counter.hh" +#include "sim/host.hh" +#include <vector> + +/** + * Implements a tournament branch predictor, hopefully identical to the one + * used in the 21264. It has a local predictor, which uses a local history + * table to index into a table of counters, and a global predictor, which + * uses a global history to index into a table of counters. A choice + * predictor chooses between the two. Only the global history register + * is speculatively updated, the rest are updated upon branches committing + * or misspeculating. + */ +class TournamentBP +{ + public: + /** + * Default branch predictor constructor. + */ + TournamentBP(unsigned localPredictorSize, + unsigned localCtrBits, + unsigned localHistoryTableSize, + unsigned localHistoryBits, + unsigned globalPredictorSize, + unsigned globalHistoryBits, + unsigned globalCtrBits, + unsigned choicePredictorSize, + unsigned choiceCtrBits, + unsigned instShiftAmt); + + /** + * Looks up the given address in the branch predictor and returns + * a true/false value as to whether it is taken. Also creates a + * BPHistory object to store any state it will need on squash/update. + * @param branch_addr The address of the branch to look up. + * @param bp_history Pointer that will be set to the BPHistory object. + * @return Whether or not the branch is taken. + */ + bool lookup(Addr &branch_addr, void * &bp_history); + + /** + * Records that there was an unconditional branch, and modifies + * the bp history to point to an object that has the previous + * global history stored in it. + * @param bp_history Pointer that will be set to the BPHistory object. + */ + void uncondBr(void * &bp_history); + + /** + * Updates the branch predictor with the actual result of a branch. + * @param branch_addr The address of the branch to update. + * @param taken Whether or not the branch was taken. + * @param bp_history Pointer to the BPHistory object that was created + * when the branch was predicted. + */ + void update(Addr &branch_addr, bool taken, void *bp_history); + + /** + * Restores the global branch history on a squash. + * @param bp_history Pointer to the BPHistory object that has the + * previous global branch history in it. + */ + void squash(void *bp_history); + + /** Returns the global history. */ + inline unsigned readGlobalHist() { return globalHistory; } + + private: + /** + * Returns if the branch should be taken or not, given a counter + * value. + * @param count The counter value. + */ + inline bool getPrediction(uint8_t &count); + + /** + * Returns the local history index, given a branch address. + * @param branch_addr The branch's PC address. + */ + inline unsigned calcLocHistIdx(Addr &branch_addr); + + /** Updates global history as taken. */ + inline void updateGlobalHistTaken(); + + /** Updates global history as not taken. */ + inline void updateGlobalHistNotTaken(); + + /** + * Updates local histories as taken. + * @param local_history_idx The local history table entry that + * will be updated. + */ + inline void updateLocalHistTaken(unsigned local_history_idx); + + /** + * Updates local histories as not taken. + * @param local_history_idx The local history table entry that + * will be updated. + */ + inline void updateLocalHistNotTaken(unsigned local_history_idx); + + /** + * The branch history information that is created upon predicting + * a branch. It will be passed back upon updating and squashing, + * when the BP can use this information to update/restore its + * state properly. + */ + struct BPHistory { +#ifdef DEBUG + BPHistory() + { newCount++; } + ~BPHistory() + { newCount--; } + + static int newCount; +#endif + unsigned globalHistory; + bool localPredTaken; + bool globalPredTaken; + bool globalUsed; + }; + + /** Local counters. */ + std::vector<SatCounter> localCtrs; + + /** Size of the local predictor. */ + unsigned localPredictorSize; + + /** Mask to get the proper index bits into the predictor. */ + unsigned localPredictorMask; + + /** Number of bits of the local predictor's counters. */ + unsigned localCtrBits; + + /** Array of local history table entries. */ + std::vector<unsigned> localHistoryTable; + + /** Size of the local history table. */ + unsigned localHistoryTableSize; + + /** Number of bits for each entry of the local history table. + * @todo Doesn't this come from the size of the local predictor? + */ + unsigned localHistoryBits; + + /** Mask to get the proper local history. */ + unsigned localHistoryMask; + + /** Array of counters that make up the global predictor. */ + std::vector<SatCounter> globalCtrs; + + /** Size of the global predictor. */ + unsigned globalPredictorSize; + + /** Number of bits of the global predictor's counters. */ + unsigned globalCtrBits; + + /** Global history register. */ + unsigned globalHistory; + + /** Number of bits for the global history. */ + unsigned globalHistoryBits; + + /** Mask to get the proper global history. */ + unsigned globalHistoryMask; + + /** Array of counters that make up the choice predictor. */ + std::vector<SatCounter> choiceCtrs; + + /** Size of the choice predictor (identical to the global predictor). */ + unsigned choicePredictorSize; + + /** Number of bits of the choice predictor's counters. */ + unsigned choiceCtrBits; + + /** Number of bits to shift the instruction over to get rid of the word + * offset. + */ + unsigned instShiftAmt; + + /** Threshold for the counter value; above the threshold is taken, + * equal to or below the threshold is not taken. + */ + unsigned threshold; +}; + +#endif // __CPU_O3_TOURNAMENT_PRED_HH__ diff --git a/src/cpu/op_class.cc b/src/cpu/op_class.cc new file mode 100644 index 000000000..f7ef49c0f --- /dev/null +++ b/src/cpu/op_class.cc @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#include "cpu/op_class.hh" + +/** OpClass enum -> description string */ +const char * +opClassStrings[Num_OpClasses] = +{ + "(null)", + "IntAlu", + "IntMult", + "IntDiv", + "FloatAdd", + "FloatCmp", + "FloatCvt", + "FloatMult", + "FloatDiv", + "FloatSqrt", + "MemRead", + "MemWrite", + "IprAccess", + "InstPrefetch" +}; + diff --git a/src/cpu/op_class.hh b/src/cpu/op_class.hh new file mode 100644 index 000000000..71819c904 --- /dev/null +++ b/src/cpu/op_class.hh @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +#ifndef __CPU__OP_CLASS_HH__ +#define __CPU__OP_CLASS_HH__ + +/** + * @file + * Definition of operation classes. + */ + +/** + * Instruction operation classes. These classes are used for + * assigning instructions to functional units. + */ +enum OpClass { + No_OpClass = 0, ///< Instruction does not use a functional unit + IntAluOp, ///< Integer ALU operaton (add/sub/logical) + IntMultOp, ///< Integer multiply + IntDivOp, ///< Integer divide + FloatAddOp, ///< Floating point add/subtract + FloatCmpOp, ///< Floating point comparison + FloatCvtOp, ///< Floating point<->integer conversion + FloatMultOp, ///< Floating point multiply + FloatDivOp, ///< Floating point divide + FloatSqrtOp, ///< Floating point square root + MemReadOp, ///< Memory read port + MemWriteOp, ///< Memory write port + IprAccessOp, ///< Internal Processor Register read/write port + InstPrefetchOp, ///< Instruction prefetch port (on I-cache) + Num_OpClasses ///< Total number of operation classes +}; + +/** + * Array mapping OpClass enum values to strings. Defined in op_class.cc. + */ +extern const char *opClassStrings[Num_OpClasses]; + +#endif // __CPU__OP_CLASS_HH__ diff --git a/src/cpu/ozone/back_end.cc b/src/cpu/ozone/back_end.cc new file mode 100644 index 000000000..a61a00c84 --- /dev/null +++ b/src/cpu/ozone/back_end.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/back_end_impl.hh" +#include "cpu/ozone/ozone_impl.hh" + +//template class BackEnd<OzoneImpl>; diff --git a/src/cpu/ozone/back_end.hh b/src/cpu/ozone/back_end.hh new file mode 100644 index 000000000..9bab6a964 --- /dev/null +++ b/src/cpu/ozone/back_end.hh @@ -0,0 +1,543 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_BACK_END_HH__ +#define __CPU_OZONE_BACK_END_HH__ + +#include <list> +#include <queue> +#include <string> + +#include "arch/faults.hh" +#include "base/timebuf.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/rename_table.hh" +#include "cpu/ozone/thread_state.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" + +class ThreadContext; + +template <class Impl> +class OzoneThreadState; + +template <class Impl> +class BackEnd +{ + public: + typedef OzoneThreadState<Impl> Thread; + + typedef typename Impl::Params Params; + typedef typename Impl::DynInst DynInst; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::FrontEnd FrontEnd; + typedef typename Impl::FullCPU::CommStruct CommStruct; + + struct SizeStruct { + int size; + }; + + typedef SizeStruct DispatchToIssue; + typedef SizeStruct IssueToExec; + typedef SizeStruct ExecToCommit; + typedef SizeStruct Writeback; + + TimeBuffer<DispatchToIssue> d2i; + typename TimeBuffer<DispatchToIssue>::wire instsToDispatch; + TimeBuffer<IssueToExec> i2e; + typename TimeBuffer<IssueToExec>::wire instsToExecute; + TimeBuffer<ExecToCommit> e2c; + TimeBuffer<Writeback> numInstsToWB; + + TimeBuffer<CommStruct> *comm; + typename TimeBuffer<CommStruct>::wire toIEW; + typename TimeBuffer<CommStruct>::wire fromCommit; + + class InstQueue { + enum queue { + NonSpec, + IQ, + ToBeScheduled, + ReadyList, + ReplayList + }; + struct pqCompare { + bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const + { + return lhs->seqNum > rhs->seqNum; + } + }; + public: + InstQueue(Params *params); + + std::string name() const; + + void regStats(); + + void setIssueExecQueue(TimeBuffer<IssueToExec> *i2e_queue); + + void setBE(BackEnd *_be) { be = _be; } + + void insert(DynInstPtr &inst); + + void scheduleReadyInsts(); + + void scheduleNonSpec(const InstSeqNum &sn); + + DynInstPtr getReadyInst(); + + void commit(const InstSeqNum &sn) {} + + void squash(const InstSeqNum &sn); + + int wakeDependents(DynInstPtr &inst); + + /** Tells memory dependence unit that a memory instruction needs to be + * rescheduled. It will re-execute once replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &inst); + + /** Re-executes all rescheduled memory instructions. */ + void replayMemInst(DynInstPtr &inst); + + /** Completes memory instruction. */ + void completeMemInst(DynInstPtr &inst); + + void violation(DynInstPtr &inst, DynInstPtr &violation) { } + + bool isFull() { return numInsts >= size; } + + void dumpInsts(); + + private: + bool find(queue q, typename std::list<DynInstPtr>::iterator it); + BackEnd *be; + TimeBuffer<IssueToExec> *i2e; + typename TimeBuffer<IssueToExec>::wire numIssued; + typedef typename std::list<DynInstPtr> InstList; + typedef typename std::list<DynInstPtr>::iterator InstListIt; + typedef typename std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> ReadyInstQueue; + // Not sure I need the IQ list; it just needs to be a count. + InstList iq; + InstList toBeScheduled; + InstList readyList; + InstList nonSpec; + InstList replayList; + ReadyInstQueue readyQueue; + public: + int size; + int numInsts; + int width; + + Stats::VectorDistribution<> occ_dist; + + Stats::Vector<> inst_count; + Stats::Vector<> peak_inst_count; + Stats::Scalar<> empty_count; + Stats::Scalar<> current_count; + Stats::Scalar<> fullCount; + + Stats::Formula occ_rate; + Stats::Formula avg_residency; + Stats::Formula empty_rate; + Stats::Formula full_rate; + }; + + /** LdWriteback event for a load completion. */ + class LdWritebackEvent : public Event { + private: + /** Instruction that is writing back data to the register file. */ + DynInstPtr inst; + /** Pointer to IEW stage. */ + BackEnd *be; + + public: + /** Constructs a load writeback event. */ + LdWritebackEvent(DynInstPtr &_inst, BackEnd *be); + + /** Processes writeback event. */ + virtual void process(); + /** Returns the description of the writeback event. */ + virtual const char *description(); + }; + + BackEnd(Params *params); + + std::string name() const; + + void regStats(); + + void setCPU(FullCPU *cpu_ptr) + { cpu = cpu_ptr; } + + void setFrontEnd(FrontEnd *front_end_ptr) + { frontEnd = front_end_ptr; } + + void setTC(ThreadContext *tc_ptr) + { tc = tc_ptr; } + + void setThreadState(Thread *thread_ptr) + { thread = thread_ptr; } + + void setCommBuffer(TimeBuffer<CommStruct> *_comm); + + void tick(); + void squash(); + void squashFromTC(); + bool tcSquash; + + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + Addr readCommitPC() { return commitPC; } + + Addr commitPC; + + bool robEmpty() { return instList.empty(); } + + bool isFull() { return numInsts >= numROBEntries; } + bool isBlocked() { return status == Blocked || dispatchStatus == Blocked; } + + /** Tells memory dependence unit that a memory instruction needs to be + * rescheduled. It will re-execute once replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &inst) + { IQ.rescheduleMemInst(inst); } + + /** Re-executes all rescheduled memory instructions. */ + void replayMemInst(DynInstPtr &inst) + { IQ.replayMemInst(inst); } + + /** Completes memory instruction. */ + void completeMemInst(DynInstPtr &inst) + { IQ.completeMemInst(inst); } + + void fetchFault(Fault &fault); + + private: + void updateStructures(); + void dispatchInsts(); + void dispatchStall(); + void checkDispatchStatus(); + void scheduleReadyInsts(); + void executeInsts(); + void commitInsts(); + void addToIQ(DynInstPtr &inst); + void addToLSQ(DynInstPtr &inst); + void instToCommit(DynInstPtr &inst); + void writebackInsts(); + bool commitInst(int inst_num); + void squash(const InstSeqNum &sn); + void squashDueToBranch(DynInstPtr &inst); + void squashDueToMemBlocked(DynInstPtr &inst); + void updateExeInstStats(DynInstPtr &inst); + void updateComInstStats(DynInstPtr &inst); + + public: + FullCPU *cpu; + + FrontEnd *frontEnd; + + ThreadContext *tc; + + Thread *thread; + + enum Status { + Running, + Idle, + DcacheMissStall, + DcacheMissComplete, + Blocked + }; + + Status status; + + Status dispatchStatus; + + Counter funcExeInst; + + private: +// typedef typename Impl::InstQueue InstQueue; + + InstQueue IQ; + + typedef typename Impl::LdstQueue LdstQueue; + + LdstQueue LSQ; + public: + RenameTable<Impl> commitRenameTable; + + RenameTable<Impl> renameTable; + private: + class DCacheCompletionEvent : public Event + { + private: + BackEnd *be; + + public: + DCacheCompletionEvent(BackEnd *_be); + + virtual void process(); + virtual const char *description(); + }; + + friend class DCacheCompletionEvent; + + DCacheCompletionEvent cacheCompletionEvent; + + MemInterface *dcacheInterface; + + Request *memReq; + + // General back end width. Used if the more specific isn't given. + int width; + + // Dispatch width. + int dispatchWidth; + int numDispatchEntries; + int dispatchSize; + + int issueWidth; + + // Writeback width + int wbWidth; + + // Commit width + int commitWidth; + + /** Index into queue of instructions being written back. */ + unsigned wbNumInst; + + /** Cycle number within the queue of instructions being written + * back. Used in case there are too many instructions writing + * back at the current cycle and writesbacks need to be scheduled + * for the future. See comments in instToCommit(). + */ + unsigned wbCycle; + + int numROBEntries; + int numInsts; + + bool squashPending; + InstSeqNum squashSeqNum; + Addr squashNextPC; + + Fault faultFromFetch; + + private: + typedef typename std::list<DynInstPtr>::iterator InstListIt; + + std::list<DynInstPtr> instList; + std::list<DynInstPtr> dispatch; + std::list<DynInstPtr> writeback; + + int latency; + + int squashLatency; + + bool exactFullStall; + + bool fetchRedirect[Impl::MaxThreads]; + + // number of cycles stalled for D-cache misses +/* Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; +*/ + Stats::Vector<> rob_cap_events; + Stats::Vector<> rob_cap_inst_count; + Stats::Vector<> iq_cap_events; + Stats::Vector<> iq_cap_inst_count; + // total number of instructions executed + Stats::Vector<> exe_inst; + Stats::Vector<> exe_swp; + Stats::Vector<> exe_nop; + Stats::Vector<> exe_refs; + Stats::Vector<> exe_loads; + Stats::Vector<> exe_branches; + + Stats::Vector<> issued_ops; + + // total number of loads forwaded from LSQ stores + Stats::Vector<> lsq_forw_loads; + + // total number of loads ignored due to invalid addresses + Stats::Vector<> inv_addr_loads; + + // total number of software prefetches ignored due to invalid addresses + Stats::Vector<> inv_addr_swpfs; + // ready loads blocked due to memory disambiguation + Stats::Vector<> lsq_blocked_loads; + + Stats::Scalar<> lsqInversion; + + Stats::Vector<> n_issued_dist; + Stats::VectorDistribution<> issue_delay_dist; + + Stats::VectorDistribution<> queue_res_dist; +/* + Stats::Vector<> stat_fu_busy; + Stats::Vector2d<> stat_fuBusy; + Stats::Vector<> dist_unissued; + Stats::Vector2d<> stat_issued_inst_type; + + Stats::Formula misspec_cnt; + Stats::Formula misspec_ipc; + Stats::Formula issue_rate; + Stats::Formula issue_stores; + Stats::Formula issue_op_rate; + Stats::Formula fu_busy_rate; + Stats::Formula commit_stores; + Stats::Formula commit_ipc; + Stats::Formula commit_ipb; + Stats::Formula lsq_inv_rate; +*/ + Stats::Vector<> writeback_count; + Stats::Vector<> producer_inst; + Stats::Vector<> consumer_inst; + Stats::Vector<> wb_penalized; + + Stats::Formula wb_rate; + Stats::Formula wb_fanout; + Stats::Formula wb_penalized_rate; + + // total number of instructions committed + Stats::Vector<> stat_com_inst; + Stats::Vector<> stat_com_swp; + Stats::Vector<> stat_com_refs; + Stats::Vector<> stat_com_loads; + Stats::Vector<> stat_com_membars; + Stats::Vector<> stat_com_branches; + + Stats::Distribution<> n_committed_dist; + + Stats::Scalar<> commit_eligible_samples; + Stats::Vector<> commit_eligible; + + Stats::Scalar<> ROB_fcount; + Stats::Formula ROB_full_rate; + + Stats::Vector<> ROB_count; // cumulative ROB occupancy + Stats::Formula ROB_occ_rate; + Stats::VectorDistribution<> ROB_occ_dist; + public: + void dumpInsts(); +}; + +template <class Impl> +template <class T> +Fault +BackEnd<Impl>::read(RequestPtr req, T &data, int load_idx) +{ +/* memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = cpu->translateDataReadReq(memReq); + + // if we have a cache, do cache access too + if (fault == NoFault && dcacheInterface) { + memReq->cmd = Read; + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT && dcacheInterface->doEvents()) { + // Fix this hack for keeping funcExeInst correct with loads that + // are executed twice. + --funcExeInst; + + memReq->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); +// status = DcacheMissStall; + DPRINTF(OzoneCPU, "Dcache miss stall!\n"); + } else { + // do functional access + fault = thread->mem->read(memReq, data); + + } + } +*/ +/* + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Read"); +*/ + return LSQ.read(req, data, load_idx); +} + +template <class Impl> +template <class T> +Fault +BackEnd<Impl>::write(RequestPtr req, T &data, int store_idx) +{ +/* + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = cpu->translateDataWriteReq(memReq); + + if (fault == NoFault && dcacheInterface) { + memReq->cmd = Write; + memcpy(memReq->data,(uint8_t *)&data,memReq->size); + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT && dcacheInterface->doEvents()) { + memReq->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); +// status = DcacheMissStall; + DPRINTF(OzoneCPU, "Dcache miss stall!\n"); + } + } + + if (res && (fault == NoFault)) + *res = memReq->result; + */ +/* + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Write"); +*/ + return LSQ.write(req, data, store_idx); +} + +#endif // __CPU_OZONE_BACK_END_HH__ diff --git a/src/cpu/ozone/back_end_impl.hh b/src/cpu/ozone/back_end_impl.hh new file mode 100644 index 000000000..ac3218c02 --- /dev/null +++ b/src/cpu/ozone/back_end_impl.hh @@ -0,0 +1,1933 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "encumbered/cpu/full/op_class.hh" +#include "cpu/ozone/back_end.hh" + +template <class Impl> +BackEnd<Impl>::InstQueue::InstQueue(Params *params) + : size(params->numIQEntries), numInsts(0), width(params->issueWidth) +{ +} + +template <class Impl> +std::string +BackEnd<Impl>::InstQueue::name() const +{ + return be->name() + ".iq"; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::regStats() +{ + using namespace Stats; + + occ_dist + .init(1, 0, size, 2) + .name(name() + "occ_dist") + .desc("IQ Occupancy per cycle") + .flags(total | cdf) + ; + + inst_count + .init(1) + .name(name() + "cum_num_insts") + .desc("Total occupancy") + .flags(total) + ; + + peak_inst_count + .init(1) + .name(name() + "peak_occupancy") + .desc("Peak IQ occupancy") + .flags(total) + ; + + current_count + .name(name() + "current_count") + .desc("Occupancy this cycle") + ; + + empty_count + .name(name() + "empty_count") + .desc("Number of empty cycles") + ; + + fullCount + .name(name() + "full_count") + .desc("Number of full cycles") + ; + + + occ_rate + .name(name() + "occ_rate") + .desc("Average occupancy") + .flags(total) + ; + occ_rate = inst_count / be->cpu->numCycles; + + avg_residency + .name(name() + "avg_residency") + .desc("Average IQ residency") + .flags(total) + ; + avg_residency = occ_rate / be->cpu->numCycles; + + empty_rate + .name(name() + "empty_rate") + .desc("Fraction of cycles empty") + ; + empty_rate = 100 * empty_count / be->cpu->numCycles; + + full_rate + .name(name() + "full_rate") + .desc("Fraction of cycles full") + ; + full_rate = 100 * fullCount / be->cpu->numCycles; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::setIssueExecQueue(TimeBuffer<IssueToExec> *i2e_queue) +{ + i2e = i2e_queue; + numIssued = i2e->getWire(0); +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::insert(DynInstPtr &inst) +{ + numInsts++; + inst_count[0]++; + if (!inst->isNonSpeculative()) { + DPRINTF(BE, "Instruction [sn:%lli] added to IQ\n", inst->seqNum); + if (inst->readyToIssue()) { + toBeScheduled.push_front(inst); + inst->iqIt = toBeScheduled.begin(); + inst->iqItValid = true; + } else { + iq.push_front(inst); + inst->iqIt = iq.begin(); + inst->iqItValid = true; + } + } else { + DPRINTF(BE, "Nonspeculative instruction [sn:%lli] added to IQ\n", inst->seqNum); + nonSpec.push_front(inst); + inst->iqIt = nonSpec.begin(); + inst->iqItValid = true; + } +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::scheduleReadyInsts() +{ + int scheduled = numIssued->size; + InstListIt iq_it = --toBeScheduled.end(); + InstListIt iq_end_it = toBeScheduled.end(); + + while (iq_it != iq_end_it && scheduled < width) { +// if ((*iq_it)->readyToIssue()) { + DPRINTF(BE, "Instruction [sn:%lli] PC:%#x is ready\n", + (*iq_it)->seqNum, (*iq_it)->readPC()); + readyQueue.push(*iq_it); + readyList.push_front(*iq_it); + + (*iq_it)->iqIt = readyList.begin(); + + toBeScheduled.erase(iq_it--); + + ++scheduled; +// } else { +// iq_it++; +// } + } + + numIssued->size+= scheduled; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::scheduleNonSpec(const InstSeqNum &sn) +{ +/* + InstListIt non_spec_it = nonSpec.begin(); + InstListIt non_spec_end_it = nonSpec.end(); + + while ((*non_spec_it)->seqNum != sn) { + non_spec_it++; + assert(non_spec_it != non_spec_end_it); + } +*/ + DynInstPtr inst = nonSpec.back(); + + DPRINTF(BE, "Nonspeculative instruction [sn:%lli] scheduled\n", inst->seqNum); + + assert(inst->seqNum == sn); + + assert(find(NonSpec, inst->iqIt)); + nonSpec.erase(inst->iqIt); + readyList.push_front(inst); + inst->iqIt = readyList.begin(); + readyQueue.push(inst); + numIssued->size++; +} + +template <class Impl> +typename Impl::DynInstPtr +BackEnd<Impl>::InstQueue::getReadyInst() +{ + assert(!readyList.empty()); + + DynInstPtr inst = readyQueue.top(); + readyQueue.pop(); + assert(find(ReadyList, inst->iqIt)); + readyList.erase(inst->iqIt); + inst->iqItValid = false; +// if (!inst->isMemRef()) + --numInsts; + return inst; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::squash(const InstSeqNum &sn) +{ + InstListIt iq_it = iq.begin(); + InstListIt iq_end_it = iq.end(); + + while (iq_it != iq_end_it && (*iq_it)->seqNum > sn) { + DPRINTF(BE, "Instruction [sn:%lli] removed from IQ\n", (*iq_it)->seqNum); + (*iq_it)->iqItValid = false; + iq.erase(iq_it++); + --numInsts; + } + + iq_it = nonSpec.begin(); + iq_end_it = nonSpec.end(); + + while (iq_it != iq_end_it && (*iq_it)->seqNum > sn) { + DPRINTF(BE, "Instruction [sn:%lli] removed from IQ\n", (*iq_it)->seqNum); + (*iq_it)->iqItValid = false; + nonSpec.erase(iq_it++); + --numInsts; + } + + iq_it = replayList.begin(); + iq_end_it = replayList.end(); + + while (iq_it != iq_end_it) { + if ((*iq_it)->seqNum > sn) { + DPRINTF(BE, "Instruction [sn:%lli] removed from IQ\n", (*iq_it)->seqNum); + (*iq_it)->iqItValid = false; + replayList.erase(iq_it++); + --numInsts; + } else { + iq_it++; + } + } + + assert(numInsts >= 0); +/* + InstListIt ready_it = readyList.begin(); + InstListIt ready_end_it = readyList.end(); + + while (ready_it != ready_end_it) { + if ((*ready_it)->seqNum > sn) { + readyList.erase(ready_it++); + } else { + ready_it++; + } + } +*/ +} + +template <class Impl> +int +BackEnd<Impl>::InstQueue::wakeDependents(DynInstPtr &inst) +{ + assert(!inst->isSquashed()); + std::vector<DynInstPtr> &dependents = inst->getDependents(); + int num_outputs = dependents.size(); + + DPRINTF(BE, "Waking instruction [sn:%lli] dependents in IQ\n", inst->seqNum); + + for (int i = 0; i < num_outputs; i++) { + DynInstPtr dep_inst = dependents[i]; + dep_inst->markSrcRegReady(); + DPRINTF(BE, "Marking source reg ready [sn:%lli] in IQ\n", dep_inst->seqNum); + + if (dep_inst->readyToIssue() && dep_inst->iqItValid) { + if (dep_inst->isNonSpeculative()) { + assert(find(NonSpec, dep_inst->iqIt)); + nonSpec.erase(dep_inst->iqIt); + } else { + assert(find(IQ, dep_inst->iqIt)); + iq.erase(dep_inst->iqIt); + } + + toBeScheduled.push_front(dep_inst); + dep_inst->iqIt = toBeScheduled.begin(); + } + } + return num_outputs; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::rescheduleMemInst(DynInstPtr &inst) +{ + DPRINTF(BE, "Rescheduling memory instruction [sn:%lli]\n", inst->seqNum); + assert(!inst->iqItValid); + replayList.push_front(inst); + inst->iqIt = replayList.begin(); + inst->iqItValid = true; + ++numInsts; +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::replayMemInst(DynInstPtr &inst) +{ + DPRINTF(BE, "Replaying memory instruction [sn:%lli]\n", inst->seqNum); + assert(find(ReplayList, inst->iqIt)); + InstListIt iq_it = --replayList.end(); + InstListIt iq_end_it = replayList.end(); + while (iq_it != iq_end_it) { + DynInstPtr rescheduled_inst = (*iq_it); + + DPRINTF(BE, "Memory instruction [sn:%lli] also replayed\n", inst->seqNum); + replayList.erase(iq_it--); + toBeScheduled.push_front(rescheduled_inst); + rescheduled_inst->iqIt = toBeScheduled.begin(); + } +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::completeMemInst(DynInstPtr &inst) +{ + panic("Not implemented."); +} + +template <class Impl> +bool +BackEnd<Impl>::InstQueue::find(queue q, InstListIt it) +{ + InstListIt iq_it, iq_end_it; + switch(q) { + case NonSpec: + iq_it = nonSpec.begin(); + iq_end_it = nonSpec.end(); + break; + case IQ: + iq_it = iq.begin(); + iq_end_it = iq.end(); + break; + case ToBeScheduled: + iq_it = toBeScheduled.begin(); + iq_end_it = toBeScheduled.end(); + break; + case ReadyList: + iq_it = readyList.begin(); + iq_end_it = readyList.end(); + break; + case ReplayList: + iq_it = replayList.begin(); + iq_end_it = replayList.end(); + } + + while (iq_it != it && iq_it != iq_end_it) { + iq_it++; + } + if (iq_it == it) { + return true; + } else { + return false; + } +} + +template <class Impl> +void +BackEnd<Impl>::InstQueue::dumpInsts() +{ + cprintf("IQ size: %i\n", iq.size()); + + InstListIt inst_list_it = --iq.end(); + + int num = 0; + int valid_num = 0; + while (inst_list_it != iq.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } + + cprintf("nonSpec size: %i\n", nonSpec.size()); + + inst_list_it = --nonSpec.end(); + + while (inst_list_it != nonSpec.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } + + cprintf("toBeScheduled size: %i\n", toBeScheduled.size()); + + inst_list_it = --toBeScheduled.end(); + + while (inst_list_it != toBeScheduled.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } + + cprintf("readyList size: %i\n", readyList.size()); + + inst_list_it = --readyList.end(); + + while (inst_list_it != readyList.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } +} + +template<class Impl> +BackEnd<Impl>::LdWritebackEvent::LdWritebackEvent(DynInstPtr &_inst, + BackEnd<Impl> *_be) + : Event(&mainEventQueue), inst(_inst), be(_be) +{ + this->setFlags(Event::AutoDelete); +} + +template<class Impl> +void +BackEnd<Impl>::LdWritebackEvent::process() +{ + DPRINTF(BE, "Load writeback event [sn:%lli]\n", inst->seqNum); +// DPRINTF(Activity, "Activity: Ld Writeback event [sn:%lli]\n", inst->seqNum); + + //iewStage->ldstQueue.removeMSHR(inst->threadNumber,inst->seqNum); + +// iewStage->wakeCPU(); + + if (inst->isSquashed()) { + inst = NULL; + return; + } + + if (!inst->isExecuted()) { + inst->setExecuted(); + + // Execute again to copy data to proper place. + inst->completeAcc(); + } + + // Need to insert instruction into queue to commit + be->instToCommit(inst); + + //wroteToTimeBuffer = true; +// iewStage->activityThisCycle(); + + inst = NULL; +} + +template<class Impl> +const char * +BackEnd<Impl>::LdWritebackEvent::description() +{ + return "Load writeback event"; +} + + +template <class Impl> +BackEnd<Impl>::DCacheCompletionEvent::DCacheCompletionEvent(BackEnd *_be) + : Event(&mainEventQueue, CPU_Tick_Pri), be(_be) +{ +} + +template <class Impl> +void +BackEnd<Impl>::DCacheCompletionEvent::process() +{ +} + +template <class Impl> +const char * +BackEnd<Impl>::DCacheCompletionEvent::description() +{ + return "Cache completion event"; +} + +template <class Impl> +BackEnd<Impl>::BackEnd(Params *params) + : d2i(5, 5), i2e(5, 5), e2c(5, 5), numInstsToWB(5, 5), + xcSquash(false), IQ(params), + cacheCompletionEvent(this), width(params->backEndWidth), + exactFullStall(true) +{ + numROBEntries = params->numROBEntries; + numInsts = 0; + numDispatchEntries = 32; + IQ.setBE(this); + LSQ.setBE(this); + + // Setup IQ and LSQ with their parameters here. + instsToDispatch = d2i.getWire(-1); + + instsToExecute = i2e.getWire(-1); + + IQ.setIssueExecQueue(&i2e); + + dispatchWidth = params->dispatchWidth ? params->dispatchWidth : width; + issueWidth = params->issueWidth ? params->issueWidth : width; + wbWidth = params->wbWidth ? params->wbWidth : width; + commitWidth = params->commitWidth ? params->commitWidth : width; + + LSQ.init(params, params->LQEntries, params->SQEntries, 0); + + dispatchStatus = Running; +} + +template <class Impl> +std::string +BackEnd<Impl>::name() const +{ + return cpu->name() + ".backend"; +} + +template <class Impl> +void +BackEnd<Impl>::regStats() +{ + using namespace Stats; + rob_cap_events + .init(cpu->number_of_threads) + .name(name() + ".ROB:cap_events") + .desc("number of cycles where ROB cap was active") + .flags(total) + ; + + rob_cap_inst_count + .init(cpu->number_of_threads) + .name(name() + ".ROB:cap_inst") + .desc("number of instructions held up by ROB cap") + .flags(total) + ; + + iq_cap_events + .init(cpu->number_of_threads) + .name(name() +".IQ:cap_events" ) + .desc("number of cycles where IQ cap was active") + .flags(total) + ; + + iq_cap_inst_count + .init(cpu->number_of_threads) + .name(name() + ".IQ:cap_inst") + .desc("number of instructions held up by IQ cap") + .flags(total) + ; + + + exe_inst + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:count") + .desc("number of insts issued") + .flags(total) + ; + + exe_swp + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:swp") + .desc("number of swp insts issued") + .flags(total) + ; + + exe_nop + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:nop") + .desc("number of nop insts issued") + .flags(total) + ; + + exe_refs + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:refs") + .desc("number of memory reference insts issued") + .flags(total) + ; + + exe_loads + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:loads") + .desc("number of load insts issued") + .flags(total) + ; + + exe_branches + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:branches") + .desc("Number of branches issued") + .flags(total) + ; + + issued_ops + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:op_count") + .desc("number of insts issued") + .flags(total) + ; + +/* + for (int i=0; i<Num_OpClasses; ++i) { + stringstream subname; + subname << opClassStrings[i] << "_delay"; + issue_delay_dist.subname(i, subname.str()); + } +*/ + // + // Other stats + // + lsq_forw_loads + .init(cpu->number_of_threads) + .name(name() + ".LSQ:forw_loads") + .desc("number of loads forwarded via LSQ") + .flags(total) + ; + + inv_addr_loads + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:addr_loads") + .desc("number of invalid-address loads") + .flags(total) + ; + + inv_addr_swpfs + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:addr_swpfs") + .desc("number of invalid-address SW prefetches") + .flags(total) + ; + + lsq_blocked_loads + .init(cpu->number_of_threads) + .name(name() + ".LSQ:blocked_loads") + .desc("number of ready loads not issued due to memory disambiguation") + .flags(total) + ; + + lsqInversion + .name(name() + ".ISSUE:lsq_invert") + .desc("Number of times LSQ instruction issued early") + ; + + n_issued_dist + .init(issueWidth + 1) + .name(name() + ".ISSUE:issued_per_cycle") + .desc("Number of insts issued each cycle") + .flags(total | pdf | dist) + ; + issue_delay_dist + .init(Num_OpClasses,0,99,2) + .name(name() + ".ISSUE:") + .desc("cycles from operands ready to issue") + .flags(pdf | cdf) + ; + + queue_res_dist + .init(Num_OpClasses, 0, 99, 2) + .name(name() + ".IQ:residence:") + .desc("cycles from dispatch to issue") + .flags(total | pdf | cdf ) + ; + for (int i = 0; i < Num_OpClasses; ++i) { + queue_res_dist.subname(i, opClassStrings[i]); + } + + writeback_count + .init(cpu->number_of_threads) + .name(name() + ".WB:count") + .desc("cumulative count of insts written-back") + .flags(total) + ; + + producer_inst + .init(cpu->number_of_threads) + .name(name() + ".WB:producers") + .desc("num instructions producing a value") + .flags(total) + ; + + consumer_inst + .init(cpu->number_of_threads) + .name(name() + ".WB:consumers") + .desc("num instructions consuming a value") + .flags(total) + ; + + wb_penalized + .init(cpu->number_of_threads) + .name(name() + ".WB:penalized") + .desc("number of instrctions required to write to 'other' IQ") + .flags(total) + ; + + + wb_penalized_rate + .name(name() + ".WB:penalized_rate") + .desc ("fraction of instructions written-back that wrote to 'other' IQ") + .flags(total) + ; + + wb_penalized_rate = wb_penalized / writeback_count; + + wb_fanout + .name(name() + ".WB:fanout") + .desc("average fanout of values written-back") + .flags(total) + ; + + wb_fanout = producer_inst / consumer_inst; + + wb_rate + .name(name() + ".WB:rate") + .desc("insts written-back per cycle") + .flags(total) + ; + wb_rate = writeback_count / cpu->numCycles; + + stat_com_inst + .init(cpu->number_of_threads) + .name(name() + ".COM:count") + .desc("Number of instructions committed") + .flags(total) + ; + + stat_com_swp + .init(cpu->number_of_threads) + .name(name() + ".COM:swp_count") + .desc("Number of s/w prefetches committed") + .flags(total) + ; + + stat_com_refs + .init(cpu->number_of_threads) + .name(name() + ".COM:refs") + .desc("Number of memory references committed") + .flags(total) + ; + + stat_com_loads + .init(cpu->number_of_threads) + .name(name() + ".COM:loads") + .desc("Number of loads committed") + .flags(total) + ; + + stat_com_membars + .init(cpu->number_of_threads) + .name(name() + ".COM:membars") + .desc("Number of memory barriers committed") + .flags(total) + ; + + stat_com_branches + .init(cpu->number_of_threads) + .name(name() + ".COM:branches") + .desc("Number of branches committed") + .flags(total) + ; + n_committed_dist + .init(0,commitWidth,1) + .name(name() + ".COM:committed_per_cycle") + .desc("Number of insts commited each cycle") + .flags(pdf) + ; + + // + // Commit-Eligible instructions... + // + // -> The number of instructions eligible to commit in those + // cycles where we reached our commit BW limit (less the number + // actually committed) + // + // -> The average value is computed over ALL CYCLES... not just + // the BW limited cycles + // + // -> The standard deviation is computed only over cycles where + // we reached the BW limit + // + commit_eligible + .init(cpu->number_of_threads) + .name(name() + ".COM:bw_limited") + .desc("number of insts not committed due to BW limits") + .flags(total) + ; + + commit_eligible_samples + .name(name() + ".COM:bw_lim_events") + .desc("number cycles where commit BW limit reached") + ; + + ROB_fcount + .name(name() + ".ROB:full_count") + .desc("number of cycles where ROB was full") + ; + + ROB_count + .init(cpu->number_of_threads) + .name(name() + ".ROB:occupancy") + .desc(name() + ".ROB occupancy (cumulative)") + .flags(total) + ; + + ROB_full_rate + .name(name() + ".ROB:full_rate") + .desc("ROB full per cycle") + ; + ROB_full_rate = ROB_fcount / cpu->numCycles; + + ROB_occ_rate + .name(name() + ".ROB:occ_rate") + .desc("ROB occupancy rate") + .flags(total) + ; + ROB_occ_rate = ROB_count / cpu->numCycles; + + ROB_occ_dist + .init(cpu->number_of_threads,0,numROBEntries,2) + .name(name() + ".ROB:occ_dist") + .desc("ROB Occupancy per cycle") + .flags(total | cdf) + ; + + IQ.regStats(); +} + +template <class Impl> +void +BackEnd<Impl>::setCommBuffer(TimeBuffer<CommStruct> *_comm) +{ + comm = _comm; + toIEW = comm->getWire(0); + fromCommit = comm->getWire(-1); +} + +template <class Impl> +void +BackEnd<Impl>::tick() +{ + DPRINTF(BE, "Ticking back end\n"); + + ROB_count[0]+= numInsts; + + wbCycle = 0; + + if (xcSquash) { + squashFromXC(); + } + + // Read in any done instruction information and update the IQ or LSQ. + updateStructures(); + + if (dispatchStatus != Blocked) { + d2i.advance(); + dispatchInsts(); + } else { + checkDispatchStatus(); + } + + i2e.advance(); + scheduleReadyInsts(); + + e2c.advance(); + executeInsts(); + + numInstsToWB.advance(); + writebackInsts(); + + commitInsts(); + + DPRINTF(BE, "IQ entries in use: %i, ROB entries in use: %i, LSQ loads: %i, LSQ stores: %i\n", + IQ.numInsts, numInsts, LSQ.numLoads(), LSQ.numStores()); + + assert(numInsts == instList.size()); +} + +template <class Impl> +void +BackEnd<Impl>::updateStructures() +{ + if (fromCommit->doneSeqNum) { + IQ.commit(fromCommit->doneSeqNum); + LSQ.commitLoads(fromCommit->doneSeqNum); + LSQ.commitStores(fromCommit->doneSeqNum); + } + + if (fromCommit->nonSpecSeqNum) { + if (fromCommit->uncached) { + LSQ.executeLoad(fromCommit->lqIdx); + } else { + IQ.scheduleNonSpec( + fromCommit->nonSpecSeqNum); + } + } +} + +template <class Impl> +void +BackEnd<Impl>::addToIQ(DynInstPtr &inst) +{ + // Do anything IQ specific here? + IQ.insert(inst); +} + +template <class Impl> +void +BackEnd<Impl>::addToLSQ(DynInstPtr &inst) +{ + // Do anything LSQ specific here? + LSQ.insert(inst); +} + +template <class Impl> +void +BackEnd<Impl>::dispatchInsts() +{ + DPRINTF(BE, "Trying to dispatch instructions.\n"); + + // Pull instructions out of the front end. + int disp_width = dispatchWidth ? dispatchWidth : width; + + // Could model dispatching time, but in general 1 cycle is probably + // good enough. + + if (dispatchSize < numDispatchEntries) { + for (int i = 0; i < disp_width; i++) { + // Get instructions + DynInstPtr inst = frontEnd->getInst(); + + if (!inst) { + // No more instructions to get + break; + } + + DPRINTF(BE, "Processing instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + for (int i = 0; i < inst->numDestRegs(); ++i) + renameTable[inst->destRegIdx(i)] = inst; + + // Add to queue to be dispatched. + dispatch.push_back(inst); + + d2i[0].size++; + ++dispatchSize; + } + } + + assert(dispatch.size() < 64); + + for (int i = 0; i < instsToDispatch->size; ++i) { + assert(!dispatch.empty()); + // Get instruction from front of time buffer + DynInstPtr inst = dispatch.front(); + dispatch.pop_front(); + --dispatchSize; + + if (inst->isSquashed()) + continue; + + ++numInsts; + instList.push_back(inst); + + DPRINTF(BE, "Dispatching instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + addToIQ(inst); + + if (inst->isMemRef()) { + addToLSQ(inst); + } + + if (inst->isNonSpeculative()) { + inst->setCanCommit(); + } + + // Check if IQ or LSQ is full. If so we'll need to break and stop + // removing instructions. Also update the number of insts to remove + // from the queue. + if (exactFullStall) { + bool stall = false; + if (IQ.isFull()) { + DPRINTF(BE, "IQ is full!\n"); + stall = true; + } else if (LSQ.isFull()) { + DPRINTF(BE, "LSQ is full!\n"); + stall = true; + } else if (isFull()) { + DPRINTF(BE, "ROB is full!\n"); + stall = true; + ROB_fcount++; + } + if (stall) { + instsToDispatch->size-= i+1; + dispatchStall(); + return; + } + } + } + + // Check if IQ or LSQ is full. If so we'll need to break and stop + // removing instructions. Also update the number of insts to remove + // from the queue. Check here if we don't care about exact stall + // conditions. + + bool stall = false; + if (IQ.isFull()) { + DPRINTF(BE, "IQ is full!\n"); + stall = true; + } else if (LSQ.isFull()) { + DPRINTF(BE, "LSQ is full!\n"); + stall = true; + } else if (isFull()) { + DPRINTF(BE, "ROB is full!\n"); + stall = true; + ROB_fcount++; + } + if (stall) { + d2i.advance(); + dispatchStall(); + return; + } +} + +template <class Impl> +void +BackEnd<Impl>::dispatchStall() +{ + dispatchStatus = Blocked; + if (!cpu->decoupledFrontEnd) { + // Tell front end to stall here through a timebuffer, or just tell + // it directly. + } +} + +template <class Impl> +void +BackEnd<Impl>::checkDispatchStatus() +{ + DPRINTF(BE, "Checking dispatch status\n"); + assert(dispatchStatus == Blocked); + if (!IQ.isFull() && !LSQ.isFull() && !isFull()) { + DPRINTF(BE, "Dispatch no longer blocked\n"); + dispatchStatus = Running; + dispatchInsts(); + } +} + +template <class Impl> +void +BackEnd<Impl>::scheduleReadyInsts() +{ + // Tell IQ to put any ready instructions into the instruction list. + // Probably want to have a list of DynInstPtrs returned here. Then I + // can choose to either put them into a time buffer to simulate + // IQ scheduling time, or hand them directly off to the next stage. + // Do you ever want to directly hand it off to the next stage? + DPRINTF(BE, "Trying to schedule ready instructions\n"); + IQ.scheduleReadyInsts(); +} + +template <class Impl> +void +BackEnd<Impl>::executeInsts() +{ + int insts_to_execute = instsToExecute->size; + + issued_ops[0]+= insts_to_execute; + n_issued_dist[insts_to_execute]++; + + DPRINTF(BE, "Trying to execute %i instructions\n", insts_to_execute); + + fetchRedirect[0] = false; + + while (insts_to_execute > 0) { + // Get ready instruction from the IQ (or queue coming out of IQ) + // Execute the ready instruction. + // Wakeup any dependents if it's done. + DynInstPtr inst = IQ.getReadyInst(); + + DPRINTF(BE, "Executing inst [sn:%lli] PC: %#x\n", + inst->seqNum, inst->readPC()); + + ++funcExeInst; + + // Check if the instruction is squashed; if so then skip it + // and don't count it towards the FU usage. + if (inst->isSquashed()) { + DPRINTF(BE, "Execute: Instruction was squashed.\n"); + + // Not sure how to handle this plus the method of sending # of + // instructions to use. Probably will just have to count it + // towards the bandwidth usage, but not the FU usage. + --insts_to_execute; + + // Consider this instruction executed so that commit can go + // ahead and retire the instruction. + inst->setExecuted(); + + // Not sure if I should set this here or just let commit try to + // commit any squashed instructions. I like the latter a bit more. + inst->setCanCommit(); + +// ++iewExecSquashedInsts; + + continue; + } + + Fault fault = NoFault; + + // Execute instruction. + // Note that if the instruction faults, it will be handled + // at the commit stage. + if (inst->isMemRef() && + (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { + DPRINTF(BE, "Execute: Initiating access for memory " + "reference.\n"); + + // Tell the LDSTQ to execute this instruction (if it is a load). + if (inst->isLoad()) { + // Loads will mark themselves as executed, and their writeback + // event adds the instruction to the queue to commit + fault = LSQ.executeLoad(inst); + +// ++iewExecLoadInsts; + } else if (inst->isStore()) { + LSQ.executeStore(inst); + +// ++iewExecStoreInsts; + + if (!(inst->req->flags & LOCKED)) { + inst->setExecuted(); + + instToCommit(inst); + } + // Store conditionals will mark themselves as executed, and + // their writeback event will add the instruction to the queue + // to commit. + } else { + panic("Unexpected memory type!\n"); + } + + } else { + inst->execute(); + +// ++iewExecutedInsts; + + inst->setExecuted(); + + instToCommit(inst); + } + + updateExeInstStats(inst); + + // Probably should have some sort of function for this. + // More general question of how to handle squashes? Have some sort of + // squash unit that controls it? Probably... + // Check if branch was correct. This check happens after the + // instruction is added to the queue because even if the branch + // is mispredicted, the branch instruction itself is still valid. + // Only handle this if there hasn't already been something that + // redirects fetch in this group of instructions. + + // This probably needs to prioritize the redirects if a different + // scheduler is used. Currently the scheduler schedules the oldest + // instruction first, so the branch resolution order will be correct. + unsigned tid = inst->threadNumber; + + if (!fetchRedirect[tid]) { + + if (inst->mispredicted()) { + fetchRedirect[tid] = true; + + DPRINTF(BE, "Execute: Branch mispredict detected.\n"); + DPRINTF(BE, "Execute: Redirecting fetch to PC: %#x.\n", + inst->nextPC); + + // If incorrect, then signal the ROB that it must be squashed. + squashDueToBranch(inst); + + if (inst->predTaken()) { +// predictedTakenIncorrect++; + } else { +// predictedNotTakenIncorrect++; + } + } else if (LSQ.violation()) { + fetchRedirect[tid] = true; + + // Get the DynInst that caused the violation. Note that this + // clears the violation signal. + DynInstPtr violator; + violator = LSQ.getMemDepViolator(); + + DPRINTF(BE, "LDSTQ detected a violation. Violator PC: " + "%#x, inst PC: %#x. Addr is: %#x.\n", + violator->readPC(), inst->readPC(), inst->physEffAddr); + + // Tell the instruction queue that a violation has occured. +// IQ.violation(inst, violator); + + // Squash. +// squashDueToMemOrder(inst,tid); + squashDueToBranch(inst); + +// ++memOrderViolationEvents; + } else if (LSQ.loadBlocked()) { + fetchRedirect[tid] = true; + + DPRINTF(BE, "Load operation couldn't execute because the " + "memory system is blocked. PC: %#x [sn:%lli]\n", + inst->readPC(), inst->seqNum); + + squashDueToMemBlocked(inst); + } + } + +// instList.pop_front(); + + --insts_to_execute; + + // keep an instruction count + thread->numInst++; + thread->numInsts++; + } + + assert(insts_to_execute >= 0); +} + +template<class Impl> +void +BackEnd<Impl>::instToCommit(DynInstPtr &inst) +{ + int wb_width = wbWidth; + // First check the time slot that this instruction will write + // to. If there are free write ports at the time, then go ahead + // and write the instruction to that time. If there are not, + // keep looking back to see where's the first time there's a + // free slot. What happens if you run out of free spaces? + // For now naively assume that all instructions take one cycle. + // Otherwise would have to look into the time buffer based on the + // latency of the instruction. + + DPRINTF(BE, "Sending instructions to commit [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + while (numInstsToWB[wbCycle].size >= wb_width) { + ++wbCycle; + + assert(wbCycle < 5); + } + + // Add finished instruction to queue to commit. + writeback.push_back(inst); + numInstsToWB[wbCycle].size++; + + if (wbCycle) + wb_penalized[0]++; +} + +template <class Impl> +void +BackEnd<Impl>::writebackInsts() +{ + int wb_width = wbWidth; + // Using this method I'm not quite sure how to prevent an + // instruction from waking its own dependents multiple times, + // without the guarantee that commit always has enough bandwidth + // to accept all instructions being written back. This guarantee + // might not be too unrealistic. + InstListIt wb_inst_it = writeback.begin(); + InstListIt wb_end_it = writeback.end(); + int inst_num = 0; + int consumer_insts = 0; + + for (; inst_num < wb_width && + wb_inst_it != wb_end_it; inst_num++) { + DynInstPtr inst = (*wb_inst_it); + + // Some instructions will be sent to commit without having + // executed because they need commit to handle them. + // E.g. Uncached loads have not actually executed when they + // are first sent to commit. Instead commit must tell the LSQ + // when it's ready to execute the uncached load. + if (!inst->isSquashed()) { + DPRINTF(BE, "Writing back instruction [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + inst->setCanCommit(); + inst->setResultReady(); + + if (inst->isExecuted()) { + int dependents = IQ.wakeDependents(inst); + if (dependents) { + producer_inst[0]++; + consumer_insts+= dependents; + } + } + } + + writeback.erase(wb_inst_it++); + } + LSQ.writebackStores(); + consumer_inst[0]+= consumer_insts; + writeback_count[0]+= inst_num; +} + +template <class Impl> +bool +BackEnd<Impl>::commitInst(int inst_num) +{ + // Read instruction from the head of the ROB + DynInstPtr inst = instList.front(); + + // Make sure instruction is valid + assert(inst); + + if (!inst->readyToCommit()) + return false; + + DPRINTF(BE, "Trying to commit instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + // If the instruction is not executed yet, then it is a non-speculative + // or store inst. Signal backwards that it should be executed. + if (!inst->isExecuted()) { + // Keep this number correct. We have not yet actually executed + // and committed this instruction. +// thread->funcExeInst--; + + if (inst->isNonSpeculative()) { +#if !FULL_SYSTEM + // Hack to make sure syscalls aren't executed until all stores + // write back their data. This direct communication shouldn't + // be used for anything other than this. + if (inst_num > 0 || LSQ.hasStoresToWB()) { + DPRINTF(BE, "Waiting for all stores to writeback.\n"); + return false; + } +#endif + + DPRINTF(BE, "Encountered a store or non-speculative " + "instruction at the head of the ROB, PC %#x.\n", + inst->readPC()); + + // Send back the non-speculative instruction's sequence number. + toIEW->nonSpecSeqNum = inst->seqNum; + + // Change the instruction so it won't try to commit again until + // it is executed. + inst->clearCanCommit(); + +// ++commitNonSpecStalls; + + return false; + } else if (inst->isLoad()) { + DPRINTF(BE, "[sn:%lli]: Uncached load, PC %#x.\n", + inst->seqNum, inst->readPC()); + + // Send back the non-speculative instruction's sequence + // number. Maybe just tell the lsq to re-execute the load. + toIEW->nonSpecSeqNum = inst->seqNum; + toIEW->uncached = true; + toIEW->lqIdx = inst->lqIdx; + + inst->clearCanCommit(); + + return false; + } else { + panic("Trying to commit un-executed instruction " + "of unknown type!\n"); + } + } + + // Now check if it's one of the special trap or barrier or + // serializing instructions. + if (inst->isThreadSync()) + { + // Not handled for now. + panic("Barrier instructions are not handled yet.\n"); + } + + // Check if the instruction caused a fault. If so, trap. + Fault inst_fault = inst->getFault(); + + if (inst_fault != NoFault) { + if (!inst->isNop()) { +#if FULL_SYSTEM + DPRINTF(BE, "Inst [sn:%lli] PC %#x has a fault\n", + inst->seqNum, inst->readPC()); + +// assert(!thread->inSyscall); + +// thread->inSyscall = true; + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + inst_fault->invoke(thread->getXCProxy()); + + // Exit state update mode to avoid accidental updating. +// thread->inSyscall = false; + +// commitStatus = TrapPending; + + // Generate trap squash event. +// generateTrapEvent(); + + return false; +#else // !FULL_SYSTEM + panic("fault (%d) detected @ PC %08p", inst_fault, + inst->PC); +#endif // FULL_SYSTEM + } + } + + if (inst->isControl()) { +// ++commitCommittedBranches; + } + + int freed_regs = 0; + + for (int i = 0; i < inst->numDestRegs(); ++i) { + DPRINTF(BE, "Commit rename map setting register %i to [sn:%lli]\n", + (int)inst->destRegIdx(i), inst->seqNum); + thread->renameTable[inst->destRegIdx(i)] = inst; + ++freed_regs; + } + + if (inst->traceData) { + inst->traceData->finalize(); + inst->traceData = NULL; + } + + inst->clearDependents(); + + frontEnd->addFreeRegs(freed_regs); + + instList.pop_front(); + + --numInsts; + cpu->numInst++; + thread->numInsts++; + ++thread->funcExeInst; + thread->PC = inst->readNextPC(); + updateComInstStats(inst); + + // Write the done sequence number here. + toIEW->doneSeqNum = inst->seqNum; + +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + if (count == 0) + assert(!thread->inSyscall && !thread->trapPending); + oldpc = thread->readPC(); + cpu->system->pcEventQueue.service( + thread->getXCProxy()); + count++; + } while (oldpc != thread->readPC()); + if (count > 1) { + DPRINTF(BE, "PC skip function event, stopping commit\n"); +// completed_last_inst = false; +// squashPending = true; + return false; + } +#endif + return true; +} + +template <class Impl> +void +BackEnd<Impl>::commitInsts() +{ + int commit_width = commitWidth ? commitWidth : width; + + // Not sure this should be a loop or not. + int inst_num = 0; + while (!instList.empty() && inst_num < commit_width) { + if (instList.front()->isSquashed()) { + panic("No squashed insts should still be on the list!"); + instList.front()->clearDependents(); + instList.pop_front(); + continue; + } + + if (!commitInst(inst_num++)) { + break; + } + } + n_committed_dist.sample(inst_num); +} + +template <class Impl> +void +BackEnd<Impl>::squash(const InstSeqNum &sn) +{ + IQ.squash(sn); + LSQ.squash(sn); + + int freed_regs = 0; + InstListIt dispatch_end = dispatch.end(); + InstListIt insts_it = dispatch.end(); + insts_it--; + + while (insts_it != dispatch_end && (*insts_it)->seqNum > sn) + { + if ((*insts_it)->isSquashed()) { + --insts_it; + continue; + } + DPRINTF(BE, "Squashing instruction on dispatch list PC %#x, [sn:%lli].\n", + (*insts_it)->readPC(), + (*insts_it)->seqNum); + + // Mark the instruction as squashed, and ready to commit so that + // it can drain out of the pipeline. + (*insts_it)->setSquashed(); + + (*insts_it)->setCanCommit(); + + // Be careful with IPRs and such here + for (int i = 0; i < (*insts_it)->numDestRegs(); ++i) { + DynInstPtr prev_dest = (*insts_it)->getPrevDestInst(i); + DPRINTF(BE, "Commit rename map setting register %i to [sn:%lli]\n", + (int)(*insts_it)->destRegIdx(i), prev_dest); + renameTable[(*insts_it)->destRegIdx(i)] = prev_dest; + ++freed_regs; + } + + (*insts_it)->clearDependents(); + + --insts_it; + } + + insts_it = instList.end(); + insts_it--; + + while (!instList.empty() && (*insts_it)->seqNum > sn) + { + if ((*insts_it)->isSquashed()) { + --insts_it; + continue; + } + DPRINTF(BE, "Squashing instruction on inst list PC %#x, [sn:%lli].\n", + (*insts_it)->readPC(), + (*insts_it)->seqNum); + + // Mark the instruction as squashed, and ready to commit so that + // it can drain out of the pipeline. + (*insts_it)->setSquashed(); + + (*insts_it)->setCanCommit(); + + for (int i = 0; i < (*insts_it)->numDestRegs(); ++i) { + DynInstPtr prev_dest = (*insts_it)->getPrevDestInst(i); + DPRINTF(BE, "Commit rename map setting register %i to [sn:%lli]\n", + (int)(*insts_it)->destRegIdx(i), prev_dest); + renameTable[(*insts_it)->destRegIdx(i)] = prev_dest; + ++freed_regs; + } + + (*insts_it)->clearDependents(); + + instList.erase(insts_it--); + --numInsts; + } + + frontEnd->addFreeRegs(freed_regs); +} + +template <class Impl> +void +BackEnd<Impl>::squashFromXC() +{ + xcSquash = true; +} + +template <class Impl> +void +BackEnd<Impl>::squashDueToBranch(DynInstPtr &inst) +{ + // Update the branch predictor state I guess + squash(inst->seqNum); + frontEnd->squash(inst->seqNum, inst->readNextPC(), + true, inst->mispredicted()); +} + +template <class Impl> +void +BackEnd<Impl>::squashDueToMemBlocked(DynInstPtr &inst) +{ + DPRINTF(IEW, "Memory blocked, squashing load and younger insts, " + "PC: %#x [sn:%i].\n", inst->readPC(), inst->seqNum); + + squash(inst->seqNum - 1); + frontEnd->squash(inst->seqNum - 1, inst->readPC()); +} + +template <class Impl> +void +BackEnd<Impl>::fetchFault(Fault &fault) +{ + faultFromFetch = fault; +} + +template <class Impl> +void +BackEnd<Impl>::updateExeInstStats(DynInstPtr &inst) +{ + int thread_number = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) + exe_swp[thread_number]++; + else + exe_inst[thread_number]++; +#else + exe_inst[thread_number]++; +#endif + + // + // Control operations + // + if (inst->isControl()) + exe_branches[thread_number]++; + + // + // Memory operations + // + if (inst->isMemRef()) { + exe_refs[thread_number]++; + + if (inst->isLoad()) + exe_loads[thread_number]++; + } +} + +template <class Impl> +void +BackEnd<Impl>::updateComInstStats(DynInstPtr &inst) +{ + unsigned thread = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) { + stat_com_swp[thread]++; + } else { + stat_com_inst[thread]++; + } +#else + stat_com_inst[thread]++; +#endif + + // + // Control Instructions + // + if (inst->isControl()) + stat_com_branches[thread]++; + + // + // Memory references + // + if (inst->isMemRef()) { + stat_com_refs[thread]++; + + if (inst->isLoad()) { + stat_com_loads[thread]++; + } + } + + if (inst->isMemBarrier()) { + stat_com_membars[thread]++; + } +} + +template <class Impl> +void +BackEnd<Impl>::dumpInsts() +{ + int num = 0; + int valid_num = 0; + + InstListIt inst_list_it = instList.begin(); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } + + cprintf("Dispatch list size: %i\n", dispatch.size()); + + inst_list_it = dispatch.begin(); + + while (inst_list_it != dispatch.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } + + cprintf("Writeback list size: %i\n", writeback.size()); + + inst_list_it = writeback.begin(); + + while (inst_list_it != writeback.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } +} diff --git a/src/cpu/ozone/base_dyn_inst.cc b/src/cpu/ozone/base_dyn_inst.cc new file mode 100644 index 000000000..5a3a69dff --- /dev/null +++ b/src/cpu/ozone/base_dyn_inst.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/base_dyn_inst_impl.hh" +#include "cpu/ozone/ozone_impl.hh" + +// Explicit instantiation +template class BaseDynInst<OzoneImpl>; + +template <> +int +BaseDynInst<OzoneImpl>::instcount = 0; diff --git a/src/cpu/ozone/bpred_unit.cc b/src/cpu/ozone/bpred_unit.cc new file mode 100644 index 000000000..c823f5e80 --- /dev/null +++ b/src/cpu/ozone/bpred_unit.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/o3/bpred_unit_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +//#include "cpu/ozone/simple_impl.hh" + +template class BPredUnit<OzoneImpl>; +//template class BPredUnit<SimpleImpl>; diff --git a/src/cpu/ozone/checker_builder.cc b/src/cpu/ozone/checker_builder.cc new file mode 100644 index 000000000..99ba3e308 --- /dev/null +++ b/src/cpu/ozone/checker_builder.cc @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include <string> + +#include "cpu/checker/cpu_impl.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +class MemObject; + +template +class Checker<RefCountingPtr<OzoneDynInst<OzoneImpl> > >; + +/** + * Specific non-templated derived class used for SimObject configuration. + */ +class OzoneChecker : + public Checker<RefCountingPtr<OzoneDynInst<OzoneImpl> > > +{ + public: + OzoneChecker(Params *p) + : Checker<RefCountingPtr<OzoneDynInst<OzoneImpl> > >(p) + { } +}; + +//////////////////////////////////////////////////////////////////////// +// +// CheckerCPU Simulation Object +// +BEGIN_DECLARE_SIM_OBJECT_PARAMS(OzoneChecker) + + Param<Counter> max_insts_any_thread; + Param<Counter> max_insts_all_threads; + Param<Counter> max_loads_any_thread; + Param<Counter> max_loads_all_threads; + Param<Counter> stats_reset_inst; + Param<Tick> progress_interval; + +#if FULL_SYSTEM + SimObjectParam<AlphaITB *> itb; + SimObjectParam<AlphaDTB *> dtb; + SimObjectParam<System *> system; + Param<int> cpu_id; + Param<Tick> profile; +#else + SimObjectParam<Process *> workload; +#endif // FULL_SYSTEM + Param<int> clock; + + Param<bool> defer_registration; + Param<bool> exitOnError; + Param<bool> updateOnError; + Param<bool> warnOnlyOnLoadError; + Param<bool> function_trace; + Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(OzoneChecker) + +BEGIN_INIT_SIM_OBJECT_PARAMS(OzoneChecker) + + INIT_PARAM(max_insts_any_thread, + "terminate when any thread reaches this inst count"), + INIT_PARAM(max_insts_all_threads, + "terminate when all threads have reached this inst count"), + INIT_PARAM(max_loads_any_thread, + "terminate when any thread reaches this load count"), + INIT_PARAM(max_loads_all_threads, + "terminate when all threads have reached this load count"), + INIT_PARAM(stats_reset_inst, + "blah"), + INIT_PARAM_DFLT(progress_interval, "CPU Progress Interval", 0), + +#if FULL_SYSTEM + INIT_PARAM(itb, "Instruction TLB"), + INIT_PARAM(dtb, "Data TLB"), + INIT_PARAM(system, "system object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(profile, ""), +#else + INIT_PARAM(workload, "processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM(clock, "clock speed"), + + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + INIT_PARAM(exitOnError, "exit on error"), + INIT_PARAM(updateOnError, "Update the checker with the main CPU's state on error"), + INIT_PARAM_DFLT(warnOnlyOnLoadError, "warn, but don't exit, if a load " + "result errors", false), + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(OzoneChecker) + + +CREATE_SIM_OBJECT(OzoneChecker) +{ + OzoneChecker::Params *params = new OzoneChecker::Params(); + params->name = getInstanceName(); + params->numberOfThreads = 1; + params->max_insts_any_thread = 0; + params->max_insts_all_threads = 0; + params->max_loads_any_thread = 0; + params->max_loads_all_threads = 0; + params->stats_reset_inst = 0; + params->exitOnError = exitOnError; + params->updateOnError = updateOnError; + params->warnOnlyOnLoadError = warnOnlyOnLoadError; + params->deferRegistration = defer_registration; + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + params->clock = clock; + // Hack to touch all parameters. Consider not deriving Checker + // from BaseCPU..it's not really a CPU in the end. + Counter temp; + temp = max_insts_any_thread; + temp = max_insts_all_threads; + temp = max_loads_any_thread; + temp = max_loads_all_threads; + temp = stats_reset_inst; + Tick temp2 = progress_interval; + temp2++; + params->progress_interval = 0; + +#if FULL_SYSTEM + params->itb = itb; + params->dtb = dtb; + params->system = system; + params->cpu_id = cpu_id; + params->profile = profile; +#else + params->process = workload; +#endif + + OzoneChecker *cpu = new OzoneChecker(params); + return cpu; +} + +REGISTER_SIM_OBJECT("OzoneChecker", OzoneChecker) diff --git a/src/cpu/ozone/cpu.cc b/src/cpu/ozone/cpu.cc new file mode 100644 index 000000000..eb6ac37bd --- /dev/null +++ b/src/cpu/ozone/cpu.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Nathan Binkert + */ + +#include "cpu/ozone/cpu_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +//#include "cpu/ozone/simple_impl.hh" + +//template class OzoneCPU<SimpleImpl>; +template class OzoneCPU<OzoneImpl>; diff --git a/src/cpu/ozone/cpu.hh b/src/cpu/ozone/cpu.hh new file mode 100644 index 000000000..ece68282f --- /dev/null +++ b/src/cpu/ozone/cpu.hh @@ -0,0 +1,620 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_CPU_HH__ +#define __CPU_OZONE_CPU_HH__ + +#include <set> + +#include "base/statistics.hh" +#include "base/timebuf.hh" +#include "config/full_system.hh" +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/rename_table.hh" +#include "cpu/ozone/thread_state.hh" +#include "cpu/pc_event.hh" +#include "cpu/static_inst.hh" +#include "mem/page_table.hh" +#include "sim/eventq.hh" + +// forward declarations +#if FULL_SYSTEM +#include "arch/alpha/tlb.hh" + +class AlphaITB; +class AlphaDTB; +class PhysicalMemory; +class MemoryController; + +class RemoteGDB; +class GDBListener; + +namespace Kernel { + class Statistics; +}; + +#else + +class Process; + +#endif // FULL_SYSTEM + +class Checkpoint; +class EndQuiesceEvent; +class MemObject; +class Request; + +namespace Trace { + class InstRecord; +} + +template <class> +class Checker; + +/** + * Light weight out of order CPU model that approximates an out of + * order CPU. It is separated into a front end and a back end, with + * the template parameter Impl describing the classes used for each. + * The goal is to be able to specify through the Impl the class to use + * for the front end and back end, with different classes used to + * model different levels of detail. + */ +template <class Impl> +class OzoneCPU : public BaseCPU +{ + private: + typedef typename Impl::FrontEnd FrontEnd; + typedef typename Impl::BackEnd BackEnd; + typedef typename Impl::DynInst DynInst; + typedef typename Impl::DynInstPtr DynInstPtr; + + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscReg MiscReg; + + public: + class OzoneTC : public ThreadContext { + public: + OzoneCPU<Impl> *cpu; + + OzoneThreadState<Impl> *thread; + + BaseCPU *getCpuPtr(); + + void setCpuId(int id); + + int readCpuId() { return thread->readCpuId(); } + +#if FULL_SYSTEM + System *getSystemPtr() { return cpu->system; } + + PhysicalMemory *getPhysMemPtr() { return cpu->physmem; } + + AlphaITB *getITBPtr() { return cpu->itb; } + + AlphaDTB * getDTBPtr() { return cpu->dtb; } + + Kernel::Statistics *getKernelStats() + { return thread->getKernelStats(); } + + FunctionalPort *getPhysPort() { return thread->getPhysPort(); } + + VirtualPort *getVirtPort(ThreadContext *tc = NULL) + { return thread->getVirtPort(tc); } + + void delVirtPort(VirtualPort *vp); +#else + TranslatingPort *getMemPort() { return thread->getMemPort(); } + + Process *getProcessPtr() { return thread->getProcessPtr(); } +#endif + + Status status() const { return thread->status(); } + + void setStatus(Status new_status); + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + void activate(int delay = 1); + + /// Set the status to Suspended. + void suspend(); + + /// Set the status to Unallocated. + void deallocate(int delay = 0); + + /// Set the status to Halted. + void halt(); + +#if FULL_SYSTEM + void dumpFuncProfile(); +#endif + + void takeOverFrom(ThreadContext *old_context); + + void regStats(const std::string &name); + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + +#if FULL_SYSTEM + EndQuiesceEvent *getQuiesceEvent(); + + Tick readLastActivate(); + Tick readLastSuspend(); + + void profileClear(); + void profileSample(); +#endif + + int getThreadNum(); + + // Also somewhat obnoxious. Really only used for the TLB fault. + TheISA::MachInst getInst(); + + void copyArchRegs(ThreadContext *tc); + + void clearArchRegs(); + + uint64_t readIntReg(int reg_idx); + + FloatReg readFloatReg(int reg_idx, int width); + + FloatReg readFloatReg(int reg_idx); + + FloatRegBits readFloatRegBits(int reg_idx, int width); + + FloatRegBits readFloatRegBits(int reg_idx); + + void setIntReg(int reg_idx, uint64_t val); + + void setFloatReg(int reg_idx, FloatReg val, int width); + + void setFloatReg(int reg_idx, FloatReg val); + + void setFloatRegBits(int reg_idx, FloatRegBits val, int width); + + void setFloatRegBits(int reg_idx, FloatRegBits val); + + uint64_t readPC() { return thread->PC; } + void setPC(Addr val); + + uint64_t readNextPC() { return thread->nextPC; } + void setNextPC(Addr val); + + uint64_t readNextNPC() + { + return 0; + } + + void setNextNPC(uint64_t val) + { } + + public: + // ISA stuff: + MiscReg readMiscReg(int misc_reg); + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault); + + Fault setMiscReg(int misc_reg, const MiscReg &val); + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val); + + unsigned readStCondFailures() + { return thread->storeCondFailures; } + + void setStCondFailures(unsigned sc_failures) + { thread->storeCondFailures = sc_failures; } + +#if FULL_SYSTEM + bool inPalMode() { return cpu->inPalMode(); } +#endif + + bool misspeculating() { return false; } + +#if !FULL_SYSTEM + TheISA::IntReg getSyscallArg(int i) + { return thread->renameTable[TheISA::ArgumentReg0 + i]->readIntResult(); } + + // used to shift args for indirect syscall + void setSyscallArg(int i, TheISA::IntReg val) + { thread->renameTable[TheISA::ArgumentReg0 + i]->setIntResult(i); } + + void setSyscallReturn(SyscallReturn return_value) + { cpu->setSyscallReturn(return_value, thread->readTid()); } + + Counter readFuncExeInst() { return thread->funcExeInst; } + + void setFuncExeInst(Counter new_val) + { thread->funcExeInst = new_val; } +#endif + void changeRegFileContext(TheISA::RegFile::ContextParam param, + TheISA::RegFile::ContextVal val) + { panic("Not supported on Alpha!"); } + }; + + // Ozone specific thread context + OzoneTC ozoneTC; + // Thread context to be used + ThreadContext *tc; + // Checker thread context; will wrap the OzoneTC if a checker is + // being used. + ThreadContext *checkerTC; + + typedef OzoneThreadState<Impl> ImplState; + + private: + // Committed thread state for the OzoneCPU. + OzoneThreadState<Impl> thread; + + public: + // main simulation loop (one cycle) + void tick(); + + std::set<InstSeqNum> snList; + std::set<Addr> lockAddrList; + private: + struct TickEvent : public Event + { + OzoneCPU *cpu; + int width; + + TickEvent(OzoneCPU *c, int w); + void process(); + const char *description(); + }; + + TickEvent tickEvent; + + /// Schedule tick event, regardless of its current state. + void scheduleTickEvent(int delay) + { + if (tickEvent.squashed()) + tickEvent.reschedule(curTick + cycles(delay)); + else if (!tickEvent.scheduled()) + tickEvent.schedule(curTick + cycles(delay)); + } + + /// Unschedule tick event, regardless of its current state. + void unscheduleTickEvent() + { + if (tickEvent.scheduled()) + tickEvent.squash(); + } + + public: + enum Status { + Running, + Idle, + SwitchedOut + }; + + Status _status; + + public: + void post_interrupt(int int_num, int index); + + void zero_fill_64(Addr addr) { + static int warned = 0; + if (!warned) { + warn ("WH64 is not implemented"); + warned = 1; + } + }; + + typedef typename Impl::Params Params; + + OzoneCPU(Params *params); + + virtual ~OzoneCPU(); + + void init(); + + public: + BaseCPU *getCpuPtr() { return this; } + + void setCpuId(int id) { cpuId = id; } + + int readCpuId() { return cpuId; } + + int cpuId; + + void switchOut(); + void signalSwitched(); + void takeOverFrom(BaseCPU *oldCPU); + + int switchCount; + +#if FULL_SYSTEM + Addr dbg_vtophys(Addr addr); + + bool interval_stats; + + AlphaITB *itb; + AlphaDTB *dtb; + System *system; + PhysicalMemory *physmem; +#endif + + virtual Port *getPort(const std::string &name, int idx); + + MemObject *mem; + + FrontEnd *frontEnd; + + BackEnd *backEnd; + + private: + Status status() const { return _status; } + void setStatus(Status new_status) { _status = new_status; } + + virtual void activateContext(int thread_num, int delay); + virtual void suspendContext(int thread_num); + virtual void deallocateContext(int thread_num, int delay); + virtual void haltContext(int thread_num); + + // statistics + virtual void regStats(); + virtual void resetStats(); + + // number of simulated instructions + public: + Counter numInst; + Counter startNumInst; + + virtual Counter totalInstructions() const + { + return numInst - startNumInst; + } + + private: + // number of simulated loads + Counter numLoad; + Counter startNumLoad; + + // number of idle cycles + Stats::Average<> notIdleFraction; + Stats::Formula idleFraction; + + public: + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +#if FULL_SYSTEM + /** Translates instruction requestion. */ + Fault translateInstReq(RequestPtr &req, OzoneThreadState<Impl> *thread) + { + return itb->translate(req, thread->getTC()); + } + + /** Translates data read request. */ + Fault translateDataReadReq(RequestPtr &req, OzoneThreadState<Impl> *thread) + { + return dtb->translate(req, thread->getTC(), false); + } + + /** Translates data write request. */ + Fault translateDataWriteReq(RequestPtr &req, OzoneThreadState<Impl> *thread) + { + return dtb->translate(req, thread->getTC(), true); + } + +#else + /** Translates instruction requestion in syscall emulation mode. */ + Fault translateInstReq(RequestPtr &req, OzoneThreadState<Impl> *thread) + { + return thread->getProcessPtr()->pTable->translate(req); + } + + /** Translates data read request in syscall emulation mode. */ + Fault translateDataReadReq(RequestPtr &req, OzoneThreadState<Impl> *thread) + { + return thread->getProcessPtr()->pTable->translate(req); + } + + /** Translates data write request in syscall emulation mode. */ + Fault translateDataWriteReq(RequestPtr &req, OzoneThreadState<Impl> *thread) + { + return thread->getProcessPtr()->pTable->translate(req); + } +#endif + + /** Old CPU read from memory function. No longer used. */ + template <class T> + Fault read(Request *req, T &data) + { +#if 0 +#if FULL_SYSTEM && defined(TARGET_ALPHA) + if (req->flags & LOCKED) { + req->xc->setMiscReg(TheISA::Lock_Addr_DepTag, req->paddr); + req->xc->setMiscReg(TheISA::Lock_Flag_DepTag, true); + } +#endif + if (req->flags & LOCKED) { + lockAddrList.insert(req->paddr); + lockFlag = true; + } +#endif + Fault error; + + error = this->mem->read(req, data); + data = gtoh(data); + return error; + } + + + /** CPU read function, forwards read to LSQ. */ + template <class T> + Fault read(Request *req, T &data, int load_idx) + { + return backEnd->read(req, data, load_idx); + } + + /** Old CPU write to memory function. No longer used. */ + template <class T> + Fault write(Request *req, T &data) + { +#if 0 +#if FULL_SYSTEM && defined(TARGET_ALPHA) + ExecContext *xc; + + // If this is a store conditional, act appropriately + if (req->flags & LOCKED) { + xc = req->xc; + + if (req->flags & UNCACHEABLE) { + // Don't update result register (see stq_c in isa_desc) + req->result = 2; + xc->setStCondFailures(0);//Needed? [RGD] + } else { + bool lock_flag = xc->readMiscReg(TheISA::Lock_Flag_DepTag); + Addr lock_addr = xc->readMiscReg(TheISA::Lock_Addr_DepTag); + req->result = lock_flag; + if (!lock_flag || + ((lock_addr & ~0xf) != (req->paddr & ~0xf))) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + xc->setStCondFailures(xc->readStCondFailures() + 1); + if (((xc->readStCondFailures()) % 100000) == 0) { + std::cerr << "Warning: " + << xc->readStCondFailures() + << " consecutive store conditional failures " + << "on cpu " << req->xc->readCpuId() + << std::endl; + } + return NoFault; + } + else xc->setStCondFailures(0); + } + } + + // Need to clear any locked flags on other proccessors for + // this address. Only do this for succsful Store Conditionals + // and all other stores (WH64?). Unsuccessful Store + // Conditionals would have returned above, and wouldn't fall + // through. + for (int i = 0; i < this->system->threadContexts.size(); i++){ + xc = this->system->threadContexts[i]; + if ((xc->readMiscReg(TheISA::Lock_Addr_DepTag) & ~0xf) == + (req->paddr & ~0xf)) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + } + } + +#endif + + if (req->flags & LOCKED) { + if (req->flags & UNCACHEABLE) { + req->result = 2; + } else { + if (this->lockFlag) { + if (lockAddrList.find(req->paddr) != + lockAddrList.end()) { + req->result = 1; + } else { + req->result = 0; + return NoFault; + } + } else { + req->result = 0; + return NoFault; + } + } + } +#endif + + return this->mem->write(req, (T)htog(data)); + } + + /** CPU write function, forwards write to LSQ. */ + template <class T> + Fault write(Request *req, T &data, int store_idx) + { + return backEnd->write(req, data, store_idx); + } + + void prefetch(Addr addr, unsigned flags) + { + // need to do this... + } + + void writeHint(Addr addr, int size, unsigned flags) + { + // need to do this... + } + + Fault copySrcTranslate(Addr src); + + Fault copy(Addr dest); + + public: + void squashFromTC(); + + void dumpInsts() { frontEnd->dumpInsts(); } + +#if FULL_SYSTEM + Fault hwrei(); + int readIntrFlag() { return thread.intrflag; } + void setIntrFlag(int val) { thread.intrflag = val; } + bool inPalMode() { return AlphaISA::PcPAL(thread.PC); } + bool inPalMode(Addr pc) { return AlphaISA::PcPAL(pc); } + bool simPalCheck(int palFunc); + void processInterrupts(); +#else + void syscall(uint64_t &callnum); + void setSyscallReturn(SyscallReturn return_value, int tid); +#endif + + ThreadContext *tcBase() { return tc; } + + struct CommStruct { + InstSeqNum doneSeqNum; + InstSeqNum nonSpecSeqNum; + bool uncached; + unsigned lqIdx; + + bool stall; + }; + + InstSeqNum globalSeqNum; + + TimeBuffer<CommStruct> comm; + + bool decoupledFrontEnd; + + bool lockFlag; + + Stats::Scalar<> quiesceCycles; + + Checker<DynInstPtr> *checker; +}; + +#endif // __CPU_OZONE_CPU_HH__ diff --git a/src/cpu/ozone/cpu_builder.cc b/src/cpu/ozone/cpu_builder.cc new file mode 100644 index 000000000..e3e4ec433 --- /dev/null +++ b/src/cpu/ozone/cpu_builder.cc @@ -0,0 +1,468 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include <string> + +#include "cpu/checker/cpu.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/cpu.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/simple_params.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +class DerivOzoneCPU : public OzoneCPU<OzoneImpl> +{ + public: + DerivOzoneCPU(SimpleParams *p) + : OzoneCPU<OzoneImpl>(p) + { } +}; + + +//////////////////////////////////////////////////////////////////////// +// +// OzoneCPU Simulation Object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(DerivOzoneCPU) + + Param<int> clock; + Param<int> numThreads; + +#if FULL_SYSTEM +SimObjectParam<System *> system; +Param<int> cpu_id; +SimObjectParam<AlphaITB *> itb; +SimObjectParam<AlphaDTB *> dtb; +Param<Tick> profile; +#else +SimObjectVectorParam<Process *> workload; +//SimObjectParam<PageTable *> page_table; +#endif // FULL_SYSTEM + +SimObjectParam<MemObject *> mem; + +SimObjectParam<BaseCPU *> checker; + +Param<Counter> max_insts_any_thread; +Param<Counter> max_insts_all_threads; +Param<Counter> max_loads_any_thread; +Param<Counter> max_loads_all_threads; +Param<Counter> stats_reset_inst; +Param<Tick> progress_interval; + +//SimObjectParam<BaseCache *> icache; +//SimObjectParam<BaseCache *> dcache; + +Param<unsigned> cachePorts; +Param<unsigned> width; +Param<unsigned> frontEndLatency; +Param<unsigned> frontEndWidth; +Param<unsigned> backEndLatency; +Param<unsigned> backEndWidth; +Param<unsigned> backEndSquashLatency; +Param<unsigned> maxInstBufferSize; +Param<unsigned> numPhysicalRegs; +Param<unsigned> maxOutstandingMemOps; + +Param<unsigned> decodeToFetchDelay; +Param<unsigned> renameToFetchDelay; +Param<unsigned> iewToFetchDelay; +Param<unsigned> commitToFetchDelay; +Param<unsigned> fetchWidth; + +Param<unsigned> renameToDecodeDelay; +Param<unsigned> iewToDecodeDelay; +Param<unsigned> commitToDecodeDelay; +Param<unsigned> fetchToDecodeDelay; +Param<unsigned> decodeWidth; + +Param<unsigned> iewToRenameDelay; +Param<unsigned> commitToRenameDelay; +Param<unsigned> decodeToRenameDelay; +Param<unsigned> renameWidth; + +Param<unsigned> commitToIEWDelay; +Param<unsigned> renameToIEWDelay; +Param<unsigned> issueToExecuteDelay; +Param<unsigned> issueWidth; +Param<unsigned> executeWidth; +Param<unsigned> executeIntWidth; +Param<unsigned> executeFloatWidth; +Param<unsigned> executeBranchWidth; +Param<unsigned> executeMemoryWidth; + +Param<unsigned> iewToCommitDelay; +Param<unsigned> renameToROBDelay; +Param<unsigned> commitWidth; +Param<unsigned> squashWidth; + +Param<std::string> predType; +Param<unsigned> localPredictorSize; +Param<unsigned> localCtrBits; +Param<unsigned> localHistoryTableSize; +Param<unsigned> localHistoryBits; +Param<unsigned> globalPredictorSize; +Param<unsigned> globalCtrBits; +Param<unsigned> globalHistoryBits; +Param<unsigned> choicePredictorSize; +Param<unsigned> choiceCtrBits; + +Param<unsigned> BTBEntries; +Param<unsigned> BTBTagSize; + +Param<unsigned> RASSize; + +Param<unsigned> LQEntries; +Param<unsigned> SQEntries; +Param<bool> lsqLimits; +Param<unsigned> LFSTSize; +Param<unsigned> SSITSize; + +Param<unsigned> numPhysIntRegs; +Param<unsigned> numPhysFloatRegs; +Param<unsigned> numIQEntries; +Param<unsigned> numROBEntries; + +Param<bool> decoupledFrontEnd; +Param<int> dispatchWidth; +Param<int> wbWidth; + +Param<unsigned> smtNumFetchingThreads; +Param<std::string> smtFetchPolicy; +Param<std::string> smtLSQPolicy; +Param<unsigned> smtLSQThreshold; +Param<std::string> smtIQPolicy; +Param<unsigned> smtIQThreshold; +Param<std::string> smtROBPolicy; +Param<unsigned> smtROBThreshold; +Param<std::string> smtCommitPolicy; + +Param<unsigned> instShiftAmt; + +Param<bool> defer_registration; + +Param<bool> function_trace; +Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(DerivOzoneCPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(DerivOzoneCPU) + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(numThreads, "number of HW thread contexts"), + +#if FULL_SYSTEM + INIT_PARAM(system, "System object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(itb, "Instruction translation buffer"), + INIT_PARAM(dtb, "Data translation buffer"), + INIT_PARAM(profile, ""), +#else + INIT_PARAM(workload, "Processes to run"), +// INIT_PARAM(page_table, "Page table"), +#endif // FULL_SYSTEM + + INIT_PARAM_DFLT(mem, "Memory", NULL), + + INIT_PARAM_DFLT(checker, "Checker CPU", NULL), + + INIT_PARAM_DFLT(max_insts_any_thread, + "Terminate when any thread reaches this inst count", + 0), + INIT_PARAM_DFLT(max_insts_all_threads, + "Terminate when all threads have reached" + "this inst count", + 0), + INIT_PARAM_DFLT(max_loads_any_thread, + "Terminate when any thread reaches this load count", + 0), + INIT_PARAM_DFLT(max_loads_all_threads, + "Terminate when all threads have reached this load" + "count", + 0), + INIT_PARAM_DFLT(stats_reset_inst, + "blah", + 0), + INIT_PARAM_DFLT(progress_interval, "Progress interval", 0), + +// INIT_PARAM_DFLT(icache, "L1 instruction cache", NULL), +// INIT_PARAM_DFLT(dcache, "L1 data cache", NULL), + + INIT_PARAM_DFLT(cachePorts, "Cache Ports", 200), + INIT_PARAM_DFLT(width, "Width", 1), + INIT_PARAM_DFLT(frontEndLatency, "Front end latency", 1), + INIT_PARAM_DFLT(frontEndWidth, "Front end width", 1), + INIT_PARAM_DFLT(backEndLatency, "Back end latency", 1), + INIT_PARAM_DFLT(backEndWidth, "Back end width", 1), + INIT_PARAM_DFLT(backEndSquashLatency, "Back end squash latency", 1), + INIT_PARAM_DFLT(maxInstBufferSize, "Maximum instruction buffer size", 16), + INIT_PARAM(numPhysicalRegs, "Number of physical registers"), + INIT_PARAM_DFLT(maxOutstandingMemOps, "Maximum outstanding memory operations", 4), + + INIT_PARAM(decodeToFetchDelay, "Decode to fetch delay"), + INIT_PARAM(renameToFetchDelay, "Rename to fetch delay"), + INIT_PARAM(iewToFetchDelay, "Issue/Execute/Writeback to fetch" + "delay"), + INIT_PARAM(commitToFetchDelay, "Commit to fetch delay"), + INIT_PARAM(fetchWidth, "Fetch width"), + INIT_PARAM(renameToDecodeDelay, "Rename to decode delay"), + INIT_PARAM(iewToDecodeDelay, "Issue/Execute/Writeback to decode" + "delay"), + INIT_PARAM(commitToDecodeDelay, "Commit to decode delay"), + INIT_PARAM(fetchToDecodeDelay, "Fetch to decode delay"), + INIT_PARAM(decodeWidth, "Decode width"), + + INIT_PARAM(iewToRenameDelay, "Issue/Execute/Writeback to rename" + "delay"), + INIT_PARAM(commitToRenameDelay, "Commit to rename delay"), + INIT_PARAM(decodeToRenameDelay, "Decode to rename delay"), + INIT_PARAM(renameWidth, "Rename width"), + + INIT_PARAM(commitToIEWDelay, "Commit to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(renameToIEWDelay, "Rename to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(issueToExecuteDelay, "Issue to execute delay (internal" + "to the IEW stage)"), + INIT_PARAM(issueWidth, "Issue width"), + INIT_PARAM(executeWidth, "Execute width"), + INIT_PARAM(executeIntWidth, "Integer execute width"), + INIT_PARAM(executeFloatWidth, "Floating point execute width"), + INIT_PARAM(executeBranchWidth, "Branch execute width"), + INIT_PARAM(executeMemoryWidth, "Memory execute width"), + + INIT_PARAM(iewToCommitDelay, "Issue/Execute/Writeback to commit " + "delay"), + INIT_PARAM(renameToROBDelay, "Rename to reorder buffer delay"), + INIT_PARAM(commitWidth, "Commit width"), + INIT_PARAM(squashWidth, "Squash width"), + + INIT_PARAM(predType, "Type of branch predictor ('local', 'tournament')"), + INIT_PARAM(localPredictorSize, "Size of local predictor"), + INIT_PARAM(localCtrBits, "Bits per counter"), + INIT_PARAM(localHistoryTableSize, "Size of local history table"), + INIT_PARAM(localHistoryBits, "Bits for the local history"), + INIT_PARAM(globalPredictorSize, "Size of global predictor"), + INIT_PARAM(globalCtrBits, "Bits per counter"), + INIT_PARAM(globalHistoryBits, "Bits of history"), + INIT_PARAM(choicePredictorSize, "Size of choice predictor"), + INIT_PARAM(choiceCtrBits, "Bits of choice counters"), + + INIT_PARAM(BTBEntries, "Number of BTB entries"), + INIT_PARAM(BTBTagSize, "Size of the BTB tags, in bits"), + + INIT_PARAM(RASSize, "RAS size"), + + INIT_PARAM(LQEntries, "Number of load queue entries"), + INIT_PARAM(SQEntries, "Number of store queue entries"), + INIT_PARAM_DFLT(lsqLimits, "LSQ size limits dispatch", true), + INIT_PARAM(LFSTSize, "Last fetched store table size"), + INIT_PARAM(SSITSize, "Store set ID table size"), + + INIT_PARAM(numPhysIntRegs, "Number of physical integer registers"), + INIT_PARAM(numPhysFloatRegs, "Number of physical floating point " + "registers"), + INIT_PARAM(numIQEntries, "Number of instruction queue entries"), + INIT_PARAM(numROBEntries, "Number of reorder buffer entries"), + + INIT_PARAM_DFLT(decoupledFrontEnd, "Decoupled front end", true), + INIT_PARAM_DFLT(dispatchWidth, "Dispatch width", 0), + INIT_PARAM_DFLT(wbWidth, "Writeback width", 0), + + INIT_PARAM_DFLT(smtNumFetchingThreads, "SMT Number of Fetching Threads", 1), + INIT_PARAM_DFLT(smtFetchPolicy, "SMT Fetch Policy", "SingleThread"), + INIT_PARAM_DFLT(smtLSQPolicy, "SMT LSQ Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtLSQThreshold,"SMT LSQ Threshold", 100), + INIT_PARAM_DFLT(smtIQPolicy, "SMT IQ Policy", "Partitioned"), + INIT_PARAM_DFLT(smtIQThreshold, "SMT IQ Threshold", 100), + INIT_PARAM_DFLT(smtROBPolicy, "SMT ROB Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtROBThreshold,"SMT ROB Threshold", 100), + INIT_PARAM_DFLT(smtCommitPolicy,"SMT Commit Fetch Policy", "RoundRobin"), + + INIT_PARAM(instShiftAmt, "Number of bits to shift instructions by"), + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(DerivOzoneCPU) + +CREATE_SIM_OBJECT(DerivOzoneCPU) +{ + DerivOzoneCPU *cpu; + +#if FULL_SYSTEM + // Full-system only supports a single thread for the moment. + int actual_num_threads = 1; +#else + // In non-full-system mode, we infer the number of threads from + // the workload if it's not explicitly specified. + int actual_num_threads = + numThreads.isValid() ? numThreads : workload.size(); + + if (workload.size() == 0) { + fatal("Must specify at least one workload!"); + } + +#endif + + SimpleParams *params = new SimpleParams; + + params->clock = clock; + + params->name = getInstanceName(); + params->numberOfThreads = actual_num_threads; + +#if FULL_SYSTEM + params->system = system; + params->cpu_id = cpu_id; + params->itb = itb; + params->dtb = dtb; + params->profile = profile; +#else + params->workload = workload; +// params->pTable = page_table; +#endif // FULL_SYSTEM + + params->mem = mem; + params->checker = checker; + params->max_insts_any_thread = max_insts_any_thread; + params->max_insts_all_threads = max_insts_all_threads; + params->max_loads_any_thread = max_loads_any_thread; + params->max_loads_all_threads = max_loads_all_threads; + params->stats_reset_inst = stats_reset_inst; + params->progress_interval = progress_interval; + + // + // Caches + // +// params->icacheInterface = icache ? icache->getInterface() : NULL; +// params->dcacheInterface = dcache ? dcache->getInterface() : NULL; + params->cachePorts = cachePorts; + + params->width = width; + params->frontEndWidth = frontEndWidth; + params->frontEndLatency = frontEndLatency; + params->backEndWidth = backEndWidth; + params->backEndSquashLatency = backEndSquashLatency; + params->backEndLatency = backEndLatency; + params->maxInstBufferSize = maxInstBufferSize; + params->numPhysicalRegs = numPhysIntRegs + numPhysFloatRegs; + params->maxOutstandingMemOps = maxOutstandingMemOps; + + params->decodeToFetchDelay = decodeToFetchDelay; + params->renameToFetchDelay = renameToFetchDelay; + params->iewToFetchDelay = iewToFetchDelay; + params->commitToFetchDelay = commitToFetchDelay; + params->fetchWidth = fetchWidth; + + params->renameToDecodeDelay = renameToDecodeDelay; + params->iewToDecodeDelay = iewToDecodeDelay; + params->commitToDecodeDelay = commitToDecodeDelay; + params->fetchToDecodeDelay = fetchToDecodeDelay; + params->decodeWidth = decodeWidth; + + params->iewToRenameDelay = iewToRenameDelay; + params->commitToRenameDelay = commitToRenameDelay; + params->decodeToRenameDelay = decodeToRenameDelay; + params->renameWidth = renameWidth; + + params->commitToIEWDelay = commitToIEWDelay; + params->renameToIEWDelay = renameToIEWDelay; + params->issueToExecuteDelay = issueToExecuteDelay; + params->issueWidth = issueWidth; + params->executeWidth = executeWidth; + params->executeIntWidth = executeIntWidth; + params->executeFloatWidth = executeFloatWidth; + params->executeBranchWidth = executeBranchWidth; + params->executeMemoryWidth = executeMemoryWidth; + + params->iewToCommitDelay = iewToCommitDelay; + params->renameToROBDelay = renameToROBDelay; + params->commitWidth = commitWidth; + params->squashWidth = squashWidth; + + params->predType = predType; + params->localPredictorSize = localPredictorSize; + params->localCtrBits = localCtrBits; + params->localHistoryTableSize = localHistoryTableSize; + params->localHistoryBits = localHistoryBits; + params->globalPredictorSize = globalPredictorSize; + params->globalCtrBits = globalCtrBits; + params->globalHistoryBits = globalHistoryBits; + params->choicePredictorSize = choicePredictorSize; + params->choiceCtrBits = choiceCtrBits; + + params->BTBEntries = BTBEntries; + params->BTBTagSize = BTBTagSize; + + params->RASSize = RASSize; + + params->LQEntries = LQEntries; + params->SQEntries = SQEntries; + params->lsqLimits = lsqLimits; + + params->SSITSize = SSITSize; + params->LFSTSize = LFSTSize; + + params->numPhysIntRegs = numPhysIntRegs; + params->numPhysFloatRegs = numPhysFloatRegs; + params->numIQEntries = numIQEntries; + params->numROBEntries = numROBEntries; + + params->decoupledFrontEnd = decoupledFrontEnd; + params->dispatchWidth = dispatchWidth; + params->wbWidth = wbWidth; + + params->smtNumFetchingThreads = smtNumFetchingThreads; + params->smtFetchPolicy = smtFetchPolicy; + params->smtIQPolicy = smtIQPolicy; + params->smtLSQPolicy = smtLSQPolicy; + params->smtLSQThreshold = smtLSQThreshold; + params->smtROBPolicy = smtROBPolicy; + params->smtROBThreshold = smtROBThreshold; + params->smtCommitPolicy = smtCommitPolicy; + + params->instShiftAmt = 2; + + params->deferRegistration = defer_registration; + + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + + cpu = new DerivOzoneCPU(params); + + return cpu; +} + +REGISTER_SIM_OBJECT("DerivOzoneCPU", DerivOzoneCPU) diff --git a/src/cpu/ozone/cpu_impl.hh b/src/cpu/ozone/cpu_impl.hh new file mode 100644 index 000000000..5c8b5001d --- /dev/null +++ b/src/cpu/ozone/cpu_impl.hh @@ -0,0 +1,1188 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Nathan Binkert + */ + +#include "config/full_system.hh" +#include "config/use_checker.hh" + +#include "arch/isa_traits.hh" // For MachInst +#include "base/trace.hh" +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "cpu/exetrace.hh" +#include "cpu/ozone/cpu.hh" +#include "cpu/quiesce_event.hh" +#include "cpu/static_inst.hh" +#include "sim/sim_object.hh" +#include "sim/stats.hh" + +#if FULL_SYSTEM +#include "arch/faults.hh" +#include "arch/alpha/osfpal.hh" +#include "arch/alpha/tlb.hh" +#include "arch/alpha/types.hh" +#include "arch/vtophys.hh" +#include "base/callback.hh" +#include "cpu/profile.hh" +#include "kern/kernel_stats.hh" +#include "sim/faults.hh" +#include "sim/sim_events.hh" +#include "sim/sim_exit.hh" +#include "sim/system.hh" +#else // !FULL_SYSTEM +#include "sim/process.hh" +#endif // FULL_SYSTEM + +#if USE_CHECKER +#include "cpu/checker/thread_context.hh" +#endif + +using namespace TheISA; + +template <class Impl> +OzoneCPU<Impl>::TickEvent::TickEvent(OzoneCPU *c, int w) + : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c), width(w) +{ +} + +template <class Impl> +void +OzoneCPU<Impl>::TickEvent::process() +{ + cpu->tick(); +} + +template <class Impl> +const char * +OzoneCPU<Impl>::TickEvent::description() +{ + return "OzoneCPU tick event"; +} + +template <class Impl> +OzoneCPU<Impl>::OzoneCPU(Params *p) +#if FULL_SYSTEM + : BaseCPU(p), thread(this, 0), tickEvent(this, p->width), +#else + : BaseCPU(p), thread(this, 0, p->workload[0], 0, p->mem), + tickEvent(this, p->width), +#endif + mem(p->mem), comm(5, 5) +{ + frontEnd = new FrontEnd(p); + backEnd = new BackEnd(p); + + _status = Idle; + + if (p->checker) { + + BaseCPU *temp_checker = p->checker; + checker = dynamic_cast<Checker<DynInstPtr> *>(temp_checker); + checker->setMemory(mem); +#if FULL_SYSTEM + checker->setSystem(p->system); +#endif + checkerTC = new CheckerThreadContext<OzoneTC>(&ozoneTC, checker); + thread.tc = checkerTC; + tc = checkerTC; +#else + panic("Checker enabled but not compiled in!"); +#endif + } else { + // If checker is not being used, then the xcProxy points + // directly to the CPU's ExecContext. + checker = NULL; + thread.tc = &ozoneTC; + tc = &ozoneTC; + } + + ozoneTC.cpu = this; + ozoneTC.thread = &thread; + + thread.inSyscall = false; + + thread.setStatus(ThreadContext::Suspended); +#if FULL_SYSTEM + // Setup thread state stuff. + thread.cpu = this; + thread.setTid(0); + + thread.quiesceEvent = new EndQuiesceEvent(tc); + + system = p->system; + itb = p->itb; + dtb = p->dtb; + physmem = p->system->physmem; + + if (p->profile) { + thread.profile = new FunctionProfile(p->system->kernelSymtab); + // @todo: This might be better as an ThreadContext instead of OzoneTC + Callback *cb = + new MakeCallback<OzoneTC, + &OzoneTC::dumpFuncProfile>(&ozoneTC); + registerExitCallback(cb); + } + + // let's fill with a dummy node for now so we don't get a segfault + // on the first cycle when there's no node available. + static ProfileNode dummyNode; + thread.profileNode = &dummyNode; + thread.profilePC = 3; +#else + thread.cpu = this; +#endif // !FULL_SYSTEM + + numInst = 0; + startNumInst = 0; + + threadContexts.push_back(tc); + + frontEnd->setCPU(this); + backEnd->setCPU(this); + + frontEnd->setTC(tc); + backEnd->setTC(tc); + + frontEnd->setThreadState(&thread); + backEnd->setThreadState(&thread); + + frontEnd->setCommBuffer(&comm); + backEnd->setCommBuffer(&comm); + + frontEnd->setBackEnd(backEnd); + backEnd->setFrontEnd(frontEnd); + + globalSeqNum = 1; + +#if FULL_SYSTEM + checkInterrupts = false; +#endif + + lockFlag = 0; + + // Setup rename table, initializing all values to ready. + for (int i = 0; i < TheISA::TotalNumRegs; ++i) { + thread.renameTable[i] = new DynInst(this); + thread.renameTable[i]->setResultReady(); + } + + frontEnd->renameTable.copyFrom(thread.renameTable); + backEnd->renameTable.copyFrom(thread.renameTable); + +#if !FULL_SYSTEM + /* Use this port to for syscall emulation writes to memory. */ + Port *mem_port; + TranslatingPort *trans_port; + trans_port = new TranslatingPort(csprintf("%s-%d-funcport", + name(), 0), + p->workload[0]->pTable, + false); + mem_port = p->mem->getPort("functional"); + mem_port->setPeer(trans_port); + trans_port->setPeer(mem_port); + thread.setMemPort(trans_port); +#else + Port *mem_port; + FunctionalPort *phys_port; + VirtualPort *virt_port; + phys_port = new FunctionalPort(csprintf("%s-%d-funcport", + name(), 0)); + mem_port = system->physmem->getPort("functional"); + mem_port->setPeer(phys_port); + phys_port->setPeer(mem_port); + + virt_port = new VirtualPort(csprintf("%s-%d-vport", + name(), 0)); + mem_port = system->physmem->getPort("functional"); + mem_port->setPeer(virt_port); + virt_port->setPeer(mem_port); + + thread.setPhysPort(phys_port); + thread.setVirtPort(virt_port); +#endif + + DPRINTF(OzoneCPU, "OzoneCPU: Created Ozone cpu object.\n"); +} + +template <class Impl> +OzoneCPU<Impl>::~OzoneCPU() +{ +} + +template <class Impl> +void +OzoneCPU<Impl>::switchOut() +{ + BaseCPU::switchOut(_sampler); + switchCount = 0; + // Front end needs state from back end, so switch out the back end first. + backEnd->switchOut(); + frontEnd->switchOut(); +} + +template <class Impl> +void +OzoneCPU<Impl>::signalSwitched() +{ + // Only complete the switchout when both the front end and back + // end have signalled they are ready to switch. + if (++switchCount == 2) { + backEnd->doSwitchOut(); + frontEnd->doSwitchOut(); +#if USE_CHECKER + if (checker) + checker->switchOut(); +#endif + + _status = SwitchedOut; +#ifndef NDEBUG + // Loop through all registers + for (int i = 0; i < AlphaISA::TotalNumRegs; ++i) { + assert(thread.renameTable[i] == frontEnd->renameTable[i]); + + assert(thread.renameTable[i] == backEnd->renameTable[i]); + + DPRINTF(OzoneCPU, "Checking if register %i matches.\n", i); + } +#endif + + if (tickEvent.scheduled()) + tickEvent.squash(); + } + assert(switchCount <= 2); +} + +template <class Impl> +void +OzoneCPU<Impl>::takeOverFrom(BaseCPU *oldCPU) +{ + BaseCPU::takeOverFrom(oldCPU); + + thread.trapPending = false; + thread.inSyscall = false; + + backEnd->takeOverFrom(); + frontEnd->takeOverFrom(); + frontEnd->renameTable.copyFrom(thread.renameTable); + backEnd->renameTable.copyFrom(thread.renameTable); + assert(!tickEvent.scheduled()); + +#ifndef NDEBUG + // Check rename table. + for (int i = 0; i < TheISA::TotalNumRegs; ++i) { + assert(thread.renameTable[i]->isResultReady()); + } +#endif + + // @todo: Fix hardcoded number + // Clear out any old information in time buffer. + for (int i = 0; i < 15; ++i) { + comm.advance(); + } + + // if any of this CPU's ThreadContexts are active, mark the CPU as + // running and schedule its tick event. + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + if (tc->status() == ThreadContext::Active && + _status != Running) { + _status = Running; + tickEvent.schedule(curTick); + } + } + // Nothing running, change status to reflect that we're no longer + // switched out. + if (_status == SwitchedOut) { + _status = Idle; + } +} + +template <class Impl> +void +OzoneCPU<Impl>::activateContext(int thread_num, int delay) +{ + // Eventually change this in SMT. + assert(thread_num == 0); + + assert(_status == Idle); + notIdleFraction++; + scheduleTickEvent(delay); + _status = Running; +#if FULL_SYSTEM + if (thread.quiesceEvent && thread.quiesceEvent->scheduled()) + thread.quiesceEvent->deschedule(); +#endif + thread.setStatus(ThreadContext::Active); + frontEnd->wakeFromQuiesce(); +} + +template <class Impl> +void +OzoneCPU<Impl>::suspendContext(int thread_num) +{ + // Eventually change this in SMT. + assert(thread_num == 0); + // @todo: Figure out how to initially set the status properly so + // this is running. +// assert(_status == Running); + notIdleFraction--; + unscheduleTickEvent(); + _status = Idle; +} + +template <class Impl> +void +OzoneCPU<Impl>::deallocateContext(int thread_num, int delay) +{ + // for now, these are equivalent + suspendContext(thread_num); +} + +template <class Impl> +void +OzoneCPU<Impl>::haltContext(int thread_num) +{ + // for now, these are equivalent + suspendContext(thread_num); +} + +template <class Impl> +void +OzoneCPU<Impl>::regStats() +{ + using namespace Stats; + + BaseCPU::regStats(); + + thread.numInsts + .name(name() + ".num_insts") + .desc("Number of instructions executed") + ; + + thread.numMemRefs + .name(name() + ".num_refs") + .desc("Number of memory references") + ; + + notIdleFraction + .name(name() + ".not_idle_fraction") + .desc("Percentage of non-idle cycles") + ; + + idleFraction + .name(name() + ".idle_fraction") + .desc("Percentage of idle cycles") + ; + + quiesceCycles + .name(name() + ".quiesce_cycles") + .desc("Number of cycles spent in quiesce") + ; + + idleFraction = constant(1.0) - notIdleFraction; + + frontEnd->regStats(); + backEnd->regStats(); +} + +template <class Impl> +void +OzoneCPU<Impl>::resetStats() +{ +// startNumInst = numInst; + notIdleFraction = (_status != Idle); +} + +template <class Impl> +void +OzoneCPU<Impl>::init() +{ + BaseCPU::init(); + + // Mark this as in syscall so it won't need to squash + thread.inSyscall = true; +#if FULL_SYSTEM + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + + // initialize CPU, including PC + TheISA::initCPU(tc, tc->readCpuId()); + } +#endif + frontEnd->renameTable.copyFrom(thread.renameTable); + backEnd->renameTable.copyFrom(thread.renameTable); + + thread.inSyscall = false; +} + +template <class Impl> +Port * +OzoneCPU<Impl>::getPort(const std::string &if_name, int idx) +{ + if (if_name == "dcache_port") + return backEnd->getDcachePort(); + else if (if_name == "icache_port") + return frontEnd->getIcachePort(); + else + panic("No Such Port\n"); +} + +template <class Impl> +void +OzoneCPU<Impl>::serialize(std::ostream &os) +{ + BaseCPU::serialize(os); + SERIALIZE_ENUM(_status); + nameOut(os, csprintf("%s.tc", name())); + ozoneTC.serialize(os); + nameOut(os, csprintf("%s.tickEvent", name())); + tickEvent.serialize(os); + + // Use SimpleThread's ability to checkpoint to make it easier to + // write out the registers. Also make this static so it doesn't + // get instantiated multiple times (causes a panic in statistics). + static CPUExecContext temp; + + nameOut(os, csprintf("%s.xc.0", name())); + temp.copyXC(thread.getXCProxy()); + temp.serialize(os); +} + +template <class Impl> +void +OzoneCPU<Impl>::unserialize(Checkpoint *cp, const std::string §ion) +{ + BaseCPU::unserialize(cp, section); + UNSERIALIZE_ENUM(_status); + ozoneTC.unserialize(cp, csprintf("%s.tc", section)); + tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); + + // Use SimpleThread's ability to checkpoint to make it easier to + // read in the registers. Also make this static so it doesn't + // get instantiated multiple times (causes a panic in statistics). + static CPUExecContext temp; + + temp.copyXC(thread.getXCProxy()); + temp.unserialize(cp, csprintf("%s.xc.0", section)); + thread.getXCProxy()->copyArchRegs(temp.getProxy()); +} + +template <class Impl> +Fault +OzoneCPU<Impl>::copySrcTranslate(Addr src) +{ + panic("Copy not implemented!\n"); + return NoFault; +#if 0 + static bool no_warn = true; + int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64; + // Only support block sizes of 64 atm. + assert(blk_size == 64); + int offset = src & (blk_size - 1); + + // Make sure block doesn't span page + if (no_warn && + (src & TheISA::PageMask) != ((src + blk_size) & TheISA::PageMask) && + (src >> 40) != 0xfffffc) { + warn("Copied block source spans pages %x.", src); + no_warn = false; + } + + memReq->reset(src & ~(blk_size - 1), blk_size); + + // translate to physical address + Fault fault = tc->translateDataReadReq(memReq); + + assert(fault != Alignment_Fault); + + if (fault == NoFault) { + tc->copySrcAddr = src; + tc->copySrcPhysAddr = memReq->paddr + offset; + } else { + tc->copySrcAddr = 0; + tc->copySrcPhysAddr = 0; + } + return fault; +#endif +} + +template <class Impl> +Fault +OzoneCPU<Impl>::copy(Addr dest) +{ + panic("Copy not implemented!\n"); + return NoFault; +#if 0 + static bool no_warn = true; + int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64; + // Only support block sizes of 64 atm. + assert(blk_size == 64); + uint8_t data[blk_size]; + //assert(tc->copySrcAddr); + int offset = dest & (blk_size - 1); + + // Make sure block doesn't span page + if (no_warn && + (dest & TheISA::PageMask) != ((dest + blk_size) & TheISA::PageMask) && + (dest >> 40) != 0xfffffc) { + no_warn = false; + warn("Copied block destination spans pages %x. ", dest); + } + + memReq->reset(dest & ~(blk_size -1), blk_size); + // translate to physical address + Fault fault = tc->translateDataWriteReq(memReq); + + assert(fault != Alignment_Fault); + + if (fault == NoFault) { + Addr dest_addr = memReq->paddr + offset; + // Need to read straight from memory since we have more than 8 bytes. + memReq->paddr = tc->copySrcPhysAddr; + tc->mem->read(memReq, data); + memReq->paddr = dest_addr; + tc->mem->write(memReq, data); + if (dcacheInterface) { + memReq->cmd = Copy; + memReq->completionEvent = NULL; + memReq->paddr = tc->copySrcPhysAddr; + memReq->dest = dest_addr; + memReq->size = 64; + memReq->time = curTick; + dcacheInterface->access(memReq); + } + } + return fault; +#endif +} + +#if FULL_SYSTEM +template <class Impl> +Addr +OzoneCPU<Impl>::dbg_vtophys(Addr addr) +{ + return vtophys(tc, addr); +} +#endif // FULL_SYSTEM + +#if FULL_SYSTEM +template <class Impl> +void +OzoneCPU<Impl>::post_interrupt(int int_num, int index) +{ + BaseCPU::post_interrupt(int_num, index); + + if (_status == Idle) { + DPRINTF(IPI,"Suspended Processor awoke\n"); +// thread.activate(); + // Hack for now. Otherwise might have to go through the tc, or + // I need to figure out what's the right thing to call. + activateContext(thread.readTid(), 1); + } +} +#endif // FULL_SYSTEM + +/* start simulation, program loaded, processor precise state initialized */ +template <class Impl> +void +OzoneCPU<Impl>::tick() +{ + DPRINTF(OzoneCPU, "\n\nOzoneCPU: Ticking cpu.\n"); + + _status = Running; + thread.renameTable[ZeroReg]->setIntResult(0); + thread.renameTable[ZeroReg+TheISA::FP_Base_DepTag]-> + setDoubleResult(0.0); + + comm.advance(); + frontEnd->tick(); + backEnd->tick(); + + // check for instruction-count-based events + comInstEventQueue[0]->serviceEvents(numInst); + + if (!tickEvent.scheduled() && _status == Running) + tickEvent.schedule(curTick + cycles(1)); +} + +template <class Impl> +void +OzoneCPU<Impl>::squashFromTC() +{ + thread.inSyscall = true; + backEnd->generateTCEvent(); +} + +#if !FULL_SYSTEM +template <class Impl> +void +OzoneCPU<Impl>::syscall(uint64_t &callnum) +{ + // Not sure this copy is needed, depending on how the TC proxy is made. + thread.renameTable.copyFrom(backEnd->renameTable); + + thread.inSyscall = true; + + thread.funcExeInst++; + + DPRINTF(OzoneCPU, "FuncExeInst: %i\n", thread.funcExeInst); + + thread.process->syscall(callnum, tc); + + thread.funcExeInst--; + + thread.inSyscall = false; + + frontEnd->renameTable.copyFrom(thread.renameTable); + backEnd->renameTable.copyFrom(thread.renameTable); +} + +template <class Impl> +void +OzoneCPU<Impl>::setSyscallReturn(SyscallReturn return_value, int tid) +{ + // check for error condition. Alpha syscall convention is to + // indicate success/failure in reg a3 (r19) and put the + // return value itself in the standard return value reg (v0). + if (return_value.successful()) { + // no error + thread.renameTable[SyscallSuccessReg]->setIntResult(0); + thread.renameTable[ReturnValueReg]->setIntResult( + return_value.value()); + } else { + // got an error, return details + thread.renameTable[SyscallSuccessReg]->setIntResult((IntReg) -1); + thread.renameTable[ReturnValueReg]->setIntResult( + -return_value.value()); + } +} +#else +template <class Impl> +Fault +OzoneCPU<Impl>::hwrei() +{ + // Need to move this to ISA code + // May also need to make this per thread + + lockFlag = false; + lockAddrList.clear(); + thread.kernelStats->hwrei(); + + checkInterrupts = true; + + // FIXME: XXX check for interrupts? XXX + return NoFault; +} + +template <class Impl> +void +OzoneCPU<Impl>::processInterrupts() +{ + // Check for interrupts here. For now can copy the code that + // exists within isa_fullsys_traits.hh. Also assume that thread 0 + // is the one that handles the interrupts. + + // Check if there are any outstanding interrupts + //Handle the interrupts + int ipl = 0; + int summary = 0; + + checkInterrupts = false; + + if (thread.readMiscReg(IPR_ASTRR)) + panic("asynchronous traps not implemented\n"); + + if (thread.readMiscReg(IPR_SIRR)) { + for (int i = INTLEVEL_SOFTWARE_MIN; + i < INTLEVEL_SOFTWARE_MAX; i++) { + if (thread.readMiscReg(IPR_SIRR) & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; + summary |= (ULL(1) << i); + } + } + } + + uint64_t interrupts = intr_status(); + + if (interrupts) { + for (int i = INTLEVEL_EXTERNAL_MIN; + i < INTLEVEL_EXTERNAL_MAX; i++) { + if (interrupts & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = i; + summary |= (ULL(1) << i); + } + } + } + + if (ipl && ipl > thread.readMiscReg(IPR_IPLR)) { + thread.setMiscReg(IPR_ISR, summary); + thread.setMiscReg(IPR_INTID, ipl); + // @todo: Make this more transparent + if (checker) { + checker->threadBase()->setMiscReg(IPR_ISR, summary); + checker->threadBase()->setMiscReg(IPR_INTID, ipl); + } + Fault fault = new InterruptFault; + fault->invoke(thread.getTC()); + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + thread.readMiscReg(IPR_IPLR), ipl, summary); + } +} + +template <class Impl> +bool +OzoneCPU<Impl>::simPalCheck(int palFunc) +{ + // Need to move this to ISA code + // May also need to make this per thread + thread.kernelStats->callpal(palFunc, tc); + + switch (palFunc) { + case PAL::halt: + haltContext(thread.readTid()); + if (--System::numSystemsRunning == 0) + exitSimLoop("all cpus halted"); + break; + + case PAL::bpt: + case PAL::bugchk: + if (system->breakpoint()) + return false; + break; + } + + return true; +} +#endif + +template <class Impl> +BaseCPU * +OzoneCPU<Impl>::OzoneTC::getCpuPtr() +{ + return cpu; +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setCpuId(int id) +{ + cpu->cpuId = id; + thread->setCpuId(id); +} + +#if FULL_SYSTEM +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::delVirtPort(VirtualPort *vp) +{ + delete vp->getPeer(); + delete vp; +} +#endif + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setStatus(Status new_status) +{ + thread->setStatus(new_status); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::activate(int delay) +{ + cpu->activateContext(thread->readTid(), delay); +} + +/// Set the status to Suspended. +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::suspend() +{ + cpu->suspendContext(thread->readTid()); +} + +/// Set the status to Unallocated. +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::deallocate(int delay) +{ + cpu->deallocateContext(thread->readTid(), delay); +} + +/// Set the status to Halted. +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::halt() +{ + cpu->haltContext(thread->readTid()); +} + +#if FULL_SYSTEM +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::dumpFuncProfile() +{ + thread->dumpFuncProfile(); +} +#endif + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::takeOverFrom(ThreadContext *old_context) +{ + // some things should already be set up +#if FULL_SYSTEM + assert(getSystemPtr() == old_context->getSystemPtr()); +#else + assert(getProcessPtr() == old_context->getProcessPtr()); +#endif + + // copy over functional state + setStatus(old_context->status()); + copyArchRegs(old_context); + setCpuId(old_context->readCpuId()); + + thread->inst = old_context->getInst(); +#if !FULL_SYSTEM + setFuncExeInst(old_context->readFuncExeInst()); +#else + EndQuiesceEvent *other_quiesce = old_context->getQuiesceEvent(); + if (other_quiesce) { + // Point the quiesce event's TC at this TC so that it wakes up + // the proper CPU. + other_quiesce->tc = this; + } + if (thread->quiesceEvent) { + thread->quiesceEvent->tc = this; + } + + // Copy kernel stats pointer from old context. + thread->kernelStats = old_context->getKernelStats(); +// storeCondFailures = 0; + cpu->lockFlag = false; +#endif + + old_context->setStatus(ThreadContext::Unallocated); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::regStats(const std::string &name) +{ +#if FULL_SYSTEM + thread->kernelStats = new Kernel::Statistics(cpu->system); + thread->kernelStats->regStats(name + ".kern"); +#endif +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::serialize(std::ostream &os) +{ + // Once serialization is added, serialize the quiesce event and + // kernel stats. Will need to make sure there aren't multiple + // things that serialize them. +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::unserialize(Checkpoint *cp, const std::string §ion) +{ } + +#if FULL_SYSTEM +template <class Impl> +EndQuiesceEvent * +OzoneCPU<Impl>::OzoneTC::getQuiesceEvent() +{ + return thread->quiesceEvent; +} + +template <class Impl> +Tick +OzoneCPU<Impl>::OzoneTC::readLastActivate() +{ + return thread->lastActivate; +} + +template <class Impl> +Tick +OzoneCPU<Impl>::OzoneTC::readLastSuspend() +{ + return thread->lastSuspend; +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::profileClear() +{ + thread->profileClear(); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::profileSample() +{ + thread->profileSample(); +} +#endif + +template <class Impl> +int +OzoneCPU<Impl>::OzoneTC::getThreadNum() +{ + return thread->readTid(); +} + +template <class Impl> +TheISA::MachInst +OzoneCPU<Impl>::OzoneTC::getInst() +{ + return thread->getInst(); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::copyArchRegs(ThreadContext *tc) +{ + thread->PC = tc->readPC(); + thread->nextPC = tc->readNextPC(); + + cpu->frontEnd->setPC(thread->PC); + cpu->frontEnd->setNextPC(thread->nextPC); + + // First loop through the integer registers. + for (int i = 0; i < TheISA::NumIntRegs; ++i) { +/* DPRINTF(OzoneCPU, "Copying over register %i, had data %lli, " + "now has data %lli.\n", + i, thread->renameTable[i]->readIntResult(), + tc->readIntReg(i)); +*/ + thread->renameTable[i]->setIntResult(tc->readIntReg(i)); + } + + // Then loop through the floating point registers. + for (int i = 0; i < TheISA::NumFloatRegs; ++i) { + int fp_idx = i + TheISA::FP_Base_DepTag; + thread->renameTable[fp_idx]->setIntResult(tc->readFloatRegBits(i)); + } + +#if !FULL_SYSTEM + thread->funcExeInst = tc->readFuncExeInst(); +#endif + + // Need to copy the TC values into the current rename table, + // copy the misc regs. + copyMiscRegs(tc, this); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::clearArchRegs() +{ + panic("Unimplemented!"); +} + +template <class Impl> +uint64_t +OzoneCPU<Impl>::OzoneTC::readIntReg(int reg_idx) +{ + return thread->renameTable[reg_idx]->readIntResult(); +} + +template <class Impl> +TheISA::FloatReg +OzoneCPU<Impl>::OzoneTC::readFloatReg(int reg_idx, int width) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + switch(width) { + case 32: + return thread->renameTable[idx]->readFloatResult(); + case 64: + return thread->renameTable[idx]->readDoubleResult(); + default: + panic("Unsupported width!"); + return 0; + } +} + +template <class Impl> +double +OzoneCPU<Impl>::OzoneTC::readFloatReg(int reg_idx) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + return thread->renameTable[idx]->readFloatResult(); +} + +template <class Impl> +uint64_t +OzoneCPU<Impl>::OzoneTC::readFloatRegBits(int reg_idx, int width) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + return thread->renameTable[idx]->readIntResult(); +} + +template <class Impl> +uint64_t +OzoneCPU<Impl>::OzoneTC::readFloatRegBits(int reg_idx) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + return thread->renameTable[idx]->readIntResult(); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setIntReg(int reg_idx, uint64_t val) +{ + thread->renameTable[reg_idx]->setIntResult(val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setFloatReg(int reg_idx, FloatReg val, int width) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + switch(width) { + case 32: + panic("Unimplemented!"); + break; + case 64: + thread->renameTable[idx]->setDoubleResult(val); + break; + default: + panic("Unsupported width!"); + } + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setFloatReg(int reg_idx, FloatReg val) +{ + int idx = reg_idx + TheISA::FP_Base_DepTag; + + thread->renameTable[idx]->setDoubleResult(val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setFloatRegBits(int reg_idx, FloatRegBits val, + int width) +{ + panic("Unimplemented!"); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setFloatRegBits(int reg_idx, FloatRegBits val) +{ + panic("Unimplemented!"); +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setPC(Addr val) +{ + thread->PC = val; + cpu->frontEnd->setPC(val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +void +OzoneCPU<Impl>::OzoneTC::setNextPC(Addr val) +{ + thread->nextPC = val; + cpu->frontEnd->setNextPC(val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } +} + +template <class Impl> +TheISA::MiscReg +OzoneCPU<Impl>::OzoneTC::readMiscReg(int misc_reg) +{ + return thread->miscRegFile.readReg(misc_reg); +} + +template <class Impl> +TheISA::MiscReg +OzoneCPU<Impl>::OzoneTC::readMiscRegWithEffect(int misc_reg, Fault &fault) +{ + return thread->miscRegFile.readRegWithEffect(misc_reg, + fault, this); +} + +template <class Impl> +Fault +OzoneCPU<Impl>::OzoneTC::setMiscReg(int misc_reg, const MiscReg &val) +{ + // Needs to setup a squash event unless we're in syscall mode + Fault ret_fault = thread->miscRegFile.setReg(misc_reg, val); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } + + return ret_fault; +} + +template <class Impl> +Fault +OzoneCPU<Impl>::OzoneTC::setMiscRegWithEffect(int misc_reg, const MiscReg &val) +{ + // Needs to setup a squash event unless we're in syscall mode + Fault ret_fault = thread->miscRegFile.setRegWithEffect(misc_reg, val, + this); + + if (!thread->inSyscall) { + cpu->squashFromTC(); + } + + return ret_fault; +} diff --git a/src/cpu/ozone/dyn_inst.cc b/src/cpu/ozone/dyn_inst.cc new file mode 100644 index 000000000..732fb96b8 --- /dev/null +++ b/src/cpu/ozone/dyn_inst.cc @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/dyn_inst_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +//#include "cpu/ozone/simple_impl.hh" + +template class OzoneDynInst<OzoneImpl>; +//template class OzoneDynInst<SimpleImpl>; + diff --git a/src/cpu/ozone/dyn_inst.hh b/src/cpu/ozone/dyn_inst.hh new file mode 100644 index 000000000..75ac464ec --- /dev/null +++ b/src/cpu/ozone/dyn_inst.hh @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_DYN_INST_HH__ +#define __CPU_OZONE_DYN_INST_HH__ + +#include "arch/isa_traits.hh" +#include "arch/types.hh" +#include "config/full_system.hh" +#include "cpu/base_dyn_inst.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/cpu.hh" // MUST include this +#include "cpu/ozone/ozone_impl.hh" + +#include <list> +#include <vector> + +template <class Impl> +class OzoneDynInst : public BaseDynInst<Impl> +{ + public: + // Typedefs + typedef typename Impl::OzoneCPU OzoneCPU; + + typedef typename OzoneCPU::ImplState ImplState; + + // Typedef for DynInstPtr. This is really just a RefCountingPtr<OoODynInst>. + typedef typename Impl::DynInstPtr DynInstPtr; + + typedef TheISA::ExtMachInst ExtMachInst; + typedef TheISA::MachInst MachInst; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscReg MiscReg; + typedef typename std::list<DynInstPtr>::iterator ListIt; + + // Note that this is duplicated from the BaseDynInst class; I'm + // simply not sure the enum would carry through so I could use it + // in array declarations in this class. + enum { + MaxInstSrcRegs = TheISA::MaxInstSrcRegs, + MaxInstDestRegs = TheISA::MaxInstDestRegs + }; + + OzoneDynInst(OzoneCPU *cpu); + + OzoneDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, + InstSeqNum seq_num, OzoneCPU *cpu); + + OzoneDynInst(StaticInstPtr inst); + + ~OzoneDynInst(); + + void setSrcInst(DynInstPtr &newSrcInst, int regIdx) + { srcInsts[regIdx] = newSrcInst; } + + bool srcInstReady(int regIdx); + + void setPrevDestInst(DynInstPtr &oldDestInst, int regIdx) + { prevDestInst[regIdx] = oldDestInst; } + + DynInstPtr &getPrevDestInst(int regIdx) + { return prevDestInst[regIdx]; } + + void addDependent(DynInstPtr &dependent_inst); + + std::vector<DynInstPtr> &getDependents() { return dependents; } + std::vector<DynInstPtr> &getMemDeps() { return memDependents; } + std::list<DynInstPtr> &getMemSrcs() { return srcMemInsts; } + + void wakeDependents(); + + void wakeMemDependents(); + + void addMemDependent(DynInstPtr &inst) { memDependents.push_back(inst); } + + void addSrcMemInst(DynInstPtr &inst) { srcMemInsts.push_back(inst); } + + void markMemInstReady(OzoneDynInst<Impl> *inst); + + // For now I will remove instructions from the list when they wake + // up. In the future, you only really need a counter. + bool memDepReady() { return srcMemInsts.empty(); } + + private: + void initInstPtrs(); + + std::vector<DynInstPtr> dependents; + + std::vector<DynInstPtr> memDependents; + + std::list<DynInstPtr> srcMemInsts; + + /** The instruction that produces the value of the source + * registers. These may be NULL if the value has already been + * read from the source instruction. + */ + DynInstPtr srcInsts[MaxInstSrcRegs]; + + /** + * Previous rename instruction for this destination. + */ + DynInstPtr prevDestInst[MaxInstSrcRegs]; + + public: + + Fault initiateAcc(); + + Fault completeAcc(Packet *pkt); + + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to redice overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + uint64_t readIntReg(const StaticInst *si, int idx) + { + return srcInsts[idx]->readIntResult(); + } + + FloatReg readFloatReg(const StaticInst *si, int idx, int width) + { + switch(width) { + case 32: + return srcInsts[idx]->readFloatResult(); + case 64: + return srcInsts[idx]->readDoubleResult(); + default: + panic("Width not supported"); + return 0; + } + } + + FloatReg readFloatReg(const StaticInst *si, int idx) + { + return srcInsts[idx]->readFloatResult(); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width) + { + return srcInsts[idx]->readIntResult(); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx) + { + return srcInsts[idx]->readIntResult(); + } + + /** @todo: Make results into arrays so they can handle multiple dest + * registers. + */ + void setIntReg(const StaticInst *si, int idx, uint64_t val) + { + BaseDynInst<Impl>::setIntReg(si, idx, val); + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) + { + BaseDynInst<Impl>::setFloatReg(si, idx, val, width); + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val) + { + BaseDynInst<Impl>::setFloatReg(si, idx, val); + } + + void setFloatRegBits(const StaticInst *si, int idx, + FloatRegBits val, int width) + { + BaseDynInst<Impl>::setFloatRegBits(si, idx, val); + } + + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val) + { + BaseDynInst<Impl>::setFloatRegBits(si, idx, val); + } + + void setIntResult(uint64_t result) { this->instResult.integer = result; } + void setDoubleResult(double result) { this->instResult.dbl = result; } + + bool srcsReady(); + bool eaSrcsReady(); + + Fault execute(); + + Fault executeEAComp() + { return NoFault; } + + Fault executeMemAcc() + { return this->staticInst->memAccInst()->execute(this, this->traceData); } + + void clearDependents(); + + void clearMemDependents(); + + public: + // ISA stuff + MiscReg readMiscReg(int misc_reg); + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault); + + Fault setMiscReg(int misc_reg, const MiscReg &val); + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val); + +#if FULL_SYSTEM + Fault hwrei(); + int readIntrFlag(); + void setIntrFlag(int val); + bool inPalMode(); + void trap(Fault fault); + bool simPalCheck(int palFunc); +#else + void syscall(uint64_t &callnum); +#endif + + ListIt iqIt; + bool iqItValid; +}; + +#endif // __CPU_OZONE_DYN_INST_HH__ diff --git a/src/cpu/ozone/dyn_inst_impl.hh b/src/cpu/ozone/dyn_inst_impl.hh new file mode 100644 index 000000000..ba0d70417 --- /dev/null +++ b/src/cpu/ozone/dyn_inst_impl.hh @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2005-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "arch/faults.hh" +#include "config/full_system.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "kern/kernel_stats.hh" + +template <class Impl> +OzoneDynInst<Impl>::OzoneDynInst(OzoneCPU *cpu) + : BaseDynInst<Impl>(0, 0, 0, 0, cpu) +{ + this->setResultReady(); + + initInstPtrs(); +} + +template <class Impl> +OzoneDynInst<Impl>::OzoneDynInst(ExtMachInst inst, Addr PC, Addr Pred_PC, + InstSeqNum seq_num, OzoneCPU *cpu) + : BaseDynInst<Impl>(inst, PC, Pred_PC, seq_num, cpu) +{ + initInstPtrs(); +} + +template <class Impl> +OzoneDynInst<Impl>::OzoneDynInst(StaticInstPtr _staticInst) + : BaseDynInst<Impl>(_staticInst) +{ + initInstPtrs(); +} + +template <class Impl> +OzoneDynInst<Impl>::~OzoneDynInst() +{ + DPRINTF(BE, "[sn:%lli] destructor called\n", this->seqNum); + for (int i = 0; i < this->numSrcRegs(); ++i) { + srcInsts[i] = NULL; + } + + for (int i = 0; i < this->numDestRegs(); ++i) { + prevDestInst[i] = NULL; + } + + dependents.clear(); +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::execute() +{ + // @todo: Pretty convoluted way to avoid squashing from happening when using + // the XC during an instruction's execution (specifically for instructions + // that have sideeffects that use the XC). Fix this. + bool in_syscall = this->thread->inSyscall; + this->thread->inSyscall = true; + + this->fault = this->staticInst->execute(this, this->traceData); + + this->thread->inSyscall = in_syscall; + + return this->fault; +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::initiateAcc() +{ + // @todo: Pretty convoluted way to avoid squashing from happening when using + // the XC during an instruction's execution (specifically for instructions + // that have sideeffects that use the XC). Fix this. + bool in_syscall = this->thread->inSyscall; + this->thread->inSyscall = true; + + this->fault = this->staticInst->initiateAcc(this, this->traceData); + + this->thread->inSyscall = in_syscall; + + return this->fault; +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::completeAcc(Packet *pkt) +{ + this->fault = this->staticInst->completeAcc(pkt, this, this->traceData); + + return this->fault; +} + +template <class Impl> +bool +OzoneDynInst<Impl>::srcInstReady(int regIdx) +{ + return srcInsts[regIdx]->isResultReady(); +} + +template <class Impl> +void +OzoneDynInst<Impl>::addDependent(DynInstPtr &dependent_inst) +{ + dependents.push_back(dependent_inst); +} + +template <class Impl> +void +OzoneDynInst<Impl>::wakeDependents() +{ + for (int i = 0; i < dependents.size(); ++i) { + dependents[i]->markSrcRegReady(); + } +} + +template <class Impl> +void +OzoneDynInst<Impl>::wakeMemDependents() +{ + for (int i = 0; i < memDependents.size(); ++i) { + memDependents[i]->markMemInstReady(this); + } +} + +template <class Impl> +void +OzoneDynInst<Impl>::markMemInstReady(OzoneDynInst<Impl> *inst) +{ + ListIt mem_it = srcMemInsts.begin(); + while ((*mem_it) != inst && mem_it != srcMemInsts.end()) { + mem_it++; + } + assert(mem_it != srcMemInsts.end()); + + srcMemInsts.erase(mem_it); +} + +template <class Impl> +void +OzoneDynInst<Impl>::initInstPtrs() +{ + for (int i = 0; i < MaxInstSrcRegs; ++i) { + srcInsts[i] = NULL; + } + iqItValid = false; +} + +template <class Impl> +bool +OzoneDynInst<Impl>::srcsReady() +{ + for (int i = 0; i < this->numSrcRegs(); ++i) { + if (!srcInsts[i]->isResultReady()) + return false; + } + + return true; +} + +template <class Impl> +bool +OzoneDynInst<Impl>::eaSrcsReady() +{ + for (int i = 1; i < this->numSrcRegs(); ++i) { + if (!srcInsts[i]->isResultReady()) + return false; + } + + return true; +} + +template <class Impl> +void +OzoneDynInst<Impl>::clearDependents() +{ + dependents.clear(); + for (int i = 0; i < this->numSrcRegs(); ++i) { + srcInsts[i] = NULL; + } + for (int i = 0; i < this->numDestRegs(); ++i) { + prevDestInst[i] = NULL; + } +} + +template <class Impl> +void +OzoneDynInst<Impl>::clearMemDependents() +{ + memDependents.clear(); +} + +template <class Impl> +MiscReg +OzoneDynInst<Impl>::readMiscReg(int misc_reg) +{ + return this->thread->readMiscReg(misc_reg); +} + +template <class Impl> +MiscReg +OzoneDynInst<Impl>::readMiscRegWithEffect(int misc_reg, Fault &fault) +{ + return this->thread->readMiscRegWithEffect(misc_reg, fault); +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::setMiscReg(int misc_reg, const MiscReg &val) +{ + this->setIntResult(val); + return this->thread->setMiscReg(misc_reg, val); +} + +template <class Impl> +Fault +OzoneDynInst<Impl>::setMiscRegWithEffect(int misc_reg, const MiscReg &val) +{ + return this->thread->setMiscRegWithEffect(misc_reg, val); +} + +#if FULL_SYSTEM + +template <class Impl> +Fault +OzoneDynInst<Impl>::hwrei() +{ + if (!this->cpu->inPalMode(this->readPC())) + return new AlphaISA::UnimplementedOpcodeFault; + + this->setNextPC(this->thread->readMiscReg(AlphaISA::IPR_EXC_ADDR)); + + this->cpu->hwrei(); + + // FIXME: XXX check for interrupts? XXX + return NoFault; +} + +template <class Impl> +int +OzoneDynInst<Impl>::readIntrFlag() +{ +return this->cpu->readIntrFlag(); +} + +template <class Impl> +void +OzoneDynInst<Impl>::setIntrFlag(int val) +{ + this->cpu->setIntrFlag(val); +} + +template <class Impl> +bool +OzoneDynInst<Impl>::inPalMode() +{ + return this->cpu->inPalMode(); +} + +template <class Impl> +void +OzoneDynInst<Impl>::trap(Fault fault) +{ + fault->invoke(this->thread->getTC()); +} + +template <class Impl> +bool +OzoneDynInst<Impl>::simPalCheck(int palFunc) +{ + return this->cpu->simPalCheck(palFunc); +} +#else +template <class Impl> +void +OzoneDynInst<Impl>::syscall(uint64_t &callnum) +{ + this->cpu->syscall(callnum); +} +#endif diff --git a/src/cpu/ozone/ea_list.cc b/src/cpu/ozone/ea_list.cc new file mode 100644 index 000000000..5ef240700 --- /dev/null +++ b/src/cpu/ozone/ea_list.cc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Nathan Binkert + */ + +#include "arch/isa_traits.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ooo_cpu/ea_list.hh" + +void +EAList::addAddr(const InstSeqNum &new_sn, const Addr &new_ea) +{ + instEA newEA(new_sn, new_ea); + + eaList.push_back(newEA); +} + +void +EAList::clearAddr(const InstSeqNum &sn_to_clear, const Addr &ea_to_clear) +{ + eaListIt list_it = eaList.begin(); + + while (list_it != eaList.end() && (*list_it).first != sn_to_clear) { + assert((*list_it).second == ea_to_clear); + } +} + +bool +EAList::checkConflict(const InstSeqNum &check_sn, const Addr &check_ea) const +{ + const constEAListIt list_it = eaList.begin(); + + while (list_it != eaList.end() && (*list_it).first < check_sn) { + if ((*list_it).second == check_ea) { + return true; + } + } + + return false; +} + +void +EAList::clear() +{ + eaList.clear(); +} + +void +EAList::commit(const InstSeqNum &commit_sn) +{ + while (!eaList.empty() && eaList.front().first <= commit_sn) { + eaList.pop_front(); + } +} diff --git a/src/cpu/ozone/ea_list.hh b/src/cpu/ozone/ea_list.hh new file mode 100644 index 000000000..d9e9d701f --- /dev/null +++ b/src/cpu/ozone/ea_list.hh @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + * Nathan Binkert + */ + +#ifndef __CPU_EA_LIST_HH__ +#define __CPU_EA_LIST_HH__ + +#include <list> +#include <utility> + +#include "cpu/inst_seq.hh" +#include "sim/host.hh" + +/** + * Simple class to hold onto a list of pairs, each pair having a memory + * instruction's sequence number and effective addr. This list can be used + * for memory disambiguation. However, if I ever want to forward results, I + * may have to use a list that holds DynInstPtrs. Hence this may change in + * the future. + */ +class EAList { + private: + typedef std::pair<InstSeqNum, Addr> instEA; + typedef std::list<instEA>::iterator eaListIt; + typedef std::list<instEA>::const_iterator constEAListIt; + + std::list<instEA> eaList; + + public: + EAList() { } + ~EAList() { } + + void addAddr(const InstSeqNum &new_sn, const Addr &new_ea); + + void clearAddr(const InstSeqNum &sn_to_clear, const Addr &ea_to_clear); + + /** Checks if any instructions older than check_sn have a conflicting + * address with check_ea. Note that this function does not handle the + * sequence number rolling over. + */ + bool checkConflict(const InstSeqNum &check_sn, const Addr &check_ea) const; + + void clear(); + + void commit(const InstSeqNum &commit_sn); +}; + +#endif // __CPU_EA_LIST_HH__ diff --git a/src/cpu/ozone/front_end.cc b/src/cpu/ozone/front_end.cc new file mode 100644 index 000000000..cfd033564 --- /dev/null +++ b/src/cpu/ozone/front_end.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/front_end_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +//#include "cpu/ozone/simple_impl.hh" + +template class FrontEnd<OzoneImpl>; +//template class FrontEnd<SimpleImpl>; diff --git a/src/cpu/ozone/front_end.hh b/src/cpu/ozone/front_end.hh new file mode 100644 index 000000000..5ffd3666e --- /dev/null +++ b/src/cpu/ozone/front_end.hh @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_FRONT_END_HH__ +#define __CPU_OZONE_FRONT_END_HH__ + +#include <deque> + +#include "arch/utility.hh" +#include "base/timebuf.hh" +#include "cpu/inst_seq.hh" +#include "cpu/o3/bpred_unit.hh" +#include "cpu/ozone/rename_table.hh" +#include "mem/port.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" +#include "sim/stats.hh" + +class ThreadContext; +class MemObject; +template <class> +class OzoneThreadState; +class PageTable; +template <class> +class TimeBuffer; + +template <class Impl> +class FrontEnd +{ + public: + typedef typename Impl::Params Params; + typedef typename Impl::DynInst DynInst; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::CPUType CPUType; + typedef typename Impl::BackEnd BackEnd; + + typedef typename Impl::CPUType::OzoneTC OzoneTC; + typedef typename Impl::CPUType::CommStruct CommStruct; + + /** IcachePort class. Handles doing the communication with the + * cache/memory. + */ + class IcachePort : public Port + { + protected: + /** Pointer to FE. */ + FrontEnd<Impl> *fe; + + public: + /** Default constructor. */ + IcachePort(FrontEnd<Impl> *_fe) + : fe(_fe) + { } + + protected: + /** Atomic version of receive. Panics. */ + virtual Tick recvAtomic(PacketPtr pkt); + + /** Functional version of receive. Panics. */ + virtual void recvFunctional(PacketPtr pkt); + + /** Receives status change. Other than range changing, panics. */ + virtual void recvStatusChange(Status status); + + /** Returns the address ranges of this device. */ + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + /** Timing version of receive. Handles setting fetch to the + * proper status to start fetching. */ + virtual bool recvTiming(PacketPtr pkt); + + /** Handles doing a retry of a failed fetch. */ + virtual void recvRetry(); + }; + + FrontEnd(Params *params); + + std::string name() const; + + void setCPU(CPUType *cpu_ptr); + + void setBackEnd(BackEnd *back_end_ptr) + { backEnd = back_end_ptr; } + + void setCommBuffer(TimeBuffer<CommStruct> *_comm); + + void setTC(ThreadContext *tc_ptr); + + void setThreadState(OzoneThreadState<Impl> *thread_ptr) + { thread = thread_ptr; } + + void regStats(); + + Port *getIcachePort() { return &icachePort; } + + void tick(); + Fault fetchCacheLine(); + void processInst(DynInstPtr &inst); + void squash(const InstSeqNum &squash_num, const Addr &next_PC, + const bool is_branch = false, const bool branch_taken = false); + DynInstPtr getInst(); + + void processCacheCompletion(Packet *pkt); + + void addFreeRegs(int num_freed); + + bool isEmpty() { return instBuffer.empty(); } + + void switchOut(); + + void doSwitchOut(); + + void takeOverFrom(ThreadContext *old_tc = NULL); + + bool isSwitchedOut() { return switchedOut; } + + bool switchedOut; + + private: + void recvRetry(); + + bool updateStatus(); + + void checkBE(); + DynInstPtr getInstFromCacheline(); + void renameInst(DynInstPtr &inst); + // Returns true if we need to stop the front end this cycle + bool processBarriers(DynInstPtr &inst); + + void handleFault(Fault &fault); + public: + Fault getFault() { return fetchFault; } + private: + Fault fetchFault; + + // Align an address (typically a PC) to the start of an I-cache block. + // We fold in the PISA 64- to 32-bit conversion here as well. + Addr icacheBlockAlignPC(Addr addr) + { + addr = TheISA::realPCToFetchPC(addr); + return (addr & ~(cacheBlkMask)); + } + + InstSeqNum getAndIncrementInstSeq() + { return cpu->globalSeqNum++; } + + public: + CPUType *cpu; + + BackEnd *backEnd; + + ThreadContext *tc; + + OzoneThreadState<Impl> *thread; + + enum Status { + Running, + Idle, + IcacheWaitResponse, + IcacheWaitRetry, + IcacheAccessComplete, + SerializeBlocked, + SerializeComplete, + RenameBlocked, + QuiescePending, + TrapPending, + BEBlocked + }; + + Status status; + + private: + TimeBuffer<CommStruct> *comm; + typename TimeBuffer<CommStruct>::wire fromCommit; + + typedef typename Impl::BranchPred BranchPred; + + BranchPred branchPred; + + IcachePort icachePort; + + MemObject *mem; + + RequestPtr memReq; + + /** Mask to get a cache block's address. */ + Addr cacheBlkMask; + + unsigned cacheBlkSize; + + Addr cacheBlkPC; + + /** The cache line being fetched. */ + uint8_t *cacheData; + + bool fetchCacheLineNextCycle; + + bool cacheBlkValid; + + bool cacheBlocked; + + /** The packet that is waiting to be retried. */ + PacketPtr retryPkt; + + public: + RenameTable<Impl> renameTable; + + private: + Addr PC; + Addr nextPC; + + public: + void setPC(Addr val) { PC = val; } + void setNextPC(Addr val) { nextPC = val; } + + void wakeFromQuiesce(); + + void dumpInsts(); + + private: + TimeBuffer<int> numInstsReady; + + typedef typename std::deque<DynInstPtr> InstBuff; + typedef typename InstBuff::iterator InstBuffIt; + + InstBuff feBuffer; + + InstBuff instBuffer; + + int instBufferSize; + + int maxInstBufferSize; + + int latency; + + int width; + + int freeRegs; + + int numPhysRegs; + + bool serializeNext; + + DynInstPtr barrierInst; + + public: + bool interruptPending; + private: + // number of idle cycles +/* + Stats::Average<> notIdleFraction; + Stats::Formula idleFraction; +*/ + // @todo: Consider making these vectors and tracking on a per thread basis. + /** Stat for total number of cycles stalled due to an icache miss. */ + Stats::Scalar<> icacheStallCycles; + /** Stat for total number of fetched instructions. */ + Stats::Scalar<> fetchedInsts; + Stats::Scalar<> fetchedBranches; + /** Stat for total number of predicted branches. */ + Stats::Scalar<> predictedBranches; + /** Stat for total number of cycles spent fetching. */ + Stats::Scalar<> fetchCycles; + + Stats::Scalar<> fetchIdleCycles; + /** Stat for total number of cycles spent squashing. */ + Stats::Scalar<> fetchSquashCycles; + /** Stat for total number of cycles spent blocked due to other stages in + * the pipeline. + */ + Stats::Scalar<> fetchBlockedCycles; + /** Stat for total number of fetched cache lines. */ + Stats::Scalar<> fetchedCacheLines; + + Stats::Scalar<> fetchIcacheSquashes; + /** Distribution of number of instructions fetched each cycle. */ + Stats::Distribution<> fetchNisnDist; +// Stats::Vector<> qfull_iq_occupancy; +// Stats::VectorDistribution<> qfull_iq_occ_dist_; + Stats::Formula idleRate; + Stats::Formula branchRate; + Stats::Formula fetchRate; + Stats::Scalar<> IFQCount; // cumulative IFQ occupancy + Stats::Formula IFQOccupancy; + Stats::Formula IFQLatency; + Stats::Scalar<> IFQFcount; // cumulative IFQ full count + Stats::Formula IFQFullRate; + + Stats::Scalar<> dispatchCountStat; + Stats::Scalar<> dispatchedSerializing; + Stats::Scalar<> dispatchedTempSerializing; + Stats::Scalar<> dispatchSerializeStallCycles; + Stats::Formula dispatchRate; + Stats::Formula regIntFull; + Stats::Formula regFpFull; +}; + +#endif // __CPU_OZONE_FRONT_END_HH__ diff --git a/src/cpu/ozone/front_end_impl.hh b/src/cpu/ozone/front_end_impl.hh new file mode 100644 index 000000000..d34716de6 --- /dev/null +++ b/src/cpu/ozone/front_end_impl.hh @@ -0,0 +1,1016 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "config/use_checker.hh" + +#include "arch/faults.hh" +#include "arch/isa_traits.hh" +#include "arch/utility.hh" +#include "base/statistics.hh" +#include "cpu/thread_context.hh" +#include "cpu/exetrace.hh" +#include "cpu/ozone/front_end.hh" +#include "mem/mem_object.hh" +#include "mem/packet.hh" +#include "mem/request.hh" + +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + +using namespace TheISA; + +template<class Impl> +Tick +FrontEnd<Impl>::IcachePort::recvAtomic(PacketPtr pkt) +{ + panic("FrontEnd doesn't expect recvAtomic callback!"); + return curTick; +} + +template<class Impl> +void +FrontEnd<Impl>::IcachePort::recvFunctional(PacketPtr pkt) +{ + panic("FrontEnd doesn't expect recvFunctional callback!"); +} + +template<class Impl> +void +FrontEnd<Impl>::IcachePort::recvStatusChange(Status status) +{ + if (status == RangeChange) + return; + + panic("FrontEnd doesn't expect recvStatusChange callback!"); +} + +template<class Impl> +bool +FrontEnd<Impl>::IcachePort::recvTiming(Packet *pkt) +{ + fe->processCacheCompletion(pkt); + return true; +} + +template<class Impl> +void +FrontEnd<Impl>::IcachePort::recvRetry() +{ + fe->recvRetry(); +} + +template <class Impl> +FrontEnd<Impl>::FrontEnd(Params *params) + : branchPred(params), + icachePort(this), + mem(params->mem), + numInstsReady(params->frontEndLatency, 0), + instBufferSize(0), + maxInstBufferSize(params->maxInstBufferSize), + latency(params->frontEndLatency), + width(params->frontEndWidth), + freeRegs(params->numPhysicalRegs), + numPhysRegs(params->numPhysicalRegs), + serializeNext(false), + interruptPending(false) +{ + switchedOut = false; + + status = Idle; + + memReq = NULL; + // Size of cache block. + cacheBlkSize = 64; + + assert(isPowerOf2(cacheBlkSize)); + + // Create mask to get rid of offset bits. + cacheBlkMask = (cacheBlkSize - 1); + + // Create space to store a cache line. + cacheData = new uint8_t[cacheBlkSize]; + + fetchCacheLineNextCycle = true; + + cacheBlkValid = cacheBlocked = false; + + retryPkt = NULL; + + fetchFault = NoFault; +} + +template <class Impl> +std::string +FrontEnd<Impl>::name() const +{ + return cpu->name() + ".frontend"; +} + +template <class Impl> +void +FrontEnd<Impl>::setCPU(CPUType *cpu_ptr) +{ + cpu = cpu_ptr; + + icachePort.setName(this->name() + "-iport"); + +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->setIcachePort(&icachePort); + } +#endif +} + +template <class Impl> +void +FrontEnd<Impl>::setCommBuffer(TimeBuffer<CommStruct> *_comm) +{ + comm = _comm; + // @todo: Hardcoded for now. Allow this to be set by a latency. + fromCommit = comm->getWire(-1); +} + +template <class Impl> +void +FrontEnd<Impl>::setTC(ThreadContext *tc_ptr) +{ + tc = tc_ptr; +} + +template <class Impl> +void +FrontEnd<Impl>::regStats() +{ + icacheStallCycles + .name(name() + ".icacheStallCycles") + .desc("Number of cycles fetch is stalled on an Icache miss") + .prereq(icacheStallCycles); + + fetchedInsts + .name(name() + ".fetchedInsts") + .desc("Number of instructions fetch has processed") + .prereq(fetchedInsts); + + fetchedBranches + .name(name() + ".fetchedBranches") + .desc("Number of fetched branches") + .prereq(fetchedBranches); + + predictedBranches + .name(name() + ".predictedBranches") + .desc("Number of branches that fetch has predicted taken") + .prereq(predictedBranches); + + fetchCycles + .name(name() + ".fetchCycles") + .desc("Number of cycles fetch has run and was not squashing or" + " blocked") + .prereq(fetchCycles); + + fetchIdleCycles + .name(name() + ".fetchIdleCycles") + .desc("Number of cycles fetch was idle") + .prereq(fetchIdleCycles); + + fetchSquashCycles + .name(name() + ".fetchSquashCycles") + .desc("Number of cycles fetch has spent squashing") + .prereq(fetchSquashCycles); + + fetchBlockedCycles + .name(name() + ".fetchBlockedCycles") + .desc("Number of cycles fetch has spent blocked") + .prereq(fetchBlockedCycles); + + fetchedCacheLines + .name(name() + ".fetchedCacheLines") + .desc("Number of cache lines fetched") + .prereq(fetchedCacheLines); + + fetchIcacheSquashes + .name(name() + ".fetchIcacheSquashes") + .desc("Number of outstanding Icache misses that were squashed") + .prereq(fetchIcacheSquashes); + + fetchNisnDist + .init(/* base value */ 0, + /* last value */ width, + /* bucket size */ 1) + .name(name() + ".rateDist") + .desc("Number of instructions fetched each cycle (Total)") + .flags(Stats::pdf); + + idleRate + .name(name() + ".idleRate") + .desc("Percent of cycles fetch was idle") + .prereq(idleRate); + idleRate = fetchIdleCycles * 100 / cpu->numCycles; + + branchRate + .name(name() + ".branchRate") + .desc("Number of branch fetches per cycle") + .flags(Stats::total); + branchRate = fetchedBranches / cpu->numCycles; + + fetchRate + .name(name() + ".rate") + .desc("Number of inst fetches per cycle") + .flags(Stats::total); + fetchRate = fetchedInsts / cpu->numCycles; + + IFQCount + .name(name() + ".IFQ:count") + .desc("cumulative IFQ occupancy") + ; + + IFQFcount + .name(name() + ".IFQ:fullCount") + .desc("cumulative IFQ full count") + .flags(Stats::total) + ; + + IFQOccupancy + .name(name() + ".IFQ:occupancy") + .desc("avg IFQ occupancy (inst's)") + ; + IFQOccupancy = IFQCount / cpu->numCycles; + + IFQLatency + .name(name() + ".IFQ:latency") + .desc("avg IFQ occupant latency (cycle's)") + .flags(Stats::total) + ; + + IFQFullRate + .name(name() + ".IFQ:fullRate") + .desc("fraction of time (cycles) IFQ was full") + .flags(Stats::total); + ; + IFQFullRate = IFQFcount * Stats::constant(100) / cpu->numCycles; + + dispatchCountStat + .name(name() + ".DIS:count") + .desc("cumulative count of dispatched insts") + .flags(Stats::total) + ; + + dispatchedSerializing + .name(name() + ".DIS:serializingInsts") + .desc("count of serializing insts dispatched") + .flags(Stats::total) + ; + + dispatchedTempSerializing + .name(name() + ".DIS:tempSerializingInsts") + .desc("count of temporary serializing insts dispatched") + .flags(Stats::total) + ; + + dispatchSerializeStallCycles + .name(name() + ".DIS:serializeStallCycles") + .desc("count of cycles dispatch stalled for serializing inst") + .flags(Stats::total) + ; + + dispatchRate + .name(name() + ".DIS:rate") + .desc("dispatched insts per cycle") + .flags(Stats::total) + ; + dispatchRate = dispatchCountStat / cpu->numCycles; + + regIntFull + .name(name() + ".REG:int:full") + .desc("number of cycles where there were no INT registers") + ; + + regFpFull + .name(name() + ".REG:fp:full") + .desc("number of cycles where there were no FP registers") + ; + IFQLatency = IFQOccupancy / dispatchRate; + + branchPred.regStats(); +} + +template <class Impl> +void +FrontEnd<Impl>::tick() +{ + if (switchedOut) + return; + + for (int insts_to_queue = numInstsReady[-latency]; + !instBuffer.empty() && insts_to_queue; + --insts_to_queue) + { + DPRINTF(FE, "Transferring instruction [sn:%lli] to the feBuffer\n", + instBuffer.front()->seqNum); + feBuffer.push_back(instBuffer.front()); + instBuffer.pop_front(); + } + + numInstsReady.advance(); + + // @todo: Maybe I want to just have direct communication... + if (fromCommit->doneSeqNum) { + branchPred.update(fromCommit->doneSeqNum, 0); + } + + IFQCount += instBufferSize; + IFQFcount += instBufferSize == maxInstBufferSize; + + // Fetch cache line + if (status == IcacheAccessComplete) { + cacheBlkValid = true; + + status = Running; +// if (barrierInst) +// status = SerializeBlocked; + if (freeRegs <= 0) + status = RenameBlocked; + checkBE(); + } else if (status == IcacheWaitResponse || status == IcacheWaitRetry) { + DPRINTF(FE, "Still in Icache wait.\n"); + icacheStallCycles++; + return; + } + + if (status == RenameBlocked || status == SerializeBlocked || + status == TrapPending || status == BEBlocked) { + // Will cause a one cycle bubble between changing state and + // restarting. + DPRINTF(FE, "In blocked status.\n"); + + fetchBlockedCycles++; + + if (status == SerializeBlocked) { + dispatchSerializeStallCycles++; + } + updateStatus(); + return; + } else if (status == QuiescePending) { + DPRINTF(FE, "Waiting for quiesce to execute or get squashed.\n"); + return; + } else if (status != IcacheAccessComplete) { + if (fetchCacheLineNextCycle) { + Fault fault = fetchCacheLine(); + if (fault != NoFault) { + handleFault(fault); + fetchFault = fault; + return; + } + fetchCacheLineNextCycle = false; + } + // If miss, stall until it returns. + if (status == IcacheWaitResponse || status == IcacheWaitRetry) { + // Tell CPU to not tick me for now. + return; + } + } + + fetchCycles++; + + int num_inst = 0; + + // Otherwise loop and process instructions. + // One way to hack infinite width is to set width and maxInstBufferSize + // both really high. Inelegant, but probably will work. + while (num_inst < width && + instBufferSize < maxInstBufferSize) { + // Get instruction from cache line. + DynInstPtr inst = getInstFromCacheline(); + + if (!inst) { + // PC is no longer in the cache line, end fetch. + // Might want to check this at the end of the cycle so that + // there's no cycle lost to checking for a new cache line. + DPRINTF(FE, "Need to get new cache line\n"); + fetchCacheLineNextCycle = true; + break; + } + + processInst(inst); + + if (status == SerializeBlocked) { + break; + } + + // Possibly push into a time buffer that estimates the front end + // latency + instBuffer.push_back(inst); + ++instBufferSize; + numInstsReady[0]++; + ++num_inst; + +#if FULL_SYSTEM + if (inst->isQuiesce()) { +// warn("%lli: Quiesce instruction encountered, halting fetch!", curTick); + status = QuiescePending; + break; + } +#endif + + if (inst->predTaken()) { + // Start over with tick? + break; + } else if (freeRegs <= 0) { + DPRINTF(FE, "Ran out of free registers to rename to!\n"); + status = RenameBlocked; + break; + } else if (serializeNext) { + break; + } + } + + fetchNisnDist.sample(num_inst); + checkBE(); + + DPRINTF(FE, "Num insts processed: %i, Inst Buffer size: %i, Free " + "Regs %i\n", num_inst, instBufferSize, freeRegs); +} + +template <class Impl> +Fault +FrontEnd<Impl>::fetchCacheLine() +{ + // Read a cache line, based on the current PC. +#if FULL_SYSTEM + // Flag to say whether or not address is physical addr. + unsigned flags = cpu->inPalMode(PC) ? PHYSICAL : 0; +#else + unsigned flags = 0; +#endif // FULL_SYSTEM + Fault fault = NoFault; + + if (interruptPending && flags == 0) { + return fault; + } + + // Align the fetch PC so it's at the start of a cache block. + Addr fetch_PC = icacheBlockAlignPC(PC); + + DPRINTF(FE, "Fetching cache line starting at %#x.\n", fetch_PC); + + // Setup the memReq to do a read of the first isntruction's address. + // Set the appropriate read size and flags as well. + memReq = new Request(0, fetch_PC, cacheBlkSize, flags, + fetch_PC, cpu->readCpuId(), 0); + + // Translate the instruction request. + fault = cpu->translateInstReq(memReq, thread); + + // Now do the timing access to see whether or not the instruction + // exists within the cache. + if (fault == NoFault) { +#if 0 + if (cpu->system->memctrl->badaddr(memReq->paddr) || + memReq->flags & UNCACHEABLE) { + DPRINTF(FE, "Fetch: Bad address %#x (hopefully on a " + "misspeculating path!", + memReq->paddr); + return TheISA::genMachineCheckFault(); + } +#endif + + // Build packet here. + PacketPtr data_pkt = new Packet(memReq, + Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(cacheData); + + if (!icachePort.sendTiming(data_pkt)) { + assert(retryPkt == NULL); + DPRINTF(Fetch, "Out of MSHRs!\n"); + status = IcacheWaitRetry; + retryPkt = data_pkt; + cacheBlocked = true; + return NoFault; + } + + status = IcacheWaitResponse; + } + + // Note that this will set the cache block PC a bit earlier than it should + // be set. + cacheBlkPC = fetch_PC; + + ++fetchedCacheLines; + + DPRINTF(FE, "Done fetching cache line.\n"); + + return fault; +} + +template <class Impl> +void +FrontEnd<Impl>::processInst(DynInstPtr &inst) +{ + if (processBarriers(inst)) { + return; + } + + Addr inst_PC = inst->readPC(); + + if (!inst->isControl()) { + inst->setPredTarg(inst->readNextPC()); + } else { + fetchedBranches++; + if (branchPred.predict(inst, inst_PC, inst->threadNumber)) { + predictedBranches++; + } + } + + Addr next_PC = inst->readPredTarg(); + + DPRINTF(FE, "[sn:%lli] Predicted and processed inst PC %#x, next PC " + "%#x\n", inst->seqNum, inst_PC, next_PC); + +// inst->setNextPC(next_PC); + + // Not sure where I should set this + PC = next_PC; + + renameInst(inst); +} + +template <class Impl> +bool +FrontEnd<Impl>::processBarriers(DynInstPtr &inst) +{ + if (serializeNext) { + inst->setSerializeBefore(); + serializeNext = false; + } else if (!inst->isSerializing() && + !inst->isIprAccess() && + !inst->isStoreConditional()) { + return false; + } + + if ((inst->isIprAccess() || inst->isSerializeBefore()) && + !inst->isSerializeHandled()) { + DPRINTF(FE, "Serialize before instruction encountered.\n"); + + if (!inst->isTempSerializeBefore()) { + dispatchedSerializing++; + inst->setSerializeHandled(); + } else { + dispatchedTempSerializing++; + } + + // Change status over to SerializeBlocked so that other stages know + // what this is blocked on. +// status = SerializeBlocked; + +// barrierInst = inst; +// return true; + } else if ((inst->isStoreConditional() || inst->isSerializeAfter()) + && !inst->isSerializeHandled()) { + DPRINTF(FE, "Serialize after instruction encountered.\n"); + + inst->setSerializeHandled(); + + dispatchedSerializing++; + + serializeNext = true; + return false; + } + return false; +} + +template <class Impl> +void +FrontEnd<Impl>::handleFault(Fault &fault) +{ + DPRINTF(FE, "Fault at fetch, telling commit\n"); + + // We're blocked on the back end until it handles this fault. + status = TrapPending; + + // Get a sequence number. + InstSeqNum inst_seq = getAndIncrementInstSeq(); + // We will use a nop in order to carry the fault. + ExtMachInst ext_inst = TheISA::NoopMachInst; + + // Create a new DynInst from the dummy nop. + DynInstPtr instruction = new DynInst(ext_inst, PC, + PC+sizeof(MachInst), + inst_seq, cpu); + instruction->setPredTarg(instruction->readNextPC()); +// instruction->setThread(tid); + +// instruction->setASID(tid); + + instruction->setThreadState(thread); + + instruction->traceData = NULL; + + instruction->fault = fault; + instruction->setCanIssue(); + instBuffer.push_back(instruction); + numInstsReady[0]++; + ++instBufferSize; +} + +template <class Impl> +void +FrontEnd<Impl>::squash(const InstSeqNum &squash_num, const Addr &next_PC, + const bool is_branch, const bool branch_taken) +{ + DPRINTF(FE, "Squashing from [sn:%lli], setting PC to %#x\n", + squash_num, next_PC); + + if (fetchFault != NoFault) + fetchFault = NoFault; + + while (!instBuffer.empty() && + instBuffer.back()->seqNum > squash_num) { + DynInstPtr inst = instBuffer.back(); + + DPRINTF(FE, "Squashing instruction [sn:%lli] PC %#x\n", + inst->seqNum, inst->readPC()); + + inst->clearDependents(); + + instBuffer.pop_back(); + --instBufferSize; + + freeRegs+= inst->numDestRegs(); + } + + while (!feBuffer.empty() && + feBuffer.back()->seqNum > squash_num) { + DynInstPtr inst = feBuffer.back(); + + DPRINTF(FE, "Squashing instruction [sn:%lli] PC %#x\n", + inst->seqNum, inst->readPC()); + + inst->clearDependents(); + + feBuffer.pop_back(); + --instBufferSize; + + freeRegs+= inst->numDestRegs(); + } + + // Copy over rename table from the back end. + renameTable.copyFrom(backEnd->renameTable); + + PC = next_PC; + + // Update BP with proper information. + if (is_branch) { + branchPred.squash(squash_num, next_PC, branch_taken, 0); + } else { + branchPred.squash(squash_num, 0); + } + + // Clear the icache miss if it's outstanding. + if (status == IcacheWaitResponse) { + DPRINTF(FE, "Squashing outstanding Icache access.\n"); + memReq = NULL; + } +/* + if (status == SerializeBlocked) { + assert(barrierInst->seqNum > squash_num); + barrierInst = NULL; + } +*/ + // Unless this squash originated from the front end, we're probably + // in running mode now. + // Actually might want to make this latency dependent. + status = Running; + fetchCacheLineNextCycle = true; +} + +template <class Impl> +typename Impl::DynInstPtr +FrontEnd<Impl>::getInst() +{ + if (feBuffer.empty()) { + return NULL; + } + + DynInstPtr inst = feBuffer.front(); + + if (inst->isSerializeBefore() || inst->isIprAccess()) { + DPRINTF(FE, "Back end is getting a serialize before inst\n"); + if (!backEnd->robEmpty()) { + DPRINTF(FE, "Rob is not empty yet, not returning inst\n"); + return NULL; + } + inst->clearSerializeBefore(); + } + + feBuffer.pop_front(); + + --instBufferSize; + + dispatchCountStat++; + + return inst; +} + +template <class Impl> +void +FrontEnd<Impl>::processCacheCompletion(PacketPtr pkt) +{ + DPRINTF(FE, "Processing cache completion\n"); + + // Do something here. + if (status != IcacheWaitResponse || + pkt->req != memReq || + switchedOut) { + DPRINTF(FE, "Previous fetch was squashed.\n"); + fetchIcacheSquashes++; + delete pkt->req; + delete pkt; + return; + } + + status = IcacheAccessComplete; + +/* if (checkStall(tid)) { + fetchStatus[tid] = Blocked; + } else { + fetchStatus[tid] = IcacheMissComplete; + } +*/ +// memcpy(cacheData, memReq->data, memReq->size); + + // Reset the completion event to NULL. +// memReq->completionEvent = NULL; + delete pkt->req; + delete pkt; + memReq = NULL; +} + +template <class Impl> +void +FrontEnd<Impl>::addFreeRegs(int num_freed) +{ + if (status == RenameBlocked && freeRegs + num_freed > 0) { + status = Running; + } + + DPRINTF(FE, "Adding %i freed registers\n", num_freed); + + freeRegs+= num_freed; + +// assert(freeRegs <= numPhysRegs); + if (freeRegs > numPhysRegs) + freeRegs = numPhysRegs; +} + +template <class Impl> +void +FrontEnd<Impl>::recvRetry() +{ + assert(cacheBlocked); + if (retryPkt != NULL) { + assert(status == IcacheWaitRetry); + + if (icachePort.sendTiming(retryPkt)) { + status = IcacheWaitResponse; + retryPkt = NULL; + cacheBlocked = false; + } + } else { + // Access has been squashed since it was sent out. Just clear + // the cache being blocked. + cacheBlocked = false; + } + +} + +template <class Impl> +bool +FrontEnd<Impl>::updateStatus() +{ + bool serialize_block = !backEnd->robEmpty() || instBufferSize; + bool be_block = cpu->decoupledFrontEnd ? false : backEnd->isBlocked(); + bool ret_val = false; + + if (status == SerializeBlocked && !serialize_block) { + status = SerializeComplete; + ret_val = true; + } + + if (status == BEBlocked && !be_block) { +// if (barrierInst) { +// status = SerializeBlocked; +// } else { + status = Running; +// } + ret_val = true; + } + return ret_val; +} + +template <class Impl> +void +FrontEnd<Impl>::checkBE() +{ + bool be_block = cpu->decoupledFrontEnd ? false : backEnd->isBlocked(); + if (be_block) { + if (status == Running || status == Idle) { + status = BEBlocked; + } + } +} + +template <class Impl> +typename Impl::DynInstPtr +FrontEnd<Impl>::getInstFromCacheline() +{ +/* + if (status == SerializeComplete) { + DynInstPtr inst = barrierInst; + status = Running; + barrierInst = NULL; + inst->clearSerializeBefore(); + return inst; + } +*/ + InstSeqNum inst_seq; + MachInst inst; + // @todo: Fix this magic number used here to handle word offset (and + // getting rid of PAL bit) + unsigned offset = (PC & cacheBlkMask) & ~3; + + // PC of inst is not in this cache block + if (PC >= (cacheBlkPC + cacheBlkSize) || PC < cacheBlkPC || !cacheBlkValid) { + return NULL; + } + + ////////////////////////// + // Fetch one instruction + ////////////////////////// + + // Get a sequence number. + inst_seq = getAndIncrementInstSeq(); + + // Make sure this is a valid index. + assert(offset <= cacheBlkSize - sizeof(MachInst)); + + // Get the instruction from the array of the cache line. + inst = htog(*reinterpret_cast<MachInst *>(&cacheData[offset])); + + ExtMachInst decode_inst = TheISA::makeExtMI(inst, PC); + + // Create a new DynInst from the instruction fetched. + DynInstPtr instruction = new DynInst(decode_inst, PC, PC+sizeof(MachInst), + inst_seq, cpu); + + instruction->setThreadState(thread); + + DPRINTF(FE, "Instruction [sn:%lli] created, with PC %#x\n%s\n", + inst_seq, instruction->readPC(), + instruction->staticInst->disassemble(PC)); + + instruction->traceData = + Trace::getInstRecord(curTick, tc, + instruction->staticInst, + instruction->readPC()); + + // Increment stat of fetched instructions. + ++fetchedInsts; + + return instruction; +} + +template <class Impl> +void +FrontEnd<Impl>::renameInst(DynInstPtr &inst) +{ + DynInstPtr src_inst = NULL; + int num_src_regs = inst->numSrcRegs(); + if (num_src_regs == 0) { + inst->setCanIssue(); + } else { + for (int i = 0; i < num_src_regs; ++i) { + src_inst = renameTable[inst->srcRegIdx(i)]; + + inst->setSrcInst(src_inst, i); + + DPRINTF(FE, "[sn:%lli]: Src reg %i is inst [sn:%lli]\n", + inst->seqNum, (int)inst->srcRegIdx(i), src_inst->seqNum); + + if (src_inst->isResultReady()) { + DPRINTF(FE, "Reg ready.\n"); + inst->markSrcRegReady(i); + } else { + DPRINTF(FE, "Adding to dependent list.\n"); + src_inst->addDependent(inst); + } + } + } + + for (int i = 0; i < inst->numDestRegs(); ++i) { + RegIndex idx = inst->destRegIdx(i); + + DPRINTF(FE, "Dest reg %i is now inst [sn:%lli], was previously " + "[sn:%lli]\n", + (int)inst->destRegIdx(i), inst->seqNum, + renameTable[idx]->seqNum); + + inst->setPrevDestInst(renameTable[idx], i); + + renameTable[idx] = inst; + --freeRegs; + } +} + +template <class Impl> +void +FrontEnd<Impl>::wakeFromQuiesce() +{ + DPRINTF(FE, "Waking up from quiesce\n"); + // Hopefully this is safe + status = Running; +} + +template <class Impl> +void +FrontEnd<Impl>::switchOut() +{ + switchedOut = true; + cpu->signalSwitched(); +} + +template <class Impl> +void +FrontEnd<Impl>::doSwitchOut() +{ + memReq = NULL; + squash(0, 0); + instBuffer.clear(); + instBufferSize = 0; + feBuffer.clear(); + status = Idle; +} + +template <class Impl> +void +FrontEnd<Impl>::takeOverFrom(ThreadContext *old_tc) +{ + assert(freeRegs == numPhysRegs); + fetchCacheLineNextCycle = true; + + cacheBlkValid = false; + +#if !FULL_SYSTEM +// pTable = params->pTable; +#endif + fetchFault = NoFault; + serializeNext = false; + barrierInst = NULL; + status = Running; + switchedOut = false; + interruptPending = false; +} + +template <class Impl> +void +FrontEnd<Impl>::dumpInsts() +{ + cprintf("instBuffer size: %i\n", instBuffer.size()); + + InstBuffIt buff_it = instBuffer.begin(); + + for (int num = 0; buff_it != instBuffer.end(); num++) { + cprintf("Instruction:%i\nPC:%#x\n[tid:%i]\n[sn:%lli]\nIssued:%i\n" + "Squashed:%i\n\n", + num, (*buff_it)->readPC(), (*buff_it)->threadNumber, + (*buff_it)->seqNum, (*buff_it)->isIssued(), + (*buff_it)->isSquashed()); + buff_it++; + } +} diff --git a/src/cpu/ozone/inorder_back_end.cc b/src/cpu/ozone/inorder_back_end.cc new file mode 100644 index 000000000..bc6618e4a --- /dev/null +++ b/src/cpu/ozone/inorder_back_end.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/inorder_back_end_impl.hh" +#include "cpu/ozone/simple_impl.hh" + +template class InorderBackEnd<SimpleImpl>; diff --git a/src/cpu/ozone/inorder_back_end.hh b/src/cpu/ozone/inorder_back_end.hh new file mode 100644 index 000000000..ffdba2f6c --- /dev/null +++ b/src/cpu/ozone/inorder_back_end.hh @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_INORDER_BACK_END_HH__ +#define __CPU_OZONE_INORDER_BACK_END_HH__ + +#include <list> + +#include "arch/faults.hh" +#include "base/timebuf.hh" +#include "cpu/thread_context.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/rename_table.hh" +#include "cpu/ozone/thread_state.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" + +template <class Impl> +class InorderBackEnd +{ + public: + typedef typename Impl::Params Params; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::FrontEnd FrontEnd; + + typedef typename FullCPU::OzoneTC OzoneTC; + typedef typename Impl::FullCPU::CommStruct CommStruct; + + InorderBackEnd(Params *params); + + std::string name() const; + + void setCPU(FullCPU *cpu_ptr) + { cpu = cpu_ptr; } + + void setFrontEnd(FrontEnd *front_end_ptr) + { frontEnd = front_end_ptr; } + + void setCommBuffer(TimeBuffer<CommStruct> *_comm) + { comm = _comm; } + + void setTC(ThreadContext *tc_ptr); + + void setThreadState(OzoneThreadState<Impl> *thread_ptr); + + void regStats() { } + +#if FULL_SYSTEM + void checkInterrupts(); +#endif + + void tick(); + void executeInsts(); + void squash(const InstSeqNum &squash_num, const Addr &next_PC); + + void squashFromXC(); + void generateXCEvent() { } + + bool robEmpty() { return instList.empty(); } + + bool isFull() { return false; } + bool isBlocked() { return status == DcacheMissStoreStall || + status == DcacheMissLoadStall || + interruptBlocked; } + + void fetchFault(Fault &fault); + + void dumpInsts(); + + private: + void handleFault(); + + void setSquashInfoFromTC(); + + bool squashPending; + InstSeqNum squashSeqNum; + Addr squashNextPC; + + Fault faultFromFetch; + + bool interruptBlocked; + + public: + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + template <class T> + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); + + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + Addr readCommitPC() { return commitPC; } + + Addr commitPC; + + void switchOut() { panic("Not implemented!"); } + void doSwitchOut() { panic("Not implemented!"); } + void takeOverFrom(ThreadContext *old_tc = NULL) { panic("Not implemented!"); } + + public: + FullCPU *cpu; + + FrontEnd *frontEnd; + + ThreadContext *tc; + + OzoneThreadState<Impl> *thread; + + RenameTable<Impl> renameTable; + + protected: + enum Status { + Running, + Idle, + DcacheMissLoadStall, + DcacheMissStoreStall, + DcacheMissComplete, + Blocked + }; + + Status status; + + class DCacheCompletionEvent : public Event + { + private: + InorderBackEnd *be; + + public: + DCacheCompletionEvent(InorderBackEnd *_be); + + virtual void process(); + virtual const char *description(); + + DynInstPtr inst; + }; + + friend class DCacheCompletionEvent; + + DCacheCompletionEvent cacheCompletionEvent; + +// MemInterface *dcacheInterface; + + RequestPtr memReq; + + private: + typedef typename std::list<DynInstPtr>::iterator InstListIt; + + std::list<DynInstPtr> instList; + + // General back end width. Used if the more specific isn't given. + int width; + + int latency; + + int squashLatency; + + TimeBuffer<int> numInstsToWB; + TimeBuffer<int>::wire instsAdded; + TimeBuffer<int>::wire instsToExecute; + + TimeBuffer<CommStruct> *comm; + // number of cycles stalled for D-cache misses + Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; +}; + +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::read(Addr addr, T &data, unsigned flags) +{ + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = cpu->translateDataReadReq(memReq); + + // if we have a cache, do cache access too + if (fault == NoFault && dcacheInterface) { + memReq->cmd = Read; + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT) { + // Fix this hack for keeping funcExeInst correct with loads that + // are executed twice. + memReq->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); + status = DcacheMissLoadStall; + DPRINTF(IBE, "Dcache miss stall!\n"); + } else { + // do functional access + DPRINTF(IBE, "Dcache hit!\n"); + } + } +/* + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Read"); +*/ + return fault; +} +#if 0 +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::read(MemReqPtr &req, T &data) +{ +#if FULL_SYSTEM && defined(TARGET_ALPHA) + if (req->flags & LOCKED) { + req->xc->setMiscReg(TheISA::Lock_Addr_DepTag, req->paddr); + req->xc->setMiscReg(TheISA::Lock_Flag_DepTag, true); + } +#endif + + Fault error; + error = thread->mem->read(req, data); + data = LittleEndianGuest::gtoh(data); + return error; +} +#endif + +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::write(T data, Addr addr, unsigned flags, uint64_t *res) +{ + memReq->reset(addr, sizeof(T), flags); + + // translate to physical address + Fault fault = cpu->translateDataWriteReq(memReq); + + if (fault == NoFault && dcacheInterface) { + memReq->cmd = Write; +// memcpy(memReq->data,(uint8_t *)&data,memReq->size); + memReq->completionEvent = NULL; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(memReq); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT) { + memReq->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); + status = DcacheMissStoreStall; + DPRINTF(IBE, "Dcache miss stall!\n"); + } else { + DPRINTF(IBE, "Dcache hit!\n"); + } + } + + if (res && (fault == NoFault)) + *res = memReq->result; +/* + if (!dcacheInterface && (memReq->flags & UNCACHEABLE)) + recordEvent("Uncached Write"); +*/ + return fault; +} +#if 0 +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::write(MemReqPtr &req, T &data) +{ +#if FULL_SYSTEM && defined(TARGET_ALPHA) + ExecContext *xc; + + // If this is a store conditional, act appropriately + if (req->flags & LOCKED) { + xc = req->xc; + + if (req->flags & UNCACHEABLE) { + // Don't update result register (see stq_c in isa_desc) + req->result = 2; + xc->setStCondFailures(0);//Needed? [RGD] + } else { + bool lock_flag = xc->readMiscReg(TheISA::Lock_Flag_DepTag); + Addr lock_addr = xc->readMiscReg(TheISA::Lock_Addr_DepTag); + req->result = lock_flag; + if (!lock_flag || + ((lock_addr & ~0xf) != (req->paddr & ~0xf))) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + xc->setStCondFailures(xc->readStCondFailures() + 1); + if (((xc->readStCondFailures()) % 100000) == 0) { + std::cerr << "Warning: " + << xc->readStCondFailures() + << " consecutive store conditional failures " + << "on cpu " << req->xc->readCpuId() + << std::endl; + } + return NoFault; + } + else xc->setStCondFailures(0); + } + } + + // Need to clear any locked flags on other proccessors for + // this address. Only do this for succsful Store Conditionals + // and all other stores (WH64?). Unsuccessful Store + // Conditionals would have returned above, and wouldn't fall + // through. + for (int i = 0; i < cpu->system->execContexts.size(); i++){ + xc = cpu->system->execContexts[i]; + if ((xc->readMiscReg(TheISA::Lock_Addr_DepTag) & ~0xf) == + (req->paddr & ~0xf)) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + } + } + +#endif + return thread->mem->write(req, (T)LittleEndianGuest::htog(data)); +} +#endif + +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::read(MemReqPtr &req, T &data, int load_idx) +{ +// panic("Unimplemented!"); +// memReq->reset(addr, sizeof(T), flags); + + // translate to physical address +// Fault fault = cpu->translateDataReadReq(req); + req->cmd = Read; + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + req->flags &= ~INST_READ; + Fault fault = cpu->read(req, data); + memcpy(req->data, &data, sizeof(T)); + + // if we have a cache, do cache access too + if (dcacheInterface) { + MemAccessResult result = dcacheInterface->access(req); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT) { + req->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); + status = DcacheMissLoadStall; + DPRINTF(IBE, "Dcache miss load stall!\n"); + } else { + DPRINTF(IBE, "Dcache hit!\n"); + + } + } + +/* + if (!dcacheInterface && (req->flags & UNCACHEABLE)) + recordEvent("Uncached Read"); +*/ + return NoFault; +} + +template <class Impl> +template <class T> +Fault +InorderBackEnd<Impl>::write(MemReqPtr &req, T &data, int store_idx) +{ +// req->reset(addr, sizeof(T), flags); + + // translate to physical address +// Fault fault = cpu->translateDataWriteReq(req); + + req->cmd = Write; + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + memcpy(req->data, (uint8_t *)&data, req->size); + + switch(req->size) { + case 1: + cpu->write(req, (uint8_t &)data); + break; + case 2: + cpu->write(req, (uint16_t &)data); + break; + case 4: + cpu->write(req, (uint32_t &)data); + break; + case 8: + cpu->write(req, (uint64_t &)data); + break; + default: + panic("Unexpected store size!\n"); + } + + if (dcacheInterface) { + req->cmd = Write; + req->data = new uint8_t[64]; + memcpy(req->data,(uint8_t *)&data,req->size); + req->completionEvent = NULL; + req->time = curTick; + req->flags &= ~INST_READ; + MemAccessResult result = dcacheInterface->access(req); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + if (result != MA_HIT) { + req->completionEvent = &cacheCompletionEvent; + lastDcacheStall = curTick; +// unscheduleTickEvent(); + status = DcacheMissStoreStall; + DPRINTF(IBE, "Dcache miss store stall!\n"); + } else { + DPRINTF(IBE, "Dcache hit!\n"); + + } + } +/* + if (req->flags & LOCKED) { + if (req->flags & UNCACHEABLE) { + // Don't update result register (see stq_c in isa_desc) + req->result = 2; + } else { + req->result = 1; + } + } +*/ +/* + if (res && (fault == NoFault)) + *res = req->result; + */ +/* + if (!dcacheInterface && (req->flags & UNCACHEABLE)) + recordEvent("Uncached Write"); +*/ + return NoFault; +} + +#endif // __CPU_OZONE_INORDER_BACK_END_HH__ diff --git a/src/cpu/ozone/inorder_back_end_impl.hh b/src/cpu/ozone/inorder_back_end_impl.hh new file mode 100644 index 000000000..16ebac163 --- /dev/null +++ b/src/cpu/ozone/inorder_back_end_impl.hh @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "arch/faults.hh" +#include "arch/types.hh" +#include "cpu/ozone/inorder_back_end.hh" +#include "cpu/ozone/thread_state.hh" + +template <class Impl> +InorderBackEnd<Impl>::InorderBackEnd(Params *params) + : squashPending(false), + squashSeqNum(0), + squashNextPC(0), + faultFromFetch(NoFault), + interruptBlocked(false), + cacheCompletionEvent(this), + dcacheInterface(params->dcacheInterface), + width(params->backEndWidth), + latency(params->backEndLatency), + squashLatency(params->backEndSquashLatency), + numInstsToWB(0, latency + 1) +{ + instsAdded = numInstsToWB.getWire(latency); + instsToExecute = numInstsToWB.getWire(0); + + memReq = new MemReq; + memReq->data = new uint8_t[64]; + status = Running; +} + +template <class Impl> +std::string +InorderBackEnd<Impl>::name() const +{ + return cpu->name() + ".inorderbackend"; +} + +template <class Impl> +void +InorderBackEnd<Impl>::setXC(ExecContext *xc_ptr) +{ + xc = xc_ptr; + memReq->xc = xc; +} + +template <class Impl> +void +InorderBackEnd<Impl>::setThreadState(OzoneThreadState<Impl> *thread_ptr) +{ + thread = thread_ptr; + thread->setFuncExeInst(0); +} + +#if FULL_SYSTEM +template <class Impl> +void +InorderBackEnd<Impl>::checkInterrupts() +{ + //Check if there are any outstanding interrupts + //Handle the interrupts + int ipl = 0; + int summary = 0; + + cpu->checkInterrupts = false; + + if (thread->readMiscReg(IPR_ASTRR)) + panic("asynchronous traps not implemented\n"); + + if (thread->readMiscReg(IPR_SIRR)) { + for (int i = INTLEVEL_SOFTWARE_MIN; + i < INTLEVEL_SOFTWARE_MAX; i++) { + if (thread->readMiscReg(IPR_SIRR) & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; + summary |= (ULL(1) << i); + } + } + } + + uint64_t interrupts = cpu->intr_status(); + + if (interrupts) { + for (int i = INTLEVEL_EXTERNAL_MIN; + i < INTLEVEL_EXTERNAL_MAX; i++) { + if (interrupts & (ULL(1) << i)) { + // See table 4-19 of the 21164 hardware reference + ipl = i; + summary |= (ULL(1) << i); + } + } + } + + if (ipl && ipl > thread->readMiscReg(IPR_IPLR)) { + thread->inSyscall = true; + + thread->setMiscReg(IPR_ISR, summary); + thread->setMiscReg(IPR_INTID, ipl); + Fault(new InterruptFault)->invoke(xc); + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + thread->readMiscReg(IPR_IPLR), ipl, summary); + + // May need to go 1 inst prior + squashPending = true; + + thread->inSyscall = false; + + setSquashInfoFromXC(); + } +} +#endif + +template <class Impl> +void +InorderBackEnd<Impl>::tick() +{ + // Squash due to an external source + // Not sure if this or an interrupt has higher priority + if (squashPending) { + squash(squashSeqNum, squashNextPC); + return; + } + + // if (interrupt) then set thread PC, stall front end, record that + // I'm waiting for it to drain. (for now just squash) +#if FULL_SYSTEM + if (interruptBlocked || + (cpu->checkInterrupts && + cpu->check_interrupts() && + !cpu->inPalMode())) { + if (!robEmpty()) { + interruptBlocked = true; + } else if (robEmpty() && cpu->inPalMode()) { + // Will need to let the front end continue a bit until + // we're out of pal mode. Hopefully we never get into an + // infinite loop... + interruptBlocked = false; + } else { + interruptBlocked = false; + checkInterrupts(); + return; + } + } +#endif + + if (status != DcacheMissLoadStall && + status != DcacheMissStoreStall) { + for (int i = 0; i < width && (*instsAdded) < width; ++i) { + DynInstPtr inst = frontEnd->getInst(); + + if (!inst) + break; + + instList.push_back(inst); + + (*instsAdded)++; + } + +#if FULL_SYSTEM + if (faultFromFetch && robEmpty() && frontEnd->isEmpty()) { + handleFault(); + } else { + executeInsts(); + } +#else + executeInsts(); +#endif + } +} + +template <class Impl> +void +InorderBackEnd<Impl>::executeInsts() +{ + bool completed_last_inst = true; + int insts_to_execute = *instsToExecute; + int freed_regs = 0; + + while (insts_to_execute > 0) { + assert(!instList.empty()); + DynInstPtr inst = instList.front(); + + commitPC = inst->readPC(); + + thread->setPC(commitPC); + thread->setNextPC(inst->readNextPC()); + +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + if (count == 0) + assert(!thread->inSyscall && !thread->trapPending); + oldpc = thread->readPC(); + cpu->system->pcEventQueue.service( + thread->getXCProxy()); + count++; + } while (oldpc != thread->readPC()); + if (count > 1) { + DPRINTF(IBE, "PC skip function event, stopping commit\n"); + completed_last_inst = false; + squashPending = true; + break; + } +#endif + + Fault inst_fault = NoFault; + + if (status == DcacheMissComplete) { + DPRINTF(IBE, "Completing inst [sn:%lli]\n", inst->seqNum); + status = Running; + } else if (inst->isMemRef() && status != DcacheMissComplete && + (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { + DPRINTF(IBE, "Initiating mem op inst [sn:%lli] PC: %#x\n", + inst->seqNum, inst->readPC()); + + cacheCompletionEvent.inst = inst; + inst_fault = inst->initiateAcc(); + if (inst_fault == NoFault && + status != DcacheMissLoadStall && + status != DcacheMissStoreStall) { + inst_fault = inst->completeAcc(); + } + ++thread->funcExeInst; + } else { + DPRINTF(IBE, "Executing inst [sn:%lli] PC: %#x\n", + inst->seqNum, inst->readPC()); + inst_fault = inst->execute(); + ++thread->funcExeInst; + } + + // Will need to be able to break this loop in case the load + // misses. Split access/complete ops would be useful here + // with writeback events. + if (status == DcacheMissLoadStall) { + *instsToExecute = insts_to_execute; + + completed_last_inst = false; + break; + } else if (status == DcacheMissStoreStall) { + // Figure out how to fix this hack. Probably have DcacheMissLoad + // vs DcacheMissStore. + *instsToExecute = insts_to_execute; + completed_last_inst = false; +/* + instList.pop_front(); + --insts_to_execute; + if (inst->traceData) { + inst->traceData->finalize(); + } +*/ + + // Don't really need to stop for a store stall as long as + // the memory system is able to handle store forwarding + // and such. Breaking out might help avoid the cache + // interface becoming blocked. + break; + } + + inst->setExecuted(); + inst->setResultReady(); + inst->setCanCommit(); + + instList.pop_front(); + + --insts_to_execute; + --(*instsToExecute); + + if (inst->traceData) { + inst->traceData->finalize(); + inst->traceData = NULL; + } + + if (inst_fault != NoFault) { +#if FULL_SYSTEM + DPRINTF(IBE, "Inst [sn:%lli] PC %#x has a fault\n", + inst->seqNum, inst->readPC()); + + assert(!thread->inSyscall); + + thread->inSyscall = true; + + // Hack for now; DTB will sometimes need the machine instruction + // for when faults happen. So we will set it here, prior to the + // DTB possibly needing it for this translation. + thread->setInst( + static_cast<TheISA::MachInst>(inst->staticInst->machInst)); + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + inst_fault->invoke(xc); + + // Exit state update mode to avoid accidental updating. + thread->inSyscall = false; + + squashPending = true; + + // Generate trap squash event. +// generateTrapEvent(tid); + completed_last_inst = false; + break; +#else // !FULL_SYSTEM + panic("fault (%d) detected @ PC %08p", inst_fault, + inst->PC); +#endif // FULL_SYSTEM + } + + for (int i = 0; i < inst->numDestRegs(); ++i) { + renameTable[inst->destRegIdx(i)] = inst; + thread->renameTable[inst->destRegIdx(i)] = inst; + ++freed_regs; + } + + inst->clearDependents(); + + comm->access(0)->doneSeqNum = inst->seqNum; + + if (inst->mispredicted()) { + squash(inst->seqNum, inst->readNextPC()); + + thread->setNextPC(inst->readNextPC()); + + break; + } else if (squashPending) { + // Something external happened that caused the CPU to squash. + // Break out of commit and handle the squash next cycle. + break; + } + // If it didn't mispredict, then it executed fine. Send back its + // registers and BP info? What about insts that may still have + // latency, like loads? Probably can send back the information after + // it is completed. + + // keep an instruction count + cpu->numInst++; + thread->numInsts++; + } + + frontEnd->addFreeRegs(freed_regs); + + assert(insts_to_execute >= 0); + + // Should only advance this if I have executed all instructions. + if (insts_to_execute == 0) { + numInstsToWB.advance(); + } + + // Should I set the PC to the next PC here? What do I set next PC to? + if (completed_last_inst) { + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readPC() + sizeof(MachInst)); + } + + if (squashPending) { + setSquashInfoFromXC(); + } +} + +template <class Impl> +void +InorderBackEnd<Impl>::handleFault() +{ + DPRINTF(Commit, "Handling fault from fetch\n"); + + assert(!thread->inSyscall); + + thread->inSyscall = true; + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + faultFromFetch->invoke(xc); + + // Exit state update mode to avoid accidental updating. + thread->inSyscall = false; + + squashPending = true; + + setSquashInfoFromXC(); +} + +template <class Impl> +void +InorderBackEnd<Impl>::squash(const InstSeqNum &squash_num, const Addr &next_PC) +{ + DPRINTF(IBE, "Squashing from [sn:%lli], setting PC to %#x\n", + squash_num, next_PC); + + InstListIt squash_it = --(instList.end()); + + int freed_regs = 0; + + while (!instList.empty() && (*squash_it)->seqNum > squash_num) { + DynInstPtr inst = *squash_it; + + DPRINTF(IBE, "Squashing instruction PC %#x, [sn:%lli].\n", + inst->readPC(), + inst->seqNum); + + // May cause problems with misc regs + freed_regs+= inst->numDestRegs(); + inst->clearDependents(); + squash_it--; + instList.pop_back(); + } + + frontEnd->addFreeRegs(freed_regs); + + for (int i = 0; i < latency+1; ++i) { + numInstsToWB.advance(); + } + + squashPending = false; + + // Probably want to make sure that this squash is the one that set the + // thread into inSyscall mode. + thread->inSyscall = false; + + // Tell front end to squash, reset PC to new one. + frontEnd->squash(squash_num, next_PC); + + faultFromFetch = NULL; +} + +template <class Impl> +void +InorderBackEnd<Impl>::squashFromXC() +{ + // Record that I need to squash + squashPending = true; + + thread->inSyscall = true; +} + +template <class Impl> +void +InorderBackEnd<Impl>::setSquashInfoFromXC() +{ + // Need to handle the case of the instList being empty. In that case + // probably any number works, except maybe with stores in the store buffer. + squashSeqNum = instList.empty() ? 0 : instList.front()->seqNum - 1; + + squashNextPC = thread->PC; +} + +template <class Impl> +void +InorderBackEnd<Impl>::fetchFault(Fault &fault) +{ + faultFromFetch = fault; +} + +template <class Impl> +void +InorderBackEnd<Impl>::dumpInsts() +{ + int num = 0; + int valid_num = 0; + + InstListIt inst_list_it = instList.begin(); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } +} + +template <class Impl> +InorderBackEnd<Impl>::DCacheCompletionEvent::DCacheCompletionEvent( + InorderBackEnd *_be) + : Event(&mainEventQueue, CPU_Tick_Pri), be(_be) +{ +// this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +InorderBackEnd<Impl>::DCacheCompletionEvent::process() +{ + inst->completeAcc(); + be->status = DcacheMissComplete; +} + +template <class Impl> +const char * +InorderBackEnd<Impl>::DCacheCompletionEvent::description() +{ + return "DCache completion event"; +} diff --git a/src/cpu/ozone/inst_queue.cc b/src/cpu/ozone/inst_queue.cc new file mode 100644 index 000000000..7ce5d67ad --- /dev/null +++ b/src/cpu/ozone/inst_queue.cc @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/simple_impl.hh" +#include "cpu/ozone/inst_queue_impl.hh" + +// Force instantiation of InstructionQueue. +template class InstQueue<SimpleImpl>; +template class InstQueue<OzoneImpl>; diff --git a/src/cpu/ozone/inst_queue.hh b/src/cpu/ozone/inst_queue.hh new file mode 100644 index 000000000..0158fd2d2 --- /dev/null +++ b/src/cpu/ozone/inst_queue.hh @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_INST_QUEUE_HH__ +#define __CPU_OZONE_INST_QUEUE_HH__ + +#include <list> +#include <map> +#include <queue> +#include <vector> + +#include "base/statistics.hh" +#include "base/timebuf.hh" +#include "cpu/inst_seq.hh" +#include "sim/host.hh" + +class FUPool; +class MemInterface; + +/** + * A standard instruction queue class. It holds ready instructions, in + * order, in seperate priority queues to facilitate the scheduling of + * instructions. The IQ uses a separate linked list to track dependencies. + * Similar to the rename map and the free list, it expects that + * floating point registers have their indices start after the integer + * registers (ie with 96 int and 96 fp registers, regs 0-95 are integer + * and 96-191 are fp). This remains true even for both logical and + * physical register indices. The IQ depends on the memory dependence unit to + * track when memory operations are ready in terms of ordering; register + * dependencies are tracked normally. Right now the IQ also handles the + * execution timing; this is mainly to allow back-to-back scheduling without + * requiring IEW to be able to peek into the IQ. At the end of the execution + * latency, the instruction is put into the queue to execute, where it will + * have the execute() function called on it. + * @todo: Make IQ able to handle multiple FU pools. + */ +template <class Impl> +class InstQueue +{ + public: + //Typedefs from the Impl. + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::Params Params; + typedef typename Impl::IssueStruct IssueStruct; +/* + typedef typename Impl::CPUPol::IEW IEW; + typedef typename Impl::CPUPol::MemDepUnit MemDepUnit; + typedef typename Impl::CPUPol::IssueStruct IssueStruct; + typedef typename Impl::CPUPol::TimeStruct TimeStruct; +*/ + // Typedef of iterator through the list of instructions. + typedef typename std::list<DynInstPtr>::iterator ListIt; + + friend class Impl::FullCPU; +#if 0 + /** FU completion event class. */ + class FUCompletion : public Event { + private: + /** Executing instruction. */ + DynInstPtr inst; + + /** Index of the FU used for executing. */ + int fuIdx; + + /** Pointer back to the instruction queue. */ + InstQueue<Impl> *iqPtr; + + public: + /** Construct a FU completion event. */ + FUCompletion(DynInstPtr &_inst, int fu_idx, + InstQueue<Impl> *iq_ptr); + + virtual void process(); + virtual const char *description(); + }; +#endif + /** Constructs an IQ. */ + InstQueue(Params *params); + + /** Destructs the IQ. */ + ~InstQueue(); + + /** Returns the name of the IQ. */ + std::string name() const; + + /** Registers statistics. */ + void regStats(); + + /** Sets CPU pointer. */ + void setCPU(FullCPU *_cpu) { cpu = _cpu; } +#if 0 + /** Sets active threads list. */ + void setActiveThreads(list<unsigned> *at_ptr); + + /** Sets the IEW pointer. */ + void setIEW(IEW *iew_ptr) { iewStage = iew_ptr; } +#endif + /** Sets the timer buffer between issue and execute. */ + void setIssueToExecuteQueue(TimeBuffer<IssueStruct> *i2eQueue); +#if 0 + /** Sets the global time buffer. */ + void setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr); + + /** Number of entries needed for given amount of threads. */ + int entryAmount(int num_threads); + + /** Resets max entries for all threads. */ + void resetEntries(); +#endif + /** Returns total number of free entries. */ + unsigned numFreeEntries(); + + /** Returns number of free entries for a thread. */ + unsigned numFreeEntries(unsigned tid); + + /** Returns whether or not the IQ is full. */ + bool isFull(); + + /** Returns whether or not the IQ is full for a specific thread. */ + bool isFull(unsigned tid); + + /** Returns if there are any ready instructions in the IQ. */ + bool hasReadyInsts(); + + /** Inserts a new instruction into the IQ. */ + void insert(DynInstPtr &new_inst); + + /** Inserts a new, non-speculative instruction into the IQ. */ + void insertNonSpec(DynInstPtr &new_inst); +#if 0 + /** + * Advances the tail of the IQ, used if an instruction is not added to the + * IQ for scheduling. + * @todo: Rename this function. + */ + void advanceTail(DynInstPtr &inst); + + /** Process FU completion event. */ + void processFUCompletion(DynInstPtr &inst, int fu_idx); +#endif + /** + * Schedules ready instructions, adding the ready ones (oldest first) to + * the queue to execute. + */ + void scheduleReadyInsts(); + + /** Schedules a single specific non-speculative instruction. */ + void scheduleNonSpec(const InstSeqNum &inst); + + /** + * Commits all instructions up to and including the given sequence number, + * for a specific thread. + */ + void commit(const InstSeqNum &inst, unsigned tid = 0); + + /** Wakes all dependents of a completed instruction. */ + void wakeDependents(DynInstPtr &completed_inst); + + /** Adds a ready memory instruction to the ready list. */ + void addReadyMemInst(DynInstPtr &ready_inst); +#if 0 + /** + * Reschedules a memory instruction. It will be ready to issue once + * replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &resched_inst); + + /** Replays a memory instruction. It must be rescheduled first. */ + void replayMemInst(DynInstPtr &replay_inst); +#endif + /** Completes a memory operation. */ + void completeMemInst(DynInstPtr &completed_inst); +#if 0 + /** Indicates an ordering violation between a store and a load. */ + void violation(DynInstPtr &store, DynInstPtr &faulting_load); +#endif + /** + * Squashes instructions for a thread. Squashing information is obtained + * from the time buffer. + */ + void squash(unsigned tid); // Probably want the ISN + + /** Returns the number of used entries for a thread. */ + unsigned getCount(unsigned tid) { return count[tid]; }; + + /** Updates the number of free entries. */ + void updateFreeEntries(int num) { freeEntries += num; } + + /** Debug function to print all instructions. */ + void printInsts(); + + private: + /** Does the actual squashing. */ + void doSquash(unsigned tid); + + ///////////////////////// + // Various pointers + ///////////////////////// + + /** Pointer to the CPU. */ + FullCPU *cpu; + + /** Cache interface. */ + MemInterface *dcacheInterface; +#if 0 + /** Pointer to IEW stage. */ + IEW *iewStage; + + /** The memory dependence unit, which tracks/predicts memory dependences + * between instructions. + */ + MemDepUnit memDepUnit[Impl::MaxThreads]; +#endif + /** The queue to the execute stage. Issued instructions will be written + * into it. + */ + TimeBuffer<IssueStruct> *issueToExecuteQueue; +#if 0 + /** The backwards time buffer. */ + TimeBuffer<TimeStruct> *timeBuffer; + + /** Wire to read information from timebuffer. */ + typename TimeBuffer<TimeStruct>::wire fromCommit; + + /** Function unit pool. */ + FUPool *fuPool; +#endif + ////////////////////////////////////// + // Instruction lists, ready queues, and ordering + ////////////////////////////////////// + + /** List of all the instructions in the IQ (some of which may be issued). */ + std::list<DynInstPtr> instList[Impl::MaxThreads]; + + /** + * Struct for comparing entries to be added to the priority queue. This + * gives reverse ordering to the instructions in terms of sequence + * numbers: the instructions with smaller sequence numbers (and hence + * are older) will be at the top of the priority queue. + */ + struct pqCompare { + bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const + { + return lhs->seqNum > rhs->seqNum; + } + }; + + /** + * Struct for an IQ entry. It includes the instruction and an iterator + * to the instruction's spot in the IQ. + */ + struct IQEntry { + DynInstPtr inst; + ListIt iqIt; + }; + + typedef std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> + ReadyInstQueue; + + typedef std::map<DynInstPtr, pqCompare> ReadyInstMap; + typedef typename std::map<DynInstPtr, pqCompare>::iterator ReadyMapIt; + + /** List of ready instructions. + */ + ReadyInstQueue readyInsts; + + /** List of non-speculative instructions that will be scheduled + * once the IQ gets a signal from commit. While it's redundant to + * have the key be a part of the value (the sequence number is stored + * inside of DynInst), when these instructions are woken up only + * the sequence number will be available. Thus it is most efficient to be + * able to search by the sequence number alone. + */ + std::map<InstSeqNum, DynInstPtr> nonSpecInsts; + + typedef typename std::map<InstSeqNum, DynInstPtr>::iterator NonSpecMapIt; +#if 0 + /** Entry for the list age ordering by op class. */ + struct ListOrderEntry { + OpClass queueType; + InstSeqNum oldestInst; + }; + + /** List that contains the age order of the oldest instruction of each + * ready queue. Used to select the oldest instruction available + * among op classes. + */ + std::list<ListOrderEntry> listOrder; + + typedef typename std::list<ListOrderEntry>::iterator ListOrderIt; + + /** Tracks if each ready queue is on the age order list. */ + bool queueOnList[Num_OpClasses]; + + /** Iterators of each ready queue. Points to their spot in the age order + * list. + */ + ListOrderIt readyIt[Num_OpClasses]; + + /** Add an op class to the age order list. */ + void addToOrderList(OpClass op_class); + + /** + * Called when the oldest instruction has been removed from a ready queue; + * this places that ready queue into the proper spot in the age order list. + */ + void moveToYoungerInst(ListOrderIt age_order_it); +#endif + ////////////////////////////////////// + // Various parameters + ////////////////////////////////////// +#if 0 + /** IQ Resource Sharing Policy */ + enum IQPolicy { + Dynamic, + Partitioned, + Threshold + }; + + /** IQ sharing policy for SMT. */ + IQPolicy iqPolicy; +#endif + /** Number of Total Threads*/ + unsigned numThreads; +#if 0 + /** Pointer to list of active threads. */ + list<unsigned> *activeThreads; +#endif + /** Per Thread IQ count */ + unsigned count[Impl::MaxThreads]; + + /** Max IQ Entries Per Thread */ + unsigned maxEntries[Impl::MaxThreads]; + + /** Number of free IQ entries left. */ + unsigned freeEntries; + + /** The number of entries in the instruction queue. */ + unsigned numEntries; + + /** The total number of instructions that can be issued in one cycle. */ + unsigned totalWidth; +#if 0 + /** The number of physical registers in the CPU. */ + unsigned numPhysRegs; + + /** The number of physical integer registers in the CPU. */ + unsigned numPhysIntRegs; + + /** The number of floating point registers in the CPU. */ + unsigned numPhysFloatRegs; +#endif + /** Delay between commit stage and the IQ. + * @todo: Make there be a distinction between the delays within IEW. + */ + unsigned commitToIEWDelay; + + ////////////////////////////////// + // Variables needed for squashing + ////////////////////////////////// + + /** The sequence number of the squashed instruction. */ + InstSeqNum squashedSeqNum[Impl::MaxThreads]; + + /** Iterator that points to the last instruction that has been squashed. + * This will not be valid unless the IQ is in the process of squashing. + */ + ListIt squashIt[Impl::MaxThreads]; +#if 0 + /////////////////////////////////// + // Dependency graph stuff + /////////////////////////////////// + + class DependencyEntry + { + public: + DependencyEntry() + : inst(NULL), next(NULL) + { } + + DynInstPtr inst; + //Might want to include data about what arch. register the + //dependence is waiting on. + DependencyEntry *next; + + //This function, and perhaps this whole class, stand out a little + //bit as they don't fit a classification well. I want access + //to the underlying structure of the linked list, yet at + //the same time it feels like this should be something abstracted + //away. So for now it will sit here, within the IQ, until + //a better implementation is decided upon. + // This function probably shouldn't be within the entry... + void insert(DynInstPtr &new_inst); + + void remove(DynInstPtr &inst_to_remove); + + // Debug variable, remove when done testing. + static unsigned mem_alloc_counter; + }; + + /** Array of linked lists. Each linked list is a list of all the + * instructions that depend upon a given register. The actual + * register's index is used to index into the graph; ie all + * instructions in flight that are dependent upon r34 will be + * in the linked list of dependGraph[34]. + */ + DependencyEntry *dependGraph; + + /** A cache of the recently woken registers. It is 1 if the register + * has been woken up recently, and 0 if the register has been added + * to the dependency graph and has not yet received its value. It + * is basically a secondary scoreboard, and should pretty much mirror + * the scoreboard that exists in the rename map. + */ + vector<bool> regScoreboard; + + /** Adds an instruction to the dependency graph, as a producer. */ + bool addToDependents(DynInstPtr &new_inst); + + /** Adds an instruction to the dependency graph, as a consumer. */ + void createDependency(DynInstPtr &new_inst); +#endif + /** Moves an instruction to the ready queue if it is ready. */ + void addIfReady(DynInstPtr &inst); + + /** Debugging function to count how many entries are in the IQ. It does + * a linear walk through the instructions, so do not call this function + * during normal execution. + */ + int countInsts(); +#if 0 + /** Debugging function to dump out the dependency graph. + */ + void dumpDependGraph(); +#endif + /** Debugging function to dump all the list sizes, as well as print + * out the list of nonspeculative instructions. Should not be used + * in any other capacity, but it has no harmful sideaffects. + */ + void dumpLists(); + + /** Debugging function to dump out all instructions that are in the + * IQ. + */ + void dumpInsts(); + + /** Stat for number of instructions added. */ + Stats::Scalar<> iqInstsAdded; + /** Stat for number of non-speculative instructions added. */ + Stats::Scalar<> iqNonSpecInstsAdded; +// Stats::Scalar<> iqIntInstsAdded; + /** Stat for number of integer instructions issued. */ + Stats::Scalar<> iqIntInstsIssued; +// Stats::Scalar<> iqFloatInstsAdded; + /** Stat for number of floating point instructions issued. */ + Stats::Scalar<> iqFloatInstsIssued; +// Stats::Scalar<> iqBranchInstsAdded; + /** Stat for number of branch instructions issued. */ + Stats::Scalar<> iqBranchInstsIssued; +// Stats::Scalar<> iqMemInstsAdded; + /** Stat for number of memory instructions issued. */ + Stats::Scalar<> iqMemInstsIssued; +// Stats::Scalar<> iqMiscInstsAdded; + /** Stat for number of miscellaneous instructions issued. */ + Stats::Scalar<> iqMiscInstsIssued; + /** Stat for number of squashed instructions that were ready to issue. */ + Stats::Scalar<> iqSquashedInstsIssued; + /** Stat for number of squashed instructions examined when squashing. */ + Stats::Scalar<> iqSquashedInstsExamined; + /** Stat for number of squashed instruction operands examined when + * squashing. + */ + Stats::Scalar<> iqSquashedOperandsExamined; + /** Stat for number of non-speculative instructions removed due to a squash. + */ + Stats::Scalar<> iqSquashedNonSpecRemoved; + +}; + +#endif //__CPU_OZONE_INST_QUEUE_HH__ diff --git a/src/cpu/ozone/inst_queue_impl.hh b/src/cpu/ozone/inst_queue_impl.hh new file mode 100644 index 000000000..32a940241 --- /dev/null +++ b/src/cpu/ozone/inst_queue_impl.hh @@ -0,0 +1,1343 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +// Todo: +// Current ordering allows for 0 cycle added-to-scheduled. Could maybe fake +// it; either do in reverse order, or have added instructions put into a +// different ready queue that, in scheduleRreadyInsts(), gets put onto the +// normal ready queue. This would however give only a one cycle delay, +// but probably is more flexible to actually add in a delay parameter than +// just running it backwards. + +#include <vector> + +#include "sim/root.hh" + +#include "cpu/ozone/inst_queue.hh" +#if 0 +template <class Impl> +InstQueue<Impl>::FUCompletion::FUCompletion(DynInstPtr &_inst, + int fu_idx, + InstQueue<Impl> *iq_ptr) + : Event(&mainEventQueue, Stat_Event_Pri), + inst(_inst), fuIdx(fu_idx), iqPtr(iq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +InstQueue<Impl>::FUCompletion::process() +{ + iqPtr->processFUCompletion(inst, fuIdx); +} + + +template <class Impl> +const char * +InstQueue<Impl>::FUCompletion::description() +{ + return "Functional unit completion event"; +} +#endif +template <class Impl> +InstQueue<Impl>::InstQueue(Params *params) + : dcacheInterface(params->dcacheInterface), +// fuPool(params->fuPool), + numEntries(params->numIQEntries), + totalWidth(params->issueWidth), +// numPhysIntRegs(params->numPhysIntRegs), +// numPhysFloatRegs(params->numPhysFloatRegs), + commitToIEWDelay(params->commitToIEWDelay) +{ +// assert(fuPool); + +// numThreads = params->numberOfThreads; + numThreads = 1; + + //Initialize thread IQ counts + for (int i = 0; i <numThreads; i++) { + count[i] = 0; + } + + // Initialize the number of free IQ entries. + freeEntries = numEntries; + + // Set the number of physical registers as the number of int + float +// numPhysRegs = numPhysIntRegs + numPhysFloatRegs; + +// DPRINTF(IQ, "There are %i physical registers.\n", numPhysRegs); + + //Create an entry for each physical register within the + //dependency graph. +// dependGraph = new DependencyEntry[numPhysRegs]; + + // Resize the register scoreboard. +// regScoreboard.resize(numPhysRegs); +/* + //Initialize Mem Dependence Units + for (int i = 0; i < numThreads; i++) { + memDepUnit[i].init(params,i); + memDepUnit[i].setIQ(this); + } + + // Initialize all the head pointers to point to NULL, and all the + // entries as unready. + // Note that in actuality, the registers corresponding to the logical + // registers start off as ready. However this doesn't matter for the + // IQ as the instruction should have been correctly told if those + // registers are ready in rename. Thus it can all be initialized as + // unready. + for (int i = 0; i < numPhysRegs; ++i) { + dependGraph[i].next = NULL; + dependGraph[i].inst = NULL; + regScoreboard[i] = false; + } +*/ + for (int i = 0; i < numThreads; ++i) { + squashedSeqNum[i] = 0; + } +/* + for (int i = 0; i < Num_OpClasses; ++i) { + queueOnList[i] = false; + readyIt[i] = listOrder.end(); + } + + string policy = params->smtIQPolicy; + + //Convert string to lowercase + std::transform(policy.begin(), policy.end(), policy.begin(), + (int(*)(int)) tolower); + + //Figure out resource sharing policy + if (policy == "dynamic") { + iqPolicy = Dynamic; + + //Set Max Entries to Total ROB Capacity + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = numEntries; + } + + } else if (policy == "partitioned") { + iqPolicy = Partitioned; + + //@todo:make work if part_amt doesnt divide evenly. + int part_amt = numEntries / numThreads; + + //Divide ROB up evenly + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = part_amt; + } + + DPRINTF(Fetch, "IQ sharing policy set to Partitioned:" + "%i entries per thread.\n",part_amt); + + } else if (policy == "threshold") { + iqPolicy = Threshold; + + double threshold = (double)params->smtIQThreshold / 100; + + int thresholdIQ = (int)((double)threshold * numEntries); + + //Divide up by threshold amount + for (int i = 0; i < numThreads; i++) { + maxEntries[i] = thresholdIQ; + } + + DPRINTF(Fetch, "IQ sharing policy set to Threshold:" + "%i entries per thread.\n",thresholdIQ); + } else { + assert(0 && "Invalid IQ Sharing Policy.Options Are:{Dynamic," + "Partitioned, Threshold}"); + } +*/ +} + +template <class Impl> +InstQueue<Impl>::~InstQueue() +{ + // Clear the dependency graph +/* + DependencyEntry *curr; + DependencyEntry *prev; + + for (int i = 0; i < numPhysRegs; ++i) { + curr = dependGraph[i].next; + + while (curr) { + DependencyEntry::mem_alloc_counter--; + + prev = curr; + curr = prev->next; + prev->inst = NULL; + + delete prev; + } + + if (dependGraph[i].inst) { + dependGraph[i].inst = NULL; + } + + dependGraph[i].next = NULL; + } + + assert(DependencyEntry::mem_alloc_counter == 0); + + delete [] dependGraph; +*/ +} + +template <class Impl> +std::string +InstQueue<Impl>::name() const +{ + return cpu->name() + ".iq"; +} + +template <class Impl> +void +InstQueue<Impl>::regStats() +{ + iqInstsAdded + .name(name() + ".iqInstsAdded") + .desc("Number of instructions added to the IQ (excludes non-spec)") + .prereq(iqInstsAdded); + + iqNonSpecInstsAdded + .name(name() + ".iqNonSpecInstsAdded") + .desc("Number of non-speculative instructions added to the IQ") + .prereq(iqNonSpecInstsAdded); + +// iqIntInstsAdded; + + iqIntInstsIssued + .name(name() + ".iqIntInstsIssued") + .desc("Number of integer instructions issued") + .prereq(iqIntInstsIssued); + +// iqFloatInstsAdded; + + iqFloatInstsIssued + .name(name() + ".iqFloatInstsIssued") + .desc("Number of float instructions issued") + .prereq(iqFloatInstsIssued); + +// iqBranchInstsAdded; + + iqBranchInstsIssued + .name(name() + ".iqBranchInstsIssued") + .desc("Number of branch instructions issued") + .prereq(iqBranchInstsIssued); + +// iqMemInstsAdded; + + iqMemInstsIssued + .name(name() + ".iqMemInstsIssued") + .desc("Number of memory instructions issued") + .prereq(iqMemInstsIssued); + +// iqMiscInstsAdded; + + iqMiscInstsIssued + .name(name() + ".iqMiscInstsIssued") + .desc("Number of miscellaneous instructions issued") + .prereq(iqMiscInstsIssued); + + iqSquashedInstsIssued + .name(name() + ".iqSquashedInstsIssued") + .desc("Number of squashed instructions issued") + .prereq(iqSquashedInstsIssued); + + iqSquashedInstsExamined + .name(name() + ".iqSquashedInstsExamined") + .desc("Number of squashed instructions iterated over during squash;" + " mainly for profiling") + .prereq(iqSquashedInstsExamined); + + iqSquashedOperandsExamined + .name(name() + ".iqSquashedOperandsExamined") + .desc("Number of squashed operands that are examined and possibly " + "removed from graph") + .prereq(iqSquashedOperandsExamined); + + iqSquashedNonSpecRemoved + .name(name() + ".iqSquashedNonSpecRemoved") + .desc("Number of squashed non-spec instructions that were removed") + .prereq(iqSquashedNonSpecRemoved); +/* + for ( int i=0; i < numThreads; i++) { + // Tell mem dependence unit to reg stats as well. + memDepUnit[i].regStats(); + } +*/ +} +/* +template <class Impl> +void +InstQueue<Impl>::setActiveThreads(list<unsigned> *at_ptr) +{ + DPRINTF(IQ, "Setting active threads list pointer.\n"); + activeThreads = at_ptr; +} +*/ +template <class Impl> +void +InstQueue<Impl>::setIssueToExecuteQueue(TimeBuffer<IssueStruct> *i2e_ptr) +{ + DPRINTF(IQ, "Set the issue to execute queue.\n"); + issueToExecuteQueue = i2e_ptr; +} +/* +template <class Impl> +void +InstQueue<Impl>::setTimeBuffer(TimeBuffer<TimeStruct> *tb_ptr) +{ + DPRINTF(IQ, "Set the time buffer.\n"); + timeBuffer = tb_ptr; + + fromCommit = timeBuffer->getWire(-commitToIEWDelay); +} + +template <class Impl> +int +InstQueue<Impl>::entryAmount(int num_threads) +{ + if (iqPolicy == Partitioned) { + return numEntries / num_threads; + } else { + return 0; + } +} + + +template <class Impl> +void +InstQueue<Impl>::resetEntries() +{ + if (iqPolicy != Dynamic || numThreads > 1) { + int active_threads = (*activeThreads).size(); + + list<unsigned>::iterator threads = (*activeThreads).begin(); + list<unsigned>::iterator list_end = (*activeThreads).end(); + + while (threads != list_end) { + if (iqPolicy == Partitioned) { + maxEntries[*threads++] = numEntries / active_threads; + } else if(iqPolicy == Threshold && active_threads == 1) { + maxEntries[*threads++] = numEntries; + } + } + } +} +*/ +template <class Impl> +unsigned +InstQueue<Impl>::numFreeEntries() +{ + return freeEntries; +} + +template <class Impl> +unsigned +InstQueue<Impl>::numFreeEntries(unsigned tid) +{ + return maxEntries[tid] - count[tid]; +} + +// Might want to do something more complex if it knows how many instructions +// will be issued this cycle. +template <class Impl> +bool +InstQueue<Impl>::isFull() +{ + if (freeEntries == 0) { + return(true); + } else { + return(false); + } +} + +template <class Impl> +bool +InstQueue<Impl>::isFull(unsigned tid) +{ + if (numFreeEntries(tid) == 0) { + return(true); + } else { + return(false); + } +} + +template <class Impl> +bool +InstQueue<Impl>::hasReadyInsts() +{ +/* + if (!listOrder.empty()) { + return true; + } + + for (int i = 0; i < Num_OpClasses; ++i) { + if (!readyInsts[i].empty()) { + return true; + } + } + + return false; +*/ + return readyInsts.empty(); +} + +template <class Impl> +void +InstQueue<Impl>::insert(DynInstPtr &new_inst) +{ + // Make sure the instruction is valid + assert(new_inst); + + DPRINTF(IQ, "Adding instruction PC %#x to the IQ.\n", + new_inst->readPC()); + + // Check if there are any free entries. Panic if there are none. + // Might want to have this return a fault in the future instead of + // panicing. + assert(freeEntries != 0); + + instList[new_inst->threadNumber].push_back(new_inst); + + // Decrease the number of free entries. + --freeEntries; + + //Mark Instruction as in IQ +// new_inst->setInIQ(); +/* + // Look through its source registers (physical regs), and mark any + // dependencies. + addToDependents(new_inst); + + // Have this instruction set itself as the producer of its destination + // register(s). + createDependency(new_inst); +*/ + // If it's a memory instruction, add it to the memory dependency + // unit. +// if (new_inst->isMemRef()) { +// memDepUnit[new_inst->threadNumber].insert(new_inst); +// } else { + // If the instruction is ready then add it to the ready list. + addIfReady(new_inst); +// } + + ++iqInstsAdded; + + + //Update Thread IQ Count + count[new_inst->threadNumber]++; + + assert(freeEntries == (numEntries - countInsts())); +} + +template <class Impl> +void +InstQueue<Impl>::insertNonSpec(DynInstPtr &new_inst) +{ + nonSpecInsts[new_inst->seqNum] = new_inst; + + // @todo: Clean up this code; can do it by setting inst as unable + // to issue, then calling normal insert on the inst. + + // Make sure the instruction is valid + assert(new_inst); + + DPRINTF(IQ, "Adding instruction PC %#x to the IQ.\n", + new_inst->readPC()); + + // Check if there are any free entries. Panic if there are none. + // Might want to have this return a fault in the future instead of + // panicing. + assert(freeEntries != 0); + + instList[new_inst->threadNumber].push_back(new_inst); + + // Decrease the number of free entries. + --freeEntries; + + //Mark Instruction as in IQ +// new_inst->setInIQ(); +/* + // Have this instruction set itself as the producer of its destination + // register(s). + createDependency(new_inst); + + // If it's a memory instruction, add it to the memory dependency + // unit. + if (new_inst->isMemRef()) { + memDepUnit[new_inst->threadNumber].insertNonSpec(new_inst); + } +*/ + ++iqNonSpecInstsAdded; + + //Update Thread IQ Count + count[new_inst->threadNumber]++; + + assert(freeEntries == (numEntries - countInsts())); +} +/* +template <class Impl> +void +InstQueue<Impl>::advanceTail(DynInstPtr &inst) +{ + // Have this instruction set itself as the producer of its destination + // register(s). + createDependency(inst); +} + +template <class Impl> +void +InstQueue<Impl>::addToOrderList(OpClass op_class) +{ + assert(!readyInsts[op_class].empty()); + + ListOrderEntry queue_entry; + + queue_entry.queueType = op_class; + + queue_entry.oldestInst = readyInsts[op_class].top()->seqNum; + + ListOrderIt list_it = listOrder.begin(); + ListOrderIt list_end_it = listOrder.end(); + + while (list_it != list_end_it) { + if ((*list_it).oldestInst > queue_entry.oldestInst) { + break; + } + + list_it++; + } + + readyIt[op_class] = listOrder.insert(list_it, queue_entry); + queueOnList[op_class] = true; +} + +template <class Impl> +void +InstQueue<Impl>::moveToYoungerInst(ListOrderIt list_order_it) +{ + // Get iterator of next item on the list + // Delete the original iterator + // Determine if the next item is either the end of the list or younger + // than the new instruction. If so, then add in a new iterator right here. + // If not, then move along. + ListOrderEntry queue_entry; + OpClass op_class = (*list_order_it).queueType; + ListOrderIt next_it = list_order_it; + + ++next_it; + + queue_entry.queueType = op_class; + queue_entry.oldestInst = readyInsts[op_class].top()->seqNum; + + while (next_it != listOrder.end() && + (*next_it).oldestInst < queue_entry.oldestInst) { + ++next_it; + } + + readyIt[op_class] = listOrder.insert(next_it, queue_entry); +} + +template <class Impl> +void +InstQueue<Impl>::processFUCompletion(DynInstPtr &inst, int fu_idx) +{ + // The CPU could have been sleeping until this op completed (*extremely* + // long latency op). Wake it if it was. This may be overkill. + iewStage->wakeCPU(); + + fuPool->freeUnit(fu_idx); + + int &size = issueToExecuteQueue->access(0)->size; + + issueToExecuteQueue->access(0)->insts[size++] = inst; +} +*/ +// @todo: Figure out a better way to remove the squashed items from the +// lists. Checking the top item of each list to see if it's squashed +// wastes time and forces jumps. +template <class Impl> +void +InstQueue<Impl>::scheduleReadyInsts() +{ + DPRINTF(IQ, "Attempting to schedule ready instructions from " + "the IQ.\n"); + +// IssueStruct *i2e_info = issueToExecuteQueue->access(0); +/* + // Will need to reorder the list if either a queue is not on the list, + // or it has an older instruction than last time. + for (int i = 0; i < Num_OpClasses; ++i) { + if (!readyInsts[i].empty()) { + if (!queueOnList[i]) { + addToOrderList(OpClass(i)); + } else if (readyInsts[i].top()->seqNum < + (*readyIt[i]).oldestInst) { + listOrder.erase(readyIt[i]); + addToOrderList(OpClass(i)); + } + } + } + + // Have iterator to head of the list + // While I haven't exceeded bandwidth or reached the end of the list, + // Try to get a FU that can do what this op needs. + // If successful, change the oldestInst to the new top of the list, put + // the queue in the proper place in the list. + // Increment the iterator. + // This will avoid trying to schedule a certain op class if there are no + // FUs that handle it. + ListOrderIt order_it = listOrder.begin(); + ListOrderIt order_end_it = listOrder.end(); + int total_issued = 0; + int exec_queue_slot = i2e_info->size; + + while (exec_queue_slot < totalWidth && order_it != order_end_it) { + OpClass op_class = (*order_it).queueType; + + assert(!readyInsts[op_class].empty()); + + DynInstPtr issuing_inst = readyInsts[op_class].top(); + + assert(issuing_inst->seqNum == (*order_it).oldestInst); + + if (issuing_inst->isSquashed()) { + readyInsts[op_class].pop(); + + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; + } + + listOrder.erase(order_it++); + + ++iqSquashedInstsIssued; + + continue; + } + + int idx = fuPool->getUnit(op_class); + + if (idx != -1) { + int op_latency = fuPool->getOpLatency(op_class); + + if (op_latency == 1) { + i2e_info->insts[exec_queue_slot++] = issuing_inst; + i2e_info->size++; + + // Add the FU onto the list of FU's to be freed next cycle. + fuPool->freeUnit(idx); + } else { + int issue_latency = fuPool->getIssueLatency(op_class); + + if (issue_latency > 1) { + // Generate completion event for the FU + FUCompletion *execution = new FUCompletion(issuing_inst, + idx, this); + + execution->schedule(curTick + issue_latency - 1); + } else { + i2e_info->insts[exec_queue_slot++] = issuing_inst; + i2e_info->size++; + + // Add the FU onto the list of FU's to be freed next cycle. + fuPool->freeUnit(idx); + } + } + + DPRINTF(IQ, "Thread %i: Issuing instruction PC %#x " + "[sn:%lli]\n", + issuing_inst->threadNumber, issuing_inst->readPC(), + issuing_inst->seqNum); + + readyInsts[op_class].pop(); + + if (!readyInsts[op_class].empty()) { + moveToYoungerInst(order_it); + } else { + readyIt[op_class] = listOrder.end(); + queueOnList[op_class] = false; + } + + issuing_inst->setIssued(); + ++total_issued; + + if (!issuing_inst->isMemRef()) { + // Memory instructions can not be freed from the IQ until they + // complete. + ++freeEntries; + count[issuing_inst->threadNumber]--; + issuing_inst->removeInIQ(); + } else { + memDepUnit[issuing_inst->threadNumber].issue(issuing_inst); + } + + listOrder.erase(order_it++); + } else { + ++order_it; + } + } + + if (total_issued) { + cpu->activityThisCycle(); + } else { + DPRINTF(IQ, "Not able to schedule any instructions.\n"); + } +*/ +} + +template <class Impl> +void +InstQueue<Impl>::scheduleNonSpec(const InstSeqNum &inst) +{ + DPRINTF(IQ, "Marking nonspeculative instruction with sequence " + "number %i as ready to execute.\n", inst); + + NonSpecMapIt inst_it = nonSpecInsts.find(inst); + + assert(inst_it != nonSpecInsts.end()); + +// unsigned tid = (*inst_it).second->threadNumber; + + // Mark this instruction as ready to issue. + (*inst_it).second->setCanIssue(); + + // Now schedule the instruction. +// if (!(*inst_it).second->isMemRef()) { + addIfReady((*inst_it).second); +// } else { +// memDepUnit[tid].nonSpecInstReady((*inst_it).second); +// } + + nonSpecInsts.erase(inst_it); +} + +template <class Impl> +void +InstQueue<Impl>::commit(const InstSeqNum &inst, unsigned tid) +{ + /*Need to go through each thread??*/ + DPRINTF(IQ, "[tid:%i]: Committing instructions older than [sn:%i]\n", + tid,inst); + + ListIt iq_it = instList[tid].begin(); + + while (iq_it != instList[tid].end() && + (*iq_it)->seqNum <= inst) { + ++iq_it; + instList[tid].pop_front(); + } + + assert(freeEntries == (numEntries - countInsts())); +} + +template <class Impl> +void +InstQueue<Impl>::wakeDependents(DynInstPtr &completed_inst) +{ + DPRINTF(IQ, "Waking dependents of completed instruction.\n"); + // Look at the physical destination register of the DynInst + // and look it up on the dependency graph. Then mark as ready + // any instructions within the instruction queue. +/* + DependencyEntry *curr; + DependencyEntry *prev; +*/ + // Tell the memory dependence unit to wake any dependents on this + // instruction if it is a memory instruction. Also complete the memory + // instruction at this point since we know it executed fine. + // @todo: Might want to rename "completeMemInst" to + // something that indicates that it won't need to be replayed, and call + // this earlier. Might not be a big deal. + if (completed_inst->isMemRef()) { +// memDepUnit[completed_inst->threadNumber].wakeDependents(completed_inst); + completeMemInst(completed_inst); + } + completed_inst->wakeDependents(); +/* + for (int dest_reg_idx = 0; + dest_reg_idx < completed_inst->numDestRegs(); + dest_reg_idx++) + { + PhysRegIndex dest_reg = + completed_inst->renamedDestRegIdx(dest_reg_idx); + + // Special case of uniq or control registers. They are not + // handled by the IQ and thus have no dependency graph entry. + // @todo Figure out a cleaner way to handle this. + if (dest_reg >= numPhysRegs) { + continue; + } + + DPRINTF(IQ, "Waking any dependents on register %i.\n", + (int) dest_reg); + + //Maybe abstract this part into a function. + //Go through the dependency chain, marking the registers as ready + //within the waiting instructions. + + curr = dependGraph[dest_reg].next; + + while (curr) { + DPRINTF(IQ, "Waking up a dependent instruction, PC%#x.\n", + curr->inst->readPC()); + + // Might want to give more information to the instruction + // so that it knows which of its source registers is ready. + // However that would mean that the dependency graph entries + // would need to hold the src_reg_idx. + curr->inst->markSrcRegReady(); + + addIfReady(curr->inst); + + DependencyEntry::mem_alloc_counter--; + + prev = curr; + curr = prev->next; + prev->inst = NULL; + + delete prev; + } + + // Reset the head node now that all of its dependents have been woken + // up. + dependGraph[dest_reg].next = NULL; + dependGraph[dest_reg].inst = NULL; + + // Mark the scoreboard as having that register ready. + regScoreboard[dest_reg] = true; + } +*/ +} + +template <class Impl> +void +InstQueue<Impl>::addReadyMemInst(DynInstPtr &ready_inst) +{ +// OpClass op_class = ready_inst->opClass(); + + readyInsts.push(ready_inst); + + DPRINTF(IQ, "Instruction is ready to issue, putting it onto " + "the ready list, PC %#x opclass:%i [sn:%lli].\n", + ready_inst->readPC(), ready_inst->opClass(), ready_inst->seqNum); +} +/* +template <class Impl> +void +InstQueue<Impl>::rescheduleMemInst(DynInstPtr &resched_inst) +{ + memDepUnit[resched_inst->threadNumber].reschedule(resched_inst); +} + +template <class Impl> +void +InstQueue<Impl>::replayMemInst(DynInstPtr &replay_inst) +{ + memDepUnit[replay_inst->threadNumber].replay(replay_inst); +} +*/ +template <class Impl> +void +InstQueue<Impl>::completeMemInst(DynInstPtr &completed_inst) +{ + int tid = completed_inst->threadNumber; + + DPRINTF(IQ, "Completing mem instruction PC:%#x [sn:%lli]\n", + completed_inst->readPC(), completed_inst->seqNum); + + ++freeEntries; + +// completed_inst->memOpDone = true; + +// memDepUnit[tid].completed(completed_inst); + + count[tid]--; +} +/* +template <class Impl> +void +InstQueue<Impl>::violation(DynInstPtr &store, + DynInstPtr &faulting_load) +{ + memDepUnit[store->threadNumber].violation(store, faulting_load); +} +*/ +template <class Impl> +void +InstQueue<Impl>::squash(unsigned tid) +{ + DPRINTF(IQ, "[tid:%i]: Starting to squash instructions in " + "the IQ.\n", tid); + + // Read instruction sequence number of last instruction out of the + // time buffer. +// squashedSeqNum[tid] = fromCommit->commitInfo[tid].doneSeqNum; + + // Setup the squash iterator to point to the tail. + squashIt[tid] = instList[tid].end(); + --squashIt[tid]; + + // Call doSquash if there are insts in the IQ + if (count[tid] > 0) { + doSquash(tid); + } + + // Also tell the memory dependence unit to squash. +// memDepUnit[tid].squash(squashedSeqNum[tid], tid); +} + +template <class Impl> +void +InstQueue<Impl>::doSquash(unsigned tid) +{ + // Make sure the squashed sequence number is valid. + assert(squashedSeqNum[tid] != 0); + + DPRINTF(IQ, "[tid:%i]: Squashing until sequence number %i!\n", + tid, squashedSeqNum[tid]); + + // Squash any instructions younger than the squashed sequence number + // given. + while (squashIt[tid] != instList[tid].end() && + (*squashIt[tid])->seqNum > squashedSeqNum[tid]) { + + DynInstPtr squashed_inst = (*squashIt[tid]); + + // Only handle the instruction if it actually is in the IQ and + // hasn't already been squashed in the IQ. + if (squashed_inst->threadNumber != tid || + squashed_inst->isSquashedInIQ()) { + --squashIt[tid]; + continue; + } + + if (!squashed_inst->isIssued() || + (squashed_inst->isMemRef()/* && + !squashed_inst->memOpDone*/)) { + + // Remove the instruction from the dependency list. + if (!squashed_inst->isNonSpeculative()) { +/* + for (int src_reg_idx = 0; + src_reg_idx < squashed_inst->numSrcRegs(); + src_reg_idx++) + { + PhysRegIndex src_reg = + squashed_inst->renamedSrcRegIdx(src_reg_idx); + + // Only remove it from the dependency graph if it was + // placed there in the first place. + // HACK: This assumes that instructions woken up from the + // dependency chain aren't informed that a specific src + // register has become ready. This may not always be true + // in the future. + // Instead of doing a linked list traversal, we can just + // remove these squashed instructions either at issue time, + // or when the register is overwritten. The only downside + // to this is it leaves more room for error. + + if (!squashed_inst->isReadySrcRegIdx(src_reg_idx) && + src_reg < numPhysRegs) { + dependGraph[src_reg].remove(squashed_inst); + } + + + ++iqSquashedOperandsExamined; + } +*/ + // Might want to remove producers as well. + } else { + nonSpecInsts[squashed_inst->seqNum] = NULL; + + nonSpecInsts.erase(squashed_inst->seqNum); + + ++iqSquashedNonSpecRemoved; + } + + // Might want to also clear out the head of the dependency graph. + + // Mark it as squashed within the IQ. + squashed_inst->setSquashedInIQ(); + + // @todo: Remove this hack where several statuses are set so the + // inst will flow through the rest of the pipeline. + squashed_inst->setIssued(); + squashed_inst->setCanCommit(); +// squashed_inst->removeInIQ(); + + //Update Thread IQ Count + count[squashed_inst->threadNumber]--; + + ++freeEntries; + + if (numThreads > 1) { + DPRINTF(IQ, "[tid:%i]: Instruction PC %#x squashed.\n", + tid, squashed_inst->readPC()); + } else { + DPRINTF(IQ, "Instruction PC %#x squashed.\n", + squashed_inst->readPC()); + } + } + + --squashIt[tid]; + ++iqSquashedInstsExamined; + } +} +/* +template <class Impl> +void +InstQueue<Impl>::DependencyEntry::insert(DynInstPtr &new_inst) +{ + //Add this new, dependent instruction at the head of the dependency + //chain. + + // First create the entry that will be added to the head of the + // dependency chain. + DependencyEntry *new_entry = new DependencyEntry; + new_entry->next = this->next; + new_entry->inst = new_inst; + + // Then actually add it to the chain. + this->next = new_entry; + + ++mem_alloc_counter; +} + +template <class Impl> +void +InstQueue<Impl>::DependencyEntry::remove(DynInstPtr &inst_to_remove) +{ + DependencyEntry *prev = this; + DependencyEntry *curr = this->next; + + // Make sure curr isn't NULL. Because this instruction is being + // removed from a dependency list, it must have been placed there at + // an earlier time. The dependency chain should not be empty, + // unless the instruction dependent upon it is already ready. + if (curr == NULL) { + return; + } + + // Find the instruction to remove within the dependency linked list. + while (curr->inst != inst_to_remove) { + prev = curr; + curr = curr->next; + + assert(curr != NULL); + } + + // Now remove this instruction from the list. + prev->next = curr->next; + + --mem_alloc_counter; + + // Could push this off to the destructor of DependencyEntry + curr->inst = NULL; + + delete curr; +} + +template <class Impl> +bool +InstQueue<Impl>::addToDependents(DynInstPtr &new_inst) +{ + // Loop through the instruction's source registers, adding + // them to the dependency list if they are not ready. + int8_t total_src_regs = new_inst->numSrcRegs(); + bool return_val = false; + + for (int src_reg_idx = 0; + src_reg_idx < total_src_regs; + src_reg_idx++) + { + // Only add it to the dependency graph if it's not ready. + if (!new_inst->isReadySrcRegIdx(src_reg_idx)) { + PhysRegIndex src_reg = new_inst->renamedSrcRegIdx(src_reg_idx); + + // Check the IQ's scoreboard to make sure the register + // hasn't become ready while the instruction was in flight + // between stages. Only if it really isn't ready should + // it be added to the dependency graph. + if (src_reg >= numPhysRegs) { + continue; + } else if (regScoreboard[src_reg] == false) { + DPRINTF(IQ, "Instruction PC %#x has src reg %i that " + "is being added to the dependency chain.\n", + new_inst->readPC(), src_reg); + + dependGraph[src_reg].insert(new_inst); + + // Change the return value to indicate that something + // was added to the dependency graph. + return_val = true; + } else { + DPRINTF(IQ, "Instruction PC %#x has src reg %i that " + "became ready before it reached the IQ.\n", + new_inst->readPC(), src_reg); + // Mark a register ready within the instruction. + new_inst->markSrcRegReady(); + } + } + } + + return return_val; +} + +template <class Impl> +void +InstQueue<Impl>::createDependency(DynInstPtr &new_inst) +{ + //Actually nothing really needs to be marked when an + //instruction becomes the producer of a register's value, + //but for convenience a ptr to the producing instruction will + //be placed in the head node of the dependency links. + int8_t total_dest_regs = new_inst->numDestRegs(); + + for (int dest_reg_idx = 0; + dest_reg_idx < total_dest_regs; + dest_reg_idx++) + { + PhysRegIndex dest_reg = new_inst->renamedDestRegIdx(dest_reg_idx); + + // Instructions that use the misc regs will have a reg number + // higher than the normal physical registers. In this case these + // registers are not renamed, and there is no need to track + // dependencies as these instructions must be executed at commit. + if (dest_reg >= numPhysRegs) { + continue; + } + + if (dependGraph[dest_reg].next) { + dumpDependGraph(); + panic("Dependency graph %i not empty!", dest_reg); + } + + dependGraph[dest_reg].inst = new_inst; + + // Mark the scoreboard to say it's not yet ready. + regScoreboard[dest_reg] = false; + } +} +*/ +template <class Impl> +void +InstQueue<Impl>::addIfReady(DynInstPtr &inst) +{ + //If the instruction now has all of its source registers + // available, then add it to the list of ready instructions. + if (inst->readyToIssue()) { + + //Add the instruction to the proper ready list. + if (inst->isMemRef()) { + + DPRINTF(IQ, "Checking if memory instruction can issue.\n"); + + // Message to the mem dependence unit that this instruction has + // its registers ready. + +// memDepUnit[inst->threadNumber].regsReady(inst); + + return; + } + +// OpClass op_class = inst->opClass(); + + DPRINTF(IQ, "Instruction is ready to issue, putting it onto " + "the ready list, PC %#x opclass:%i [sn:%lli].\n", + inst->readPC(), inst->opClass(), inst->seqNum); + + readyInsts.push(inst); + } +} + +template <class Impl> +int +InstQueue<Impl>::countInsts() +{ + //ksewell:This works but definitely could use a cleaner write + //with a more intuitive way of counting. Right now it's + //just brute force .... + +#if 0 + int total_insts = 0; + + for (int i = 0; i < numThreads; ++i) { + ListIt count_it = instList[i].begin(); + + while (count_it != instList[i].end()) { + if (!(*count_it)->isSquashed() && !(*count_it)->isSquashedInIQ()) { + if (!(*count_it)->isIssued()) { + ++total_insts; + } else if ((*count_it)->isMemRef() && + !(*count_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++total_insts; + } + } + + ++count_it; + } + } + + return total_insts; +#else + return numEntries - freeEntries; +#endif +} +/* +template <class Impl> +void +InstQueue<Impl>::dumpDependGraph() +{ + DependencyEntry *curr; + + for (int i = 0; i < numPhysRegs; ++i) + { + curr = &dependGraph[i]; + + if (curr->inst) { + cprintf("dependGraph[%i]: producer: %#x [sn:%lli] consumer: ", + i, curr->inst->readPC(), curr->inst->seqNum); + } else { + cprintf("dependGraph[%i]: No producer. consumer: ", i); + } + + while (curr->next != NULL) { + curr = curr->next; + + cprintf("%#x [sn:%lli] ", + curr->inst->readPC(), curr->inst->seqNum); + } + + cprintf("\n"); + } +} +*/ +template <class Impl> +void +InstQueue<Impl>::dumpLists() +{ + for (int i = 0; i < Num_OpClasses; ++i) { + cprintf("Ready list %i size: %i\n", i, readyInsts.size()); + + cprintf("\n"); + } + + cprintf("Non speculative list size: %i\n", nonSpecInsts.size()); + + NonSpecMapIt non_spec_it = nonSpecInsts.begin(); + NonSpecMapIt non_spec_end_it = nonSpecInsts.end(); + + cprintf("Non speculative list: "); + + while (non_spec_it != non_spec_end_it) { + cprintf("%#x [sn:%lli]", (*non_spec_it).second->readPC(), + (*non_spec_it).second->seqNum); + ++non_spec_it; + } + + cprintf("\n"); +/* + ListOrderIt list_order_it = listOrder.begin(); + ListOrderIt list_order_end_it = listOrder.end(); + int i = 1; + + cprintf("List order: "); + + while (list_order_it != list_order_end_it) { + cprintf("%i OpClass:%i [sn:%lli] ", i, (*list_order_it).queueType, + (*list_order_it).oldestInst); + + ++list_order_it; + ++i; + } +*/ + cprintf("\n"); +} + + +template <class Impl> +void +InstQueue<Impl>::dumpInsts() +{ + for (int i = 0; i < numThreads; ++i) { +// int num = 0; +// int valid_num = 0; +/* + ListIt inst_list_it = instList[i].begin(); + + while (inst_list_it != instList[i].end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it++; + ++num; + } +*/ + } +} diff --git a/src/cpu/ozone/lsq_unit.cc b/src/cpu/ozone/lsq_unit.cc new file mode 100644 index 000000000..e37971dba --- /dev/null +++ b/src/cpu/ozone/lsq_unit.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/lsq_unit_impl.hh" + +// Force the instantiation of LDSTQ for all the implementations we care about. +template class OzoneLSQ<OzoneImpl>; + diff --git a/src/cpu/ozone/lsq_unit.hh b/src/cpu/ozone/lsq_unit.hh new file mode 100644 index 000000000..38c1c09a2 --- /dev/null +++ b/src/cpu/ozone/lsq_unit.hh @@ -0,0 +1,636 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_LSQ_UNIT_HH__ +#define __CPU_OZONE_LSQ_UNIT_HH__ + +#include <map> +#include <queue> +#include <algorithm> + +#include "arch/faults.hh" +#include "arch/types.hh" +#include "config/full_system.hh" +#include "base/hashmap.hh" +#include "cpu/inst_seq.hh" +#include "mem/mem_interface.hh" +//#include "mem/page_table.hh" +#include "sim/sim_object.hh" + +class PageTable; + +/** + * Class that implements the actual LQ and SQ for each specific thread. + * Both are circular queues; load entries are freed upon committing, while + * store entries are freed once they writeback. The LSQUnit tracks if there + * are memory ordering violations, and also detects partial load to store + * forwarding cases (a store only has part of a load's data) that requires + * the load to wait until the store writes back. In the former case it + * holds onto the instruction until the dependence unit looks at it, and + * in the latter it stalls the LSQ until the store writes back. At that + * point the load is replayed. + */ +template <class Impl> +class OzoneLSQ { + public: + typedef typename Impl::Params Params; + typedef typename Impl::FullCPU FullCPU; + typedef typename Impl::BackEnd BackEnd; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::IssueStruct IssueStruct; + + typedef TheISA::IntReg IntReg; + + typedef typename std::map<InstSeqNum, DynInstPtr>::iterator LdMapIt; + + private: + class StoreCompletionEvent : public Event { + public: + /** Constructs a store completion event. */ + StoreCompletionEvent(int store_idx, Event *wb_event, OzoneLSQ *lsq_ptr); + + /** Processes the store completion event. */ + void process(); + + /** Returns the description of this event. */ + const char *description(); + + private: + /** The store index of the store being written back. */ + int storeIdx; + /** The writeback event for the store. Needed for store + * conditionals. + */ + Event *wbEvent; + /** The pointer to the LSQ unit that issued the store. */ + OzoneLSQ<Impl> *lsqPtr; + }; + + friend class StoreCompletionEvent; + + public: + /** Constructs an LSQ unit. init() must be called prior to use. */ + OzoneLSQ(); + + /** Initializes the LSQ unit with the specified number of entries. */ + void init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id); + + /** Returns the name of the LSQ unit. */ + std::string name() const; + + /** Sets the CPU pointer. */ + void setCPU(FullCPU *cpu_ptr) + { cpu = cpu_ptr; } + + /** Sets the back-end stage pointer. */ + void setBE(BackEnd *be_ptr) + { be = be_ptr; } + + /** Ticks the LSQ unit, which in this case only resets the number of + * used cache ports. + * @todo: Move the number of used ports up to the LSQ level so it can + * be shared by all LSQ units. + */ + void tick() { usedPorts = 0; } + + /** Inserts an instruction. */ + void insert(DynInstPtr &inst); + /** Inserts a load instruction. */ + void insertLoad(DynInstPtr &load_inst); + /** Inserts a store instruction. */ + void insertStore(DynInstPtr &store_inst); + + /** Executes a load instruction. */ + Fault executeLoad(DynInstPtr &inst); + + Fault executeLoad(int lq_idx); + /** Executes a store instruction. */ + Fault executeStore(DynInstPtr &inst); + + /** Commits the head load. */ + void commitLoad(); + /** Commits a specific load, given by the sequence number. */ + void commitLoad(InstSeqNum &inst); + /** Commits loads older than a specific sequence number. */ + void commitLoads(InstSeqNum &youngest_inst); + + /** Commits stores older than a specific sequence number. */ + void commitStores(InstSeqNum &youngest_inst); + + /** Writes back stores. */ + void writebackStores(); + + // @todo: Include stats in the LSQ unit. + //void regStats(); + + /** Clears all the entries in the LQ. */ + void clearLQ(); + + /** Clears all the entries in the SQ. */ + void clearSQ(); + + /** Resizes the LQ to a given size. */ + void resizeLQ(unsigned size); + + /** Resizes the SQ to a given size. */ + void resizeSQ(unsigned size); + + /** Squashes all instructions younger than a specific sequence number. */ + void squash(const InstSeqNum &squashed_num); + + /** Returns if there is a memory ordering violation. Value is reset upon + * call to getMemDepViolator(). + */ + bool violation() { return memDepViolator; } + + /** Returns the memory ordering violator. */ + DynInstPtr getMemDepViolator(); + + /** Returns if a load became blocked due to the memory system. It clears + * the bool's value upon this being called. + */ + inline bool loadBlocked(); + + /** Returns the number of free entries (min of free LQ and SQ entries). */ + unsigned numFreeEntries(); + + /** Returns the number of loads ready to execute. */ + int numLoadsReady(); + + /** Returns the number of loads in the LQ. */ + int numLoads() { return loads; } + + /** Returns the number of stores in the SQ. */ + int numStores() { return stores; } + + /** Returns if either the LQ or SQ is full. */ + bool isFull() { return lqFull() || sqFull(); } + + /** Returns if the LQ is full. */ + bool lqFull() { return loads >= (LQEntries - 1); } + + /** Returns if the SQ is full. */ + bool sqFull() { return stores >= (SQEntries - 1); } + + /** Debugging function to dump instructions in the LSQ. */ + void dumpInsts(); + + /** Returns the number of instructions in the LSQ. */ + unsigned getCount() { return loads + stores; } + + /** Returns if there are any stores to writeback. */ + bool hasStoresToWB() { return storesToWB; } + + /** Returns the number of stores to writeback. */ + int numStoresToWB() { return storesToWB; } + + /** Returns if the LSQ unit will writeback on this cycle. */ + bool willWB() { return storeQueue[storeWBIdx].canWB && + !storeQueue[storeWBIdx].completed && + !dcacheInterface->isBlocked(); } + + private: + /** Completes the store at the specified index. */ + void completeStore(int store_idx); + + /** Increments the given store index (circular queue). */ + inline void incrStIdx(int &store_idx); + /** Decrements the given store index (circular queue). */ + inline void decrStIdx(int &store_idx); + /** Increments the given load index (circular queue). */ + inline void incrLdIdx(int &load_idx); + /** Decrements the given load index (circular queue). */ + inline void decrLdIdx(int &load_idx); + + private: + /** Pointer to the CPU. */ + FullCPU *cpu; + + /** Pointer to the back-end stage. */ + BackEnd *be; + + /** Pointer to the D-cache. */ + MemInterface *dcacheInterface; + + /** Pointer to the page table. */ + PageTable *pTable; + + public: + struct SQEntry { + /** Constructs an empty store queue entry. */ + SQEntry() + : inst(NULL), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0) + { } + + /** Constructs a store queue entry for a given instruction. */ + SQEntry(DynInstPtr &_inst) + : inst(_inst), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0) + { } + + /** The store instruction. */ + DynInstPtr inst; + /** The memory request for the store. */ + MemReqPtr req; + /** The size of the store. */ + int size; + /** The store data. */ + IntReg data; + /** Whether or not the store can writeback. */ + bool canWB; + /** Whether or not the store is committed. */ + bool committed; + /** Whether or not the store is completed. */ + bool completed; + }; + + enum Status { + Running, + Idle, + DcacheMissStall, + DcacheMissSwitch + }; + + private: + /** The OzoneLSQ thread id. */ + unsigned lsqID; + + /** The status of the LSQ unit. */ + Status _status; + + /** The store queue. */ + std::vector<SQEntry> storeQueue; + + /** The load queue. */ + std::vector<DynInstPtr> loadQueue; + + // Consider making these 16 bits + /** The number of LQ entries. */ + unsigned LQEntries; + /** The number of SQ entries. */ + unsigned SQEntries; + + /** The number of load instructions in the LQ. */ + int loads; + /** The number of store instructions in the SQ (excludes those waiting to + * writeback). + */ + int stores; + /** The number of store instructions in the SQ waiting to writeback. */ + int storesToWB; + + /** The index of the head instruction in the LQ. */ + int loadHead; + /** The index of the tail instruction in the LQ. */ + int loadTail; + + /** The index of the head instruction in the SQ. */ + int storeHead; + /** The index of the first instruction that is ready to be written back, + * and has not yet been written back. + */ + int storeWBIdx; + /** The index of the tail instruction in the SQ. */ + int storeTail; + + /// @todo Consider moving to a more advanced model with write vs read ports + /** The number of cache ports available each cycle. */ + int cachePorts; + + /** The number of used cache ports in this cycle. */ + int usedPorts; + + //list<InstSeqNum> mshrSeqNums; + + //Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; + + /** Wire to read information from the issue stage time queue. */ + typename TimeBuffer<IssueStruct>::wire fromIssue; + + // Make these per thread? + /** Whether or not the LSQ is stalled. */ + bool stalled; + /** The store that causes the stall due to partial store to load + * forwarding. + */ + InstSeqNum stallingStoreIsn; + /** The index of the above store. */ + int stallingLoadIdx; + + /** Whether or not a load is blocked due to the memory system. It is + * cleared when this value is checked via loadBlocked(). + */ + bool isLoadBlocked; + + /** The oldest faulting load instruction. */ + DynInstPtr loadFaultInst; + /** The oldest faulting store instruction. */ + DynInstPtr storeFaultInst; + + /** The oldest load that caused a memory ordering violation. */ + DynInstPtr memDepViolator; + + // Will also need how many read/write ports the Dcache has. Or keep track + // of that in stage that is one level up, and only call executeLoad/Store + // the appropriate number of times. + + public: + /** Executes the load at the given index. */ + template <class T> + Fault read(MemReqPtr &req, T &data, int load_idx); + + /** Executes the store at the given index. */ + template <class T> + Fault write(MemReqPtr &req, T &data, int store_idx); + + /** Returns the index of the head load instruction. */ + int getLoadHead() { return loadHead; } + /** Returns the sequence number of the head load instruction. */ + InstSeqNum getLoadHeadSeqNum() + { + if (loadQueue[loadHead]) { + return loadQueue[loadHead]->seqNum; + } else { + return 0; + } + + } + + /** Returns the index of the head store instruction. */ + int getStoreHead() { return storeHead; } + /** Returns the sequence number of the head store instruction. */ + InstSeqNum getStoreHeadSeqNum() + { + if (storeQueue[storeHead].inst) { + return storeQueue[storeHead].inst->seqNum; + } else { + return 0; + } + + } + + /** Returns whether or not the LSQ unit is stalled. */ + bool isStalled() { return stalled; } +}; + +template <class Impl> +template <class T> +Fault +OzoneLSQ<Impl>::read(MemReqPtr &req, T &data, int load_idx) +{ + //Depending on issue2execute delay a squashed load could + //execute if it is found to be squashed in the same + //cycle it is scheduled to execute + assert(loadQueue[load_idx]); + + if (loadQueue[load_idx]->isExecuted()) { + panic("Should not reach this point with split ops!"); + + memcpy(&data,req->data,req->size); + + return NoFault; + } + + // Make sure this isn't an uncacheable access + // A bit of a hackish way to get uncached accesses to work only if they're + // at the head of the LSQ and are ready to commit (at the head of the ROB + // too). + // @todo: Fix uncached accesses. + if (req->flags & UNCACHEABLE && + (load_idx != loadHead || !loadQueue[load_idx]->readyToCommit())) { + + return TheISA::genMachineCheckFault(); + } + + // Check the SQ for any previous stores that might lead to forwarding + int store_idx = loadQueue[load_idx]->sqIdx; + + int store_size = 0; + + DPRINTF(OzoneLSQ, "Read called, load idx: %i, store idx: %i, " + "storeHead: %i addr: %#x\n", + load_idx, store_idx, storeHead, req->paddr); + + while (store_idx != -1) { + // End once we've reached the top of the LSQ + if (store_idx == storeWBIdx) { + break; + } + + // Move the index to one younger + if (--store_idx < 0) + store_idx += SQEntries; + + assert(storeQueue[store_idx].inst); + + store_size = storeQueue[store_idx].size; + + if (store_size == 0) + continue; + + // Check if the store data is within the lower and upper bounds of + // addresses that the request needs. + bool store_has_lower_limit = + req->vaddr >= storeQueue[store_idx].inst->effAddr; + bool store_has_upper_limit = + (req->vaddr + req->size) <= (storeQueue[store_idx].inst->effAddr + + store_size); + bool lower_load_has_store_part = + req->vaddr < (storeQueue[store_idx].inst->effAddr + + store_size); + bool upper_load_has_store_part = + (req->vaddr + req->size) > storeQueue[store_idx].inst->effAddr; + + // If the store's data has all of the data needed, we can forward. + if (store_has_lower_limit && store_has_upper_limit) { + + int shift_amt = req->vaddr & (store_size - 1); + // Assumes byte addressing + shift_amt = shift_amt << 3; + + // Cast this to type T? + data = storeQueue[store_idx].data >> shift_amt; + + req->cmd = Read; + assert(!req->completionEvent); + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + + memcpy(req->data, &data, req->size); + + DPRINTF(OzoneLSQ, "Forwarding from store idx %i to load to " + "addr %#x, data %#x\n", + store_idx, req->vaddr, *(req->data)); + + typename BackEnd::LdWritebackEvent *wb = + new typename BackEnd::LdWritebackEvent(loadQueue[load_idx], + be); + + // We'll say this has a 1 cycle load-store forwarding latency + // for now. + // FIXME - Need to make this a parameter. + wb->schedule(curTick); + + // Should keep track of stat for forwarded data + return NoFault; + } else if ((store_has_lower_limit && lower_load_has_store_part) || + (store_has_upper_limit && upper_load_has_store_part) || + (lower_load_has_store_part && upper_load_has_store_part)) { + // This is the partial store-load forwarding case where a store + // has only part of the load's data. + + // If it's already been written back, then don't worry about + // stalling on it. + if (storeQueue[store_idx].completed) { + continue; + } + + // Must stall load and force it to retry, so long as it's the oldest + // load that needs to do so. + if (!stalled || + (stalled && + loadQueue[load_idx]->seqNum < + loadQueue[stallingLoadIdx]->seqNum)) { + stalled = true; + stallingStoreIsn = storeQueue[store_idx].inst->seqNum; + stallingLoadIdx = load_idx; + } + + // Tell IQ/mem dep unit that this instruction will need to be + // rescheduled eventually + be->rescheduleMemInst(loadQueue[load_idx]); + + DPRINTF(OzoneLSQ, "Load-store forwarding mis-match. " + "Store idx %i to load addr %#x\n", + store_idx, req->vaddr); + + return NoFault; + } + } + + + // If there's no forwarding case, then go access memory + DynInstPtr inst = loadQueue[load_idx]; + + ++usedPorts; + + // if we have a cache, do cache access too + if (dcacheInterface) { + if (dcacheInterface->isBlocked()) { + isLoadBlocked = true; + // No fault occurred, even though the interface is blocked. + return NoFault; + } + + DPRINTF(OzoneLSQ, "D-cache: PC:%#x reading from paddr:%#x " + "vaddr:%#x flags:%i\n", + inst->readPC(), req->paddr, req->vaddr, req->flags); + + // Setup MemReq pointer + req->cmd = Read; + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + + assert(!req->completionEvent); + typedef typename BackEnd::LdWritebackEvent LdWritebackEvent; + + LdWritebackEvent *wb = new LdWritebackEvent(loadQueue[load_idx], be); + + req->completionEvent = wb; + + // Do Cache Access + MemAccessResult result = dcacheInterface->access(req); + + // Ugly hack to get an event scheduled *only* if the access is + // a miss. We really should add first-class support for this + // at some point. + // @todo: Probably should support having no events + if (result != MA_HIT) { + DPRINTF(OzoneLSQ, "D-cache miss!\n"); + DPRINTF(Activity, "Activity: ld accessing mem miss [sn:%lli]\n", + inst->seqNum); + + lastDcacheStall = curTick; + + _status = DcacheMissStall; + + wb->setDcacheMiss(); + + } else { +// DPRINTF(Activity, "Activity: ld accessing mem hit [sn:%lli]\n", +// inst->seqNum); + + DPRINTF(OzoneLSQ, "D-cache hit!\n"); + } + } else { + fatal("Must use D-cache with new memory system"); + } + + return NoFault; +} + +template <class Impl> +template <class T> +Fault +OzoneLSQ<Impl>::write(MemReqPtr &req, T &data, int store_idx) +{ + assert(storeQueue[store_idx].inst); + + DPRINTF(OzoneLSQ, "Doing write to store idx %i, addr %#x data %#x" + " | storeHead:%i [sn:%i]\n", + store_idx, req->paddr, data, storeHead, + storeQueue[store_idx].inst->seqNum); + + storeQueue[store_idx].req = req; + storeQueue[store_idx].size = sizeof(T); + storeQueue[store_idx].data = data; + + // This function only writes the data to the store queue, so no fault + // can happen here. + return NoFault; +} + +template <class Impl> +inline bool +OzoneLSQ<Impl>::loadBlocked() +{ + bool ret_val = isLoadBlocked; + isLoadBlocked = false; + return ret_val; +} + +#endif // __CPU_OZONE_LSQ_UNIT_HH__ diff --git a/src/cpu/ozone/lsq_unit_impl.hh b/src/cpu/ozone/lsq_unit_impl.hh new file mode 100644 index 000000000..ee0804036 --- /dev/null +++ b/src/cpu/ozone/lsq_unit_impl.hh @@ -0,0 +1,840 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "arch/faults.hh" +#include "base/str.hh" +#include "cpu/ozone/lsq_unit.hh" + +template <class Impl> +OzoneLSQ<Impl>::StoreCompletionEvent::StoreCompletionEvent(int store_idx, + Event *wb_event, + OzoneLSQ<Impl> *lsq_ptr) + : Event(&mainEventQueue), + storeIdx(store_idx), + wbEvent(wb_event), + lsqPtr(lsq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +OzoneLSQ<Impl>::StoreCompletionEvent::process() +{ + DPRINTF(OzoneLSQ, "Cache miss complete for store idx:%i\n", storeIdx); + + //lsqPtr->removeMSHR(lsqPtr->storeQueue[storeIdx].inst->seqNum); + +// lsqPtr->cpu->wakeCPU(); + if (wbEvent) + wbEvent->process(); + lsqPtr->completeStore(storeIdx); +} + +template <class Impl> +const char * +OzoneLSQ<Impl>::StoreCompletionEvent::description() +{ + return "LSQ store completion event"; +} + +template <class Impl> +OzoneLSQ<Impl>::OzoneLSQ() + : loads(0), stores(0), storesToWB(0), stalled(false), isLoadBlocked(false) +{ +} + +template<class Impl> +void +OzoneLSQ<Impl>::init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id) + +{ + DPRINTF(OzoneLSQ, "Creating OzoneLSQ%i object.\n",id); + + lsqID = id; + + LQEntries = maxLQEntries; + SQEntries = maxSQEntries; + + loadQueue.resize(LQEntries); + storeQueue.resize(SQEntries); + + + // May want to initialize these entries to NULL + + loadHead = loadTail = 0; + + storeHead = storeWBIdx = storeTail = 0; + + usedPorts = 0; + cachePorts = params->cachePorts; + + dcacheInterface = params->dcacheInterface; + + loadFaultInst = storeFaultInst = memDepViolator = NULL; +} + +template<class Impl> +std::string +OzoneLSQ<Impl>::name() const +{ + return "lsqunit"; +} + +template<class Impl> +void +OzoneLSQ<Impl>::clearLQ() +{ + loadQueue.clear(); +} + +template<class Impl> +void +OzoneLSQ<Impl>::clearSQ() +{ + storeQueue.clear(); +} + +template<class Impl> +void +OzoneLSQ<Impl>::resizeLQ(unsigned size) +{ + assert( size >= LQEntries); + + if (size > LQEntries) { + while (size > loadQueue.size()) { + DynInstPtr dummy; + loadQueue.push_back(dummy); + LQEntries++; + } + } else { + LQEntries = size; + } + +} + +template<class Impl> +void +OzoneLSQ<Impl>::resizeSQ(unsigned size) +{ + if (size > SQEntries) { + while (size > storeQueue.size()) { + SQEntry dummy; + storeQueue.push_back(dummy); + SQEntries++; + } + } else { + SQEntries = size; + } +} + +template <class Impl> +void +OzoneLSQ<Impl>::insert(DynInstPtr &inst) +{ + // Make sure we really have a memory reference. + assert(inst->isMemRef()); + + // Make sure it's one of the two classes of memory references. + assert(inst->isLoad() || inst->isStore()); + + if (inst->isLoad()) { + insertLoad(inst); + } else { + insertStore(inst); + } + +// inst->setInLSQ(); +} + +template <class Impl> +void +OzoneLSQ<Impl>::insertLoad(DynInstPtr &load_inst) +{ + assert((loadTail + 1) % LQEntries != loadHead && loads < LQEntries); + + DPRINTF(OzoneLSQ, "Inserting load PC %#x, idx:%i [sn:%lli]\n", + load_inst->readPC(), loadTail, load_inst->seqNum); + + load_inst->lqIdx = loadTail; + + if (stores == 0) { + load_inst->sqIdx = -1; + } else { + load_inst->sqIdx = storeTail; + } + + loadQueue[loadTail] = load_inst; + + incrLdIdx(loadTail); + + ++loads; +} + +template <class Impl> +void +OzoneLSQ<Impl>::insertStore(DynInstPtr &store_inst) +{ + // Make sure it is not full before inserting an instruction. + assert((storeTail + 1) % SQEntries != storeHead); + assert(stores < SQEntries); + + DPRINTF(OzoneLSQ, "Inserting store PC %#x, idx:%i [sn:%lli]\n", + store_inst->readPC(), storeTail, store_inst->seqNum); + + store_inst->sqIdx = storeTail; + store_inst->lqIdx = loadTail; + + storeQueue[storeTail] = SQEntry(store_inst); + + incrStIdx(storeTail); + + ++stores; + +} + +template <class Impl> +typename Impl::DynInstPtr +OzoneLSQ<Impl>::getMemDepViolator() +{ + DynInstPtr temp = memDepViolator; + + memDepViolator = NULL; + + return temp; +} + +template <class Impl> +unsigned +OzoneLSQ<Impl>::numFreeEntries() +{ + unsigned free_lq_entries = LQEntries - loads; + unsigned free_sq_entries = SQEntries - stores; + + // Both the LQ and SQ entries have an extra dummy entry to differentiate + // empty/full conditions. Subtract 1 from the free entries. + if (free_lq_entries < free_sq_entries) { + return free_lq_entries - 1; + } else { + return free_sq_entries - 1; + } +} + +template <class Impl> +int +OzoneLSQ<Impl>::numLoadsReady() +{ + int load_idx = loadHead; + int retval = 0; + + while (load_idx != loadTail) { + assert(loadQueue[load_idx]); + + if (loadQueue[load_idx]->readyToIssue()) { + ++retval; + } + } + + return retval; +} + +#if 0 +template <class Impl> +Fault +OzoneLSQ<Impl>::executeLoad() +{ + Fault load_fault = NoFault; + DynInstPtr load_inst; + + assert(readyLoads.size() != 0); + + // Execute a ready load. + LdMapIt ready_it = readyLoads.begin(); + + load_inst = (*ready_it).second; + + // Execute the instruction, which is held in the data portion of the + // iterator. + load_fault = load_inst->execute(); + + // If it executed successfully, then switch it over to the executed + // loads list. + if (load_fault == NoFault) { + executedLoads[load_inst->seqNum] = load_inst; + + readyLoads.erase(ready_it); + } else { + loadFaultInst = load_inst; + } + + return load_fault; +} +#endif + +template <class Impl> +Fault +OzoneLSQ<Impl>::executeLoad(DynInstPtr &inst) +{ + // Execute a specific load. + Fault load_fault = NoFault; + + DPRINTF(OzoneLSQ, "Executing load PC %#x, [sn:%lli]\n", + inst->readPC(),inst->seqNum); + + // Make sure it's really in the list. + // Normally it should always be in the list. However, + /* due to a syscall it may not be the list. +#ifdef DEBUG + int i = loadHead; + while (1) { + if (i == loadTail && !find(inst)) { + assert(0 && "Load not in the queue!"); + } else if (loadQueue[i] == inst) { + break; + } + + i = i + 1; + if (i >= LQEntries) { + i = 0; + } + } +#endif // DEBUG*/ + + load_fault = inst->initiateAcc(); + + // Might want to make sure that I'm not overwriting a previously faulting + // instruction that hasn't been checked yet. + // Actually probably want the oldest faulting load + if (load_fault != NoFault) { + // Maybe just set it as can commit here, although that might cause + // some other problems with sending traps to the ROB too quickly. +// iewStage->instToCommit(inst); +// iewStage->activityThisCycle(); + } + + return load_fault; +} + +template <class Impl> +Fault +OzoneLSQ<Impl>::executeLoad(int lq_idx) +{ + // Very hackish. Not sure the best way to check that this + // instruction is at the head of the ROB. I should have some sort + // of extra information here so that I'm not overloading the + // canCommit signal for 15 different things. + loadQueue[lq_idx]->setCanCommit(); + Fault ret_fault = executeLoad(loadQueue[lq_idx]); + loadQueue[lq_idx]->clearCanCommit(); + return ret_fault; +} + +template <class Impl> +Fault +OzoneLSQ<Impl>::executeStore(DynInstPtr &store_inst) +{ + // Make sure that a store exists. + assert(stores != 0); + + int store_idx = store_inst->sqIdx; + + DPRINTF(OzoneLSQ, "Executing store PC %#x [sn:%lli]\n", + store_inst->readPC(), store_inst->seqNum); + + // Check the recently completed loads to see if any match this store's + // address. If so, then we have a memory ordering violation. + int load_idx = store_inst->lqIdx; + + Fault store_fault = store_inst->initiateAcc(); + + // Store size should now be available. Use it to get proper offset for + // addr comparisons. + int size = storeQueue[store_idx].size; + + if (size == 0) { + DPRINTF(OzoneLSQ,"Fault on Store PC %#x, [sn:%lli],Size = 0\n", + store_inst->readPC(),store_inst->seqNum); + + return store_fault; + } + + assert(store_fault == NoFault); + + if (!storeFaultInst) { + if (store_fault != NoFault) { + panic("Fault in a store instruction!"); + storeFaultInst = store_inst; + } else if (store_inst->isNonSpeculative()) { + // Nonspeculative accesses (namely store conditionals) + // need to set themselves as able to writeback if we + // haven't had a fault by here. + storeQueue[store_idx].canWB = true; + + ++storesToWB; + } + } + + if (!memDepViolator) { + while (load_idx != loadTail) { + // Actually should only check loads that have actually executed + // Might be safe because effAddr is set to InvalAddr when the + // dyn inst is created. + + // Must actually check all addrs in the proper size range + // Which is more correct than needs to be. What if for now we just + // assume all loads are quad-word loads, and do the addr based + // on that. + // @todo: Fix this, magic number being used here + if ((loadQueue[load_idx]->effAddr >> 8) == + (store_inst->effAddr >> 8)) { + // A load incorrectly passed this store. Squash and refetch. + // For now return a fault to show that it was unsuccessful. + memDepViolator = loadQueue[load_idx]; + + return TheISA::genMachineCheckFault(); + } + + incrLdIdx(load_idx); + } + + // If we've reached this point, there was no violation. + memDepViolator = NULL; + } + + return store_fault; +} + +template <class Impl> +void +OzoneLSQ<Impl>::commitLoad() +{ + assert(loadQueue[loadHead]); + + DPRINTF(OzoneLSQ, "[sn:%lli] Committing head load instruction, PC %#x\n", + loadQueue[loadHead]->seqNum, loadQueue[loadHead]->readPC()); + + + loadQueue[loadHead] = NULL; + + incrLdIdx(loadHead); + + --loads; +} + +template <class Impl> +void +OzoneLSQ<Impl>::commitLoad(InstSeqNum &inst) +{ + // Hopefully I don't use this function too much + panic("Don't use this function!"); + + int i = loadHead; + while (1) { + if (i == loadTail) { + assert(0 && "Load not in the queue!"); + } else if (loadQueue[i]->seqNum == inst) { + break; + } + + ++i; + if (i >= LQEntries) { + i = 0; + } + } + +// loadQueue[i]->removeInLSQ(); + loadQueue[i] = NULL; + --loads; +} + +template <class Impl> +void +OzoneLSQ<Impl>::commitLoads(InstSeqNum &youngest_inst) +{ + assert(loads == 0 || loadQueue[loadHead]); + + while (loads != 0 && loadQueue[loadHead]->seqNum <= youngest_inst) { + commitLoad(); + } +} + +template <class Impl> +void +OzoneLSQ<Impl>::commitStores(InstSeqNum &youngest_inst) +{ + assert(stores == 0 || storeQueue[storeHead].inst); + + int store_idx = storeHead; + + while (store_idx != storeTail) { + assert(storeQueue[store_idx].inst); + if (!storeQueue[store_idx].canWB) { + if (storeQueue[store_idx].inst->seqNum > youngest_inst) { + break; + } + DPRINTF(OzoneLSQ, "Marking store as able to write back, PC " + "%#x [sn:%lli]\n", + storeQueue[store_idx].inst->readPC(), + storeQueue[store_idx].inst->seqNum); + + storeQueue[store_idx].canWB = true; + +// --stores; + ++storesToWB; + } + + incrStIdx(store_idx); + } +} + +template <class Impl> +void +OzoneLSQ<Impl>::writebackStores() +{ + while (storesToWB > 0 && + storeWBIdx != storeTail && + storeQueue[storeWBIdx].inst && + storeQueue[storeWBIdx].canWB && + usedPorts < cachePorts) { + + if (storeQueue[storeWBIdx].size == 0) { + completeStore(storeWBIdx); + + incrStIdx(storeWBIdx); + + continue; + } + + if (dcacheInterface && dcacheInterface->isBlocked()) { + DPRINTF(OzoneLSQ, "Unable to write back any more stores, cache" + " is blocked!\n"); + break; + } + + ++usedPorts; + + if (storeQueue[storeWBIdx].inst->isDataPrefetch()) { + incrStIdx(storeWBIdx); + + continue; + } + + assert(storeQueue[storeWBIdx].req); + assert(!storeQueue[storeWBIdx].committed); + + MemReqPtr req = storeQueue[storeWBIdx].req; + storeQueue[storeWBIdx].committed = true; + +// Fault fault = cpu->translateDataReadReq(req); + req->cmd = Write; + req->completionEvent = NULL; + req->time = curTick; + assert(!req->data); + req->data = new uint8_t[64]; + memcpy(req->data, (uint8_t *)&storeQueue[storeWBIdx].data, req->size); + + DPRINTF(OzoneLSQ, "D-Cache: Writing back store idx:%i PC:%#x " + "to Addr:%#x, data:%#x [sn:%lli]\n", + storeWBIdx,storeQueue[storeWBIdx].inst->readPC(), + req->paddr, *(req->data), + storeQueue[storeWBIdx].inst->seqNum); + +// if (fault != NoFault) { + //What should we do if there is a fault??? + //for now panic +// panic("Page Table Fault!!!!!\n"); +// } + + if (dcacheInterface) { + MemAccessResult result = dcacheInterface->access(req); + + //@todo temp fix for LL/SC (works fine for 1 CPU) + if (req->flags & LOCKED) { + req->result=1; + panic("LL/SC! oh no no support!!!"); + } + + if (isStalled() && + storeQueue[storeWBIdx].inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load idx:%i\n", + stallingStoreIsn, stallingLoadIdx); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst(loadQueue[stallingLoadIdx]); + } + + if (result != MA_HIT && dcacheInterface->doEvents()) { + Event *wb = NULL; +/* + typename IEW::LdWritebackEvent *wb = NULL; + if (req->flags & LOCKED) { + // Stx_C does not generate a system port transaction. + req->result=0; + wb = new typename IEW::LdWritebackEvent(storeQueue[storeWBIdx].inst, + iewStage); + } +*/ + DPRINTF(OzoneLSQ,"D-Cache Write Miss!\n"); + +// DPRINTF(Activity, "Active st accessing mem miss [sn:%lli]\n", +// storeQueue[storeWBIdx].inst->seqNum); + + // Will stores need their own kind of writeback events? + // Do stores even need writeback events? + assert(!req->completionEvent); + req->completionEvent = new + StoreCompletionEvent(storeWBIdx, wb, this); + + lastDcacheStall = curTick; + + _status = DcacheMissStall; + + //mshrSeqNums.push_back(storeQueue[storeWBIdx].inst->seqNum); + + //DPRINTF(OzoneLSQ, "Added MSHR. count = %i\n",mshrSeqNums.size()); + + // Increment stat here or something + } else { + DPRINTF(OzoneLSQ,"D-Cache: Write Hit on idx:%i !\n", + storeWBIdx); + +// DPRINTF(Activity, "Active st accessing mem hit [sn:%lli]\n", +// storeQueue[storeWBIdx].inst->seqNum); + + if (req->flags & LOCKED) { + // Stx_C does not generate a system port transaction. + req->result=1; + typename BackEnd::LdWritebackEvent *wb = + new typename BackEnd::LdWritebackEvent(storeQueue[storeWBIdx].inst, + be); + wb->schedule(curTick); + } + + completeStore(storeWBIdx); + } + + incrStIdx(storeWBIdx); + } else { + panic("Must HAVE DCACHE!!!!!\n"); + } + } + + // Not sure this should set it to 0. + usedPorts = 0; + + assert(stores >= 0 && storesToWB >= 0); +} + +/*template <class Impl> +void +OzoneLSQ<Impl>::removeMSHR(InstSeqNum seqNum) +{ + list<InstSeqNum>::iterator mshr_it = find(mshrSeqNums.begin(), + mshrSeqNums.end(), + seqNum); + + if (mshr_it != mshrSeqNums.end()) { + mshrSeqNums.erase(mshr_it); + DPRINTF(OzoneLSQ, "Removing MSHR. count = %i\n",mshrSeqNums.size()); + } +}*/ + +template <class Impl> +void +OzoneLSQ<Impl>::squash(const InstSeqNum &squashed_num) +{ + DPRINTF(OzoneLSQ, "Squashing until [sn:%lli]!" + "(Loads:%i Stores:%i)\n",squashed_num,loads,stores); + + int load_idx = loadTail; + decrLdIdx(load_idx); + + while (loads != 0 && loadQueue[load_idx]->seqNum > squashed_num) { + + // Clear the smart pointer to make sure it is decremented. + DPRINTF(OzoneLSQ,"Load Instruction PC %#x squashed, " + "[sn:%lli]\n", + loadQueue[load_idx]->readPC(), + loadQueue[load_idx]->seqNum); + + if (isStalled() && load_idx == stallingLoadIdx) { + stalled = false; + stallingStoreIsn = 0; + stallingLoadIdx = 0; + } + +// loadQueue[load_idx]->squashed = true; + loadQueue[load_idx] = NULL; + --loads; + + // Inefficient! + loadTail = load_idx; + + decrLdIdx(load_idx); + } + + int store_idx = storeTail; + decrStIdx(store_idx); + + while (stores != 0 && storeQueue[store_idx].inst->seqNum > squashed_num) { + + // Clear the smart pointer to make sure it is decremented. + DPRINTF(OzoneLSQ,"Store Instruction PC %#x squashed, " + "idx:%i [sn:%lli]\n", + storeQueue[store_idx].inst->readPC(), + store_idx, storeQueue[store_idx].inst->seqNum); + + // I don't think this can happen. It should have been cleared by the + // stalling load. + if (isStalled() && + storeQueue[store_idx].inst->seqNum == stallingStoreIsn) { + panic("Is stalled should have been cleared by stalling load!\n"); + stalled = false; + stallingStoreIsn = 0; + } + +// storeQueue[store_idx].inst->squashed = true; + storeQueue[store_idx].inst = NULL; + storeQueue[store_idx].canWB = 0; + + if (storeQueue[store_idx].req) { + assert(!storeQueue[store_idx].req->completionEvent); + } + storeQueue[store_idx].req = NULL; + --stores; + + // Inefficient! + storeTail = store_idx; + + decrStIdx(store_idx); + } +} + +template <class Impl> +void +OzoneLSQ<Impl>::dumpInsts() +{ + cprintf("Load store queue: Dumping instructions.\n"); + cprintf("Load queue size: %i\n", loads); + cprintf("Load queue: "); + + int load_idx = loadHead; + + while (load_idx != loadTail && loadQueue[load_idx]) { + cprintf("[sn:%lli] %#x ", loadQueue[load_idx]->seqNum, + loadQueue[load_idx]->readPC()); + + incrLdIdx(load_idx); + } + + cprintf("\nStore queue size: %i\n", stores); + cprintf("Store queue: "); + + int store_idx = storeHead; + + while (store_idx != storeTail && storeQueue[store_idx].inst) { + cprintf("[sn:%lli] %#x ", storeQueue[store_idx].inst->seqNum, + storeQueue[store_idx].inst->readPC()); + + incrStIdx(store_idx); + } + + cprintf("\n"); +} + +template <class Impl> +void +OzoneLSQ<Impl>::completeStore(int store_idx) +{ + assert(storeQueue[store_idx].inst); + storeQueue[store_idx].completed = true; + --storesToWB; + // A bit conservative because a store completion may not free up entries, + // but hopefully avoids two store completions in one cycle from making + // the CPU tick twice. +// cpu->activityThisCycle(); + + if (store_idx == storeHead) { + do { + incrStIdx(storeHead); + + --stores; + } while (storeQueue[storeHead].completed && + storeHead != storeTail); + +// be->updateLSQNextCycle = true; + } + + DPRINTF(OzoneLSQ, "Store head idx:%i\n", storeHead); + + if (isStalled() && + storeQueue[store_idx].inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load idx:%i\n", + stallingStoreIsn, stallingLoadIdx); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst(loadQueue[stallingLoadIdx]); + } +} + +template <class Impl> +inline void +OzoneLSQ<Impl>::incrStIdx(int &store_idx) +{ + if (++store_idx >= SQEntries) + store_idx = 0; +} + +template <class Impl> +inline void +OzoneLSQ<Impl>::decrStIdx(int &store_idx) +{ + if (--store_idx < 0) + store_idx += SQEntries; +} + +template <class Impl> +inline void +OzoneLSQ<Impl>::incrLdIdx(int &load_idx) +{ + if (++load_idx >= LQEntries) + load_idx = 0; +} + +template <class Impl> +inline void +OzoneLSQ<Impl>::decrLdIdx(int &load_idx) +{ + if (--load_idx < 0) + load_idx += LQEntries; +} diff --git a/src/cpu/ozone/lw_back_end.cc b/src/cpu/ozone/lw_back_end.cc new file mode 100644 index 000000000..71e51f038 --- /dev/null +++ b/src/cpu/ozone/lw_back_end.cc @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/lw_back_end_impl.hh" +#include "cpu/ozone/ozone_impl.hh" + +template class LWBackEnd<OzoneImpl>; diff --git a/src/cpu/ozone/lw_back_end.hh b/src/cpu/ozone/lw_back_end.hh new file mode 100644 index 000000000..49c6a1ae2 --- /dev/null +++ b/src/cpu/ozone/lw_back_end.hh @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_LW_BACK_END_HH__ +#define __CPU_OZONE_LW_BACK_END_HH__ + +#include <list> +#include <queue> +#include <set> +#include <string> + +#include "arch/faults.hh" +#include "base/timebuf.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/rename_table.hh" +#include "cpu/ozone/thread_state.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" + +template <class> +class Checker; +class ThreadContext; + +template <class Impl> +class OzoneThreadState; + +class Port; + +template <class Impl> +class LWBackEnd +{ + public: + typedef OzoneThreadState<Impl> Thread; + + typedef typename Impl::Params Params; + typedef typename Impl::DynInst DynInst; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::OzoneCPU OzoneCPU; + typedef typename Impl::FrontEnd FrontEnd; + typedef typename Impl::OzoneCPU::CommStruct CommStruct; + + struct SizeStruct { + int size; + }; + + typedef SizeStruct DispatchToIssue; + typedef SizeStruct IssueToExec; + typedef SizeStruct ExecToCommit; + typedef SizeStruct Writeback; + + TimeBuffer<DispatchToIssue> d2i; + typename TimeBuffer<DispatchToIssue>::wire instsToDispatch; + TimeBuffer<IssueToExec> i2e; + typename TimeBuffer<IssueToExec>::wire instsToExecute; + TimeBuffer<ExecToCommit> e2c; + TimeBuffer<int> numInstsToWB; + + TimeBuffer<CommStruct> *comm; + typename TimeBuffer<CommStruct>::wire toIEW; + typename TimeBuffer<CommStruct>::wire fromCommit; + + class TrapEvent : public Event { + private: + LWBackEnd<Impl> *be; + + public: + TrapEvent(LWBackEnd<Impl> *_be); + + void process(); + const char *description(); + }; + + LWBackEnd(Params *params); + + std::string name() const; + + void regStats(); + + void setCPU(OzoneCPU *cpu_ptr); + + void setFrontEnd(FrontEnd *front_end_ptr) + { frontEnd = front_end_ptr; } + + void setTC(ThreadContext *tc_ptr) + { tc = tc_ptr; } + + void setThreadState(Thread *thread_ptr) + { thread = thread_ptr; } + + void setCommBuffer(TimeBuffer<CommStruct> *_comm); + + Port *getDcachePort() { return LSQ.getDcachePort(); } + + void tick(); + void squash(); + void generateTCEvent() { tcSquash = true; } + void squashFromTC(); + void squashFromTrap(); + void checkInterrupts(); + bool trapSquash; + bool tcSquash; + + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + Addr readCommitPC() { return commitPC; } + + Addr commitPC; + + Tick lastCommitCycle; + + bool robEmpty() { return numInsts == 0; } + + bool isFull() { return numInsts >= numROBEntries; } + bool isBlocked() { return status == Blocked || dispatchStatus == Blocked; } + + void fetchFault(Fault &fault); + + int wakeDependents(DynInstPtr &inst, bool memory_deps = false); + + /** Tells memory dependence unit that a memory instruction needs to be + * rescheduled. It will re-execute once replayMemInst() is called. + */ + void rescheduleMemInst(DynInstPtr &inst); + + /** Re-executes all rescheduled memory instructions. */ + void replayMemInst(DynInstPtr &inst); + + /** Completes memory instruction. */ + void completeMemInst(DynInstPtr &inst) { } + + void addDcacheMiss(DynInstPtr &inst) + { + waitingMemOps.insert(inst->seqNum); + numWaitingMemOps++; + DPRINTF(BE, "Adding a Dcache miss mem op [sn:%lli], total %i\n", + inst->seqNum, numWaitingMemOps); + } + + void removeDcacheMiss(DynInstPtr &inst) + { + assert(waitingMemOps.find(inst->seqNum) != waitingMemOps.end()); + waitingMemOps.erase(inst->seqNum); + numWaitingMemOps--; + DPRINTF(BE, "Removing a Dcache miss mem op [sn:%lli], total %i\n", + inst->seqNum, numWaitingMemOps); + } + + void addWaitingMemOp(DynInstPtr &inst) + { + waitingMemOps.insert(inst->seqNum); + numWaitingMemOps++; + DPRINTF(BE, "Adding a waiting mem op [sn:%lli], total %i\n", + inst->seqNum, numWaitingMemOps); + } + + void removeWaitingMemOp(DynInstPtr &inst) + { + assert(waitingMemOps.find(inst->seqNum) != waitingMemOps.end()); + waitingMemOps.erase(inst->seqNum); + numWaitingMemOps--; + DPRINTF(BE, "Removing a waiting mem op [sn:%lli], total %i\n", + inst->seqNum, numWaitingMemOps); + } + + void instToCommit(DynInstPtr &inst); + void readyInstsForCommit(); + + void switchOut(); + void doSwitchOut(); + void takeOverFrom(ThreadContext *old_tc = NULL); + + bool isSwitchedOut() { return switchedOut; } + + private: + void generateTrapEvent(Tick latency = 0); + void handleFault(Fault &fault, Tick latency = 0); + void updateStructures(); + void dispatchInsts(); + void dispatchStall(); + void checkDispatchStatus(); + void executeInsts(); + void commitInsts(); + void addToLSQ(DynInstPtr &inst); + void writebackInsts(); + bool commitInst(int inst_num); + void squash(const InstSeqNum &sn); + void squashDueToBranch(DynInstPtr &inst); + void squashDueToMemViolation(DynInstPtr &inst); + void squashDueToMemBlocked(DynInstPtr &inst); + void updateExeInstStats(DynInstPtr &inst); + void updateComInstStats(DynInstPtr &inst); + + public: + OzoneCPU *cpu; + + FrontEnd *frontEnd; + + ThreadContext *tc; + + Thread *thread; + + enum Status { + Running, + Idle, + DcacheMissStall, + DcacheMissComplete, + Blocked, + TrapPending + }; + + Status status; + + Status dispatchStatus; + + Status commitStatus; + + Counter funcExeInst; + + private: + typedef typename Impl::LdstQueue LdstQueue; + + LdstQueue LSQ; + public: + RenameTable<Impl> commitRenameTable; + + RenameTable<Impl> renameTable; + private: + int latency; + + // General back end width. Used if the more specific isn't given. + int width; + + // Dispatch width. + int dispatchWidth; + int dispatchSize; + + int waitingInsts; + + int issueWidth; + + // Writeback width + int wbWidth; + + // Commit width + int commitWidth; + + /** Index into queue of instructions being written back. */ + unsigned wbNumInst; + + /** Cycle number within the queue of instructions being written + * back. Used in case there are too many instructions writing + * back at the current cycle and writesbacks need to be scheduled + * for the future. See comments in instToCommit(). + */ + unsigned wbCycle; + + int numROBEntries; + int numInsts; + bool lsqLimits; + + std::set<InstSeqNum> waitingMemOps; + typedef std::set<InstSeqNum>::iterator MemIt; + int numWaitingMemOps; + unsigned maxOutstandingMemOps; + + bool squashPending; + InstSeqNum squashSeqNum; + Addr squashNextPC; + + bool switchedOut; + bool switchPending; + + DynInstPtr memBarrier; + + private: + struct pqCompare { + bool operator() (const DynInstPtr &lhs, const DynInstPtr &rhs) const + { + return lhs->seqNum > rhs->seqNum; + } + }; + + typedef typename std::priority_queue<DynInstPtr, std::vector<DynInstPtr>, pqCompare> ReadyInstQueue; + ReadyInstQueue exeList; + + typedef typename std::list<DynInstPtr>::iterator InstListIt; + + std::list<DynInstPtr> instList; + std::list<DynInstPtr> waitingList; + std::list<DynInstPtr> replayList; + std::list<DynInstPtr> writeback; + + int squashLatency; + + bool exactFullStall; + + // number of cycles stalled for D-cache misses +/* Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; +*/ + Stats::Vector<> robCapEvents; + Stats::Vector<> robCapInstCount; + Stats::Vector<> iqCapEvents; + Stats::Vector<> iqCapInstCount; + // total number of instructions executed + Stats::Vector<> exeInst; + Stats::Vector<> exeSwp; + Stats::Vector<> exeNop; + Stats::Vector<> exeRefs; + Stats::Vector<> exeLoads; + Stats::Vector<> exeBranches; + + Stats::Vector<> issuedOps; + + // total number of loads forwaded from LSQ stores + Stats::Vector<> lsqForwLoads; + + // total number of loads ignored due to invalid addresses + Stats::Vector<> invAddrLoads; + + // total number of software prefetches ignored due to invalid addresses + Stats::Vector<> invAddrSwpfs; + // ready loads blocked due to memory disambiguation + Stats::Vector<> lsqBlockedLoads; + + Stats::Scalar<> lsqInversion; + + Stats::Vector<> nIssuedDist; +/* + Stats::VectorDistribution<> issueDelayDist; + + Stats::VectorDistribution<> queueResDist; +*/ +/* + Stats::Vector<> stat_fu_busy; + Stats::Vector2d<> stat_fuBusy; + Stats::Vector<> dist_unissued; + Stats::Vector2d<> stat_issued_inst_type; + + Stats::Formula misspec_cnt; + Stats::Formula misspec_ipc; + Stats::Formula issue_rate; + Stats::Formula issue_stores; + Stats::Formula issue_op_rate; + Stats::Formula fu_busy_rate; + Stats::Formula commit_stores; + Stats::Formula commit_ipc; + Stats::Formula commit_ipb; + Stats::Formula lsq_inv_rate; +*/ + Stats::Vector<> writebackCount; + Stats::Vector<> producerInst; + Stats::Vector<> consumerInst; + Stats::Vector<> wbPenalized; + + Stats::Formula wbRate; + Stats::Formula wbFanout; + Stats::Formula wbPenalizedRate; + + // total number of instructions committed + Stats::Vector<> statComInst; + Stats::Vector<> statComSwp; + Stats::Vector<> statComRefs; + Stats::Vector<> statComLoads; + Stats::Vector<> statComMembars; + Stats::Vector<> statComBranches; + + Stats::Distribution<> nCommittedDist; + + Stats::Scalar<> commitEligibleSamples; + Stats::Vector<> commitEligible; + + Stats::Vector<> squashedInsts; + Stats::Vector<> ROBSquashedInsts; + + Stats::Scalar<> ROBFcount; + Stats::Formula ROBFullRate; + + Stats::Vector<> ROBCount; // cumulative ROB occupancy + Stats::Formula ROBOccRate; +// Stats::VectorDistribution<> ROBOccDist; + public: + void dumpInsts(); + + Checker<DynInstPtr> *checker; +}; + +template <class Impl> +template <class T> +Fault +LWBackEnd<Impl>::read(RequestPtr req, T &data, int load_idx) +{ + return LSQ.read(req, data, load_idx); +} + +template <class Impl> +template <class T> +Fault +LWBackEnd<Impl>::write(RequestPtr req, T &data, int store_idx) +{ + return LSQ.write(req, data, store_idx); +} + +#endif // __CPU_OZONE_LW_BACK_END_HH__ diff --git a/src/cpu/ozone/lw_back_end_impl.hh b/src/cpu/ozone/lw_back_end_impl.hh new file mode 100644 index 000000000..f87a2bc57 --- /dev/null +++ b/src/cpu/ozone/lw_back_end_impl.hh @@ -0,0 +1,1719 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "config/use_checker.hh" + +#include "cpu/ozone/lw_back_end.hh" +#include "cpu/op_class.hh" + +#if USE_CHECKER +#include "cpu/checker/cpu.hh" +#endif + +template <class Impl> +void +LWBackEnd<Impl>::generateTrapEvent(Tick latency) +{ + DPRINTF(BE, "Generating trap event\n"); + + TrapEvent *trap = new TrapEvent(this); + + trap->schedule(curTick + cpu->cycles(latency)); + + thread->trapPending = true; +} + +template <class Impl> +int +LWBackEnd<Impl>::wakeDependents(DynInstPtr &inst, bool memory_deps) +{ + assert(!inst->isSquashed()); + std::vector<DynInstPtr> &dependents = memory_deps ? inst->getMemDeps() : + inst->getDependents(); + int num_outputs = dependents.size(); + + DPRINTF(BE, "Waking instruction [sn:%lli] dependents in IQ\n", inst->seqNum); + + for (int i = 0; i < num_outputs; i++) { + DynInstPtr dep_inst = dependents[i]; + if (!memory_deps) { + dep_inst->markSrcRegReady(); + } else { + if (!dep_inst->isSquashed()) + dep_inst->markMemInstReady(inst.get()); + } + + DPRINTF(BE, "Marking source reg ready [sn:%lli] in IQ\n", dep_inst->seqNum); + + if (dep_inst->readyToIssue() && dep_inst->isInROB() && + !dep_inst->isNonSpeculative() && !dep_inst->isStoreConditional() && + dep_inst->memDepReady() && !dep_inst->isMemBarrier() && + !dep_inst->isWriteBarrier()) { + DPRINTF(BE, "Adding instruction to exeList [sn:%lli]\n", + dep_inst->seqNum); + exeList.push(dep_inst); + if (dep_inst->iqItValid) { + DPRINTF(BE, "Removing instruction from waiting list\n"); + waitingList.erase(dep_inst->iqIt); + waitingInsts--; + dep_inst->iqItValid = false; + assert(waitingInsts >= 0); + } + if (dep_inst->isMemRef()) { + removeWaitingMemOp(dep_inst); + DPRINTF(BE, "Issued a waiting mem op [sn:%lli]\n", + dep_inst->seqNum); + } + } + } + return num_outputs; +} + +template <class Impl> +void +LWBackEnd<Impl>::rescheduleMemInst(DynInstPtr &inst) +{ + replayList.push_front(inst); +} + +template <class Impl> +LWBackEnd<Impl>::TrapEvent::TrapEvent(LWBackEnd<Impl> *_be) + : Event(&mainEventQueue, CPU_Tick_Pri), be(_be) +{ + this->setFlags(Event::AutoDelete); +} + +template <class Impl> +void +LWBackEnd<Impl>::TrapEvent::process() +{ + be->trapSquash = true; +} + +template <class Impl> +const char * +LWBackEnd<Impl>::TrapEvent::description() +{ + return "Trap event"; +} + +template <class Impl> +void +LWBackEnd<Impl>::replayMemInst(DynInstPtr &inst) +{ + bool found_inst = false; + while (!replayList.empty()) { + exeList.push(replayList.front()); + if (replayList.front() == inst) { + found_inst = true; + } + replayList.pop_front(); + } + assert(found_inst); +} + +template <class Impl> +LWBackEnd<Impl>::LWBackEnd(Params *params) + : d2i(5, 5), i2e(5, 5), e2c(5, 5), numInstsToWB(params->backEndLatency, 0), + trapSquash(false), tcSquash(false), + latency(params->backEndLatency), + width(params->backEndWidth), lsqLimits(params->lsqLimits), + exactFullStall(true) +{ + numROBEntries = params->numROBEntries; + numInsts = 0; + maxOutstandingMemOps = params->maxOutstandingMemOps; + numWaitingMemOps = 0; + waitingInsts = 0; + switchedOut = false; + switchPending = false; + + LSQ.setBE(this); + + // Setup IQ and LSQ with their parameters here. + instsToDispatch = d2i.getWire(-1); + + instsToExecute = i2e.getWire(-1); + + dispatchWidth = params->dispatchWidth ? params->dispatchWidth : width; + issueWidth = params->issueWidth ? params->issueWidth : width; + wbWidth = params->wbWidth ? params->wbWidth : width; + commitWidth = params->commitWidth ? params->commitWidth : width; + + LSQ.init(params, params->LQEntries, params->SQEntries, 0); + + dispatchStatus = Running; + commitStatus = Running; +} + +template <class Impl> +std::string +LWBackEnd<Impl>::name() const +{ + return cpu->name() + ".backend"; +} + +template <class Impl> +void +LWBackEnd<Impl>::regStats() +{ + using namespace Stats; + LSQ.regStats(); + + robCapEvents + .init(cpu->number_of_threads) + .name(name() + ".ROB:cap_events") + .desc("number of cycles where ROB cap was active") + .flags(total) + ; + + robCapInstCount + .init(cpu->number_of_threads) + .name(name() + ".ROB:cap_inst") + .desc("number of instructions held up by ROB cap") + .flags(total) + ; + + iqCapEvents + .init(cpu->number_of_threads) + .name(name() +".IQ:cap_events" ) + .desc("number of cycles where IQ cap was active") + .flags(total) + ; + + iqCapInstCount + .init(cpu->number_of_threads) + .name(name() + ".IQ:cap_inst") + .desc("number of instructions held up by IQ cap") + .flags(total) + ; + + exeInst + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:count") + .desc("number of insts issued") + .flags(total) + ; + + exeSwp + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:swp") + .desc("number of swp insts issued") + .flags(total) + ; + + exeNop + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:nop") + .desc("number of nop insts issued") + .flags(total) + ; + + exeRefs + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:refs") + .desc("number of memory reference insts issued") + .flags(total) + ; + + exeLoads + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:loads") + .desc("number of load insts issued") + .flags(total) + ; + + exeBranches + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:branches") + .desc("Number of branches issued") + .flags(total) + ; + + issuedOps + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:op_count") + .desc("number of insts issued") + .flags(total) + ; + +/* + for (int i=0; i<Num_OpClasses; ++i) { + stringstream subname; + subname << opClassStrings[i] << "_delay"; + issue_delay_dist.subname(i, subname.str()); + } +*/ + // + // Other stats + // + lsqForwLoads + .init(cpu->number_of_threads) + .name(name() + ".LSQ:forw_loads") + .desc("number of loads forwarded via LSQ") + .flags(total) + ; + + invAddrLoads + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:addr_loads") + .desc("number of invalid-address loads") + .flags(total) + ; + + invAddrSwpfs + .init(cpu->number_of_threads) + .name(name() + ".ISSUE:addr_swpfs") + .desc("number of invalid-address SW prefetches") + .flags(total) + ; + + lsqBlockedLoads + .init(cpu->number_of_threads) + .name(name() + ".LSQ:blocked_loads") + .desc("number of ready loads not issued due to memory disambiguation") + .flags(total) + ; + + lsqInversion + .name(name() + ".ISSUE:lsq_invert") + .desc("Number of times LSQ instruction issued early") + ; + + nIssuedDist + .init(issueWidth + 1) + .name(name() + ".ISSUE:issued_per_cycle") + .desc("Number of insts issued each cycle") + .flags(total | pdf | dist) + ; +/* + issueDelayDist + .init(Num_OpClasses,0,99,2) + .name(name() + ".ISSUE:") + .desc("cycles from operands ready to issue") + .flags(pdf | cdf) + ; + + queueResDist + .init(Num_OpClasses, 0, 99, 2) + .name(name() + ".IQ:residence:") + .desc("cycles from dispatch to issue") + .flags(total | pdf | cdf ) + ; + for (int i = 0; i < Num_OpClasses; ++i) { + queueResDist.subname(i, opClassStrings[i]); + } +*/ + writebackCount + .init(cpu->number_of_threads) + .name(name() + ".WB:count") + .desc("cumulative count of insts written-back") + .flags(total) + ; + + producerInst + .init(cpu->number_of_threads) + .name(name() + ".WB:producers") + .desc("num instructions producing a value") + .flags(total) + ; + + consumerInst + .init(cpu->number_of_threads) + .name(name() + ".WB:consumers") + .desc("num instructions consuming a value") + .flags(total) + ; + + wbPenalized + .init(cpu->number_of_threads) + .name(name() + ".WB:penalized") + .desc("number of instrctions required to write to 'other' IQ") + .flags(total) + ; + + + wbPenalizedRate + .name(name() + ".WB:penalized_rate") + .desc ("fraction of instructions written-back that wrote to 'other' IQ") + .flags(total) + ; + + wbPenalizedRate = wbPenalized / writebackCount; + + wbFanout + .name(name() + ".WB:fanout") + .desc("average fanout of values written-back") + .flags(total) + ; + + wbFanout = producerInst / consumerInst; + + wbRate + .name(name() + ".WB:rate") + .desc("insts written-back per cycle") + .flags(total) + ; + wbRate = writebackCount / cpu->numCycles; + + statComInst + .init(cpu->number_of_threads) + .name(name() + ".COM:count") + .desc("Number of instructions committed") + .flags(total) + ; + + statComSwp + .init(cpu->number_of_threads) + .name(name() + ".COM:swp_count") + .desc("Number of s/w prefetches committed") + .flags(total) + ; + + statComRefs + .init(cpu->number_of_threads) + .name(name() + ".COM:refs") + .desc("Number of memory references committed") + .flags(total) + ; + + statComLoads + .init(cpu->number_of_threads) + .name(name() + ".COM:loads") + .desc("Number of loads committed") + .flags(total) + ; + + statComMembars + .init(cpu->number_of_threads) + .name(name() + ".COM:membars") + .desc("Number of memory barriers committed") + .flags(total) + ; + + statComBranches + .init(cpu->number_of_threads) + .name(name() + ".COM:branches") + .desc("Number of branches committed") + .flags(total) + ; + nCommittedDist + .init(0,commitWidth,1) + .name(name() + ".COM:committed_per_cycle") + .desc("Number of insts commited each cycle") + .flags(pdf) + ; + + // + // Commit-Eligible instructions... + // + // -> The number of instructions eligible to commit in those + // cycles where we reached our commit BW limit (less the number + // actually committed) + // + // -> The average value is computed over ALL CYCLES... not just + // the BW limited cycles + // + // -> The standard deviation is computed only over cycles where + // we reached the BW limit + // + commitEligible + .init(cpu->number_of_threads) + .name(name() + ".COM:bw_limited") + .desc("number of insts not committed due to BW limits") + .flags(total) + ; + + commitEligibleSamples + .name(name() + ".COM:bw_lim_events") + .desc("number cycles where commit BW limit reached") + ; + + squashedInsts + .init(cpu->number_of_threads) + .name(name() + ".COM:squashed_insts") + .desc("Number of instructions removed from inst list") + ; + + ROBSquashedInsts + .init(cpu->number_of_threads) + .name(name() + ".COM:rob_squashed_insts") + .desc("Number of instructions removed from inst list when they reached the head of the ROB") + ; + + ROBFcount + .name(name() + ".ROB:full_count") + .desc("number of cycles where ROB was full") + ; + + ROBCount + .init(cpu->number_of_threads) + .name(name() + ".ROB:occupancy") + .desc(name() + ".ROB occupancy (cumulative)") + .flags(total) + ; + + ROBFullRate + .name(name() + ".ROB:full_rate") + .desc("ROB full per cycle") + ; + ROBFullRate = ROBFcount / cpu->numCycles; + + ROBOccRate + .name(name() + ".ROB:occ_rate") + .desc("ROB occupancy rate") + .flags(total) + ; + ROBOccRate = ROBCount / cpu->numCycles; +/* + ROBOccDist + .init(cpu->number_of_threads,0,numROBEntries,2) + .name(name() + ".ROB:occ_dist") + .desc("ROB Occupancy per cycle") + .flags(total | cdf) + ; +*/ +} + +template <class Impl> +void +LWBackEnd<Impl>::setCPU(OzoneCPU *cpu_ptr) +{ + cpu = cpu_ptr; + LSQ.setCPU(cpu_ptr); + checker = cpu->checker; +} + +template <class Impl> +void +LWBackEnd<Impl>::setCommBuffer(TimeBuffer<CommStruct> *_comm) +{ + comm = _comm; + toIEW = comm->getWire(0); + fromCommit = comm->getWire(-1); +} + +#if FULL_SYSTEM +template <class Impl> +void +LWBackEnd<Impl>::checkInterrupts() +{ + if (cpu->checkInterrupts && + cpu->check_interrupts() && + !cpu->inPalMode(thread->readPC()) && + !trapSquash && + !tcSquash) { + frontEnd->interruptPending = true; + if (robEmpty() && !LSQ.hasStoresToWB()) { + // Will need to squash all instructions currently in flight and have + // the interrupt handler restart at the last non-committed inst. + // Most of that can be handled through the trap() function. The + // processInterrupts() function really just checks for interrupts + // and then calls trap() if there is an interrupt present. + + // Not sure which thread should be the one to interrupt. For now + // always do thread 0. + assert(!thread->inSyscall); + thread->inSyscall = true; + + // CPU will handle implementation of the interrupt. + cpu->processInterrupts(); + + // Now squash or record that I need to squash this cycle. + commitStatus = TrapPending; + + // Exit state update mode to avoid accidental updating. + thread->inSyscall = false; + + // Generate trap squash event. + generateTrapEvent(); + + DPRINTF(BE, "Interrupt detected.\n"); + } else { + DPRINTF(BE, "Interrupt must wait for ROB to drain.\n"); + } + } +} +#endif + +template <class Impl> +void +LWBackEnd<Impl>::handleFault(Fault &fault, Tick latency) +{ + DPRINTF(BE, "Handling fault!\n"); + + assert(!thread->inSyscall); + + thread->inSyscall = true; + + // Consider holding onto the trap and waiting until the trap event + // happens for this to be executed. + fault->invoke(thread->getTC()); + + // Exit state update mode to avoid accidental updating. + thread->inSyscall = false; + + commitStatus = TrapPending; + + // Generate trap squash event. + generateTrapEvent(latency); +} + +template <class Impl> +void +LWBackEnd<Impl>::tick() +{ + DPRINTF(BE, "Ticking back end\n"); + + // Read in any done instruction information and update the IQ or LSQ. + updateStructures(); + + if (switchPending && robEmpty() && !LSQ.hasStoresToWB()) { + cpu->signalSwitched(); + return; + } + + readyInstsForCommit(); + + numInstsToWB.advance(); + + ROBCount[0]+= numInsts; + + wbCycle = 0; + +#if FULL_SYSTEM + checkInterrupts(); +#endif + + if (trapSquash) { + assert(!tcSquash); + squashFromTrap(); + } else if (tcSquash) { + squashFromTC(); + } + + if (dispatchStatus != Blocked) { + dispatchInsts(); + } else { + checkDispatchStatus(); + } + + if (commitStatus != TrapPending) { + executeInsts(); + + commitInsts(); + } + + LSQ.writebackStores(); + + DPRINTF(BE, "Waiting insts: %i, mem ops: %i, ROB entries in use: %i, " + "LSQ loads: %i, LSQ stores: %i\n", + waitingInsts, numWaitingMemOps, numInsts, + LSQ.numLoads(), LSQ.numStores()); + +#ifdef DEBUG + assert(numInsts == instList.size()); + assert(waitingInsts == waitingList.size()); + assert(numWaitingMemOps == waitingMemOps.size()); + assert(!switchedOut); +#endif +} + +template <class Impl> +void +LWBackEnd<Impl>::updateStructures() +{ + if (fromCommit->doneSeqNum) { + LSQ.commitLoads(fromCommit->doneSeqNum); + LSQ.commitStores(fromCommit->doneSeqNum); + } + + if (fromCommit->nonSpecSeqNum) { + if (fromCommit->uncached) { +// LSQ.executeLoad(fromCommit->lqIdx); + } else { +// IQ.scheduleNonSpec( +// fromCommit->nonSpecSeqNum); + } + } +} + +template <class Impl> +void +LWBackEnd<Impl>::addToLSQ(DynInstPtr &inst) +{ + // Do anything LSQ specific here? + LSQ.insert(inst); +} + +template <class Impl> +void +LWBackEnd<Impl>::dispatchInsts() +{ + DPRINTF(BE, "Trying to dispatch instructions.\n"); + + while (numInsts < numROBEntries && + numWaitingMemOps < maxOutstandingMemOps) { + // Get instruction from front of time buffer + if (lsqLimits && LSQ.isFull()) { + break; + } + + DynInstPtr inst = frontEnd->getInst(); + if (!inst) { + break; + } else if (inst->isSquashed()) { + continue; + } + + ++numInsts; + instList.push_front(inst); + + inst->setInROB(); + + DPRINTF(BE, "Dispatching instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + for (int i = 0; i < inst->numDestRegs(); ++i) + renameTable[inst->destRegIdx(i)] = inst; + + if (inst->isMemBarrier() || inst->isWriteBarrier()) { + if (memBarrier) { + DPRINTF(BE, "Instruction [sn:%lli] is waiting on " + "barrier [sn:%lli].\n", + inst->seqNum, memBarrier->seqNum); + memBarrier->addMemDependent(inst); + inst->addSrcMemInst(memBarrier); + } + memBarrier = inst; + inst->setCanCommit(); + } else if (inst->readyToIssue() && + !inst->isNonSpeculative() && + !inst->isStoreConditional()) { + if (inst->isMemRef()) { + + LSQ.insert(inst); + if (memBarrier) { + DPRINTF(BE, "Instruction [sn:%lli] is waiting on " + "barrier [sn:%lli].\n", + inst->seqNum, memBarrier->seqNum); + memBarrier->addMemDependent(inst); + inst->addSrcMemInst(memBarrier); + addWaitingMemOp(inst); + + waitingList.push_front(inst); + inst->iqIt = waitingList.begin(); + inst->iqItValid = true; + waitingInsts++; + } else { + DPRINTF(BE, "Instruction [sn:%lli] ready, addding to " + "exeList.\n", + inst->seqNum); + exeList.push(inst); + } + } else if (inst->isNop()) { + DPRINTF(BE, "Nop encountered [sn:%lli], skipping exeList.\n", + inst->seqNum); + inst->setIssued(); + inst->setExecuted(); + inst->setCanCommit(); + numInstsToWB[0]++; + } else { + DPRINTF(BE, "Instruction [sn:%lli] ready, addding to " + "exeList.\n", + inst->seqNum); + exeList.push(inst); + } + } else { + if (inst->isNonSpeculative() || inst->isStoreConditional()) { + inst->setCanCommit(); + DPRINTF(BE, "Adding non speculative instruction\n"); + } + + if (inst->isMemRef()) { + addWaitingMemOp(inst); + LSQ.insert(inst); + if (memBarrier) { + memBarrier->addMemDependent(inst); + inst->addSrcMemInst(memBarrier); + + DPRINTF(BE, "Instruction [sn:%lli] is waiting on " + "barrier [sn:%lli].\n", + inst->seqNum, memBarrier->seqNum); + } + } + + DPRINTF(BE, "Instruction [sn:%lli] not ready, addding to " + "waitingList.\n", + inst->seqNum); + waitingList.push_front(inst); + inst->iqIt = waitingList.begin(); + inst->iqItValid = true; + waitingInsts++; + } + } + + // Check if IQ or LSQ is full. If so we'll need to break and stop + // removing instructions. Also update the number of insts to remove + // from the queue. Check here if we don't care about exact stall + // conditions. +/* + bool stall = false; + if (IQ.isFull()) { + DPRINTF(BE, "IQ is full!\n"); + stall = true; + } else if (LSQ.isFull()) { + DPRINTF(BE, "LSQ is full!\n"); + stall = true; + } else if (isFull()) { + DPRINTF(BE, "ROB is full!\n"); + stall = true; + ROB_fcount++; + } + if (stall) { + d2i.advance(); + dispatchStall(); + return; + } +*/ +} + +template <class Impl> +void +LWBackEnd<Impl>::dispatchStall() +{ + dispatchStatus = Blocked; + if (!cpu->decoupledFrontEnd) { + // Tell front end to stall here through a timebuffer, or just tell + // it directly. + } +} + +template <class Impl> +void +LWBackEnd<Impl>::checkDispatchStatus() +{ + DPRINTF(BE, "Checking dispatch status\n"); + assert(dispatchStatus == Blocked); + if (!LSQ.isFull() && !isFull()) { + DPRINTF(BE, "Dispatch no longer blocked\n"); + dispatchStatus = Running; + dispatchInsts(); + } +} + +template <class Impl> +void +LWBackEnd<Impl>::executeInsts() +{ + DPRINTF(BE, "Trying to execute instructions\n"); + + int num_executed = 0; + while (!exeList.empty() && num_executed < issueWidth) { + DynInstPtr inst = exeList.top(); + + DPRINTF(BE, "Executing inst [sn:%lli] PC: %#x\n", + inst->seqNum, inst->readPC()); + + // Check if the instruction is squashed; if so then skip it + // and don't count it towards the FU usage. + if (inst->isSquashed()) { + DPRINTF(BE, "Execute: Instruction was squashed.\n"); + + // Not sure how to handle this plus the method of sending # of + // instructions to use. Probably will just have to count it + // towards the bandwidth usage, but not the FU usage. + ++num_executed; + + // Consider this instruction executed so that commit can go + // ahead and retire the instruction. + inst->setExecuted(); + + // Not sure if I should set this here or just let commit try to + // commit any squashed instructions. I like the latter a bit more. + inst->setCanCommit(); + +// ++iewExecSquashedInsts; + exeList.pop(); + + continue; + } + + Fault fault = NoFault; + + // Execute instruction. + // Note that if the instruction faults, it will be handled + // at the commit stage. + if (inst->isMemRef() && + (!inst->isDataPrefetch() && !inst->isInstPrefetch())) { + DPRINTF(BE, "Execute: Initiating access for memory " + "reference.\n"); + + if (inst->isLoad()) { + LSQ.executeLoad(inst); + } else if (inst->isStore()) { + Fault fault = LSQ.executeStore(inst); + + if (!inst->isStoreConditional() && fault == NoFault) { + inst->setExecuted(); + + instToCommit(inst); + } else if (fault != NoFault) { + // If the instruction faulted, then we need to send it along to commit + // without the instruction completing. + // Send this instruction to commit, also make sure iew stage + // realizes there is activity. + inst->setExecuted(); + + instToCommit(inst); + } + } else { + panic("Unknown mem type!"); + } + } else { + inst->execute(); + + inst->setExecuted(); + + instToCommit(inst); + } + + updateExeInstStats(inst); + + ++funcExeInst; + ++num_executed; + + exeList.pop(); + + if (inst->mispredicted()) { + squashDueToBranch(inst); + break; + } else if (LSQ.violation()) { + // Get the DynInst that caused the violation. Note that this + // clears the violation signal. + DynInstPtr violator; + violator = LSQ.getMemDepViolator(); + + DPRINTF(BE, "LDSTQ detected a violation. Violator PC: " + "%#x, inst PC: %#x. Addr is: %#x.\n", + violator->readPC(), inst->readPC(), inst->physEffAddr); + + // Squash. + squashDueToMemViolation(inst); + } + } + + issuedOps[0]+= num_executed; + nIssuedDist[num_executed]++; +} + +template<class Impl> +void +LWBackEnd<Impl>::instToCommit(DynInstPtr &inst) +{ + DPRINTF(BE, "Sending instructions to commit [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + if (!inst->isSquashed()) { + if (inst->isExecuted()) { + inst->setResultReady(); + int dependents = wakeDependents(inst); + if (dependents) { + producerInst[0]++; + consumerInst[0]+= dependents; + } + } + } + + writeback.push_back(inst); + + numInstsToWB[0]++; + + writebackCount[0]++; +} + +template <class Impl> +void +LWBackEnd<Impl>::readyInstsForCommit() +{ + for (int i = numInstsToWB[-latency]; + !writeback.empty() && i; + --i) + { + DynInstPtr inst = writeback.front(); + writeback.pop_front(); + if (!inst->isSquashed()) { + DPRINTF(BE, "Writing back instruction [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + inst->setCanCommit(); + } + } +} + +#if 0 +template <class Impl> +void +LWBackEnd<Impl>::writebackInsts() +{ + int wb_width = wbWidth; + // Using this method I'm not quite sure how to prevent an + // instruction from waking its own dependents multiple times, + // without the guarantee that commit always has enough bandwidth + // to accept all instructions being written back. This guarantee + // might not be too unrealistic. + InstListIt wb_inst_it = writeback.begin(); + InstListIt wb_end_it = writeback.end(); + int inst_num = 0; + int consumer_insts = 0; + + for (; inst_num < wb_width && + wb_inst_it != wb_end_it; inst_num++) { + DynInstPtr inst = (*wb_inst_it); + + // Some instructions will be sent to commit without having + // executed because they need commit to handle them. + // E.g. Uncached loads have not actually executed when they + // are first sent to commit. Instead commit must tell the LSQ + // when it's ready to execute the uncached load. + if (!inst->isSquashed()) { + DPRINTF(BE, "Writing back instruction [sn:%lli] PC %#x.\n", + inst->seqNum, inst->readPC()); + + inst->setCanCommit(); + inst->setResultReady(); + + if (inst->isExecuted()) { + int dependents = wakeDependents(inst); + if (dependents) { + producer_inst[0]++; + consumer_insts+= dependents; + } + } + } + + writeback.erase(wb_inst_it++); + } + LSQ.writebackStores(); + consumer_inst[0]+= consumer_insts; + writeback_count[0]+= inst_num; +} +#endif +template <class Impl> +bool +LWBackEnd<Impl>::commitInst(int inst_num) +{ + // Read instruction from the head of the ROB + DynInstPtr inst = instList.back(); + + // Make sure instruction is valid + assert(inst); + + if (!inst->readyToCommit()) + return false; + + DPRINTF(BE, "Trying to commit instruction [sn:%lli] PC:%#x\n", + inst->seqNum, inst->readPC()); + + thread->setPC(inst->readPC()); + thread->setNextPC(inst->readNextPC()); + inst->setAtCommit(); + + // If the instruction is not executed yet, then it is a non-speculative + // or store inst. Signal backwards that it should be executed. + if (!inst->isExecuted()) { + if (inst->isNonSpeculative() || + (inst->isStoreConditional() && inst->getFault() == NoFault) || + inst->isMemBarrier() || + inst->isWriteBarrier()) { +#if !FULL_SYSTEM + // Hack to make sure syscalls aren't executed until all stores + // write back their data. This direct communication shouldn't + // be used for anything other than this. + if (inst_num > 0 || LSQ.hasStoresToWB()) +#else + if ((inst->isMemBarrier() || inst->isWriteBarrier() || + inst->isQuiesce()) && + LSQ.hasStoresToWB()) +#endif + { + DPRINTF(BE, "Waiting for all stores to writeback.\n"); + return false; + } + + DPRINTF(BE, "Encountered a store or non-speculative " + "instruction at the head of the ROB, PC %#x.\n", + inst->readPC()); + + if (inst->isMemBarrier() || inst->isWriteBarrier()) { + DPRINTF(BE, "Waking dependents on barrier [sn:%lli]\n", + inst->seqNum); + assert(memBarrier); + wakeDependents(inst, true); + if (memBarrier == inst) + memBarrier = NULL; + inst->clearMemDependents(); + } + + // Send back the non-speculative instruction's sequence number. + if (inst->iqItValid) { + DPRINTF(BE, "Removing instruction from waiting list\n"); + waitingList.erase(inst->iqIt); + inst->iqItValid = false; + waitingInsts--; + assert(waitingInsts >= 0); + if (inst->isStore()) + removeWaitingMemOp(inst); + } + + exeList.push(inst); + + // Change the instruction so it won't try to commit again until + // it is executed. + inst->clearCanCommit(); + +// ++commitNonSpecStalls; + + return false; + } else if (inst->isLoad()) { + DPRINTF(BE, "[sn:%lli]: Uncached load, PC %#x.\n", + inst->seqNum, inst->readPC()); + + // Send back the non-speculative instruction's sequence + // number. Maybe just tell the lsq to re-execute the load. + + // Send back the non-speculative instruction's sequence number. + if (inst->iqItValid) { + DPRINTF(BE, "Removing instruction from waiting list\n"); + waitingList.erase(inst->iqIt); + inst->iqItValid = false; + waitingInsts--; + assert(waitingInsts >= 0); + removeWaitingMemOp(inst); + } + replayMemInst(inst); + + inst->clearCanCommit(); + + return false; + } else { + panic("Trying to commit un-executed instruction " + "of unknown type!\n"); + } + } + + // Not handled for now. + assert(!inst->isThreadSync()); + assert(inst->memDepReady()); + // Stores will mark themselves as totally completed as they need + // to wait to writeback to memory. @todo: Hack...attempt to fix + // having the checker be forced to wait until a store completes in + // order to check all of the instructions. If the store at the + // head of the check list misses, but a later store hits, then + // loads in the checker may see the younger store values instead + // of the store they should see. Either the checker needs its own + // memory (annoying to update), its own store buffer (how to tell + // which value is correct?), or something else... + if (!inst->isStore()) { + inst->setCompleted(); + } + // Check if the instruction caused a fault. If so, trap. + Fault inst_fault = inst->getFault(); + + // Use checker prior to updating anything due to traps or PC + // based events. +#if USE_CHECKER + if (checker) { + checker->verify(inst); + } +#endif + + if (inst_fault != NoFault) { + DPRINTF(BE, "Inst [sn:%lli] PC %#x has a fault\n", + inst->seqNum, inst->readPC()); + + // Instruction is completed as it has a fault. + inst->setCompleted(); + + if (LSQ.hasStoresToWB()) { + DPRINTF(BE, "Stores still in flight, will wait until drained.\n"); + return false; + } else if (inst_num != 0) { + DPRINTF(BE, "Will wait until instruction is head of commit group.\n"); + return false; + } +#if USE_CHECKER + else if (checker && inst->isStore()) { + checker->verify(inst); + } +#endif + + thread->setInst( + static_cast<TheISA::MachInst>(inst->staticInst->machInst)); + + handleFault(inst_fault); + return false; + } + + int freed_regs = 0; + + for (int i = 0; i < inst->numDestRegs(); ++i) { + DPRINTF(BE, "Commit rename map setting reg %i to [sn:%lli]\n", + (int)inst->destRegIdx(i), inst->seqNum); + thread->renameTable[inst->destRegIdx(i)] = inst; + ++freed_regs; + } + +#if FULL_SYSTEM + if (thread->profile) { +// bool usermode = +// (xc->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0; +// thread->profilePC = usermode ? 1 : inst->readPC(); + thread->profilePC = inst->readPC(); + ProfileNode *node = thread->profile->consume(thread->getXCProxy(), + inst->staticInst); + + if (node) + thread->profileNode = node; + } +#endif + + if (inst->traceData) { + inst->traceData->setFetchSeq(inst->seqNum); + inst->traceData->setCPSeq(thread->numInst); + inst->traceData->finalize(); + inst->traceData = NULL; + } + + if (inst->isCopy()) + panic("Should not commit any copy instructions!"); + + inst->clearDependents(); + + frontEnd->addFreeRegs(freed_regs); + + instList.pop_back(); + + --numInsts; + ++thread->funcExeInst; + // Maybe move this to where the fault is handled; if the fault is + // handled, don't try to set this myself as the fault will set it. + // If not, then I set thread->PC = thread->nextPC and + // thread->nextPC = thread->nextPC + 4. + thread->setPC(thread->readNextPC()); + thread->setNextPC(thread->readNextPC() + sizeof(TheISA::MachInst)); + updateComInstStats(inst); + + // Write the done sequence number here. + toIEW->doneSeqNum = inst->seqNum; + lastCommitCycle = curTick; + +#if FULL_SYSTEM + int count = 0; + Addr oldpc; + do { + if (count == 0) + assert(!thread->inSyscall && !thread->trapPending); + oldpc = thread->readPC(); + cpu->system->pcEventQueue.service( + thread->getTC()); + count++; + } while (oldpc != thread->readPC()); + if (count > 1) { + DPRINTF(BE, "PC skip function event, stopping commit\n"); + tcSquash = true; + return false; + } +#endif + return true; +} + +template <class Impl> +void +LWBackEnd<Impl>::commitInsts() +{ + // Not sure this should be a loop or not. + int inst_num = 0; + while (!instList.empty() && inst_num < commitWidth) { + if (instList.back()->isSquashed()) { + instList.back()->clearDependents(); + ROBSquashedInsts[instList.back()->threadNumber]++; + instList.pop_back(); + --numInsts; + continue; + } + + if (!commitInst(inst_num++)) { + DPRINTF(BE, "Can't commit, Instruction [sn:%lli] PC " + "%#x is head of ROB and not ready\n", + instList.back()->seqNum, instList.back()->readPC()); + --inst_num; + break; + } + } + nCommittedDist.sample(inst_num); +} + +template <class Impl> +void +LWBackEnd<Impl>::squash(const InstSeqNum &sn) +{ + LSQ.squash(sn); + + int freed_regs = 0; + InstListIt insts_end_it = waitingList.end(); + InstListIt insts_it = waitingList.begin(); + + while (insts_it != insts_end_it && (*insts_it)->seqNum > sn) + { + if ((*insts_it)->isSquashed()) { + ++insts_it; + continue; + } + DPRINTF(BE, "Squashing instruction on waitingList PC %#x, [sn:%lli].\n", + (*insts_it)->readPC(), + (*insts_it)->seqNum); + + if ((*insts_it)->isMemRef()) { + DPRINTF(BE, "Squashing a waiting mem op [sn:%lli]\n", + (*insts_it)->seqNum); + removeWaitingMemOp((*insts_it)); + } + + waitingList.erase(insts_it++); + waitingInsts--; + } + assert(waitingInsts >= 0); + + insts_it = instList.begin(); + + while (!instList.empty() && (*insts_it)->seqNum > sn) + { + if ((*insts_it)->isSquashed()) { + panic("Instruction should not be already squashed and on list!"); + ++insts_it; + continue; + } + DPRINTF(BE, "Squashing instruction on inst list PC %#x, [sn:%lli].\n", + (*insts_it)->readPC(), + (*insts_it)->seqNum); + + // Mark the instruction as squashed, and ready to commit so that + // it can drain out of the pipeline. + (*insts_it)->setSquashed(); + + (*insts_it)->setCanCommit(); + + (*insts_it)->clearInROB(); + + for (int i = 0; i < (*insts_it)->numDestRegs(); ++i) { + DynInstPtr prev_dest = (*insts_it)->getPrevDestInst(i); + DPRINTF(BE, "Commit rename map setting reg %i to [sn:%lli]\n", + (int)(*insts_it)->destRegIdx(i), prev_dest->seqNum); + renameTable[(*insts_it)->destRegIdx(i)] = prev_dest; + ++freed_regs; + } + + (*insts_it)->clearDependents(); + + squashedInsts[(*insts_it)->threadNumber]++; + + instList.erase(insts_it++); + --numInsts; + } + + while (memBarrier && memBarrier->seqNum > sn) { + DPRINTF(BE, "[sn:%lli] Memory barrier squashed (or previously " + "squashed)\n", memBarrier->seqNum); + memBarrier->clearMemDependents(); + if (memBarrier->memDepReady()) { + DPRINTF(BE, "No previous barrier\n"); + memBarrier = NULL; + } else { + std::list<DynInstPtr> &srcs = memBarrier->getMemSrcs(); + memBarrier = srcs.front(); + srcs.pop_front(); + assert(srcs.empty()); + DPRINTF(BE, "Previous barrier: [sn:%lli]\n", + memBarrier->seqNum); + } + } + + insts_it = replayList.begin(); + insts_end_it = replayList.end(); + while (!replayList.empty() && insts_it != insts_end_it) { + if ((*insts_it)->seqNum < sn) { + ++insts_it; + continue; + } + assert((*insts_it)->isSquashed()); + + replayList.erase(insts_it++); + } + + frontEnd->addFreeRegs(freed_regs); +} + +template <class Impl> +void +LWBackEnd<Impl>::squashFromTC() +{ + InstSeqNum squashed_inst = robEmpty() ? 0 : instList.back()->seqNum - 1; + squash(squashed_inst); + frontEnd->squash(squashed_inst, thread->readPC(), + false, false); + frontEnd->interruptPending = false; + + thread->trapPending = false; + thread->inSyscall = false; + tcSquash = false; + commitStatus = Running; +} + +template <class Impl> +void +LWBackEnd<Impl>::squashFromTrap() +{ + InstSeqNum squashed_inst = robEmpty() ? 0 : instList.back()->seqNum - 1; + squash(squashed_inst); + frontEnd->squash(squashed_inst, thread->readPC(), + false, false); + frontEnd->interruptPending = false; + + thread->trapPending = false; + thread->inSyscall = false; + trapSquash = false; + commitStatus = Running; +} + +template <class Impl> +void +LWBackEnd<Impl>::squashDueToBranch(DynInstPtr &inst) +{ + // Update the branch predictor state I guess + DPRINTF(BE, "Squashing due to branch [sn:%lli], will restart at PC %#x\n", + inst->seqNum, inst->readNextPC()); + squash(inst->seqNum); + frontEnd->squash(inst->seqNum, inst->readNextPC(), + true, inst->mispredicted()); +} + +template <class Impl> +void +LWBackEnd<Impl>::squashDueToMemViolation(DynInstPtr &inst) +{ + // Update the branch predictor state I guess + DPRINTF(BE, "Squashing due to violation [sn:%lli], will restart at PC %#x\n", + inst->seqNum, inst->readNextPC()); + squash(inst->seqNum); + frontEnd->squash(inst->seqNum, inst->readNextPC(), + false, inst->mispredicted()); +} + +template <class Impl> +void +LWBackEnd<Impl>::squashDueToMemBlocked(DynInstPtr &inst) +{ + DPRINTF(IEW, "Memory blocked, squashing load and younger insts, " + "PC: %#x [sn:%i].\n", inst->readPC(), inst->seqNum); + + squash(inst->seqNum - 1); + frontEnd->squash(inst->seqNum - 1, inst->readPC()); +} + +template <class Impl> +void +LWBackEnd<Impl>::switchOut() +{ + switchPending = true; +} + +template <class Impl> +void +LWBackEnd<Impl>::doSwitchOut() +{ + switchedOut = true; + switchPending = false; + // Need to get rid of all committed, non-speculative state and write it + // to memory/TC. In this case this is stores that have committed and not + // yet written back. + assert(robEmpty()); + assert(!LSQ.hasStoresToWB()); + writeback.clear(); + for (int i = 0; i < numInstsToWB.getSize() + 1; ++i) + numInstsToWB.advance(); + +// squash(0); + assert(waitingList.empty()); + assert(instList.empty()); + assert(replayList.empty()); + assert(writeback.empty()); + LSQ.switchOut(); +} + +template <class Impl> +void +LWBackEnd<Impl>::takeOverFrom(ThreadContext *old_tc) +{ + assert(!squashPending); + squashSeqNum = 0; + squashNextPC = 0; + tcSquash = false; + trapSquash = false; + + numInsts = 0; + numWaitingMemOps = 0; + waitingMemOps.clear(); + waitingInsts = 0; + switchedOut = false; + dispatchStatus = Running; + commitStatus = Running; + LSQ.takeOverFrom(old_tc); +} + +template <class Impl> +void +LWBackEnd<Impl>::updateExeInstStats(DynInstPtr &inst) +{ + int thread_number = inst->threadNumber; + + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) + exeSwp[thread_number]++; + else + exeInst[thread_number]++; +#else + exeInst[thread_number]++; +#endif + + // + // Control operations + // + if (inst->isControl()) + exeBranches[thread_number]++; + + // + // Memory operations + // + if (inst->isMemRef()) { + exeRefs[thread_number]++; + + if (inst->isLoad()) + exeLoads[thread_number]++; + } +} + +template <class Impl> +void +LWBackEnd<Impl>::updateComInstStats(DynInstPtr &inst) +{ + unsigned tid = inst->threadNumber; + + // keep an instruction count + thread->numInst++; + thread->numInsts++; + + cpu->numInst++; + // + // Pick off the software prefetches + // +#ifdef TARGET_ALPHA + if (inst->isDataPrefetch()) { + statComSwp[tid]++; + } else { + statComInst[tid]++; + } +#else + statComInst[tid]++; +#endif + + // + // Control Instructions + // + if (inst->isControl()) + statComBranches[tid]++; + + // + // Memory references + // + if (inst->isMemRef()) { + statComRefs[tid]++; + + if (inst->isLoad()) { + statComLoads[tid]++; + } + } + + if (inst->isMemBarrier()) { + statComMembars[tid]++; + } +} + +template <class Impl> +void +LWBackEnd<Impl>::dumpInsts() +{ + int num = 0; + int valid_num = 0; + + InstListIt inst_list_it = --(instList.end()); + + cprintf("ExeList size: %i\n", exeList.size()); + + cprintf("Inst list size: %i\n", instList.size()); + + while (inst_list_it != instList.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } + + inst_list_it = --(writeback.end()); + + cprintf("Writeback list size: %i\n", writeback.size()); + + while (inst_list_it != writeback.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } + + cprintf("Waiting list size: %i\n", waitingList.size()); + + inst_list_it = --(waitingList.end()); + + while (inst_list_it != waitingList.end()) + { + cprintf("Instruction:%i\n", + num); + if (!(*inst_list_it)->isSquashed()) { + if (!(*inst_list_it)->isIssued()) { + ++valid_num; + cprintf("Count:%i\n", valid_num); + } else if ((*inst_list_it)->isMemRef() && + !(*inst_list_it)->memOpDone) { + // Loads that have not been marked as executed still count + // towards the total instructions. + ++valid_num; + cprintf("Count:%i\n", valid_num); + } + } + + cprintf("PC:%#x\n[sn:%lli]\n[tid:%i]\n" + "Issued:%i\nSquashed:%i\n", + (*inst_list_it)->readPC(), + (*inst_list_it)->seqNum, + (*inst_list_it)->threadNumber, + (*inst_list_it)->isIssued(), + (*inst_list_it)->isSquashed()); + + if ((*inst_list_it)->isMemRef()) { + cprintf("MemOpDone:%i\n", (*inst_list_it)->memOpDone); + } + + cprintf("\n"); + + inst_list_it--; + ++num; + } + + cprintf("waitingMemOps list size: %i\n", waitingMemOps.size()); + + MemIt waiting_it = waitingMemOps.begin(); + + while (waiting_it != waitingMemOps.end()) + { + cprintf("[sn:%lli] ", (*waiting_it)); + waiting_it++; + ++num; + } + cprintf("\n"); +} diff --git a/src/cpu/ozone/lw_lsq.cc b/src/cpu/ozone/lw_lsq.cc new file mode 100644 index 000000000..8674e83a0 --- /dev/null +++ b/src/cpu/ozone/lw_lsq.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/ozone_impl.hh" +#include "cpu/ozone/lw_lsq_impl.hh" + +// Force the instantiation of LDSTQ for all the implementations we care about. +template class OzoneLWLSQ<OzoneImpl>; + diff --git a/src/cpu/ozone/lw_lsq.hh b/src/cpu/ozone/lw_lsq.hh new file mode 100644 index 000000000..6640a9f34 --- /dev/null +++ b/src/cpu/ozone/lw_lsq.hh @@ -0,0 +1,708 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_LW_LSQ_HH__ +#define __CPU_OZONE_LW_LSQ_HH__ + +#include <list> +#include <map> +#include <queue> +#include <algorithm> + +#include "arch/faults.hh" +#include "arch/types.hh" +#include "config/full_system.hh" +#include "base/hashmap.hh" +#include "cpu/inst_seq.hh" +#include "mem/packet.hh" +#include "mem/port.hh" +//#include "mem/page_table.hh" +#include "sim/debug.hh" +#include "sim/sim_object.hh" + +class MemObject; + +/** + * Class that implements the actual LQ and SQ for each specific thread. + * Both are circular queues; load entries are freed upon committing, while + * store entries are freed once they writeback. The LSQUnit tracks if there + * are memory ordering violations, and also detects partial load to store + * forwarding cases (a store only has part of a load's data) that requires + * the load to wait until the store writes back. In the former case it + * holds onto the instruction until the dependence unit looks at it, and + * in the latter it stalls the LSQ until the store writes back. At that + * point the load is replayed. + */ +template <class Impl> +class OzoneLWLSQ { + public: + typedef typename Impl::Params Params; + typedef typename Impl::OzoneCPU OzoneCPU; + typedef typename Impl::BackEnd BackEnd; + typedef typename Impl::DynInstPtr DynInstPtr; + typedef typename Impl::IssueStruct IssueStruct; + + typedef TheISA::IntReg IntReg; + + typedef typename std::map<InstSeqNum, DynInstPtr>::iterator LdMapIt; + + public: + /** Constructs an LSQ unit. init() must be called prior to use. */ + OzoneLWLSQ(); + + /** Initializes the LSQ unit with the specified number of entries. */ + void init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id); + + /** Returns the name of the LSQ unit. */ + std::string name() const; + + void regStats(); + + /** Sets the CPU pointer. */ + void setCPU(OzoneCPU *cpu_ptr); + + /** Sets the back-end stage pointer. */ + void setBE(BackEnd *be_ptr) + { be = be_ptr; } + + Port *getDcachePort() { return &dcachePort; } + + /** Ticks the LSQ unit, which in this case only resets the number of + * used cache ports. + * @todo: Move the number of used ports up to the LSQ level so it can + * be shared by all LSQ units. + */ + void tick() { usedPorts = 0; } + + /** Inserts an instruction. */ + void insert(DynInstPtr &inst); + /** Inserts a load instruction. */ + void insertLoad(DynInstPtr &load_inst); + /** Inserts a store instruction. */ + void insertStore(DynInstPtr &store_inst); + + /** Executes a load instruction. */ + Fault executeLoad(DynInstPtr &inst); + + /** Executes a store instruction. */ + Fault executeStore(DynInstPtr &inst); + + /** Commits the head load. */ + void commitLoad(); + /** Commits loads older than a specific sequence number. */ + void commitLoads(InstSeqNum &youngest_inst); + + /** Commits stores older than a specific sequence number. */ + void commitStores(InstSeqNum &youngest_inst); + + /** Writes back stores. */ + void writebackStores(); + + /** Completes the data access that has been returned from the + * memory system. */ + void completeDataAccess(PacketPtr pkt); + + // @todo: Include stats in the LSQ unit. + //void regStats(); + + /** Clears all the entries in the LQ. */ + void clearLQ(); + + /** Clears all the entries in the SQ. */ + void clearSQ(); + + /** Resizes the LQ to a given size. */ + void resizeLQ(unsigned size); + + /** Resizes the SQ to a given size. */ + void resizeSQ(unsigned size); + + /** Squashes all instructions younger than a specific sequence number. */ + void squash(const InstSeqNum &squashed_num); + + /** Returns if there is a memory ordering violation. Value is reset upon + * call to getMemDepViolator(). + */ + bool violation() { return memDepViolator; } + + /** Returns the memory ordering violator. */ + DynInstPtr getMemDepViolator(); + + /** Returns if a load became blocked due to the memory system. It clears + * the bool's value upon this being called. + */ + bool loadBlocked() + { return isLoadBlocked; } + + void clearLoadBlocked() + { isLoadBlocked = false; } + + bool isLoadBlockedHandled() + { return loadBlockedHandled; } + + void setLoadBlockedHandled() + { loadBlockedHandled = true; } + + /** Returns the number of free entries (min of free LQ and SQ entries). */ + unsigned numFreeEntries(); + + /** Returns the number of loads ready to execute. */ + int numLoadsReady(); + + /** Returns the number of loads in the LQ. */ + int numLoads() { return loads; } + + /** Returns the number of stores in the SQ. */ + int numStores() { return stores + storesInFlight; } + + /** Returns if either the LQ or SQ is full. */ + bool isFull() { return lqFull() || sqFull(); } + + /** Returns if the LQ is full. */ + bool lqFull() { return loads >= (LQEntries - 1); } + + /** Returns if the SQ is full. */ + bool sqFull() { return (stores + storesInFlight) >= (SQEntries - 1); } + + /** Debugging function to dump instructions in the LSQ. */ + void dumpInsts(); + + /** Returns the number of instructions in the LSQ. */ + unsigned getCount() { return loads + stores; } + + /** Returns if there are any stores to writeback. */ + bool hasStoresToWB() { return storesToWB; } + + /** Returns the number of stores to writeback. */ + int numStoresToWB() { return storesToWB; } + + /** Returns if the LSQ unit will writeback on this cycle. */ + bool willWB() { return storeQueue.back().canWB && + !storeQueue.back().completed && + !isStoreBlocked; } + + void switchOut(); + + void takeOverFrom(ThreadContext *old_tc = NULL); + + bool isSwitchedOut() { return switchedOut; } + + bool switchedOut; + + private: + /** Writes back the instruction, sending it to IEW. */ + void writeback(DynInstPtr &inst, PacketPtr pkt); + + /** Handles completing the send of a store to memory. */ + void storePostSend(Packet *pkt, DynInstPtr &inst); + + /** Completes the store at the specified index. */ + void completeStore(DynInstPtr &inst); + + void removeStore(int store_idx); + + /** Handles doing the retry. */ + void recvRetry(); + + private: + /** Pointer to the CPU. */ + OzoneCPU *cpu; + + /** Pointer to the back-end stage. */ + BackEnd *be; + + MemObject *mem; + + class DcachePort : public Port + { + protected: + OzoneLWLSQ *lsq; + + public: + DcachePort(OzoneLWLSQ *_lsq) + : lsq(_lsq) + { } + + protected: + virtual Tick recvAtomic(PacketPtr pkt); + + virtual void recvFunctional(PacketPtr pkt); + + virtual void recvStatusChange(Status status); + + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + virtual bool recvTiming(PacketPtr pkt); + + virtual void recvRetry(); + }; + + /** D-cache port. */ + DcachePort dcachePort; + + public: + struct SQEntry { + /** Constructs an empty store queue entry. */ + SQEntry() + : inst(NULL), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0), lqIt(NULL) + { } + + /** Constructs a store queue entry for a given instruction. */ + SQEntry(DynInstPtr &_inst) + : inst(_inst), req(NULL), size(0), data(0), + canWB(0), committed(0), completed(0), lqIt(NULL) + { } + + /** The store instruction. */ + DynInstPtr inst; + /** The memory request for the store. */ + RequestPtr req; + /** The size of the store. */ + int size; + /** The store data. */ + IntReg data; + /** Whether or not the store can writeback. */ + bool canWB; + /** Whether or not the store is committed. */ + bool committed; + /** Whether or not the store is completed. */ + bool completed; + + typename std::list<DynInstPtr>::iterator lqIt; + }; + + /** Derived class to hold any sender state the LSQ needs. */ + class LSQSenderState : public Packet::SenderState + { + public: + /** Default constructor. */ + LSQSenderState() + : noWB(false) + { } + + /** Instruction who initiated the access to memory. */ + DynInstPtr inst; + /** Whether or not it is a load. */ + bool isLoad; + /** The LQ/SQ index of the instruction. */ + int idx; + /** Whether or not the instruction will need to writeback. */ + bool noWB; + }; + + /** Writeback event, specifically for when stores forward data to loads. */ + class WritebackEvent : public Event { + public: + /** Constructs a writeback event. */ + WritebackEvent(DynInstPtr &_inst, PacketPtr pkt, OzoneLWLSQ *lsq_ptr); + + /** Processes the writeback event. */ + void process(); + + /** Returns the description of this event. */ + const char *description(); + + private: + /** Instruction whose results are being written back. */ + DynInstPtr inst; + + /** The packet that would have been sent to memory. */ + PacketPtr pkt; + + /** The pointer to the LSQ unit that issued the store. */ + OzoneLWLSQ<Impl> *lsqPtr; + }; + + enum Status { + Running, + Idle, + DcacheMissStall, + DcacheMissSwitch + }; + + private: + /** The OzoneLWLSQ thread id. */ + unsigned lsqID; + + /** The status of the LSQ unit. */ + Status _status; + + /** The store queue. */ + std::list<SQEntry> storeQueue; + /** The load queue. */ + std::list<DynInstPtr> loadQueue; + + typedef typename std::list<SQEntry>::iterator SQIt; + typedef typename std::list<DynInstPtr>::iterator LQIt; + + + struct HashFn { + size_t operator() (const int a) const + { + unsigned hash = (((a >> 14) ^ ((a >> 2) & 0xffff))) & 0x7FFFFFFF; + + return hash; + } + }; + + m5::hash_map<int, SQIt, HashFn> SQItHash; + std::queue<int> SQIndices; + m5::hash_map<int, LQIt, HashFn> LQItHash; + std::queue<int> LQIndices; + + typedef typename m5::hash_map<int, LQIt, HashFn>::iterator LQHashIt; + typedef typename m5::hash_map<int, SQIt, HashFn>::iterator SQHashIt; + // Consider making these 16 bits + /** The number of LQ entries. */ + unsigned LQEntries; + /** The number of SQ entries. */ + unsigned SQEntries; + + /** The number of load instructions in the LQ. */ + int loads; + /** The number of store instructions in the SQ (excludes those waiting to + * writeback). + */ + int stores; + + int storesToWB; + + public: + int storesInFlight; + + private: + /// @todo Consider moving to a more advanced model with write vs read ports + /** The number of cache ports available each cycle. */ + int cachePorts; + + /** The number of used cache ports in this cycle. */ + int usedPorts; + + //list<InstSeqNum> mshrSeqNums; + + /** Tota number of memory ordering violations. */ + Stats::Scalar<> lsqMemOrderViolation; + + //Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; + + // Make these per thread? + /** Whether or not the LSQ is stalled. */ + bool stalled; + /** The store that causes the stall due to partial store to load + * forwarding. + */ + InstSeqNum stallingStoreIsn; + /** The index of the above store. */ + LQIt stallingLoad; + + /** The packet that needs to be retried. */ + PacketPtr retryPkt; + + /** Whehter or not a store is blocked due to the memory system. */ + bool isStoreBlocked; + + /** Whether or not a load is blocked due to the memory system. It is + * cleared when this value is checked via loadBlocked(). + */ + bool isLoadBlocked; + + bool loadBlockedHandled; + + InstSeqNum blockedLoadSeqNum; + + /** The oldest faulting load instruction. */ + DynInstPtr loadFaultInst; + /** The oldest faulting store instruction. */ + DynInstPtr storeFaultInst; + + /** The oldest load that caused a memory ordering violation. */ + DynInstPtr memDepViolator; + + // Will also need how many read/write ports the Dcache has. Or keep track + // of that in stage that is one level up, and only call executeLoad/Store + // the appropriate number of times. + + public: + /** Executes the load at the given index. */ + template <class T> + Fault read(RequestPtr req, T &data, int load_idx); + + /** Executes the store at the given index. */ + template <class T> + Fault write(RequestPtr req, T &data, int store_idx); + + /** Returns the sequence number of the head load instruction. */ + InstSeqNum getLoadHeadSeqNum() + { + if (!loadQueue.empty()) { + return loadQueue.back()->seqNum; + } else { + return 0; + } + + } + + /** Returns the sequence number of the head store instruction. */ + InstSeqNum getStoreHeadSeqNum() + { + if (!storeQueue.empty()) { + return storeQueue.back().inst->seqNum; + } else { + return 0; + } + + } + + /** Returns whether or not the LSQ unit is stalled. */ + bool isStalled() { return stalled; } +}; + +template <class Impl> +template <class T> +Fault +OzoneLWLSQ<Impl>::read(RequestPtr req, T &data, int load_idx) +{ + //Depending on issue2execute delay a squashed load could + //execute if it is found to be squashed in the same + //cycle it is scheduled to execute + typename m5::hash_map<int, LQIt, HashFn>::iterator + lq_hash_it = LQItHash.find(load_idx); + assert(lq_hash_it != LQItHash.end()); + DynInstPtr inst = (*(*lq_hash_it).second); + + // Make sure this isn't an uncacheable access + // A bit of a hackish way to get uncached accesses to work only if they're + // at the head of the LSQ and are ready to commit (at the head of the ROB + // too). + // @todo: Fix uncached accesses. + if (req->getFlags() & UNCACHEABLE && + (inst != loadQueue.back() || !inst->isAtCommit())) { + DPRINTF(OzoneLSQ, "[sn:%lli] Uncached load and not head of " + "commit/LSQ!\n", + inst->seqNum); + be->rescheduleMemInst(inst); + return TheISA::genMachineCheckFault(); + } + + // Check the SQ for any previous stores that might lead to forwarding + SQIt sq_it = storeQueue.begin(); + int store_size = 0; + + DPRINTF(OzoneLSQ, "Read called, load idx: %i addr: %#x\n", + load_idx, req->getPaddr()); + + while (sq_it != storeQueue.end() && (*sq_it).inst->seqNum > inst->seqNum) + ++sq_it; + + while (1) { + // End once we've reached the top of the LSQ + if (sq_it == storeQueue.end()) { + break; + } + + assert((*sq_it).inst); + + store_size = (*sq_it).size; + + if (store_size == 0 || (*sq_it).committed) { + sq_it++; + continue; + } + + // Check if the store data is within the lower and upper bounds of + // addresses that the request needs. + bool store_has_lower_limit = + req->getVaddr() >= (*sq_it).inst->effAddr; + bool store_has_upper_limit = + (req->getVaddr() + req->getSize()) <= ((*sq_it).inst->effAddr + + store_size); + bool lower_load_has_store_part = + req->getVaddr() < ((*sq_it).inst->effAddr + + store_size); + bool upper_load_has_store_part = + (req->getVaddr() + req->getSize()) > (*sq_it).inst->effAddr; + + // If the store's data has all of the data needed, we can forward. + if (store_has_lower_limit && store_has_upper_limit) { + int shift_amt = req->getVaddr() & (store_size - 1); + // Assumes byte addressing + shift_amt = shift_amt << 3; + + // Cast this to type T? + data = (*sq_it).data >> shift_amt; + + assert(!inst->memData); + inst->memData = new uint8_t[64]; + + memcpy(inst->memData, &data, req->getSize()); + + DPRINTF(OzoneLSQ, "Forwarding from store [sn:%lli] to load to " + "[sn:%lli] addr %#x, data %#x\n", + (*sq_it).inst->seqNum, inst->seqNum, req->getVaddr(), + *(inst->memData)); + + PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(inst->memData); + + WritebackEvent *wb = new WritebackEvent(inst, data_pkt, this); + + // We'll say this has a 1 cycle load-store forwarding latency + // for now. + // @todo: Need to make this a parameter. + wb->schedule(curTick); + + // Should keep track of stat for forwarded data + return NoFault; + } else if ((store_has_lower_limit && lower_load_has_store_part) || + (store_has_upper_limit && upper_load_has_store_part) || + (lower_load_has_store_part && upper_load_has_store_part)) { + // This is the partial store-load forwarding case where a store + // has only part of the load's data. + + // If it's already been written back, then don't worry about + // stalling on it. + if ((*sq_it).completed) { + sq_it++; + break; + } + + // Must stall load and force it to retry, so long as it's the oldest + // load that needs to do so. + if (!stalled || + (stalled && + inst->seqNum < + (*stallingLoad)->seqNum)) { + stalled = true; + stallingStoreIsn = (*sq_it).inst->seqNum; + stallingLoad = (*lq_hash_it).second; + } + + // Tell IQ/mem dep unit that this instruction will need to be + // rescheduled eventually + be->rescheduleMemInst(inst); + + DPRINTF(OzoneLSQ, "Load-store forwarding mis-match. " + "Store [sn:%lli] to load addr %#x\n", + (*sq_it).inst->seqNum, req->getVaddr()); + + return NoFault; + } + sq_it++; + } + + // If there's no forwarding case, then go access memory + DPRINTF(OzoneLSQ, "Doing functional access for inst PC %#x\n", + inst->readPC()); + + assert(!inst->memData); + inst->memData = new uint8_t[64]; + + ++usedPorts; + + DPRINTF(OzoneLSQ, "Doing timing access for inst PC %#x\n", + inst->readPC()); + + PacketPtr data_pkt = new Packet(req, Packet::ReadReq, Packet::Broadcast); + data_pkt->dataStatic(inst->memData); + + LSQSenderState *state = new LSQSenderState; + state->isLoad = true; + state->idx = load_idx; + state->inst = inst; + data_pkt->senderState = state; + + // if we have a cache, do cache access too + if (!dcachePort.sendTiming(data_pkt)) { + // There's an older load that's already going to squash. + if (isLoadBlocked && blockedLoadSeqNum < inst->seqNum) + return NoFault; + + // Record that the load was blocked due to memory. This + // load will squash all instructions after it, be + // refetched, and re-executed. + isLoadBlocked = true; + loadBlockedHandled = false; + blockedLoadSeqNum = inst->seqNum; + // No fault occurred, even though the interface is blocked. + return NoFault; + } + + if (req->getFlags() & LOCKED) { + cpu->lockFlag = true; + } + + if (data_pkt->result != Packet::Success) { + DPRINTF(OzoneLSQ, "OzoneLSQ: D-cache miss!\n"); + DPRINTF(Activity, "Activity: ld accessing mem miss [sn:%lli]\n", + inst->seqNum); + } else { + DPRINTF(OzoneLSQ, "OzoneLSQ: D-cache hit!\n"); + DPRINTF(Activity, "Activity: ld accessing mem hit [sn:%lli]\n", + inst->seqNum); + } + + return NoFault; +} + +template <class Impl> +template <class T> +Fault +OzoneLWLSQ<Impl>::write(RequestPtr req, T &data, int store_idx) +{ + SQHashIt sq_hash_it = SQItHash.find(store_idx); + assert(sq_hash_it != SQItHash.end()); + + SQIt sq_it = (*sq_hash_it).second; + assert((*sq_it).inst); + + DPRINTF(OzoneLSQ, "Doing write to store idx %i, addr %#x data %#x" + " | [sn:%lli]\n", + store_idx, req->getPaddr(), data, (*sq_it).inst->seqNum); + + (*sq_it).req = req; + (*sq_it).size = sizeof(T); + (*sq_it).data = data; +/* + assert(!req->data); + req->data = new uint8_t[64]; + memcpy(req->data, (uint8_t *)&(*sq_it).data, req->size); +*/ + + // This function only writes the data to the store queue, so no fault + // can happen here. + return NoFault; +} + +#endif // __CPU_OZONE_LW_LSQ_HH__ diff --git a/src/cpu/ozone/lw_lsq_impl.hh b/src/cpu/ozone/lw_lsq_impl.hh new file mode 100644 index 000000000..31ffa9d67 --- /dev/null +++ b/src/cpu/ozone/lw_lsq_impl.hh @@ -0,0 +1,993 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "config/use_checker.hh" + +#include "arch/faults.hh" +#include "base/str.hh" +#include "cpu/ozone/lw_lsq.hh" +#include "cpu/checker/cpu.hh" + +template<class Impl> +OzoneLWLSQ<Impl>::WritebackEvent::WritebackEvent(DynInstPtr &_inst, PacketPtr _pkt, + OzoneLWLSQ *lsq_ptr) + : Event(&mainEventQueue), inst(_inst), pkt(_pkt), lsqPtr(lsq_ptr) +{ + this->setFlags(Event::AutoDelete); +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::WritebackEvent::process() +{ + if (!lsqPtr->isSwitchedOut()) { + lsqPtr->writeback(inst, pkt); + } + delete pkt; +} + +template<class Impl> +const char * +OzoneLWLSQ<Impl>::WritebackEvent::description() +{ + return "Store writeback event"; +} + +template <class Impl> +Tick +OzoneLWLSQ<Impl>::DcachePort::recvAtomic(PacketPtr pkt) +{ + panic("O3CPU model does not work with atomic mode!"); + return curTick; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::DcachePort::recvFunctional(PacketPtr pkt) +{ + panic("O3CPU doesn't expect recvFunctional callback!"); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::DcachePort::recvStatusChange(Status status) +{ + if (status == RangeChange) + return; + + panic("O3CPU doesn't expect recvStatusChange callback!"); +} + +template <class Impl> +bool +OzoneLWLSQ<Impl>::DcachePort::recvTiming(PacketPtr pkt) +{ + lsq->completeDataAccess(pkt); + return true; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::DcachePort::recvRetry() +{ + lsq->recvRetry(); +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::completeDataAccess(PacketPtr pkt) +{ + LSQSenderState *state = dynamic_cast<LSQSenderState *>(pkt->senderState); + DynInstPtr inst = state->inst; + DPRINTF(IEW, "Writeback event [sn:%lli]\n", inst->seqNum); + DPRINTF(Activity, "Activity: Writeback event [sn:%lli]\n", inst->seqNum); + + //iewStage->ldstQueue.removeMSHR(inst->threadNumber,inst->seqNum); + + if (isSwitchedOut() || inst->isSquashed()) { + delete state; + delete pkt; + return; + } else { + if (!state->noWB) { + writeback(inst, pkt); + } + + if (inst->isStore()) { + completeStore(state->idx); + } + } + + delete state; + delete pkt; +} + +template <class Impl> +OzoneLWLSQ<Impl>::OzoneLWLSQ() + : switchedOut(false), dcachePort(this), loads(0), stores(0), + storesToWB(0), storesInFlight(0), stalled(false), isStoreBlocked(false), + isLoadBlocked(false), loadBlockedHandled(false) +{ +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::init(Params *params, unsigned maxLQEntries, + unsigned maxSQEntries, unsigned id) +{ + DPRINTF(OzoneLSQ, "Creating OzoneLWLSQ%i object.\n",id); + + lsqID = id; + + LQEntries = maxLQEntries; + SQEntries = maxSQEntries; + + for (int i = 0; i < LQEntries * 2; i++) { + LQIndices.push(i); + SQIndices.push(i); + } + + mem = params->mem; + + usedPorts = 0; + cachePorts = params->cachePorts; + + loadFaultInst = storeFaultInst = memDepViolator = NULL; + + blockedLoadSeqNum = 0; +} + +template<class Impl> +std::string +OzoneLWLSQ<Impl>::name() const +{ + return "lsqunit"; +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::regStats() +{ + lsqMemOrderViolation + .name(name() + ".memOrderViolation") + .desc("Number of memory ordering violations"); +OzoneLWLSQ<Impl>::setCPU(OzoneCPU *cpu_ptr) +{ + cpu = cpu_ptr; + dcachePort.setName(this->name() + "-dport"); + +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->setDcachePort(&dcachePort); + } +#endif +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::clearLQ() +{ + loadQueue.clear(); +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::clearSQ() +{ + storeQueue.clear(); +} +/* +template<class Impl> +void +OzoneLWLSQ<Impl>::setPageTable(PageTable *pt_ptr) +{ + DPRINTF(OzoneLSQ, "Setting the page table pointer.\n"); + pTable = pt_ptr; +} +*/ +template<class Impl> +void +OzoneLWLSQ<Impl>::resizeLQ(unsigned size) +{ + assert( size >= LQEntries); + + if (size > LQEntries) { + while (size > loadQueue.size()) { + DynInstPtr dummy; + loadQueue.push_back(dummy); + LQEntries++; + } + } else { + LQEntries = size; + } + +} + +template<class Impl> +void +OzoneLWLSQ<Impl>::resizeSQ(unsigned size) +{ + if (size > SQEntries) { + while (size > storeQueue.size()) { + SQEntry dummy; + storeQueue.push_back(dummy); + SQEntries++; + } + } else { + SQEntries = size; + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::insert(DynInstPtr &inst) +{ + // Make sure we really have a memory reference. + assert(inst->isMemRef()); + + // Make sure it's one of the two classes of memory references. + assert(inst->isLoad() || inst->isStore()); + + if (inst->isLoad()) { + insertLoad(inst); + } else { + insertStore(inst); + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::insertLoad(DynInstPtr &load_inst) +{ + assert(loads < LQEntries * 2); + assert(!LQIndices.empty()); + int load_index = LQIndices.front(); + LQIndices.pop(); + + DPRINTF(OzoneLSQ, "Inserting load PC %#x, idx:%i [sn:%lli]\n", + load_inst->readPC(), load_index, load_inst->seqNum); + + load_inst->lqIdx = load_index; + + loadQueue.push_front(load_inst); + LQItHash[load_index] = loadQueue.begin(); + + ++loads; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::insertStore(DynInstPtr &store_inst) +{ + // Make sure it is not full before inserting an instruction. + assert(stores - storesToWB < SQEntries); + + assert(!SQIndices.empty()); + int store_index = SQIndices.front(); + SQIndices.pop(); + + DPRINTF(OzoneLSQ, "Inserting store PC %#x, idx:%i [sn:%lli]\n", + store_inst->readPC(), store_index, store_inst->seqNum); + + store_inst->sqIdx = store_index; + SQEntry entry(store_inst); + if (loadQueue.empty()) { + entry.lqIt = loadQueue.end(); + } else { + entry.lqIt = loadQueue.begin(); + } + storeQueue.push_front(entry); + + SQItHash[store_index] = storeQueue.begin(); + + ++stores; +} + +template <class Impl> +typename Impl::DynInstPtr +OzoneLWLSQ<Impl>::getMemDepViolator() +{ + DynInstPtr temp = memDepViolator; + + memDepViolator = NULL; + + return temp; +} + +template <class Impl> +unsigned +OzoneLWLSQ<Impl>::numFreeEntries() +{ + unsigned free_lq_entries = LQEntries - loads; + unsigned free_sq_entries = SQEntries - (stores + storesInFlight); + + // Both the LQ and SQ entries have an extra dummy entry to differentiate + // empty/full conditions. Subtract 1 from the free entries. + if (free_lq_entries < free_sq_entries) { + return free_lq_entries - 1; + } else { + return free_sq_entries - 1; + } +} + +template <class Impl> +int +OzoneLWLSQ<Impl>::numLoadsReady() +{ + int retval = 0; + LQIt lq_it = loadQueue.begin(); + LQIt end_it = loadQueue.end(); + + while (lq_it != end_it) { + if ((*lq_it)->readyToIssue()) { + ++retval; + } + } + + return retval; +} + +template <class Impl> +Fault +OzoneLWLSQ<Impl>::executeLoad(DynInstPtr &inst) +{ + // Execute a specific load. + Fault load_fault = NoFault; + + DPRINTF(OzoneLSQ, "Executing load PC %#x, [sn:%lli]\n", + inst->readPC(),inst->seqNum); + + // Make sure it's really in the list. + // Normally it should always be in the list. However, + /* due to a syscall it may not be the list. +#ifdef DEBUG + int i = loadHead; + while (1) { + if (i == loadTail && !find(inst)) { + assert(0 && "Load not in the queue!"); + } else if (loadQueue[i] == inst) { + break; + } + + i = i + 1; + if (i >= LQEntries) { + i = 0; + } + } +#endif // DEBUG*/ + + load_fault = inst->initiateAcc(); + + // Might want to make sure that I'm not overwriting a previously faulting + // instruction that hasn't been checked yet. + // Actually probably want the oldest faulting load + if (load_fault != NoFault) { + DPRINTF(OzoneLSQ, "Load [sn:%lli] has a fault\n", inst->seqNum); + if (!(inst->req->flags & UNCACHEABLE && !inst->isAtCommit())) { + inst->setExecuted(); + } + // Maybe just set it as can commit here, although that might cause + // some other problems with sending traps to the ROB too quickly. + be->instToCommit(inst); +// iewStage->activityThisCycle(); + } + + return load_fault; +} + +template <class Impl> +Fault +OzoneLWLSQ<Impl>::executeStore(DynInstPtr &store_inst) +{ + // Make sure that a store exists. + assert(stores != 0); + + int store_idx = store_inst->sqIdx; + SQHashIt sq_hash_it = SQItHash.find(store_idx); + assert(sq_hash_it != SQItHash.end()); + DPRINTF(OzoneLSQ, "Executing store PC %#x [sn:%lli]\n", + store_inst->readPC(), store_inst->seqNum); + + SQIt sq_it = (*sq_hash_it).second; + + Fault store_fault = store_inst->initiateAcc(); + + // Store size should now be available. Use it to get proper offset for + // addr comparisons. + int size = (*sq_it).size; + + if (size == 0) { + DPRINTF(OzoneLSQ,"Fault on Store PC %#x, [sn:%lli],Size = 0\n", + store_inst->readPC(),store_inst->seqNum); + + return store_fault; + } + + assert(store_fault == NoFault); + + if (!storeFaultInst) { + if (store_fault != NoFault) { + panic("Fault in a store instruction!"); + storeFaultInst = store_inst; + } else if (store_inst->isStoreConditional()) { + // Store conditionals need to set themselves as able to + // writeback if we haven't had a fault by here. + (*sq_it).canWB = true; + + ++storesToWB; + DPRINTF(OzoneLSQ, "Nonspeculative store! storesToWB:%i\n", + storesToWB); + } + } + + LQIt lq_it = --(loadQueue.end()); + + if (!memDepViolator) { + while (lq_it != loadQueue.end()) { + if ((*lq_it)->seqNum < store_inst->seqNum) { + lq_it--; + continue; + } + // Actually should only check loads that have actually executed + // Might be safe because effAddr is set to InvalAddr when the + // dyn inst is created. + + // Must actually check all addrs in the proper size range + // Which is more correct than needs to be. What if for now we just + // assume all loads are quad-word loads, and do the addr based + // on that. + // @todo: Fix this, magic number being used here + if (((*lq_it)->effAddr >> 8) == + (store_inst->effAddr >> 8)) { + // A load incorrectly passed this store. Squash and refetch. + // For now return a fault to show that it was unsuccessful. + memDepViolator = (*lq_it); + ++lsqMemOrderViolation; + + return TheISA::genMachineCheckFault(); + } + + lq_it--; + } + + // If we've reached this point, there was no violation. + memDepViolator = NULL; + } + + return store_fault; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::commitLoad() +{ + assert(!loadQueue.empty()); + + DPRINTF(OzoneLSQ, "[sn:%lli] Committing head load instruction, PC %#x\n", + loadQueue.back()->seqNum, loadQueue.back()->readPC()); + + LQIndices.push(loadQueue.back()->lqIdx); + LQItHash.erase(loadQueue.back()->lqIdx); + + loadQueue.pop_back(); + + --loads; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::commitLoads(InstSeqNum &youngest_inst) +{ + assert(loads == 0 || !loadQueue.empty()); + + while (loads != 0 && + loadQueue.back()->seqNum <= youngest_inst) { + commitLoad(); + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::commitStores(InstSeqNum &youngest_inst) +{ + assert(stores == 0 || !storeQueue.empty()); + + SQIt sq_it = --(storeQueue.end()); + while (!storeQueue.empty() && sq_it != storeQueue.end()) { + assert((*sq_it).inst); + if (!(*sq_it).canWB) { + if ((*sq_it).inst->seqNum > youngest_inst) { + break; + } + ++storesToWB; + + DPRINTF(OzoneLSQ, "Marking store as able to write back, PC " + "%#x [sn:%lli], storesToWB:%i\n", + (*sq_it).inst->readPC(), + (*sq_it).inst->seqNum, + storesToWB); + + (*sq_it).canWB = true; + } + + sq_it--; + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::writebackStores() +{ + SQIt sq_it = --(storeQueue.end()); + while (storesToWB > 0 && + sq_it != storeQueue.end() && + (*sq_it).inst && + (*sq_it).canWB && + usedPorts < cachePorts) { + + if (isStoreBlocked) { + DPRINTF(OzoneLSQ, "Unable to write back any more stores, cache" + " is blocked!\n"); + break; + } + + DynInstPtr inst = (*sq_it).inst; + + if ((*sq_it).size == 0 && !(*sq_it).completed) { + sq_it--; + removeStore(inst->sqIdx); + completeStore(inst); + continue; + } + + if (inst->isDataPrefetch() || (*sq_it).committed) { + sq_it--; + continue; + } + + ++usedPorts; + + assert((*sq_it).req); + assert(!(*sq_it).committed); + + Request *req = (*sq_it).req; + (*sq_it).committed = true; + + assert(!inst->memData); + inst->memData = new uint8_t[64]; + memcpy(inst->memData, (uint8_t *)&(*sq_it).data, + req->getSize()); + + PacketPtr data_pkt = new Packet(req, Packet::WriteReq, Packet::Broadcast); + data_pkt->dataStatic(inst->memData); + + LSQSenderState *state = new LSQSenderState; + state->isLoad = false; + state->idx = inst->sqIdx; + state->inst = inst; + data_pkt->senderState = state; + + DPRINTF(OzoneLSQ, "D-Cache: Writing back store PC:%#x " + "to Addr:%#x, data:%#x [sn:%lli]\n", + (*sq_it).inst->readPC(), + req->getPaddr(), *(inst->memData), + inst->seqNum); + + // @todo: Remove this SC hack once the memory system handles it. + if (req->getFlags() & LOCKED) { + if (req->getFlags() & UNCACHEABLE) { + req->setScResult(2); + } else { + if (cpu->lockFlag) { + req->setScResult(1); + } else { + req->setScResult(0); + // Hack: Instantly complete this store. + completeDataAccess(data_pkt); + --sq_it; + continue; + } + } + } else { + // Non-store conditionals do not need a writeback. + state->noWB = true; + } + + if (!dcachePort.sendTiming(data_pkt)) { + // Need to handle becoming blocked on a store. + isStoreBlocked = true; + assert(retryPkt == NULL); + retryPkt = data_pkt; + } else { + storePostSend(data_pkt, inst); + --sq_it; + } +/* + DPRINTF(OzoneLSQ, "D-Cache: Writing back store idx:%i PC:%#x " + "to Addr:%#x, data:%#x [sn:%lli]\n", + inst->sqIdx,inst->readPC(), + req->paddr, *(req->data), + inst->seqNum); + DPRINTF(OzoneLSQ, "StoresInFlight: %i\n", + storesInFlight + 1); + + if (dcacheInterface) { + assert(!req->completionEvent); + StoreCompletionEvent *store_event = new + StoreCompletionEvent(inst, be, NULL, this); + req->completionEvent = store_event; + + MemAccessResult result = dcacheInterface->access(req); + + if (isStalled() && + inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load [sn:%lli]\n", + stallingStoreIsn, (*stallingLoad)->seqNum); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst((*stallingLoad)); + } + + if (result != MA_HIT && dcacheInterface->doEvents()) { + store_event->miss = true; + typename BackEnd::LdWritebackEvent *wb = NULL; + if (req->flags & LOCKED) { + wb = new typename BackEnd::LdWritebackEvent(inst, + be); + store_event->wbEvent = wb; + } + + DPRINTF(OzoneLSQ,"D-Cache Write Miss!\n"); + +// DPRINTF(Activity, "Active st accessing mem miss [sn:%lli]\n", +// inst->seqNum); + + be->addDcacheMiss(inst); + + lastDcacheStall = curTick; + + _status = DcacheMissStall; + + // Increment stat here or something + + sq_it--; + } else { + DPRINTF(OzoneLSQ,"D-Cache: Write Hit on idx:%i !\n", + inst->sqIdx); + +// DPRINTF(Activity, "Active st accessing mem hit [sn:%lli]\n", +// inst->seqNum); + + if (req->flags & LOCKED) { + // Stx_C does not generate a system port + // transaction in the 21264, but that might be + // hard to accomplish in this model. + + typename BackEnd::LdWritebackEvent *wb = + new typename BackEnd::LdWritebackEvent(inst, + be); + store_event->wbEvent = wb; + } + sq_it--; + } + ++storesInFlight; +// removeStore(inst->sqIdx); + } else { + panic("Must HAVE DCACHE!!!!!\n"); + } +*/ + } + + // Not sure this should set it to 0. + usedPorts = 0; + + assert(stores >= 0 && storesToWB >= 0); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::squash(const InstSeqNum &squashed_num) +{ + DPRINTF(OzoneLSQ, "Squashing until [sn:%lli]!" + "(Loads:%i Stores:%i)\n",squashed_num,loads,stores+storesInFlight); + + + LQIt lq_it = loadQueue.begin(); + + while (loads != 0 && (*lq_it)->seqNum > squashed_num) { + assert(!loadQueue.empty()); + // Clear the smart pointer to make sure it is decremented. + DPRINTF(OzoneLSQ,"Load Instruction PC %#x squashed, " + "[sn:%lli]\n", + (*lq_it)->readPC(), + (*lq_it)->seqNum); + + if (isStalled() && lq_it == stallingLoad) { + stalled = false; + stallingStoreIsn = 0; + stallingLoad = NULL; + } + + --loads; + + // Inefficient! + LQHashIt lq_hash_it = LQItHash.find((*lq_it)->lqIdx); + assert(lq_hash_it != LQItHash.end()); + LQItHash.erase(lq_hash_it); + LQIndices.push((*lq_it)->lqIdx); + loadQueue.erase(lq_it++); + } + + if (isLoadBlocked) { + if (squashed_num < blockedLoadSeqNum) { + isLoadBlocked = false; + loadBlockedHandled = false; + blockedLoadSeqNum = 0; + } + } + + SQIt sq_it = storeQueue.begin(); + + while (stores != 0 && (*sq_it).inst->seqNum > squashed_num) { + assert(!storeQueue.empty()); + + if ((*sq_it).canWB) { + break; + } + + // Clear the smart pointer to make sure it is decremented. + DPRINTF(OzoneLSQ,"Store Instruction PC %#x idx:%i squashed [sn:%lli]\n", + (*sq_it).inst->readPC(), (*sq_it).inst->sqIdx, + (*sq_it).inst->seqNum); + + // I don't think this can happen. It should have been cleared by the + // stalling load. + if (isStalled() && + (*sq_it).inst->seqNum == stallingStoreIsn) { + panic("Is stalled should have been cleared by stalling load!\n"); + stalled = false; + stallingStoreIsn = 0; + } + + SQHashIt sq_hash_it = SQItHash.find((*sq_it).inst->sqIdx); + assert(sq_hash_it != SQItHash.end()); + SQItHash.erase(sq_hash_it); + SQIndices.push((*sq_it).inst->sqIdx); + (*sq_it).inst = NULL; + (*sq_it).canWB = 0; + (*sq_it).req = NULL; + --stores; + storeQueue.erase(sq_it++); + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::dumpInsts() +{ + cprintf("Load store queue: Dumping instructions.\n"); + cprintf("Load queue size: %i\n", loads); + cprintf("Load queue: "); + + LQIt lq_it = --(loadQueue.end()); + + while (lq_it != loadQueue.end() && (*lq_it)) { + cprintf("[sn:%lli] %#x ", (*lq_it)->seqNum, + (*lq_it)->readPC()); + + lq_it--; + } + + cprintf("\nStore queue size: %i\n", stores); + cprintf("Store queue: "); + + SQIt sq_it = --(storeQueue.end()); + + while (sq_it != storeQueue.end() && (*sq_it).inst) { + cprintf("[sn:%lli]\nPC:%#x\nSize:%i\nCommitted:%i\nCompleted:%i\ncanWB:%i\n", + (*sq_it).inst->seqNum, + (*sq_it).inst->readPC(), + (*sq_it).size, + (*sq_it).committed, + (*sq_it).completed, + (*sq_it).canWB); + + sq_it--; + } + + cprintf("\n"); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::storePostSend(Packet *pkt, DynInstPtr &inst) +{ + if (isStalled() && + inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load [sn:%lli]\n", + stallingStoreIsn, (*stallingLoad)->seqNum); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst((*stallingLoad)); + } + + if (!inst->isStoreConditional()) { + // The store is basically completed at this time. This + // only works so long as the checker doesn't try to + // verify the value in memory for stores. + inst->setCompleted(); +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->verify(inst); + } +#endif + } + + if (pkt->result != Packet::Success) { + DPRINTF(OzoneLSQ,"D-Cache Write Miss!\n"); + + DPRINTF(Activity, "Active st accessing mem miss [sn:%lli]\n", + inst->seqNum); + + //mshrSeqNums.push_back(storeQueue[storeWBIdx].inst->seqNum); + + //DPRINTF(OzoneLWLSQ, "Added MSHR. count = %i\n",mshrSeqNums.size()); + + // @todo: Increment stat here. + } else { + DPRINTF(OzoneLSQ,"D-Cache: Write Hit!\n"); + + DPRINTF(Activity, "Active st accessing mem hit [sn:%lli]\n", + inst->seqNum); + } +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::writeback(DynInstPtr &inst, PacketPtr pkt) +{ + // Squashed instructions do not need to complete their access. + if (inst->isSquashed()) { + assert(!inst->isStore()); + return; + } + + if (!inst->isExecuted()) { + inst->setExecuted(); + + // Complete access to copy data to proper place. + inst->completeAcc(pkt); + } + + // Need to insert instruction into queue to commit + be->instToCommit(inst); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::removeStore(int store_idx) +{ + SQHashIt sq_hash_it = SQItHash.find(store_idx); + assert(sq_hash_it != SQItHash.end()); + SQIt sq_it = (*sq_hash_it).second; + + assert((*sq_it).inst); + (*sq_it).completed = true; + DynInstPtr inst = (*sq_it).inst; + + if (isStalled() && + inst->seqNum == stallingStoreIsn) { + DPRINTF(OzoneLSQ, "Unstalling, stalling store [sn:%lli] " + "load [sn:%lli]\n", + stallingStoreIsn, (*stallingLoad)->seqNum); + stalled = false; + stallingStoreIsn = 0; + be->replayMemInst((*stallingLoad)); + } + + DPRINTF(OzoneLSQ, "Completing store idx:%i [sn:%lli], storesToWB:%i\n", + inst->sqIdx, inst->seqNum, storesToWB); + + assert(!storeQueue.empty()); + SQItHash.erase(sq_hash_it); + SQIndices.push(inst->sqIdx); + storeQueue.erase(sq_it); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::completeStore(DynInstPtr &inst) +{ + --storesToWB; + --stores; + + inst->setCompleted(); +#if USE_CHECKER + if (cpu->checker) { + cpu->checker->verify(inst); + } +#endif +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::recvRetry() +{ + panic("Unimplemented!"); +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::switchOut() +{ + assert(storesToWB == 0); + switchedOut = true; + + // Clear the queue to free up resources + assert(stores == 0); + assert(storeQueue.empty()); + assert(loads == 0); + assert(loadQueue.empty()); + assert(storesInFlight == 0); + storeQueue.clear(); + loadQueue.clear(); + loads = stores = storesToWB = storesInFlight = 0; +} + +template <class Impl> +void +OzoneLWLSQ<Impl>::takeOverFrom(ThreadContext *old_tc) +{ + // Clear out any old state. May be redundant if this is the first time + // the CPU is being used. + stalled = false; + isLoadBlocked = false; + loadBlockedHandled = false; + switchedOut = false; + + // Could do simple checks here to see if indices are on twice + while (!LQIndices.empty()) + LQIndices.pop(); + while (!SQIndices.empty()) + SQIndices.pop(); + + for (int i = 0; i < LQEntries * 2; i++) { + LQIndices.push(i); + SQIndices.push(i); + } + + usedPorts = 0; + + loadFaultInst = storeFaultInst = memDepViolator = NULL; + + blockedLoadSeqNum = 0; +} diff --git a/src/cpu/ozone/null_predictor.hh b/src/cpu/ozone/null_predictor.hh new file mode 100644 index 000000000..0751338b7 --- /dev/null +++ b/src/cpu/ozone/null_predictor.hh @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_NULL_PREDICTOR_HH__ +#define __CPU_OZONE_NULL_PREDICTOR_HH__ + +#include "cpu/inst_seq.hh" +#include "sim/host.hh" + +template <class Impl> +class NullPredictor +{ + public: + typedef typename Impl::Params Params; + typedef typename Impl::DynInstPtr DynInstPtr; + + NullPredictor(Params *p) { } + + struct BPredInfo { + BPredInfo() + : PC(0), nextPC(0) + { } + + BPredInfo(const Addr &pc, const Addr &next_pc) + : PC(pc), nextPC(next_pc) + { } + + Addr PC; + Addr nextPC; + }; + + BPredInfo lookup(Addr &PC) { return BPredInfo(PC, PC+4); } + + void undo(BPredInfo &bp_info) { return; } + + /** + * Predicts whether or not the instruction is a taken branch, and the + * target of the branch if it is taken. + * @param inst The branch instruction. + * @param PC The predicted PC is passed back through this parameter. + * @param tid The thread id. + * @return Returns if the branch is taken or not. + */ + bool predict(DynInstPtr &inst, Addr &PC, unsigned tid) + { return false; } + + /** + * Tells the branch predictor to commit any updates until the given + * sequence number. + * @param done_sn The sequence number to commit any older updates up until. + * @param tid The thread id. + */ + void update(const InstSeqNum &done_sn, unsigned tid) { } + + /** + * Squashes all outstanding updates until a given sequence number. + * @param squashed_sn The sequence number to squash any younger updates up + * until. + * @param tid The thread id. + */ + void squash(const InstSeqNum &squashed_sn, unsigned tid) { } + + /** + * Squashes all outstanding updates until a given sequence number, and + * corrects that sn's update with the proper address and taken/not taken. + * @param squashed_sn The sequence number to squash any younger updates up + * until. + * @param corr_target The correct branch target. + * @param actually_taken The correct branch direction. + * @param tid The thread id. + */ + void squash(const InstSeqNum &squashed_sn, const Addr &corr_target, + bool actually_taken, unsigned tid) + { } + +}; + +#endif // __CPU_OZONE_NULL_PREDICTOR_HH__ diff --git a/src/cpu/ozone/ozone_base_dyn_inst.cc b/src/cpu/ozone/ozone_base_dyn_inst.cc new file mode 100644 index 000000000..5a3a69dff --- /dev/null +++ b/src/cpu/ozone/ozone_base_dyn_inst.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/base_dyn_inst_impl.hh" +#include "cpu/ozone/ozone_impl.hh" + +// Explicit instantiation +template class BaseDynInst<OzoneImpl>; + +template <> +int +BaseDynInst<OzoneImpl>::instcount = 0; diff --git a/src/cpu/ozone/ozone_impl.hh b/src/cpu/ozone/ozone_impl.hh new file mode 100644 index 000000000..2271cd68a --- /dev/null +++ b/src/cpu/ozone/ozone_impl.hh @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_OZONE_IMPL_HH__ +#define __CPU_OZONE_OZONE_IMPL_HH__ + +#include "cpu/o3/bpred_unit.hh" +#include "cpu/ozone/front_end.hh" +#include "cpu/ozone/inst_queue.hh" +#include "cpu/ozone/lw_lsq.hh" +#include "cpu/ozone/lw_back_end.hh" +#include "cpu/ozone/null_predictor.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/simple_params.hh" + +template <class Impl> +class OzoneCPU; + +template <class Impl> +class OzoneDynInst; + +struct OzoneImpl { + typedef SimpleParams Params; + typedef OzoneCPU<OzoneImpl> OzoneCPU; + typedef OzoneCPU CPUType; + + // Would like to put these into their own area. +// typedef NullPredictor BranchPred; + typedef BPredUnit<OzoneImpl> BranchPred; + typedef FrontEnd<OzoneImpl> FrontEnd; + // Will need IQ, LSQ eventually + typedef LWBackEnd<OzoneImpl> BackEnd; + + typedef InstQueue<OzoneImpl> InstQueue; + typedef OzoneLWLSQ<OzoneImpl> LdstQueue; + + typedef OzoneDynInst<OzoneImpl> DynInst; + typedef RefCountingPtr<DynInst> DynInstPtr; + + typedef uint64_t IssueStruct; + + enum { + MaxThreads = 1 + }; +}; + +#endif // __CPU_OZONE_OZONE_IMPL_HH__ diff --git a/src/cpu/ozone/rename_table.cc b/src/cpu/ozone/rename_table.cc new file mode 100644 index 000000000..a44054b6e --- /dev/null +++ b/src/cpu/ozone/rename_table.cc @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/ozone/rename_table_impl.hh" +#include "cpu/ozone/ozone_impl.hh" +//#include "cpu/ozone/simple_impl.hh" + +template class RenameTable<OzoneImpl>; +//template class RenameTable<SimpleImpl>; diff --git a/src/cpu/ozone/rename_table.hh b/src/cpu/ozone/rename_table.hh new file mode 100644 index 000000000..0b67d9635 --- /dev/null +++ b/src/cpu/ozone/rename_table.hh @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_RENAME_TABLE_HH__ +#define __CPU_OZONE_RENAME_TABLE_HH__ + +#include "arch/isa_traits.hh" + +/** Rename table that holds the rename of each architectural register to + * producing DynInst. Needs to support copying from one table to another. + */ + +template <class Impl> +class RenameTable { + public: + typedef typename Impl::DynInstPtr DynInstPtr; + + RenameTable(); + + void copyFrom(const RenameTable<Impl> &table_to_copy); + + DynInstPtr &operator [] (int index) + { return table[index]; } + + DynInstPtr table[TheISA::TotalNumRegs]; +}; + +#endif // __CPU_OZONE_RENAME_TABLE_HH__ diff --git a/src/cpu/ozone/rename_table_impl.hh b/src/cpu/ozone/rename_table_impl.hh new file mode 100644 index 000000000..67bab7337 --- /dev/null +++ b/src/cpu/ozone/rename_table_impl.hh @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include <cstdlib> // Not really sure what to include to get NULL +#include "cpu/ozone/rename_table.hh" + +template <class Impl> +RenameTable<Impl>::RenameTable() +{ + // Actually should set these to dummy dyn insts that have the initial value + // and force their values to be initialized. This keeps everything the + // same. + for (int i = 0; i < TheISA::TotalNumRegs; ++i) { + table[i] = NULL; + } +} + +template <class Impl> +void +RenameTable<Impl>::copyFrom(const RenameTable<Impl> &table_to_copy) +{ + for (int i = 0; i < TheISA::TotalNumRegs; ++i) { + table[i] = table_to_copy.table[i]; + } +} diff --git a/src/cpu/ozone/simple_base_dyn_inst.cc b/src/cpu/ozone/simple_base_dyn_inst.cc new file mode 100644 index 000000000..fdaeaf57e --- /dev/null +++ b/src/cpu/ozone/simple_base_dyn_inst.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/base_dyn_inst_impl.hh" +#include "cpu/ozone/simple_impl.hh" + +// Explicit instantiation +template class BaseDynInst<SimpleImpl>; + +template <> +int +BaseDynInst<SimpleImpl>::instcount = 0; diff --git a/src/cpu/ozone/simple_cpu_builder.cc b/src/cpu/ozone/simple_cpu_builder.cc new file mode 100644 index 000000000..baaf7c708 --- /dev/null +++ b/src/cpu/ozone/simple_cpu_builder.cc @@ -0,0 +1,452 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include <string> + +#include "cpu/checker/cpu.hh" +#include "cpu/inst_seq.hh" +#include "cpu/ozone/cpu_impl.hh" +#include "cpu/ozone/simple_impl.hh" +#include "cpu/ozone/simple_params.hh" +#include "mem/cache/base_cache.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/sim_object.hh" + +template +class OzoneCPU<SimpleImpl>; + +class SimpleOzoneCPU : public OzoneCPU<SimpleImpl> +{ + public: + SimpleOzoneCPU(SimpleParams *p) + : OzoneCPU<SimpleImpl>(p) + { } +}; + +//////////////////////////////////////////////////////////////////////// +// +// OzoneCPU Simulation Object +// + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleOzoneCPU) + + Param<int> clock; + Param<int> numThreads; + +#if FULL_SYSTEM +SimObjectParam<System *> system; +Param<int> cpu_id; +SimObjectParam<AlphaITB *> itb; +SimObjectParam<AlphaDTB *> dtb; +#else +SimObjectVectorParam<Process *> workload; +//SimObjectParam<PageTable *> page_table; +#endif // FULL_SYSTEM + +SimObjectParam<FunctionalMemory *> mem; + +SimObjectParam<BaseCPU *> checker; + +Param<Counter> max_insts_any_thread; +Param<Counter> max_insts_all_threads; +Param<Counter> max_loads_any_thread; +Param<Counter> max_loads_all_threads; + +SimObjectParam<BaseCache *> icache; +SimObjectParam<BaseCache *> dcache; + +Param<unsigned> cachePorts; +Param<unsigned> width; +Param<unsigned> frontEndWidth; +Param<unsigned> backEndWidth; +Param<unsigned> backEndSquashLatency; +Param<unsigned> backEndLatency; +Param<unsigned> maxInstBufferSize; +Param<unsigned> numPhysicalRegs; + +Param<unsigned> decodeToFetchDelay; +Param<unsigned> renameToFetchDelay; +Param<unsigned> iewToFetchDelay; +Param<unsigned> commitToFetchDelay; +Param<unsigned> fetchWidth; + +Param<unsigned> renameToDecodeDelay; +Param<unsigned> iewToDecodeDelay; +Param<unsigned> commitToDecodeDelay; +Param<unsigned> fetchToDecodeDelay; +Param<unsigned> decodeWidth; + +Param<unsigned> iewToRenameDelay; +Param<unsigned> commitToRenameDelay; +Param<unsigned> decodeToRenameDelay; +Param<unsigned> renameWidth; + +Param<unsigned> commitToIEWDelay; +Param<unsigned> renameToIEWDelay; +Param<unsigned> issueToExecuteDelay; +Param<unsigned> issueWidth; +Param<unsigned> executeWidth; +Param<unsigned> executeIntWidth; +Param<unsigned> executeFloatWidth; +Param<unsigned> executeBranchWidth; +Param<unsigned> executeMemoryWidth; + +Param<unsigned> iewToCommitDelay; +Param<unsigned> renameToROBDelay; +Param<unsigned> commitWidth; +Param<unsigned> squashWidth; + +Param<std::string> predType; +Param<unsigned> localPredictorSize; +Param<unsigned> localCtrBits; +Param<unsigned> localHistoryTableSize; +Param<unsigned> localHistoryBits; +Param<unsigned> globalPredictorSize; +Param<unsigned> globalCtrBits; +Param<unsigned> globalHistoryBits; +Param<unsigned> choicePredictorSize; +Param<unsigned> choiceCtrBits; + +Param<unsigned> BTBEntries; +Param<unsigned> BTBTagSize; + +Param<unsigned> RASSize; + +Param<unsigned> LQEntries; +Param<unsigned> SQEntries; +Param<unsigned> LFSTSize; +Param<unsigned> SSITSize; + +Param<unsigned> numPhysIntRegs; +Param<unsigned> numPhysFloatRegs; +Param<unsigned> numIQEntries; +Param<unsigned> numROBEntries; + +Param<bool> decoupledFrontEnd; +Param<int> dispatchWidth; +Param<int> wbWidth; + +Param<unsigned> smtNumFetchingThreads; +Param<std::string> smtFetchPolicy; +Param<std::string> smtLSQPolicy; +Param<unsigned> smtLSQThreshold; +Param<std::string> smtIQPolicy; +Param<unsigned> smtIQThreshold; +Param<std::string> smtROBPolicy; +Param<unsigned> smtROBThreshold; +Param<std::string> smtCommitPolicy; + +Param<unsigned> instShiftAmt; + +Param<bool> defer_registration; + +Param<bool> function_trace; +Param<Tick> function_trace_start; + +END_DECLARE_SIM_OBJECT_PARAMS(SimpleOzoneCPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleOzoneCPU) + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(numThreads, "number of HW thread contexts"), + +#if FULL_SYSTEM + INIT_PARAM(system, "System object"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(itb, "Instruction translation buffer"), + INIT_PARAM(dtb, "Data translation buffer"), +#else + INIT_PARAM(workload, "Processes to run"), +// INIT_PARAM(page_table, "Page table"), +#endif // FULL_SYSTEM + + INIT_PARAM_DFLT(mem, "Memory", NULL), + + INIT_PARAM_DFLT(checker, "Checker CPU", NULL), + + INIT_PARAM_DFLT(max_insts_any_thread, + "Terminate when any thread reaches this inst count", + 0), + INIT_PARAM_DFLT(max_insts_all_threads, + "Terminate when all threads have reached" + "this inst count", + 0), + INIT_PARAM_DFLT(max_loads_any_thread, + "Terminate when any thread reaches this load count", + 0), + INIT_PARAM_DFLT(max_loads_all_threads, + "Terminate when all threads have reached this load" + "count", + 0), + + INIT_PARAM_DFLT(icache, "L1 instruction cache", NULL), + INIT_PARAM_DFLT(dcache, "L1 data cache", NULL), + + INIT_PARAM_DFLT(cachePorts, "Cache Ports", 200), + INIT_PARAM_DFLT(width, "Width", 1), + INIT_PARAM_DFLT(frontEndWidth, "Front end width", 1), + INIT_PARAM_DFLT(backEndWidth, "Back end width", 1), + INIT_PARAM_DFLT(backEndSquashLatency, "Back end squash latency", 1), + INIT_PARAM_DFLT(backEndLatency, "Back end latency", 1), + INIT_PARAM_DFLT(maxInstBufferSize, "Maximum instruction buffer size", 16), + INIT_PARAM(numPhysicalRegs, "Number of physical registers"), + + INIT_PARAM(decodeToFetchDelay, "Decode to fetch delay"), + INIT_PARAM(renameToFetchDelay, "Rename to fetch delay"), + INIT_PARAM(iewToFetchDelay, "Issue/Execute/Writeback to fetch" + "delay"), + INIT_PARAM(commitToFetchDelay, "Commit to fetch delay"), + INIT_PARAM(fetchWidth, "Fetch width"), + INIT_PARAM(renameToDecodeDelay, "Rename to decode delay"), + INIT_PARAM(iewToDecodeDelay, "Issue/Execute/Writeback to decode" + "delay"), + INIT_PARAM(commitToDecodeDelay, "Commit to decode delay"), + INIT_PARAM(fetchToDecodeDelay, "Fetch to decode delay"), + INIT_PARAM(decodeWidth, "Decode width"), + + INIT_PARAM(iewToRenameDelay, "Issue/Execute/Writeback to rename" + "delay"), + INIT_PARAM(commitToRenameDelay, "Commit to rename delay"), + INIT_PARAM(decodeToRenameDelay, "Decode to rename delay"), + INIT_PARAM(renameWidth, "Rename width"), + + INIT_PARAM(commitToIEWDelay, "Commit to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(renameToIEWDelay, "Rename to " + "Issue/Execute/Writeback delay"), + INIT_PARAM(issueToExecuteDelay, "Issue to execute delay (internal" + "to the IEW stage)"), + INIT_PARAM(issueWidth, "Issue width"), + INIT_PARAM(executeWidth, "Execute width"), + INIT_PARAM(executeIntWidth, "Integer execute width"), + INIT_PARAM(executeFloatWidth, "Floating point execute width"), + INIT_PARAM(executeBranchWidth, "Branch execute width"), + INIT_PARAM(executeMemoryWidth, "Memory execute width"), + + INIT_PARAM(iewToCommitDelay, "Issue/Execute/Writeback to commit " + "delay"), + INIT_PARAM(renameToROBDelay, "Rename to reorder buffer delay"), + INIT_PARAM(commitWidth, "Commit width"), + INIT_PARAM(squashWidth, "Squash width"), + + INIT_PARAM(predType, "Type of branch predictor ('local', 'tournament')"), + INIT_PARAM(localPredictorSize, "Size of local predictor"), + INIT_PARAM(localCtrBits, "Bits per counter"), + INIT_PARAM(localHistoryTableSize, "Size of local history table"), + INIT_PARAM(localHistoryBits, "Bits for the local history"), + INIT_PARAM(globalPredictorSize, "Size of global predictor"), + INIT_PARAM(globalCtrBits, "Bits per counter"), + INIT_PARAM(globalHistoryBits, "Bits of history"), + INIT_PARAM(choicePredictorSize, "Size of choice predictor"), + INIT_PARAM(choiceCtrBits, "Bits of choice counters"), + + INIT_PARAM(BTBEntries, "Number of BTB entries"), + INIT_PARAM(BTBTagSize, "Size of the BTB tags, in bits"), + + INIT_PARAM(RASSize, "RAS size"), + + INIT_PARAM(LQEntries, "Number of load queue entries"), + INIT_PARAM(SQEntries, "Number of store queue entries"), + INIT_PARAM(LFSTSize, "Last fetched store table size"), + INIT_PARAM(SSITSize, "Store set ID table size"), + + INIT_PARAM(numPhysIntRegs, "Number of physical integer registers"), + INIT_PARAM(numPhysFloatRegs, "Number of physical floating point " + "registers"), + INIT_PARAM(numIQEntries, "Number of instruction queue entries"), + INIT_PARAM(numROBEntries, "Number of reorder buffer entries"), + + INIT_PARAM_DFLT(decoupledFrontEnd, "Decoupled front end", true), + INIT_PARAM_DFLT(dispatchWidth, "Dispatch width", 0), + INIT_PARAM_DFLT(wbWidth, "Writeback width", 0), + + INIT_PARAM_DFLT(smtNumFetchingThreads, "SMT Number of Fetching Threads", 1), + INIT_PARAM_DFLT(smtFetchPolicy, "SMT Fetch Policy", "SingleThread"), + INIT_PARAM_DFLT(smtLSQPolicy, "SMT LSQ Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtLSQThreshold,"SMT LSQ Threshold", 100), + INIT_PARAM_DFLT(smtIQPolicy, "SMT IQ Policy", "Partitioned"), + INIT_PARAM_DFLT(smtIQThreshold, "SMT IQ Threshold", 100), + INIT_PARAM_DFLT(smtROBPolicy, "SMT ROB Sharing Policy", "Partitioned"), + INIT_PARAM_DFLT(smtROBThreshold,"SMT ROB Threshold", 100), + INIT_PARAM_DFLT(smtCommitPolicy,"SMT Commit Fetch Policy", "RoundRobin"), + + INIT_PARAM(instShiftAmt, "Number of bits to shift instructions by"), + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace") + +END_INIT_SIM_OBJECT_PARAMS(SimpleOzoneCPU) + +CREATE_SIM_OBJECT(SimpleOzoneCPU) +{ + SimpleOzoneCPU *cpu; + +#if FULL_SYSTEM + // Full-system only supports a single thread for the moment. + int actual_num_threads = 1; +#else + // In non-full-system mode, we infer the number of threads from + // the workload if it's not explicitly specified. + int actual_num_threads = + numThreads.isValid() ? numThreads : workload.size(); + + if (workload.size() == 0) { + fatal("Must specify at least one workload!"); + } + +#endif + + SimpleParams *params = new SimpleParams; + + params->clock = clock; + + params->name = getInstanceName(); + params->numberOfThreads = actual_num_threads; + +#if FULL_SYSTEM + params->system = system; + params->cpu_id = cpu_id; + params->itb = itb; + params->dtb = dtb; +#else + params->workload = workload; +// params->pTable = page_table; +#endif // FULL_SYSTEM + + params->mem = mem; + params->checker = checker; + params->max_insts_any_thread = max_insts_any_thread; + params->max_insts_all_threads = max_insts_all_threads; + params->max_loads_any_thread = max_loads_any_thread; + params->max_loads_all_threads = max_loads_all_threads; + + // + // Caches + // + params->icacheInterface = icache ? icache->getInterface() : NULL; + params->dcacheInterface = dcache ? dcache->getInterface() : NULL; + params->cachePorts = cachePorts; + + params->width = width; + params->frontEndWidth = frontEndWidth; + params->backEndWidth = backEndWidth; + params->backEndSquashLatency = backEndSquashLatency; + params->backEndLatency = backEndLatency; + params->maxInstBufferSize = maxInstBufferSize; + params->numPhysicalRegs = numPhysIntRegs + numPhysFloatRegs; + + params->decodeToFetchDelay = decodeToFetchDelay; + params->renameToFetchDelay = renameToFetchDelay; + params->iewToFetchDelay = iewToFetchDelay; + params->commitToFetchDelay = commitToFetchDelay; + params->fetchWidth = fetchWidth; + + params->renameToDecodeDelay = renameToDecodeDelay; + params->iewToDecodeDelay = iewToDecodeDelay; + params->commitToDecodeDelay = commitToDecodeDelay; + params->fetchToDecodeDelay = fetchToDecodeDelay; + params->decodeWidth = decodeWidth; + + params->iewToRenameDelay = iewToRenameDelay; + params->commitToRenameDelay = commitToRenameDelay; + params->decodeToRenameDelay = decodeToRenameDelay; + params->renameWidth = renameWidth; + + params->commitToIEWDelay = commitToIEWDelay; + params->renameToIEWDelay = renameToIEWDelay; + params->issueToExecuteDelay = issueToExecuteDelay; + params->issueWidth = issueWidth; + params->executeWidth = executeWidth; + params->executeIntWidth = executeIntWidth; + params->executeFloatWidth = executeFloatWidth; + params->executeBranchWidth = executeBranchWidth; + params->executeMemoryWidth = executeMemoryWidth; + + params->iewToCommitDelay = iewToCommitDelay; + params->renameToROBDelay = renameToROBDelay; + params->commitWidth = commitWidth; + params->squashWidth = squashWidth; + + params->predType = predType; + params->localPredictorSize = localPredictorSize; + params->localCtrBits = localCtrBits; + params->localHistoryTableSize = localHistoryTableSize; + params->localHistoryBits = localHistoryBits; + params->globalPredictorSize = globalPredictorSize; + params->globalCtrBits = globalCtrBits; + params->globalHistoryBits = globalHistoryBits; + params->choicePredictorSize = choicePredictorSize; + params->choiceCtrBits = choiceCtrBits; + + params->BTBEntries = BTBEntries; + params->BTBTagSize = BTBTagSize; + + params->RASSize = RASSize; + + params->LQEntries = LQEntries; + params->SQEntries = SQEntries; + + params->SSITSize = SSITSize; + params->LFSTSize = LFSTSize; + + params->numPhysIntRegs = numPhysIntRegs; + params->numPhysFloatRegs = numPhysFloatRegs; + params->numIQEntries = numIQEntries; + params->numROBEntries = numROBEntries; + + params->decoupledFrontEnd = decoupledFrontEnd; + params->dispatchWidth = dispatchWidth; + params->wbWidth = wbWidth; + + params->smtNumFetchingThreads = smtNumFetchingThreads; + params->smtFetchPolicy = smtFetchPolicy; + params->smtIQPolicy = smtIQPolicy; + params->smtLSQPolicy = smtLSQPolicy; + params->smtLSQThreshold = smtLSQThreshold; + params->smtROBPolicy = smtROBPolicy; + params->smtROBThreshold = smtROBThreshold; + params->smtCommitPolicy = smtCommitPolicy; + + params->instShiftAmt = 2; + + params->deferRegistration = defer_registration; + + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + + cpu = new SimpleOzoneCPU(params); + + return cpu; +} + +REGISTER_SIM_OBJECT("SimpleOzoneCPU", SimpleOzoneCPU) + diff --git a/src/cpu/ozone/simple_impl.hh b/src/cpu/ozone/simple_impl.hh new file mode 100644 index 000000000..42002180b --- /dev/null +++ b/src/cpu/ozone/simple_impl.hh @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_SIMPLE_IMPL_HH__ +#define __CPU_OZONE_SIMPLE_IMPL_HH__ + +#include "cpu/o3/bpred_unit.hh" +#include "cpu/ozone/cpu.hh" +#include "cpu/ozone/front_end.hh" +#include "cpu/ozone/inorder_back_end.hh" +#include "cpu/ozone/null_predictor.hh" +#include "cpu/ozone/dyn_inst.hh" +#include "cpu/ozone/simple_params.hh" + +//template <class Impl> +//class OzoneCPU; + +template <class Impl> +class OzoneDynInst; + +struct SimpleImpl { + typedef SimpleParams Params; + typedef OzoneCPU<SimpleImpl> OzoneCPU; + typedef OzoneCPU FullCPU; + + // Would like to put these into their own area. +// typedef NullPredictor BranchPred; + typedef BPredUnit<SimpleImpl> BranchPred; + typedef FrontEnd<SimpleImpl> FrontEnd; + // Will need IQ, LSQ eventually + typedef InorderBackEnd<SimpleImpl> BackEnd; + + typedef OzoneDynInst<SimpleImpl> DynInst; + typedef RefCountingPtr<DynInst> DynInstPtr; + + typedef uint64_t IssueStruct; + + enum { + MaxThreads = 1 + }; +}; + +#endif // __CPU_OZONE_SIMPLE_IMPL_HH__ diff --git a/src/cpu/ozone/simple_params.hh b/src/cpu/ozone/simple_params.hh new file mode 100644 index 000000000..3f63d2e1d --- /dev/null +++ b/src/cpu/ozone/simple_params.hh @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_SIMPLE_PARAMS_HH__ +#define __CPU_OZONE_SIMPLE_PARAMS_HH__ + +#include "cpu/ozone/cpu.hh" + +//Forward declarations +class AlphaDTB; +class AlphaITB; +class FUPool; +class MemObject; +class PageTable; +class Process; +class System; + +/** + * This file defines the parameters that will be used for the OzoneCPU. + * This must be defined externally so that the Impl can have a params class + * defined that it can pass to all of the individual stages. + */ + +class SimpleParams : public BaseCPU::Params +{ + public: + +#if FULL_SYSTEM + AlphaITB *itb; AlphaDTB *dtb; +#else + std::vector<Process *> workload; +#endif // FULL_SYSTEM + + //Page Table + PageTable *pTable; + + MemObject *mem; + + // + // Caches + // +// MemInterface *icacheInterface; +// MemInterface *dcacheInterface; + + unsigned cachePorts; + unsigned width; + unsigned frontEndLatency; + unsigned frontEndWidth; + unsigned backEndLatency; + unsigned backEndWidth; + unsigned backEndSquashLatency; + unsigned maxInstBufferSize; + unsigned numPhysicalRegs; + unsigned maxOutstandingMemOps; + // + // Fetch + // + unsigned decodeToFetchDelay; + unsigned renameToFetchDelay; + unsigned iewToFetchDelay; + unsigned commitToFetchDelay; + unsigned fetchWidth; + + // + // Decode + // + unsigned renameToDecodeDelay; + unsigned iewToDecodeDelay; + unsigned commitToDecodeDelay; + unsigned fetchToDecodeDelay; + unsigned decodeWidth; + + // + // Rename + // + unsigned iewToRenameDelay; + unsigned commitToRenameDelay; + unsigned decodeToRenameDelay; + unsigned renameWidth; + + // + // IEW + // + unsigned commitToIEWDelay; + unsigned renameToIEWDelay; + unsigned issueToExecuteDelay; + unsigned issueWidth; + unsigned executeWidth; + unsigned executeIntWidth; + unsigned executeFloatWidth; + unsigned executeBranchWidth; + unsigned executeMemoryWidth; + FUPool *fuPool; + + // + // Commit + // + unsigned iewToCommitDelay; + unsigned renameToROBDelay; + unsigned commitWidth; + unsigned squashWidth; + + // + // Branch predictor (BP & BTB) + // + std::string predType; + unsigned localPredictorSize; + unsigned localCtrBits; + unsigned localHistoryTableSize; + unsigned localHistoryBits; + unsigned globalPredictorSize; + unsigned globalCtrBits; + unsigned globalHistoryBits; + unsigned choicePredictorSize; + unsigned choiceCtrBits; + + unsigned BTBEntries; + unsigned BTBTagSize; + + unsigned RASSize; + + // + // Load store queue + // + unsigned LQEntries; + unsigned SQEntries; + bool lsqLimits; + + // + // Memory dependence + // + unsigned SSITSize; + unsigned LFSTSize; + + // + // Miscellaneous + // + unsigned numPhysIntRegs; + unsigned numPhysFloatRegs; + unsigned numIQEntries; + unsigned numROBEntries; + + bool decoupledFrontEnd; + int dispatchWidth; + int wbWidth; + + //SMT Parameters + unsigned smtNumFetchingThreads; + + std::string smtFetchPolicy; + + std::string smtIQPolicy; + unsigned smtIQThreshold; + + std::string smtLSQPolicy; + unsigned smtLSQThreshold; + + std::string smtCommitPolicy; + + std::string smtROBPolicy; + unsigned smtROBThreshold; + + // Probably can get this from somewhere. + unsigned instShiftAmt; +}; + +#endif // __CPU_OZONE_SIMPLE_PARAMS_HH__ diff --git a/src/cpu/ozone/thread_state.hh b/src/cpu/ozone/thread_state.hh new file mode 100644 index 000000000..adaa8e71b --- /dev/null +++ b/src/cpu/ozone/thread_state.hh @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_OZONE_THREAD_STATE_HH__ +#define __CPU_OZONE_THREAD_STATE_HH__ + +#include "arch/faults.hh" +#include "arch/types.hh" +#include "arch/regfile.hh" +#include "base/callback.hh" +#include "base/output.hh" +#include "cpu/thread_context.hh" +#include "cpu/thread_state.hh" +#include "sim/process.hh" +#include "sim/sim_exit.hh" + +class Event; +//class Process; + +#if FULL_SYSTEM +class EndQuiesceEvent; +class FunctionProfile; +class ProfileNode; +#else +class Process; +class FunctionalMemory; +#endif + +// Maybe this ozone thread state should only really have committed state? +// I need to think about why I'm using this and what it's useful for. Clearly +// has benefits for SMT; basically serves same use as SimpleThread. +// Makes the ExecContext proxy easier. Gives organization/central access point +// to state of a thread that can be accessed normally (i.e. not in-flight +// stuff within a OoO processor). Does this need an TC proxy within it? +template <class Impl> +struct OzoneThreadState : public ThreadState { + typedef typename ThreadContext::Status Status; + typedef typename Impl::CPUType CPUType; + typedef TheISA::MiscReg MiscReg; + +#if FULL_SYSTEM + OzoneThreadState(CPUType *_cpu, int _thread_num) + : ThreadState(-1, _thread_num), + cpu(_cpu), intrflag(0), inSyscall(0), trapPending(0) + { + if (cpu->params->profile) { + profile = new FunctionProfile(cpu->params->system->kernelSymtab); + Callback *cb = + new MakeCallback<OzoneThreadState, + &OzoneThreadState::dumpFuncProfile>(this); + registerExitCallback(cb); + } + + // let's fill with a dummy node for now so we don't get a segfault + // on the first cycle when there's no node available. + static ProfileNode dummyNode; + profileNode = &dummyNode; + profilePC = 3; + miscRegFile.clear(); + } +#else + OzoneThreadState(CPUType *_cpu, int _thread_num, Process *_process, + int _asid, MemObject *mem) + : ThreadState(-1, _thread_num, _process, _asid, mem), + cpu(_cpu), inSyscall(0), trapPending(0) + { + miscRegFile.clear(); + } +#endif + + RenameTable<Impl> renameTable; + + Addr PC; + + Addr nextPC; + + TheISA::MiscRegFile miscRegFile; + + int intrflag; + + typename Impl::CPUType *cpu; + + bool inSyscall; + + bool trapPending; + + ThreadContext *tc; + + ThreadContext *getTC() { return tc; } + + MiscReg readMiscReg(int misc_reg) + { + return miscRegFile.readReg(misc_reg); + } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { + return miscRegFile.readRegWithEffect(misc_reg, fault, tc); + } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + return miscRegFile.setReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + return miscRegFile.setRegWithEffect(misc_reg, val, tc); + } + + uint64_t readPC() + { return PC; } + + void setPC(uint64_t val) + { PC = val; } + + uint64_t readNextPC() + { return nextPC; } + + void setNextPC(uint64_t val) + { nextPC = val; } + +#if FULL_SYSTEM + void dumpFuncProfile() + { + std::ostream *os = simout.create(csprintf("profile.%s.dat", cpu->name())); + profile->dump(xcProxy, *os); + } +#endif +}; + +#endif // __CPU_OZONE_THREAD_STATE_HH__ diff --git a/src/cpu/pc_event.cc b/src/cpu/pc_event.cc new file mode 100644 index 000000000..fca357fe3 --- /dev/null +++ b/src/cpu/pc_event.cc @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#include <algorithm> +#include <map> +#include <string> +#include <utility> + +#include "base/trace.hh" +#include "config/full_system.hh" +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "cpu/pc_event.hh" +#include "sim/debug.hh" +#include "sim/root.hh" +#include "sim/system.hh" + +using namespace std; + +PCEventQueue::PCEventQueue() +{} + +PCEventQueue::~PCEventQueue() +{} + +bool +PCEventQueue::remove(PCEvent *event) +{ + int removed = 0; + range_t range = equal_range(event); + for (iterator i = range.first; i != range.second; ++i) { + if (*i == event) { + DPRINTF(PCEvent, "PC based event removed at %#x: %s\n", + event->pc(), event->descr()); + pc_map.erase(i); + ++removed; + } + } + + return removed > 0; +} + +bool +PCEventQueue::schedule(PCEvent *event) +{ + pc_map.push_back(event); + sort(pc_map.begin(), pc_map.end(), MapCompare()); + + DPRINTF(PCEvent, "PC based event scheduled for %#x: %s\n", + event->pc(), event->descr()); + + return true; +} + +bool +PCEventQueue::doService(ThreadContext *tc) +{ + Addr pc = tc->readPC() & ~0x3; + int serviced = 0; + range_t range = equal_range(pc); + for (iterator i = range.first; i != range.second; ++i) { + // Make sure that the pc wasn't changed as the side effect of + // another event. This for example, prevents two invocations + // of the SkipFuncEvent. Maybe we should have separate PC + // event queues for each processor? + if (pc != (tc->readPC() & ~0x3)) + continue; + + DPRINTF(PCEvent, "PC based event serviced at %#x: %s\n", + (*i)->pc(), (*i)->descr()); + + (*i)->process(tc); + ++serviced; + } + + return serviced > 0; +} + +void +PCEventQueue::dump() const +{ + const_iterator i = pc_map.begin(); + const_iterator e = pc_map.end(); + + for (; i != e; ++i) + cprintf("%d: event at %#x: %s\n", curTick, (*i)->pc(), + (*i)->descr()); +} + +PCEventQueue::range_t +PCEventQueue::equal_range(Addr pc) +{ + return std::equal_range(pc_map.begin(), pc_map.end(), pc, MapCompare()); +} + +BreakPCEvent::BreakPCEvent(PCEventQueue *q, const std::string &desc, Addr addr, + bool del) + : PCEvent(q, desc, addr), remove(del) +{ +} + +void +BreakPCEvent::process(ThreadContext *tc) +{ + StringWrap name(tc->getCpuPtr()->name() + ".break_event"); + DPRINTFN("break event %s triggered\n", descr()); + debug_break(); + if (remove) + delete this; +} + +#if FULL_SYSTEM +extern "C" +void +sched_break_pc_sys(System *sys, Addr addr) +{ + new BreakPCEvent(&sys->pcEventQueue, "debug break", addr, true); +} + +extern "C" +void +sched_break_pc(Addr addr) +{ + for (vector<System *>::iterator sysi = System::systemList.begin(); + sysi != System::systemList.end(); ++sysi) { + sched_break_pc_sys(*sysi, addr); + } + +} +#endif diff --git a/src/cpu/pc_event.hh b/src/cpu/pc_event.hh new file mode 100644 index 000000000..6b048b2c2 --- /dev/null +++ b/src/cpu/pc_event.hh @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __PC_EVENT_HH__ +#define __PC_EVENT_HH__ + +#include <vector> + +#include "base/misc.hh" + +class ThreadContext; +class PCEventQueue; + +class PCEvent +{ + protected: + std::string description; + PCEventQueue *queue; + Addr evpc; + + public: + PCEvent(PCEventQueue *q, const std::string &desc, Addr pc); + + virtual ~PCEvent() { if (queue) remove(); } + + // for DPRINTF + virtual const std::string name() const { return description; } + + std::string descr() const { return description; } + Addr pc() const { return evpc; } + + bool remove(); + virtual void process(ThreadContext *tc) = 0; +}; + +class PCEventQueue +{ + protected: + typedef PCEvent * record_t; + class MapCompare { + public: + bool operator()(const record_t &l, const record_t &r) const { + return l->pc() < r->pc(); + } + bool operator()(const record_t &l, Addr pc) const { + return l->pc() < pc; + } + bool operator()(Addr pc, const record_t &r) const { + return pc < r->pc(); + } + }; + typedef std::vector<record_t> map_t; + + public: + typedef map_t::iterator iterator; + typedef map_t::const_iterator const_iterator; + + protected: + typedef std::pair<iterator, iterator> range_t; + typedef std::pair<const_iterator, const_iterator> const_range_t; + + protected: + map_t pc_map; + + bool doService(ThreadContext *tc); + + public: + PCEventQueue(); + ~PCEventQueue(); + + bool remove(PCEvent *event); + bool schedule(PCEvent *event); + bool service(ThreadContext *tc) + { + if (pc_map.empty()) + return false; + + return doService(tc); + } + + range_t equal_range(Addr pc); + range_t equal_range(PCEvent *event) { return equal_range(event->pc()); } + + void dump() const; +}; + + +inline +PCEvent::PCEvent(PCEventQueue *q, const std::string &desc, Addr pc) + : description(desc), queue(q), evpc(pc) +{ + queue->schedule(this); +} + +inline bool +PCEvent::remove() +{ + if (!queue) + panic("cannot remove an uninitialized event;"); + + return queue->remove(this); +} + +class BreakPCEvent : public PCEvent +{ + protected: + bool remove; + + public: + BreakPCEvent(PCEventQueue *q, const std::string &desc, Addr addr, + bool del = false); + virtual void process(ThreadContext *tc); +}; + +#endif // __PC_EVENT_HH__ diff --git a/src/cpu/profile.cc b/src/cpu/profile.cc new file mode 100644 index 000000000..4f04615e9 --- /dev/null +++ b/src/cpu/profile.cc @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <string> + +#include "base/bitfield.hh" +#include "base/callback.hh" +#include "base/statistics.hh" +#include "base/trace.hh" +#include "base/loader/symtab.hh" +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "cpu/profile.hh" + +using namespace std; + +ProfileNode::ProfileNode() + : count(0) +{ } + +void +ProfileNode::dump(const string &symbol, uint64_t id, const SymbolTable *symtab, + ostream &os) const +{ + ccprintf(os, "%#x %s %d ", id, symbol, count); + ChildList::const_iterator i, end = children.end(); + for (i = children.begin(); i != end; ++i) { + const ProfileNode *node = i->second; + ccprintf(os, "%#x ", (intptr_t)node); + } + + ccprintf(os, "\n"); + + for (i = children.begin(); i != end; ++i) { + Addr addr = i->first; + string symbol; + if (addr == 1) + symbol = "user"; + else if (addr == 2) + symbol = "console"; + else if (addr == 3) + symbol = "unknown"; + else if (!symtab->findSymbol(addr, symbol)) + panic("could not find symbol for address %#x\n", addr); + + const ProfileNode *node = i->second; + node->dump(symbol, (intptr_t)node, symtab, os); + } +} + +void +ProfileNode::clear() +{ + count = 0; + ChildList::iterator i, end = children.end(); + for (i = children.begin(); i != end; ++i) + i->second->clear(); +} + +FunctionProfile::FunctionProfile(const SymbolTable *_symtab) + : reset(0), symtab(_symtab) +{ + reset = new MakeCallback<FunctionProfile, &FunctionProfile::clear>(this); + Stats::registerResetCallback(reset); +} + +FunctionProfile::~FunctionProfile() +{ + if (reset) + delete reset; +} + +ProfileNode * +FunctionProfile::consume(const vector<Addr> &stack) +{ + ProfileNode *current = ⊤ + for (int i = 0, size = stack.size(); i < size; ++i) { + ProfileNode *&ptr = current->children[stack[size - i - 1]]; + if (ptr == NULL) + ptr = new ProfileNode; + + current = ptr; + } + + return current; +} + +void +FunctionProfile::clear() +{ + top.clear(); + pc_count.clear(); +} + +void +FunctionProfile::dump(ThreadContext *tc, ostream &os) const +{ + ccprintf(os, ">>>PC data\n"); + map<Addr, Counter>::const_iterator i, end = pc_count.end(); + for (i = pc_count.begin(); i != end; ++i) { + Addr pc = i->first; + Counter count = i->second; + + std::string symbol; + if (pc == 1) + ccprintf(os, "user %d\n", count); + else if (symtab->findSymbol(pc, symbol) && !symbol.empty()) + ccprintf(os, "%s %d\n", symbol, count); + else + ccprintf(os, "%#x %d\n", pc, count); + } + + ccprintf(os, ">>>function data\n"); + top.dump("top", 0, symtab, os); +} + +void +FunctionProfile::sample(ProfileNode *node, Addr pc) +{ + node->count++; + + Addr symaddr; + if (symtab->findNearestAddr(pc, symaddr)) { + pc_count[symaddr]++; + } else { + // record PC even if we don't have a symbol to avoid + // silently biasing the histogram + pc_count[pc]++; + } +} diff --git a/src/cpu/profile.hh b/src/cpu/profile.hh new file mode 100644 index 000000000..7f9625241 --- /dev/null +++ b/src/cpu/profile.hh @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __CPU_PROFILE_HH__ +#define __CPU_PROFILE_HH__ + +#include <map> + +#include "cpu/static_inst.hh" +#include "sim/host.hh" +#include "arch/stacktrace.hh" + +class ThreadContext; + +class ProfileNode +{ + private: + friend class FunctionProfile; + + typedef std::map<Addr, ProfileNode *> ChildList; + ChildList children; + + public: + Counter count; + + public: + ProfileNode(); + + void dump(const std::string &symbol, uint64_t id, + const SymbolTable *symtab, std::ostream &os) const; + void clear(); +}; + +class Callback; +class FunctionProfile +{ + private: + Callback *reset; + const SymbolTable *symtab; + ProfileNode top; + std::map<Addr, Counter> pc_count; + StackTrace trace; + + public: + FunctionProfile(const SymbolTable *symtab); + ~FunctionProfile(); + + ProfileNode *consume(ThreadContext *tc, StaticInstPtr inst); + ProfileNode *consume(const std::vector<Addr> &stack); + void clear(); + void dump(ThreadContext *tc, std::ostream &out) const; + void sample(ProfileNode *node, Addr pc); +}; + +inline ProfileNode * +FunctionProfile::consume(ThreadContext *tc, StaticInstPtr inst) +{ + if (!trace.trace(tc, inst)) + return NULL; + trace.dprintf(); + return consume(trace.getstack()); +} + +#endif // __CPU_PROFILE_HH__ diff --git a/src/cpu/quiesce_event.cc b/src/cpu/quiesce_event.cc new file mode 100644 index 000000000..8dd20db02 --- /dev/null +++ b/src/cpu/quiesce_event.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "cpu/thread_context.hh" +#include "cpu/quiesce_event.hh" + +EndQuiesceEvent::EndQuiesceEvent(ThreadContext *_tc) + : Event(&mainEventQueue), tc(_tc) +{ +} + +void +EndQuiesceEvent::process() +{ + tc->activate(); +} + +const char* +EndQuiesceEvent::description() +{ + return "End Quiesce Event."; +} diff --git a/src/cpu/quiesce_event.hh b/src/cpu/quiesce_event.hh new file mode 100644 index 000000000..3de40f97e --- /dev/null +++ b/src/cpu/quiesce_event.hh @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_QUIESCE_EVENT_HH__ +#define __CPU_QUIESCE_EVENT_HH__ + +#include "sim/eventq.hh" + +class ThreadContext; + +/** Event for timing out quiesce instruction */ +struct EndQuiesceEvent : public Event +{ + /** A pointer to the thread context that is quiesced */ + ThreadContext *tc; + + EndQuiesceEvent(ThreadContext *_tc); + + /** Event process to occur at interrupt*/ + virtual void process(); + + /** Event description */ + virtual const char *description(); +}; + +#endif // __CPU_QUIESCE_EVENT_HH__ diff --git a/src/cpu/simple/atomic.cc b/src/cpu/simple/atomic.cc new file mode 100644 index 000000000..c396f5033 --- /dev/null +++ b/src/cpu/simple/atomic.cc @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#include "arch/utility.hh" +#include "cpu/exetrace.hh" +#include "cpu/simple/atomic.hh" +#include "mem/packet_impl.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; +using namespace TheISA; + +AtomicSimpleCPU::TickEvent::TickEvent(AtomicSimpleCPU *c) + : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c) +{ +} + + +void +AtomicSimpleCPU::TickEvent::process() +{ + cpu->tick(); +} + +const char * +AtomicSimpleCPU::TickEvent::description() +{ + return "AtomicSimpleCPU tick event"; +} + +Port * +AtomicSimpleCPU::getPort(const std::string &if_name, int idx) +{ + if (if_name == "dcache_port") + return &dcachePort; + else if (if_name == "icache_port") + return &icachePort; + else + panic("No Such Port\n"); +} + +void +AtomicSimpleCPU::init() +{ + //Create Memory Ports (conect them up) +// Port *mem_dport = mem->getPort(""); +// dcachePort.setPeer(mem_dport); +// mem_dport->setPeer(&dcachePort); + +// Port *mem_iport = mem->getPort(""); +// icachePort.setPeer(mem_iport); +// mem_iport->setPeer(&icachePort); + + BaseCPU::init(); +#if FULL_SYSTEM + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + + // initialize CPU, including PC + TheISA::initCPU(tc, tc->readCpuId()); + } +#endif +} + +bool +AtomicSimpleCPU::CpuPort::recvTiming(Packet *pkt) +{ + panic("AtomicSimpleCPU doesn't expect recvAtomic callback!"); + return true; +} + +Tick +AtomicSimpleCPU::CpuPort::recvAtomic(Packet *pkt) +{ + panic("AtomicSimpleCPU doesn't expect recvAtomic callback!"); + return curTick; +} + +void +AtomicSimpleCPU::CpuPort::recvFunctional(Packet *pkt) +{ + panic("AtomicSimpleCPU doesn't expect recvFunctional callback!"); +} + +void +AtomicSimpleCPU::CpuPort::recvStatusChange(Status status) +{ + if (status == RangeChange) + return; + + panic("AtomicSimpleCPU doesn't expect recvStatusChange callback!"); +} + +void +AtomicSimpleCPU::CpuPort::recvRetry() +{ + panic("AtomicSimpleCPU doesn't expect recvRetry callback!"); +} + + +AtomicSimpleCPU::AtomicSimpleCPU(Params *p) + : BaseSimpleCPU(p), tickEvent(this), + width(p->width), simulate_stalls(p->simulate_stalls), + icachePort(name() + "-iport", this), dcachePort(name() + "-iport", this) +{ + _status = Idle; + + // @todo fix me and get the real cpu id & thread number!!! + ifetch_req = new Request(); + ifetch_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE + ifetch_pkt = new Packet(ifetch_req, Packet::ReadReq, Packet::Broadcast); + ifetch_pkt->dataStatic(&inst); + + data_read_req = new Request(); + data_read_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE + data_read_pkt = new Packet(data_read_req, Packet::ReadReq, + Packet::Broadcast); + data_read_pkt->dataStatic(&dataReg); + + data_write_req = new Request(); + data_write_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE + data_write_pkt = new Packet(data_write_req, Packet::WriteReq, + Packet::Broadcast); +} + + +AtomicSimpleCPU::~AtomicSimpleCPU() +{ +} + +void +AtomicSimpleCPU::serialize(ostream &os) +{ + SimObject::State so_state = SimObject::getState(); + SERIALIZE_ENUM(so_state); + nameOut(os, csprintf("%s.tickEvent", name())); + tickEvent.serialize(os); + BaseSimpleCPU::serialize(os); +} + +void +AtomicSimpleCPU::unserialize(Checkpoint *cp, const string §ion) +{ + SimObject::State so_state; + UNSERIALIZE_ENUM(so_state); + tickEvent.unserialize(cp, csprintf("%s.tickEvent", section)); + BaseSimpleCPU::unserialize(cp, section); +} + +void +AtomicSimpleCPU::resume() +{ + assert(system->getMemoryMode() == System::Atomic); + changeState(SimObject::Running); + if (thread->status() == ThreadContext::Active) { + if (!tickEvent.scheduled()) + tickEvent.schedule(curTick); + } +} + +void +AtomicSimpleCPU::switchOut() +{ + assert(status() == Running || status() == Idle); + _status = SwitchedOut; + + tickEvent.squash(); +} + + +void +AtomicSimpleCPU::takeOverFrom(BaseCPU *oldCPU) +{ + BaseCPU::takeOverFrom(oldCPU); + + assert(!tickEvent.scheduled()); + + // if any of this CPU's ThreadContexts are active, mark the CPU as + // running and schedule its tick event. + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + if (tc->status() == ThreadContext::Active && _status != Running) { + _status = Running; + tickEvent.schedule(curTick); + break; + } + } +} + + +void +AtomicSimpleCPU::activateContext(int thread_num, int delay) +{ + assert(thread_num == 0); + assert(thread); + + assert(_status == Idle); + assert(!tickEvent.scheduled()); + + notIdleFraction++; + tickEvent.schedule(curTick + cycles(delay)); + _status = Running; +} + + +void +AtomicSimpleCPU::suspendContext(int thread_num) +{ + assert(thread_num == 0); + assert(thread); + + assert(_status == Running); + + // tick event may not be scheduled if this gets called from inside + // an instruction's execution, e.g. "quiesce" + if (tickEvent.scheduled()) + tickEvent.deschedule(); + + notIdleFraction--; + _status = Idle; +} + + +template <class T> +Fault +AtomicSimpleCPU::read(Addr addr, T &data, unsigned flags) +{ + data_read_req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); + + if (traceData) { + traceData->setAddr(addr); + } + + // translate to physical address + Fault fault = thread->translateDataReadReq(data_read_req); + + // Now do the access. + if (fault == NoFault) { + data_read_pkt->reinitFromRequest(); + + dcache_latency = dcachePort.sendAtomic(data_read_pkt); + dcache_access = true; + + assert(data_read_pkt->result == Packet::Success); + data = data_read_pkt->get<T>(); + + } + + // This will need a new way to tell if it has a dcache attached. + if (data_read_req->getFlags() & UNCACHEABLE) + recordEvent("Uncached Read"); + + return fault; +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template +Fault +AtomicSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags); + +template +Fault +AtomicSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags); + +template +Fault +AtomicSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags); + +template +Fault +AtomicSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +AtomicSimpleCPU::read(Addr addr, double &data, unsigned flags) +{ + return read(addr, *(uint64_t*)&data, flags); +} + +template<> +Fault +AtomicSimpleCPU::read(Addr addr, float &data, unsigned flags) +{ + return read(addr, *(uint32_t*)&data, flags); +} + + +template<> +Fault +AtomicSimpleCPU::read(Addr addr, int32_t &data, unsigned flags) +{ + return read(addr, (uint32_t&)data, flags); +} + + +template <class T> +Fault +AtomicSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) +{ + data_write_req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); + + if (traceData) { + traceData->setAddr(addr); + } + + // translate to physical address + Fault fault = thread->translateDataWriteReq(data_write_req); + + // Now do the access. + if (fault == NoFault) { + data = htog(data); + data_write_pkt->reinitFromRequest(); + data_write_pkt->dataStatic(&data); + + dcache_latency = dcachePort.sendAtomic(data_write_pkt); + dcache_access = true; + + assert(data_write_pkt->result == Packet::Success); + + if (res && data_write_req->getFlags() & LOCKED) { + *res = data_write_req->getScResult(); + } + } + + // This will need a new way to tell if it's hooked up to a cache or not. + if (data_write_req->getFlags() & UNCACHEABLE) + recordEvent("Uncached Write"); + + // If the write needs to have a fault on the access, consider calling + // changeStatus() and changing it to "bad addr write" or something. + return fault; +} + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +template +Fault +AtomicSimpleCPU::write(uint64_t data, Addr addr, + unsigned flags, uint64_t *res); + +template +Fault +AtomicSimpleCPU::write(uint32_t data, Addr addr, + unsigned flags, uint64_t *res); + +template +Fault +AtomicSimpleCPU::write(uint16_t data, Addr addr, + unsigned flags, uint64_t *res); + +template +Fault +AtomicSimpleCPU::write(uint8_t data, Addr addr, + unsigned flags, uint64_t *res); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +AtomicSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint64_t*)&data, addr, flags, res); +} + +template<> +Fault +AtomicSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint32_t*)&data, addr, flags, res); +} + + +template<> +Fault +AtomicSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) +{ + return write((uint32_t)data, addr, flags, res); +} + + +void +AtomicSimpleCPU::tick() +{ + Tick latency = cycles(1); // instruction takes one cycle by default + + for (int i = 0; i < width; ++i) { + numCycles++; + + checkForInterrupts(); + + Fault fault = setupFetchRequest(ifetch_req); + + if (fault == NoFault) { + ifetch_pkt->reinitFromRequest(); + + Tick icache_latency = icachePort.sendAtomic(ifetch_pkt); + // ifetch_req is initialized to read the instruction directly + // into the CPU object's inst field. + + dcache_access = false; // assume no dcache access + preExecute(); + fault = curStaticInst->execute(this, traceData); + postExecute(); + + if (simulate_stalls) { + Tick icache_stall = icache_latency - cycles(1); + Tick dcache_stall = + dcache_access ? dcache_latency - cycles(1) : 0; + Tick stall_cycles = (icache_stall + dcache_stall) / cycles(1); + if (cycles(stall_cycles) < (icache_stall + dcache_stall)) + latency += cycles(stall_cycles+1); + else + latency += cycles(stall_cycles); + } + + } + + advancePC(fault); + } + + if (_status != Idle) + tickEvent.schedule(curTick + latency); +} + + +//////////////////////////////////////////////////////////////////////// +// +// AtomicSimpleCPU Simulation Object +// +BEGIN_DECLARE_SIM_OBJECT_PARAMS(AtomicSimpleCPU) + + Param<Counter> max_insts_any_thread; + Param<Counter> max_insts_all_threads; + Param<Counter> max_loads_any_thread; + Param<Counter> max_loads_all_threads; + SimObjectParam<MemObject *> mem; + SimObjectParam<System *> system; + +#if FULL_SYSTEM + SimObjectParam<AlphaITB *> itb; + SimObjectParam<AlphaDTB *> dtb; + Param<int> cpu_id; + Param<Tick> profile; +#else + SimObjectParam<Process *> workload; +#endif // FULL_SYSTEM + + Param<int> clock; + + Param<bool> defer_registration; + Param<int> width; + Param<bool> function_trace; + Param<Tick> function_trace_start; + Param<bool> simulate_stalls; + +END_DECLARE_SIM_OBJECT_PARAMS(AtomicSimpleCPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(AtomicSimpleCPU) + + INIT_PARAM(max_insts_any_thread, + "terminate when any thread reaches this inst count"), + INIT_PARAM(max_insts_all_threads, + "terminate when all threads have reached this inst count"), + INIT_PARAM(max_loads_any_thread, + "terminate when any thread reaches this load count"), + INIT_PARAM(max_loads_all_threads, + "terminate when all threads have reached this load count"), + INIT_PARAM(mem, "memory"), + INIT_PARAM(system, "system object"), + +#if FULL_SYSTEM + INIT_PARAM(itb, "Instruction TLB"), + INIT_PARAM(dtb, "Data TLB"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(profile, ""), +#else + INIT_PARAM(workload, "processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + INIT_PARAM(width, "cpu width"), + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace"), + INIT_PARAM(simulate_stalls, "Simulate cache stall cycles") + +END_INIT_SIM_OBJECT_PARAMS(AtomicSimpleCPU) + + +CREATE_SIM_OBJECT(AtomicSimpleCPU) +{ + AtomicSimpleCPU::Params *params = new AtomicSimpleCPU::Params(); + params->name = getInstanceName(); + params->numberOfThreads = 1; + params->max_insts_any_thread = max_insts_any_thread; + params->max_insts_all_threads = max_insts_all_threads; + params->max_loads_any_thread = max_loads_any_thread; + params->max_loads_all_threads = max_loads_all_threads; + params->deferRegistration = defer_registration; + params->clock = clock; + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + params->width = width; + params->simulate_stalls = simulate_stalls; + params->mem = mem; + params->system = system; + +#if FULL_SYSTEM + params->itb = itb; + params->dtb = dtb; + params->cpu_id = cpu_id; + params->profile = profile; +#else + params->process = workload; +#endif + + AtomicSimpleCPU *cpu = new AtomicSimpleCPU(params); + return cpu; +} + +REGISTER_SIM_OBJECT("AtomicSimpleCPU", AtomicSimpleCPU) + diff --git a/src/cpu/simple/atomic.hh b/src/cpu/simple/atomic.hh new file mode 100644 index 000000000..b602af558 --- /dev/null +++ b/src/cpu/simple/atomic.hh @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __CPU_SIMPLE_ATOMIC_HH__ +#define __CPU_SIMPLE_ATOMIC_HH__ + +#include "cpu/simple/base.hh" + +class AtomicSimpleCPU : public BaseSimpleCPU +{ + public: + + struct Params : public BaseSimpleCPU::Params { + int width; + bool simulate_stalls; + }; + + AtomicSimpleCPU(Params *params); + virtual ~AtomicSimpleCPU(); + + virtual void init(); + + public: + // + enum Status { + Running, + Idle, + SwitchedOut + }; + + protected: + Status _status; + + Status status() const { return _status; } + + private: + + struct TickEvent : public Event + { + AtomicSimpleCPU *cpu; + + TickEvent(AtomicSimpleCPU *c); + void process(); + const char *description(); + }; + + TickEvent tickEvent; + + const int width; + const bool simulate_stalls; + + // main simulation loop (one cycle) + void tick(); + + class CpuPort : public Port + { + + AtomicSimpleCPU *cpu; + + public: + + CpuPort(const std::string &_name, AtomicSimpleCPU *_cpu) + : Port(_name), cpu(_cpu) + { } + + protected: + + virtual bool recvTiming(Packet *pkt); + + virtual Tick recvAtomic(Packet *pkt); + + virtual void recvFunctional(Packet *pkt); + + virtual void recvStatusChange(Status status); + + virtual void recvRetry(); + + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + }; + + CpuPort icachePort; + CpuPort dcachePort; + + Request *ifetch_req; + Packet *ifetch_pkt; + Request *data_read_req; + Packet *data_read_pkt; + Request *data_write_req; + Packet *data_write_pkt; + + bool dcache_access; + Tick dcache_latency; + + public: + + virtual Port *getPort(const std::string &if_name, int idx = -1); + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + virtual void resume(); + + void switchOut(); + void takeOverFrom(BaseCPU *oldCPU); + + virtual void activateContext(int thread_num, int delay); + virtual void suspendContext(int thread_num); + + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + template <class T> + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); +}; + +#endif // __CPU_SIMPLE_ATOMIC_HH__ diff --git a/src/cpu/simple/base.cc b/src/cpu/simple/base.cc new file mode 100644 index 000000000..522fe79aa --- /dev/null +++ b/src/cpu/simple/base.cc @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#include "arch/utility.hh" +#include "arch/faults.hh" +#include "base/cprintf.hh" +#include "base/inifile.hh" +#include "base/loader/symtab.hh" +#include "base/misc.hh" +#include "base/pollevent.hh" +#include "base/range.hh" +#include "base/stats/events.hh" +#include "base/trace.hh" +#include "cpu/base.hh" +#include "cpu/exetrace.hh" +#include "cpu/profile.hh" +#include "cpu/simple/base.hh" +#include "cpu/simple_thread.hh" +#include "cpu/smt.hh" +#include "cpu/static_inst.hh" +#include "cpu/thread_context.hh" +#include "kern/kernel_stats.hh" +#include "mem/packet_impl.hh" +#include "sim/builder.hh" +#include "sim/byteswap.hh" +#include "sim/debug.hh" +#include "sim/host.hh" +#include "sim/sim_events.hh" +#include "sim/sim_object.hh" +#include "sim/stats.hh" +#include "sim/system.hh" + +#if FULL_SYSTEM +#include "base/remote_gdb.hh" +#include "arch/tlb.hh" +#include "arch/stacktrace.hh" +#include "arch/vtophys.hh" +#else // !FULL_SYSTEM +#include "mem/mem_object.hh" +#endif // FULL_SYSTEM + +using namespace std; +using namespace TheISA; + +BaseSimpleCPU::BaseSimpleCPU(Params *p) + : BaseCPU(p), mem(p->mem), thread(NULL) +{ +#if FULL_SYSTEM + thread = new SimpleThread(this, 0, p->system, p->itb, p->dtb); +#else + thread = new SimpleThread(this, /* thread_num */ 0, p->process, + /* asid */ 0, mem); +#endif // !FULL_SYSTEM + + thread->setStatus(ThreadContext::Suspended); + + tc = thread->getTC(); + + numInst = 0; + startNumInst = 0; + numLoad = 0; + startNumLoad = 0; + lastIcacheStall = 0; + lastDcacheStall = 0; + + threadContexts.push_back(tc); +} + +BaseSimpleCPU::~BaseSimpleCPU() +{ +} + +void +BaseSimpleCPU::deallocateContext(int thread_num) +{ + // for now, these are equivalent + suspendContext(thread_num); +} + + +void +BaseSimpleCPU::haltContext(int thread_num) +{ + // for now, these are equivalent + suspendContext(thread_num); +} + + +void +BaseSimpleCPU::regStats() +{ + using namespace Stats; + + BaseCPU::regStats(); + + numInsts + .name(name() + ".num_insts") + .desc("Number of instructions executed") + ; + + numMemRefs + .name(name() + ".num_refs") + .desc("Number of memory references") + ; + + notIdleFraction + .name(name() + ".not_idle_fraction") + .desc("Percentage of non-idle cycles") + ; + + idleFraction + .name(name() + ".idle_fraction") + .desc("Percentage of idle cycles") + ; + + icacheStallCycles + .name(name() + ".icache_stall_cycles") + .desc("ICache total stall cycles") + .prereq(icacheStallCycles) + ; + + dcacheStallCycles + .name(name() + ".dcache_stall_cycles") + .desc("DCache total stall cycles") + .prereq(dcacheStallCycles) + ; + + icacheRetryCycles + .name(name() + ".icache_retry_cycles") + .desc("ICache total retry cycles") + .prereq(icacheRetryCycles) + ; + + dcacheRetryCycles + .name(name() + ".dcache_retry_cycles") + .desc("DCache total retry cycles") + .prereq(dcacheRetryCycles) + ; + + idleFraction = constant(1.0) - notIdleFraction; +} + +void +BaseSimpleCPU::resetStats() +{ +// startNumInst = numInst; + // notIdleFraction = (_status != Idle); +} + +void +BaseSimpleCPU::serialize(ostream &os) +{ + BaseCPU::serialize(os); +// SERIALIZE_SCALAR(inst); + nameOut(os, csprintf("%s.xc.0", name())); + thread->serialize(os); +} + +void +BaseSimpleCPU::unserialize(Checkpoint *cp, const string §ion) +{ + BaseCPU::unserialize(cp, section); +// UNSERIALIZE_SCALAR(inst); + thread->unserialize(cp, csprintf("%s.xc.0", section)); +} + +void +change_thread_state(int thread_number, int activate, int priority) +{ +} + +Fault +BaseSimpleCPU::copySrcTranslate(Addr src) +{ +#if 0 + static bool no_warn = true; + int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64; + // Only support block sizes of 64 atm. + assert(blk_size == 64); + int offset = src & (blk_size - 1); + + // Make sure block doesn't span page + if (no_warn && + (src & PageMask) != ((src + blk_size) & PageMask) && + (src >> 40) != 0xfffffc) { + warn("Copied block source spans pages %x.", src); + no_warn = false; + } + + memReq->reset(src & ~(blk_size - 1), blk_size); + + // translate to physical address + Fault fault = thread->translateDataReadReq(req); + + if (fault == NoFault) { + thread->copySrcAddr = src; + thread->copySrcPhysAddr = memReq->paddr + offset; + } else { + assert(!fault->isAlignmentFault()); + + thread->copySrcAddr = 0; + thread->copySrcPhysAddr = 0; + } + return fault; +#else + return NoFault; +#endif +} + +Fault +BaseSimpleCPU::copy(Addr dest) +{ +#if 0 + static bool no_warn = true; + int blk_size = (dcacheInterface) ? dcacheInterface->getBlockSize() : 64; + // Only support block sizes of 64 atm. + assert(blk_size == 64); + uint8_t data[blk_size]; + //assert(thread->copySrcAddr); + int offset = dest & (blk_size - 1); + + // Make sure block doesn't span page + if (no_warn && + (dest & PageMask) != ((dest + blk_size) & PageMask) && + (dest >> 40) != 0xfffffc) { + no_warn = false; + warn("Copied block destination spans pages %x. ", dest); + } + + memReq->reset(dest & ~(blk_size -1), blk_size); + // translate to physical address + Fault fault = thread->translateDataWriteReq(req); + + if (fault == NoFault) { + Addr dest_addr = memReq->paddr + offset; + // Need to read straight from memory since we have more than 8 bytes. + memReq->paddr = thread->copySrcPhysAddr; + thread->mem->read(memReq, data); + memReq->paddr = dest_addr; + thread->mem->write(memReq, data); + if (dcacheInterface) { + memReq->cmd = Copy; + memReq->completionEvent = NULL; + memReq->paddr = thread->copySrcPhysAddr; + memReq->dest = dest_addr; + memReq->size = 64; + memReq->time = curTick; + memReq->flags &= ~INST_READ; + dcacheInterface->access(memReq); + } + } + else + assert(!fault->isAlignmentFault()); + + return fault; +#else + panic("copy not implemented"); + return NoFault; +#endif +} + +#if FULL_SYSTEM +Addr +BaseSimpleCPU::dbg_vtophys(Addr addr) +{ + return vtophys(tc, addr); +} +#endif // FULL_SYSTEM + +#if FULL_SYSTEM +void +BaseSimpleCPU::post_interrupt(int int_num, int index) +{ + BaseCPU::post_interrupt(int_num, index); + + if (thread->status() == ThreadContext::Suspended) { + DPRINTF(IPI,"Suspended Processor awoke\n"); + thread->activate(); + } +} +#endif // FULL_SYSTEM + +void +BaseSimpleCPU::checkForInterrupts() +{ +#if FULL_SYSTEM + if (checkInterrupts && check_interrupts() && !thread->inPalMode()) { + int ipl = 0; + int summary = 0; + checkInterrupts = false; + + if (thread->readMiscReg(IPR_SIRR)) { + for (int i = INTLEVEL_SOFTWARE_MIN; + i < INTLEVEL_SOFTWARE_MAX; i++) { + if (thread->readMiscReg(IPR_SIRR) & (ULL(1) << i)) { + // See table 4-19 of 21164 hardware reference + ipl = (i - INTLEVEL_SOFTWARE_MIN) + 1; + summary |= (ULL(1) << i); + } + } + } + + uint64_t interrupts = thread->cpu->intr_status(); + for (int i = INTLEVEL_EXTERNAL_MIN; + i < INTLEVEL_EXTERNAL_MAX; i++) { + if (interrupts & (ULL(1) << i)) { + // See table 4-19 of 21164 hardware reference + ipl = i; + summary |= (ULL(1) << i); + } + } + + if (thread->readMiscReg(IPR_ASTRR)) + panic("asynchronous traps not implemented\n"); + + if (ipl && ipl > thread->readMiscReg(IPR_IPLR)) { + thread->setMiscReg(IPR_ISR, summary); + thread->setMiscReg(IPR_INTID, ipl); + + Fault(new InterruptFault)->invoke(tc); + + DPRINTF(Flow, "Interrupt! IPLR=%d ipl=%d summary=%x\n", + thread->readMiscReg(IPR_IPLR), ipl, summary); + } + } +#endif +} + + +Fault +BaseSimpleCPU::setupFetchRequest(Request *req) +{ + // set up memory request for instruction fetch +#if ISA_HAS_DELAY_SLOT + DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p NNPC:%08p\n",thread->readPC(), + thread->readNextPC(),thread->readNextNPC()); +#else + DPRINTF(Fetch,"Fetch: PC:%08p NPC:%08p",thread->readPC(), + thread->readNextPC()); +#endif + + req->setVirt(0, thread->readPC() & ~3, sizeof(MachInst), + (FULL_SYSTEM && (thread->readPC() & 1)) ? PHYSICAL : 0, + thread->readPC()); + + Fault fault = thread->translateInstReq(req); + + return fault; +} + + +void +BaseSimpleCPU::preExecute() +{ + // maintain $r0 semantics + thread->setIntReg(ZeroReg, 0); +#if THE_ISA == ALPHA_ISA + thread->setFloatReg(ZeroReg, 0.0); +#endif // ALPHA_ISA + + // keep an instruction count + numInst++; + numInsts++; + + thread->funcExeInst++; + + // check for instruction-count-based events + comInstEventQueue[0]->serviceEvents(numInst); + + // decode the instruction + inst = gtoh(inst); + curStaticInst = StaticInst::decode(makeExtMI(inst, thread->readPC())); + + traceData = Trace::getInstRecord(curTick, tc, curStaticInst, + thread->readPC()); + + DPRINTF(Decode,"Decode: Decoded %s instruction (opcode: 0x%x): 0x%x\n", + curStaticInst->getName(), curStaticInst->getOpcode(), + curStaticInst->machInst); + +#if FULL_SYSTEM + thread->setInst(inst); +#endif // FULL_SYSTEM +} + +void +BaseSimpleCPU::postExecute() +{ +#if FULL_SYSTEM + if (thread->profile) { + bool usermode = + (thread->readMiscReg(AlphaISA::IPR_DTB_CM) & 0x18) != 0; + thread->profilePC = usermode ? 1 : thread->readPC(); + ProfileNode *node = thread->profile->consume(tc, inst); + if (node) + thread->profileNode = node; + } +#endif + + if (curStaticInst->isMemRef()) { + numMemRefs++; + } + + if (curStaticInst->isLoad()) { + ++numLoad; + comLoadEventQueue[0]->serviceEvents(numLoad); + } + + traceFunctions(thread->readPC()); + + if (traceData) { + traceData->finalize(); + } +} + + +void +BaseSimpleCPU::advancePC(Fault fault) +{ + if (fault != NoFault) { + fault->invoke(tc); + } + else { + // go to the next instruction + thread->setPC(thread->readNextPC()); +#if ISA_HAS_DELAY_SLOT + thread->setNextPC(thread->readNextNPC()); + thread->setNextNPC(thread->readNextNPC() + sizeof(MachInst)); + assert(thread->readNextPC() != thread->readNextNPC()); +#else + thread->setNextPC(thread->readNextPC() + sizeof(MachInst)); +#endif + + } + +#if FULL_SYSTEM + Addr oldpc; + do { + oldpc = thread->readPC(); + system->pcEventQueue.service(tc); + } while (oldpc != thread->readPC()); +#endif +} + diff --git a/src/cpu/simple/base.hh b/src/cpu/simple/base.hh new file mode 100644 index 000000000..57cfa3c2c --- /dev/null +++ b/src/cpu/simple/base.hh @@ -0,0 +1,317 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Dave Greene + * Nathan Binkert + */ + +#ifndef __CPU_SIMPLE_BASE_HH__ +#define __CPU_SIMPLE_BASE_HH__ + +#include "base/statistics.hh" +#include "config/full_system.hh" +#include "cpu/base.hh" +#include "cpu/simple_thread.hh" +#include "cpu/pc_event.hh" +#include "cpu/static_inst.hh" +#include "mem/packet.hh" +#include "mem/port.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" + +// forward declarations +#if FULL_SYSTEM +class Processor; +class AlphaITB; +class AlphaDTB; +class MemObject; + +class RemoteGDB; +class GDBListener; + +#else + +class Process; + +#endif // FULL_SYSTEM + +class ThreadContext; +class Checkpoint; + +namespace Trace { + class InstRecord; +} + + +class BaseSimpleCPU : public BaseCPU +{ + protected: + typedef TheISA::MachInst MachInst; + typedef TheISA::MiscReg MiscReg; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + + MemObject *mem; + + protected: + Trace::InstRecord *traceData; + + public: + void post_interrupt(int int_num, int index); + + void zero_fill_64(Addr addr) { + static int warned = 0; + if (!warned) { + warn ("WH64 is not implemented"); + warned = 1; + } + }; + + public: + struct Params : public BaseCPU::Params + { + MemObject *mem; +#if FULL_SYSTEM + AlphaITB *itb; + AlphaDTB *dtb; +#else + Process *process; +#endif + }; + BaseSimpleCPU(Params *params); + virtual ~BaseSimpleCPU(); + + public: + /** SimpleThread object, provides all the architectural state. */ + SimpleThread *thread; + + /** ThreadContext object, provides an interface for external + * objects to modify this thread's state. + */ + ThreadContext *tc; + +#if FULL_SYSTEM + Addr dbg_vtophys(Addr addr); + + bool interval_stats; +#endif + + // current instruction + MachInst inst; + + // Static data storage + TheISA::IntReg dataReg; + + StaticInstPtr curStaticInst; + + void checkForInterrupts(); + Fault setupFetchRequest(Request *req); + void preExecute(); + void postExecute(); + void advancePC(Fault fault); + + virtual void deallocateContext(int thread_num); + virtual void haltContext(int thread_num); + + // statistics + virtual void regStats(); + virtual void resetStats(); + + // number of simulated instructions + Counter numInst; + Counter startNumInst; + Stats::Scalar<> numInsts; + + virtual Counter totalInstructions() const + { + return numInst - startNumInst; + } + + // number of simulated memory references + Stats::Scalar<> numMemRefs; + + // number of simulated loads + Counter numLoad; + Counter startNumLoad; + + // number of idle cycles + Stats::Average<> notIdleFraction; + Stats::Formula idleFraction; + + // number of cycles stalled for I-cache responses + Stats::Scalar<> icacheStallCycles; + Counter lastIcacheStall; + + // number of cycles stalled for I-cache retries + Stats::Scalar<> icacheRetryCycles; + Counter lastIcacheRetry; + + // number of cycles stalled for D-cache responses + Stats::Scalar<> dcacheStallCycles; + Counter lastDcacheStall; + + // number of cycles stalled for D-cache retries + Stats::Scalar<> dcacheRetryCycles; + Counter lastDcacheRetry; + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + // These functions are only used in CPU models that split + // effective address computation from the actual memory access. + void setEA(Addr EA) { panic("BaseSimpleCPU::setEA() not implemented\n"); } + Addr getEA() { panic("BaseSimpleCPU::getEA() not implemented\n"); } + + void prefetch(Addr addr, unsigned flags) + { + // need to do this... + } + + void writeHint(Addr addr, int size, unsigned flags) + { + // need to do this... + } + + Fault copySrcTranslate(Addr src); + + Fault copy(Addr dest); + + // The register accessor methods provide the index of the + // instruction's operand (e.g., 0 or 1), not the architectural + // register index, to simplify the implementation of register + // renaming. We find the architectural register index by indexing + // into the instruction's own operand index table. Note that a + // raw pointer to the StaticInst is provided instead of a + // ref-counted StaticInstPtr to redice overhead. This is fine as + // long as these methods don't copy the pointer into any long-term + // storage (which is pretty hard to imagine they would have reason + // to do). + + uint64_t readIntReg(const StaticInst *si, int idx) + { + return thread->readIntReg(si->srcRegIdx(idx)); + } + + FloatReg readFloatReg(const StaticInst *si, int idx, int width) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatReg(reg_idx, width); + } + + FloatReg readFloatReg(const StaticInst *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatReg(reg_idx); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx, int width) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatRegBits(reg_idx, width); + } + + FloatRegBits readFloatRegBits(const StaticInst *si, int idx) + { + int reg_idx = si->srcRegIdx(idx) - TheISA::FP_Base_DepTag; + return thread->readFloatRegBits(reg_idx); + } + + void setIntReg(const StaticInst *si, int idx, uint64_t val) + { + thread->setIntReg(si->destRegIdx(idx), val); + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val, int width) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatReg(reg_idx, val, width); + } + + void setFloatReg(const StaticInst *si, int idx, FloatReg val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatReg(reg_idx, val); + } + + void setFloatRegBits(const StaticInst *si, int idx, + FloatRegBits val, int width) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatRegBits(reg_idx, val, width); + } + + void setFloatRegBits(const StaticInst *si, int idx, FloatRegBits val) + { + int reg_idx = si->destRegIdx(idx) - TheISA::FP_Base_DepTag; + thread->setFloatRegBits(reg_idx, val); + } + + uint64_t readPC() { return thread->readPC(); } + uint64_t readNextPC() { return thread->readNextPC(); } + uint64_t readNextNPC() { return thread->readNextNPC(); } + + void setPC(uint64_t val) { thread->setPC(val); } + void setNextPC(uint64_t val) { thread->setNextPC(val); } + void setNextNPC(uint64_t val) { thread->setNextNPC(val); } + + MiscReg readMiscReg(int misc_reg) + { + return thread->readMiscReg(misc_reg); + } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { + return thread->readMiscRegWithEffect(misc_reg, fault); + } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + return thread->setMiscReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + return thread->setMiscRegWithEffect(misc_reg, val); + } + +#if FULL_SYSTEM + Fault hwrei() { return thread->hwrei(); } + int readIntrFlag() { return thread->readIntrFlag(); } + void setIntrFlag(int val) { thread->setIntrFlag(val); } + bool inPalMode() { return thread->inPalMode(); } + void ev5_trap(Fault fault) { fault->invoke(tc); } + bool simPalCheck(int palFunc) { return thread->simPalCheck(palFunc); } +#else + void syscall(int64_t callnum) { thread->syscall(callnum); } +#endif + + bool misspeculating() { return thread->misspeculating(); } + ThreadContext *tcBase() { return tc; } +}; + +#endif // __CPU_SIMPLE_BASE_HH__ diff --git a/src/cpu/simple/timing.cc b/src/cpu/simple/timing.cc new file mode 100644 index 000000000..5c1654f7e --- /dev/null +++ b/src/cpu/simple/timing.cc @@ -0,0 +1,676 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#include "arch/utility.hh" +#include "cpu/exetrace.hh" +#include "cpu/simple/timing.hh" +#include "mem/packet_impl.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; +using namespace TheISA; + +Port * +TimingSimpleCPU::getPort(const std::string &if_name, int idx) +{ + if (if_name == "dcache_port") + return &dcachePort; + else if (if_name == "icache_port") + return &icachePort; + else + panic("No Such Port\n"); +} + +void +TimingSimpleCPU::init() +{ + BaseCPU::init(); +#if FULL_SYSTEM + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + + // initialize CPU, including PC + TheISA::initCPU(tc, tc->readCpuId()); + } +#endif +} + +Tick +TimingSimpleCPU::CpuPort::recvAtomic(Packet *pkt) +{ + panic("TimingSimpleCPU doesn't expect recvAtomic callback!"); + return curTick; +} + +void +TimingSimpleCPU::CpuPort::recvFunctional(Packet *pkt) +{ + panic("TimingSimpleCPU doesn't expect recvFunctional callback!"); +} + +void +TimingSimpleCPU::CpuPort::recvStatusChange(Status status) +{ + if (status == RangeChange) + return; + + panic("TimingSimpleCPU doesn't expect recvStatusChange callback!"); +} + + +void +TimingSimpleCPU::CpuPort::TickEvent::schedule(Packet *_pkt, Tick t) +{ + pkt = _pkt; + Event::schedule(t); +} + +TimingSimpleCPU::TimingSimpleCPU(Params *p) + : BaseSimpleCPU(p), icachePort(this, p->clock), dcachePort(this, p->clock) +{ + _status = Idle; + ifetch_pkt = dcache_pkt = NULL; + drainEvent = NULL; + fetchEvent = NULL; + changeState(SimObject::Running); +} + + +TimingSimpleCPU::~TimingSimpleCPU() +{ +} + +void +TimingSimpleCPU::serialize(ostream &os) +{ + SimObject::State so_state = SimObject::getState(); + SERIALIZE_ENUM(so_state); + BaseSimpleCPU::serialize(os); +} + +void +TimingSimpleCPU::unserialize(Checkpoint *cp, const string §ion) +{ + SimObject::State so_state; + UNSERIALIZE_ENUM(so_state); + BaseSimpleCPU::unserialize(cp, section); +} + +unsigned int +TimingSimpleCPU::drain(Event *drain_event) +{ + // TimingSimpleCPU is ready to drain if it's not waiting for + // an access to complete. + if (status() == Idle || status() == Running || status() == SwitchedOut) { + changeState(SimObject::Drained); + return 0; + } else { + changeState(SimObject::Draining); + drainEvent = drain_event; + return 1; + } +} + +void +TimingSimpleCPU::resume() +{ + if (_status != SwitchedOut && _status != Idle) { + // Delete the old event if it existed. + if (fetchEvent) { + if (fetchEvent->scheduled()) + fetchEvent->deschedule(); + + delete fetchEvent; + } + + fetchEvent = + new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false); + fetchEvent->schedule(curTick); + } + + assert(system->getMemoryMode() == System::Timing); + changeState(SimObject::Running); +} + +void +TimingSimpleCPU::switchOut() +{ + assert(status() == Running || status() == Idle); + _status = SwitchedOut; + + // If we've been scheduled to resume but are then told to switch out, + // we'll need to cancel it. + if (fetchEvent && fetchEvent->scheduled()) + fetchEvent->deschedule(); +} + + +void +TimingSimpleCPU::takeOverFrom(BaseCPU *oldCPU) +{ + BaseCPU::takeOverFrom(oldCPU); + + // if any of this CPU's ThreadContexts are active, mark the CPU as + // running and schedule its tick event. + for (int i = 0; i < threadContexts.size(); ++i) { + ThreadContext *tc = threadContexts[i]; + if (tc->status() == ThreadContext::Active && _status != Running) { + _status = Running; + break; + } + } +} + + +void +TimingSimpleCPU::activateContext(int thread_num, int delay) +{ + assert(thread_num == 0); + assert(thread); + + assert(_status == Idle); + + notIdleFraction++; + _status = Running; + // kick things off by initiating the fetch of the next instruction + fetchEvent = + new EventWrapper<TimingSimpleCPU, &TimingSimpleCPU::fetch>(this, false); + fetchEvent->schedule(curTick + cycles(delay)); +} + + +void +TimingSimpleCPU::suspendContext(int thread_num) +{ + assert(thread_num == 0); + assert(thread); + + assert(_status == Running); + + // just change status to Idle... if status != Running, + // completeInst() will not initiate fetch of next instruction. + + notIdleFraction--; + _status = Idle; +} + + +template <class T> +Fault +TimingSimpleCPU::read(Addr addr, T &data, unsigned flags) +{ + // need to fill in CPU & thread IDs here + Request *data_read_req = new Request(); + data_read_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE + data_read_req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); + + if (traceData) { + traceData->setAddr(data_read_req->getVaddr()); + } + + // translate to physical address + Fault fault = thread->translateDataReadReq(data_read_req); + + // Now do the access. + if (fault == NoFault) { + Packet *data_read_pkt = + new Packet(data_read_req, Packet::ReadReq, Packet::Broadcast); + data_read_pkt->dataDynamic<T>(new T); + + if (!dcachePort.sendTiming(data_read_pkt)) { + _status = DcacheRetry; + dcache_pkt = data_read_pkt; + } else { + _status = DcacheWaitResponse; + dcache_pkt = NULL; + } + } + + // This will need a new way to tell if it has a dcache attached. + if (data_read_req->getFlags() & UNCACHEABLE) + recordEvent("Uncached Read"); + + return fault; +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template +Fault +TimingSimpleCPU::read(Addr addr, uint64_t &data, unsigned flags); + +template +Fault +TimingSimpleCPU::read(Addr addr, uint32_t &data, unsigned flags); + +template +Fault +TimingSimpleCPU::read(Addr addr, uint16_t &data, unsigned flags); + +template +Fault +TimingSimpleCPU::read(Addr addr, uint8_t &data, unsigned flags); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +TimingSimpleCPU::read(Addr addr, double &data, unsigned flags) +{ + return read(addr, *(uint64_t*)&data, flags); +} + +template<> +Fault +TimingSimpleCPU::read(Addr addr, float &data, unsigned flags) +{ + return read(addr, *(uint32_t*)&data, flags); +} + + +template<> +Fault +TimingSimpleCPU::read(Addr addr, int32_t &data, unsigned flags) +{ + return read(addr, (uint32_t&)data, flags); +} + + +template <class T> +Fault +TimingSimpleCPU::write(T data, Addr addr, unsigned flags, uint64_t *res) +{ + // need to fill in CPU & thread IDs here + Request *data_write_req = new Request(); + data_write_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE + data_write_req->setVirt(0, addr, sizeof(T), flags, thread->readPC()); + + // translate to physical address + Fault fault = thread->translateDataWriteReq(data_write_req); + // Now do the access. + if (fault == NoFault) { + Packet *data_write_pkt = + new Packet(data_write_req, Packet::WriteReq, Packet::Broadcast); + data_write_pkt->allocate(); + data_write_pkt->set(data); + + if (!dcachePort.sendTiming(data_write_pkt)) { + _status = DcacheRetry; + dcache_pkt = data_write_pkt; + } else { + _status = DcacheWaitResponse; + dcache_pkt = NULL; + } + } + + // This will need a new way to tell if it's hooked up to a cache or not. + if (data_write_req->getFlags() & UNCACHEABLE) + recordEvent("Uncached Write"); + + // If the write needs to have a fault on the access, consider calling + // changeStatus() and changing it to "bad addr write" or something. + return fault; +} + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +template +Fault +TimingSimpleCPU::write(uint64_t data, Addr addr, + unsigned flags, uint64_t *res); + +template +Fault +TimingSimpleCPU::write(uint32_t data, Addr addr, + unsigned flags, uint64_t *res); + +template +Fault +TimingSimpleCPU::write(uint16_t data, Addr addr, + unsigned flags, uint64_t *res); + +template +Fault +TimingSimpleCPU::write(uint8_t data, Addr addr, + unsigned flags, uint64_t *res); + +#endif //DOXYGEN_SHOULD_SKIP_THIS + +template<> +Fault +TimingSimpleCPU::write(double data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint64_t*)&data, addr, flags, res); +} + +template<> +Fault +TimingSimpleCPU::write(float data, Addr addr, unsigned flags, uint64_t *res) +{ + return write(*(uint32_t*)&data, addr, flags, res); +} + + +template<> +Fault +TimingSimpleCPU::write(int32_t data, Addr addr, unsigned flags, uint64_t *res) +{ + return write((uint32_t)data, addr, flags, res); +} + + +void +TimingSimpleCPU::fetch() +{ + checkForInterrupts(); + + // need to fill in CPU & thread IDs here + Request *ifetch_req = new Request(); + ifetch_req->setThreadContext(0,0); //Need CPU/Thread IDS HERE + Fault fault = setupFetchRequest(ifetch_req); + + ifetch_pkt = new Packet(ifetch_req, Packet::ReadReq, Packet::Broadcast); + ifetch_pkt->dataStatic(&inst); + + if (fault == NoFault) { + if (!icachePort.sendTiming(ifetch_pkt)) { + // Need to wait for retry + _status = IcacheRetry; + } else { + // Need to wait for cache to respond + _status = IcacheWaitResponse; + // ownership of packet transferred to memory system + ifetch_pkt = NULL; + } + } else { + // fetch fault: advance directly to next instruction (fault handler) + advanceInst(fault); + } +} + + +void +TimingSimpleCPU::advanceInst(Fault fault) +{ + advancePC(fault); + + if (_status == Running) { + // kick off fetch of next instruction... callback from icache + // response will cause that instruction to be executed, + // keeping the CPU running. + fetch(); + } +} + + +void +TimingSimpleCPU::completeIfetch(Packet *pkt) +{ + // received a response from the icache: execute the received + // instruction + assert(pkt->result == Packet::Success); + assert(_status == IcacheWaitResponse); + + _status = Running; + + delete pkt->req; + delete pkt; + + if (getState() == SimObject::Draining) { + completeDrain(); + return; + } + + preExecute(); + if (curStaticInst->isMemRef() && !curStaticInst->isDataPrefetch()) { + // load or store: just send to dcache + Fault fault = curStaticInst->initiateAcc(this, traceData); + if (fault == NoFault) { + // successfully initiated access: instruction will + // complete in dcache response callback + assert(_status == DcacheWaitResponse); + } else { + // fault: complete now to invoke fault handler + postExecute(); + advanceInst(fault); + } + } else { + // non-memory instruction: execute completely now + Fault fault = curStaticInst->execute(this, traceData); + postExecute(); + advanceInst(fault); + } +} + +void +TimingSimpleCPU::IcachePort::ITickEvent::process() +{ + cpu->completeIfetch(pkt); +} + +bool +TimingSimpleCPU::IcachePort::recvTiming(Packet *pkt) +{ + // These next few lines could be replaced with something faster + // who knows what though + Tick time = pkt->req->getTime(); + while (time < curTick) + time += lat; + + if (time == curTick) + cpu->completeIfetch(pkt); + else + tickEvent.schedule(pkt, time); + + return true; +} + +void +TimingSimpleCPU::IcachePort::recvRetry() +{ + // we shouldn't get a retry unless we have a packet that we're + // waiting to transmit + assert(cpu->ifetch_pkt != NULL); + assert(cpu->_status == IcacheRetry); + Packet *tmp = cpu->ifetch_pkt; + if (sendTiming(tmp)) { + cpu->_status = IcacheWaitResponse; + cpu->ifetch_pkt = NULL; + } +} + +void +TimingSimpleCPU::completeDataAccess(Packet *pkt) +{ + // received a response from the dcache: complete the load or store + // instruction + assert(pkt->result == Packet::Success); + assert(_status == DcacheWaitResponse); + _status = Running; + + if (getState() == SimObject::Draining) { + completeDrain(); + + delete pkt->req; + delete pkt; + + return; + } + + Fault fault = curStaticInst->completeAcc(pkt, this, traceData); + + delete pkt->req; + delete pkt; + + postExecute(); + advanceInst(fault); +} + + +void +TimingSimpleCPU::completeDrain() +{ + DPRINTF(Config, "Done draining\n"); + changeState(SimObject::Drained); + drainEvent->process(); +} + +bool +TimingSimpleCPU::DcachePort::recvTiming(Packet *pkt) +{ + Tick time = pkt->req->getTime(); + while (time < curTick) + time += lat; + + if (time == curTick) + cpu->completeDataAccess(pkt); + else + tickEvent.schedule(pkt, time); + + return true; +} + +void +TimingSimpleCPU::DcachePort::DTickEvent::process() +{ + cpu->completeDataAccess(pkt); +} + +void +TimingSimpleCPU::DcachePort::recvRetry() +{ + // we shouldn't get a retry unless we have a packet that we're + // waiting to transmit + assert(cpu->dcache_pkt != NULL); + assert(cpu->_status == DcacheRetry); + Packet *tmp = cpu->dcache_pkt; + if (sendTiming(tmp)) { + cpu->_status = DcacheWaitResponse; + cpu->dcache_pkt = NULL; + } +} + + +//////////////////////////////////////////////////////////////////////// +// +// TimingSimpleCPU Simulation Object +// +BEGIN_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU) + + Param<Counter> max_insts_any_thread; + Param<Counter> max_insts_all_threads; + Param<Counter> max_loads_any_thread; + Param<Counter> max_loads_all_threads; + SimObjectParam<MemObject *> mem; + SimObjectParam<System *> system; + +#if FULL_SYSTEM + SimObjectParam<AlphaITB *> itb; + SimObjectParam<AlphaDTB *> dtb; + Param<int> cpu_id; + Param<Tick> profile; +#else + SimObjectParam<Process *> workload; +#endif // FULL_SYSTEM + + Param<int> clock; + + Param<bool> defer_registration; + Param<int> width; + Param<bool> function_trace; + Param<Tick> function_trace_start; + Param<bool> simulate_stalls; + +END_DECLARE_SIM_OBJECT_PARAMS(TimingSimpleCPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU) + + INIT_PARAM(max_insts_any_thread, + "terminate when any thread reaches this inst count"), + INIT_PARAM(max_insts_all_threads, + "terminate when all threads have reached this inst count"), + INIT_PARAM(max_loads_any_thread, + "terminate when any thread reaches this load count"), + INIT_PARAM(max_loads_all_threads, + "terminate when all threads have reached this load count"), + INIT_PARAM(mem, "memory"), + INIT_PARAM(system, "system object"), + +#if FULL_SYSTEM + INIT_PARAM(itb, "Instruction TLB"), + INIT_PARAM(dtb, "Data TLB"), + INIT_PARAM(cpu_id, "processor ID"), + INIT_PARAM(profile, ""), +#else + INIT_PARAM(workload, "processes to run"), +#endif // FULL_SYSTEM + + INIT_PARAM(clock, "clock speed"), + INIT_PARAM(defer_registration, "defer system registration (for sampling)"), + INIT_PARAM(width, "cpu width"), + INIT_PARAM(function_trace, "Enable function trace"), + INIT_PARAM(function_trace_start, "Cycle to start function trace"), + INIT_PARAM(simulate_stalls, "Simulate cache stall cycles") + +END_INIT_SIM_OBJECT_PARAMS(TimingSimpleCPU) + + +CREATE_SIM_OBJECT(TimingSimpleCPU) +{ + TimingSimpleCPU::Params *params = new TimingSimpleCPU::Params(); + params->name = getInstanceName(); + params->numberOfThreads = 1; + params->max_insts_any_thread = max_insts_any_thread; + params->max_insts_all_threads = max_insts_all_threads; + params->max_loads_any_thread = max_loads_any_thread; + params->max_loads_all_threads = max_loads_all_threads; + params->deferRegistration = defer_registration; + params->clock = clock; + params->functionTrace = function_trace; + params->functionTraceStart = function_trace_start; + params->mem = mem; + params->system = system; + +#if FULL_SYSTEM + params->itb = itb; + params->dtb = dtb; + params->cpu_id = cpu_id; + params->profile = profile; +#else + params->process = workload; +#endif + + TimingSimpleCPU *cpu = new TimingSimpleCPU(params); + return cpu; +} + +REGISTER_SIM_OBJECT("TimingSimpleCPU", TimingSimpleCPU) + diff --git a/src/cpu/simple/timing.hh b/src/cpu/simple/timing.hh new file mode 100644 index 000000000..d03fa4bc0 --- /dev/null +++ b/src/cpu/simple/timing.hh @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __CPU_SIMPLE_TIMING_HH__ +#define __CPU_SIMPLE_TIMING_HH__ + +#include "cpu/simple/base.hh" + +class TimingSimpleCPU : public BaseSimpleCPU +{ + public: + + struct Params : public BaseSimpleCPU::Params { + }; + + TimingSimpleCPU(Params *params); + virtual ~TimingSimpleCPU(); + + virtual void init(); + + public: + // + enum Status { + Idle, + Running, + IcacheRetry, + IcacheWaitResponse, + IcacheWaitSwitch, + DcacheRetry, + DcacheWaitResponse, + DcacheWaitSwitch, + SwitchedOut + }; + + protected: + Status _status; + + Status status() const { return _status; } + + Event *drainEvent; + + Event *fetchEvent; + + private: + + class CpuPort : public Port + { + protected: + TimingSimpleCPU *cpu; + Tick lat; + + public: + + CpuPort(const std::string &_name, TimingSimpleCPU *_cpu, Tick _lat) + : Port(_name), cpu(_cpu), lat(_lat) + { } + + protected: + + virtual Tick recvAtomic(Packet *pkt); + + virtual void recvFunctional(Packet *pkt); + + virtual void recvStatusChange(Status status); + + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + struct TickEvent : public Event + { + Packet *pkt; + TimingSimpleCPU *cpu; + + TickEvent(TimingSimpleCPU *_cpu) + :Event(&mainEventQueue), cpu(_cpu) {} + const char *description() { return "Timing CPU clock event"; } + void schedule(Packet *_pkt, Tick t); + }; + + }; + + class IcachePort : public CpuPort + { + public: + + IcachePort(TimingSimpleCPU *_cpu, Tick _lat) + : CpuPort(_cpu->name() + "-iport", _cpu, _lat), tickEvent(_cpu) + { } + + protected: + + virtual bool recvTiming(Packet *pkt); + + virtual void recvRetry(); + + struct ITickEvent : public TickEvent + { + + ITickEvent(TimingSimpleCPU *_cpu) + : TickEvent(_cpu) {} + void process(); + const char *description() { return "Timing CPU clock event"; } + }; + + ITickEvent tickEvent; + + }; + + class DcachePort : public CpuPort + { + public: + + DcachePort(TimingSimpleCPU *_cpu, Tick _lat) + : CpuPort(_cpu->name() + "-dport", _cpu, _lat), tickEvent(_cpu) + { } + + protected: + + virtual bool recvTiming(Packet *pkt); + + virtual void recvRetry(); + + struct DTickEvent : public TickEvent + { + DTickEvent(TimingSimpleCPU *_cpu) + : TickEvent(_cpu) {} + void process(); + const char *description() { return "Timing CPU clock event"; } + }; + + DTickEvent tickEvent; + + }; + + IcachePort icachePort; + DcachePort dcachePort; + + Packet *ifetch_pkt; + Packet *dcache_pkt; + + public: + + virtual Port *getPort(const std::string &if_name, int idx = -1); + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + virtual unsigned int drain(Event *drain_event); + virtual void resume(); + + void switchOut(); + void takeOverFrom(BaseCPU *oldCPU); + + virtual void activateContext(int thread_num, int delay); + virtual void suspendContext(int thread_num); + + template <class T> + Fault read(Addr addr, T &data, unsigned flags); + + template <class T> + Fault write(T data, Addr addr, unsigned flags, uint64_t *res); + + void fetch(); + void completeIfetch(Packet *); + void completeDataAccess(Packet *); + void advanceInst(Fault fault); + private: + void completeDrain(); +}; + +#endif // __CPU_SIMPLE_TIMING_HH__ diff --git a/src/cpu/simple_thread.cc b/src/cpu/simple_thread.cc new file mode 100644 index 000000000..4fc47c982 --- /dev/null +++ b/src/cpu/simple_thread.cc @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2001-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + * Lisa Hsu + * Kevin Lim + */ + +#include <string> + +#include "arch/isa_traits.hh" +#include "cpu/base.hh" +#include "cpu/simple_thread.hh" +#include "cpu/thread_context.hh" + +#if FULL_SYSTEM +#include "base/callback.hh" +#include "base/cprintf.hh" +#include "base/output.hh" +#include "base/trace.hh" +#include "cpu/profile.hh" +#include "cpu/quiesce_event.hh" +#include "kern/kernel_stats.hh" +#include "sim/serialize.hh" +#include "sim/sim_exit.hh" +#include "arch/stacktrace.hh" +#else +#include "sim/process.hh" +#include "sim/system.hh" +#include "mem/translating_port.hh" +#endif + +using namespace std; + +// constructor +#if FULL_SYSTEM +SimpleThread::SimpleThread(BaseCPU *_cpu, int _thread_num, System *_sys, + AlphaITB *_itb, AlphaDTB *_dtb, + bool use_kernel_stats) + : ThreadState(-1, _thread_num), cpu(_cpu), system(_sys), itb(_itb), + dtb(_dtb) + +{ + tc = new ProxyThreadContext<SimpleThread>(this); + + quiesceEvent = new EndQuiesceEvent(tc); + + regs.clear(); + + if (cpu->params->profile) { + profile = new FunctionProfile(system->kernelSymtab); + Callback *cb = + new MakeCallback<SimpleThread, + &SimpleThread::dumpFuncProfile>(this); + registerExitCallback(cb); + } + + // let's fill with a dummy node for now so we don't get a segfault + // on the first cycle when there's no node available. + static ProfileNode dummyNode; + profileNode = &dummyNode; + profilePC = 3; + + if (use_kernel_stats) { + kernelStats = new Kernel::Statistics(system); + } else { + kernelStats = NULL; + } + Port *mem_port; + physPort = new FunctionalPort(csprintf("%s-%d-funcport", + cpu->name(), tid)); + mem_port = system->physmem->getPort("functional"); + mem_port->setPeer(physPort); + physPort->setPeer(mem_port); + + virtPort = new VirtualPort(csprintf("%s-%d-vport", + cpu->name(), tid)); + mem_port = system->physmem->getPort("functional"); + mem_port->setPeer(virtPort); + virtPort->setPeer(mem_port); +} +#else +SimpleThread::SimpleThread(BaseCPU *_cpu, int _thread_num, + Process *_process, int _asid, MemObject* memobj) + : ThreadState(-1, _thread_num, _process, _asid, memobj), + cpu(_cpu) +{ + /* Use this port to for syscall emulation writes to memory. */ + Port *mem_port; + port = new TranslatingPort(csprintf("%s-%d-funcport", + cpu->name(), tid), + process->pTable, false); + mem_port = memobj->getPort("functional"); + mem_port->setPeer(port); + port->setPeer(mem_port); + + regs.clear(); + tc = new ProxyThreadContext<SimpleThread>(this); +} + +#endif + +SimpleThread::SimpleThread() +#if FULL_SYSTEM + : ThreadState(-1, -1) +#else + : ThreadState(-1, -1, NULL, -1, NULL) +#endif +{ + tc = new ProxyThreadContext<SimpleThread>(this); + regs.clear(); +} + +SimpleThread::~SimpleThread() +{ + delete tc; +} + +void +SimpleThread::takeOverFrom(ThreadContext *oldContext) +{ + // some things should already be set up +#if FULL_SYSTEM + assert(system == oldContext->getSystemPtr()); +#else + assert(process == oldContext->getProcessPtr()); +#endif + + copyState(oldContext); +#if FULL_SYSTEM + EndQuiesceEvent *quiesce = oldContext->getQuiesceEvent(); + if (quiesce) { + // Point the quiesce event's TC at this TC so that it wakes up + // the proper CPU. + quiesce->tc = tc; + } + if (quiesceEvent) { + quiesceEvent->tc = tc; + } + + Kernel::Statistics *stats = oldContext->getKernelStats(); + if (stats) { + kernelStats = stats; + } +#endif + + storeCondFailures = 0; + + oldContext->setStatus(ThreadContext::Unallocated); +} + +void +SimpleThread::copyTC(ThreadContext *context) +{ + copyState(context); + +#if FULL_SYSTEM + EndQuiesceEvent *quiesce = context->getQuiesceEvent(); + if (quiesce) { + quiesceEvent = quiesce; + } + Kernel::Statistics *stats = context->getKernelStats(); + if (stats) { + kernelStats = stats; + } +#endif +} + +void +SimpleThread::copyState(ThreadContext *oldContext) +{ + // copy over functional state + _status = oldContext->status(); + copyArchRegs(oldContext); + cpuId = oldContext->readCpuId(); +#if !FULL_SYSTEM + funcExeInst = oldContext->readFuncExeInst(); +#endif + inst = oldContext->getInst(); +} + +void +SimpleThread::serialize(ostream &os) +{ + ThreadState::serialize(os); + regs.serialize(os); + // thread_num and cpu_id are deterministic from the config +} + + +void +SimpleThread::unserialize(Checkpoint *cp, const std::string §ion) +{ + ThreadState::unserialize(cp, section); + regs.unserialize(cp, section); + // thread_num and cpu_id are deterministic from the config +} + +#if FULL_SYSTEM +void +SimpleThread::dumpFuncProfile() +{ + std::ostream *os = simout.create(csprintf("profile.%s.dat", cpu->name())); + profile->dump(tc, *os); +} +#endif + +void +SimpleThread::activate(int delay) +{ + if (status() == ThreadContext::Active) + return; + + lastActivate = curTick; + + if (status() == ThreadContext::Unallocated) { + cpu->activateWhenReady(tid); + return; + } + + _status = ThreadContext::Active; + + // status() == Suspended + cpu->activateContext(tid, delay); +} + +void +SimpleThread::suspend() +{ + if (status() == ThreadContext::Suspended) + return; + + lastActivate = curTick; + lastSuspend = curTick; +/* +#if FULL_SYSTEM + // Don't change the status from active if there are pending interrupts + if (cpu->check_interrupts()) { + assert(status() == ThreadContext::Active); + return; + } +#endif +*/ + _status = ThreadContext::Suspended; + cpu->suspendContext(tid); +} + +void +SimpleThread::deallocate() +{ + if (status() == ThreadContext::Unallocated) + return; + + _status = ThreadContext::Unallocated; + cpu->deallocateContext(tid); +} + +void +SimpleThread::halt() +{ + if (status() == ThreadContext::Halted) + return; + + _status = ThreadContext::Halted; + cpu->haltContext(tid); +} + + +void +SimpleThread::regStats(const string &name) +{ +#if FULL_SYSTEM + if (kernelStats) + kernelStats->regStats(name + ".kern"); +#endif +} + +void +SimpleThread::copyArchRegs(ThreadContext *src_tc) +{ + TheISA::copyRegs(src_tc, tc); +} + +#if FULL_SYSTEM +VirtualPort* +SimpleThread::getVirtPort(ThreadContext *src_tc) +{ + if (!src_tc) + return virtPort; + + VirtualPort *vp; + Port *mem_port; + + vp = new VirtualPort("tc-vport", src_tc); + mem_port = system->physmem->getPort("functional"); + mem_port->setPeer(vp); + vp->setPeer(mem_port); + return vp; +} + +void +SimpleThread::delVirtPort(VirtualPort *vp) +{ + if (vp != virtPort) { + delete vp->getPeer(); + delete vp; + } +} + + +#endif + diff --git a/src/cpu/simple_thread.hh b/src/cpu/simple_thread.hh new file mode 100644 index 000000000..242cfd0e1 --- /dev/null +++ b/src/cpu/simple_thread.hh @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2001-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +#ifndef __CPU_SIMPLE_THREAD_HH__ +#define __CPU_SIMPLE_THREAD_HH__ + +#include "arch/isa_traits.hh" +#include "config/full_system.hh" +#include "cpu/thread_context.hh" +#include "cpu/thread_state.hh" +#include "mem/physical.hh" +#include "mem/request.hh" +#include "sim/byteswap.hh" +#include "sim/eventq.hh" +#include "sim/host.hh" +#include "sim/serialize.hh" + +class BaseCPU; + +#if FULL_SYSTEM + +#include "sim/system.hh" +#include "arch/tlb.hh" + +class FunctionProfile; +class ProfileNode; +class FunctionalPort; +class PhysicalPort; + +namespace Kernel { + class Statistics; +}; + +#else // !FULL_SYSTEM + +#include "sim/process.hh" +#include "mem/page_table.hh" +class TranslatingPort; + +#endif // FULL_SYSTEM + +/** + * The SimpleThread object provides a combination of the ThreadState + * object and the ThreadContext interface. It implements the + * ThreadContext interface so that a ProxyThreadContext class can be + * made using SimpleThread as the template parameter (see + * thread_context.hh). It adds to the ThreadState object by adding all + * the objects needed for simple functional execution, including a + * simple architectural register file, and pointers to the ITB and DTB + * in full system mode. For CPU models that do not need more advanced + * ways to hold state (i.e. a separate physical register file, or + * separate fetch and commit PC's), this SimpleThread class provides + * all the necessary state for full architecture-level functional + * simulation. See the AtomicSimpleCPU or TimingSimpleCPU for + * examples. + */ + +class SimpleThread : public ThreadState +{ + protected: + typedef TheISA::RegFile RegFile; + typedef TheISA::MachInst MachInst; + typedef TheISA::MiscRegFile MiscRegFile; + typedef TheISA::MiscReg MiscReg; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + public: + typedef ThreadContext::Status Status; + + protected: + RegFile regs; // correct-path register context + + public: + // pointer to CPU associated with this SimpleThread + BaseCPU *cpu; + + ProxyThreadContext<SimpleThread> *tc; + + System *system; + +#if FULL_SYSTEM + AlphaITB *itb; + AlphaDTB *dtb; +#endif + + // constructor: initialize SimpleThread from given process structure +#if FULL_SYSTEM + SimpleThread(BaseCPU *_cpu, int _thread_num, System *_system, + AlphaITB *_itb, AlphaDTB *_dtb, + bool use_kernel_stats = true); +#else + SimpleThread(BaseCPU *_cpu, int _thread_num, Process *_process, int _asid, + MemObject *memobj); +#endif + + SimpleThread(); + + virtual ~SimpleThread(); + + virtual void takeOverFrom(ThreadContext *oldContext); + + void regStats(const std::string &name); + + void copyTC(ThreadContext *context); + + void copyState(ThreadContext *oldContext); + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + /*************************************************************** + * SimpleThread functions to provide CPU with access to various + * state, and to provide address translation methods. + **************************************************************/ + + /** Returns the pointer to this SimpleThread's ThreadContext. Used + * when a ThreadContext must be passed to objects outside of the + * CPU. + */ + ThreadContext *getTC() { return tc; } + +#if FULL_SYSTEM + int getInstAsid() { return regs.instAsid(); } + int getDataAsid() { return regs.dataAsid(); } + + Fault translateInstReq(RequestPtr &req) + { + return itb->translate(req, tc); + } + + Fault translateDataReadReq(RequestPtr &req) + { + return dtb->translate(req, tc, false); + } + + Fault translateDataWriteReq(RequestPtr &req) + { + return dtb->translate(req, tc, true); + } + + void dumpFuncProfile(); + + int readIntrFlag() { return regs.intrflag; } + void setIntrFlag(int val) { regs.intrflag = val; } + Fault hwrei(); + + bool simPalCheck(int palFunc); +#else + Fault translateInstReq(RequestPtr &req) + { + return process->pTable->translate(req); + } + + Fault translateDataReadReq(RequestPtr &req) + { + return process->pTable->translate(req); + } + + Fault translateDataWriteReq(RequestPtr &req) + { + return process->pTable->translate(req); + } +#endif + + /******************************************* + * ThreadContext interface functions. + ******************************************/ + + BaseCPU *getCpuPtr() { return cpu; } + + int getThreadNum() { return tid; } + +#if FULL_SYSTEM + System *getSystemPtr() { return system; } + + AlphaITB *getITBPtr() { return itb; } + + AlphaDTB *getDTBPtr() { return dtb; } + + FunctionalPort *getPhysPort() { return physPort; } + + /** Return a virtual port. If no thread context is specified then a static + * port is returned. Otherwise a port is created and returned. It must be + * deleted by deleteVirtPort(). */ + VirtualPort *getVirtPort(ThreadContext *tc); + + void delVirtPort(VirtualPort *vp); +#endif + + Status status() const { return _status; } + + void setStatus(Status newStatus) { _status = newStatus; } + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + void activate(int delay = 1); + + /// Set the status to Suspended. + void suspend(); + + /// Set the status to Unallocated. + void deallocate(); + + /// Set the status to Halted. + void halt(); + +/* + template <class T> + Fault read(RequestPtr &req, T &data) + { +#if FULL_SYSTEM && THE_ISA == ALPHA_ISA + if (req->flags & LOCKED) { + req->xc->setMiscReg(TheISA::Lock_Addr_DepTag, req->paddr); + req->xc->setMiscReg(TheISA::Lock_Flag_DepTag, true); + } +#endif + + Fault error; + error = mem->prot_read(req->paddr, data, req->size); + data = LittleEndianGuest::gtoh(data); + return error; + } + + template <class T> + Fault write(RequestPtr &req, T &data) + { +#if FULL_SYSTEM && THE_ISA == ALPHA_ISA + ExecContext *xc; + + // If this is a store conditional, act appropriately + if (req->flags & LOCKED) { + xc = req->xc; + + if (req->flags & UNCACHEABLE) { + // Don't update result register (see stq_c in isa_desc) + req->result = 2; + xc->setStCondFailures(0);//Needed? [RGD] + } else { + bool lock_flag = xc->readMiscReg(TheISA::Lock_Flag_DepTag); + Addr lock_addr = xc->readMiscReg(TheISA::Lock_Addr_DepTag); + req->result = lock_flag; + if (!lock_flag || + ((lock_addr & ~0xf) != (req->paddr & ~0xf))) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + xc->setStCondFailures(xc->readStCondFailures() + 1); + if (((xc->readStCondFailures()) % 100000) == 0) { + std::cerr << "Warning: " + << xc->readStCondFailures() + << " consecutive store conditional failures " + << "on cpu " << req->xc->readCpuId() + << std::endl; + } + return NoFault; + } + else xc->setStCondFailures(0); + } + } + + // Need to clear any locked flags on other proccessors for + // this address. Only do this for succsful Store Conditionals + // and all other stores (WH64?). Unsuccessful Store + // Conditionals would have returned above, and wouldn't fall + // through. + for (int i = 0; i < system->execContexts.size(); i++){ + xc = system->execContexts[i]; + if ((xc->readMiscReg(TheISA::Lock_Addr_DepTag) & ~0xf) == + (req->paddr & ~0xf)) { + xc->setMiscReg(TheISA::Lock_Flag_DepTag, false); + } + } + +#endif + return mem->prot_write(req->paddr, (T)htog(data), req->size); + } +*/ + virtual bool misspeculating(); + + Fault instRead(RequestPtr &req) + { + panic("instRead not implemented"); + // return funcPhysMem->read(req, inst); + return NoFault; + } + + void copyArchRegs(ThreadContext *tc); + + void clearArchRegs() { regs.clear(); } + + // + // New accessors for new decoder. + // + uint64_t readIntReg(int reg_idx) + { + return regs.readIntReg(reg_idx); + } + + FloatReg readFloatReg(int reg_idx, int width) + { + return regs.readFloatReg(reg_idx, width); + } + + FloatReg readFloatReg(int reg_idx) + { + return regs.readFloatReg(reg_idx); + } + + FloatRegBits readFloatRegBits(int reg_idx, int width) + { + return regs.readFloatRegBits(reg_idx, width); + } + + FloatRegBits readFloatRegBits(int reg_idx) + { + return regs.readFloatRegBits(reg_idx); + } + + void setIntReg(int reg_idx, uint64_t val) + { + regs.setIntReg(reg_idx, val); + } + + void setFloatReg(int reg_idx, FloatReg val, int width) + { + regs.setFloatReg(reg_idx, val, width); + } + + void setFloatReg(int reg_idx, FloatReg val) + { + regs.setFloatReg(reg_idx, val); + } + + void setFloatRegBits(int reg_idx, FloatRegBits val, int width) + { + regs.setFloatRegBits(reg_idx, val, width); + } + + void setFloatRegBits(int reg_idx, FloatRegBits val) + { + regs.setFloatRegBits(reg_idx, val); + } + + uint64_t readPC() + { + return regs.readPC(); + } + + void setPC(uint64_t val) + { + regs.setPC(val); + } + + uint64_t readNextPC() + { + return regs.readNextPC(); + } + + void setNextPC(uint64_t val) + { + regs.setNextPC(val); + } + + uint64_t readNextNPC() + { + return regs.readNextNPC(); + } + + void setNextNPC(uint64_t val) + { + regs.setNextNPC(val); + } + + MiscReg readMiscReg(int misc_reg) + { + return regs.readMiscReg(misc_reg); + } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { + return regs.readMiscRegWithEffect(misc_reg, fault, tc); + } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { + return regs.setMiscReg(misc_reg, val); + } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { + return regs.setMiscRegWithEffect(misc_reg, val, tc); + } + + unsigned readStCondFailures() { return storeCondFailures; } + + void setStCondFailures(unsigned sc_failures) + { storeCondFailures = sc_failures; } + +#if FULL_SYSTEM + bool inPalMode() { return AlphaISA::PcPAL(regs.readPC()); } +#endif + +#if !FULL_SYSTEM + TheISA::IntReg getSyscallArg(int i) + { + return regs.readIntReg(TheISA::ArgumentReg0 + i); + } + + // used to shift args for indirect syscall + void setSyscallArg(int i, TheISA::IntReg val) + { + regs.setIntReg(TheISA::ArgumentReg0 + i, val); + } + + void setSyscallReturn(SyscallReturn return_value) + { + TheISA::setSyscallReturn(return_value, ®s); + } + + void syscall(int64_t callnum) + { + process->syscall(callnum, tc); + } +#endif + + void changeRegFileContext(TheISA::RegContextParam param, + TheISA::RegContextVal val) + { + regs.changeContext(param, val); + } +}; + + +// for non-speculative execution context, spec_mode is always false +inline bool +SimpleThread::misspeculating() +{ + return false; +} + +#endif // __CPU_CPU_EXEC_CONTEXT_HH__ diff --git a/src/cpu/smt.hh b/src/cpu/smt.hh new file mode 100644 index 000000000..cac1a8fd5 --- /dev/null +++ b/src/cpu/smt.hh @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/** + * @file + * Defines SMT_MAX_THREADS. + */ + +#ifndef __SMT_HH__ +#define __SMT_HH__ + +#ifndef SMT_MAX_THREADS +/** The number of TPUs in any processor. */ +#define SMT_MAX_THREADS 4 +#endif + +/** + * The maximum number of active threads across all cpus. Used to + * initialize per-thread statistics in the cache. + * + * NB: Be careful to only use it once all the CPUs that you care about + * have been initialized + */ +extern int maxThreadsPerCPU; + +/** + * Changes the status and priority of the thread with the given number. + * @param thread_number The thread to change. + * @param activate The new active status. + * @param priority The new priority. + */ +void change_thread_state(int thread_number, int activate, int priority); + +#endif // __SMT_HH__ diff --git a/src/cpu/static_inst.cc b/src/cpu/static_inst.cc new file mode 100644 index 000000000..c311d2282 --- /dev/null +++ b/src/cpu/static_inst.cc @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +#include <iostream> +#include "cpu/static_inst.hh" +#include "sim/root.hh" + +StaticInstPtr StaticInst::nullStaticInstPtr; + +// Define the decode cache hash map. +StaticInst::DecodeCache StaticInst::decodeCache; + +void +StaticInst::dumpDecodeCacheStats() +{ + using namespace std; + + cerr << "Decode hash table stats @ " << curTick << ":" << endl; + cerr << "\tnum entries = " << decodeCache.size() << endl; + cerr << "\tnum buckets = " << decodeCache.bucket_count() << endl; + vector<int> hist(100, 0); + int max_hist = 0; + for (int i = 0; i < decodeCache.bucket_count(); ++i) { + int count = decodeCache.elems_in_bucket(i); + if (count > max_hist) + max_hist = count; + hist[count]++; + } + for (int i = 0; i <= max_hist; ++i) { + cerr << "\tbuckets of size " << i << " = " << hist[i] << endl; + } +} + +bool +StaticInst::hasBranchTarget(Addr pc, ThreadContext *tc, Addr &tgt) const +{ + if (isDirectCtrl()) { + tgt = branchTarget(pc); + return true; + } + + if (isIndirectCtrl()) { + tgt = branchTarget(tc); + return true; + } + + return false; +} + diff --git a/src/cpu/static_inst.hh b/src/cpu/static_inst.hh new file mode 100644 index 000000000..578d14191 --- /dev/null +++ b/src/cpu/static_inst.hh @@ -0,0 +1,488 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __CPU_STATIC_INST_HH__ +#define __CPU_STATIC_INST_HH__ + +#include <bitset> +#include <string> + +#include "arch/isa_traits.hh" +#include "sim/faults.hh" +#include "base/bitfield.hh" +#include "base/hashmap.hh" +#include "base/misc.hh" +#include "base/refcnt.hh" +#include "cpu/op_class.hh" +#include "cpu/o3/dyn_inst.hh" +#include "sim/faults.hh" +#include "sim/host.hh" + +// forward declarations +struct AlphaSimpleImpl; +struct OzoneImpl; +struct SimpleImpl; +class ThreadContext; +class DynInst; +class Packet; + +template <class Impl> +class OzoneDynInst; + +class CheckerCPU; +class FastCPU; +class AtomicSimpleCPU; +class TimingSimpleCPU; +class InorderCPU; +class SymbolTable; + +namespace Trace { + class InstRecord; +} + +/** + * Base, ISA-independent static instruction class. + * + * The main component of this class is the vector of flags and the + * associated methods for reading them. Any object that can rely + * solely on these flags can process instructions without being + * recompiled for multiple ISAs. + */ +class StaticInstBase : public RefCounted +{ + protected: + + /// Set of boolean static instruction properties. + /// + /// Notes: + /// - The IsInteger and IsFloating flags are based on the class of + /// registers accessed by the instruction. Although most + /// instructions will have exactly one of these two flags set, it + /// is possible for an instruction to have neither (e.g., direct + /// unconditional branches, memory barriers) or both (e.g., an + /// FP/int conversion). + /// - If IsMemRef is set, then exactly one of IsLoad or IsStore + /// will be set. + /// - If IsControl is set, then exactly one of IsDirectControl or + /// IsIndirect Control will be set, and exactly one of + /// IsCondControl or IsUncondControl will be set. + /// - IsSerializing, IsMemBarrier, and IsWriteBarrier are + /// implemented as flags since in the current model there's no + /// other way for instructions to inject behavior into the + /// pipeline outside of fetch. Once we go to an exec-in-exec CPU + /// model we should be able to get rid of these flags and + /// implement this behavior via the execute() methods. + /// + enum Flags { + IsNop, ///< Is a no-op (no effect at all). + + IsInteger, ///< References integer regs. + IsFloating, ///< References FP regs. + + IsMemRef, ///< References memory (load, store, or prefetch). + IsLoad, ///< Reads from memory (load or prefetch). + IsStore, ///< Writes to memory. + IsStoreConditional, ///< Store conditional instruction. + IsInstPrefetch, ///< Instruction-cache prefetch. + IsDataPrefetch, ///< Data-cache prefetch. + IsCopy, ///< Fast Cache block copy + + IsControl, ///< Control transfer instruction. + IsDirectControl, ///< PC relative control transfer. + IsIndirectControl, ///< Register indirect control transfer. + IsCondControl, ///< Conditional control transfer. + IsUncondControl, ///< Unconditional control transfer. + IsCall, ///< Subroutine call. + IsReturn, ///< Subroutine return. + + IsCondDelaySlot,///< Conditional Delay-Slot Instruction + + IsThreadSync, ///< Thread synchronization operation. + + IsSerializing, ///< Serializes pipeline: won't execute until all + /// older instructions have committed. + IsSerializeBefore, + IsSerializeAfter, + IsMemBarrier, ///< Is a memory barrier + IsWriteBarrier, ///< Is a write barrier + + IsNonSpeculative, ///< Should not be executed speculatively + IsQuiesce, ///< Is a quiesce instruction + + IsIprAccess, ///< Accesses IPRs + IsUnverifiable, ///< Can't be verified by a checker + + NumFlags + }; + + /// Flag values for this instruction. + std::bitset<NumFlags> flags; + + /// See opClass(). + OpClass _opClass; + + /// See numSrcRegs(). + int8_t _numSrcRegs; + + /// See numDestRegs(). + int8_t _numDestRegs; + + /// The following are used to track physical register usage + /// for machines with separate int & FP reg files. + //@{ + int8_t _numFPDestRegs; + int8_t _numIntDestRegs; + //@} + + /// Constructor. + /// It's important to initialize everything here to a sane + /// default, since the decoder generally only overrides + /// the fields that are meaningful for the particular + /// instruction. + StaticInstBase(OpClass __opClass) + : _opClass(__opClass), _numSrcRegs(0), _numDestRegs(0), + _numFPDestRegs(0), _numIntDestRegs(0) + { + } + + public: + + /// @name Register information. + /// The sum of numFPDestRegs() and numIntDestRegs() equals + /// numDestRegs(). The former two functions are used to track + /// physical register usage for machines with separate int & FP + /// reg files. + //@{ + /// Number of source registers. + int8_t numSrcRegs() const { return _numSrcRegs; } + /// Number of destination registers. + int8_t numDestRegs() const { return _numDestRegs; } + /// Number of floating-point destination regs. + int8_t numFPDestRegs() const { return _numFPDestRegs; } + /// Number of integer destination regs. + int8_t numIntDestRegs() const { return _numIntDestRegs; } + //@} + + /// @name Flag accessors. + /// These functions are used to access the values of the various + /// instruction property flags. See StaticInstBase::Flags for descriptions + /// of the individual flags. + //@{ + + bool isNop() const { return flags[IsNop]; } + + bool isMemRef() const { return flags[IsMemRef]; } + bool isLoad() const { return flags[IsLoad]; } + bool isStore() const { return flags[IsStore]; } + bool isStoreConditional() const { return flags[IsStoreConditional]; } + bool isInstPrefetch() const { return flags[IsInstPrefetch]; } + bool isDataPrefetch() const { return flags[IsDataPrefetch]; } + bool isCopy() const { return flags[IsCopy];} + + bool isInteger() const { return flags[IsInteger]; } + bool isFloating() const { return flags[IsFloating]; } + + bool isControl() const { return flags[IsControl]; } + bool isCall() const { return flags[IsCall]; } + bool isReturn() const { return flags[IsReturn]; } + bool isDirectCtrl() const { return flags[IsDirectControl]; } + bool isIndirectCtrl() const { return flags[IsIndirectControl]; } + bool isCondCtrl() const { return flags[IsCondControl]; } + bool isUncondCtrl() const { return flags[IsUncondControl]; } + bool isCondDelaySlot() const { return flags[IsCondDelaySlot]; } + + bool isThreadSync() const { return flags[IsThreadSync]; } + bool isSerializing() const { return flags[IsSerializing] || + flags[IsSerializeBefore] || + flags[IsSerializeAfter]; } + bool isSerializeBefore() const { return flags[IsSerializeBefore]; } + bool isSerializeAfter() const { return flags[IsSerializeAfter]; } + bool isMemBarrier() const { return flags[IsMemBarrier]; } + bool isWriteBarrier() const { return flags[IsWriteBarrier]; } + bool isNonSpeculative() const { return flags[IsNonSpeculative]; } + bool isQuiesce() const { return flags[IsQuiesce]; } + bool isIprAccess() const { return flags[IsIprAccess]; } + bool isUnverifiable() const { return flags[IsUnverifiable]; } + //@} + + /// Operation class. Used to select appropriate function unit in issue. + OpClass opClass() const { return _opClass; } +}; + + +// forward declaration +class StaticInstPtr; + +/** + * Generic yet ISA-dependent static instruction class. + * + * This class builds on StaticInstBase, defining fields and interfaces + * that are generic across all ISAs but that differ in details + * according to the specific ISA being used. + */ +class StaticInst : public StaticInstBase +{ + public: + + /// Binary machine instruction type. + typedef TheISA::MachInst MachInst; + /// Binary extended machine instruction type. + typedef TheISA::ExtMachInst ExtMachInst; + /// Logical register index type. + typedef TheISA::RegIndex RegIndex; + + enum { + MaxInstSrcRegs = TheISA::MaxInstSrcRegs, //< Max source regs + MaxInstDestRegs = TheISA::MaxInstDestRegs, //< Max dest regs + }; + + + /// Return logical index (architectural reg num) of i'th destination reg. + /// Only the entries from 0 through numDestRegs()-1 are valid. + RegIndex destRegIdx(int i) const { return _destRegIdx[i]; } + + /// Return logical index (architectural reg num) of i'th source reg. + /// Only the entries from 0 through numSrcRegs()-1 are valid. + RegIndex srcRegIdx(int i) const { return _srcRegIdx[i]; } + + /// Pointer to a statically allocated "null" instruction object. + /// Used to give eaCompInst() and memAccInst() something to return + /// when called on non-memory instructions. + static StaticInstPtr nullStaticInstPtr; + + /** + * Memory references only: returns "fake" instruction representing + * the effective address part of the memory operation. Used to + * obtain the dependence info (numSrcRegs and srcRegIdx[]) for + * just the EA computation. + */ + virtual const + StaticInstPtr &eaCompInst() const { return nullStaticInstPtr; } + + /** + * Memory references only: returns "fake" instruction representing + * the memory access part of the memory operation. Used to + * obtain the dependence info (numSrcRegs and srcRegIdx[]) for + * just the memory access (not the EA computation). + */ + virtual const + StaticInstPtr &memAccInst() const { return nullStaticInstPtr; } + + /// The binary machine instruction. + const ExtMachInst machInst; + + protected: + + /// See destRegIdx(). + RegIndex _destRegIdx[MaxInstDestRegs]; + /// See srcRegIdx(). + RegIndex _srcRegIdx[MaxInstSrcRegs]; + + /** + * Base mnemonic (e.g., "add"). Used by generateDisassembly() + * methods. Also useful to readily identify instructions from + * within the debugger when #cachedDisassembly has not been + * initialized. + */ + const char *mnemonic; + + /** + * String representation of disassembly (lazily evaluated via + * disassemble()). + */ + mutable std::string *cachedDisassembly; + + /** + * Internal function to generate disassembly string. + */ + virtual std::string + generateDisassembly(Addr pc, const SymbolTable *symtab) const = 0; + + /// Constructor. + StaticInst(const char *_mnemonic, ExtMachInst _machInst, OpClass __opClass) + : StaticInstBase(__opClass), + machInst(_machInst), mnemonic(_mnemonic), cachedDisassembly(0) + { + } + + public: + + virtual ~StaticInst() + { + if (cachedDisassembly) + delete cachedDisassembly; + } + +/** + * The execute() signatures are auto-generated by scons based on the + * set of CPU models we are compiling in today. + */ +#include "cpu/static_inst_exec_sigs.hh" + + /** + * Return the target address for a PC-relative branch. + * Invalid if not a PC-relative branch (i.e. isDirectCtrl() + * should be true). + */ + virtual Addr branchTarget(Addr branchPC) const + { + panic("StaticInst::branchTarget() called on instruction " + "that is not a PC-relative branch."); + } + + /** + * Return the target address for an indirect branch (jump). The + * register value is read from the supplied thread context, so + * the result is valid only if the thread context is about to + * execute the branch in question. Invalid if not an indirect + * branch (i.e. isIndirectCtrl() should be true). + */ + virtual Addr branchTarget(ThreadContext *tc) const + { + panic("StaticInst::branchTarget() called on instruction " + "that is not an indirect branch."); + } + + /** + * Return true if the instruction is a control transfer, and if so, + * return the target address as well. + */ + bool hasBranchTarget(Addr pc, ThreadContext *tc, Addr &tgt) const; + + /** + * Return string representation of disassembled instruction. + * The default version of this function will call the internal + * virtual generateDisassembly() function to get the string, + * then cache it in #cachedDisassembly. If the disassembly + * should not be cached, this function should be overridden directly. + */ + virtual const std::string &disassemble(Addr pc, + const SymbolTable *symtab = 0) const + { + if (!cachedDisassembly) + cachedDisassembly = + new std::string(generateDisassembly(pc, symtab)); + + return *cachedDisassembly; + } + + /// Decoded instruction cache type. + /// For now we're using a generic hash_map; this seems to work + /// pretty well. + typedef m5::hash_map<ExtMachInst, StaticInstPtr> DecodeCache; + + /// A cache of decoded instruction objects. + static DecodeCache decodeCache; + + /** + * Dump some basic stats on the decode cache hash map. + * Only gets called if DECODE_CACHE_HASH_STATS is defined. + */ + static void dumpDecodeCacheStats(); + + /// Decode a machine instruction. + /// @param mach_inst The binary instruction to decode. + /// @retval A pointer to the corresponding StaticInst object. + //This is defined as inline below. + static StaticInstPtr decode(ExtMachInst mach_inst); + + /// Return opcode of machine instruction + uint32_t getOpcode() { return bits(machInst, 31, 26);} + + /// Return name of machine instruction + std::string getName() { return mnemonic; } +}; + +typedef RefCountingPtr<StaticInstBase> StaticInstBasePtr; + +/// Reference-counted pointer to a StaticInst object. +/// This type should be used instead of "StaticInst *" so that +/// StaticInst objects can be properly reference-counted. +class StaticInstPtr : public RefCountingPtr<StaticInst> +{ + public: + /// Constructor. + StaticInstPtr() + : RefCountingPtr<StaticInst>() + { + } + + /// Conversion from "StaticInst *". + StaticInstPtr(StaticInst *p) + : RefCountingPtr<StaticInst>(p) + { + } + + /// Copy constructor. + StaticInstPtr(const StaticInstPtr &r) + : RefCountingPtr<StaticInst>(r) + { + } + + /// Construct directly from machine instruction. + /// Calls StaticInst::decode(). + StaticInstPtr(TheISA::ExtMachInst mach_inst) + : RefCountingPtr<StaticInst>(StaticInst::decode(mach_inst)) + { + } + + /// Convert to pointer to StaticInstBase class. + operator const StaticInstBasePtr() + { + return this->get(); + } +}; + +inline StaticInstPtr +StaticInst::decode(StaticInst::ExtMachInst mach_inst) +{ +#ifdef DECODE_CACHE_HASH_STATS + // Simple stats on decode hash_map. Turns out the default + // hash function is as good as anything I could come up with. + const int dump_every_n = 10000000; + static int decodes_til_dump = dump_every_n; + + if (--decodes_til_dump == 0) { + dumpDecodeCacheStats(); + decodes_til_dump = dump_every_n; + } +#endif + + DecodeCache::iterator iter = decodeCache.find(mach_inst); + if (iter != decodeCache.end()) { + return iter->second; + } + + StaticInstPtr si = TheISA::decodeInst(mach_inst); + decodeCache[mach_inst] = si; + return si; +} + +#endif // __CPU_STATIC_INST_HH__ diff --git a/src/cpu/thread_context.hh b/src/cpu/thread_context.hh new file mode 100644 index 000000000..73046097d --- /dev/null +++ b/src/cpu/thread_context.hh @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_THREAD_CONTEXT_HH__ +#define __CPU_THREAD_CONTEXT_HH__ + +#include "arch/types.hh" +#include "arch/regfile.hh" +#include "arch/syscallreturn.hh" +#include "config/full_system.hh" +#include "mem/request.hh" +#include "sim/faults.hh" +#include "sim/host.hh" +#include "sim/serialize.hh" +#include "sim/byteswap.hh" + +// @todo: Figure out a more architecture independent way to obtain the ITB and +// DTB pointers. +class AlphaDTB; +class AlphaITB; +class BaseCPU; +class EndQuiesceEvent; +class Event; +class TranslatingPort; +class FunctionalPort; +class VirtualPort; +class Process; +class System; +namespace Kernel { + class Statistics; +}; + +/** + * ThreadContext is the external interface to all thread state for + * anything outside of the CPU. It provides all accessor methods to + * state that might be needed by external objects, ranging from + * register values to things such as kernel stats. It is an abstract + * base class; the CPU can create its own ThreadContext by either + * deriving from it, or using the templated ProxyThreadContext. + * + * The ThreadContext is slightly different than the ExecContext. The + * ThreadContext provides access to an individual thread's state; an + * ExecContext provides ISA access to the CPU (meaning it is + * implicitly multithreaded on SMT systems). Additionally the + * ThreadState is an abstract class that exactly defines the + * interface; the ExecContext is a more implicit interface that must + * be implemented so that the ISA can access whatever state it needs. + */ +class ThreadContext +{ + protected: + typedef TheISA::RegFile RegFile; + typedef TheISA::MachInst MachInst; + typedef TheISA::IntReg IntReg; + typedef TheISA::FloatReg FloatReg; + typedef TheISA::FloatRegBits FloatRegBits; + typedef TheISA::MiscRegFile MiscRegFile; + typedef TheISA::MiscReg MiscReg; + public: + enum Status + { + /// Initialized but not running yet. All CPUs start in + /// this state, but most transition to Active on cycle 1. + /// In MP or SMT systems, non-primary contexts will stay + /// in this state until a thread is assigned to them. + Unallocated, + + /// Running. Instructions should be executed only when + /// the context is in this state. + Active, + + /// Temporarily inactive. Entered while waiting for + /// synchronization, etc. + Suspended, + + /// Permanently shut down. Entered when target executes + /// m5exit pseudo-instruction. When all contexts enter + /// this state, the simulation will terminate. + Halted + }; + + virtual ~ThreadContext() { }; + + virtual BaseCPU *getCpuPtr() = 0; + + virtual void setCpuId(int id) = 0; + + virtual int readCpuId() = 0; + +#if FULL_SYSTEM + virtual System *getSystemPtr() = 0; + + virtual AlphaITB *getITBPtr() = 0; + + virtual AlphaDTB * getDTBPtr() = 0; + + virtual Kernel::Statistics *getKernelStats() = 0; + + virtual FunctionalPort *getPhysPort() = 0; + + virtual VirtualPort *getVirtPort(ThreadContext *tc = NULL) = 0; + + virtual void delVirtPort(VirtualPort *vp) = 0; +#else + virtual TranslatingPort *getMemPort() = 0; + + virtual Process *getProcessPtr() = 0; +#endif + + virtual Status status() const = 0; + + virtual void setStatus(Status new_status) = 0; + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + virtual void activate(int delay = 1) = 0; + + /// Set the status to Suspended. + virtual void suspend() = 0; + + /// Set the status to Unallocated. + virtual void deallocate(int delay = 0) = 0; + + /// Set the status to Halted. + virtual void halt() = 0; + +#if FULL_SYSTEM + virtual void dumpFuncProfile() = 0; +#endif + + virtual void takeOverFrom(ThreadContext *old_context) = 0; + + virtual void regStats(const std::string &name) = 0; + + virtual void serialize(std::ostream &os) = 0; + virtual void unserialize(Checkpoint *cp, const std::string §ion) = 0; + +#if FULL_SYSTEM + virtual EndQuiesceEvent *getQuiesceEvent() = 0; + + // Not necessarily the best location for these... + // Having an extra function just to read these is obnoxious + virtual Tick readLastActivate() = 0; + virtual Tick readLastSuspend() = 0; + + virtual void profileClear() = 0; + virtual void profileSample() = 0; +#endif + + virtual int getThreadNum() = 0; + + // Also somewhat obnoxious. Really only used for the TLB fault. + // However, may be quite useful in SPARC. + virtual TheISA::MachInst getInst() = 0; + + virtual void copyArchRegs(ThreadContext *tc) = 0; + + virtual void clearArchRegs() = 0; + + // + // New accessors for new decoder. + // + virtual uint64_t readIntReg(int reg_idx) = 0; + + virtual FloatReg readFloatReg(int reg_idx, int width) = 0; + + virtual FloatReg readFloatReg(int reg_idx) = 0; + + virtual FloatRegBits readFloatRegBits(int reg_idx, int width) = 0; + + virtual FloatRegBits readFloatRegBits(int reg_idx) = 0; + + virtual void setIntReg(int reg_idx, uint64_t val) = 0; + + virtual void setFloatReg(int reg_idx, FloatReg val, int width) = 0; + + virtual void setFloatReg(int reg_idx, FloatReg val) = 0; + + virtual void setFloatRegBits(int reg_idx, FloatRegBits val) = 0; + + virtual void setFloatRegBits(int reg_idx, FloatRegBits val, int width) = 0; + + virtual uint64_t readPC() = 0; + + virtual void setPC(uint64_t val) = 0; + + virtual uint64_t readNextPC() = 0; + + virtual void setNextPC(uint64_t val) = 0; + + virtual uint64_t readNextNPC() = 0; + + virtual void setNextNPC(uint64_t val) = 0; + + virtual MiscReg readMiscReg(int misc_reg) = 0; + + virtual MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) = 0; + + virtual Fault setMiscReg(int misc_reg, const MiscReg &val) = 0; + + virtual Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) = 0; + + // Also not necessarily the best location for these two. Hopefully will go + // away once we decide upon where st cond failures goes. + virtual unsigned readStCondFailures() = 0; + + virtual void setStCondFailures(unsigned sc_failures) = 0; + +#if FULL_SYSTEM + virtual bool inPalMode() = 0; +#endif + + // Only really makes sense for old CPU model. Still could be useful though. + virtual bool misspeculating() = 0; + +#if !FULL_SYSTEM + virtual IntReg getSyscallArg(int i) = 0; + + // used to shift args for indirect syscall + virtual void setSyscallArg(int i, IntReg val) = 0; + + virtual void setSyscallReturn(SyscallReturn return_value) = 0; + + // Same with st cond failures. + virtual Counter readFuncExeInst() = 0; + + // This function exits the thread context in the CPU and returns + // 1 if the CPU has no more active threads (meaning it's OK to exit); + // Used in syscall-emulation mode when a thread calls the exit syscall. + virtual int exit() { return 1; }; +#endif + + virtual void changeRegFileContext(TheISA::RegContextParam param, + TheISA::RegContextVal val) = 0; +}; + +/** + * ProxyThreadContext class that provides a way to implement a + * ThreadContext without having to derive from it. ThreadContext is an + * abstract class, so anything that derives from it and uses its + * interface will pay the overhead of virtual function calls. This + * class is created to enable a user-defined Thread object to be used + * wherever ThreadContexts are used, without paying the overhead of + * virtual function calls when it is used by itself. See + * simple_thread.hh for an example of this. + */ +template <class TC> +class ProxyThreadContext : public ThreadContext +{ + public: + ProxyThreadContext(TC *actual_tc) + { actualTC = actual_tc; } + + private: + TC *actualTC; + + public: + + BaseCPU *getCpuPtr() { return actualTC->getCpuPtr(); } + + void setCpuId(int id) { actualTC->setCpuId(id); } + + int readCpuId() { return actualTC->readCpuId(); } + +#if FULL_SYSTEM + System *getSystemPtr() { return actualTC->getSystemPtr(); } + + AlphaITB *getITBPtr() { return actualTC->getITBPtr(); } + + AlphaDTB *getDTBPtr() { return actualTC->getDTBPtr(); } + + Kernel::Statistics *getKernelStats() { return actualTC->getKernelStats(); } + + FunctionalPort *getPhysPort() { return actualTC->getPhysPort(); } + + VirtualPort *getVirtPort(ThreadContext *tc = NULL) { return actualTC->getVirtPort(tc); } + + void delVirtPort(VirtualPort *vp) { return actualTC->delVirtPort(vp); } +#else + TranslatingPort *getMemPort() { return actualTC->getMemPort(); } + + Process *getProcessPtr() { return actualTC->getProcessPtr(); } +#endif + + Status status() const { return actualTC->status(); } + + void setStatus(Status new_status) { actualTC->setStatus(new_status); } + + /// Set the status to Active. Optional delay indicates number of + /// cycles to wait before beginning execution. + void activate(int delay = 1) { actualTC->activate(delay); } + + /// Set the status to Suspended. + void suspend() { actualTC->suspend(); } + + /// Set the status to Unallocated. + void deallocate(int delay = 0) { actualTC->deallocate(); } + + /// Set the status to Halted. + void halt() { actualTC->halt(); } + +#if FULL_SYSTEM + void dumpFuncProfile() { actualTC->dumpFuncProfile(); } +#endif + + void takeOverFrom(ThreadContext *oldContext) + { actualTC->takeOverFrom(oldContext); } + + void regStats(const std::string &name) { actualTC->regStats(name); } + + void serialize(std::ostream &os) { actualTC->serialize(os); } + void unserialize(Checkpoint *cp, const std::string §ion) + { actualTC->unserialize(cp, section); } + +#if FULL_SYSTEM + EndQuiesceEvent *getQuiesceEvent() { return actualTC->getQuiesceEvent(); } + + Tick readLastActivate() { return actualTC->readLastActivate(); } + Tick readLastSuspend() { return actualTC->readLastSuspend(); } + + void profileClear() { return actualTC->profileClear(); } + void profileSample() { return actualTC->profileSample(); } +#endif + + int getThreadNum() { return actualTC->getThreadNum(); } + + // @todo: Do I need this? + MachInst getInst() { return actualTC->getInst(); } + + // @todo: Do I need this? + void copyArchRegs(ThreadContext *tc) { actualTC->copyArchRegs(tc); } + + void clearArchRegs() { actualTC->clearArchRegs(); } + + // + // New accessors for new decoder. + // + uint64_t readIntReg(int reg_idx) + { return actualTC->readIntReg(reg_idx); } + + FloatReg readFloatReg(int reg_idx, int width) + { return actualTC->readFloatReg(reg_idx, width); } + + FloatReg readFloatReg(int reg_idx) + { return actualTC->readFloatReg(reg_idx); } + + FloatRegBits readFloatRegBits(int reg_idx, int width) + { return actualTC->readFloatRegBits(reg_idx, width); } + + FloatRegBits readFloatRegBits(int reg_idx) + { return actualTC->readFloatRegBits(reg_idx); } + + void setIntReg(int reg_idx, uint64_t val) + { actualTC->setIntReg(reg_idx, val); } + + void setFloatReg(int reg_idx, FloatReg val, int width) + { actualTC->setFloatReg(reg_idx, val, width); } + + void setFloatReg(int reg_idx, FloatReg val) + { actualTC->setFloatReg(reg_idx, val); } + + void setFloatRegBits(int reg_idx, FloatRegBits val, int width) + { actualTC->setFloatRegBits(reg_idx, val, width); } + + void setFloatRegBits(int reg_idx, FloatRegBits val) + { actualTC->setFloatRegBits(reg_idx, val); } + + uint64_t readPC() { return actualTC->readPC(); } + + void setPC(uint64_t val) { actualTC->setPC(val); } + + uint64_t readNextPC() { return actualTC->readNextPC(); } + + void setNextPC(uint64_t val) { actualTC->setNextPC(val); } + + uint64_t readNextNPC() { return actualTC->readNextNPC(); } + + void setNextNPC(uint64_t val) { actualTC->setNextNPC(val); } + + MiscReg readMiscReg(int misc_reg) + { return actualTC->readMiscReg(misc_reg); } + + MiscReg readMiscRegWithEffect(int misc_reg, Fault &fault) + { return actualTC->readMiscRegWithEffect(misc_reg, fault); } + + Fault setMiscReg(int misc_reg, const MiscReg &val) + { return actualTC->setMiscReg(misc_reg, val); } + + Fault setMiscRegWithEffect(int misc_reg, const MiscReg &val) + { return actualTC->setMiscRegWithEffect(misc_reg, val); } + + unsigned readStCondFailures() + { return actualTC->readStCondFailures(); } + + void setStCondFailures(unsigned sc_failures) + { actualTC->setStCondFailures(sc_failures); } +#if FULL_SYSTEM + bool inPalMode() { return actualTC->inPalMode(); } +#endif + + // @todo: Fix this! + bool misspeculating() { return actualTC->misspeculating(); } + +#if !FULL_SYSTEM + IntReg getSyscallArg(int i) { return actualTC->getSyscallArg(i); } + + // used to shift args for indirect syscall + void setSyscallArg(int i, IntReg val) + { actualTC->setSyscallArg(i, val); } + + void setSyscallReturn(SyscallReturn return_value) + { actualTC->setSyscallReturn(return_value); } + + Counter readFuncExeInst() { return actualTC->readFuncExeInst(); } +#endif + + void changeRegFileContext(TheISA::RegContextParam param, + TheISA::RegContextVal val) + { + actualTC->changeRegFileContext(param, val); + } +}; + +#endif diff --git a/src/cpu/thread_state.cc b/src/cpu/thread_state.cc new file mode 100644 index 000000000..6a96560f1 --- /dev/null +++ b/src/cpu/thread_state.cc @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#include "base/output.hh" +#include "cpu/profile.hh" +#include "cpu/thread_state.hh" +#include "sim/serialize.hh" + +#if FULL_SYSTEM +#include "cpu/quiesce_event.hh" +#include "kern/kernel_stats.hh" +#endif + +#if FULL_SYSTEM +ThreadState::ThreadState(int _cpuId, int _tid) + : cpuId(_cpuId), tid(_tid), lastActivate(0), lastSuspend(0), + profile(NULL), profileNode(NULL), profilePC(0), quiesceEvent(NULL), + funcExeInst(0), storeCondFailures(0) +#else +ThreadState::ThreadState(int _cpuId, int _tid, Process *_process, + short _asid, MemObject *mem) + : cpuId(_cpuId), tid(_tid), lastActivate(0), lastSuspend(0), + process(_process), asid(_asid), + funcExeInst(0), storeCondFailures(0) +#endif +{ + numInst = 0; + numLoad = 0; +} + +void +ThreadState::serialize(std::ostream &os) +{ + SERIALIZE_ENUM(_status); + // thread_num and cpu_id are deterministic from the config + SERIALIZE_SCALAR(funcExeInst); + SERIALIZE_SCALAR(inst); + +#if FULL_SYSTEM + Tick quiesceEndTick = 0; + if (quiesceEvent->scheduled()) + quiesceEndTick = quiesceEvent->when(); + SERIALIZE_SCALAR(quiesceEndTick); + if (kernelStats) + kernelStats->serialize(os); +#endif +} + +void +ThreadState::unserialize(Checkpoint *cp, const std::string §ion) +{ + + UNSERIALIZE_ENUM(_status); + // thread_num and cpu_id are deterministic from the config + UNSERIALIZE_SCALAR(funcExeInst); + UNSERIALIZE_SCALAR(inst); + +#if FULL_SYSTEM + Tick quiesceEndTick; + UNSERIALIZE_SCALAR(quiesceEndTick); + if (quiesceEndTick) + quiesceEvent->schedule(quiesceEndTick); + if (kernelStats) + kernelStats->unserialize(cp, section); +#endif +} + +#if FULL_SYSTEM + +void +ThreadState::profileClear() +{ + if (profile) + profile->clear(); +} + +void +ThreadState::profileSample() +{ + if (profile) + profile->sample(profileNode, profilePC); +} + +#endif diff --git a/src/cpu/thread_state.hh b/src/cpu/thread_state.hh new file mode 100644 index 000000000..5479f8478 --- /dev/null +++ b/src/cpu/thread_state.hh @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Kevin Lim + */ + +#ifndef __CPU_THREAD_STATE_HH__ +#define __CPU_THREAD_STATE_HH__ + +#include "arch/types.hh" +#include "cpu/profile.hh" +#include "cpu/thread_context.hh" + +#if !FULL_SYSTEM +#include "mem/mem_object.hh" +#include "mem/translating_port.hh" +#include "sim/process.hh" +#endif + +#if FULL_SYSTEM +class EndQuiesceEvent; +class FunctionProfile; +class ProfileNode; +namespace Kernel { + class Statistics; +}; +#endif + +class Checkpoint; + +/** + * Struct for holding general thread state that is needed across CPU + * models. This includes things such as pointers to the process, + * memory, quiesce events, and certain stats. This can be expanded + * to hold more thread-specific stats within it. + */ +struct ThreadState { + typedef ThreadContext::Status Status; + +#if FULL_SYSTEM + ThreadState(int _cpuId, int _tid); +#else + ThreadState(int _cpuId, int _tid, Process *_process, + short _asid, MemObject *mem); +#endif + + void serialize(std::ostream &os); + + void unserialize(Checkpoint *cp, const std::string §ion); + + void setCpuId(int id) { cpuId = id; } + + int readCpuId() { return cpuId; } + + void setTid(int id) { tid = id; } + + int readTid() { return tid; } + + Tick readLastActivate() { return lastActivate; } + + Tick readLastSuspend() { return lastSuspend; } + +#if FULL_SYSTEM + void dumpFuncProfile(); + + EndQuiesceEvent *getQuiesceEvent() { return quiesceEvent; } + + void profileClear(); + + void profileSample(); + + Kernel::Statistics *getKernelStats() { return kernelStats; } + + FunctionalPort *getPhysPort() { return physPort; } + + void setPhysPort(FunctionalPort *port) { physPort = port; } + + VirtualPort *getVirtPort(ThreadContext *tc = NULL) { return virtPort; } + + void setVirtPort(VirtualPort *port) { virtPort = port; } +#else + Process *getProcessPtr() { return process; } + + TranslatingPort *getMemPort() { return port; } + + void setMemPort(TranslatingPort *_port) { port = _port; } + + int getInstAsid() { return asid; } + int getDataAsid() { return asid; } +#endif + + /** Sets the current instruction being committed. */ + void setInst(TheISA::MachInst _inst) { inst = _inst; } + + /** Returns the current instruction being committed. */ + TheISA::MachInst getInst() { return inst; } + + /** Reads the number of instructions functionally executed and + * committed. + */ + Counter readFuncExeInst() { return funcExeInst; } + + /** Sets the total number of instructions functionally executed + * and committed. + */ + void setFuncExeInst(Counter new_val) { funcExeInst = new_val; } + + /** Returns the status of this thread. */ + Status status() const { return _status; } + + /** Sets the status of this thread. */ + void setStatus(Status new_status) { _status = new_status; } + + /** Number of instructions committed. */ + Counter numInst; + /** Stat for number instructions committed. */ + Stats::Scalar<> numInsts; + /** Stat for number of memory references. */ + Stats::Scalar<> numMemRefs; + + /** Number of simulated loads, used for tracking events based on + * the number of loads committed. + */ + Counter numLoad; + + /** The number of simulated loads committed prior to this run. */ + Counter startNumLoad; + + protected: + ThreadContext::Status _status; + + // ID of this context w.r.t. the System or Process object to which + // it belongs. For full-system mode, this is the system CPU ID. + int cpuId; + + // Index of hardware thread context on the CPU that this represents. + int tid; + + public: + /** Last time activate was called on this thread. */ + Tick lastActivate; + + /** Last time suspend was called on this thread. */ + Tick lastSuspend; + +#if FULL_SYSTEM + public: + FunctionProfile *profile; + ProfileNode *profileNode; + Addr profilePC; + EndQuiesceEvent *quiesceEvent; + + Kernel::Statistics *kernelStats; + protected: + /** A functional port outgoing only for functional accesses to physical + * addresses.*/ + FunctionalPort *physPort; + + /** A functional port, outgoing only, for functional accesse to virtual + * addresses. That doen't require execution context information */ + VirtualPort *virtPort; +#else + TranslatingPort *port; + + Process *process; + + // Address space ID. Note that this is used for TIMING cache + // simulation only; all functional memory accesses should use + // one of the FunctionalMemory pointers above. + short asid; + +#endif + +#if FULL_SYSTEM + void profileClear() + { + if (profile) + profile->clear(); + } + + void profileSample() + { + if (profile) + profile->sample(profileNode, profilePC); + } +#endif + + /** Current instruction the thread is committing. Only set and + * used for DTB faults currently. + */ + TheISA::MachInst inst; + + public: + /** + * Temporary storage to pass the source address from copy_load to + * copy_store. + * @todo Remove this temporary when we have a better way to do it. + */ + Addr copySrcAddr; + /** + * Temp storage for the physical source address of a copy. + * @todo Remove this temporary when we have a better way to do it. + */ + Addr copySrcPhysAddr; + + /* + * number of executed instructions, for matching with syscall trace + * points in EIO files. + */ + Counter funcExeInst; + + // + // Count failed store conditionals so we can warn of apparent + // application deadlock situations. + unsigned storeCondFailures; +}; + +#endif // __CPU_THREAD_STATE_HH__ diff --git a/src/cpu/trace/opt_cpu.cc b/src/cpu/trace/opt_cpu.cc new file mode 100644 index 000000000..996e89f01 --- /dev/null +++ b/src/cpu/trace/opt_cpu.cc @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definition of a memory trace CPU object for optimal caches. Uses a memory + * trace to access a fully associative cache with optimal replacement. + */ + +#include <algorithm> // For heap functions. + +#include "cpu/trace/opt_cpu.hh" +#include "cpu/trace/reader/mem_trace_reader.hh" + +#include "sim/builder.hh" +#include "sim/sim_events.hh" + +using namespace std; + +OptCPU::OptCPU(const string &name, + MemTraceReader *_trace, + int block_size, + int cache_size, + int _assoc) + : SimObject(name), tickEvent(this), trace(_trace), + numBlks(cache_size/block_size), assoc(_assoc), numSets(numBlks/assoc), + setMask(numSets - 1) +{ + int log_block_size = 0; + int tmp_block_size = block_size; + while (tmp_block_size > 1) { + ++log_block_size; + tmp_block_size = tmp_block_size >> 1; + } + assert(1<<log_block_size == block_size); + MemReqPtr req; + trace->getNextReq(req); + refInfo.resize(numSets); + while (req) { + RefInfo temp; + temp.addr = req->paddr >> log_block_size; + int set = temp.addr & setMask; + refInfo[set].push_back(temp); + trace->getNextReq(req); + } + + // Initialize top level of lookup table. + lookupTable.resize(16); + + // Annotate references with next ref time. + for (int k = 0; k < numSets; ++k) { + for (RefIndex i = refInfo[k].size() - 1; i >= 0; --i) { + Addr addr = refInfo[k][i].addr; + initTable(addr, InfiniteRef); + refInfo[k][i].nextRefTime = lookupValue(addr); + setValue(addr, i); + } + } + + // Reset the lookup table + for (int j = 0; j < 16; ++j) { + if (lookupTable[j].size() == (1<<16)) { + for (int k = 0; k < (1<<16); ++k) { + if (lookupTable[j][k].size() == (1<<16)) { + for (int l = 0; l < (1<<16); ++l) { + lookupTable[j][k][l] = -1; + } + } + } + } + } + + tickEvent.schedule(0); + + hits = 0; + misses = 0; +} + +void +OptCPU::processSet(int set) +{ + // Initialize cache + int blks_in_cache = 0; + RefIndex i = 0; + cacheHeap.clear(); + cacheHeap.resize(assoc); + + while (blks_in_cache < assoc) { + RefIndex cache_index = lookupValue(refInfo[set][i].addr); + if (cache_index == -1) { + // First reference to this block + misses++; + cache_index = blks_in_cache++; + setValue(refInfo[set][i].addr, cache_index); + } else { + hits++; + } + // update cache heap to most recent reference + cacheHeap[cache_index] = i; + if (++i >= refInfo[set].size()) { + return; + } + } + for (int start = assoc/2; start >= 0; --start) { + heapify(set,start); + } + //verifyHeap(set,0); + + for (; i < refInfo[set].size(); ++i) { + RefIndex cache_index = lookupValue(refInfo[set][i].addr); + if (cache_index == -1) { + // miss + misses++; + // replace from cacheHeap[0] + // mark replaced block as absent + setValue(refInfo[set][cacheHeap[0]].addr, -1); + setValue(refInfo[set][i].addr, 0); + cacheHeap[0] = i; + heapify(set, 0); + // Make sure its in the cache + assert(lookupValue(refInfo[set][i].addr) != -1); + } else { + // hit + hits++; + assert(refInfo[set][cacheHeap[cache_index]].addr == + refInfo[set][i].addr); + assert(refInfo[set][cacheHeap[cache_index]].nextRefTime == i); + assert(heapLeft(cache_index) >= assoc); + + cacheHeap[cache_index] = i; + processRankIncrease(set, cache_index); + assert(lookupValue(refInfo[set][i].addr) != -1); + } + } +} +void +OptCPU::tick() +{ + // Do opt simulation + + int references = 0; + for (int set = 0; set < numSets; ++set) { + if (!refInfo[set].empty()) { + processSet(set); + } + references += refInfo[set].size(); + } + // exit; + fprintf(stderr,"sys.cpu.misses %d #opt cache misses\n",misses); + fprintf(stderr,"sys.cpu.hits %d #opt cache hits\n", hits); + fprintf(stderr,"sys.cpu.accesses %d #opt cache acceses\n", references); + exitSimLoop("end of memory trace reached"); +} + +void +OptCPU::initTable(Addr addr, RefIndex index) +{ + int l1_index = (addr >> 32) & 0x0f; + int l2_index = (addr >> 16) & 0xffff; + assert(l1_index == addr >> 32); + if (lookupTable[l1_index].size() != (1<<16)) { + lookupTable[l1_index].resize(1<<16); + } + if (lookupTable[l1_index][l2_index].size() != (1<<16)) { + lookupTable[l1_index][l2_index].resize(1<<16, index); + } +} + +OptCPU::TickEvent::TickEvent(OptCPU *c) + : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c) +{ +} + +void +OptCPU::TickEvent::process() +{ + cpu->tick(); +} + +const char * +OptCPU::TickEvent::description() +{ + return "OptCPU tick event"; +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(OptCPU) + + SimObjectParam<MemTraceReader *> data_trace; + Param<int> size; + Param<int> block_size; +Param<int> assoc; + +END_DECLARE_SIM_OBJECT_PARAMS(OptCPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(OptCPU) + + INIT_PARAM_DFLT(data_trace, "memory trace", NULL), + INIT_PARAM(size, "cache size"), + INIT_PARAM(block_size, "block size"), + INIT_PARAM(assoc,"associativity") + +END_INIT_SIM_OBJECT_PARAMS(OptCPU) + +CREATE_SIM_OBJECT(OptCPU) +{ + return new OptCPU(getInstanceName(), + data_trace, + block_size, + size, + assoc); +} + +REGISTER_SIM_OBJECT("OptCPU", OptCPU) diff --git a/src/cpu/trace/opt_cpu.hh b/src/cpu/trace/opt_cpu.hh new file mode 100644 index 000000000..dfb122319 --- /dev/null +++ b/src/cpu/trace/opt_cpu.hh @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of a memory trace CPU object for optimal caches. Uses a memory + * trace to access a fully associative cache with optimal replacement. + */ + +#ifndef __CPU_TRACE_OPT_CPU_HH__ +#define __CPU_TRACE_OPT_CPU_HH__ + +#include <vector> + +#include "mem/mem_req.hh" // for MemReqPtr +#include "sim/eventq.hh" // for Event +#include "sim/sim_object.hh" + +// Forward Declaration +class MemTraceReader; + +/** + * A CPU object to simulate a fully-associative cache with optimal replacement. + */ +class OptCPU : public SimObject +{ + private: + typedef int RefIndex; + + typedef std::vector<RefIndex> L3Table; + typedef std::vector<L3Table> L2Table; + typedef std::vector<L2Table> L1Table; + + /** + * Event to call OptCPU::tick + */ + class TickEvent : public Event + { + private: + /** The associated CPU */ + OptCPU *cpu; + + public: + /** + * Construct this event; + */ + TickEvent(OptCPU *c); + + /** + * Call the tick function. + */ + void process(); + + /** + * Return a string description of this event. + */ + const char *description(); + }; + + TickEvent tickEvent; + + class RefInfo + { + public: + RefIndex nextRefTime; + Addr addr; + }; + + /** Reference Information, per set. */ + std::vector<std::vector<RefInfo> > refInfo; + + /** Lookup table to track blocks in the cache heap */ + L1Table lookupTable; + + /** + * Return the correct value in the lookup table. + */ + RefIndex lookupValue(Addr addr) + { + int l1_index = (addr >> 32) & 0x0f; + int l2_index = (addr >> 16) & 0xffff; + int l3_index = addr & 0xffff; + assert(l1_index == addr >> 32); + return lookupTable[l1_index][l2_index][l3_index]; + } + + /** + * Set the value in the lookup table. + */ + void setValue(Addr addr, RefIndex index) + { + int l1_index = (addr >> 32) & 0x0f; + int l2_index = (addr >> 16) & 0xffff; + int l3_index = addr & 0xffff; + assert(l1_index == addr >> 32); + lookupTable[l1_index][l2_index][l3_index]=index; + } + + /** + * Initialize the lookup table to the given value. + */ + void initTable(Addr addr, RefIndex index); + + void heapSwap(int set, int a, int b) { + RefIndex tmp = cacheHeap[a]; + cacheHeap[a] = cacheHeap[b]; + cacheHeap[b] = tmp; + + setValue(refInfo[set][cacheHeap[a]].addr, a); + setValue(refInfo[set][cacheHeap[b]].addr, b); + } + + int heapLeft(int index) { return index + index + 1; } + int heapRight(int index) { return index + index + 2; } + int heapParent(int index) { return (index - 1) >> 1; } + + RefIndex heapRank(int set, int index) { + return refInfo[set][cacheHeap[index]].nextRefTime; + } + + void heapify(int set, int start){ + int left = heapLeft(start); + int right = heapRight(start); + int max = start; + if (left < assoc && heapRank(set, left) > heapRank(set, start)) { + max = left; + } + if (right < assoc && heapRank(set, right) > heapRank(set, max)) { + max = right; + } + + if (max != start) { + heapSwap(set, start, max); + heapify(set, max); + } + } + + void verifyHeap(int set, int start) { + int left = heapLeft(start); + int right = heapRight(start); + + if (left < assoc) { + assert(heapRank(set, start) >= heapRank(set, left)); + verifyHeap(set, left); + } + if (right < assoc) { + assert(heapRank(set, start) >= heapRank(set, right)); + verifyHeap(set, right); + } + } + + void processRankIncrease(int set, int start) { + int parent = heapParent(start); + while (start > 0 && heapRank(set,parent) < heapRank(set,start)) { + heapSwap(set, parent, start); + start = parent; + parent = heapParent(start); + } + } + + void processSet(int set); + + static const RefIndex InfiniteRef = 0x7fffffff; + + /** Memory reference trace. */ + MemTraceReader *trace; + + /** Cache heap for replacement. */ + std::vector<RefIndex> cacheHeap; + + /** The number of blocks in the cache. */ + const int numBlks; + + const int assoc; + const int numSets; + const int setMask; + + + int misses; + int hits; + + public: + /** + * Construct a OptCPU object. + */ + OptCPU(const std::string &name, + MemTraceReader *_trace, + int block_size, + int cache_size, + int assoc); + + /** + * Perform the optimal replacement simulation. + */ + void tick(); +}; + +#endif // __CPU_TRACE_OPT_CPU_HH__ diff --git a/src/cpu/trace/reader/ibm_reader.cc b/src/cpu/trace/reader/ibm_reader.cc new file mode 100644 index 000000000..87e13f307 --- /dev/null +++ b/src/cpu/trace/reader/ibm_reader.cc @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of a IBM memory trace format reader. + */ +#include <sstream> + +#include "cpu/trace/reader/ibm_reader.hh" +#include "sim/builder.hh" +#include "base/misc.hh" // for fatal + +using namespace std; + +IBMReader::IBMReader(const string &name, const string &filename) + : MemTraceReader(name) +{ + if (strcmp((filename.c_str() + filename.length() -3), ".gz") == 0) { + // Compressed file, need to use a pipe to gzip. + stringstream buf; + buf << "gzip -d -c " << filename << endl; + trace = popen(buf.str().c_str(), "r"); + } else { + trace = fopen(filename.c_str(), "rb"); + } + if (!trace) { + fatal("Can't open file %s", filename); + } +} + +Tick +IBMReader::getNextReq(MemReqPtr &req) +{ + MemReqPtr tmp_req; + + int c = getc(trace); + if (c != EOF) { + tmp_req = new MemReq(); + //int cpu_id = (c & 0xf0) >> 4; + int type = c & 0x0f; + // We have L1 miss traces, so all accesses are 128 bytes + tmp_req->size = 128; + + tmp_req->paddr = 0; + for (int i = 2; i >= 0; --i) { + c = getc(trace); + if (c == EOF) { + fatal("Unexpected end of file"); + } + tmp_req->paddr |= ((c & 0xff) << (8 * i)); + } + tmp_req->paddr = tmp_req->paddr << 7; + + switch(type) { + case IBM_COND_EXCLUSIVE_FETCH: + case IBM_READ_ONLY_FETCH: + tmp_req->cmd = Read; + break; + case IBM_EXCLUSIVE_FETCH: + case IBM_FETCH_NO_DATA: + tmp_req->cmd = Write; + break; + case IBM_INST_FETCH: + tmp_req->cmd = Read; + break; + default: + fatal("Unknown trace entry type."); + } + + } + req = tmp_req; + return 0; +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(IBMReader) + + Param<string> filename; + +END_DECLARE_SIM_OBJECT_PARAMS(IBMReader) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(IBMReader) + + INIT_PARAM(filename, "trace file") + +END_INIT_SIM_OBJECT_PARAMS(IBMReader) + + +CREATE_SIM_OBJECT(IBMReader) +{ + return new IBMReader(getInstanceName(), filename); +} + +REGISTER_SIM_OBJECT("IBMReader", IBMReader) diff --git a/src/cpu/trace/reader/ibm_reader.hh b/src/cpu/trace/reader/ibm_reader.hh new file mode 100644 index 000000000..a72f62e03 --- /dev/null +++ b/src/cpu/trace/reader/ibm_reader.hh @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definition of a IBM memory trace format reader. + */ + +#ifndef __IBM_READER_HH__ +#define __IBM_READER_HH__ + +#include <stdio.h> +#include "cpu/trace/reader/mem_trace_reader.hh" +#include "mem/mem_req.hh" + +/** + * A memory trace reader for the IBM memory trace format. + */ +class IBMReader : public MemTraceReader +{ + /** IBM trace file. */ + FILE* trace; + + enum IBMType { + IBM_INST_FETCH, + IBM_READ_ONLY_FETCH, + IBM_COND_EXCLUSIVE_FETCH, + IBM_EXCLUSIVE_FETCH, + IBM_FETCH_NO_DATA + }; + + public: + /** + * Construct an IBMReader. + */ + IBMReader(const std::string &name, const std::string &filename); + + /** + * Read the next request from the trace. Returns the request in the + * provided MemReqPtr and the cycle of the request in the return value. + * @param req Return the next request from the trace. + * @return IBM traces don't store timing information, return 0 + */ + virtual Tick getNextReq(MemReqPtr &req); +}; + +#endif //__IBM_READER_HH__ + diff --git a/src/cpu/trace/reader/itx_reader.cc b/src/cpu/trace/reader/itx_reader.cc new file mode 100644 index 000000000..e4738eed8 --- /dev/null +++ b/src/cpu/trace/reader/itx_reader.cc @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of a Intel ITX memory trace format reader. + */ +#include <sstream> + +#include "cpu/trace/reader/itx_reader.hh" +#include "sim/builder.hh" +#include "base/misc.hh" // for fatal + +using namespace std; + +ITXReader::ITXReader(const string &name, const string &filename) + : MemTraceReader(name) +{ + if (strcmp((filename.c_str() + filename.length() -3), ".gz") == 0) { + // Compressed file, need to use a pipe to gzip. + stringstream buf; + buf << "gzip -d -c " << filename << endl; + trace = popen(buf.str().c_str(), "r"); + } else { + trace = fopen(filename.c_str(), "rb"); + } + if (!trace) { + fatal("Can't open file %s", filename); + } + traceFormat = 0; + int c; + for (int i = 0; i < 4; ++i) { + c = getc(trace); + if (c == EOF) { + fatal("Unexpected end of trace file."); + } + traceFormat |= (c & 0xff) << (8 * i); + } + if (traceFormat > 2) + fatal("Invalid trace format."); +} + +Tick +ITXReader::getNextReq(MemReqPtr &req) +{ + MemReqPtr tmp_req = new MemReq(); + bool phys_val; + do { + int c = getc(trace); + if (c != EOF) { + // Decode first byte + // phys_val<1> | type <2:0> | size <3:0> + phys_val = c & 0x80; + tmp_req->size = (c & 0x0f) + 1; + int type = (c & 0x70) >> 4; + + // Could be a compressed instruction entry, expand if necessary + if (type == ITXCodeComp) { + if (traceFormat != 2) { + fatal("Compressed code entry in non CompCode trace."); + } + if (!codeVirtValid) { + fatal("Corrupt CodeComp entry."); + } + + tmp_req->vaddr = codeVirtAddr; + codeVirtAddr += tmp_req->size; + if (phys_val) { + if (!codePhysValid) { + fatal("Corrupt CodeComp entry."); + } + tmp_req->paddr = codePhysAddr; + if (((tmp_req->paddr & 0xfff) + tmp_req->size) & ~0xfff) { + // Crossed page boundary, next physical address is + // invalid + codePhysValid = false; + } else { + codePhysAddr += tmp_req->size; + } + assert(tmp_req->paddr >> 36 == 0); + } else { + codePhysValid = false; + } + type = ITXCode; + tmp_req->cmd = Read; + } else { + // Normal entry + tmp_req->vaddr = 0; + for (int i = 0; i < 4; ++i) { + c = getc(trace); + if (c == EOF) { + fatal("Unexpected end of trace file."); + } + tmp_req->vaddr |= (c & 0xff) << (8 * i); + } + if (type == ITXCode) { + codeVirtAddr = tmp_req->vaddr + tmp_req->size; + codeVirtValid = true; + } + tmp_req->paddr = 0; + if (phys_val) { + c = getc(trace); + if (c == EOF) { + fatal("Unexpected end of trace file."); + } + // Get the page offset from the virtual address. + tmp_req->paddr = tmp_req->vaddr & 0xfff; + tmp_req->paddr |= (c & 0xf0) << 8; + tmp_req->paddr |= (Addr)(c & 0x0f) << 32; + for (int i = 2; i < 4; ++i) { + c = getc(trace); + if (c == EOF) { + fatal("Unexpected end of trace file."); + } + tmp_req->paddr |= (Addr)(c & 0xff) << (8 * i); + } + if (type == ITXCode) { + if (((tmp_req->paddr & 0xfff) + tmp_req->size) + & ~0xfff) { + // Crossing the page boundary, next physical + // address isn't valid + codePhysValid = false; + } else { + codePhysAddr = tmp_req->paddr + tmp_req->size; + codePhysValid = true; + } + } + assert(tmp_req->paddr >> 36 == 0); + } else if (type == ITXCode) { + codePhysValid = false; + } + switch(type) { + case ITXRead: + tmp_req->cmd = Read; + break; + case ITXWrite: + tmp_req->cmd = Write; + break; + case ITXWriteback: + tmp_req->cmd = Writeback; + break; + case ITXCode: + tmp_req->cmd = Read; + tmp_req->flags |= INST_READ; + break; + default: + fatal("Unknown ITX type"); + } + } + } else { + // EOF need to return a null request + MemReqPtr null_req; + req = null_req; + return 0; + } + } while (!phys_val); + req = tmp_req; + assert(!req || (req->paddr >> 36) == 0); + return 0; +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(ITXReader) + + Param<string> filename; + +END_DECLARE_SIM_OBJECT_PARAMS(ITXReader) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(ITXReader) + + INIT_PARAM(filename, "trace file") + +END_INIT_SIM_OBJECT_PARAMS(ITXReader) + + +CREATE_SIM_OBJECT(ITXReader) +{ + return new ITXReader(getInstanceName(), filename); +} + +REGISTER_SIM_OBJECT("ITXReader", ITXReader) diff --git a/src/cpu/trace/reader/itx_reader.hh b/src/cpu/trace/reader/itx_reader.hh new file mode 100644 index 000000000..63a4c9ac9 --- /dev/null +++ b/src/cpu/trace/reader/itx_reader.hh @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definition of a Intel ITX memory trace format reader. + */ + +#ifndef __ITX_READER_HH__ +#define __ITX_READER_HH__ + +#include <stdio.h> +#include <string> + +#include "cpu/trace/reader/mem_trace_reader.hh" +#include "mem/mem_req.hh" + + +/** + * A memory trace reader for the Intel ITX memory trace format. + */ +class ITXReader : public MemTraceReader +{ + private: + /** Trace file. */ + FILE *trace; + + bool codeVirtValid; + Addr codeVirtAddr; + bool codePhysValid; + Addr codePhysAddr; + + int traceFormat; + + enum ITXType { + ITXRead, + ITXWrite, + ITXWriteback, + ITXCode, + ITXCodeComp + }; + + public: + /** + * Construct an ITXReader. + */ + ITXReader(const std::string &name, const std::string &filename); + + /** + * Read the next request from the trace. Returns the request in the + * provided MemReqPtr and the cycle of the request in the return value. + * @param req Return the next request from the trace. + * @return ITX traces don't store timing information, return 0 + */ + virtual Tick getNextReq(MemReqPtr &req); +}; + +#endif //__ITX_READER_HH__ + diff --git a/src/cpu/trace/reader/m5_reader.cc b/src/cpu/trace/reader/m5_reader.cc new file mode 100644 index 000000000..8efcb022b --- /dev/null +++ b/src/cpu/trace/reader/m5_reader.cc @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of a memory trace reader for a M5 memory trace. + */ + +#include "cpu/trace/reader/m5_reader.hh" +#include "mem/trace/m5_format.hh" +#include "mem/mem_cmd.hh" +#include "sim/builder.hh" + +using namespace std; + +M5Reader::M5Reader(const string &name, const string &filename) + : MemTraceReader(name) +{ + traceFile.open(filename.c_str(), ios::binary); +} + +Tick +M5Reader::getNextReq(MemReqPtr &req) +{ + M5Format ref; + + MemReqPtr tmp_req; + // Need to read EOF char before eof() will return true. + traceFile.read((char*) &ref, sizeof(ref)); + if (!traceFile.eof()) { + //traceFile.read((char*) &ref, sizeof(ref)); +#ifndef NDEBUG + int gcount = traceFile.gcount(); + assert(gcount != 0 || traceFile.eof()); + assert(gcount == sizeof(ref)); + assert(ref.cmd < 12); +#endif + tmp_req = new MemReq(); + tmp_req->paddr = ref.paddr; + tmp_req->asid = ref.asid; + // Assume asid == thread_num + tmp_req->thread_num = ref.asid; + tmp_req->cmd = (MemCmdEnum)ref.cmd; + tmp_req->size = ref.size; + tmp_req->dest = ref.dest; + } else { + ref.cycle = 0; + } + req = tmp_req; + return ref.cycle; +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(M5Reader) + + Param<string> filename; + +END_DECLARE_SIM_OBJECT_PARAMS(M5Reader) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(M5Reader) + + INIT_PARAM(filename, "trace file") + +END_INIT_SIM_OBJECT_PARAMS(M5Reader) + + +CREATE_SIM_OBJECT(M5Reader) +{ + return new M5Reader(getInstanceName(), filename); +} + +REGISTER_SIM_OBJECT("M5Reader", M5Reader) diff --git a/src/cpu/trace/reader/m5_reader.hh b/src/cpu/trace/reader/m5_reader.hh new file mode 100644 index 000000000..5007bfd5b --- /dev/null +++ b/src/cpu/trace/reader/m5_reader.hh @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definition of a memory trace reader for a M5 memory trace. + */ + +#ifndef __M5_READER_HH__ +#define __M5_READER_HH__ + +#include <fstream> + +#include "cpu/trace/reader/mem_trace_reader.hh" + +/** + * A memory trace reader for an M5 memory trace. @sa M5Writer. + */ +class M5Reader : public MemTraceReader +{ + /** The traceFile. */ + std::ifstream traceFile; + + std::string fn; + + public: + /** + * Construct an M5 memory trace reader. + */ + M5Reader(const std::string &name, const std::string &filename); + + + /** + * Read the next request from the trace. Returns the request in the + * provided MemReqPtr and the cycle of the request in the return value. + * @param req Return the next request from the trace. + * @return The cycle the reference was started. + */ + virtual Tick getNextReq(MemReqPtr &req); +}; + +#endif // __M5_READER_HH__ diff --git a/src/cpu/trace/reader/mem_trace_reader.cc b/src/cpu/trace/reader/mem_trace_reader.cc new file mode 100644 index 000000000..5623f168a --- /dev/null +++ b/src/cpu/trace/reader/mem_trace_reader.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * SimObject Declaration of pure virtual MemTraceReader class. + */ + +#include "cpu/trace/reader/mem_trace_reader.hh" +#include "sim/param.hh" + +DEFINE_SIM_OBJECT_CLASS_NAME("MemTraceReader", MemTraceReader); diff --git a/src/cpu/trace/reader/mem_trace_reader.hh b/src/cpu/trace/reader/mem_trace_reader.hh new file mode 100644 index 000000000..628a3ecdc --- /dev/null +++ b/src/cpu/trace/reader/mem_trace_reader.hh @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * Definitions for a pure virtual interface to a memory trace reader. + */ + +#ifndef __MEM_TRACE_READER_HH__ +#define __MEM_TRACE_READER_HH__ + +#include "sim/sim_object.hh" +#include "mem/mem_req.hh" // For MemReqPtr + +/** + * Pure virtual base class for memory trace readers. + */ +class MemTraceReader : public SimObject +{ + public: + /** Construct this MemoryTrace reader. */ + MemTraceReader(const std::string &name) : SimObject(name) {} + + /** + * Read the next request from the trace. Returns the request in the + * provided MemReqPtr and the cycle of the request in the return value. + * @param req Return the next request from the trace. + * @return The cycle of the request, 0 if none in trace. + */ + virtual Tick getNextReq(MemReqPtr &req) = 0; +}; + +#endif //__MEM_TRACE_READER_HH__ diff --git a/src/cpu/trace/trace_cpu.cc b/src/cpu/trace/trace_cpu.cc new file mode 100644 index 000000000..3c9da4849 --- /dev/null +++ b/src/cpu/trace/trace_cpu.cc @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of a memory trace CPU object. Uses a memory trace to drive the + * provided memory hierarchy. + */ + +#include <algorithm> // For min + +#include "cpu/trace/trace_cpu.hh" +#include "cpu/trace/reader/mem_trace_reader.hh" +#include "mem/base_mem.hh" // For PARAM constructor +#include "mem/mem_interface.hh" +#include "sim/builder.hh" +#include "sim/sim_events.hh" + +using namespace std; + +TraceCPU::TraceCPU(const string &name, + MemInterface *icache_interface, + MemInterface *dcache_interface, + MemTraceReader *data_trace) + : SimObject(name), icacheInterface(icache_interface), + dcacheInterface(dcache_interface), + dataTrace(data_trace), outstandingRequests(0), tickEvent(this) +{ + assert(dcacheInterface); + nextCycle = dataTrace->getNextReq(nextReq); + tickEvent.schedule(0); +} + +void +TraceCPU::tick() +{ + assert(outstandingRequests >= 0); + assert(outstandingRequests < 1000); + int instReqs = 0; + int dataReqs = 0; + + while (nextReq && curTick >= nextCycle) { + assert(nextReq->thread_num < 4 && "Not enough threads"); + if (nextReq->isInstRead() && icacheInterface) { + if (icacheInterface->isBlocked()) + break; + + nextReq->time = curTick; + if (nextReq->cmd == Squash) { + icacheInterface->squash(nextReq->asid); + } else { + ++instReqs; + if (icacheInterface->doEvents()) { + nextReq->completionEvent = + new TraceCompleteEvent(nextReq, this); + icacheInterface->access(nextReq); + } else { + icacheInterface->access(nextReq); + completeRequest(nextReq); + } + } + } else { + if (dcacheInterface->isBlocked()) + break; + + ++dataReqs; + nextReq->time = curTick; + if (dcacheInterface->doEvents()) { + nextReq->completionEvent = + new TraceCompleteEvent(nextReq, this); + dcacheInterface->access(nextReq); + } else { + dcacheInterface->access(nextReq); + completeRequest(nextReq); + } + + } + nextCycle = dataTrace->getNextReq(nextReq); + } + + if (!nextReq) { + // No more requests to send. Finish trailing events and exit. + if (mainEventQueue.empty()) { + exitSimLoop("end of memory trace reached"); + } else { + tickEvent.schedule(mainEventQueue.nextEventTime() + cycles(1)); + } + } else { + tickEvent.schedule(max(curTick + cycles(1), nextCycle)); + } +} + +void +TraceCPU::completeRequest(MemReqPtr& req) +{ +} + +void +TraceCompleteEvent::process() +{ + tester->completeRequest(req); +} + +const char * +TraceCompleteEvent::description() +{ + return "trace access complete"; +} + +TraceCPU::TickEvent::TickEvent(TraceCPU *c) + : Event(&mainEventQueue, CPU_Tick_Pri), cpu(c) +{ +} + +void +TraceCPU::TickEvent::process() +{ + cpu->tick(); +} + +const char * +TraceCPU::TickEvent::description() +{ + return "TraceCPU tick event"; +} + + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(TraceCPU) + + SimObjectParam<BaseMem *> icache; + SimObjectParam<BaseMem *> dcache; + SimObjectParam<MemTraceReader *> data_trace; + +END_DECLARE_SIM_OBJECT_PARAMS(TraceCPU) + +BEGIN_INIT_SIM_OBJECT_PARAMS(TraceCPU) + + INIT_PARAM_DFLT(icache, "instruction cache", NULL), + INIT_PARAM_DFLT(dcache, "data cache", NULL), + INIT_PARAM_DFLT(data_trace, "data trace", NULL) + +END_INIT_SIM_OBJECT_PARAMS(TraceCPU) + +CREATE_SIM_OBJECT(TraceCPU) +{ + return new TraceCPU(getInstanceName(), + (icache) ? icache->getInterface() : NULL, + (dcache) ? dcache->getInterface() : NULL, + data_trace); +} + +REGISTER_SIM_OBJECT("TraceCPU", TraceCPU) + diff --git a/src/cpu/trace/trace_cpu.hh b/src/cpu/trace/trace_cpu.hh new file mode 100644 index 000000000..9c96d71d5 --- /dev/null +++ b/src/cpu/trace/trace_cpu.hh @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of a memory trace CPU object. Uses a memory trace to drive the + * provided memory hierarchy. + */ + +#ifndef __CPU_TRACE_TRACE_CPU_HH__ +#define __CPU_TRACE_TRACE_CPU_HH__ + +#include <string> + +#include "mem/mem_req.hh" // for MemReqPtr +#include "sim/eventq.hh" // for Event +#include "sim/sim_object.hh" + +// Forward declaration. +class MemInterface; +class MemTraceReader; + +/** + * A cpu object for running memory traces through a memory hierarchy. + */ +class TraceCPU : public SimObject +{ + private: + /** Interface for instruction trace requests, if any. */ + MemInterface *icacheInterface; + /** Interface for data trace requests, if any. */ + MemInterface *dcacheInterface; + + /** Data reference trace. */ + MemTraceReader *dataTrace; + + /** Number of outstanding requests. */ + int outstandingRequests; + + /** Cycle of the next request, 0 if not available. */ + Tick nextCycle; + + /** Next request. */ + MemReqPtr nextReq; + + /** + * Event to call the TraceCPU::tick + */ + class TickEvent : public Event + { + private: + /** The associated CPU */ + TraceCPU *cpu; + + public: + /** + * Construct this event; + */ + TickEvent(TraceCPU *c); + + /** + * Call the tick function. + */ + void process(); + + /** + * Return a string description of this event. + */ + const char *description(); + }; + + TickEvent tickEvent; + + public: + /** + * Construct a TraceCPU object. + */ + TraceCPU(const std::string &name, + MemInterface *icache_interface, + MemInterface *dcache_interface, + MemTraceReader *data_trace); + + inline Tick cycles(int numCycles) { return numCycles; } + + /** + * Perform all the accesses for one cycle. + */ + void tick(); + + /** + * Handle a completed memory request. + */ + void completeRequest(MemReqPtr &req); +}; + +class TraceCompleteEvent : public Event +{ + MemReqPtr req; + TraceCPU *tester; + + public: + + TraceCompleteEvent(MemReqPtr &_req, TraceCPU *_tester) + : Event(&mainEventQueue), req(_req), tester(_tester) + { + setFlags(AutoDelete); + } + + void process(); + + virtual const char *description(); +}; + +#endif // __CPU_TRACE_TRACE_CPU_HH__ + diff --git a/src/dev/alpha_access.h b/src/dev/alpha_access.h new file mode 100644 index 000000000..4adeaf84b --- /dev/null +++ b/src/dev/alpha_access.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __ALPHA_ACCESS_H__ +#define __ALPHA_ACCESS_H__ + +/** @file + * System Console Memory Mapped Register Definition + */ + +#define ALPHA_ACCESS_VERSION (1305) + +#ifdef CONSOLE +typedef unsigned uint32_t; +typedef unsigned long uint64_t; +#endif + +// This structure hacked up from simos +struct AlphaAccess +{ + uint32_t last_offset; // 00: must be first field + uint32_t version; // 04: + uint32_t numCPUs; // 08: + uint32_t intrClockFrequency; // 0C: Hz + uint64_t cpuClock; // 10: MHz + uint64_t mem_size; // 18: + + // Loaded kernel + uint64_t kernStart; // 20: + uint64_t kernEnd; // 28: + uint64_t entryPoint; // 30: + + // console disk stuff + uint64_t diskUnit; // 38: + uint64_t diskCount; // 40: + uint64_t diskPAddr; // 48: + uint64_t diskBlock; // 50: + uint64_t diskOperation; // 58: + + // console simple output stuff + uint64_t outputChar; // 60: Placeholder for output + uint64_t inputChar; // 68: Placeholder for input + + // MP boot + uint64_t cpuStack[64]; // 70: +}; + +#endif // __ALPHA_ACCESS_H__ diff --git a/src/dev/alpha_console.cc b/src/dev/alpha_console.cc new file mode 100644 index 000000000..181bbf934 --- /dev/null +++ b/src/dev/alpha_console.cc @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ali Saidi + * Steve Reinhardt + * Erik Hallnor + */ + +/** @file + * Alpha Console Definition + */ + +#include <cstddef> +#include <string> + +#include "arch/alpha/system.hh" +#include "base/inifile.hh" +#include "base/str.hh" +#include "base/trace.hh" +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "dev/alpha_console.hh" +#include "dev/platform.hh" +#include "dev/simconsole.hh" +#include "dev/simple_disk.hh" +#include "mem/physical.hh" +#include "sim/builder.hh" +#include "sim/sim_object.hh" + +using namespace std; +using namespace AlphaISA; + +AlphaConsole::AlphaConsole(Params *p) + : BasicPioDevice(p), disk(p->disk), + console(params()->cons), system(params()->alpha_sys), cpu(params()->cpu) +{ + + pioSize = sizeof(struct AlphaAccess); + + alphaAccess = new Access(); + alphaAccess->last_offset = pioSize - 1; + + alphaAccess->version = ALPHA_ACCESS_VERSION; + alphaAccess->diskUnit = 1; + + alphaAccess->diskCount = 0; + alphaAccess->diskPAddr = 0; + alphaAccess->diskBlock = 0; + alphaAccess->diskOperation = 0; + alphaAccess->outputChar = 0; + alphaAccess->inputChar = 0; + bzero(alphaAccess->cpuStack, sizeof(alphaAccess->cpuStack)); + +} + +void +AlphaConsole::startup() +{ + system->setAlphaAccess(pioAddr); + alphaAccess->numCPUs = system->getNumCPUs(); + alphaAccess->kernStart = system->getKernelStart(); + alphaAccess->kernEnd = system->getKernelEnd(); + alphaAccess->entryPoint = system->getKernelEntry(); + alphaAccess->mem_size = system->physmem->size(); + alphaAccess->cpuClock = cpu->frequency() / 1000000; // In MHz + alphaAccess->intrClockFrequency = params()->platform->intrFrequency(); +} + +Tick +AlphaConsole::read(Packet *pkt) +{ + + /** XXX Do we want to push the addr munging to a bus brige or something? So + * the device has it's physical address and then the bridge adds on whatever + * machine dependent address swizzle is required? + */ + + assert(pkt->result == Packet::Unknown); + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + + Addr daddr = pkt->getAddr() - pioAddr; + + pkt->allocate(); + + switch (pkt->getSize()) + { + case sizeof(uint32_t): + switch (daddr) + { + case offsetof(AlphaAccess, last_offset): + pkt->set(alphaAccess->last_offset); + break; + case offsetof(AlphaAccess, version): + pkt->set(alphaAccess->version); + break; + case offsetof(AlphaAccess, numCPUs): + pkt->set(alphaAccess->numCPUs); + break; + case offsetof(AlphaAccess, intrClockFrequency): + pkt->set(alphaAccess->intrClockFrequency); + break; + default: + /* Old console code read in everyting as a 32bit int + * we now break that for better error checking. + */ + pkt->result = Packet::BadAddress; + } + DPRINTF(AlphaConsole, "read: offset=%#x val=%#x\n", daddr, + pkt->get<uint32_t>()); + break; + case sizeof(uint64_t): + switch (daddr) + { + case offsetof(AlphaAccess, inputChar): + pkt->set(console->console_in()); + break; + case offsetof(AlphaAccess, cpuClock): + pkt->set(alphaAccess->cpuClock); + break; + case offsetof(AlphaAccess, mem_size): + pkt->set(alphaAccess->mem_size); + break; + case offsetof(AlphaAccess, kernStart): + pkt->set(alphaAccess->kernStart); + break; + case offsetof(AlphaAccess, kernEnd): + pkt->set(alphaAccess->kernEnd); + break; + case offsetof(AlphaAccess, entryPoint): + pkt->set(alphaAccess->entryPoint); + break; + case offsetof(AlphaAccess, diskUnit): + pkt->set(alphaAccess->diskUnit); + break; + case offsetof(AlphaAccess, diskCount): + pkt->set(alphaAccess->diskCount); + break; + case offsetof(AlphaAccess, diskPAddr): + pkt->set(alphaAccess->diskPAddr); + break; + case offsetof(AlphaAccess, diskBlock): + pkt->set(alphaAccess->diskBlock); + break; + case offsetof(AlphaAccess, diskOperation): + pkt->set(alphaAccess->diskOperation); + break; + case offsetof(AlphaAccess, outputChar): + pkt->set(alphaAccess->outputChar); + break; + default: + int cpunum = (daddr - offsetof(AlphaAccess, cpuStack)) / + sizeof(alphaAccess->cpuStack[0]); + + if (cpunum >= 0 && cpunum < 64) + pkt->set(alphaAccess->cpuStack[cpunum]); + else + panic("Unknown 64bit access, %#x\n", daddr); + } + DPRINTF(AlphaConsole, "read: offset=%#x val=%#x\n", daddr, + pkt->get<uint64_t>()); + break; + default: + pkt->result = Packet::BadAddress; + } + if (pkt->result == Packet::Unknown) + pkt->result = Packet::Success; + return pioDelay; +} + +Tick +AlphaConsole::write(Packet *pkt) +{ + assert(pkt->result == Packet::Unknown); + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + Addr daddr = pkt->getAddr() - pioAddr; + + uint64_t val = pkt->get<uint64_t>(); + assert(pkt->getSize() == sizeof(uint64_t)); + + switch (daddr) { + case offsetof(AlphaAccess, diskUnit): + alphaAccess->diskUnit = val; + break; + + case offsetof(AlphaAccess, diskCount): + alphaAccess->diskCount = val; + break; + + case offsetof(AlphaAccess, diskPAddr): + alphaAccess->diskPAddr = val; + break; + + case offsetof(AlphaAccess, diskBlock): + alphaAccess->diskBlock = val; + break; + + case offsetof(AlphaAccess, diskOperation): + if (val == 0x13) + disk->read(alphaAccess->diskPAddr, alphaAccess->diskBlock, + alphaAccess->diskCount); + else + panic("Invalid disk operation!"); + + break; + + case offsetof(AlphaAccess, outputChar): + console->out((char)(val & 0xff)); + break; + + default: + int cpunum = (daddr - offsetof(AlphaAccess, cpuStack)) / + sizeof(alphaAccess->cpuStack[0]); + warn("%d: Trying to launch CPU number %d!", curTick, cpunum); + assert(val > 0 && "Must not access primary cpu"); + if (cpunum >= 0 && cpunum < 64) + alphaAccess->cpuStack[cpunum] = val; + else + panic("Unknown 64bit access, %#x\n", daddr); + } + + pkt->result = Packet::Success; + + return pioDelay; +} + +void +AlphaConsole::Access::serialize(ostream &os) +{ + SERIALIZE_SCALAR(last_offset); + SERIALIZE_SCALAR(version); + SERIALIZE_SCALAR(numCPUs); + SERIALIZE_SCALAR(mem_size); + SERIALIZE_SCALAR(cpuClock); + SERIALIZE_SCALAR(intrClockFrequency); + SERIALIZE_SCALAR(kernStart); + SERIALIZE_SCALAR(kernEnd); + SERIALIZE_SCALAR(entryPoint); + SERIALIZE_SCALAR(diskUnit); + SERIALIZE_SCALAR(diskCount); + SERIALIZE_SCALAR(diskPAddr); + SERIALIZE_SCALAR(diskBlock); + SERIALIZE_SCALAR(diskOperation); + SERIALIZE_SCALAR(outputChar); + SERIALIZE_SCALAR(inputChar); + SERIALIZE_ARRAY(cpuStack,64); +} + +void +AlphaConsole::Access::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(last_offset); + UNSERIALIZE_SCALAR(version); + UNSERIALIZE_SCALAR(numCPUs); + UNSERIALIZE_SCALAR(mem_size); + UNSERIALIZE_SCALAR(cpuClock); + UNSERIALIZE_SCALAR(intrClockFrequency); + UNSERIALIZE_SCALAR(kernStart); + UNSERIALIZE_SCALAR(kernEnd); + UNSERIALIZE_SCALAR(entryPoint); + UNSERIALIZE_SCALAR(diskUnit); + UNSERIALIZE_SCALAR(diskCount); + UNSERIALIZE_SCALAR(diskPAddr); + UNSERIALIZE_SCALAR(diskBlock); + UNSERIALIZE_SCALAR(diskOperation); + UNSERIALIZE_SCALAR(outputChar); + UNSERIALIZE_SCALAR(inputChar); + UNSERIALIZE_ARRAY(cpuStack, 64); +} + +void +AlphaConsole::serialize(ostream &os) +{ + alphaAccess->serialize(os); +} + +void +AlphaConsole::unserialize(Checkpoint *cp, const std::string §ion) +{ + alphaAccess->unserialize(cp, section); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(AlphaConsole) + + SimObjectParam<SimConsole *> sim_console; + SimObjectParam<SimpleDisk *> disk; + Param<Addr> pio_addr; + SimObjectParam<AlphaSystem *> system; + SimObjectParam<BaseCPU *> cpu; + SimObjectParam<Platform *> platform; + Param<Tick> pio_latency; + +END_DECLARE_SIM_OBJECT_PARAMS(AlphaConsole) + +BEGIN_INIT_SIM_OBJECT_PARAMS(AlphaConsole) + + INIT_PARAM(sim_console, "The Simulator Console"), + INIT_PARAM(disk, "Simple Disk"), + INIT_PARAM(pio_addr, "Device Address"), + INIT_PARAM(system, "system object"), + INIT_PARAM(cpu, "Processor"), + INIT_PARAM(platform, "platform"), + INIT_PARAM_DFLT(pio_latency, "Programmed IO latency", 1000) + +END_INIT_SIM_OBJECT_PARAMS(AlphaConsole) + +CREATE_SIM_OBJECT(AlphaConsole) +{ + AlphaConsole::Params *p = new AlphaConsole::Params; + p->name = getInstanceName(); + p->platform = platform; + p->pio_addr = pio_addr; + p->pio_delay = pio_latency; + p->cons = sim_console; + p->disk = disk; + p->alpha_sys = system; + p->system = system; + p->cpu = cpu; + return new AlphaConsole(p); +} + +REGISTER_SIM_OBJECT("AlphaConsole", AlphaConsole) diff --git a/src/dev/alpha_console.hh b/src/dev/alpha_console.hh new file mode 100644 index 000000000..b6360d40f --- /dev/null +++ b/src/dev/alpha_console.hh @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/** @file + * System Console Interface + */ + +#ifndef __ALPHA_CONSOLE_HH__ +#define __ALPHA_CONSOLE_HH__ + +#include "base/range.hh" +#include "dev/alpha_access.h" +#include "dev/io_device.hh" +#include "sim/host.hh" +#include "sim/sim_object.hh" + +class BaseCPU; +class SimConsole; +class AlphaSystem; +class SimpleDisk; + +/** + * Memory mapped interface to the system console. This device + * represents a shared data region between the OS Kernel and the + * System Console. + * + * The system console is a small standalone program that is initially + * run when the system boots. It contains the necessary code to + * access the boot disk, to read/write from the console, and to pass + * boot parameters to the kernel. + * + * This version of the system console is very different from the one + * that would be found in a real system. Many of the functions use + * some sort of backdoor to get their job done. For example, reading + * from the boot device on a real system would require a minimal + * device driver to access the disk controller, but since we have a + * simulator here, we are able to bypass the disk controller and + * access the disk image directly. There are also some things like + * reading the kernel off the disk image into memory that are normally + * taken care of by the console that are now taken care of by the + * simulator. + * + * These shortcuts are acceptable since the system console is + * primarily used doing boot before the kernel has loaded its device + * drivers. + */ +class AlphaConsole : public BasicPioDevice +{ + protected: + struct Access : public AlphaAccess + { + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + }; + + union { + Access *alphaAccess; + uint8_t *consoleData; + }; + + /** the disk must be accessed from the console */ + SimpleDisk *disk; + + /** the system console (the terminal) is accessable from the console */ + SimConsole *console; + + /** a pointer to the system we are running in */ + AlphaSystem *system; + + /** a pointer to the CPU boot cpu */ + BaseCPU *cpu; + + public: + struct Params : public BasicPioDevice::Params + { + SimConsole *cons; + SimpleDisk *disk; + AlphaSystem *alpha_sys; + BaseCPU *cpu; + }; + protected: + const Params *params() const {return (const Params *)_params; } + + public: + + /** Standard Constructor */ + AlphaConsole(Params *p); + + virtual void startup(); + + /** + * memory mapped reads and writes + */ + virtual Tick read(Packet *pkt); + virtual Tick write(Packet *pkt); + + /** + * standard serialization routines for checkpointing + */ + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +#endif // __ALPHA_CONSOLE_HH__ diff --git a/src/dev/baddev.cc b/src/dev/baddev.cc new file mode 100644 index 000000000..e4297be19 --- /dev/null +++ b/src/dev/baddev.cc @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** @file + * BadDevice implemenation + */ + +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "dev/baddev.hh" +#include "dev/platform.hh" +#include "mem/port.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; +using namespace TheISA; + +BadDevice::BadDevice(Params *p) + : BasicPioDevice(p), devname(p->device_name) +{ + pioSize = 0xf; +} + +Tick +BadDevice::read(Packet *pkt) +{ + panic("Device %s not imlpmented\n", devname); +} + +Tick +BadDevice::write(Packet *pkt) +{ + panic("Device %s not imlpmented\n", devname); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(BadDevice) + + Param<string> devicename; + Param<Addr> pio_addr; + SimObjectParam<System *> system; + SimObjectParam<Platform *> platform; + Param<Tick> pio_latency; + +END_DECLARE_SIM_OBJECT_PARAMS(BadDevice) + +BEGIN_INIT_SIM_OBJECT_PARAMS(BadDevice) + + INIT_PARAM(devicename, "Name of device to error on"), + INIT_PARAM(pio_addr, "Device Address"), + INIT_PARAM(system, "system object"), + INIT_PARAM(platform, "platform"), + INIT_PARAM_DFLT(pio_latency, "Programmed IO latency", 1000) + +END_INIT_SIM_OBJECT_PARAMS(BadDevice) + +CREATE_SIM_OBJECT(BadDevice) +{ + BadDevice::Params *p = new BadDevice::Params; + p->name =getInstanceName(); + p->platform = platform; + p->pio_addr = pio_addr; + p->pio_delay = pio_latency; + p->system = system; + p->device_name = devicename; + return new BadDevice(p); +} + +REGISTER_SIM_OBJECT("BadDevice", BadDevice) diff --git a/src/dev/baddev.hh b/src/dev/baddev.hh new file mode 100644 index 000000000..d7d778af4 --- /dev/null +++ b/src/dev/baddev.hh @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** @file + * This devices just panics when touched. For example if you have a + * kernel that touches the frame buffer which isn't allowed. + */ + +#ifndef __DEV_BADDEV_HH__ +#define __DEV_BADDEV_HH__ + +#include "base/range.hh" +#include "dev/io_device.hh" + + +/** + * BadDevice + * This device just panics when accessed. It is supposed to warn + * the user that the kernel they are running has unsupported + * options (i.e. frame buffer) + */ +class BadDevice : public BasicPioDevice +{ + private: + std::string devname; + + public: + struct Params : public BasicPioDevice::Params + { + std::string device_name; + }; + protected: + const Params *params() const { return (const Params *)_params; } + + public: + /** + * Constructor for the Baddev Class. + * @param p object parameters + * @param a base address of the write + */ + BadDevice(Params *p); + + virtual Tick read(Packet *pkt); + virtual Tick write(Packet *pkt); +}; + +#endif // __DEV_BADDEV_HH__ diff --git a/src/dev/disk_image.cc b/src/dev/disk_image.cc new file mode 100644 index 000000000..f70d2ccdb --- /dev/null +++ b/src/dev/disk_image.cc @@ -0,0 +1,472 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/** @file + * Disk Image Definitions + */ + +#include <sys/types.h> +#include <sys/uio.h> +#include <errno.h> +#include <unistd.h> + +#include <cstring> +#include <fstream> +#include <string> + +#include "base/callback.hh" +#include "base/misc.hh" +#include "base/trace.hh" +#include "dev/disk_image.hh" +#include "sim/builder.hh" +#include "sim/sim_exit.hh" +#include "sim/byteswap.hh" + +using namespace std; + +//////////////////////////////////////////////////////////////////////// +// +// Raw Disk image +// +RawDiskImage::RawDiskImage(const string &name, const string &filename, + bool rd_only) + : DiskImage(name), disk_size(0) +{ open(filename, rd_only); } + +RawDiskImage::~RawDiskImage() +{ close(); } + +void +RawDiskImage::open(const string &filename, bool rd_only) +{ + if (!filename.empty()) { + initialized = true; + readonly = rd_only; + file = filename; + + ios::openmode mode = ios::in | ios::binary; + if (!readonly) + mode |= ios::out; + stream.open(file.c_str(), mode); + if (!stream.is_open()) + panic("Error opening %s", filename); + } +} + +void +RawDiskImage::close() +{ + stream.close(); +} + +off_t +RawDiskImage::size() const +{ + if (disk_size == 0) { + if (!stream.is_open()) + panic("file not open!\n"); + stream.seekg(0, ios::end); + disk_size = stream.tellg(); + } + + return disk_size / SectorSize; +} + +off_t +RawDiskImage::read(uint8_t *data, off_t offset) const +{ + if (!initialized) + panic("RawDiskImage not initialized"); + + if (!stream.is_open()) + panic("file not open!\n"); + + if (stream.seekg(offset * SectorSize, ios::beg) < 0) + panic("Could not seek to location in file"); + + streampos pos = stream.tellg(); + stream.read((char *)data, SectorSize); + + DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset); + DDUMP(DiskImageRead, data, SectorSize); + + return stream.tellg() - pos; +} + +off_t +RawDiskImage::write(const uint8_t *data, off_t offset) +{ + if (!initialized) + panic("RawDiskImage not initialized"); + + if (readonly) + panic("Cannot write to a read only disk image"); + + if (!stream.is_open()) + panic("file not open!\n"); + + if (stream.seekp(offset * SectorSize, ios::beg) < 0) + panic("Could not seek to location in file"); + + DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset); + DDUMP(DiskImageWrite, data, SectorSize); + + streampos pos = stream.tellp(); + stream.write((const char *)data, SectorSize); + return stream.tellp() - pos; +} + +DEFINE_SIM_OBJECT_CLASS_NAME("DiskImage", DiskImage) + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage) + + Param<string> image_file; + Param<bool> read_only; + +END_DECLARE_SIM_OBJECT_PARAMS(RawDiskImage) + +BEGIN_INIT_SIM_OBJECT_PARAMS(RawDiskImage) + + INIT_PARAM(image_file, "disk image file"), + INIT_PARAM_DFLT(read_only, "read only image", false) + +END_INIT_SIM_OBJECT_PARAMS(RawDiskImage) + + +CREATE_SIM_OBJECT(RawDiskImage) +{ + return new RawDiskImage(getInstanceName(), image_file, read_only); +} + +REGISTER_SIM_OBJECT("RawDiskImage", RawDiskImage) + +//////////////////////////////////////////////////////////////////////// +// +// Copy on Write Disk image +// +const int CowDiskImage::VersionMajor = 1; +const int CowDiskImage::VersionMinor = 0; + +CowDiskImage::CowDiskImage(const string &name, DiskImage *kid, int hash_size) + : DiskImage(name), child(kid), table(NULL) +{ init(hash_size); } + +class CowDiskCallback : public Callback +{ + private: + CowDiskImage *image; + + public: + CowDiskCallback(CowDiskImage *i) : image(i) {} + void process() { image->save(); delete this; } +}; + +CowDiskImage::CowDiskImage(const string &name, DiskImage *kid, int hash_size, + const string &file, bool read_only) + : DiskImage(name), filename(file), child(kid), table(NULL) +{ + if (!open(filename)) { + assert(!read_only && "why have a non-existent read only file?"); + init(hash_size); + } + + if (!read_only) + registerExitCallback(new CowDiskCallback(this)); +} + +CowDiskImage::~CowDiskImage() +{ + SectorTable::iterator i = table->begin(); + SectorTable::iterator end = table->end(); + + while (i != end) { + delete (*i).second; + ++i; + } +} + +void +SafeRead(ifstream &stream, void *data, int count) +{ + stream.read((char *)data, count); + if (!stream.is_open()) + panic("file not open"); + + if (stream.eof()) + panic("premature end-of-file"); + + if (stream.bad() || stream.fail()) + panic("error reading cowdisk image"); +} + +template<class T> +void +SafeRead(ifstream &stream, T &data) +{ + SafeRead(stream, &data, sizeof(data)); +} + +template<class T> +void +SafeReadSwap(ifstream &stream, T &data) +{ + SafeRead(stream, &data, sizeof(data)); + data = letoh(data); //is this the proper byte order conversion? +} + +bool +CowDiskImage::open(const string &file) +{ + ifstream stream(file.c_str()); + if (!stream.is_open()) + return false; + + if (stream.fail() || stream.bad()) + panic("Error opening %s", file); + + uint64_t magic; + SafeRead(stream, magic); + + if (memcmp(&magic, "COWDISK!", sizeof(magic)) != 0) + panic("Could not open %s: Invalid magic", file); + + uint32_t major, minor; + SafeReadSwap(stream, major); + SafeReadSwap(stream, minor); + + if (major != VersionMajor && minor != VersionMinor) + panic("Could not open %s: invalid version %d.%d != %d.%d", + file, major, minor, VersionMajor, VersionMinor); + + uint64_t sector_count; + SafeReadSwap(stream, sector_count); + table = new SectorTable(sector_count); + + + for (uint64_t i = 0; i < sector_count; i++) { + uint64_t offset; + SafeReadSwap(stream, offset); + + Sector *sector = new Sector; + SafeRead(stream, sector, sizeof(Sector)); + + assert(table->find(offset) == table->end()); + (*table)[offset] = sector; + } + + stream.close(); + + initialized = true; + return true; +} + +void +CowDiskImage::init(int hash_size) +{ + table = new SectorTable(hash_size); + + initialized = true; +} + +void +SafeWrite(ofstream &stream, const void *data, int count) +{ + stream.write((const char *)data, count); + if (!stream.is_open()) + panic("file not open"); + + if (stream.eof()) + panic("premature end-of-file"); + + if (stream.bad() || stream.fail()) + panic("error reading cowdisk image"); +} + +template<class T> +void +SafeWrite(ofstream &stream, const T &data) +{ + SafeWrite(stream, &data, sizeof(data)); +} + +template<class T> +void +SafeWriteSwap(ofstream &stream, const T &data) +{ + T swappeddata = letoh(data); //is this the proper byte order conversion? + SafeWrite(stream, &swappeddata, sizeof(data)); +} +void +CowDiskImage::save() +{ + save(filename); +} + +void +CowDiskImage::save(const string &file) +{ + if (!initialized) + panic("RawDiskImage not initialized"); + + ofstream stream(file.c_str()); + if (!stream.is_open() || stream.fail() || stream.bad()) + panic("Error opening %s", file); + + uint64_t magic; + memcpy(&magic, "COWDISK!", sizeof(magic)); + SafeWrite(stream, magic); + + SafeWriteSwap(stream, (uint32_t)VersionMajor); + SafeWriteSwap(stream, (uint32_t)VersionMinor); + SafeWriteSwap(stream, (uint64_t)table->size()); + + uint64_t size = table->size(); + SectorTable::iterator iter = table->begin(); + SectorTable::iterator end = table->end(); + + for (uint64_t i = 0; i < size; i++) { + if (iter == end) + panic("Incorrect Table Size during save of COW disk image"); + + SafeWriteSwap(stream, (uint64_t)(*iter).first); + SafeWrite(stream, (*iter).second->data, sizeof(Sector)); + ++iter; + } + + stream.close(); +} + +void +CowDiskImage::writeback() +{ + SectorTable::iterator i = table->begin(); + SectorTable::iterator end = table->end(); + + while (i != end) { + child->write((*i).second->data, (*i).first); + ++i; + } +} + +off_t +CowDiskImage::size() const +{ return child->size(); } + +off_t +CowDiskImage::read(uint8_t *data, off_t offset) const +{ + if (!initialized) + panic("CowDiskImage not initialized"); + + if (offset > size()) + panic("access out of bounds"); + + SectorTable::const_iterator i = table->find(offset); + if (i == table->end()) + return child->read(data, offset); + else { + memcpy(data, (*i).second->data, SectorSize); + DPRINTF(DiskImageRead, "read: offset=%d\n", (uint64_t)offset); + DDUMP(DiskImageRead, data, SectorSize); + return SectorSize; + } +} + +off_t +CowDiskImage::write(const uint8_t *data, off_t offset) +{ + if (!initialized) + panic("RawDiskImage not initialized"); + + if (offset > size()) + panic("access out of bounds"); + + SectorTable::iterator i = table->find(offset); + if (i == table->end()) { + Sector *sector = new Sector; + memcpy(sector, data, SectorSize); + table->insert(make_pair(offset, sector)); + } else { + memcpy((*i).second->data, data, SectorSize); + } + + DPRINTF(DiskImageWrite, "write: offset=%d\n", (uint64_t)offset); + DDUMP(DiskImageWrite, data, SectorSize); + + return SectorSize; +} + +void +CowDiskImage::serialize(ostream &os) +{ + string cowFilename = name() + ".cow"; + SERIALIZE_SCALAR(cowFilename); + save(Checkpoint::dir() + "/" + cowFilename); +} + +void +CowDiskImage::unserialize(Checkpoint *cp, const string §ion) +{ + string cowFilename; + UNSERIALIZE_SCALAR(cowFilename); + cowFilename = cp->cptDir + "/" + cowFilename; + open(cowFilename); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage) + + SimObjectParam<DiskImage *> child; + Param<string> image_file; + Param<int> table_size; + Param<bool> read_only; + +END_DECLARE_SIM_OBJECT_PARAMS(CowDiskImage) + +BEGIN_INIT_SIM_OBJECT_PARAMS(CowDiskImage) + + INIT_PARAM(child, "child image"), + INIT_PARAM_DFLT(image_file, "disk image file", ""), + INIT_PARAM_DFLT(table_size, "initial table size", 65536), + INIT_PARAM_DFLT(read_only, "don't write back to the copy-on-write file", + true) + +END_INIT_SIM_OBJECT_PARAMS(CowDiskImage) + + +CREATE_SIM_OBJECT(CowDiskImage) +{ + if (((string)image_file).empty()) + return new CowDiskImage(getInstanceName(), child, table_size); + else + return new CowDiskImage(getInstanceName(), child, table_size, + image_file, read_only); +} + +REGISTER_SIM_OBJECT("CowDiskImage", CowDiskImage) diff --git a/src/dev/disk_image.hh b/src/dev/disk_image.hh new file mode 100644 index 000000000..45d5af649 --- /dev/null +++ b/src/dev/disk_image.hh @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/** @file + * Disk Image Interfaces + */ + +#ifndef __DISK_IMAGE_HH__ +#define __DISK_IMAGE_HH__ + +#include <fstream> + +#include "base/hashmap.hh" +#include "sim/sim_object.hh" + +#define SectorSize (512) + +/** + * Basic interface for accessing a disk image. + */ +class DiskImage : public SimObject +{ + protected: + bool initialized; + + public: + DiskImage(const std::string &name) : SimObject(name), initialized(false) {} + virtual ~DiskImage() {} + + virtual off_t size() const = 0; + + virtual off_t read(uint8_t *data, off_t offset) const = 0; + virtual off_t write(const uint8_t *data, off_t offset) = 0; +}; + +/** + * Specialization for accessing a raw disk image + */ +class RawDiskImage : public DiskImage +{ + protected: + mutable std::fstream stream; + std::string file; + bool readonly; + mutable off_t disk_size; + + public: + RawDiskImage(const std::string &name, const std::string &filename, + bool rd_only); + ~RawDiskImage(); + + void close(); + void open(const std::string &filename, bool rd_only = false); + + virtual off_t size() const; + + virtual off_t read(uint8_t *data, off_t offset) const; + virtual off_t write(const uint8_t *data, off_t offset); +}; + +/** + * Specialization for accessing a copy-on-write disk image layer. + * A copy-on-write(COW) layer must be stacked on top of another disk + * image layer this layer can be another CowDiskImage, or a + * RawDiskImage. + * + * This object is designed to provide a mechanism for persistant + * changes to a main disk image, or to provide a place for temporary + * changes to the image to take place that later may be thrown away. + */ +class CowDiskImage : public DiskImage +{ + public: + static const int VersionMajor; + static const int VersionMinor; + + protected: + struct Sector { + uint8_t data[SectorSize]; + }; + typedef m5::hash_map<uint64_t, Sector *> SectorTable; + + protected: + std::string filename; + DiskImage *child; + SectorTable *table; + + public: + CowDiskImage(const std::string &name, DiskImage *kid, int hash_size); + CowDiskImage(const std::string &name, DiskImage *kid, int hash_size, + const std::string &filename, bool read_only); + ~CowDiskImage(); + + void init(int hash_size); + bool open(const std::string &file); + void save(); + void save(const std::string &file); + void writeback(); + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + virtual off_t size() const; + + virtual off_t read(uint8_t *data, off_t offset) const; + virtual off_t write(const uint8_t *data, off_t offset); +}; + +#endif // __DISK_IMAGE_HH__ diff --git a/src/dev/etherbus.cc b/src/dev/etherbus.cc new file mode 100644 index 000000000..348bb818a --- /dev/null +++ b/src/dev/etherbus.cc @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/* @file + * Device module for modelling an ethernet hub + */ + +#include <cmath> +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "dev/etherbus.hh" +#include "dev/etherdump.hh" +#include "dev/etherint.hh" +#include "dev/etherpkt.hh" +#include "sim/builder.hh" +#include "sim/root.hh" + +using namespace std; + +EtherBus::EtherBus(const string &name, double speed, bool loop, + EtherDump *packet_dump) + : SimObject(name), ticksPerByte(speed), loopback(loop), + event(&mainEventQueue, this), sender(0), dump(packet_dump) +{ +} + +void +EtherBus::txDone() +{ + devlist_t::iterator i = devlist.begin(); + devlist_t::iterator end = devlist.end(); + + DPRINTF(Ethernet, "ethernet packet received: length=%d\n", packet->length); + DDUMP(EthernetData, packet->data, packet->length); + + while (i != end) { + if (loopback || *i != sender) + (*i)->sendPacket(packet); + ++i; + } + + sender->sendDone(); + + if (dump) + dump->dump(packet); + + sender = 0; + packet = 0; +} + +void +EtherBus::reg(EtherInt *dev) +{ devlist.push_back(dev); } + +bool +EtherBus::send(EtherInt *sndr, EthPacketPtr &pkt) +{ + if (busy()) { + DPRINTF(Ethernet, "ethernet packet not sent, bus busy\n", curTick); + return false; + } + + DPRINTF(Ethernet, "ethernet packet sent: length=%d\n", pkt->length); + DDUMP(EthernetData, pkt->data, pkt->length); + + packet = pkt; + sender = sndr; + int delay = (int)ceil(((double)pkt->length * ticksPerByte) + 1.0); + DPRINTF(Ethernet, "scheduling packet: delay=%d, (rate=%f)\n", + delay, ticksPerByte); + event.schedule(curTick + delay); + + return true; +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(EtherBus) + + Param<bool> loopback; + Param<double> speed; + SimObjectParam<EtherDump *> packet_dump; + +END_DECLARE_SIM_OBJECT_PARAMS(EtherBus) + +BEGIN_INIT_SIM_OBJECT_PARAMS(EtherBus) + + INIT_PARAM(loopback, "send the packet back to the sending interface"), + INIT_PARAM(speed, "bus speed in ticks per byte"), + INIT_PARAM(packet_dump, "object to dump network packets to") + +END_INIT_SIM_OBJECT_PARAMS(EtherBus) + +CREATE_SIM_OBJECT(EtherBus) +{ + return new EtherBus(getInstanceName(), speed, loopback, packet_dump); +} + +REGISTER_SIM_OBJECT("EtherBus", EtherBus) diff --git a/src/dev/etherbus.hh b/src/dev/etherbus.hh new file mode 100644 index 000000000..2ff1d0a5e --- /dev/null +++ b/src/dev/etherbus.hh @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/* @file + * Device module for modelling an ethernet hub + */ + +#ifndef __ETHERBUS_H__ +#define __ETHERBUS_H__ + +#include "sim/eventq.hh" +#include "dev/etherpkt.hh" +#include "sim/sim_object.hh" + +class EtherDump; +class EtherInt; +class EtherBus : public SimObject +{ + protected: + typedef std::list<EtherInt *> devlist_t; + devlist_t devlist; + double ticksPerByte; + bool loopback; + + protected: + class DoneEvent : public Event + { + protected: + EtherBus *bus; + + public: + DoneEvent(EventQueue *q, EtherBus *b) + : Event(q), bus(b) {} + virtual void process() { bus->txDone(); } + virtual const char *description() { return "ethernet bus completion"; } + }; + + DoneEvent event; + EthPacketPtr packet; + EtherInt *sender; + EtherDump *dump; + + public: + EtherBus(const std::string &name, double speed, bool loopback, + EtherDump *dump); + virtual ~EtherBus() {} + + void txDone(); + void reg(EtherInt *dev); + bool busy() const { return (bool)packet; } + bool send(EtherInt *sender, EthPacketPtr &packet); +}; + +#endif // __ETHERBUS_H__ diff --git a/src/dev/etherdump.cc b/src/dev/etherdump.cc new file mode 100644 index 000000000..0c986cc21 --- /dev/null +++ b/src/dev/etherdump.cc @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/* @file + * Simple object for creating a simple pcap style packet trace + */ + +#include <sys/time.h> + +#include <algorithm> +#include <string> + +#include "base/misc.hh" +#include "base/output.hh" +#include "dev/etherdump.hh" +#include "sim/builder.hh" +#include "sim/root.hh" + +using std::string; + +EtherDump::EtherDump(const string &name, const string &file, int max) + : SimObject(name), stream(file.c_str()), maxlen(max) +{ +} + +#define DLT_EN10MB 1 // Ethernet (10Mb) +#define TCPDUMP_MAGIC 0xa1b2c3d4 +#define PCAP_VERSION_MAJOR 2 +#define PCAP_VERSION_MINOR 4 + +struct pcap_file_header { + uint32_t magic; + uint16_t version_major; + uint16_t version_minor; + int32_t thiszone; // gmt to local correction + uint32_t sigfigs; // accuracy of timestamps + uint32_t snaplen; // max length saved portion of each pkt + uint32_t linktype; // data link type (DLT_*) +}; + +struct pcap_pkthdr { + uint32_t seconds; + uint32_t microseconds; + uint32_t caplen; // length of portion present + uint32_t len; // length this packet (off wire) +}; + +void +EtherDump::init() +{ + curtime = time(NULL); + struct pcap_file_header hdr; + hdr.magic = TCPDUMP_MAGIC; + hdr.version_major = PCAP_VERSION_MAJOR; + hdr.version_minor = PCAP_VERSION_MINOR; + + hdr.thiszone = -5 * 3600; + hdr.snaplen = 1500; + hdr.sigfigs = 0; + hdr.linktype = DLT_EN10MB; + + stream.write(reinterpret_cast<char *>(&hdr), sizeof(hdr)); + + /* + * output an empty packet with the current time so that we know + * when the simulation began. This allows us to correlate packets + * to sim_cycles. + */ + pcap_pkthdr pkthdr; + pkthdr.seconds = curtime; + pkthdr.microseconds = 0; + pkthdr.caplen = 0; + pkthdr.len = 0; + stream.write(reinterpret_cast<char *>(&pkthdr), sizeof(pkthdr)); + + stream.flush(); +} + +void +EtherDump::dumpPacket(EthPacketPtr &packet) +{ + pcap_pkthdr pkthdr; + pkthdr.seconds = curtime + (curTick / Clock::Int::s); + pkthdr.microseconds = (curTick / Clock::Int::us) % ULL(1000000); + pkthdr.caplen = std::min(packet->length, maxlen); + pkthdr.len = packet->length; + stream.write(reinterpret_cast<char *>(&pkthdr), sizeof(pkthdr)); + stream.write(reinterpret_cast<char *>(packet->data), pkthdr.caplen); + stream.flush(); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(EtherDump) + + Param<string> file; + Param<int> maxlen; + +END_DECLARE_SIM_OBJECT_PARAMS(EtherDump) + +BEGIN_INIT_SIM_OBJECT_PARAMS(EtherDump) + + INIT_PARAM(file, "file to dump packets to"), + INIT_PARAM(maxlen, "max portion of packet data to dump") + +END_INIT_SIM_OBJECT_PARAMS(EtherDump) + +CREATE_SIM_OBJECT(EtherDump) +{ + return new EtherDump(getInstanceName(), simout.resolve(file), maxlen); +} + +REGISTER_SIM_OBJECT("EtherDump", EtherDump) diff --git a/src/dev/etherdump.hh b/src/dev/etherdump.hh new file mode 100644 index 000000000..f3080f341 --- /dev/null +++ b/src/dev/etherdump.hh @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/* @file + * Simple object for creating a simple pcap style packet trace + */ + +#ifndef __ETHERDUMP_H__ +#define __ETHERDUMP_H__ + +#include <fstream> +#include "dev/etherpkt.hh" +#include "sim/sim_object.hh" + +/* + * Simple object for creating a simple pcap style packet trace + */ +class EtherDump : public SimObject +{ + private: + std::ofstream stream; + const int maxlen; + void dumpPacket(EthPacketPtr &packet); + void init(); + + Tick curtime; + + public: + EtherDump(const std::string &name, const std::string &file, int max); + + inline void dump(EthPacketPtr &pkt) { dumpPacket(pkt); } +}; + +#endif // __ETHERDUMP_H__ diff --git a/src/dev/etherint.cc b/src/dev/etherint.cc new file mode 100644 index 000000000..3f81591e9 --- /dev/null +++ b/src/dev/etherint.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include "dev/etherint.hh" +#include "base/misc.hh" +#include "sim/param.hh" +#include "sim/sim_object.hh" + +void +EtherInt::setPeer(EtherInt *p) +{ + if (peer && peer != p) + panic("You cannot change the peer once it is set.\n" + "Current peer=%s Desired peer=%s", peer->name(), p->name()); + + peer = p; +} + +DEFINE_SIM_OBJECT_CLASS_NAME("EtherInt", EtherInt) + diff --git a/src/dev/etherint.hh b/src/dev/etherint.hh new file mode 100644 index 000000000..dfc224ecc --- /dev/null +++ b/src/dev/etherint.hh @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/* @file + * Class representing the actual interface between two ethernet + * components. + */ + +#ifndef __DEV_ETHERINT_HH__ +#define __DEV_ETHERINT_HH__ + +#include <string> + +#include "dev/etherpkt.hh" +#include "sim/sim_object.hh" + +/* + * Class representing the actual interface between two ethernet + * components. These components are intended to attach to another + * ethernet interface on one side and whatever device on the other. + */ +class EtherInt : public SimObject +{ + protected: + EtherInt *peer; + + public: + EtherInt(const std::string &name) : SimObject(name), peer(NULL) {} + virtual ~EtherInt() {} + + void setPeer(EtherInt *p); + + void recvDone() { peer->sendDone(); } + virtual void sendDone() = 0; + + bool sendPacket(EthPacketPtr packet) + { return peer ? peer->recvPacket(packet) : true; } + virtual bool recvPacket(EthPacketPtr packet) = 0; +}; + +#endif // __DEV_ETHERINT_HH__ diff --git a/src/dev/etherlink.cc b/src/dev/etherlink.cc new file mode 100644 index 000000000..cd3812270 --- /dev/null +++ b/src/dev/etherlink.cc @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ron Dreslinski + */ + +/* @file + * Device module for modelling a fixed bandwidth full duplex ethernet link + */ + +#include <cmath> +#include <deque> +#include <string> +#include <vector> + +#include "base/random.hh" +#include "base/trace.hh" +#include "dev/etherdump.hh" +#include "dev/etherint.hh" +#include "dev/etherlink.hh" +#include "dev/etherpkt.hh" +#include "sim/builder.hh" +#include "sim/serialize.hh" +#include "sim/system.hh" +#include "sim/root.hh" + +using namespace std; + +EtherLink::EtherLink(const string &name, EtherInt *peer0, EtherInt *peer1, + double rate, Tick delay, Tick delayVar, EtherDump *dump) + : SimObject(name) +{ + link[0] = new Link(name + ".link0", this, 0, rate, delay, delayVar, dump); + link[1] = new Link(name + ".link1", this, 1, rate, delay, delayVar, dump); + + interface[0] = new Interface(name + ".int0", link[0], link[1]); + interface[1] = new Interface(name + ".int1", link[1], link[0]); + + interface[0]->setPeer(peer0); + peer0->setPeer(interface[0]); + interface[1]->setPeer(peer1); + peer1->setPeer(interface[1]); +} + +EtherLink::~EtherLink() +{ + delete link[0]; + delete link[1]; + + delete interface[0]; + delete interface[1]; +} + +EtherLink::Interface::Interface(const string &name, Link *tx, Link *rx) + : EtherInt(name), txlink(tx) +{ + tx->setTxInt(this); + rx->setRxInt(this); +} + +EtherLink::Link::Link(const string &name, EtherLink *p, int num, + double rate, Tick delay, Tick delay_var, EtherDump *d) + : objName(name), parent(p), number(num), txint(NULL), rxint(NULL), + ticksPerByte(rate), linkDelay(delay), delayVar(delay_var), dump(d), + doneEvent(this) +{ } + +void +EtherLink::serialize(ostream &os) +{ + link[0]->serialize("link0", os); + link[1]->serialize("link1", os); +} + +void +EtherLink::unserialize(Checkpoint *cp, const string §ion) +{ + link[0]->unserialize("link0", cp, section); + link[1]->unserialize("link1", cp, section); +} + +void +EtherLink::Link::txComplete(EthPacketPtr packet) +{ + DPRINTF(Ethernet, "packet received: len=%d\n", packet->length); + DDUMP(EthernetData, packet->data, packet->length); + rxint->sendPacket(packet); +} + +class LinkDelayEvent : public Event +{ + protected: + EtherLink::Link *link; + EthPacketPtr packet; + + public: + // non-scheduling version for createForUnserialize() + LinkDelayEvent(); + LinkDelayEvent(EtherLink::Link *link, EthPacketPtr pkt, Tick when); + + void process(); + + virtual void serialize(ostream &os); + virtual void unserialize(Checkpoint *cp, const string §ion); + static Serializable *createForUnserialize(Checkpoint *cp, + const string §ion); +}; + +void +EtherLink::Link::txDone() +{ + if (dump) + dump->dump(packet); + + if (linkDelay > 0) { + DPRINTF(Ethernet, "packet delayed: delay=%d\n", linkDelay); + new LinkDelayEvent(this, packet, curTick + linkDelay); + } else { + txComplete(packet); + } + + packet = 0; + assert(!busy()); + + txint->sendDone(); +} + +bool +EtherLink::Link::transmit(EthPacketPtr pkt) +{ + if (busy()) { + DPRINTF(Ethernet, "packet not sent, link busy\n"); + return false; + } + + DPRINTF(Ethernet, "packet sent: len=%d\n", pkt->length); + DDUMP(EthernetData, pkt->data, pkt->length); + + packet = pkt; + Tick delay = (Tick)ceil(((double)pkt->length * ticksPerByte) + 1.0); + if (delayVar != 0) { + Random<Tick> var; + delay += var.uniform(0, delayVar); + } + DPRINTF(Ethernet, "scheduling packet: delay=%d, (rate=%f)\n", + delay, ticksPerByte); + doneEvent.schedule(curTick + delay); + + return true; +} + +void +EtherLink::Link::serialize(const string &base, ostream &os) +{ + bool packet_exists = packet; + paramOut(os, base + ".packet_exists", packet_exists); + if (packet_exists) + packet->serialize(base + ".packet", os); + + bool event_scheduled = doneEvent.scheduled(); + paramOut(os, base + ".event_scheduled", event_scheduled); + if (event_scheduled) { + Tick event_time = doneEvent.when(); + paramOut(os, base + ".event_time", event_time); + } + +} + +void +EtherLink::Link::unserialize(const string &base, Checkpoint *cp, + const string §ion) +{ + bool packet_exists; + paramIn(cp, section, base + ".packet_exists", packet_exists); + if (packet_exists) { + packet = new EthPacketData(16384); + packet->unserialize(base + ".packet", cp, section); + } + + bool event_scheduled; + paramIn(cp, section, base + ".event_scheduled", event_scheduled); + if (event_scheduled) { + Tick event_time; + paramIn(cp, section, base + ".event_time", event_time); + doneEvent.schedule(event_time); + } +} + +LinkDelayEvent::LinkDelayEvent() + : Event(&mainEventQueue), link(NULL) +{ + setFlags(AutoSerialize); + setFlags(AutoDelete); +} + +LinkDelayEvent::LinkDelayEvent(EtherLink::Link *l, EthPacketPtr p, Tick when) + : Event(&mainEventQueue), link(l), packet(p) +{ + setFlags(AutoSerialize); + setFlags(AutoDelete); + schedule(when); +} + +void +LinkDelayEvent::process() +{ + link->txComplete(packet); +} + +void +LinkDelayEvent::serialize(ostream &os) +{ + paramOut(os, "type", string("LinkDelayEvent")); + Event::serialize(os); + + EtherLink *parent = link->parent; + bool number = link->number; + SERIALIZE_OBJPTR(parent); + SERIALIZE_SCALAR(number); + + packet->serialize("packet", os); +} + + +void +LinkDelayEvent::unserialize(Checkpoint *cp, const string §ion) +{ + Event::unserialize(cp, section); + + EtherLink *parent; + bool number; + UNSERIALIZE_OBJPTR(parent); + UNSERIALIZE_SCALAR(number); + + link = parent->link[number]; + + packet = new EthPacketData(16384); + packet->unserialize("packet", cp, section); +} + + +Serializable * +LinkDelayEvent::createForUnserialize(Checkpoint *cp, const string §ion) +{ + return new LinkDelayEvent(); +} + +REGISTER_SERIALIZEABLE("LinkDelayEvent", LinkDelayEvent) + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(EtherLink) + + SimObjectParam<EtherInt *> int1; + SimObjectParam<EtherInt *> int2; + Param<double> speed; + Param<Tick> delay; + Param<Tick> delay_var; + SimObjectParam<EtherDump *> dump; + +END_DECLARE_SIM_OBJECT_PARAMS(EtherLink) + +BEGIN_INIT_SIM_OBJECT_PARAMS(EtherLink) + + INIT_PARAM(int1, "interface 1"), + INIT_PARAM(int2, "interface 2"), + INIT_PARAM(speed, "link speed in bits per second"), + INIT_PARAM(delay, "transmit delay of packets in us"), + INIT_PARAM(delay_var, "Difference in amount of time to traverse wire"), + INIT_PARAM(dump, "object to dump network packets to") + +END_INIT_SIM_OBJECT_PARAMS(EtherLink) + +CREATE_SIM_OBJECT(EtherLink) +{ + return new EtherLink(getInstanceName(), int1, int2, speed, delay, delay_var, + dump); +} + +REGISTER_SIM_OBJECT("EtherLink", EtherLink) diff --git a/src/dev/etherlink.hh b/src/dev/etherlink.hh new file mode 100644 index 000000000..bb2854810 --- /dev/null +++ b/src/dev/etherlink.hh @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/* @file + * Device module for modelling a fixed bandwidth full duplex ethernet link + */ + +#ifndef __DEV_ETHERLINK_HH__ +#define __DEV_ETHERLINK_HH__ + +#include "dev/etherint.hh" +#include "dev/etherpkt.hh" +#include "sim/eventq.hh" +#include "sim/host.hh" +#include "sim/sim_object.hh" + +class EtherDump; +class Checkpoint; +/* + * Model for a fixed bandwidth full duplex ethernet link + */ +class EtherLink : public SimObject +{ + protected: + class Interface; + + friend class LinkDelayEvent; + /* + * Model for a single uni-directional link + */ + class Link + { + protected: + std::string objName; + + EtherLink *parent; + int number; + + Interface *txint; + Interface *rxint; + + double ticksPerByte; + Tick linkDelay; + Tick delayVar; + EtherDump *dump; + + protected: + /* + * Transfer is complete + */ + EthPacketPtr packet; + void txDone(); + typedef EventWrapper<Link, &Link::txDone> DoneEvent; + friend void DoneEvent::process(); + DoneEvent doneEvent; + + friend class LinkDelayEvent; + void txComplete(EthPacketPtr packet); + + public: + Link(const std::string &name, EtherLink *p, int num, + double rate, Tick delay, Tick delay_var, EtherDump *dump); + ~Link() {} + + const std::string name() const { return objName; } + + bool busy() const { return (bool)packet; } + bool transmit(EthPacketPtr packet); + + void setTxInt(Interface *i) { assert(!txint); txint = i; } + void setRxInt(Interface *i) { assert(!rxint); rxint = i; } + + void serialize(const std::string &base, std::ostream &os); + void unserialize(const std::string &base, Checkpoint *cp, + const std::string §ion); + }; + + /* + * Interface at each end of the link + */ + class Interface : public EtherInt + { + private: + Link *txlink; + + public: + Interface(const std::string &name, Link *txlink, Link *rxlink); + bool recvPacket(EthPacketPtr packet) { return txlink->transmit(packet); } + void sendDone() { peer->sendDone(); } + }; + + Link *link[2]; + EtherInt *interface[2]; + + public: + EtherLink(const std::string &name, EtherInt *peer0, EtherInt *peer1, + double rate, Tick delay, Tick delayVar, EtherDump *dump); + virtual ~EtherLink(); + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +}; + +#endif // __ETHERLINK_HH__ diff --git a/src/dev/etherpkt.cc b/src/dev/etherpkt.cc new file mode 100644 index 000000000..5c552b4bd --- /dev/null +++ b/src/dev/etherpkt.cc @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iostream> + +#include "base/misc.hh" +#include "dev/etherpkt.hh" +#include "sim/serialize.hh" + +using namespace std; + +void +EthPacketData::serialize(const string &base, ostream &os) +{ + paramOut(os, base + ".length", length); + paramOut(os, base + ".slack", slack); + arrayParamOut(os, base + ".data", data, length); +} + +void +EthPacketData::unserialize(const string &base, Checkpoint *cp, + const string §ion) +{ + paramIn(cp, section, base + ".length", length); + paramIn(cp, section, base + ".slack", slack); + if (length) + arrayParamIn(cp, section, base + ".data", data, length); +} diff --git a/src/dev/etherpkt.hh b/src/dev/etherpkt.hh new file mode 100644 index 000000000..80c7baff7 --- /dev/null +++ b/src/dev/etherpkt.hh @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu + */ + +/* @file + * Reference counted class containing ethernet packet data + */ + +#ifndef __ETHERPKT_HH__ +#define __ETHERPKT_HH__ + +#include <iosfwd> +#include <memory> +#include <assert.h> + +#include "base/refcnt.hh" +#include "sim/host.hh" + +/* + * Reference counted class containing ethernet packet data + */ +class Checkpoint; +class EthPacketData : public RefCounted +{ + public: + /* + * Pointer to packet data will be deleted + */ + uint8_t *data; + + /* + * Length of the current packet + */ + int length; + + /* + * Extra space taken up by the packet in whatever data structure + * it is in. + * + * NOTE: This can only be use by *one* data structure at a time! + */ + int slack; + + public: + EthPacketData() : data(NULL), length(0), slack(0) { } + explicit EthPacketData(size_t size) + : data(new uint8_t[size]), length(0), slack(0) { } + EthPacketData(std::auto_ptr<uint8_t> d, int l, int s = 0) + : data(d.release()), length(l), slack(s) { } + ~EthPacketData() { if (data) delete [] data; } + + public: + void serialize(const std::string &base, std::ostream &os); + void unserialize(const std::string &base, Checkpoint *cp, + const std::string §ion); +}; + +typedef RefCountingPtr<EthPacketData> EthPacketPtr; + +#endif // __ETHERPKT_HH__ diff --git a/src/dev/ethertap.cc b/src/dev/ethertap.cc new file mode 100644 index 000000000..2d72383c5 --- /dev/null +++ b/src/dev/ethertap.cc @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/* @file + * Interface to connect a simulated ethernet device to the real world + */ + +#if defined(__OpenBSD__) || defined(__APPLE__) +#include <sys/param.h> +#endif +#include <netinet/in.h> + +#include <unistd.h> + +#include <deque> +#include <string> + +#include "base/misc.hh" +#include "base/pollevent.hh" +#include "base/socket.hh" +#include "base/trace.hh" +#include "dev/etherdump.hh" +#include "dev/etherint.hh" +#include "dev/etherpkt.hh" +#include "dev/ethertap.hh" +#include "sim/builder.hh" + +using namespace std; + +/** + */ +class TapListener +{ + protected: + /** + */ + class Event : public PollEvent + { + protected: + TapListener *listener; + + public: + Event(TapListener *l, int fd, int e) + : PollEvent(fd, e), listener(l) {} + + virtual void process(int revent) { listener->accept(); } + }; + + friend class Event; + Event *event; + + protected: + ListenSocket listener; + EtherTap *tap; + int port; + + public: + TapListener(EtherTap *t, int p) + : event(NULL), tap(t), port(p) {} + ~TapListener() { if (event) delete event; } + + void accept(); + void listen(); +}; + +void +TapListener::listen() +{ + while (!listener.listen(port, true)) { + DPRINTF(Ethernet, "TapListener(listen): Can't bind port %d\n", port); + port++; + } + + ccprintf(cerr, "Listening for tap connection on port %d\n", port); + event = new Event(this, listener.getfd(), POLLIN|POLLERR); + pollQueue.schedule(event); +} + +void +TapListener::accept() +{ + if (!listener.islistening()) + panic("TapListener(accept): cannot accept if we're not listening!"); + + int sfd = listener.accept(true); + if (sfd != -1) + tap->attach(sfd); +} + +/** + */ +class TapEvent : public PollEvent +{ + protected: + EtherTap *tap; + + public: + TapEvent(EtherTap *_tap, int fd, int e) + : PollEvent(fd, e), tap(_tap) {} + virtual void process(int revent) { tap->process(revent); } +}; + +EtherTap::EtherTap(const string &name, EtherDump *d, int port, int bufsz) + : EtherInt(name), event(NULL), socket(-1), buflen(bufsz), dump(d), + txEvent(this) +{ + buffer = new char[buflen]; + listener = new TapListener(this, port); + listener->listen(); +} + +EtherTap::~EtherTap() +{ + if (event) + delete event; + if (buffer) + delete [] buffer; + + delete listener; +} + +void +EtherTap::attach(int fd) +{ + if (socket != -1) + close(fd); + + buffer_offset = 0; + data_len = 0; + socket = fd; + DPRINTF(Ethernet, "EtherTap attached\n"); + event = new TapEvent(this, socket, POLLIN|POLLERR); + pollQueue.schedule(event); +} + +void +EtherTap::detach() +{ + DPRINTF(Ethernet, "EtherTap detached\n"); + delete event; + event = 0; + close(socket); + socket = -1; +} + +bool +EtherTap::recvPacket(EthPacketPtr packet) +{ + if (dump) + dump->dump(packet); + + DPRINTF(Ethernet, "EtherTap output len=%d\n", packet->length); + DDUMP(EthernetData, packet->data, packet->length); + u_int32_t len = htonl(packet->length); + write(socket, &len, sizeof(len)); + write(socket, packet->data, packet->length); + + recvDone(); + + return true; +} + +void +EtherTap::sendDone() +{} + +void +EtherTap::process(int revent) +{ + if (revent & POLLERR) { + detach(); + return; + } + + char *data = buffer + sizeof(u_int32_t); + if (!(revent & POLLIN)) + return; + + if (buffer_offset < data_len + sizeof(u_int32_t)) { + int len = read(socket, buffer + buffer_offset, buflen - buffer_offset); + if (len == 0) { + detach(); + return; + } + + buffer_offset += len; + + if (data_len == 0) + data_len = ntohl(*(u_int32_t *)buffer); + + DPRINTF(Ethernet, "Received data from peer: len=%d buffer_offset=%d " + "data_len=%d\n", len, buffer_offset, data_len); + } + + while (data_len != 0 && buffer_offset >= data_len + sizeof(u_int32_t)) { + EthPacketPtr packet; + packet = new EthPacketData(data_len); + packet->length = data_len; + memcpy(packet->data, data, data_len); + + buffer_offset -= data_len + sizeof(u_int32_t); + assert(buffer_offset >= 0); + if (buffer_offset > 0) { + memmove(buffer, data + data_len, buffer_offset); + data_len = ntohl(*(u_int32_t *)buffer); + } else + data_len = 0; + + DPRINTF(Ethernet, "EtherTap input len=%d\n", packet->length); + DDUMP(EthernetData, packet->data, packet->length); + if (!sendPacket(packet)) { + DPRINTF(Ethernet, "bus busy...buffer for retransmission\n"); + packetBuffer.push(packet); + if (!txEvent.scheduled()) + txEvent.schedule(curTick + retryTime); + } else if (dump) { + dump->dump(packet); + } + } +} + +void +EtherTap::retransmit() +{ + if (packetBuffer.empty()) + return; + + EthPacketPtr packet = packetBuffer.front(); + if (sendPacket(packet)) { + if (dump) + dump->dump(packet); + DPRINTF(Ethernet, "EtherTap retransmit\n"); + packetBuffer.front() = NULL; + packetBuffer.pop(); + } + + if (!packetBuffer.empty() && !txEvent.scheduled()) + txEvent.schedule(curTick + retryTime); +} + +//===================================================================== + +void +EtherTap::serialize(ostream &os) +{ + SERIALIZE_SCALAR(socket); + SERIALIZE_SCALAR(buflen); + uint8_t *buffer = (uint8_t *)this->buffer; + SERIALIZE_ARRAY(buffer, buflen); + SERIALIZE_SCALAR(buffer_offset); + SERIALIZE_SCALAR(data_len); + + bool tapevent_present = false; + if (event) { + tapevent_present = true; + SERIALIZE_SCALAR(tapevent_present); + event->serialize(os); + } + else { + SERIALIZE_SCALAR(tapevent_present); + } +} + +void +EtherTap::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(socket); + UNSERIALIZE_SCALAR(buflen); + uint8_t *buffer = (uint8_t *)this->buffer; + UNSERIALIZE_ARRAY(buffer, buflen); + UNSERIALIZE_SCALAR(buffer_offset); + UNSERIALIZE_SCALAR(data_len); + + bool tapevent_present; + UNSERIALIZE_SCALAR(tapevent_present); + if (tapevent_present) { + event = new TapEvent(this, socket, POLLIN|POLLERR); + + event->unserialize(cp,section); + + if (event->queued()) { + pollQueue.schedule(event); + } + } +} + +//===================================================================== + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(EtherTap) + + SimObjectParam<EtherInt *> peer; + SimObjectParam<EtherDump *> dump; + Param<unsigned> port; + Param<unsigned> bufsz; + +END_DECLARE_SIM_OBJECT_PARAMS(EtherTap) + +BEGIN_INIT_SIM_OBJECT_PARAMS(EtherTap) + + INIT_PARAM_DFLT(peer, "peer interface", NULL), + INIT_PARAM_DFLT(dump, "object to dump network packets to", NULL), + INIT_PARAM_DFLT(port, "tap port", 3500), + INIT_PARAM_DFLT(bufsz, "tap buffer size", 10000) + +END_INIT_SIM_OBJECT_PARAMS(EtherTap) + + +CREATE_SIM_OBJECT(EtherTap) +{ + EtherTap *tap = new EtherTap(getInstanceName(), dump, port, bufsz); + + if (peer) { + tap->setPeer(peer); + peer->setPeer(tap); + } + + return tap; +} + +REGISTER_SIM_OBJECT("EtherTap", EtherTap) diff --git a/src/dev/ethertap.hh b/src/dev/ethertap.hh new file mode 100644 index 000000000..f64ed7187 --- /dev/null +++ b/src/dev/ethertap.hh @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/* @file + * Interface to connect a simulated ethernet device to the real world + */ + +#ifndef __ETHERTAP_HH__ +#define __ETHERTAP_HH__ + +#include <queue> +#include <string> + +#include "dev/etherint.hh" +#include "dev/etherpkt.hh" +#include "sim/eventq.hh" +#include "base/pollevent.hh" +#include "sim/sim_object.hh" + +class TapEvent; +class TapListener; + +/* + * Interface to connect a simulated ethernet device to the real world + */ +class EtherTap : public EtherInt +{ + protected: + friend class TapEvent; + TapEvent *event; + + protected: + friend class TapListener; + TapListener *listener; + int socket; + char *buffer; + int buflen; + int32_t buffer_offset; + int32_t data_len; + + EtherDump *dump; + + void attach(int fd); + void detach(); + + protected: + std::string device; + std::queue<EthPacketPtr> packetBuffer; + + void process(int revent); + void enqueue(EthPacketData *packet); + void retransmit(); + + /* + */ + class TxEvent : public Event + { + protected: + EtherTap *tap; + + public: + TxEvent(EtherTap *_tap) + : Event(&mainEventQueue), tap(_tap) {} + void process() { tap->retransmit(); } + virtual const char *description() { return "retransmit event"; } + }; + + friend class TxEvent; + TxEvent txEvent; + + public: + EtherTap(const std::string &name, EtherDump *dump, int port, int bufsz); + virtual ~EtherTap(); + + virtual bool recvPacket(EthPacketPtr packet); + virtual void sendDone(); + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +#endif // __ETHERTAP_HH__ diff --git a/src/dev/i8254xGBe.cc b/src/dev/i8254xGBe.cc new file mode 100644 index 000000000..77c731899 --- /dev/null +++ b/src/dev/i8254xGBe.cc @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/* @file + * Device model for Intel's 8254x line of gigabit ethernet controllers. + */ + +#include "base/inet.hh" +#include "dev/i8254xGBe.hh" +#include "mem/packet.hh" +#include "sim/builder.hh" +#include "sim/stats.hh" +#include "sim/system.hh" + +IGbE::IGbE(Params *p) + : PciDev(p), etherInt(NULL) +{ + +} + + +Tick +IGbE::writeConfig(Packet *pkt) +{ + int offset = pkt->getAddr() & PCI_CONFIG_SIZE; + if (offset < PCI_DEVICE_SPECIFIC) + PciDev::writeConfig(pkt); + else + panic("Device specific PCI config space not implemented.\n"); + + /// + /// Some work may need to be done here based for the pci COMMAND bits. + /// + + return pioDelay; +} + +Tick +IGbE::read(Packet *pkt) +{ + int bar; + Addr daddr; + + if (!getBAR(pkt->getAddr(), bar, daddr)) + panic("Invalid PCI memory access to unmapped memory.\n"); + + // Only Memory register BAR is allowed + assert(bar == 0); + + DPRINTF(Ethernet, "Accessed devie register %#X\n", daddr); + + pkt->allocate(); + + + /// + /// Handle read of register here + /// + + pkt->result = Packet::Success; + return pioDelay; +} + +Tick +IGbE::write(Packet *pkt) +{ + int bar; + Addr daddr; + + if (!getBAR(pkt->getAddr(), bar, daddr)) + panic("Invalid PCI memory access to unmapped memory.\n"); + + // Only Memory register BAR is allowed + assert(bar == 0); + + DPRINTF(Ethernet, "Accessed devie register %#X\n", daddr); + + /// + /// Handle write of register here + /// + + pkt->result = Packet::Success; + return pioDelay; +} + + +bool +IGbE::ethRxPkt(EthPacketPtr packet) +{ + panic("Need to implemenet\n"); +} + + +void +IGbE::ethTxDone() +{ + panic("Need to implemenet\n"); +} + +void +IGbE::serialize(std::ostream &os) +{ + panic("Need to implemenet\n"); +} + +void +IGbE::unserialize(Checkpoint *cp, const std::string §ion) +{ + panic("Need to implemenet\n"); +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(IGbEInt) + + SimObjectParam<EtherInt *> peer; + SimObjectParam<IGbE *> device; + +END_DECLARE_SIM_OBJECT_PARAMS(IGbEInt) + +BEGIN_INIT_SIM_OBJECT_PARAMS(IGbEInt) + + INIT_PARAM_DFLT(peer, "peer interface", NULL), + INIT_PARAM(device, "Ethernet device of this interface") + +END_INIT_SIM_OBJECT_PARAMS(IGbEInt) + +CREATE_SIM_OBJECT(IGbEInt) +{ + IGbEInt *dev_int = new IGbEInt(getInstanceName(), device); + + EtherInt *p = (EtherInt *)peer; + if (p) { + dev_int->setPeer(p); + p->setPeer(dev_int); + } + + return dev_int; +} + +REGISTER_SIM_OBJECT("IGbEInt", IGbEInt) + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(IGbE) + + SimObjectParam<System *> system; + SimObjectParam<Platform *> platform; + SimObjectParam<PciConfigData *> configdata; + Param<uint32_t> pci_bus; + Param<uint32_t> pci_dev; + Param<uint32_t> pci_func; + Param<Tick> pio_latency; + Param<Tick> config_latency; + +END_DECLARE_SIM_OBJECT_PARAMS(IGbE) + +BEGIN_INIT_SIM_OBJECT_PARAMS(IGbE) + + INIT_PARAM(system, "System pointer"), + INIT_PARAM(platform, "Platform pointer"), + INIT_PARAM(configdata, "PCI Config data"), + INIT_PARAM(pci_bus, "PCI bus ID"), + INIT_PARAM(pci_dev, "PCI device number"), + INIT_PARAM(pci_func, "PCI function code"), + INIT_PARAM_DFLT(pio_latency, "Programmed IO latency in bus cycles", 1), + INIT_PARAM(config_latency, "Number of cycles for a config read or write") + +END_INIT_SIM_OBJECT_PARAMS(IGbE) + + +CREATE_SIM_OBJECT(IGbE) +{ + IGbE::Params *params = new IGbE::Params; + + params->name = getInstanceName(); + params->platform = platform; + params->system = system; + params->configData = configdata; + params->busNum = pci_bus; + params->deviceNum = pci_dev; + params->functionNum = pci_func; + params->pio_delay = pio_latency; + params->config_delay = config_latency; + + return new IGbE(params); +} + +REGISTER_SIM_OBJECT("IGbE", IGbE) diff --git a/src/dev/i8254xGBe.hh b/src/dev/i8254xGBe.hh new file mode 100644 index 000000000..88931eb6d --- /dev/null +++ b/src/dev/i8254xGBe.hh @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/* @file + * Device model for Intel's 8254x line of gigabit ethernet controllers. + */ + +#ifndef __DEV_I8254XGBE_HH__ +#define __DEV_I8254XGBE_HH__ + +#include "base/inet.hh" +#include "base/statistics.hh" +#include "dev/etherint.hh" +#include "dev/etherpkt.hh" +#include "dev/pcidev.hh" +#include "dev/pktfifo.hh" +#include "sim/eventq.hh" + +class IGbEInt; + +class IGbE : public PciDev +{ + private: + IGbEInt *etherInt; + + public: + struct Params : public PciDev::Params + { + ; + }; + + IGbE(Params *params); + ~IGbE() {;} + + virtual Tick read(Packet *pkt); + virtual Tick write(Packet *pkt); + + virtual Tick writeConfig(Packet *pkt); + + bool ethRxPkt(EthPacketPtr packet); + void ethTxDone(); + + void setEthInt(IGbEInt *i) { assert(!etherInt); etherInt = i; } + + const Params *params() const {return (const Params *)_params; } + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + +}; + +class IGbEInt : public EtherInt +{ + private: + IGbE *dev; + + public: + IGbEInt(const std::string &name, IGbE *d) + : EtherInt(name), dev(d) + { dev->setEthInt(this); } + + virtual bool recvPacket(EthPacketPtr pkt) { return dev->ethRxPkt(pkt); } + virtual void sendDone() { dev->ethTxDone(); } +}; + + + + + +#endif //__DEV_I8254XGBE_HH__ + diff --git a/src/dev/i8254xGBe_defs.hh b/src/dev/i8254xGBe_defs.hh new file mode 100644 index 000000000..81d7d0d80 --- /dev/null +++ b/src/dev/i8254xGBe_defs.hh @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/* @file + * Register and structure descriptions for Intel's 8254x line of gigabit ethernet controllers. + */ + +namespace iGbReg { + +const uint32_t CTRL = 0x00000; +const uint32_t STATUS = 0x00008; +const uint32_t EECD = 0x00010; +const uint32_t CTRL_EXT = 0x00018; +const uint32_t PBA = 0x01000; +const uint32_t ICR = 0x000C0; +const uint32_t ITR = 0x000C4; +const uint32_t ICS = 0x000C8; +const uint32_t IMS = 0x000D0; +const uint32_t IMC = 0x000D8; +const uint32_t RCTL = 0x00100; +const uint32_t RDBAL = 0x02800; +const uint32_t RDBAH = 0x02804; +const uint32_t RDLEN = 0x02808; +const uint32_t RDH = 0x02810; +const uint32_t RDT = 0x02818; +const uint32_t RDTR = 0x02820; +const uint32_t RADV = 0x0282C; +const uint32_t RSRPD = 0x02C00; +const uint32_t TCTL = 0x00400; +const uint32_t TDBAL = 0x03800; +const uint32_t TDBAH = 0x03804; +const uint32_t TDLEN = 0x03808; +const uint32_t TDH = 0x03810; +const uint32_t THT = 0x03818; +const uint32_t TIDV = 0x03820; +const uint32_t TXDMAC = 0x03000; +const uint32_t TXDCTL = 0x03828; +const uint32_t TADV = 0x0282C; +const uint32_t TSPMT = 0x03830; +const uint32_t RXDCTL = 0x02828; +const uint32_t RXCSUM = 0x05000; + +struct RxDesc { + Addr buf; + uint16_t len; + uint16_t csum; + union { + uint8_t status; + struct { // these may be in the worng order + uint8_t dd:1; // descriptor done (hw is done when 1) + uint8_t eop:1; // end of packet + uint8_t xism:1; // ignore checksum + uint8_t vp:1; // packet is vlan packet + uint8_t rsv:1; // reserved + uint8_t tcpcs:1; // TCP checksum done + uint8_t ipcs:1; // IP checksum done + uint8_t pif:1; // passed in-exact filter + } st; + }; + union { + uint8_t errors; + struct { + uint8_t ce:1; // crc error or alignment error + uint8_t se:1; // symbol error + uint8_t seq:1; // sequence error + uint8_t rsv:1; // reserved + uint8_t cxe:1; // carrier extension error + uint8_t tcpe:1; // tcp checksum error + uint8_t ipe:1; // ip checksum error + uint8_t rxe:1; // PX data error + } er; + }; + union { + uint16_t special; + struct { + uint16_t vlan:12; //vlan id + uint16_t cfi:1; // canocial form id + uint16_t pri:3; // user priority + } sp; + }; +}; + +union TxDesc { + uint8_t data[16]; + struct { + Addr buf; + uint16_t len; + uint8_t cso; + union { + uint8_t command; + struct { + uint8_t eop:1; // end of packet + uint8_t ifcs:1; // insert crc + uint8_t ic:1; // insert checksum + uint8_t rs:1; // report status + uint8_t rps:1; // report packet sent + uint8_t dext:1; // extension + uint8_t vle:1; // vlan enable + uint8_t ide:1; // interrupt delay enable + } cmd; + }; + union { + uint8_t status:4; + struct { + uint8_t dd:1; // descriptor done + uint8_t ec:1; // excess collisions + uint8_t lc:1; // late collision + uint8_t tu:1; // transmit underrun + } st; + }; + uint8_t reserved:4; + uint8_t css; + union { + uint16_t special; + struct { + uint16_t vlan:12; //vlan id + uint16_t cfi:1; // canocial form id + uint16_t pri:3; // user priority + } sp; + }; + } legacy; + + // Type 0000 descriptor + struct { + uint8_t ipcss; + uint8_t ipcso; + uint16_t ipcse; + uint8_t tucss; + uint8_t tucso; + uint16_t tucse; + uint32_t paylen:20; + uint8_t dtype:4; + union { + uint8_t tucommand; + struct { + uint8_t tcp:1; // tcp/udp + uint8_t ip:1; // ip ipv4/ipv6 + uint8_t tse:1; // tcp segment enbale + uint8_t rs:1; // report status + uint8_t rsv0:1; // reserved + uint8_t dext:1; // descriptor extension + uint8_t rsv1:1; // reserved + uint8_t ide:1; // interrupt delay enable + } tucmd; + }; + union { + uint8_t status:4; + struct { + uint8_t dd:1; + uint8_t rsvd:3; + } sta; + }; + uint8_t reserved:4; + uint8_t hdrlen; + uint16_t mss; + } t0; + + // Type 0001 descriptor + struct { + Addr buf; + uint32_t dtalen:20; + uint8_t dtype:4; + union { + uint8_t dcommand; + struct { + uint8_t eop:1; // end of packet + uint8_t ifcs:1; // insert crc + uint8_t tse:1; // segmentation enable + uint8_t rs:1; // report status + uint8_t rps:1; // report packet sent + uint8_t dext:1; // extension + uint8_t vle:1; // vlan enable + uint8_t ide:1; // interrupt delay enable + } dcmd; + }; + union { + uint8_t status:4; + struct { + uint8_t dd:1; // descriptor done + uint8_t ec:1; // excess collisions + uint8_t lc:1; // late collision + uint8_t tu:1; // transmit underrun + } sta; + }; + union { + uint8_t pktopts; + struct { + uint8_t ixsm:1; // insert ip checksum + uint8_t txsm:1; // insert tcp checksum + }; + }; + union { + uint16_t special; + struct { + uint16_t vlan:12; //vlan id + uint16_t cfi:1; // canocial form id + uint16_t pri:3; // user priority + } sp; + }; + } t1; + + // Junk to test descriptor type! + struct { + uint64_t junk; + uint32_t junk1:20; + uint8_t dtype; + uint8_t junk2:5; + uint8_t dext:1; + uint8_t junk3:2; + uint8_t junk4:4; + uint32_t junk5; + } type; +}; + +}; // iGbReg namespace diff --git a/src/dev/ide_atareg.h b/src/dev/ide_atareg.h new file mode 100644 index 000000000..5320529c8 --- /dev/null +++ b/src/dev/ide_atareg.h @@ -0,0 +1,276 @@ +/* $OpenBSD: atareg.h,v 1.12 2004/09/24 07:15:22 grange Exp $ */ +/* $NetBSD: atareg.h,v 1.5 1999/01/18 20:06:24 bouyer Exp $ */ + +/* + * Copyright (c) 1998, 2001 Manuel Bouyer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Manuel Bouyer. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _DEV_ATA_ATAREG_H_ +#define _DEV_ATA_ATAREG_H_ + +#if defined(linux) +#include <endian.h> +#else +#include <machine/endian.h> +#endif + +#define ATA_BYTE_ORDER LITTLE_ENDIAN + +/* + * Drive parameter structure for ATA/ATAPI. + * Bit fields: WDC_* : common to ATA/ATAPI + * ATA_* : ATA only + * ATAPI_* : ATAPI only. + */ +struct ataparams { + /* drive info */ + uint16_t atap_config; /* 0: general configuration */ +#define WDC_CFG_ATAPI_MASK 0xc000 +#define WDC_CFG_ATAPI 0x8000 +#define ATA_CFG_REMOVABLE 0x0080 +#define ATA_CFG_FIXED 0x0040 +#define ATAPI_CFG_TYPE_MASK 0x1f00 +#define ATAPI_CFG_TYPE(x) (((x) & ATAPI_CFG_TYPE_MASK) >> 8) +#define ATAPI_CFG_TYPE_DIRECT 0x00 +#define ATAPI_CFG_TYPE_SEQUENTIAL 0x01 +#define ATAPI_CFG_TYPE_CDROM 0x05 +#define ATAPI_CFG_TYPE_OPTICAL 0x07 +#define ATAPI_CFG_TYPE_NODEVICE 0x1F +#define ATAPI_CFG_REMOV 0x0080 +#define ATAPI_CFG_DRQ_MASK 0x0060 +#define ATAPI_CFG_STD_DRQ 0x0000 +#define ATAPI_CFG_IRQ_DRQ 0x0020 +#define ATAPI_CFG_ACCEL_DRQ 0x0040 +#define ATAPI_CFG_CMD_MASK 0x0003 +#define ATAPI_CFG_CMD_12 0x0000 +#define ATAPI_CFG_CMD_16 0x0001 +/* words 1-9 are ATA only */ + uint16_t atap_cylinders; /* 1: # of non-removable cylinders */ + uint16_t __reserved1; + uint16_t atap_heads; /* 3: # of heads */ + uint16_t __retired1[2]; /* 4-5: # of unform. bytes/track */ + uint16_t atap_sectors; /* 6: # of sectors */ + uint16_t __retired2[3]; + + uint8_t atap_serial[20]; /* 10-19: serial number */ + uint16_t __retired3[2]; + uint16_t __obsolete1; + uint8_t atap_revision[8]; /* 23-26: firmware revision */ + uint8_t atap_model[40]; /* 27-46: model number */ + uint16_t atap_multi; /* 47: maximum sectors per irq (ATA) */ + uint16_t __reserved2; + uint8_t atap_vendor; /* 49: vendor */ + uint8_t atap_capabilities1; /* 49: capability flags */ +#define WDC_CAP_IORDY 0x0800 +#define WDC_CAP_IORDY_DSBL 0x0400 +#define WDC_CAP_LBA 0x0200 +#define WDC_CAP_DMA 0x0100 +#define ATA_CAP_STBY 0x2000 +#define ATAPI_CAP_INTERL_DMA 0x8000 +#define ATAPI_CAP_CMD_QUEUE 0x4000 +#define ATAPI_CAP_OVERLP 0x2000 +#define ATAPI_CAP_ATA_RST 0x1000 + uint16_t atap_capabilities2; /* 50: capability flags (ATA) */ +#if ATA_BYTE_ORDER == LITTLE_ENDIAN + uint8_t __junk2; + uint8_t atap_oldpiotiming; /* 51: old PIO timing mode */ + uint8_t __junk3; + uint8_t atap_olddmatiming; /* 52: old DMA timing mode (ATA) */ +#else + uint8_t atap_oldpiotiming; /* 51: old PIO timing mode */ + uint8_t __junk2; + uint8_t atap_olddmatiming; /* 52: old DMA timing mode (ATA) */ + uint8_t __junk3; +#endif + uint16_t atap_extensions; /* 53: extensions supported */ +#define WDC_EXT_UDMA_MODES 0x0004 +#define WDC_EXT_MODES 0x0002 +#define WDC_EXT_GEOM 0x0001 +/* words 54-62 are ATA only */ + uint16_t atap_curcylinders; /* 54: current logical cylinders */ + uint16_t atap_curheads; /* 55: current logical heads */ + uint16_t atap_cursectors; /* 56: current logical sectors/tracks */ + uint16_t atap_curcapacity[2]; /* 57-58: current capacity */ + uint8_t atap_curmulti; /* 59: current multi-sector setting */ + uint8_t atap_curmulti_valid; /* 59: current multi-sector setting */ +#define WDC_MULTI_VALID 0x0100 +#define WDC_MULTI_MASK 0x00ff + uint32_t atap_capacity; /* 60-61: total capacity (LBA only) */ + uint16_t __retired4; +#if ATA_BYTE_ORDER == LITTLE_ENDIAN + uint8_t atap_dmamode_supp; /* 63: multiword DMA mode supported */ + uint8_t atap_dmamode_act; /* multiword DMA mode active */ + uint8_t atap_piomode_supp; /* 64: PIO mode supported */ + uint8_t __junk4; +#else + uint8_t atap_dmamode_act; /* multiword DMA mode active */ + uint8_t atap_dmamode_supp; /* 63: multiword DMA mode supported */ + uint8_t __junk4; + uint8_t atap_piomode_supp; /* 64: PIO mode supported */ +#endif + uint16_t atap_dmatiming_mimi; /* 65: minimum DMA cycle time */ + uint16_t atap_dmatiming_recom; /* 66: recommended DMA cycle time */ + uint16_t atap_piotiming; /* 67: mini PIO cycle time without FC */ + uint16_t atap_piotiming_iordy; /* 68: mini PIO cycle time with IORDY FC */ + uint16_t __reserved3[2]; +/* words 71-72 are ATAPI only */ + uint16_t atap_pkt_br; /* 71: time (ns) to bus release */ + uint16_t atap_pkt_bsyclr; /* 72: tme to clear BSY after service */ + uint16_t __reserved4[2]; + uint16_t atap_queuedepth; /* 75: */ +#define WDC_QUEUE_DEPTH_MASK 0x1f + uint16_t atap_sata_caps; /* 76: SATA capabilities */ +#define SATA_SIGNAL_GEN1 0x0002 /* SATA Gen-1 signaling speed */ +#define SATA_SIGNAL_GEN2 0x0004 /* SATA Gen-2 signaling speed */ +#define SATA_NATIVE_CMDQ 0x0100 /* native command queuing */ +#define SATA_HOST_PWR_MGMT 0x0200 /* power management (host) */ + uint16_t atap_sata_reserved; /* 77: reserved */ + uint16_t atap_sata_features_supp;/* 78: SATA features supported */ +#define SATA_NONZERO_OFFSETS 0x0002 /* non-zero buffer offsets */ +#define SATA_DMA_SETUP_AUTO 0x0004 /* DMA setup auto-activate */ +#define SATA_DRIVE_PWR_MGMT 0x0008 /* power management (device) */ + uint16_t atap_sata_features_en; /* 79: SATA features enabled */ + uint16_t atap_ata_major; /* 80: Major version number */ +#define WDC_VER_ATA1 0x0002 +#define WDC_VER_ATA2 0x0004 +#define WDC_VER_ATA3 0x0008 +#define WDC_VER_ATA4 0x0010 +#define WDC_VER_ATA5 0x0020 +#define WDC_VER_ATA6 0x0040 +#define WDC_VER_ATA7 0x0080 +#define WDC_VER_ATA8 0x0100 +#define WDC_VER_ATA9 0x0200 +#define WDC_VER_ATA10 0x0400 +#define WDC_VER_ATA11 0x0800 +#define WDC_VER_ATA12 0x1000 +#define WDC_VER_ATA13 0x2000 +#define WDC_VER_ATA14 0x4000 + uint16_t atap_ata_minor; /* 81: Minor version number */ + uint16_t atap_cmd_set1; /* 82: command set supported */ +#define WDC_CMD1_NOP 0x4000 +#define WDC_CMD1_RB 0x2000 +#define WDC_CMD1_WB 0x1000 +#define WDC_CMD1_HPA 0x0400 +#define WDC_CMD1_DVRST 0x0200 +#define WDC_CMD1_SRV 0x0100 +#define WDC_CMD1_RLSE 0x0080 +#define WDC_CMD1_AHEAD 0x0040 +#define WDC_CMD1_CACHE 0x0020 +#define WDC_CMD1_PKT 0x0010 +#define WDC_CMD1_PM 0x0008 +#define WDC_CMD1_REMOV 0x0004 +#define WDC_CMD1_SEC 0x0002 +#define WDC_CMD1_SMART 0x0001 + uint16_t atap_cmd_set2; /* 83: command set supported */ +#define ATAPI_CMD2_FCE 0x2000 /* Flush Cache Ext supported */ +#define ATAPI_CMD2_FC 0x1000 /* Flush Cache supported */ +#define ATAPI_CMD2_DCO 0x0800 /* Device Configuration Overlay supported */ +#define ATAPI_CMD2_48AD 0x0400 /* 48bit address supported */ +#define ATAPI_CMD2_AAM 0x0200 /* Automatic Acoustic Management supported */ +#define ATAPI_CMD2_SM 0x0100 /* Set Max security extension supported */ +#define ATAPI_CMD2_SF 0x0040 /* Set Features subcommand required */ +#define ATAPI_CMD2_PUIS 0x0020 /* Power up in standby supported */ +#define WDC_CMD2_RMSN 0x0010 +#define ATA_CMD2_APM 0x0008 +#define ATA_CMD2_CFA 0x0004 +#define ATA_CMD2_RWQ 0x0002 +#define WDC_CMD2_DM 0x0001 /* Download Microcode supported */ + uint16_t atap_cmd_ext; /* 84: command/features supp. ext. */ +#define ATAPI_CMDE_MSER 0x0004 /* Media serial number supported */ +#define ATAPI_CMDE_TEST 0x0002 /* SMART self-test supported */ +#define ATAPI_CMDE_SLOG 0x0001 /* SMART error logging supported */ + uint16_t atap_cmd1_en; /* 85: cmd/features enabled */ +/* bits are the same as atap_cmd_set1 */ + uint16_t atap_cmd2_en; /* 86: cmd/features enabled */ +/* bits are the same as atap_cmd_set2 */ + uint16_t atap_cmd_def; /* 87: cmd/features default */ +/* bits are NOT the same as atap_cmd_ext */ +#if ATA_BYTE_ORDER == LITTLE_ENDIAN + uint8_t atap_udmamode_supp; /* 88: Ultra-DMA mode supported */ + uint8_t atap_udmamode_act; /* Ultra-DMA mode active */ +#else + uint8_t atap_udmamode_act; /* Ultra-DMA mode active */ + uint8_t atap_udmamode_supp; /* 88: Ultra-DMA mode supported */ +#endif +/* 89-92 are ATA-only */ + uint16_t atap_seu_time; /* 89: Sec. Erase Unit compl. time */ + uint16_t atap_eseu_time; /* 90: Enhanced SEU compl. time */ + uint16_t atap_apm_val; /* 91: current APM value */ + uint16_t atap_mpasswd_rev; /* 92: Master Password revision */ + uint16_t atap_hwreset_res; /* 93: Hardware reset value */ +#define ATA_HWRES_CBLID 0x2000 /* CBLID above Vih */ +#define ATA_HWRES_D1_PDIAG 0x0800 /* Device 1 PDIAG detect OK */ +#define ATA_HWRES_D1_CSEL 0x0400 /* Device 1 used CSEL for address */ +#define ATA_HWRES_D1_JUMP 0x0200 /* Device 1 jumpered to address */ +#define ATA_HWRES_D0_SEL 0x0040 /* Device 0 responds when Dev 1 selected */ +#define ATA_HWRES_D0_DASP 0x0020 /* Device 0 DASP detect OK */ +#define ATA_HWRES_D0_PDIAG 0x0010 /* Device 0 PDIAG detect OK */ +#define ATA_HWRES_D0_DIAG 0x0008 /* Device 0 diag OK */ +#define ATA_HWRES_D0_CSEL 0x0004 /* Device 0 used CSEL for address */ +#define ATA_HWRES_D0_JUMP 0x0002 /* Device 0 jumpered to address */ +#if ATA_BYTE_ORDER == LITTLE_ENDIAN + uint8_t atap_acoustic_val; /* 94: Current acoustic level */ + uint8_t atap_acoustic_def; /* recommended level */ +#else + uint8_t atap_acoustic_def; /* recommended level */ + uint8_t atap_acoustic_val; /* 94: Current acoustic level */ +#endif + uint16_t __reserved6[5]; /* 95-99: reserved */ + uint16_t atap_max_lba[4]; /* 100-103: Max. user LBA add */ + uint16_t __reserved7[23]; /* 104-126: reserved */ + uint16_t atap_rmsn_supp; /* 127: remov. media status notif. */ +#define WDC_RMSN_SUPP_MASK 0x0003 +#define WDC_RMSN_SUPP 0x0001 + uint16_t atap_sec_st; /* 128: security status */ +#define WDC_SEC_LEV_MAX 0x0100 +#define WDC_SEC_ESE_SUPP 0x0020 +#define WDC_SEC_EXP 0x0010 +#define WDC_SEC_FROZEN 0x0008 +#define WDC_SEC_LOCKED 0x0004 +#define WDC_SEC_EN 0x0002 +#define WDC_SEC_SUPP 0x0001 + uint16_t __reserved8[31]; /* 129-159: vendor specific */ + uint16_t atap_cfa_power; /* 160: CFA powermode */ +#define ATAPI_CFA_MAX_MASK 0x0FFF +#define ATAPI_CFA_MODE1_DIS 0x1000 /* CFA Mode 1 Disabled */ +#define ATAPI_CFA_MODE1_REQ 0x2000 /* CFA Mode 1 Required */ +#define ATAPI_CFA_WORD160 0x8000 /* Word 160 supported */ + uint16_t __reserved9[15]; /* 161-175: reserved for CFA */ + uint8_t atap_media_serial[60]; /* 176-205: media serial number */ + uint16_t __reserved10[49]; /* 206-254: reserved */ +#if ATA_BYTE_ORDER == LITTLE_ENDIAN + uint8_t atap_signature; /* 255: Signature */ + uint8_t atap_checksum; /* Checksum */ +#else + uint8_t atap_checksum; /* Checksum */ + uint8_t atap_signature; /* 255: Signature */ +#endif +}; + +#undef ATA_BYTE_ORDER +#endif /* !_DEV_ATA_ATAREG_H_ */ diff --git a/src/dev/ide_ctrl.cc b/src/dev/ide_ctrl.cc new file mode 100644 index 000000000..e8d7f4817 --- /dev/null +++ b/src/dev/ide_ctrl.cc @@ -0,0 +1,796 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Andrew Schultz + * Ali Saidi + * Miguel Serrano + */ + +#include <cstddef> +#include <cstdlib> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "cpu/intr_control.hh" +#include "dev/ide_ctrl.hh" +#include "dev/ide_disk.hh" +#include "dev/pciconfigall.hh" +#include "dev/pcireg.h" +#include "dev/platform.hh" +#include "mem/packet.hh" +#include "sim/builder.hh" +#include "sim/sim_object.hh" +#include "sim/byteswap.hh" + +using namespace std; + +//// +// Initialization and destruction +//// + +IdeController::IdeController(Params *p) + : PciDev(p) +{ + // initialize the PIO interface addresses + pri_cmd_addr = 0; + pri_cmd_size = BARSize[0]; + + pri_ctrl_addr = 0; + pri_ctrl_size = BARSize[1]; + + sec_cmd_addr = 0; + sec_cmd_size = BARSize[2]; + + sec_ctrl_addr = 0; + sec_ctrl_size = BARSize[3]; + + // initialize the bus master interface (BMI) address to be configured + // via PCI + bmi_addr = 0; + bmi_size = BARSize[4]; + + // zero out all of the registers + memset(bmi_regs.data, 0, sizeof(bmi_regs)); + memset(config_regs.data, 0, sizeof(config_regs.data)); + + // setup initial values + // enable both channels + config_regs.idetim0 = htole((uint16_t)IDETIM_DECODE_EN); + config_regs.idetim1 = htole((uint16_t)IDETIM_DECODE_EN); + bmi_regs.bmis0 = DMA1CAP | DMA0CAP; + bmi_regs.bmis1 = DMA1CAP | DMA0CAP; + + // reset all internal variables + io_enabled = false; + bm_enabled = false; + memset(cmd_in_progress, 0, sizeof(cmd_in_progress)); + + // setup the disks attached to controller + memset(disks, 0, sizeof(disks)); + dev[0] = 0; + dev[1] = 0; + + if (params()->disks.size() > 3) + panic("IDE controllers support a maximum of 4 devices attached!\n"); + + for (int i = 0; i < params()->disks.size(); i++) { + disks[i] = params()->disks[i]; + disks[i]->setController(this); + } +} + +IdeController::~IdeController() +{ + for (int i = 0; i < 4; i++) + if (disks[i]) + delete disks[i]; +} + +//// +// Utility functions +/// + +void +IdeController::parseAddr(const Addr &addr, Addr &offset, IdeChannel &channel, + IdeRegType ®_type) +{ + offset = addr; + + if (addr >= pri_cmd_addr && addr < (pri_cmd_addr + pri_cmd_size)) { + offset -= pri_cmd_addr; + reg_type = COMMAND_BLOCK; + channel = PRIMARY; + } else if (addr >= pri_ctrl_addr && + addr < (pri_ctrl_addr + pri_ctrl_size)) { + offset -= pri_ctrl_addr; + reg_type = CONTROL_BLOCK; + channel = PRIMARY; + } else if (addr >= sec_cmd_addr && + addr < (sec_cmd_addr + sec_cmd_size)) { + offset -= sec_cmd_addr; + reg_type = COMMAND_BLOCK; + channel = SECONDARY; + } else if (addr >= sec_ctrl_addr && + addr < (sec_ctrl_addr + sec_ctrl_size)) { + offset -= sec_ctrl_addr; + reg_type = CONTROL_BLOCK; + channel = SECONDARY; + } else if (addr >= bmi_addr && addr < (bmi_addr + bmi_size)) { + offset -= bmi_addr; + reg_type = BMI_BLOCK; + channel = (offset < BMIC1) ? PRIMARY : SECONDARY; + } else { + panic("IDE controller access to invalid address: %#x\n", addr); + } +} + +int +IdeController::getDisk(IdeChannel channel) +{ + int disk = 0; + uint8_t *devBit = &dev[0]; + + if (channel == SECONDARY) { + disk += 2; + devBit = &dev[1]; + } + + disk += *devBit; + + assert(*devBit == 0 || *devBit == 1); + + return disk; +} + +int +IdeController::getDisk(IdeDisk *diskPtr) +{ + for (int i = 0; i < 4; i++) { + if ((long)diskPtr == (long)disks[i]) + return i; + } + return -1; +} + +bool +IdeController::isDiskSelected(IdeDisk *diskPtr) +{ + for (int i = 0; i < 4; i++) { + if ((long)diskPtr == (long)disks[i]) { + // is disk is on primary or secondary channel + int channel = i/2; + // is disk the master or slave + int devID = i%2; + + return (dev[channel] == devID); + } + } + panic("Unable to find disk by pointer!!\n"); +} + +//// +// Command completion +//// + +void +IdeController::setDmaComplete(IdeDisk *disk) +{ + int diskNum = getDisk(disk); + + if (diskNum < 0) + panic("Unable to find disk based on pointer %#x\n", disk); + + if (diskNum < 2) { + // clear the start/stop bit in the command register + bmi_regs.bmic0 &= ~SSBM; + // clear the bus master active bit in the status register + bmi_regs.bmis0 &= ~BMIDEA; + // set the interrupt bit + bmi_regs.bmis0 |= IDEINTS; + } else { + // clear the start/stop bit in the command register + bmi_regs.bmic1 &= ~SSBM; + // clear the bus master active bit in the status register + bmi_regs.bmis1 &= ~BMIDEA; + // set the interrupt bit + bmi_regs.bmis1 |= IDEINTS; + } +} + + +//// +// Read and write handling +//// + +Tick +IdeController::readConfig(Packet *pkt) +{ + int offset = pkt->getAddr() & PCI_CONFIG_SIZE; + if (offset < PCI_DEVICE_SPECIFIC) + return PciDev::readConfig(pkt); + assert(offset >= IDE_CTRL_CONF_START && (offset + 1) <= IDE_CTRL_CONF_END); + + pkt->allocate(); + + switch (pkt->getSize()) { + case sizeof(uint8_t): + switch (offset) { + case IDE_CTRL_CONF_DEV_TIMING: + pkt->set<uint8_t>(config_regs.sidetim); + break; + case IDE_CTRL_CONF_UDMA_CNTRL: + pkt->set<uint8_t>(config_regs.udmactl); + break; + case IDE_CTRL_CONF_PRIM_TIMING+1: + pkt->set<uint8_t>(htole(config_regs.idetim0) >> 8); + break; + case IDE_CTRL_CONF_SEC_TIMING+1: + pkt->set<uint8_t>(htole(config_regs.idetim1) >> 8); + break; + case IDE_CTRL_CONF_IDE_CONFIG: + pkt->set<uint8_t>(htole(config_regs.ideconfig) & 0xFF); + break; + case IDE_CTRL_CONF_IDE_CONFIG+1: + pkt->set<uint8_t>(htole(config_regs.ideconfig) >> 8); + break; + default: + panic("Invalid PCI configuration read for size 1 at offset: %#x!\n", + offset); + } + DPRINTF(IdeCtrl, "PCI read offset: %#x size: 1 data: %#x\n", offset, + (uint32_t)pkt->get<uint8_t>()); + break; + case sizeof(uint16_t): + switch (offset) { + case IDE_CTRL_CONF_PRIM_TIMING: + pkt->set<uint16_t>(config_regs.idetim0); + break; + case IDE_CTRL_CONF_SEC_TIMING: + pkt->set<uint16_t>(config_regs.idetim1); + break; + case IDE_CTRL_CONF_UDMA_TIMING: + pkt->set<uint16_t>(config_regs.udmatim); + break; + case IDE_CTRL_CONF_IDE_CONFIG: + pkt->set<uint16_t>(config_regs.ideconfig); + break; + default: + panic("Invalid PCI configuration read for size 2 offset: %#x!\n", + offset); + } + DPRINTF(IdeCtrl, "PCI read offset: %#x size: 2 data: %#x\n", offset, + (uint32_t)pkt->get<uint16_t>()); + break; + case sizeof(uint32_t): + panic("No 32bit reads implemented for this device."); + DPRINTF(IdeCtrl, "PCI read offset: %#x size: 4 data: %#x\n", offset, + (uint32_t)pkt->get<uint32_t>()); + break; + default: + panic("invalid access size(?) for PCI configspace!\n"); + } + pkt->result = Packet::Success; + return configDelay; + +} + + +Tick +IdeController::writeConfig(Packet *pkt) +{ + int offset = pkt->getAddr() & PCI_CONFIG_SIZE; + if (offset < PCI_DEVICE_SPECIFIC) { + PciDev::writeConfig(pkt); + } else { + assert(offset >= IDE_CTRL_CONF_START && (offset + 1) <= IDE_CTRL_CONF_END); + + switch (pkt->getSize()) { + case sizeof(uint8_t): + switch (offset) { + case IDE_CTRL_CONF_DEV_TIMING: + config_regs.sidetim = pkt->get<uint8_t>(); + break; + case IDE_CTRL_CONF_UDMA_CNTRL: + config_regs.udmactl = pkt->get<uint8_t>(); + break; + case IDE_CTRL_CONF_IDE_CONFIG: + config_regs.ideconfig = (config_regs.ideconfig & 0xFF00) | + (pkt->get<uint8_t>()); + break; + case IDE_CTRL_CONF_IDE_CONFIG+1: + config_regs.ideconfig = (config_regs.ideconfig & 0x00FF) | + pkt->get<uint8_t>() << 8; + break; + default: + panic("Invalid PCI configuration write for size 1 offset: %#x!\n", + offset); + } + DPRINTF(IdeCtrl, "PCI write offset: %#x size: 1 data: %#x\n", + offset, (uint32_t)pkt->get<uint8_t>()); + break; + case sizeof(uint16_t): + switch (offset) { + case IDE_CTRL_CONF_PRIM_TIMING: + config_regs.idetim0 = pkt->get<uint16_t>(); + break; + case IDE_CTRL_CONF_SEC_TIMING: + config_regs.idetim1 = pkt->get<uint16_t>(); + break; + case IDE_CTRL_CONF_UDMA_TIMING: + config_regs.udmatim = pkt->get<uint16_t>(); + break; + case IDE_CTRL_CONF_IDE_CONFIG: + config_regs.ideconfig = pkt->get<uint16_t>(); + break; + default: + panic("Invalid PCI configuration write for size 2 offset: %#x!\n", + offset); + } + DPRINTF(IdeCtrl, "PCI write offset: %#x size: 2 data: %#x\n", + offset, (uint32_t)pkt->get<uint16_t>()); + break; + case sizeof(uint32_t): + panic("Write of unimplemented PCI config. register: %x\n", offset); + break; + default: + panic("invalid access size(?) for PCI configspace!\n"); + } + } + + /* Trap command register writes and enable IO/BM as appropriate as well as + * BARs. */ + switch(offset) { + case PCI0_BASE_ADDR0: + if (BARAddrs[0] != 0) + pri_cmd_addr = BARAddrs[0]; + break; + + case PCI0_BASE_ADDR1: + if (BARAddrs[1] != 0) + pri_ctrl_addr = BARAddrs[1]; + break; + + case PCI0_BASE_ADDR2: + if (BARAddrs[2] != 0) + sec_cmd_addr = BARAddrs[2]; + break; + + case PCI0_BASE_ADDR3: + if (BARAddrs[3] != 0) + sec_ctrl_addr = BARAddrs[3]; + break; + + case PCI0_BASE_ADDR4: + if (BARAddrs[4] != 0) + bmi_addr = BARAddrs[4]; + break; + + case PCI_COMMAND: + if (letoh(config.command) & PCI_CMD_IOSE) + io_enabled = true; + else + io_enabled = false; + + if (letoh(config.command) & PCI_CMD_BME) + bm_enabled = true; + else + bm_enabled = false; + break; + } + pkt->result = Packet::Success; + return configDelay; +} + + +Tick +IdeController::read(Packet *pkt) +{ + Addr offset; + IdeChannel channel; + IdeRegType reg_type; + int disk; + + pkt->allocate(); + if (pkt->getSize() != 1 && pkt->getSize() != 2 && pkt->getSize() !=4) + panic("Bad IDE read size: %d\n", pkt->getSize()); + + parseAddr(pkt->getAddr(), offset, channel, reg_type); + + if (!io_enabled) { + pkt->result = Packet::Success; + return pioDelay; + } + + switch (reg_type) { + case BMI_BLOCK: + switch (pkt->getSize()) { + case sizeof(uint8_t): + pkt->set(bmi_regs.data[offset]); + break; + case sizeof(uint16_t): + pkt->set(*(uint16_t*)&bmi_regs.data[offset]); + break; + case sizeof(uint32_t): + pkt->set(*(uint32_t*)&bmi_regs.data[offset]); + break; + default: + panic("IDE read of BMI reg invalid size: %#x\n", pkt->getSize()); + } + break; + + case COMMAND_BLOCK: + case CONTROL_BLOCK: + disk = getDisk(channel); + + if (disks[disk] == NULL) { + pkt->set<uint8_t>(0); + break; + } + + switch (offset) { + case DATA_OFFSET: + switch (pkt->getSize()) { + case sizeof(uint16_t): + disks[disk]->read(offset, reg_type, pkt->getPtr<uint8_t>()); + break; + + case sizeof(uint32_t): + disks[disk]->read(offset, reg_type, pkt->getPtr<uint8_t>()); + disks[disk]->read(offset, reg_type, + pkt->getPtr<uint8_t>() + sizeof(uint16_t)); + break; + + default: + panic("IDE read of data reg invalid size: %#x\n", pkt->getSize()); + } + break; + default: + if (pkt->getSize() == sizeof(uint8_t)) { + disks[disk]->read(offset, reg_type, pkt->getPtr<uint8_t>()); + } else + panic("IDE read of command reg of invalid size: %#x\n", pkt->getSize()); + } + break; + default: + panic("IDE controller read of unknown register block type!\n"); + } + if (pkt->getSize() == 1) + DPRINTF(IdeCtrl, "read from offset: %#x size: %#x data: %#x\n", + offset, pkt->getSize(), (uint32_t)pkt->get<uint8_t>()); + else if (pkt->getSize() == 2) + DPRINTF(IdeCtrl, "read from offset: %#x size: %#x data: %#x\n", + offset, pkt->getSize(), pkt->get<uint16_t>()); + else + DPRINTF(IdeCtrl, "read from offset: %#x size: %#x data: %#x\n", + offset, pkt->getSize(), pkt->get<uint32_t>()); + + pkt->result = Packet::Success; + return pioDelay; +} + +Tick +IdeController::write(Packet *pkt) +{ + Addr offset; + IdeChannel channel; + IdeRegType reg_type; + int disk; + uint8_t oldVal, newVal; + + parseAddr(pkt->getAddr(), offset, channel, reg_type); + + if (!io_enabled) { + pkt->result = Packet::Success; + DPRINTF(IdeCtrl, "io not enabled\n"); + return pioDelay; + } + + switch (reg_type) { + case BMI_BLOCK: + if (!bm_enabled) { + pkt->result = Packet::Success; + return pioDelay; + } + + switch (offset) { + // Bus master IDE command register + case BMIC1: + case BMIC0: + if (pkt->getSize() != sizeof(uint8_t)) + panic("Invalid BMIC write size: %x\n", pkt->getSize()); + + // select the current disk based on DEV bit + disk = getDisk(channel); + + oldVal = bmi_regs.chan[channel].bmic; + newVal = pkt->get<uint8_t>(); + + // if a DMA transfer is in progress, R/W control cannot change + if (oldVal & SSBM) { + if ((oldVal & RWCON) ^ (newVal & RWCON)) { + (oldVal & RWCON) ? newVal |= RWCON : newVal &= ~RWCON; + } + } + + // see if the start/stop bit is being changed + if ((oldVal & SSBM) ^ (newVal & SSBM)) { + if (oldVal & SSBM) { + // stopping DMA transfer + DPRINTF(IdeCtrl, "Stopping DMA transfer\n"); + + // clear the BMIDEA bit + bmi_regs.chan[channel].bmis = + bmi_regs.chan[channel].bmis & ~BMIDEA; + + if (disks[disk] == NULL) + panic("DMA stop for disk %d which does not exist\n", + disk); + + // inform the disk of the DMA transfer abort + disks[disk]->abortDma(); + } else { + // starting DMA transfer + DPRINTF(IdeCtrl, "Starting DMA transfer\n"); + + // set the BMIDEA bit + bmi_regs.chan[channel].bmis = + bmi_regs.chan[channel].bmis | BMIDEA; + + if (disks[disk] == NULL) + panic("DMA start for disk %d which does not exist\n", + disk); + + // inform the disk of the DMA transfer start + disks[disk]->startDma(letoh(bmi_regs.chan[channel].bmidtp)); + } + } + + // update the register value + bmi_regs.chan[channel].bmic = newVal; + break; + + // Bus master IDE status register + case BMIS0: + case BMIS1: + if (pkt->getSize() != sizeof(uint8_t)) + panic("Invalid BMIS write size: %x\n", pkt->getSize()); + + oldVal = bmi_regs.chan[channel].bmis; + newVal = pkt->get<uint8_t>(); + + // the BMIDEA bit is RO + newVal |= (oldVal & BMIDEA); + + // to reset (set 0) IDEINTS and IDEDMAE, write 1 to each + if ((oldVal & IDEINTS) && (newVal & IDEINTS)) + newVal &= ~IDEINTS; // clear the interrupt? + else + (oldVal & IDEINTS) ? newVal |= IDEINTS : newVal &= ~IDEINTS; + + if ((oldVal & IDEDMAE) && (newVal & IDEDMAE)) + newVal &= ~IDEDMAE; + else + (oldVal & IDEDMAE) ? newVal |= IDEDMAE : newVal &= ~IDEDMAE; + + bmi_regs.chan[channel].bmis = newVal; + break; + + // Bus master IDE descriptor table pointer register + case BMIDTP0: + case BMIDTP1: + { + if (pkt->getSize() != sizeof(uint32_t)) + panic("Invalid BMIDTP write size: %x\n", pkt->getSize()); + + bmi_regs.chan[channel].bmidtp = htole(pkt->get<uint32_t>() & ~0x3); + } + break; + + default: + if (pkt->getSize() != sizeof(uint8_t) && + pkt->getSize() != sizeof(uint16_t) && + pkt->getSize() != sizeof(uint32_t)) + panic("IDE controller write of invalid write size: %x\n", + pkt->getSize()); + + // do a default copy of data into the registers + memcpy(&bmi_regs.data[offset], pkt->getPtr<uint8_t>(), pkt->getSize()); + } + break; + case COMMAND_BLOCK: + if (offset == IDE_SELECT_OFFSET) { + uint8_t *devBit = &dev[channel]; + *devBit = (letoh(pkt->get<uint8_t>()) & IDE_SELECT_DEV_BIT) ? 1 : 0; + } + // fall-through ok! + case CONTROL_BLOCK: + disk = getDisk(channel); + + if (disks[disk] == NULL) + break; + + switch (offset) { + case DATA_OFFSET: + switch (pkt->getSize()) { + case sizeof(uint16_t): + disks[disk]->write(offset, reg_type, pkt->getPtr<uint8_t>()); + break; + + case sizeof(uint32_t): + disks[disk]->write(offset, reg_type, pkt->getPtr<uint8_t>()); + disks[disk]->write(offset, reg_type, pkt->getPtr<uint8_t>() + + sizeof(uint16_t)); + break; + default: + panic("IDE write of data reg invalid size: %#x\n", pkt->getSize()); + } + break; + default: + if (pkt->getSize() == sizeof(uint8_t)) { + disks[disk]->write(offset, reg_type, pkt->getPtr<uint8_t>()); + } else + panic("IDE write of command reg of invalid size: %#x\n", pkt->getSize()); + } + break; + default: + panic("IDE controller write of unknown register block type!\n"); + } + + if (pkt->getSize() == 1) + DPRINTF(IdeCtrl, "write to offset: %#x size: %#x data: %#x\n", + offset, pkt->getSize(), (uint32_t)pkt->get<uint8_t>()); + else if (pkt->getSize() == 2) + DPRINTF(IdeCtrl, "write to offset: %#x size: %#x data: %#x\n", + offset, pkt->getSize(), pkt->get<uint16_t>()); + else + DPRINTF(IdeCtrl, "write to offset: %#x size: %#x data: %#x\n", + offset, pkt->getSize(), pkt->get<uint32_t>()); + + + pkt->result = Packet::Success; + return pioDelay; +} + +//// +// Serialization +//// + +void +IdeController::serialize(std::ostream &os) +{ + // Serialize the PciDev base class + PciDev::serialize(os); + + // Serialize register addresses and sizes + SERIALIZE_SCALAR(pri_cmd_addr); + SERIALIZE_SCALAR(pri_cmd_size); + SERIALIZE_SCALAR(pri_ctrl_addr); + SERIALIZE_SCALAR(pri_ctrl_size); + SERIALIZE_SCALAR(sec_cmd_addr); + SERIALIZE_SCALAR(sec_cmd_size); + SERIALIZE_SCALAR(sec_ctrl_addr); + SERIALIZE_SCALAR(sec_ctrl_size); + SERIALIZE_SCALAR(bmi_addr); + SERIALIZE_SCALAR(bmi_size); + + // Serialize registers + SERIALIZE_ARRAY(bmi_regs.data, + sizeof(bmi_regs.data) / sizeof(bmi_regs.data[0])); + SERIALIZE_ARRAY(dev, sizeof(dev) / sizeof(dev[0])); + SERIALIZE_ARRAY(config_regs.data, + sizeof(config_regs.data) / sizeof(config_regs.data[0])); + + // Serialize internal state + SERIALIZE_SCALAR(io_enabled); + SERIALIZE_SCALAR(bm_enabled); + SERIALIZE_ARRAY(cmd_in_progress, + sizeof(cmd_in_progress) / sizeof(cmd_in_progress[0])); +} + +void +IdeController::unserialize(Checkpoint *cp, const std::string §ion) +{ + // Unserialize the PciDev base class + PciDev::unserialize(cp, section); + + // Unserialize register addresses and sizes + UNSERIALIZE_SCALAR(pri_cmd_addr); + UNSERIALIZE_SCALAR(pri_cmd_size); + UNSERIALIZE_SCALAR(pri_ctrl_addr); + UNSERIALIZE_SCALAR(pri_ctrl_size); + UNSERIALIZE_SCALAR(sec_cmd_addr); + UNSERIALIZE_SCALAR(sec_cmd_size); + UNSERIALIZE_SCALAR(sec_ctrl_addr); + UNSERIALIZE_SCALAR(sec_ctrl_size); + UNSERIALIZE_SCALAR(bmi_addr); + UNSERIALIZE_SCALAR(bmi_size); + + // Unserialize registers + UNSERIALIZE_ARRAY(bmi_regs.data, + sizeof(bmi_regs.data) / sizeof(bmi_regs.data[0])); + UNSERIALIZE_ARRAY(dev, sizeof(dev) / sizeof(dev[0])); + UNSERIALIZE_ARRAY(config_regs.data, + sizeof(config_regs.data) / sizeof(config_regs.data[0])); + + // Unserialize internal state + UNSERIALIZE_SCALAR(io_enabled); + UNSERIALIZE_SCALAR(bm_enabled); + UNSERIALIZE_ARRAY(cmd_in_progress, + sizeof(cmd_in_progress) / sizeof(cmd_in_progress[0])); + pioPort->sendStatusChange(Port::RangeChange); +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeController) + + SimObjectParam<System *> system; + SimObjectParam<Platform *> platform; + SimObjectParam<PciConfigData *> configdata; + Param<uint32_t> pci_bus; + Param<uint32_t> pci_dev; + Param<uint32_t> pci_func; + Param<Tick> pio_latency; + Param<Tick> config_latency; + SimObjectVectorParam<IdeDisk *> disks; + +END_DECLARE_SIM_OBJECT_PARAMS(IdeController) + +BEGIN_INIT_SIM_OBJECT_PARAMS(IdeController) + + INIT_PARAM(system, "System pointer"), + INIT_PARAM(platform, "Platform pointer"), + INIT_PARAM(configdata, "PCI Config data"), + INIT_PARAM(pci_bus, "PCI bus ID"), + INIT_PARAM(pci_dev, "PCI device number"), + INIT_PARAM(pci_func, "PCI function code"), + INIT_PARAM_DFLT(pio_latency, "Programmed IO latency in bus cycles", 1), + INIT_PARAM(config_latency, "Number of cycles for a config read or write"), + INIT_PARAM(disks, "IDE disks attached to this controller") + +END_INIT_SIM_OBJECT_PARAMS(IdeController) + +CREATE_SIM_OBJECT(IdeController) +{ + IdeController::Params *params = new IdeController::Params; + params->name = getInstanceName(); + params->platform = platform; + params->system = system; + params->configData = configdata; + params->busNum = pci_bus; + params->deviceNum = pci_dev; + params->functionNum = pci_func; + params->pio_delay = pio_latency; + params->config_delay = config_latency; + params->disks = disks; + return new IdeController(params); +} + +REGISTER_SIM_OBJECT("IdeController", IdeController) + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/dev/ide_ctrl.hh b/src/dev/ide_ctrl.hh new file mode 100644 index 000000000..5842d322e --- /dev/null +++ b/src/dev/ide_ctrl.hh @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Andrew Schultz + * Miguel Serrano + */ + +/** @file + * Simple PCI IDE controller with bus mastering capability and UDMA + * modeled after controller in the Intel PIIX4 chip + */ + +#ifndef __IDE_CTRL_HH__ +#define __IDE_CTRL_HH__ + +#include "dev/pcidev.hh" +#include "dev/pcireg.h" +#include "dev/io_device.hh" + +#define BMIC0 0x0 // Bus master IDE command register +#define BMIS0 0x2 // Bus master IDE status register +#define BMIDTP0 0x4 // Bus master IDE descriptor table pointer register +#define BMIC1 0x8 // Bus master IDE command register +#define BMIS1 0xa // Bus master IDE status register +#define BMIDTP1 0xc // Bus master IDE descriptor table pointer register + +// Bus master IDE command register bit fields +#define RWCON 0x08 // Bus master read/write control +#define SSBM 0x01 // Start/stop bus master + +// Bus master IDE status register bit fields +#define DMA1CAP 0x40 // Drive 1 DMA capable +#define DMA0CAP 0x20 // Drive 0 DMA capable +#define IDEINTS 0x04 // IDE Interrupt Status +#define IDEDMAE 0x02 // IDE DMA error +#define BMIDEA 0x01 // Bus master IDE active + +// IDE Command byte fields +#define IDE_SELECT_OFFSET (6) +#define IDE_SELECT_DEV_BIT 0x10 + +#define IDE_FEATURE_OFFSET IDE_ERROR_OFFSET +#define IDE_COMMAND_OFFSET IDE_STATUS_OFFSET + +// IDE Timing Register bit fields +#define IDETIM_DECODE_EN 0x8000 + +// PCI device specific register byte offsets +#define IDE_CTRL_CONF_START 0x40 +#define IDE_CTRL_CONF_END ((IDE_CTRL_CONF_START) + sizeof(config_regs)) + +#define IDE_CTRL_CONF_PRIM_TIMING 0x40 +#define IDE_CTRL_CONF_SEC_TIMING 0x42 +#define IDE_CTRL_CONF_DEV_TIMING 0x44 +#define IDE_CTRL_CONF_UDMA_CNTRL 0x48 +#define IDE_CTRL_CONF_UDMA_TIMING 0x4A +#define IDE_CTRL_CONF_IDE_CONFIG 0x54 + + +enum IdeRegType { + COMMAND_BLOCK, + CONTROL_BLOCK, + BMI_BLOCK +}; + +class IdeDisk; +class IntrControl; +class PciConfigAll; +class Platform; + +/** + * Device model for an Intel PIIX4 IDE controller + */ + +class IdeController : public PciDev +{ + friend class IdeDisk; + + enum IdeChannel { + PRIMARY = 0, + SECONDARY = 1 + }; + + private: + /** Primary command block registers */ + Addr pri_cmd_addr; + Addr pri_cmd_size; + /** Primary control block registers */ + Addr pri_ctrl_addr; + Addr pri_ctrl_size; + /** Secondary command block registers */ + Addr sec_cmd_addr; + Addr sec_cmd_size; + /** Secondary control block registers */ + Addr sec_ctrl_addr; + Addr sec_ctrl_size; + /** Bus master interface (BMI) registers */ + Addr bmi_addr; + Addr bmi_size; + + private: + /** Registers used for bus master interface */ + union { + uint8_t data[16]; + + struct { + uint8_t bmic0; + uint8_t reserved_0; + uint8_t bmis0; + uint8_t reserved_1; + uint32_t bmidtp0; + uint8_t bmic1; + uint8_t reserved_2; + uint8_t bmis1; + uint8_t reserved_3; + uint32_t bmidtp1; + }; + + struct { + uint8_t bmic; + uint8_t reserved_4; + uint8_t bmis; + uint8_t reserved_5; + uint32_t bmidtp; + } chan[2]; + + } bmi_regs; + /** Shadows of the device select bit */ + uint8_t dev[2]; + /** Registers used in device specific PCI configuration */ + union { + uint8_t data[22]; + + struct { + uint16_t idetim0; + uint16_t idetim1; + uint8_t sidetim; + uint8_t reserved_0[3]; + uint8_t udmactl; + uint8_t reserved_1; + uint16_t udmatim; + uint8_t reserved_2[8]; + uint16_t ideconfig; + }; + } config_regs; + + // Internal management variables + bool io_enabled; + bool bm_enabled; + bool cmd_in_progress[4]; + + private: + /** IDE disks connected to controller */ + IdeDisk *disks[4]; + + private: + /** Parse the access address to pass on to device */ + void parseAddr(const Addr &addr, Addr &offset, IdeChannel &channel, + IdeRegType ®_type); + + /** Select the disk based on the channel and device bit */ + int getDisk(IdeChannel channel); + + /** Select the disk based on a pointer */ + int getDisk(IdeDisk *diskPtr); + + public: + /** See if a disk is selected based on its pointer */ + bool isDiskSelected(IdeDisk *diskPtr); + + public: + struct Params : public PciDev::Params + { + /** Array of disk objects */ + std::vector<IdeDisk *> disks; + }; + const Params *params() const { return (const Params *)_params; } + + public: + IdeController(Params *p); + ~IdeController(); + + virtual Tick writeConfig(Packet *pkt); + virtual Tick readConfig(Packet *pkt); + + void setDmaComplete(IdeDisk *disk); + + /** + * Read a done field for a given target. + * @param pkt Packet describing what is to be read + * @return The amount of time to complete this request + */ + virtual Tick read(Packet *pkt); + + /** + * Write a done field for a given target. + * @param pkt Packet describing what is to be written + * @return The amount of time to complete this request + */ + virtual Tick write(Packet *pkt); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +}; +#endif // __IDE_CTRL_HH_ diff --git a/src/dev/ide_disk.cc b/src/dev/ide_disk.cc new file mode 100644 index 000000000..5d3346b1e --- /dev/null +++ b/src/dev/ide_disk.cc @@ -0,0 +1,1147 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Andrew Schultz + * Ali Saidi + */ + +/** @file + * Device model implementation for an IDE disk + */ + +#include <cerrno> +#include <cstring> +#include <deque> +#include <string> + +#include "base/chunk_generator.hh" +#include "base/cprintf.hh" // csprintf +#include "base/trace.hh" +#include "dev/disk_image.hh" +#include "dev/ide_disk.hh" +#include "dev/ide_ctrl.hh" +#include "dev/tsunami.hh" +#include "dev/tsunami_pchip.hh" +#include "sim/builder.hh" +#include "sim/sim_object.hh" +#include "sim/root.hh" +#include "arch/isa_traits.hh" + +using namespace std; +using namespace TheISA; + +IdeDisk::IdeDisk(const string &name, DiskImage *img, + int id, Tick delay) + : SimObject(name), ctrl(NULL), image(img), diskDelay(delay), + dmaTransferEvent(this), dmaReadCG(NULL), dmaReadWaitEvent(this), + dmaWriteCG(NULL), dmaWriteWaitEvent(this), dmaPrdReadEvent(this), + dmaReadEvent(this), dmaWriteEvent(this) +{ + // Reset the device state + reset(id); + + // fill out the drive ID structure + memset(&driveID, 0, sizeof(struct ataparams)); + + // Calculate LBA and C/H/S values + uint16_t cylinders; + uint8_t heads; + uint8_t sectors; + + uint32_t lba_size = image->size(); + if (lba_size >= 16383*16*63) { + cylinders = 16383; + heads = 16; + sectors = 63; + } else { + if (lba_size >= 63) + sectors = 63; + else + sectors = lba_size; + + if ((lba_size / sectors) >= 16) + heads = 16; + else + heads = (lba_size / sectors); + + cylinders = lba_size / (heads * sectors); + } + + // Setup the model name + strncpy((char *)driveID.atap_model, "5MI EDD si k", + sizeof(driveID.atap_model)); + // Set the maximum multisector transfer size + driveID.atap_multi = MAX_MULTSECT; + // IORDY supported, IORDY disabled, LBA enabled, DMA enabled + driveID.atap_capabilities1 = 0x7; + // UDMA support, EIDE support + driveID.atap_extensions = 0x6; + // Setup default C/H/S settings + driveID.atap_cylinders = cylinders; + driveID.atap_sectors = sectors; + driveID.atap_heads = heads; + // Setup the current multisector transfer size + driveID.atap_curmulti = MAX_MULTSECT; + driveID.atap_curmulti_valid = 0x1; + // Number of sectors on disk + driveID.atap_capacity = lba_size; + // Multiword DMA mode 2 and below supported + driveID.atap_dmamode_supp = 0x4; + // Set PIO mode 4 and 3 supported + driveID.atap_piomode_supp = 0x3; + // Set DMA mode 4 and below supported + driveID.atap_udmamode_supp = 0x1f; + // Statically set hardware config word + driveID.atap_hwreset_res = 0x4001; + + //arbitrary for now... + driveID.atap_ata_major = WDC_VER_ATA7; +} + +IdeDisk::~IdeDisk() +{ + // destroy the data buffer + delete [] dataBuffer; +} + +void +IdeDisk::reset(int id) +{ + // initialize the data buffer and shadow registers + dataBuffer = new uint8_t[MAX_DMA_SIZE]; + + memset(dataBuffer, 0, MAX_DMA_SIZE); + memset(&cmdReg, 0, sizeof(CommandReg_t)); + memset(&curPrd.entry, 0, sizeof(PrdEntry_t)); + + curPrdAddr = 0; + curSector = 0; + cmdBytes = 0; + cmdBytesLeft = 0; + drqBytesLeft = 0; + dmaRead = false; + intrPending = false; + + // set the device state to idle + dmaState = Dma_Idle; + + if (id == DEV0) { + devState = Device_Idle_S; + devID = DEV0; + } else if (id == DEV1) { + devState = Device_Idle_NS; + devID = DEV1; + } else { + panic("Invalid device ID: %#x\n", id); + } + + // set the device ready bit + status = STATUS_DRDY_BIT; + + /* The error register must be set to 0x1 on start-up to + indicate that no diagnostic error was detected */ + cmdReg.error = 0x1; +} + +//// +// Utility functions +//// + +bool +IdeDisk::isDEVSelect() +{ + return ctrl->isDiskSelected(this); +} + +Addr +IdeDisk::pciToDma(Addr pciAddr) +{ + if (ctrl) + return ctrl->plat->pciToDma(pciAddr); + else + panic("Access to unset controller!\n"); +} + +//// +// Device registers read/write +//// + +void +IdeDisk::read(const Addr &offset, IdeRegType reg_type, uint8_t *data) +{ + DevAction_t action = ACT_NONE; + + switch (reg_type) { + case COMMAND_BLOCK: + switch (offset) { + // Data transfers occur two bytes at a time + case DATA_OFFSET: + *(uint16_t*)data = cmdReg.data; + action = ACT_DATA_READ_SHORT; + break; + case ERROR_OFFSET: + *data = cmdReg.error; + break; + case NSECTOR_OFFSET: + *data = cmdReg.sec_count; + break; + case SECTOR_OFFSET: + *data = cmdReg.sec_num; + break; + case LCYL_OFFSET: + *data = cmdReg.cyl_low; + break; + case HCYL_OFFSET: + *data = cmdReg.cyl_high; + break; + case DRIVE_OFFSET: + *data = cmdReg.drive; + break; + case STATUS_OFFSET: + *data = status; + action = ACT_STAT_READ; + break; + default: + panic("Invalid IDE command register offset: %#x\n", offset); + } + break; + case CONTROL_BLOCK: + if (offset == ALTSTAT_OFFSET) + *data = status; + else + panic("Invalid IDE control register offset: %#x\n", offset); + break; + default: + panic("Unknown register block!\n"); + } + DPRINTF(IdeDisk, "Read to disk at offset: %#x data %#x\n", offset, + (uint32_t)*data); + + if (action != ACT_NONE) + updateState(action); +} + +void +IdeDisk::write(const Addr &offset, IdeRegType reg_type, const uint8_t *data) +{ + DevAction_t action = ACT_NONE; + + switch (reg_type) { + case COMMAND_BLOCK: + switch (offset) { + case DATA_OFFSET: + cmdReg.data = *(uint16_t*)data; + action = ACT_DATA_WRITE_SHORT; + break; + case FEATURES_OFFSET: + break; + case NSECTOR_OFFSET: + cmdReg.sec_count = *data; + break; + case SECTOR_OFFSET: + cmdReg.sec_num = *data; + break; + case LCYL_OFFSET: + cmdReg.cyl_low = *data; + break; + case HCYL_OFFSET: + cmdReg.cyl_high = *data; + break; + case DRIVE_OFFSET: + cmdReg.drive = *data; + action = ACT_SELECT_WRITE; + break; + case COMMAND_OFFSET: + cmdReg.command = *data; + action = ACT_CMD_WRITE; + break; + default: + panic("Invalid IDE command register offset: %#x\n", offset); + } + break; + case CONTROL_BLOCK: + if (offset == CONTROL_OFFSET) { + if (*data & CONTROL_RST_BIT) { + // force the device into the reset state + devState = Device_Srst; + action = ACT_SRST_SET; + } else if (devState == Device_Srst && !(*data & CONTROL_RST_BIT)) + action = ACT_SRST_CLEAR; + + nIENBit = (*data & CONTROL_IEN_BIT) ? true : false; + } + else + panic("Invalid IDE control register offset: %#x\n", offset); + break; + default: + panic("Unknown register block!\n"); + } + + DPRINTF(IdeDisk, "Write to disk at offset: %#x data %#x\n", offset, + (uint32_t)*data); + if (action != ACT_NONE) + updateState(action); +} + +//// +// Perform DMA transactions +//// + +void +IdeDisk::doDmaTransfer() +{ + if (dmaState != Dma_Transfer || devState != Transfer_Data_Dma) + panic("Inconsistent DMA transfer state: dmaState = %d devState = %d\n", + dmaState, devState); + + if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) { + dmaTransferEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + return; + } else + ctrl->dmaRead(curPrdAddr, sizeof(PrdEntry_t), &dmaPrdReadEvent, + (uint8_t*)&curPrd.entry); +} + +void +IdeDisk::dmaPrdReadDone() +{ + DPRINTF(IdeDisk, + "PRD: baseAddr:%#x (%#x) byteCount:%d (%d) eot:%#x sector:%d\n", + curPrd.getBaseAddr(), pciToDma(curPrd.getBaseAddr()), + curPrd.getByteCount(), (cmdBytesLeft/SectorSize), + curPrd.getEOT(), curSector); + + // the prd pointer has already been translated, so just do an increment + curPrdAddr = curPrdAddr + sizeof(PrdEntry_t); + + if (dmaRead) + doDmaDataRead(); + else + doDmaDataWrite(); +} + +void +IdeDisk::doDmaDataRead() +{ + /** @todo we need to figure out what the delay actually will be */ + Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); + + DPRINTF(IdeDisk, "doDmaRead, diskDelay: %d totalDiskDelay: %d\n", + diskDelay, totalDiskDelay); + + dmaReadWaitEvent.schedule(curTick + totalDiskDelay); +} + +void +IdeDisk::regStats() +{ + using namespace Stats; + dmaReadFullPages + .name(name() + ".dma_read_full_pages") + .desc("Number of full page size DMA reads (not PRD).") + ; + dmaReadBytes + .name(name() + ".dma_read_bytes") + .desc("Number of bytes transfered via DMA reads (not PRD).") + ; + dmaReadTxs + .name(name() + ".dma_read_txs") + .desc("Number of DMA read transactions (not PRD).") + ; + + dmaWriteFullPages + .name(name() + ".dma_write_full_pages") + .desc("Number of full page size DMA writes.") + ; + dmaWriteBytes + .name(name() + ".dma_write_bytes") + .desc("Number of bytes transfered via DMA writes.") + ; + dmaWriteTxs + .name(name() + ".dma_write_txs") + .desc("Number of DMA write transactions.") + ; +} + +void +IdeDisk::doDmaRead() +{ + + if (!dmaReadCG) { + // clear out the data buffer + memset(dataBuffer, 0, MAX_DMA_SIZE); + dmaReadCG = new ChunkGenerator(curPrd.getBaseAddr(), + curPrd.getByteCount(), TheISA::PageBytes); + + } + if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) { + dmaReadWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + return; + } else if (!dmaReadCG->done()) { + assert(dmaReadCG->complete() < MAX_DMA_SIZE); + ctrl->dmaRead(pciToDma(dmaReadCG->addr()), dmaReadCG->size(), + &dmaReadWaitEvent, dataBuffer + dmaReadCG->complete()); + dmaReadBytes += dmaReadCG->size(); + dmaReadTxs++; + if (dmaReadCG->size() == TheISA::PageBytes) + dmaReadFullPages++; + dmaReadCG->next(); + } else { + assert(dmaReadCG->done()); + delete dmaReadCG; + dmaReadCG = NULL; + dmaReadDone(); + } +} + +void +IdeDisk::dmaReadDone() +{ + + uint32_t bytesWritten = 0; + + + // write the data to the disk image + for (bytesWritten = 0; bytesWritten < curPrd.getByteCount(); + bytesWritten += SectorSize) { + + cmdBytesLeft -= SectorSize; + writeDisk(curSector++, (uint8_t *)(dataBuffer + bytesWritten)); + } + + // check for the EOT + if (curPrd.getEOT()) { + assert(cmdBytesLeft == 0); + dmaState = Dma_Idle; + updateState(ACT_DMA_DONE); + } else { + doDmaTransfer(); + } +} + +void +IdeDisk::doDmaDataWrite() +{ + /** @todo we need to figure out what the delay actually will be */ + Tick totalDiskDelay = diskDelay + (curPrd.getByteCount() / SectorSize); + uint32_t bytesRead = 0; + + DPRINTF(IdeDisk, "doDmaWrite, diskDelay: %d totalDiskDelay: %d\n", + diskDelay, totalDiskDelay); + + memset(dataBuffer, 0, MAX_DMA_SIZE); + assert(cmdBytesLeft <= MAX_DMA_SIZE); + while (bytesRead < curPrd.getByteCount()) { + readDisk(curSector++, (uint8_t *)(dataBuffer + bytesRead)); + bytesRead += SectorSize; + cmdBytesLeft -= SectorSize; + } + + dmaWriteWaitEvent.schedule(curTick + totalDiskDelay); +} + +void +IdeDisk::doDmaWrite() +{ + + if (!dmaWriteCG) { + // clear out the data buffer + dmaWriteCG = new ChunkGenerator(curPrd.getBaseAddr(), + curPrd.getByteCount(), TheISA::PageBytes); + } + if (ctrl->dmaPending() || ctrl->getState() != SimObject::Running) { + dmaWriteWaitEvent.schedule(curTick + DMA_BACKOFF_PERIOD); + return; + } else if (!dmaWriteCG->done()) { + assert(dmaWriteCG->complete() < MAX_DMA_SIZE); + ctrl->dmaWrite(pciToDma(dmaWriteCG->addr()), dmaWriteCG->size(), + &dmaWriteWaitEvent, dataBuffer + dmaWriteCG->complete()); + dmaWriteBytes += dmaWriteCG->size(); + dmaWriteTxs++; + if (dmaWriteCG->size() == TheISA::PageBytes) + dmaWriteFullPages++; + dmaWriteCG->next(); + } else { + assert(dmaWriteCG->done()); + delete dmaWriteCG; + dmaWriteCG = NULL; + dmaWriteDone(); + } +} + +void +IdeDisk::dmaWriteDone() +{ + // check for the EOT + if (curPrd.getEOT()) { + assert(cmdBytesLeft == 0); + dmaState = Dma_Idle; + updateState(ACT_DMA_DONE); + } else { + doDmaTransfer(); + } +} + +//// +// Disk utility routines +/// + +void +IdeDisk::readDisk(uint32_t sector, uint8_t *data) +{ + uint32_t bytesRead = image->read(data, sector); + + if (bytesRead != SectorSize) + panic("Can't read from %s. Only %d of %d read. errno=%d\n", + name(), bytesRead, SectorSize, errno); +} + +void +IdeDisk::writeDisk(uint32_t sector, uint8_t *data) +{ + uint32_t bytesWritten = image->write(data, sector); + + if (bytesWritten != SectorSize) + panic("Can't write to %s. Only %d of %d written. errno=%d\n", + name(), bytesWritten, SectorSize, errno); +} + +//// +// Setup and handle commands +//// + +void +IdeDisk::startDma(const uint32_t &prdTableBase) +{ + if (dmaState != Dma_Start) + panic("Inconsistent DMA state, should be in Dma_Start!\n"); + + if (devState != Transfer_Data_Dma) + panic("Inconsistent device state for DMA start!\n"); + + // PRD base address is given by bits 31:2 + curPrdAddr = pciToDma((Addr)(prdTableBase & ~ULL(0x3))); + + dmaState = Dma_Transfer; + + // schedule dma transfer (doDmaTransfer) + dmaTransferEvent.schedule(curTick + 1); +} + +void +IdeDisk::abortDma() +{ + if (dmaState == Dma_Idle) + panic("Inconsistent DMA state, should be Start or Transfer!"); + + if (devState != Transfer_Data_Dma && devState != Prepare_Data_Dma) + panic("Inconsistent device state, should be Transfer or Prepare!\n"); + + updateState(ACT_CMD_ERROR); +} + +void +IdeDisk::startCommand() +{ + DevAction_t action = ACT_NONE; + uint32_t size = 0; + dmaRead = false; + + // Decode commands + switch (cmdReg.command) { + // Supported non-data commands + case WDSF_READ_NATIVE_MAX: + size = image->size() - 1; + cmdReg.sec_num = (size & 0xff); + cmdReg.cyl_low = ((size & 0xff00) >> 8); + cmdReg.cyl_high = ((size & 0xff0000) >> 16); + cmdReg.head = ((size & 0xf000000) >> 24); + + devState = Command_Execution; + action = ACT_CMD_COMPLETE; + break; + + case WDCC_RECAL: + case WDCC_IDP: + case WDCC_STANDBY_IMMED: + case WDCC_FLUSHCACHE: + case WDSF_VERIFY: + case WDSF_SEEK: + case SET_FEATURES: + case WDCC_SETMULTI: + devState = Command_Execution; + action = ACT_CMD_COMPLETE; + break; + + // Supported PIO data-in commands + case WDCC_IDENTIFY: + cmdBytes = cmdBytesLeft = sizeof(struct ataparams); + devState = Prepare_Data_In; + action = ACT_DATA_READY; + break; + + case WDCC_READMULTI: + case WDCC_READ: + if (!(cmdReg.drive & DRIVE_LBA_BIT)) + panic("Attempt to perform CHS access, only supports LBA\n"); + + if (cmdReg.sec_count == 0) + cmdBytes = cmdBytesLeft = (256 * SectorSize); + else + cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); + + curSector = getLBABase(); + + /** @todo make this a scheduled event to simulate disk delay */ + devState = Prepare_Data_In; + action = ACT_DATA_READY; + break; + + // Supported PIO data-out commands + case WDCC_WRITEMULTI: + case WDCC_WRITE: + if (!(cmdReg.drive & DRIVE_LBA_BIT)) + panic("Attempt to perform CHS access, only supports LBA\n"); + + if (cmdReg.sec_count == 0) + cmdBytes = cmdBytesLeft = (256 * SectorSize); + else + cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); + + curSector = getLBABase(); + + devState = Prepare_Data_Out; + action = ACT_DATA_READY; + break; + + // Supported DMA commands + case WDCC_WRITEDMA: + dmaRead = true; // a write to the disk is a DMA read from memory + case WDCC_READDMA: + if (!(cmdReg.drive & DRIVE_LBA_BIT)) + panic("Attempt to perform CHS access, only supports LBA\n"); + + if (cmdReg.sec_count == 0) + cmdBytes = cmdBytesLeft = (256 * SectorSize); + else + cmdBytes = cmdBytesLeft = (cmdReg.sec_count * SectorSize); + + curSector = getLBABase(); + + devState = Prepare_Data_Dma; + action = ACT_DMA_READY; + break; + + default: + panic("Unsupported ATA command: %#x\n", cmdReg.command); + } + + if (action != ACT_NONE) { + // set the BSY bit + status |= STATUS_BSY_BIT; + // clear the DRQ bit + status &= ~STATUS_DRQ_BIT; + // clear the DF bit + status &= ~STATUS_DF_BIT; + + updateState(action); + } +} + +//// +// Handle setting and clearing interrupts +//// + +void +IdeDisk::intrPost() +{ + DPRINTF(IdeDisk, "Posting Interrupt\n"); + if (intrPending) + panic("Attempt to post an interrupt with one pending\n"); + + intrPending = true; + + // talk to controller to set interrupt + if (ctrl) { + ctrl->bmi_regs.bmis0 |= IDEINTS; + ctrl->intrPost(); + } +} + +void +IdeDisk::intrClear() +{ + DPRINTF(IdeDisk, "Clearing Interrupt\n"); + if (!intrPending) + panic("Attempt to clear a non-pending interrupt\n"); + + intrPending = false; + + // talk to controller to clear interrupt + if (ctrl) + ctrl->intrClear(); +} + +//// +// Manage the device internal state machine +//// + +void +IdeDisk::updateState(DevAction_t action) +{ + switch (devState) { + case Device_Srst: + if (action == ACT_SRST_SET) { + // set the BSY bit + status |= STATUS_BSY_BIT; + } else if (action == ACT_SRST_CLEAR) { + // clear the BSY bit + status &= ~STATUS_BSY_BIT; + + // reset the device state + reset(devID); + } + break; + + case Device_Idle_S: + if (action == ACT_SELECT_WRITE && !isDEVSelect()) { + devState = Device_Idle_NS; + } else if (action == ACT_CMD_WRITE) { + startCommand(); + } + + break; + + case Device_Idle_SI: + if (action == ACT_SELECT_WRITE && !isDEVSelect()) { + devState = Device_Idle_NS; + intrClear(); + } else if (action == ACT_STAT_READ || isIENSet()) { + devState = Device_Idle_S; + intrClear(); + } else if (action == ACT_CMD_WRITE) { + intrClear(); + startCommand(); + } + + break; + + case Device_Idle_NS: + if (action == ACT_SELECT_WRITE && isDEVSelect()) { + if (!isIENSet() && intrPending) { + devState = Device_Idle_SI; + intrPost(); + } + if (isIENSet() || !intrPending) { + devState = Device_Idle_S; + } + } + break; + + case Command_Execution: + if (action == ACT_CMD_COMPLETE) { + // clear the BSY bit + setComplete(); + + if (!isIENSet()) { + devState = Device_Idle_SI; + intrPost(); + } else { + devState = Device_Idle_S; + } + } + break; + + case Prepare_Data_In: + if (action == ACT_CMD_ERROR) { + // clear the BSY bit + setComplete(); + + if (!isIENSet()) { + devState = Device_Idle_SI; + intrPost(); + } else { + devState = Device_Idle_S; + } + } else if (action == ACT_DATA_READY) { + // clear the BSY bit + status &= ~STATUS_BSY_BIT; + // set the DRQ bit + status |= STATUS_DRQ_BIT; + + // copy the data into the data buffer + if (cmdReg.command == WDCC_IDENTIFY) { + // Reset the drqBytes for this block + drqBytesLeft = sizeof(struct ataparams); + + memcpy((void *)dataBuffer, (void *)&driveID, + sizeof(struct ataparams)); + } else { + // Reset the drqBytes for this block + drqBytesLeft = SectorSize; + + readDisk(curSector++, dataBuffer); + } + + // put the first two bytes into the data register + memcpy((void *)&cmdReg.data, (void *)dataBuffer, + sizeof(uint16_t)); + + if (!isIENSet()) { + devState = Data_Ready_INTRQ_In; + intrPost(); + } else { + devState = Transfer_Data_In; + } + } + break; + + case Data_Ready_INTRQ_In: + if (action == ACT_STAT_READ) { + devState = Transfer_Data_In; + intrClear(); + } + break; + + case Transfer_Data_In: + if (action == ACT_DATA_READ_BYTE || action == ACT_DATA_READ_SHORT) { + if (action == ACT_DATA_READ_BYTE) { + panic("DEBUG: READING DATA ONE BYTE AT A TIME!\n"); + } else { + drqBytesLeft -= 2; + cmdBytesLeft -= 2; + + // copy next short into data registers + if (drqBytesLeft) + memcpy((void *)&cmdReg.data, + (void *)&dataBuffer[SectorSize - drqBytesLeft], + sizeof(uint16_t)); + } + + if (drqBytesLeft == 0) { + if (cmdBytesLeft == 0) { + // Clear the BSY bit + setComplete(); + devState = Device_Idle_S; + } else { + devState = Prepare_Data_In; + // set the BSY_BIT + status |= STATUS_BSY_BIT; + // clear the DRQ_BIT + status &= ~STATUS_DRQ_BIT; + + /** @todo change this to a scheduled event to simulate + disk delay */ + updateState(ACT_DATA_READY); + } + } + } + break; + + case Prepare_Data_Out: + if (action == ACT_CMD_ERROR || cmdBytesLeft == 0) { + // clear the BSY bit + setComplete(); + + if (!isIENSet()) { + devState = Device_Idle_SI; + intrPost(); + } else { + devState = Device_Idle_S; + } + } else if (action == ACT_DATA_READY && cmdBytesLeft != 0) { + // clear the BSY bit + status &= ~STATUS_BSY_BIT; + // set the DRQ bit + status |= STATUS_DRQ_BIT; + + // clear the data buffer to get it ready for writes + memset(dataBuffer, 0, MAX_DMA_SIZE); + + // reset the drqBytes for this block + drqBytesLeft = SectorSize; + + if (cmdBytesLeft == cmdBytes || isIENSet()) { + devState = Transfer_Data_Out; + } else { + devState = Data_Ready_INTRQ_Out; + intrPost(); + } + } + break; + + case Data_Ready_INTRQ_Out: + if (action == ACT_STAT_READ) { + devState = Transfer_Data_Out; + intrClear(); + } + break; + + case Transfer_Data_Out: + if (action == ACT_DATA_WRITE_BYTE || + action == ACT_DATA_WRITE_SHORT) { + + if (action == ACT_DATA_READ_BYTE) { + panic("DEBUG: WRITING DATA ONE BYTE AT A TIME!\n"); + } else { + // copy the latest short into the data buffer + memcpy((void *)&dataBuffer[SectorSize - drqBytesLeft], + (void *)&cmdReg.data, + sizeof(uint16_t)); + + drqBytesLeft -= 2; + cmdBytesLeft -= 2; + } + + if (drqBytesLeft == 0) { + // copy the block to the disk + writeDisk(curSector++, dataBuffer); + + // set the BSY bit + status |= STATUS_BSY_BIT; + // set the seek bit + status |= STATUS_SEEK_BIT; + // clear the DRQ bit + status &= ~STATUS_DRQ_BIT; + + devState = Prepare_Data_Out; + + /** @todo change this to a scheduled event to simulate + disk delay */ + updateState(ACT_DATA_READY); + } + } + break; + + case Prepare_Data_Dma: + if (action == ACT_CMD_ERROR) { + // clear the BSY bit + setComplete(); + + if (!isIENSet()) { + devState = Device_Idle_SI; + intrPost(); + } else { + devState = Device_Idle_S; + } + } else if (action == ACT_DMA_READY) { + // clear the BSY bit + status &= ~STATUS_BSY_BIT; + // set the DRQ bit + status |= STATUS_DRQ_BIT; + + devState = Transfer_Data_Dma; + + if (dmaState != Dma_Idle) + panic("Inconsistent DMA state, should be Dma_Idle\n"); + + dmaState = Dma_Start; + // wait for the write to the DMA start bit + } + break; + + case Transfer_Data_Dma: + if (action == ACT_CMD_ERROR || action == ACT_DMA_DONE) { + // clear the BSY bit + setComplete(); + // set the seek bit + status |= STATUS_SEEK_BIT; + // clear the controller state for DMA transfer + ctrl->setDmaComplete(this); + + if (!isIENSet()) { + devState = Device_Idle_SI; + intrPost(); + } else { + devState = Device_Idle_S; + } + } + break; + + default: + panic("Unknown IDE device state: %#x\n", devState); + } +} + +void +IdeDisk::serialize(ostream &os) +{ + // Check all outstanding events to see if they are scheduled + // these are all mutually exclusive + Tick reschedule = 0; + Events_t event = None; + + int eventCount = 0; + + if (dmaTransferEvent.scheduled()) { + reschedule = dmaTransferEvent.when(); + event = Transfer; + eventCount++; + } + if (dmaReadWaitEvent.scheduled()) { + reschedule = dmaReadWaitEvent.when(); + event = ReadWait; + eventCount++; + } + if (dmaWriteWaitEvent.scheduled()) { + reschedule = dmaWriteWaitEvent.when(); + event = WriteWait; + eventCount++; + } + if (dmaPrdReadEvent.scheduled()) { + reschedule = dmaPrdReadEvent.when(); + event = PrdRead; + eventCount++; + } + if (dmaReadEvent.scheduled()) { + reschedule = dmaReadEvent.when(); + event = DmaRead; + eventCount++; + } + if (dmaWriteEvent.scheduled()) { + reschedule = dmaWriteEvent.when(); + event = DmaWrite; + eventCount++; + } + + assert(eventCount <= 1); + + SERIALIZE_SCALAR(reschedule); + SERIALIZE_ENUM(event); + + // Serialize device registers + SERIALIZE_SCALAR(cmdReg.data); + SERIALIZE_SCALAR(cmdReg.sec_count); + SERIALIZE_SCALAR(cmdReg.sec_num); + SERIALIZE_SCALAR(cmdReg.cyl_low); + SERIALIZE_SCALAR(cmdReg.cyl_high); + SERIALIZE_SCALAR(cmdReg.drive); + SERIALIZE_SCALAR(cmdReg.command); + SERIALIZE_SCALAR(status); + SERIALIZE_SCALAR(nIENBit); + SERIALIZE_SCALAR(devID); + + // Serialize the PRD related information + SERIALIZE_SCALAR(curPrd.entry.baseAddr); + SERIALIZE_SCALAR(curPrd.entry.byteCount); + SERIALIZE_SCALAR(curPrd.entry.endOfTable); + SERIALIZE_SCALAR(curPrdAddr); + + /** @todo need to serialized chunk generator stuff!! */ + // Serialize current transfer related information + SERIALIZE_SCALAR(cmdBytesLeft); + SERIALIZE_SCALAR(cmdBytes); + SERIALIZE_SCALAR(drqBytesLeft); + SERIALIZE_SCALAR(curSector); + SERIALIZE_SCALAR(dmaRead); + SERIALIZE_SCALAR(intrPending); + SERIALIZE_ENUM(devState); + SERIALIZE_ENUM(dmaState); + SERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); +} + +void +IdeDisk::unserialize(Checkpoint *cp, const string §ion) +{ + // Reschedule events that were outstanding + // these are all mutually exclusive + Tick reschedule = 0; + Events_t event = None; + + UNSERIALIZE_SCALAR(reschedule); + UNSERIALIZE_ENUM(event); + + switch (event) { + case None : break; + case Transfer : dmaTransferEvent.schedule(reschedule); break; + case ReadWait : dmaReadWaitEvent.schedule(reschedule); break; + case WriteWait : dmaWriteWaitEvent.schedule(reschedule); break; + case PrdRead : dmaPrdReadEvent.schedule(reschedule); break; + case DmaRead : dmaReadEvent.schedule(reschedule); break; + case DmaWrite : dmaWriteEvent.schedule(reschedule); break; + } + + // Unserialize device registers + UNSERIALIZE_SCALAR(cmdReg.data); + UNSERIALIZE_SCALAR(cmdReg.sec_count); + UNSERIALIZE_SCALAR(cmdReg.sec_num); + UNSERIALIZE_SCALAR(cmdReg.cyl_low); + UNSERIALIZE_SCALAR(cmdReg.cyl_high); + UNSERIALIZE_SCALAR(cmdReg.drive); + UNSERIALIZE_SCALAR(cmdReg.command); + UNSERIALIZE_SCALAR(status); + UNSERIALIZE_SCALAR(nIENBit); + UNSERIALIZE_SCALAR(devID); + + // Unserialize the PRD related information + UNSERIALIZE_SCALAR(curPrd.entry.baseAddr); + UNSERIALIZE_SCALAR(curPrd.entry.byteCount); + UNSERIALIZE_SCALAR(curPrd.entry.endOfTable); + UNSERIALIZE_SCALAR(curPrdAddr); + + /** @todo need to serialized chunk generator stuff!! */ + // Unserialize current transfer related information + UNSERIALIZE_SCALAR(cmdBytes); + UNSERIALIZE_SCALAR(cmdBytesLeft); + UNSERIALIZE_SCALAR(drqBytesLeft); + UNSERIALIZE_SCALAR(curSector); + UNSERIALIZE_SCALAR(dmaRead); + UNSERIALIZE_SCALAR(intrPending); + UNSERIALIZE_ENUM(devState); + UNSERIALIZE_ENUM(dmaState); + UNSERIALIZE_ARRAY(dataBuffer, MAX_DMA_SIZE); +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +enum DriveID { master, slave }; +static const char *DriveID_strings[] = { "master", "slave" }; +BEGIN_DECLARE_SIM_OBJECT_PARAMS(IdeDisk) + + SimObjectParam<DiskImage *> image; + SimpleEnumParam<DriveID> driveID; + Param<int> delay; + +END_DECLARE_SIM_OBJECT_PARAMS(IdeDisk) + +BEGIN_INIT_SIM_OBJECT_PARAMS(IdeDisk) + + INIT_PARAM(image, "Disk image"), + INIT_ENUM_PARAM(driveID, "Drive ID (0=master 1=slave)", DriveID_strings), + INIT_PARAM_DFLT(delay, "Fixed disk delay in microseconds", 1) + +END_INIT_SIM_OBJECT_PARAMS(IdeDisk) + + +CREATE_SIM_OBJECT(IdeDisk) +{ + return new IdeDisk(getInstanceName(), image, driveID, delay); +} + +REGISTER_SIM_OBJECT("IdeDisk", IdeDisk) + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/dev/ide_disk.hh b/src/dev/ide_disk.hh new file mode 100644 index 000000000..2ed860013 --- /dev/null +++ b/src/dev/ide_disk.hh @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Andrew Schultz + */ + +/** @file + * Device model for an IDE disk + */ + +#ifndef __IDE_DISK_HH__ +#define __IDE_DISK_HH__ + +#include "base/statistics.hh" +#include "dev/disk_image.hh" +#include "dev/ide_atareg.h" +#include "dev/ide_ctrl.hh" +#include "dev/ide_wdcreg.h" +#include "dev/io_device.hh" +#include "sim/eventq.hh" + +class ChunkGenerator; + +#define DMA_BACKOFF_PERIOD 200 + +#define MAX_DMA_SIZE (131072) // 128K +#define MAX_MULTSECT (128) + +#define PRD_BASE_MASK 0xfffffffe +#define PRD_COUNT_MASK 0xfffe +#define PRD_EOT_MASK 0x8000 + +typedef struct PrdEntry { + uint32_t baseAddr; + uint16_t byteCount; + uint16_t endOfTable; +} PrdEntry_t; + +class PrdTableEntry { + public: + PrdEntry_t entry; + + uint32_t getBaseAddr() + { + return (entry.baseAddr & PRD_BASE_MASK); + } + + uint32_t getByteCount() + { + return ((entry.byteCount == 0) ? MAX_DMA_SIZE : + (entry.byteCount & PRD_COUNT_MASK)); + } + + uint16_t getEOT() + { + return (entry.endOfTable & PRD_EOT_MASK); + } +}; + +#define DATA_OFFSET (0) +#define ERROR_OFFSET (1) +#define FEATURES_OFFSET (1) +#define NSECTOR_OFFSET (2) +#define SECTOR_OFFSET (3) +#define LCYL_OFFSET (4) +#define HCYL_OFFSET (5) +#define SELECT_OFFSET (6) +#define DRIVE_OFFSET (6) +#define STATUS_OFFSET (7) +#define COMMAND_OFFSET (7) + +#define CONTROL_OFFSET (2) +#define ALTSTAT_OFFSET (2) + +#define SELECT_DEV_BIT 0x10 +#define CONTROL_RST_BIT 0x04 +#define CONTROL_IEN_BIT 0x02 +#define STATUS_BSY_BIT 0x80 +#define STATUS_DRDY_BIT 0x40 +#define STATUS_DRQ_BIT 0x08 +#define STATUS_SEEK_BIT 0x10 +#define STATUS_DF_BIT 0x20 +#define DRIVE_LBA_BIT 0x40 + +#define DEV0 (0) +#define DEV1 (1) + +typedef struct CommandReg { + uint16_t data; + uint8_t error; + uint8_t sec_count; + uint8_t sec_num; + uint8_t cyl_low; + uint8_t cyl_high; + union { + uint8_t drive; + uint8_t head; + }; + uint8_t command; +} CommandReg_t; + +typedef enum Events { + None = 0, + Transfer, + ReadWait, + WriteWait, + PrdRead, + DmaRead, + DmaWrite +} Events_t; + +typedef enum DevAction { + ACT_NONE = 0, + ACT_CMD_WRITE, + ACT_CMD_COMPLETE, + ACT_CMD_ERROR, + ACT_SELECT_WRITE, + ACT_STAT_READ, + ACT_DATA_READY, + ACT_DATA_READ_BYTE, + ACT_DATA_READ_SHORT, + ACT_DATA_WRITE_BYTE, + ACT_DATA_WRITE_SHORT, + ACT_DMA_READY, + ACT_DMA_DONE, + ACT_SRST_SET, + ACT_SRST_CLEAR +} DevAction_t; + +typedef enum DevState { + // Device idle + Device_Idle_S = 0, + Device_Idle_SI, + Device_Idle_NS, + + // Software reset + Device_Srst, + + // Non-data commands + Command_Execution, + + // PIO data-in (data to host) + Prepare_Data_In, + Data_Ready_INTRQ_In, + Transfer_Data_In, + + // PIO data-out (data from host) + Prepare_Data_Out, + Data_Ready_INTRQ_Out, + Transfer_Data_Out, + + // DMA protocol + Prepare_Data_Dma, + Transfer_Data_Dma +} DevState_t; + +typedef enum DmaState { + Dma_Idle = 0, + Dma_Start, + Dma_Transfer +} DmaState_t; + +class PhysicalMemory; +class IdeController; + +/** + * IDE Disk device model + */ +class IdeDisk : public SimObject +{ + protected: + /** The IDE controller for this disk. */ + IdeController *ctrl; + /** The image that contains the data of this disk. */ + DiskImage *image; + + protected: + /** The disk delay in microseconds. */ + int diskDelay; + + private: + /** Drive identification structure for this disk */ + struct ataparams driveID; + /** Data buffer for transfers */ + uint8_t *dataBuffer; + /** Number of bytes in command data transfer */ + uint32_t cmdBytes; + /** Number of bytes left in command data transfer */ + uint32_t cmdBytesLeft; + /** Number of bytes left in DRQ block */ + uint32_t drqBytesLeft; + /** Current sector in access */ + uint32_t curSector; + /** Command block registers */ + CommandReg_t cmdReg; + /** Status register */ + uint8_t status; + /** Interrupt enable bit */ + bool nIENBit; + /** Device state */ + DevState_t devState; + /** Dma state */ + DmaState_t dmaState; + /** Dma transaction is a read */ + bool dmaRead; + /** PRD table base address */ + uint32_t curPrdAddr; + /** PRD entry */ + PrdTableEntry curPrd; + /** Device ID (master=0/slave=1) */ + int devID; + /** Interrupt pending */ + bool intrPending; + + Stats::Scalar<> dmaReadFullPages; + Stats::Scalar<> dmaReadBytes; + Stats::Scalar<> dmaReadTxs; + Stats::Scalar<> dmaWriteFullPages; + Stats::Scalar<> dmaWriteBytes; + Stats::Scalar<> dmaWriteTxs; + Stats::Formula rdBandwidth; + Stats::Formula wrBandwidth; + Stats::Formula totBandwidth; + Stats::Formula totBytes; + + public: + /** + * Create and initialize this Disk. + * @param name The name of this disk. + * @param img The disk image of this disk. + * @param id The disk ID (master=0/slave=1) + * @param disk_delay The disk delay in milliseconds + */ + IdeDisk(const std::string &name, DiskImage *img, int id, Tick disk_delay); + + /** + * Delete the data buffer. + */ + ~IdeDisk(); + + /** + * Reset the device state + */ + void reset(int id); + + /** + * Register Statistics + */ + void regStats(); + + /** + * Set the controller for this device + * @param c The IDE controller + */ + void setController(IdeController *c) { + if (ctrl) panic("Cannot change the controller once set!\n"); + ctrl = c; + } + + // Device register read/write + void read(const Addr &offset, IdeRegType regtype, uint8_t *data); + void write(const Addr &offset, IdeRegType regtype, const uint8_t *data); + + // Start/abort functions + void startDma(const uint32_t &prdTableBase); + void abortDma(); + + private: + void startCommand(); + + // Interrupt management + void intrPost(); + void intrClear(); + + // DMA stuff + void doDmaTransfer(); + friend class EventWrapper<IdeDisk, &IdeDisk::doDmaTransfer>; + EventWrapper<IdeDisk, &IdeDisk::doDmaTransfer> dmaTransferEvent; + + void doDmaDataRead(); + + void doDmaRead(); + ChunkGenerator *dmaReadCG; + friend class EventWrapper<IdeDisk, &IdeDisk::doDmaRead>; + EventWrapper<IdeDisk, &IdeDisk::doDmaRead> dmaReadWaitEvent; + + void doDmaDataWrite(); + + void doDmaWrite(); + ChunkGenerator *dmaWriteCG; + friend class EventWrapper<IdeDisk, &IdeDisk::doDmaWrite>; + EventWrapper<IdeDisk, &IdeDisk::doDmaWrite> dmaWriteWaitEvent; + + void dmaPrdReadDone(); + friend class EventWrapper<IdeDisk, &IdeDisk::dmaPrdReadDone>; + EventWrapper<IdeDisk, &IdeDisk::dmaPrdReadDone> dmaPrdReadEvent; + + void dmaReadDone(); + friend class EventWrapper<IdeDisk, &IdeDisk::dmaReadDone>; + EventWrapper<IdeDisk, &IdeDisk::dmaReadDone> dmaReadEvent; + + void dmaWriteDone(); + friend class EventWrapper<IdeDisk, &IdeDisk::dmaWriteDone>; + EventWrapper<IdeDisk, &IdeDisk::dmaWriteDone> dmaWriteEvent; + + // Disk image read/write + void readDisk(uint32_t sector, uint8_t *data); + void writeDisk(uint32_t sector, uint8_t *data); + + // State machine management + void updateState(DevAction_t action); + + // Utility functions + bool isBSYSet() { return (status & STATUS_BSY_BIT); } + bool isIENSet() { return nIENBit; } + bool isDEVSelect(); + + void setComplete() + { + // clear out the status byte + status = 0; + // set the DRDY bit + status |= STATUS_DRDY_BIT; + // set the SEEK bit + status |= STATUS_SEEK_BIT; + } + + uint32_t getLBABase() + { + return (Addr)(((cmdReg.head & 0xf) << 24) | (cmdReg.cyl_high << 16) | + (cmdReg.cyl_low << 8) | (cmdReg.sec_num)); + } + + inline Addr pciToDma(Addr pciAddr); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint to use. + * @param section The section name describing this object. + */ + void unserialize(Checkpoint *cp, const std::string §ion); +}; + + +#endif // __IDE_DISK_HH__ diff --git a/src/dev/ide_wdcreg.h b/src/dev/ide_wdcreg.h new file mode 100644 index 000000000..ed7475ec8 --- /dev/null +++ b/src/dev/ide_wdcreg.h @@ -0,0 +1,197 @@ +/* $OpenBSD: wdcreg.h,v 1.13 2004/09/24 07:05:44 grange Exp $ */ +/* $NetBSD: wdcreg.h,v 1.22 1999/03/07 14:02:54 bouyer Exp $ */ + +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)wdreg.h 7.1 (Berkeley) 5/9/91 + */ + +#ifndef _DEV_IC_WDCREG_H_ +#define _DEV_IC_WDCREG_H_ + +/* + * Controller register (wdr_ctlr) + */ +#define WDCTL_4BIT 0x08 /* use four head bits (wd1003) */ +#define WDCTL_RST 0x04 /* reset the controller */ +#define WDCTL_IDS 0x02 /* disable controller interrupts */ + +/* + * Status bits. + */ +#define WDCS_BSY 0x80 /* busy */ +#define WDCS_DRDY 0x40 /* drive ready */ +#define WDCS_DWF 0x20 /* drive write fault */ +#define WDCS_DSC 0x10 /* drive seek complete */ +#define WDCS_DRQ 0x08 /* data request */ +#define WDCS_CORR 0x04 /* corrected data */ +#define WDCS_IDX 0x02 /* index */ +#define WDCS_ERR 0x01 /* error */ +#define WDCS_BITS "\020\010BSY\007DRDY\006DWF\005DSC\004DRQ\003CORR\002IDX\001ERR" + +/* + * Error bits. + */ +#define WDCE_BBK 0x80 /* bad block detected */ +#define WDCE_CRC 0x80 /* CRC error (Ultra-DMA only) */ +#define WDCE_UNC 0x40 /* uncorrectable data error */ +#define WDCE_MC 0x20 /* media changed */ +#define WDCE_IDNF 0x10 /* id not found */ +#define WDCE_MCR 0x08 /* media change requested */ +#define WDCE_ABRT 0x04 /* aborted command */ +#define WDCE_TK0NF 0x02 /* track 0 not found */ +#define WDCE_AMNF 0x01 /* address mark not found */ + +/* + * Commands for Disk Controller. + */ +#define WDCC_NOP 0x00 /* NOP - Always fail with "aborted command" */ +#define WDCC_RECAL 0x10 /* disk restore code -- resets cntlr */ + +#define WDCC_READ 0x20 /* disk read code */ +#define WDCC_WRITE 0x30 /* disk write code */ +#define WDCC__LONG 0x02 /* modifier -- access ecc bytes */ +#define WDCC__NORETRY 0x01 /* modifier -- no retrys */ + +#define WDCC_FORMAT 0x50 /* disk format code */ +#define WDCC_DIAGNOSE 0x90 /* controller diagnostic */ +#define WDCC_IDP 0x91 /* initialize drive parameters */ + +#define WDCC_READMULTI 0xc4 /* read multiple */ +#define WDCC_WRITEMULTI 0xc5 /* write multiple */ +#define WDCC_SETMULTI 0xc6 /* set multiple mode */ + +#define WDCC_READDMA 0xc8 /* read with DMA */ +#define WDCC_WRITEDMA 0xca /* write with DMA */ + +#define WDCC_ACKMC 0xdb /* acknowledge media change */ +#define WDCC_LOCK 0xde /* lock drawer */ +#define WDCC_UNLOCK 0xdf /* unlock drawer */ + +#define WDCC_FLUSHCACHE 0xe7 /* Flush cache */ +#define WDCC_IDENTIFY 0xec /* read parameters from controller */ +#define SET_FEATURES 0xef /* set features */ + +#define WDCC_IDLE 0xe3 /* set idle timer & enter idle mode */ +#define WDCC_IDLE_IMMED 0xe1 /* enter idle mode */ +#define WDCC_SLEEP 0xe6 /* enter sleep mode */ +#define WDCC_STANDBY 0xe2 /* set standby timer & enter standby mode */ +#define WDCC_STANDBY_IMMED 0xe0 /* enter standby mode */ +#define WDCC_CHECK_PWR 0xe5 /* check power mode */ + +#define WDCC_READ_EXT 0x24 /* read 48-bit addressing */ +#define WDCC_WRITE_EXT 0x34 /* write 48-bit addressing */ + +#define WDCC_READMULTI_EXT 0x29 /* read multiple 48-bit addressing */ +#define WDCC_WRITEMULTI_EXT 0x39 /* write multiple 48-bit addressing */ + +#define WDCC_READDMA_EXT 0x25 /* read 48-bit addressing with DMA */ +#define WDCC_WRITEDMA_EXT 0x35 /* write 48-bit addressing with DMA */ + +#define WDCC_FLUSHCACHE_EXT 0xea /* 48-bit addressing flush cache */ + +/* Subcommands for SET_FEATURES (features register ) */ +#define WDSF_8BIT_PIO_EN 0x01 /* Enable 8bit PIO (CFA featureset) */ +#define WDSF_EN_WR_CACHE 0x02 +#define WDSF_SET_MODE 0x03 +#define WDSF_REASSIGN_EN 0x04 /* Obsolete in ATA-6 */ +#define WDSF_APM_EN 0x05 /* Enable Adv. Power Management */ +#define WDSF_PUIS_EN 0x06 /* Enable Power-Up In Standby */ +#define WDSF_PUIS_SPINUP 0x07 /* Power-Up In Standby spin-up */ +#define WDSF_CFA_MODE1_EN 0x0A /* Enable CFA power mode 1 */ +#define WDSF_RMSN_DS 0x31 /* Disable Removable Media Status */ +#define WDSF_RETRY_DS 0x33 /* Obsolete in ATA-6 */ +#define WDSF_AAM_EN 0x42 /* Enable Autom. Acoustic Management */ +#define WDSF_SET_CACHE_SGMT 0x54 /* Obsolete in ATA-6 */ +#define WDSF_READAHEAD_DS 0x55 /* Disable read look-ahead */ +#define WDSF_RLSE_EN 0x5D /* Enable release interrupt */ +#define WDSF_SRV_EN 0x5E /* Enable SERVICE interrupt */ +#define WDSF_POD_DS 0x66 +#define WDSF_ECC_DS 0x77 +#define WDSF_8BIT_PIO_DS 0x81 /* Disable 8bit PIO (CFA featureset) */ +#define WDSF_WRITE_CACHE_DS 0x82 +#define WDSF_REASSIGN_DS 0x84 +#define WDSF_APM_DS 0x85 /* Disable Adv. Power Management */ +#define WDSF_PUIS_DS 0x86 /* Disable Power-Up In Standby */ +#define WDSF_ECC_EN 0x88 +#define WDSF_CFA_MODE1_DS 0x8A /* Disable CFA power mode 1 */ +#define WDSF_RMSN_EN 0x95 /* Enable Removable Media Status */ +#define WDSF_RETRY_EN 0x99 /* Obsolete in ATA-6 */ +#define WDSF_SET_CURRENT 0x9A /* Obsolete in ATA-6 */ +#define WDSF_READAHEAD_EN 0xAA +#define WDSF_PREFETCH_SET 0xAB /* Obsolete in ATA-6 */ +#define WDSF_AAM_DS 0xC2 /* Disable Autom. Acoustic Management */ +#define WDSF_POD_EN 0xCC +#define WDSF_RLSE_DS 0xDD /* Disable release interrupt */ +#define WDSF_SRV_DS 0xDE /* Disable SERVICE interrupt */ +#define WDSF_READ_NATIVE_MAX 0xF8 +#define WDSF_SEEK 0x70 +#define WDSF_VERIFY 0x40 + +/* parameters uploaded to device/heads register */ +#define WDSD_IBM 0xa0 /* forced to 512 byte sector, ecc */ +#define WDSD_CHS 0x00 /* cylinder/head/sector addressing */ +#define WDSD_LBA 0x40 /* logical block addressing */ + +/* Commands for ATAPI devices */ +#define ATAPI_CHECK_POWER_MODE 0xe5 +#define ATAPI_EXEC_DRIVE_DIAGS 0x90 +#define ATAPI_IDLE_IMMEDIATE 0xe1 +#define ATAPI_NOP 0x00 +#define ATAPI_PKT_CMD 0xa0 +#define ATAPI_IDENTIFY_DEVICE 0xa1 +#define ATAPI_SOFT_RESET 0x08 +#define ATAPI_DEVICE_RESET 0x08 /* ATA/ATAPI-5 name for soft reset */ +#define ATAPI_SLEEP 0xe6 +#define ATAPI_STANDBY_IMMEDIATE 0xe0 +#define ATAPI_SMART 0xB0 /* SMART operations */ +#define ATAPI_SETMAX 0xF9 /* Set Max Address */ +#define ATAPI_WRITEEXT 0x34 /* Write sectors Ext */ +#define ATAPI_SETMAXEXT 0x37 /* Set Max Address Ext */ +#define ATAPI_WRITEMULTIEXT 0x39 /* Write Multi Ext */ + +/* Bytes used by ATAPI_PACKET_COMMAND ( feature register) */ +#define ATAPI_PKT_CMD_FTRE_DMA 0x01 +#define ATAPI_PKT_CMD_FTRE_OVL 0x02 + +/* ireason */ +#define WDCI_CMD 0x01 /* command(1) or data(0) */ +#define WDCI_IN 0x02 /* transfer to(1) or from(0) the host */ +#define WDCI_RELEASE 0x04 /* bus released until completion */ + +#define PHASE_CMDOUT (WDCS_DRQ | WDCI_CMD) +#define PHASE_DATAIN (WDCS_DRQ | WDCI_IN) +#define PHASE_DATAOUT WDCS_DRQ +#define PHASE_COMPLETED (WDCI_IN | WDCI_CMD) +#define PHASE_ABORTED 0 + +#endif /* !_DEV_IC_WDCREG_H_ */ diff --git a/src/dev/io_device.cc b/src/dev/io_device.cc new file mode 100644 index 000000000..408d8de3e --- /dev/null +++ b/src/dev/io_device.cc @@ -0,0 +1,265 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Nathan Binkert + */ + +#include "base/chunk_generator.hh" +#include "base/trace.hh" +#include "dev/io_device.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + + +PioPort::PioPort(PioDevice *dev, System *s, std::string pname) + : SimpleTimingPort(dev->name() + pname), device(dev) +{ } + + +Tick +PioPort::recvAtomic(Packet *pkt) +{ + return pkt->isRead() ? device->read(pkt) : device->write(pkt); +} + +void +PioPort::getDeviceAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) +{ + snoop.clear(); + device->addressRanges(resp); +} + + +PioDevice::~PioDevice() +{ + if (pioPort) + delete pioPort; +} + +void +PioDevice::init() +{ + if (!pioPort) + panic("Pio port not connected to anything!"); + pioPort->sendStatusChange(Port::RangeChange); +} + + +unsigned int +PioDevice::drain(Event *de) +{ + unsigned int count; + count = pioPort->drain(de); + if (count) + changeState(Draining); + else + changeState(Drained); + return count; +} + +void +BasicPioDevice::addressRanges(AddrRangeList &range_list) +{ + assert(pioSize != 0); + range_list.clear(); + range_list.push_back(RangeSize(pioAddr, pioSize)); +} + + +DmaPort::DmaPort(DmaDevice *dev, System *s) + : Port(dev->name() + "-dmaport"), device(dev), sys(s), pendingCount(0), + actionInProgress(0), drainEvent(NULL) +{ } + +bool +DmaPort::recvTiming(Packet *pkt) +{ + + + if (pkt->result == Packet::Nacked) { + DPRINTF(DMA, "Received nacked Pkt %#x with State: %#x Addr: %#x\n", + pkt, pkt->senderState, pkt->getAddr()); + pkt->reinitNacked(); + sendDma(pkt, true); + } else if (pkt->senderState) { + DmaReqState *state; + DPRINTF(DMA, "Received response Pkt %#x with State: %#x Addr: %#x\n", + pkt, pkt->senderState, pkt->getAddr()); + state = dynamic_cast<DmaReqState*>(pkt->senderState); + pendingCount--; + + assert(pendingCount >= 0); + assert(state); + + state->numBytes += pkt->req->getSize(); + if (state->totBytes == state->numBytes) { + state->completionEvent->process(); + delete state; + } + delete pkt->req; + delete pkt; + + if (pendingCount == 0 && drainEvent) { + drainEvent->process(); + drainEvent = NULL; + } + } else { + panic("Got packet without sender state... huh?\n"); + } + + return true; +} + +DmaDevice::DmaDevice(Params *p) + : PioDevice(p), dmaPort(NULL) +{ } + + +unsigned int +DmaDevice::drain(Event *de) +{ + unsigned int count; + count = pioPort->drain(de) + dmaPort->drain(de); + if (count) + changeState(Draining); + else + changeState(Drained); + return count; +} + +unsigned int +DmaPort::drain(Event *de) +{ + if (pendingCount == 0) + return 0; + drainEvent = de; + return 1; +} + + +void +DmaPort::recvRetry() +{ + Packet* pkt = transmitList.front(); + bool result = true; + while (result && transmitList.size()) { + DPRINTF(DMA, "Retry on Packet %#x with senderState: %#x\n", + pkt, pkt->senderState); + result = sendTiming(pkt); + if (result) { + DPRINTF(DMA, "-- Done\n"); + transmitList.pop_front(); + } else { + DPRINTF(DMA, "-- Failed, queued\n"); + } + } +} + + +void +DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, + uint8_t *data) +{ + assert(event); + + assert(device->getState() == SimObject::Running); + + DmaReqState *reqState = new DmaReqState(event, this, size); + + for (ChunkGenerator gen(addr, size, peerBlockSize()); + !gen.done(); gen.next()) { + Request *req = new Request(gen.addr(), gen.size(), 0); + Packet *pkt = new Packet(req, cmd, Packet::Broadcast); + + // Increment the data pointer on a write + if (data) + pkt->dataStatic(data + gen.complete()); + + pkt->senderState = reqState; + + assert(pendingCount >= 0); + pendingCount++; + sendDma(pkt); + } + +} + + +void +DmaPort::sendDma(Packet *pkt, bool front) +{ + // some kind of selction between access methods + // more work is going to have to be done to make + // switching actually work + + System::MemoryMode state = sys->getMemoryMode(); + if (state == System::Timing) { + DPRINTF(DMA, "Attempting to send Packet %#x with addr: %#x\n", + pkt, pkt->getAddr()); + if (transmitList.size() || !sendTiming(pkt)) { + if (front) + transmitList.push_front(pkt); + else + transmitList.push_back(pkt); + DPRINTF(DMA, "-- Failed: queued\n"); + } else { + DPRINTF(DMA, "-- Done\n"); + } + } else if (state == System::Atomic) { + Tick lat; + lat = sendAtomic(pkt); + assert(pkt->senderState); + DmaReqState *state = dynamic_cast<DmaReqState*>(pkt->senderState); + assert(state); + + state->numBytes += pkt->req->getSize(); + if (state->totBytes == state->numBytes) { + state->completionEvent->schedule(curTick + lat); + delete state; + delete pkt->req; + } + pendingCount--; + assert(pendingCount >= 0); + delete pkt; + + if (pendingCount == 0 && drainEvent) { + drainEvent->process(); + drainEvent = NULL; + } + + } else + panic("Unknown memory command state."); +} + +DmaDevice::~DmaDevice() +{ + if (dmaPort) + delete dmaPort; +} + + diff --git a/src/dev/io_device.hh b/src/dev/io_device.hh new file mode 100644 index 000000000..df4f494cb --- /dev/null +++ b/src/dev/io_device.hh @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Nathan Binkert + */ + +#ifndef __DEV_IO_DEVICE_HH__ +#define __DEV_IO_DEVICE_HH__ + +#include "mem/mem_object.hh" +#include "mem/packet_impl.hh" +#include "sim/sim_object.hh" +#include "mem/tport.hh" + +class Event; +class Platform; +class PioDevice; +class DmaDevice; +class System; + +/** + * The PioPort class is a programmed i/o port that all devices that are + * sensitive to an address range use. The port takes all the memory + * access types and roles them into one read() and write() call that the device + * must respond to. The device must also provide the addressRanges() function + * with which it returns the address ranges it is interested in. + */ +class PioPort : public SimpleTimingPort +{ + protected: + /** The device that this port serves. */ + PioDevice *device; + + virtual Tick recvAtomic(Packet *pkt); + + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop); + + public: + + PioPort(PioDevice *dev, System *s, std::string pname = "-pioport"); +}; + + +class DmaPort : public Port +{ + protected: + struct DmaReqState : public Packet::SenderState + { + /** Event to call on the device when this transaction (all packets) + * complete. */ + Event *completionEvent; + + /** Where we came from for some sanity checking. */ + Port *outPort; + + /** Total number of bytes that this transaction involves. */ + Addr totBytes; + + /** Number of bytes that have been acked for this transaction. */ + Addr numBytes; + + DmaReqState(Event *ce, Port *p, Addr tb) + : completionEvent(ce), outPort(p), totBytes(tb), numBytes(0) + {} + }; + + DmaDevice *device; + std::list<Packet*> transmitList; + + /** The system that device/port are in. This is used to select which mode + * we are currently operating in. */ + System *sys; + + /** Number of outstanding packets the dma port has. */ + int pendingCount; + + /** If a dmaAction is in progress. */ + int actionInProgress; + + /** If we need to drain, keep the drain event around until we're done + * here.*/ + Event *drainEvent; + + virtual bool recvTiming(Packet *pkt); + virtual Tick recvAtomic(Packet *pkt) + { panic("dma port shouldn't be used for pio access."); } + virtual void recvFunctional(Packet *pkt) + { panic("dma port shouldn't be used for pio access."); } + + virtual void recvStatusChange(Status status) + { ; } + + virtual void recvRetry() ; + + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { resp.clear(); snoop.clear(); } + + void sendDma(Packet *pkt, bool front = false); + + public: + DmaPort(DmaDevice *dev, System *s); + + void dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, + uint8_t *data = NULL); + + bool dmaPending() { return pendingCount > 0; } + + unsigned int drain(Event *de); +}; + +/** + * This device is the base class which all devices senstive to an address range + * inherit from. There are three pure virtual functions which all devices must + * implement addressRanges(), read(), and write(). The magic do choose which + * mode we are in, etc is handled by the PioPort so the device doesn't have to + * bother. + */ +class PioDevice : public MemObject +{ + protected: + + /** The platform we are in. This is used to decide what type of memory + * transaction we should perform. */ + Platform *platform; + + System *sys; + + /** The pioPort that handles the requests for us and provides us requests + * that it sees. */ + PioPort *pioPort; + + virtual void addressRanges(AddrRangeList &range_list) = 0; + + /** Pure virtual function that the device must implement. Called + * when a read command is recieved by the port. + * @param pkt Packet describing this request + * @return number of ticks it took to complete + */ + virtual Tick read(Packet *pkt) = 0; + + /** Pure virtual function that the device must implement. Called when a + * write command is recieved by the port. + * @param pkt Packet describing this request + * @return number of ticks it took to complete + */ + virtual Tick write(Packet *pkt) = 0; + + public: + /** Params struct which is extended through each device based on + * the parameters it needs. Since we are re-writing everything, we + * might as well start from the bottom this time. */ + struct Params + { + std::string name; + Platform *platform; + System *system; + }; + + protected: + Params *_params; + + public: + const Params *params() const { return _params; } + + PioDevice(Params *p) + : MemObject(p->name), platform(p->platform), sys(p->system), + pioPort(NULL), _params(p) + {} + + virtual ~PioDevice(); + + virtual void init(); + + virtual unsigned int drain(Event *de); + + virtual Port *getPort(const std::string &if_name, int idx = -1) + { + if (if_name == "pio") { + if (pioPort != NULL) + panic("pio port already connected to."); + pioPort = new PioPort(this, sys); + return pioPort; + } else + return NULL; + } + friend class PioPort; + +}; + +class BasicPioDevice : public PioDevice +{ + public: + struct Params : public PioDevice::Params + { + Addr pio_addr; + Tick pio_delay; + }; + + protected: + /** Address that the device listens to. */ + Addr pioAddr; + + /** Size that the device's address range. */ + Addr pioSize; + + /** Delay that the device experinces on an access. */ + Tick pioDelay; + + public: + BasicPioDevice(Params *p) + : PioDevice(p), pioAddr(p->pio_addr), pioSize(0), + pioDelay(p->pio_delay) + {} + + /** return the address ranges that this device responds to. + * @param range_list range list to populate with ranges + */ + void addressRanges(AddrRangeList &range_list); + +}; + +class DmaDevice : public PioDevice +{ + protected: + DmaPort *dmaPort; + + public: + DmaDevice(Params *p); + virtual ~DmaDevice(); + + void dmaWrite(Addr addr, int size, Event *event, uint8_t *data) + { dmaPort->dmaAction(Packet::WriteReq, addr, size, event, data) ; } + + void dmaRead(Addr addr, int size, Event *event, uint8_t *data = NULL) + { dmaPort->dmaAction(Packet::ReadReq, addr, size, event, data); } + + bool dmaPending() { return dmaPort->dmaPending(); } + + virtual unsigned int drain(Event *de); + + virtual Port *getPort(const std::string &if_name, int idx = -1) + { + if (if_name == "pio") { + if (pioPort != NULL) + panic("pio port already connected to."); + pioPort = new PioPort(this, sys); + return pioPort; + } else if (if_name == "dma") { + if (dmaPort != NULL) + panic("dma port already connected to."); + dmaPort = new DmaPort(this, sys); + return dmaPort; + } else + return NULL; + } + + friend class DmaPort; +}; + + +#endif // __DEV_IO_DEVICE_HH__ diff --git a/src/dev/isa_fake.cc b/src/dev/isa_fake.cc new file mode 100644 index 000000000..9622f015c --- /dev/null +++ b/src/dev/isa_fake.cc @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Miguel Serrano + * Ali Saidi + */ + +/** @file + * Isa Fake Device implementation + */ + +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "dev/isa_fake.hh" +#include "mem/packet.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; + +IsaFake::IsaFake(Params *p) + : BasicPioDevice(p) +{ + pioSize = p->pio_size; +} + +Tick +IsaFake::read(Packet *pkt) +{ + assert(pkt->result == Packet::Unknown); + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + + DPRINTF(Tsunami, "read va=%#x size=%d\n", pkt->getAddr(), pkt->getSize()); + + switch (pkt->getSize()) { + pkt->set(0xFFFFFFFFFFFFFFFFULL); + break; + case sizeof(uint32_t): + pkt->set((uint32_t)0xFFFFFFFF); + break; + case sizeof(uint16_t): + pkt->set((uint16_t)0xFFFF); + break; + case sizeof(uint8_t): + pkt->set((uint8_t)0xFF); + break; + default: + panic("invalid access size(?) for PCI configspace!\n"); + } + pkt->result = Packet::Success; + return pioDelay; +} + +Tick +IsaFake::write(Packet *pkt) +{ + DPRINTF(Tsunami, "write - va=%#x size=%d \n", pkt->getAddr(), pkt->getSize()); + pkt->result = Packet::Success; + return pioDelay; +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(IsaFake) + + Param<Addr> pio_addr; + Param<Tick> pio_latency; + Param<Addr> pio_size; + SimObjectParam<Platform *> platform; + SimObjectParam<System *> system; + +END_DECLARE_SIM_OBJECT_PARAMS(IsaFake) + +BEGIN_INIT_SIM_OBJECT_PARAMS(IsaFake) + + INIT_PARAM(pio_addr, "Device Address"), + INIT_PARAM(pio_latency, "Programmed IO latency"), + INIT_PARAM(pio_size, "Size of address range"), + INIT_PARAM(platform, "platform"), + INIT_PARAM(system, "system object") + +END_INIT_SIM_OBJECT_PARAMS(IsaFake) + +CREATE_SIM_OBJECT(IsaFake) +{ + IsaFake::Params *p = new IsaFake::Params; + p->name = getInstanceName(); + p->pio_addr = pio_addr; + p->pio_delay = pio_latency; + p->pio_size = pio_size; + p->platform = platform; + p->system = system; + return new IsaFake(p); +} + +REGISTER_SIM_OBJECT("IsaFake", IsaFake) diff --git a/src/dev/isa_fake.hh b/src/dev/isa_fake.hh new file mode 100644 index 000000000..5166882f8 --- /dev/null +++ b/src/dev/isa_fake.hh @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Miguel Serrano + * Ali Saidi + */ + +/** @file + * Declaration of a fake device. + */ + +#ifndef __ISA_FAKE_HH__ +#define __ISA_FAKE_HH__ + +#include "dev/tsunami.hh" +#include "base/range.hh" +#include "dev/io_device.hh" + +/** + * IsaFake is a device that returns -1 on all reads and + * accepts all writes. It is meant to be placed at an address range + * so that an mcheck doesn't occur when an os probes a piece of hw + * that doesn't exist (e.g. UARTs1-3). + */ +class IsaFake : public BasicPioDevice +{ + public: + struct Params : public BasicPioDevice::Params + { + Addr pio_size; + }; + protected: + const Params *params() const { return (const Params*)_params; } + + public: + /** + * The constructor for Tsunmami Fake just registers itself with the MMU. + * @param p params structure + */ + IsaFake(Params *p); + + /** + * This read always returns -1. + * @param pkt The memory request. + * @param data Where to put the data. + */ + virtual Tick read(Packet *pkt); + + /** + * All writes are simply ignored. + * @param pkt The memory request. + * @param data the data to not write. + */ + virtual Tick write(Packet *pkt); +}; + +#endif // __TSUNAMI_FAKE_HH__ diff --git a/src/dev/ns_gige.cc b/src/dev/ns_gige.cc new file mode 100644 index 000000000..704afcf7d --- /dev/null +++ b/src/dev/ns_gige.cc @@ -0,0 +1,2917 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu + */ + +/** @file + * Device module for modelling the National Semiconductor + * DP83820 ethernet controller. Does not support priority queueing + */ +#include <deque> +#include <string> + +#include "arch/alpha/ev5.hh" +#include "base/inet.hh" +#include "cpu/thread_context.hh" +#include "dev/etherlink.hh" +#include "dev/ns_gige.hh" +#include "dev/pciconfigall.hh" +#include "mem/packet.hh" +#include "sim/builder.hh" +#include "sim/debug.hh" +#include "sim/host.hh" +#include "sim/stats.hh" +#include "sim/system.hh" + +const char *NsRxStateStrings[] = +{ + "rxIdle", + "rxDescRefr", + "rxDescRead", + "rxFifoBlock", + "rxFragWrite", + "rxDescWrite", + "rxAdvance" +}; + +const char *NsTxStateStrings[] = +{ + "txIdle", + "txDescRefr", + "txDescRead", + "txFifoBlock", + "txFragRead", + "txDescWrite", + "txAdvance" +}; + +const char *NsDmaState[] = +{ + "dmaIdle", + "dmaReading", + "dmaWriting", + "dmaReadWaiting", + "dmaWriteWaiting" +}; + +using namespace std; +using namespace Net; +using namespace TheISA; + +/////////////////////////////////////////////////////////////////////// +// +// NSGigE PCI Device +// +NSGigE::NSGigE(Params *p) + : PciDev(p), ioEnable(false), + txFifo(p->tx_fifo_size), rxFifo(p->rx_fifo_size), + txPacket(0), rxPacket(0), txPacketBufPtr(NULL), rxPacketBufPtr(NULL), + txXferLen(0), rxXferLen(0), clock(p->clock), + txState(txIdle), txEnable(false), CTDD(false), + txFragPtr(0), txDescCnt(0), txDmaState(dmaIdle), rxState(rxIdle), + rxEnable(false), CRDD(false), rxPktBytes(0), + rxFragPtr(0), rxDescCnt(0), rxDmaState(dmaIdle), extstsEnable(false), + eepromState(eepromStart), rxDmaReadEvent(this), rxDmaWriteEvent(this), + txDmaReadEvent(this), txDmaWriteEvent(this), + dmaDescFree(p->dma_desc_free), dmaDataFree(p->dma_data_free), + txDelay(p->tx_delay), rxDelay(p->rx_delay), + rxKickTick(0), rxKickEvent(this), txKickTick(0), txKickEvent(this), + txEvent(this), rxFilterEnable(p->rx_filter), acceptBroadcast(false), + acceptMulticast(false), acceptUnicast(false), + acceptPerfect(false), acceptArp(false), multicastHashEnable(false), + intrTick(0), cpuPendingIntr(false), + intrEvent(0), interface(0) +{ + + intrDelay = p->intr_delay; + dmaReadDelay = p->dma_read_delay; + dmaWriteDelay = p->dma_write_delay; + dmaReadFactor = p->dma_read_factor; + dmaWriteFactor = p->dma_write_factor; + + regsReset(); + memcpy(&rom.perfectMatch, p->eaddr.bytes(), ETH_ADDR_LEN); + + memset(&rxDesc32, 0, sizeof(rxDesc32)); + memset(&txDesc32, 0, sizeof(txDesc32)); + memset(&rxDesc64, 0, sizeof(rxDesc64)); + memset(&txDesc64, 0, sizeof(txDesc64)); +} + +NSGigE::~NSGigE() +{} + +void +NSGigE::regStats() +{ + txBytes + .name(name() + ".txBytes") + .desc("Bytes Transmitted") + .prereq(txBytes) + ; + + rxBytes + .name(name() + ".rxBytes") + .desc("Bytes Received") + .prereq(rxBytes) + ; + + txPackets + .name(name() + ".txPackets") + .desc("Number of Packets Transmitted") + .prereq(txBytes) + ; + + rxPackets + .name(name() + ".rxPackets") + .desc("Number of Packets Received") + .prereq(rxBytes) + ; + + txIpChecksums + .name(name() + ".txIpChecksums") + .desc("Number of tx IP Checksums done by device") + .precision(0) + .prereq(txBytes) + ; + + rxIpChecksums + .name(name() + ".rxIpChecksums") + .desc("Number of rx IP Checksums done by device") + .precision(0) + .prereq(rxBytes) + ; + + txTcpChecksums + .name(name() + ".txTcpChecksums") + .desc("Number of tx TCP Checksums done by device") + .precision(0) + .prereq(txBytes) + ; + + rxTcpChecksums + .name(name() + ".rxTcpChecksums") + .desc("Number of rx TCP Checksums done by device") + .precision(0) + .prereq(rxBytes) + ; + + txUdpChecksums + .name(name() + ".txUdpChecksums") + .desc("Number of tx UDP Checksums done by device") + .precision(0) + .prereq(txBytes) + ; + + rxUdpChecksums + .name(name() + ".rxUdpChecksums") + .desc("Number of rx UDP Checksums done by device") + .precision(0) + .prereq(rxBytes) + ; + + descDmaReads + .name(name() + ".descDMAReads") + .desc("Number of descriptors the device read w/ DMA") + .precision(0) + ; + + descDmaWrites + .name(name() + ".descDMAWrites") + .desc("Number of descriptors the device wrote w/ DMA") + .precision(0) + ; + + descDmaRdBytes + .name(name() + ".descDmaReadBytes") + .desc("number of descriptor bytes read w/ DMA") + .precision(0) + ; + + descDmaWrBytes + .name(name() + ".descDmaWriteBytes") + .desc("number of descriptor bytes write w/ DMA") + .precision(0) + ; + + txBandwidth + .name(name() + ".txBandwidth") + .desc("Transmit Bandwidth (bits/s)") + .precision(0) + .prereq(txBytes) + ; + + rxBandwidth + .name(name() + ".rxBandwidth") + .desc("Receive Bandwidth (bits/s)") + .precision(0) + .prereq(rxBytes) + ; + + totBandwidth + .name(name() + ".totBandwidth") + .desc("Total Bandwidth (bits/s)") + .precision(0) + .prereq(totBytes) + ; + + totPackets + .name(name() + ".totPackets") + .desc("Total Packets") + .precision(0) + .prereq(totBytes) + ; + + totBytes + .name(name() + ".totBytes") + .desc("Total Bytes") + .precision(0) + .prereq(totBytes) + ; + + totPacketRate + .name(name() + ".totPPS") + .desc("Total Tranmission Rate (packets/s)") + .precision(0) + .prereq(totBytes) + ; + + txPacketRate + .name(name() + ".txPPS") + .desc("Packet Tranmission Rate (packets/s)") + .precision(0) + .prereq(txBytes) + ; + + rxPacketRate + .name(name() + ".rxPPS") + .desc("Packet Reception Rate (packets/s)") + .precision(0) + .prereq(rxBytes) + ; + + postedSwi + .name(name() + ".postedSwi") + .desc("number of software interrupts posted to CPU") + .precision(0) + ; + + totalSwi + .name(name() + ".totalSwi") + .desc("total number of Swi written to ISR") + .precision(0) + ; + + coalescedSwi + .name(name() + ".coalescedSwi") + .desc("average number of Swi's coalesced into each post") + .precision(0) + ; + + postedRxIdle + .name(name() + ".postedRxIdle") + .desc("number of rxIdle interrupts posted to CPU") + .precision(0) + ; + + totalRxIdle + .name(name() + ".totalRxIdle") + .desc("total number of RxIdle written to ISR") + .precision(0) + ; + + coalescedRxIdle + .name(name() + ".coalescedRxIdle") + .desc("average number of RxIdle's coalesced into each post") + .precision(0) + ; + + postedRxOk + .name(name() + ".postedRxOk") + .desc("number of RxOk interrupts posted to CPU") + .precision(0) + ; + + totalRxOk + .name(name() + ".totalRxOk") + .desc("total number of RxOk written to ISR") + .precision(0) + ; + + coalescedRxOk + .name(name() + ".coalescedRxOk") + .desc("average number of RxOk's coalesced into each post") + .precision(0) + ; + + postedRxDesc + .name(name() + ".postedRxDesc") + .desc("number of RxDesc interrupts posted to CPU") + .precision(0) + ; + + totalRxDesc + .name(name() + ".totalRxDesc") + .desc("total number of RxDesc written to ISR") + .precision(0) + ; + + coalescedRxDesc + .name(name() + ".coalescedRxDesc") + .desc("average number of RxDesc's coalesced into each post") + .precision(0) + ; + + postedTxOk + .name(name() + ".postedTxOk") + .desc("number of TxOk interrupts posted to CPU") + .precision(0) + ; + + totalTxOk + .name(name() + ".totalTxOk") + .desc("total number of TxOk written to ISR") + .precision(0) + ; + + coalescedTxOk + .name(name() + ".coalescedTxOk") + .desc("average number of TxOk's coalesced into each post") + .precision(0) + ; + + postedTxIdle + .name(name() + ".postedTxIdle") + .desc("number of TxIdle interrupts posted to CPU") + .precision(0) + ; + + totalTxIdle + .name(name() + ".totalTxIdle") + .desc("total number of TxIdle written to ISR") + .precision(0) + ; + + coalescedTxIdle + .name(name() + ".coalescedTxIdle") + .desc("average number of TxIdle's coalesced into each post") + .precision(0) + ; + + postedTxDesc + .name(name() + ".postedTxDesc") + .desc("number of TxDesc interrupts posted to CPU") + .precision(0) + ; + + totalTxDesc + .name(name() + ".totalTxDesc") + .desc("total number of TxDesc written to ISR") + .precision(0) + ; + + coalescedTxDesc + .name(name() + ".coalescedTxDesc") + .desc("average number of TxDesc's coalesced into each post") + .precision(0) + ; + + postedRxOrn + .name(name() + ".postedRxOrn") + .desc("number of RxOrn posted to CPU") + .precision(0) + ; + + totalRxOrn + .name(name() + ".totalRxOrn") + .desc("total number of RxOrn written to ISR") + .precision(0) + ; + + coalescedRxOrn + .name(name() + ".coalescedRxOrn") + .desc("average number of RxOrn's coalesced into each post") + .precision(0) + ; + + coalescedTotal + .name(name() + ".coalescedTotal") + .desc("average number of interrupts coalesced into each post") + .precision(0) + ; + + postedInterrupts + .name(name() + ".postedInterrupts") + .desc("number of posts to CPU") + .precision(0) + ; + + droppedPackets + .name(name() + ".droppedPackets") + .desc("number of packets dropped") + .precision(0) + ; + + coalescedSwi = totalSwi / postedInterrupts; + coalescedRxIdle = totalRxIdle / postedInterrupts; + coalescedRxOk = totalRxOk / postedInterrupts; + coalescedRxDesc = totalRxDesc / postedInterrupts; + coalescedTxOk = totalTxOk / postedInterrupts; + coalescedTxIdle = totalTxIdle / postedInterrupts; + coalescedTxDesc = totalTxDesc / postedInterrupts; + coalescedRxOrn = totalRxOrn / postedInterrupts; + + coalescedTotal = (totalSwi + totalRxIdle + totalRxOk + totalRxDesc + + totalTxOk + totalTxIdle + totalTxDesc + + totalRxOrn) / postedInterrupts; + + txBandwidth = txBytes * Stats::constant(8) / simSeconds; + rxBandwidth = rxBytes * Stats::constant(8) / simSeconds; + totBandwidth = txBandwidth + rxBandwidth; + totBytes = txBytes + rxBytes; + totPackets = txPackets + rxPackets; + + txPacketRate = txPackets / simSeconds; + rxPacketRate = rxPackets / simSeconds; +} + + +/** + * This is to write to the PCI general configuration registers + */ +Tick +NSGigE::writeConfig(Packet *pkt) +{ + int offset = pkt->getAddr() & PCI_CONFIG_SIZE; + if (offset < PCI_DEVICE_SPECIFIC) + PciDev::writeConfig(pkt); + else + panic("Device specific PCI config space not implemented!\n"); + + switch (offset) { + // seems to work fine without all these PCI settings, but i + // put in the IO to double check, an assertion will fail if we + // need to properly implement it + case PCI_COMMAND: + if (config.data[offset] & PCI_CMD_IOSE) + ioEnable = true; + else + ioEnable = false; + break; + } + pkt->result = Packet::Success; + return configDelay; +} + +/** + * This reads the device registers, which are detailed in the NS83820 + * spec sheet + */ +Tick +NSGigE::read(Packet *pkt) +{ + assert(ioEnable); + + pkt->allocate(); + + //The mask is to give you only the offset into the device register file + Addr daddr = pkt->getAddr() & 0xfff; + DPRINTF(EthernetPIO, "read da=%#x pa=%#x size=%d\n", + daddr, pkt->getAddr(), pkt->getSize()); + + + // there are some reserved registers, you can see ns_gige_reg.h and + // the spec sheet for details + if (daddr > LAST && daddr <= RESERVED) { + panic("Accessing reserved register"); + } else if (daddr > RESERVED && daddr <= 0x3FC) { + return readConfig(pkt); + } else if (daddr >= MIB_START && daddr <= MIB_END) { + // don't implement all the MIB's. hopefully the kernel + // doesn't actually DEPEND upon their values + // MIB are just hardware stats keepers + pkt->set<uint32_t>(0); + pkt->result = Packet::Success; + return pioDelay; + } else if (daddr > 0x3FC) + panic("Something is messed up!\n"); + + assert(pkt->getSize() == sizeof(uint32_t)); + uint32_t ® = *pkt->getPtr<uint32_t>(); + uint16_t rfaddr; + + switch (daddr) { + case CR: + reg = regs.command; + //these are supposed to be cleared on a read + reg &= ~(CR_RXD | CR_TXD | CR_TXR | CR_RXR); + break; + + case CFGR: + reg = regs.config; + break; + + case MEAR: + reg = regs.mear; + break; + + case PTSCR: + reg = regs.ptscr; + break; + + case ISR: + reg = regs.isr; + devIntrClear(ISR_ALL); + break; + + case IMR: + reg = regs.imr; + break; + + case IER: + reg = regs.ier; + break; + + case IHR: + reg = regs.ihr; + break; + + case TXDP: + reg = regs.txdp; + break; + + case TXDP_HI: + reg = regs.txdp_hi; + break; + + case TX_CFG: + reg = regs.txcfg; + break; + + case GPIOR: + reg = regs.gpior; + break; + + case RXDP: + reg = regs.rxdp; + break; + + case RXDP_HI: + reg = regs.rxdp_hi; + break; + + case RX_CFG: + reg = regs.rxcfg; + break; + + case PQCR: + reg = regs.pqcr; + break; + + case WCSR: + reg = regs.wcsr; + break; + + case PCR: + reg = regs.pcr; + break; + + // see the spec sheet for how RFCR and RFDR work + // basically, you write to RFCR to tell the machine + // what you want to do next, then you act upon RFDR, + // and the device will be prepared b/c of what you + // wrote to RFCR + case RFCR: + reg = regs.rfcr; + break; + + case RFDR: + rfaddr = (uint16_t)(regs.rfcr & RFCR_RFADDR); + switch (rfaddr) { + // Read from perfect match ROM octets + case 0x000: + reg = rom.perfectMatch[1]; + reg = reg << 8; + reg += rom.perfectMatch[0]; + break; + case 0x002: + reg = rom.perfectMatch[3] << 8; + reg += rom.perfectMatch[2]; + break; + case 0x004: + reg = rom.perfectMatch[5] << 8; + reg += rom.perfectMatch[4]; + break; + default: + // Read filter hash table + if (rfaddr >= FHASH_ADDR && + rfaddr < FHASH_ADDR + FHASH_SIZE) { + + // Only word-aligned reads supported + if (rfaddr % 2) + panic("unaligned read from filter hash table!"); + + reg = rom.filterHash[rfaddr - FHASH_ADDR + 1] << 8; + reg += rom.filterHash[rfaddr - FHASH_ADDR]; + break; + } + + panic("reading RFDR for something other than pattern" + " matching or hashing! %#x\n", rfaddr); + } + break; + + case SRR: + reg = regs.srr; + break; + + case MIBC: + reg = regs.mibc; + reg &= ~(MIBC_MIBS | MIBC_ACLR); + break; + + case VRCR: + reg = regs.vrcr; + break; + + case VTCR: + reg = regs.vtcr; + break; + + case VDR: + reg = regs.vdr; + break; + + case CCSR: + reg = regs.ccsr; + break; + + case TBICR: + reg = regs.tbicr; + break; + + case TBISR: + reg = regs.tbisr; + break; + + case TANAR: + reg = regs.tanar; + break; + + case TANLPAR: + reg = regs.tanlpar; + break; + + case TANER: + reg = regs.taner; + break; + + case TESR: + reg = regs.tesr; + break; + + case M5REG: + reg = 0; + if (params()->rx_thread) + reg |= M5REG_RX_THREAD; + if (params()->tx_thread) + reg |= M5REG_TX_THREAD; + if (params()->rss) + reg |= M5REG_RSS; + break; + + default: + panic("reading unimplemented register: addr=%#x", daddr); + } + + DPRINTF(EthernetPIO, "read from %#x: data=%d data=%#x\n", + daddr, reg, reg); + + pkt->result = Packet::Success; + return pioDelay; +} + +Tick +NSGigE::write(Packet *pkt) +{ + assert(ioEnable); + + Addr daddr = pkt->getAddr() & 0xfff; + DPRINTF(EthernetPIO, "write da=%#x pa=%#x size=%d\n", + daddr, pkt->getAddr(), pkt->getSize()); + + if (daddr > LAST && daddr <= RESERVED) { + panic("Accessing reserved register"); + } else if (daddr > RESERVED && daddr <= 0x3FC) { + return writeConfig(pkt); + } else if (daddr > 0x3FC) + panic("Something is messed up!\n"); + + if (pkt->getSize() == sizeof(uint32_t)) { + uint32_t reg = pkt->get<uint32_t>(); + uint16_t rfaddr; + + DPRINTF(EthernetPIO, "write data=%d data=%#x\n", reg, reg); + + switch (daddr) { + case CR: + regs.command = reg; + if (reg & CR_TXD) { + txEnable = false; + } else if (reg & CR_TXE) { + txEnable = true; + + // the kernel is enabling the transmit machine + if (txState == txIdle) + txKick(); + } + + if (reg & CR_RXD) { + rxEnable = false; + } else if (reg & CR_RXE) { + rxEnable = true; + + if (rxState == rxIdle) + rxKick(); + } + + if (reg & CR_TXR) + txReset(); + + if (reg & CR_RXR) + rxReset(); + + if (reg & CR_SWI) + devIntrPost(ISR_SWI); + + if (reg & CR_RST) { + txReset(); + rxReset(); + + regsReset(); + } + break; + + case CFGR: + if (reg & CFGR_LNKSTS || + reg & CFGR_SPDSTS || + reg & CFGR_DUPSTS || + reg & CFGR_RESERVED || + reg & CFGR_T64ADDR || + reg & CFGR_PCI64_DET) + + // First clear all writable bits + regs.config &= CFGR_LNKSTS | CFGR_SPDSTS | CFGR_DUPSTS | + CFGR_RESERVED | CFGR_T64ADDR | + CFGR_PCI64_DET; + // Now set the appropriate writable bits + regs.config |= reg & ~(CFGR_LNKSTS | CFGR_SPDSTS | CFGR_DUPSTS | + CFGR_RESERVED | CFGR_T64ADDR | + CFGR_PCI64_DET); + +// all these #if 0's are because i don't THINK the kernel needs to +// have these implemented. if there is a problem relating to one of +// these, you may need to add functionality in. + if (reg & CFGR_TBI_EN) ; + if (reg & CFGR_MODE_1000) ; + + if (reg & CFGR_AUTO_1000) + panic("CFGR_AUTO_1000 not implemented!\n"); + + if (reg & CFGR_PINT_DUPSTS || + reg & CFGR_PINT_LNKSTS || + reg & CFGR_PINT_SPDSTS) + ; + + if (reg & CFGR_TMRTEST) ; + if (reg & CFGR_MRM_DIS) ; + if (reg & CFGR_MWI_DIS) ; + + if (reg & CFGR_T64ADDR) ; + // panic("CFGR_T64ADDR is read only register!\n"); + + if (reg & CFGR_PCI64_DET) + panic("CFGR_PCI64_DET is read only register!\n"); + + if (reg & CFGR_DATA64_EN) ; + if (reg & CFGR_M64ADDR) ; + if (reg & CFGR_PHY_RST) ; + if (reg & CFGR_PHY_DIS) ; + + if (reg & CFGR_EXTSTS_EN) + extstsEnable = true; + else + extstsEnable = false; + + if (reg & CFGR_REQALG) ; + if (reg & CFGR_SB) ; + if (reg & CFGR_POW) ; + if (reg & CFGR_EXD) ; + if (reg & CFGR_PESEL) ; + if (reg & CFGR_BROM_DIS) ; + if (reg & CFGR_EXT_125) ; + if (reg & CFGR_BEM) ; + break; + + case MEAR: + // Clear writable bits + regs.mear &= MEAR_EEDO; + // Set appropriate writable bits + regs.mear |= reg & ~MEAR_EEDO; + + // FreeBSD uses the EEPROM to read PMATCH (for the MAC address) + // even though it could get it through RFDR + if (reg & MEAR_EESEL) { + // Rising edge of clock + if (reg & MEAR_EECLK && !eepromClk) + eepromKick(); + } + else { + eepromState = eepromStart; + regs.mear &= ~MEAR_EEDI; + } + + eepromClk = reg & MEAR_EECLK; + + // since phy is completely faked, MEAR_MD* don't matter + if (reg & MEAR_MDIO) ; + if (reg & MEAR_MDDIR) ; + if (reg & MEAR_MDC) ; + break; + + case PTSCR: + regs.ptscr = reg & ~(PTSCR_RBIST_RDONLY); + // these control BISTs for various parts of chip - we + // don't care or do just fake that the BIST is done + if (reg & PTSCR_RBIST_EN) + regs.ptscr |= PTSCR_RBIST_DONE; + if (reg & PTSCR_EEBIST_EN) + regs.ptscr &= ~PTSCR_EEBIST_EN; + if (reg & PTSCR_EELOAD_EN) + regs.ptscr &= ~PTSCR_EELOAD_EN; + break; + + case ISR: /* writing to the ISR has no effect */ + panic("ISR is a read only register!\n"); + + case IMR: + regs.imr = reg; + devIntrChangeMask(); + break; + + case IER: + regs.ier = reg; + break; + + case IHR: + regs.ihr = reg; + /* not going to implement real interrupt holdoff */ + break; + + case TXDP: + regs.txdp = (reg & 0xFFFFFFFC); + assert(txState == txIdle); + CTDD = false; + break; + + case TXDP_HI: + regs.txdp_hi = reg; + break; + + case TX_CFG: + regs.txcfg = reg; +#if 0 + if (reg & TX_CFG_CSI) ; + if (reg & TX_CFG_HBI) ; + if (reg & TX_CFG_MLB) ; + if (reg & TX_CFG_ATP) ; + if (reg & TX_CFG_ECRETRY) { + /* + * this could easily be implemented, but considering + * the network is just a fake pipe, wouldn't make + * sense to do this + */ + } + + if (reg & TX_CFG_BRST_DIS) ; +#endif + +#if 0 + /* we handle our own DMA, ignore the kernel's exhortations */ + if (reg & TX_CFG_MXDMA) ; +#endif + + // also, we currently don't care about fill/drain + // thresholds though this may change in the future with + // more realistic networks or a driver which changes it + // according to feedback + + break; + + case GPIOR: + // Only write writable bits + regs.gpior &= GPIOR_UNUSED | GPIOR_GP5_IN | GPIOR_GP4_IN + | GPIOR_GP3_IN | GPIOR_GP2_IN | GPIOR_GP1_IN; + regs.gpior |= reg & ~(GPIOR_UNUSED | GPIOR_GP5_IN | GPIOR_GP4_IN + | GPIOR_GP3_IN | GPIOR_GP2_IN | GPIOR_GP1_IN); + /* these just control general purpose i/o pins, don't matter */ + break; + + case RXDP: + regs.rxdp = reg; + CRDD = false; + break; + + case RXDP_HI: + regs.rxdp_hi = reg; + break; + + case RX_CFG: + regs.rxcfg = reg; +#if 0 + if (reg & RX_CFG_AEP) ; + if (reg & RX_CFG_ARP) ; + if (reg & RX_CFG_STRIPCRC) ; + if (reg & RX_CFG_RX_RD) ; + if (reg & RX_CFG_ALP) ; + if (reg & RX_CFG_AIRL) ; + + /* we handle our own DMA, ignore what kernel says about it */ + if (reg & RX_CFG_MXDMA) ; + + //also, we currently don't care about fill/drain thresholds + //though this may change in the future with more realistic + //networks or a driver which changes it according to feedback + if (reg & (RX_CFG_DRTH | RX_CFG_DRTH0)) ; +#endif + break; + + case PQCR: + /* there is no priority queueing used in the linux 2.6 driver */ + regs.pqcr = reg; + break; + + case WCSR: + /* not going to implement wake on LAN */ + regs.wcsr = reg; + break; + + case PCR: + /* not going to implement pause control */ + regs.pcr = reg; + break; + + case RFCR: + regs.rfcr = reg; + + rxFilterEnable = (reg & RFCR_RFEN) ? true : false; + acceptBroadcast = (reg & RFCR_AAB) ? true : false; + acceptMulticast = (reg & RFCR_AAM) ? true : false; + acceptUnicast = (reg & RFCR_AAU) ? true : false; + acceptPerfect = (reg & RFCR_APM) ? true : false; + acceptArp = (reg & RFCR_AARP) ? true : false; + multicastHashEnable = (reg & RFCR_MHEN) ? true : false; + +#if 0 + if (reg & RFCR_APAT) + panic("RFCR_APAT not implemented!\n"); +#endif + if (reg & RFCR_UHEN) + panic("Unicast hash filtering not used by drivers!\n"); + + if (reg & RFCR_ULM) + panic("RFCR_ULM not implemented!\n"); + + break; + + case RFDR: + rfaddr = (uint16_t)(regs.rfcr & RFCR_RFADDR); + switch (rfaddr) { + case 0x000: + rom.perfectMatch[0] = (uint8_t)reg; + rom.perfectMatch[1] = (uint8_t)(reg >> 8); + break; + case 0x002: + rom.perfectMatch[2] = (uint8_t)reg; + rom.perfectMatch[3] = (uint8_t)(reg >> 8); + break; + case 0x004: + rom.perfectMatch[4] = (uint8_t)reg; + rom.perfectMatch[5] = (uint8_t)(reg >> 8); + break; + default: + + if (rfaddr >= FHASH_ADDR && + rfaddr < FHASH_ADDR + FHASH_SIZE) { + + // Only word-aligned writes supported + if (rfaddr % 2) + panic("unaligned write to filter hash table!"); + + rom.filterHash[rfaddr - FHASH_ADDR] = (uint8_t)reg; + rom.filterHash[rfaddr - FHASH_ADDR + 1] + = (uint8_t)(reg >> 8); + break; + } + panic("writing RFDR for something other than pattern matching\ + or hashing! %#x\n", rfaddr); + } + + case BRAR: + regs.brar = reg; + break; + + case BRDR: + panic("the driver never uses BRDR, something is wrong!\n"); + + case SRR: + panic("SRR is read only register!\n"); + + case MIBC: + panic("the driver never uses MIBC, something is wrong!\n"); + + case VRCR: + regs.vrcr = reg; + break; + + case VTCR: + regs.vtcr = reg; + break; + + case VDR: + panic("the driver never uses VDR, something is wrong!\n"); + + case CCSR: + /* not going to implement clockrun stuff */ + regs.ccsr = reg; + break; + + case TBICR: + regs.tbicr = reg; + if (reg & TBICR_MR_LOOPBACK) + panic("TBICR_MR_LOOPBACK never used, something wrong!\n"); + + if (reg & TBICR_MR_AN_ENABLE) { + regs.tanlpar = regs.tanar; + regs.tbisr |= (TBISR_MR_AN_COMPLETE | TBISR_MR_LINK_STATUS); + } + +#if 0 + if (reg & TBICR_MR_RESTART_AN) ; +#endif + + break; + + case TBISR: + panic("TBISR is read only register!\n"); + + case TANAR: + // Only write the writable bits + regs.tanar &= TANAR_RF1 | TANAR_RF2 | TANAR_UNUSED; + regs.tanar |= reg & ~(TANAR_RF1 | TANAR_RF2 | TANAR_UNUSED); + + // Pause capability unimplemented +#if 0 + if (reg & TANAR_PS2) ; + if (reg & TANAR_PS1) ; +#endif + + break; + + case TANLPAR: + panic("this should only be written to by the fake phy!\n"); + + case TANER: + panic("TANER is read only register!\n"); + + case TESR: + regs.tesr = reg; + break; + + default: + panic("invalid register access daddr=%#x", daddr); + } + } else { + panic("Invalid Request Size"); + } + pkt->result = Packet::Success; + return pioDelay; +} + +void +NSGigE::devIntrPost(uint32_t interrupts) +{ + if (interrupts & ISR_RESERVE) + panic("Cannot set a reserved interrupt"); + + if (interrupts & ISR_NOIMPL) + warn("interrupt not implemented %#x\n", interrupts); + + interrupts &= ISR_IMPL; + regs.isr |= interrupts; + + if (interrupts & regs.imr) { + if (interrupts & ISR_SWI) { + totalSwi++; + } + if (interrupts & ISR_RXIDLE) { + totalRxIdle++; + } + if (interrupts & ISR_RXOK) { + totalRxOk++; + } + if (interrupts & ISR_RXDESC) { + totalRxDesc++; + } + if (interrupts & ISR_TXOK) { + totalTxOk++; + } + if (interrupts & ISR_TXIDLE) { + totalTxIdle++; + } + if (interrupts & ISR_TXDESC) { + totalTxDesc++; + } + if (interrupts & ISR_RXORN) { + totalRxOrn++; + } + } + + DPRINTF(EthernetIntr, + "interrupt written to ISR: intr=%#x isr=%#x imr=%#x\n", + interrupts, regs.isr, regs.imr); + + if ((regs.isr & regs.imr)) { + Tick when = curTick; + if ((regs.isr & regs.imr & ISR_NODELAY) == 0) + when += intrDelay; + cpuIntrPost(when); + } +} + +/* writing this interrupt counting stats inside this means that this function + is now limited to being used to clear all interrupts upon the kernel + reading isr and servicing. just telling you in case you were thinking + of expanding use. +*/ +void +NSGigE::devIntrClear(uint32_t interrupts) +{ + if (interrupts & ISR_RESERVE) + panic("Cannot clear a reserved interrupt"); + + if (regs.isr & regs.imr & ISR_SWI) { + postedSwi++; + } + if (regs.isr & regs.imr & ISR_RXIDLE) { + postedRxIdle++; + } + if (regs.isr & regs.imr & ISR_RXOK) { + postedRxOk++; + } + if (regs.isr & regs.imr & ISR_RXDESC) { + postedRxDesc++; + } + if (regs.isr & regs.imr & ISR_TXOK) { + postedTxOk++; + } + if (regs.isr & regs.imr & ISR_TXIDLE) { + postedTxIdle++; + } + if (regs.isr & regs.imr & ISR_TXDESC) { + postedTxDesc++; + } + if (regs.isr & regs.imr & ISR_RXORN) { + postedRxOrn++; + } + + if (regs.isr & regs.imr & ISR_IMPL) + postedInterrupts++; + + interrupts &= ~ISR_NOIMPL; + regs.isr &= ~interrupts; + + DPRINTF(EthernetIntr, + "interrupt cleared from ISR: intr=%x isr=%x imr=%x\n", + interrupts, regs.isr, regs.imr); + + if (!(regs.isr & regs.imr)) + cpuIntrClear(); +} + +void +NSGigE::devIntrChangeMask() +{ + DPRINTF(EthernetIntr, "interrupt mask changed: isr=%x imr=%x masked=%x\n", + regs.isr, regs.imr, regs.isr & regs.imr); + + if (regs.isr & regs.imr) + cpuIntrPost(curTick); + else + cpuIntrClear(); +} + +void +NSGigE::cpuIntrPost(Tick when) +{ + // If the interrupt you want to post is later than an interrupt + // already scheduled, just let it post in the coming one and don't + // schedule another. + // HOWEVER, must be sure that the scheduled intrTick is in the + // future (this was formerly the source of a bug) + /** + * @todo this warning should be removed and the intrTick code should + * be fixed. + */ + assert(when >= curTick); + assert(intrTick >= curTick || intrTick == 0); + if (when > intrTick && intrTick != 0) { + DPRINTF(EthernetIntr, "don't need to schedule event...intrTick=%d\n", + intrTick); + return; + } + + intrTick = when; + if (intrTick < curTick) { + debug_break(); + intrTick = curTick; + } + + DPRINTF(EthernetIntr, "going to schedule an interrupt for intrTick=%d\n", + intrTick); + + if (intrEvent) + intrEvent->squash(); + intrEvent = new IntrEvent(this, true); + intrEvent->schedule(intrTick); +} + +void +NSGigE::cpuInterrupt() +{ + assert(intrTick == curTick); + + // Whether or not there's a pending interrupt, we don't care about + // it anymore + intrEvent = 0; + intrTick = 0; + + // Don't send an interrupt if there's already one + if (cpuPendingIntr) { + DPRINTF(EthernetIntr, + "would send an interrupt now, but there's already pending\n"); + } else { + // Send interrupt + cpuPendingIntr = true; + + DPRINTF(EthernetIntr, "posting interrupt\n"); + intrPost(); + } +} + +void +NSGigE::cpuIntrClear() +{ + if (!cpuPendingIntr) + return; + + if (intrEvent) { + intrEvent->squash(); + intrEvent = 0; + } + + intrTick = 0; + + cpuPendingIntr = false; + + DPRINTF(EthernetIntr, "clearing interrupt\n"); + intrClear(); +} + +bool +NSGigE::cpuIntrPending() const +{ return cpuPendingIntr; } + +void +NSGigE::txReset() +{ + + DPRINTF(Ethernet, "transmit reset\n"); + + CTDD = false; + txEnable = false;; + txFragPtr = 0; + assert(txDescCnt == 0); + txFifo.clear(); + txState = txIdle; + assert(txDmaState == dmaIdle); +} + +void +NSGigE::rxReset() +{ + DPRINTF(Ethernet, "receive reset\n"); + + CRDD = false; + assert(rxPktBytes == 0); + rxEnable = false; + rxFragPtr = 0; + assert(rxDescCnt == 0); + assert(rxDmaState == dmaIdle); + rxFifo.clear(); + rxState = rxIdle; +} + +void +NSGigE::regsReset() +{ + memset(®s, 0, sizeof(regs)); + regs.config = (CFGR_LNKSTS | CFGR_TBI_EN | CFGR_MODE_1000); + regs.mear = 0x12; + regs.txcfg = 0x120; // set drain threshold to 1024 bytes and + // fill threshold to 32 bytes + regs.rxcfg = 0x4; // set drain threshold to 16 bytes + regs.srr = 0x0103; // set the silicon revision to rev B or 0x103 + regs.mibc = MIBC_FRZ; + regs.vdr = 0x81; // set the vlan tag type to 802.1q + regs.tesr = 0xc000; // TBI capable of both full and half duplex + regs.brar = 0xffffffff; + + extstsEnable = false; + acceptBroadcast = false; + acceptMulticast = false; + acceptUnicast = false; + acceptPerfect = false; + acceptArp = false; +} + +bool +NSGigE::doRxDmaRead() +{ + assert(rxDmaState == dmaIdle || rxDmaState == dmaReadWaiting); + rxDmaState = dmaReading; + + if (dmaPending() || getState() != Running) + rxDmaState = dmaReadWaiting; + else + dmaRead(rxDmaAddr, rxDmaLen, &rxDmaReadEvent, (uint8_t*)rxDmaData); + + return true; +} + +void +NSGigE::rxDmaReadDone() +{ + assert(rxDmaState == dmaReading); + rxDmaState = dmaIdle; + + DPRINTF(EthernetDMA, "rx dma read paddr=%#x len=%d\n", + rxDmaAddr, rxDmaLen); + DDUMP(EthernetDMA, rxDmaData, rxDmaLen); + + // If the transmit state machine has a pending DMA, let it go first + if (txDmaState == dmaReadWaiting || txDmaState == dmaWriteWaiting) + txKick(); + + rxKick(); +} + +bool +NSGigE::doRxDmaWrite() +{ + assert(rxDmaState == dmaIdle || rxDmaState == dmaWriteWaiting); + rxDmaState = dmaWriting; + + if (dmaPending() || getState() != Running) + rxDmaState = dmaWriteWaiting; + else + dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaWriteEvent, (uint8_t*)rxDmaData); + return true; +} + +void +NSGigE::rxDmaWriteDone() +{ + assert(rxDmaState == dmaWriting); + rxDmaState = dmaIdle; + + DPRINTF(EthernetDMA, "rx dma write paddr=%#x len=%d\n", + rxDmaAddr, rxDmaLen); + DDUMP(EthernetDMA, rxDmaData, rxDmaLen); + + // If the transmit state machine has a pending DMA, let it go first + if (txDmaState == dmaReadWaiting || txDmaState == dmaWriteWaiting) + txKick(); + + rxKick(); +} + +void +NSGigE::rxKick() +{ + bool is64bit = (bool)(regs.config & CFGR_M64ADDR); + + DPRINTF(EthernetSM, + "receive kick rxState=%s (rxBuf.size=%d) %d-bit\n", + NsRxStateStrings[rxState], rxFifo.size(), is64bit ? 64 : 32); + + Addr link, bufptr; + uint32_t &cmdsts = is64bit ? rxDesc64.cmdsts : rxDesc32.cmdsts; + uint32_t &extsts = is64bit ? rxDesc64.extsts : rxDesc32.extsts; + + next: + if (clock) { + if (rxKickTick > curTick) { + DPRINTF(EthernetSM, "receive kick exiting, can't run till %d\n", + rxKickTick); + + goto exit; + } + + // Go to the next state machine clock tick. + rxKickTick = curTick + cycles(1); + } + + switch(rxDmaState) { + case dmaReadWaiting: + if (doRxDmaRead()) + goto exit; + break; + case dmaWriteWaiting: + if (doRxDmaWrite()) + goto exit; + break; + default: + break; + } + + link = is64bit ? (Addr)rxDesc64.link : (Addr)rxDesc32.link; + bufptr = is64bit ? (Addr)rxDesc64.bufptr : (Addr)rxDesc32.bufptr; + + // see state machine from spec for details + // the way this works is, if you finish work on one state and can + // go directly to another, you do that through jumping to the + // label "next". however, if you have intermediate work, like DMA + // so that you can't go to the next state yet, you go to exit and + // exit the loop. however, when the DMA is done it will trigger + // an event and come back to this loop. + switch (rxState) { + case rxIdle: + if (!rxEnable) { + DPRINTF(EthernetSM, "Receive Disabled! Nothing to do.\n"); + goto exit; + } + + if (CRDD) { + rxState = rxDescRefr; + + rxDmaAddr = regs.rxdp & 0x3fffffff; + rxDmaData = + is64bit ? (void *)&rxDesc64.link : (void *)&rxDesc32.link; + rxDmaLen = is64bit ? sizeof(rxDesc64.link) : sizeof(rxDesc32.link); + rxDmaFree = dmaDescFree; + + descDmaReads++; + descDmaRdBytes += rxDmaLen; + + if (doRxDmaRead()) + goto exit; + } else { + rxState = rxDescRead; + + rxDmaAddr = regs.rxdp & 0x3fffffff; + rxDmaData = is64bit ? (void *)&rxDesc64 : (void *)&rxDesc32; + rxDmaLen = is64bit ? sizeof(rxDesc64) : sizeof(rxDesc32); + rxDmaFree = dmaDescFree; + + descDmaReads++; + descDmaRdBytes += rxDmaLen; + + if (doRxDmaRead()) + goto exit; + } + break; + + case rxDescRefr: + if (rxDmaState != dmaIdle) + goto exit; + + rxState = rxAdvance; + break; + + case rxDescRead: + if (rxDmaState != dmaIdle) + goto exit; + + DPRINTF(EthernetDesc, "rxDesc: addr=%08x read descriptor\n", + regs.rxdp & 0x3fffffff); + DPRINTF(EthernetDesc, + "rxDesc: link=%#x bufptr=%#x cmdsts=%08x extsts=%08x\n", + link, bufptr, cmdsts, extsts); + + if (cmdsts & CMDSTS_OWN) { + devIntrPost(ISR_RXIDLE); + rxState = rxIdle; + goto exit; + } else { + rxState = rxFifoBlock; + rxFragPtr = bufptr; + rxDescCnt = cmdsts & CMDSTS_LEN_MASK; + } + break; + + case rxFifoBlock: + if (!rxPacket) { + /** + * @todo in reality, we should be able to start processing + * the packet as it arrives, and not have to wait for the + * full packet ot be in the receive fifo. + */ + if (rxFifo.empty()) + goto exit; + + DPRINTF(EthernetSM, "****processing receive of new packet****\n"); + + // If we don't have a packet, grab a new one from the fifo. + rxPacket = rxFifo.front(); + rxPktBytes = rxPacket->length; + rxPacketBufPtr = rxPacket->data; + +#if TRACING_ON + if (DTRACE(Ethernet)) { + IpPtr ip(rxPacket); + if (ip) { + DPRINTF(Ethernet, "ID is %d\n", ip->id()); + TcpPtr tcp(ip); + if (tcp) { + DPRINTF(Ethernet, + "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n", + tcp->sport(), tcp->dport(), tcp->seq(), + tcp->ack()); + } + } + } +#endif + + // sanity check - i think the driver behaves like this + assert(rxDescCnt >= rxPktBytes); + rxFifo.pop(); + } + + + // dont' need the && rxDescCnt > 0 if driver sanity check + // above holds + if (rxPktBytes > 0) { + rxState = rxFragWrite; + // don't need min<>(rxPktBytes,rxDescCnt) if above sanity + // check holds + rxXferLen = rxPktBytes; + + rxDmaAddr = rxFragPtr & 0x3fffffff; + rxDmaData = rxPacketBufPtr; + rxDmaLen = rxXferLen; + rxDmaFree = dmaDataFree; + + if (doRxDmaWrite()) + goto exit; + + } else { + rxState = rxDescWrite; + + //if (rxPktBytes == 0) { /* packet is done */ + assert(rxPktBytes == 0); + DPRINTF(EthernetSM, "done with receiving packet\n"); + + cmdsts |= CMDSTS_OWN; + cmdsts &= ~CMDSTS_MORE; + cmdsts |= CMDSTS_OK; + cmdsts &= 0xffff0000; + cmdsts += rxPacket->length; //i.e. set CMDSTS_SIZE + +#if 0 + /* + * all the driver uses these are for its own stats keeping + * which we don't care about, aren't necessary for + * functionality and doing this would just slow us down. + * if they end up using this in a later version for + * functional purposes, just undef + */ + if (rxFilterEnable) { + cmdsts &= ~CMDSTS_DEST_MASK; + const EthAddr &dst = rxFifoFront()->dst(); + if (dst->unicast()) + cmdsts |= CMDSTS_DEST_SELF; + if (dst->multicast()) + cmdsts |= CMDSTS_DEST_MULTI; + if (dst->broadcast()) + cmdsts |= CMDSTS_DEST_MASK; + } +#endif + + IpPtr ip(rxPacket); + if (extstsEnable && ip) { + extsts |= EXTSTS_IPPKT; + rxIpChecksums++; + if (cksum(ip) != 0) { + DPRINTF(EthernetCksum, "Rx IP Checksum Error\n"); + extsts |= EXTSTS_IPERR; + } + TcpPtr tcp(ip); + UdpPtr udp(ip); + if (tcp) { + extsts |= EXTSTS_TCPPKT; + rxTcpChecksums++; + if (cksum(tcp) != 0) { + DPRINTF(EthernetCksum, "Rx TCP Checksum Error\n"); + extsts |= EXTSTS_TCPERR; + + } + } else if (udp) { + extsts |= EXTSTS_UDPPKT; + rxUdpChecksums++; + if (cksum(udp) != 0) { + DPRINTF(EthernetCksum, "Rx UDP Checksum Error\n"); + extsts |= EXTSTS_UDPERR; + } + } + } + rxPacket = 0; + + /* + * the driver seems to always receive into desc buffers + * of size 1514, so you never have a pkt that is split + * into multiple descriptors on the receive side, so + * i don't implement that case, hence the assert above. + */ + + DPRINTF(EthernetDesc, + "rxDesc: addr=%08x writeback cmdsts extsts\n", + regs.rxdp & 0x3fffffff); + DPRINTF(EthernetDesc, + "rxDesc: link=%#x bufptr=%#x cmdsts=%08x extsts=%08x\n", + link, bufptr, cmdsts, extsts); + + rxDmaAddr = regs.rxdp & 0x3fffffff; + rxDmaData = &cmdsts; + if (is64bit) { + rxDmaAddr += offsetof(ns_desc64, cmdsts); + rxDmaLen = sizeof(rxDesc64.cmdsts) + sizeof(rxDesc64.extsts); + } else { + rxDmaAddr += offsetof(ns_desc32, cmdsts); + rxDmaLen = sizeof(rxDesc32.cmdsts) + sizeof(rxDesc32.extsts); + } + rxDmaFree = dmaDescFree; + + descDmaWrites++; + descDmaWrBytes += rxDmaLen; + + if (doRxDmaWrite()) + goto exit; + } + break; + + case rxFragWrite: + if (rxDmaState != dmaIdle) + goto exit; + + rxPacketBufPtr += rxXferLen; + rxFragPtr += rxXferLen; + rxPktBytes -= rxXferLen; + + rxState = rxFifoBlock; + break; + + case rxDescWrite: + if (rxDmaState != dmaIdle) + goto exit; + + assert(cmdsts & CMDSTS_OWN); + + assert(rxPacket == 0); + devIntrPost(ISR_RXOK); + + if (cmdsts & CMDSTS_INTR) + devIntrPost(ISR_RXDESC); + + if (!rxEnable) { + DPRINTF(EthernetSM, "Halting the RX state machine\n"); + rxState = rxIdle; + goto exit; + } else + rxState = rxAdvance; + break; + + case rxAdvance: + if (link == 0) { + devIntrPost(ISR_RXIDLE); + rxState = rxIdle; + CRDD = true; + goto exit; + } else { + if (rxDmaState != dmaIdle) + goto exit; + rxState = rxDescRead; + regs.rxdp = link; + CRDD = false; + + rxDmaAddr = regs.rxdp & 0x3fffffff; + rxDmaData = is64bit ? (void *)&rxDesc64 : (void *)&rxDesc32; + rxDmaLen = is64bit ? sizeof(rxDesc64) : sizeof(rxDesc32); + rxDmaFree = dmaDescFree; + + if (doRxDmaRead()) + goto exit; + } + break; + + default: + panic("Invalid rxState!"); + } + + DPRINTF(EthernetSM, "entering next rxState=%s\n", + NsRxStateStrings[rxState]); + goto next; + + exit: + /** + * @todo do we want to schedule a future kick? + */ + DPRINTF(EthernetSM, "rx state machine exited rxState=%s\n", + NsRxStateStrings[rxState]); + + if (clock && !rxKickEvent.scheduled()) + rxKickEvent.schedule(rxKickTick); +} + +void +NSGigE::transmit() +{ + if (txFifo.empty()) { + DPRINTF(Ethernet, "nothing to transmit\n"); + return; + } + + DPRINTF(Ethernet, "Attempt Pkt Transmit: txFifo length=%d\n", + txFifo.size()); + if (interface->sendPacket(txFifo.front())) { +#if TRACING_ON + if (DTRACE(Ethernet)) { + IpPtr ip(txFifo.front()); + if (ip) { + DPRINTF(Ethernet, "ID is %d\n", ip->id()); + TcpPtr tcp(ip); + if (tcp) { + DPRINTF(Ethernet, + "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n", + tcp->sport(), tcp->dport(), tcp->seq(), + tcp->ack()); + } + } + } +#endif + + DDUMP(EthernetData, txFifo.front()->data, txFifo.front()->length); + txBytes += txFifo.front()->length; + txPackets++; + + DPRINTF(Ethernet, "Successful Xmit! now txFifoAvail is %d\n", + txFifo.avail()); + txFifo.pop(); + + /* + * normally do a writeback of the descriptor here, and ONLY + * after that is done, send this interrupt. but since our + * stuff never actually fails, just do this interrupt here, + * otherwise the code has to stray from this nice format. + * besides, it's functionally the same. + */ + devIntrPost(ISR_TXOK); + } + + if (!txFifo.empty() && !txEvent.scheduled()) { + DPRINTF(Ethernet, "reschedule transmit\n"); + txEvent.schedule(curTick + retryTime); + } +} + +bool +NSGigE::doTxDmaRead() +{ + assert(txDmaState == dmaIdle || txDmaState == dmaReadWaiting); + txDmaState = dmaReading; + + if (dmaPending() || getState() != Running) + txDmaState = dmaReadWaiting; + else + dmaRead(txDmaAddr, txDmaLen, &txDmaReadEvent, (uint8_t*)txDmaData); + + return true; +} + +void +NSGigE::txDmaReadDone() +{ + assert(txDmaState == dmaReading); + txDmaState = dmaIdle; + + DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n", + txDmaAddr, txDmaLen); + DDUMP(EthernetDMA, txDmaData, txDmaLen); + + // If the receive state machine has a pending DMA, let it go first + if (rxDmaState == dmaReadWaiting || rxDmaState == dmaWriteWaiting) + rxKick(); + + txKick(); +} + +bool +NSGigE::doTxDmaWrite() +{ + assert(txDmaState == dmaIdle || txDmaState == dmaWriteWaiting); + txDmaState = dmaWriting; + + if (dmaPending() || getState() != Running) + txDmaState = dmaWriteWaiting; + else + dmaWrite(txDmaAddr, txDmaLen, &txDmaWriteEvent, (uint8_t*)txDmaData); + return true; +} + +void +NSGigE::txDmaWriteDone() +{ + assert(txDmaState == dmaWriting); + txDmaState = dmaIdle; + + DPRINTF(EthernetDMA, "tx dma write paddr=%#x len=%d\n", + txDmaAddr, txDmaLen); + DDUMP(EthernetDMA, txDmaData, txDmaLen); + + // If the receive state machine has a pending DMA, let it go first + if (rxDmaState == dmaReadWaiting || rxDmaState == dmaWriteWaiting) + rxKick(); + + txKick(); +} + +void +NSGigE::txKick() +{ + bool is64bit = (bool)(regs.config & CFGR_M64ADDR); + + DPRINTF(EthernetSM, "transmit kick txState=%s %d-bit\n", + NsTxStateStrings[txState], is64bit ? 64 : 32); + + Addr link, bufptr; + uint32_t &cmdsts = is64bit ? txDesc64.cmdsts : txDesc32.cmdsts; + uint32_t &extsts = is64bit ? txDesc64.extsts : txDesc32.extsts; + + next: + if (clock) { + if (txKickTick > curTick) { + DPRINTF(EthernetSM, "transmit kick exiting, can't run till %d\n", + txKickTick); + goto exit; + } + + // Go to the next state machine clock tick. + txKickTick = curTick + cycles(1); + } + + switch(txDmaState) { + case dmaReadWaiting: + if (doTxDmaRead()) + goto exit; + break; + case dmaWriteWaiting: + if (doTxDmaWrite()) + goto exit; + break; + default: + break; + } + + link = is64bit ? (Addr)txDesc64.link : (Addr)txDesc32.link; + bufptr = is64bit ? (Addr)txDesc64.bufptr : (Addr)txDesc32.bufptr; + switch (txState) { + case txIdle: + if (!txEnable) { + DPRINTF(EthernetSM, "Transmit disabled. Nothing to do.\n"); + goto exit; + } + + if (CTDD) { + txState = txDescRefr; + + txDmaAddr = regs.txdp & 0x3fffffff; + txDmaData = + is64bit ? (void *)&txDesc64.link : (void *)&txDesc32.link; + txDmaLen = is64bit ? sizeof(txDesc64.link) : sizeof(txDesc32.link); + txDmaFree = dmaDescFree; + + descDmaReads++; + descDmaRdBytes += txDmaLen; + + if (doTxDmaRead()) + goto exit; + + } else { + txState = txDescRead; + + txDmaAddr = regs.txdp & 0x3fffffff; + txDmaData = is64bit ? (void *)&txDesc64 : (void *)&txDesc32; + txDmaLen = is64bit ? sizeof(txDesc64) : sizeof(txDesc32); + txDmaFree = dmaDescFree; + + descDmaReads++; + descDmaRdBytes += txDmaLen; + + if (doTxDmaRead()) + goto exit; + } + break; + + case txDescRefr: + if (txDmaState != dmaIdle) + goto exit; + + txState = txAdvance; + break; + + case txDescRead: + if (txDmaState != dmaIdle) + goto exit; + + DPRINTF(EthernetDesc, "txDesc: addr=%08x read descriptor\n", + regs.txdp & 0x3fffffff); + DPRINTF(EthernetDesc, + "txDesc: link=%#x bufptr=%#x cmdsts=%#08x extsts=%#08x\n", + link, bufptr, cmdsts, extsts); + + if (cmdsts & CMDSTS_OWN) { + txState = txFifoBlock; + txFragPtr = bufptr; + txDescCnt = cmdsts & CMDSTS_LEN_MASK; + } else { + devIntrPost(ISR_TXIDLE); + txState = txIdle; + goto exit; + } + break; + + case txFifoBlock: + if (!txPacket) { + DPRINTF(EthernetSM, "****starting the tx of a new packet****\n"); + txPacket = new EthPacketData(16384); + txPacketBufPtr = txPacket->data; + } + + if (txDescCnt == 0) { + DPRINTF(EthernetSM, "the txDescCnt == 0, done with descriptor\n"); + if (cmdsts & CMDSTS_MORE) { + DPRINTF(EthernetSM, "there are more descriptors to come\n"); + txState = txDescWrite; + + cmdsts &= ~CMDSTS_OWN; + + txDmaAddr = regs.txdp & 0x3fffffff; + txDmaData = &cmdsts; + if (is64bit) { + txDmaAddr += offsetof(ns_desc64, cmdsts); + txDmaLen = sizeof(txDesc64.cmdsts); + } else { + txDmaAddr += offsetof(ns_desc32, cmdsts); + txDmaLen = sizeof(txDesc32.cmdsts); + } + txDmaFree = dmaDescFree; + + if (doTxDmaWrite()) + goto exit; + + } else { /* this packet is totally done */ + DPRINTF(EthernetSM, "This packet is done, let's wrap it up\n"); + /* deal with the the packet that just finished */ + if ((regs.vtcr & VTCR_PPCHK) && extstsEnable) { + IpPtr ip(txPacket); + if (extsts & EXTSTS_UDPPKT) { + UdpPtr udp(ip); + udp->sum(0); + udp->sum(cksum(udp)); + txUdpChecksums++; + } else if (extsts & EXTSTS_TCPPKT) { + TcpPtr tcp(ip); + tcp->sum(0); + tcp->sum(cksum(tcp)); + txTcpChecksums++; + } + if (extsts & EXTSTS_IPPKT) { + ip->sum(0); + ip->sum(cksum(ip)); + txIpChecksums++; + } + } + + txPacket->length = txPacketBufPtr - txPacket->data; + // this is just because the receive can't handle a + // packet bigger want to make sure + if (txPacket->length > 1514) + panic("transmit packet too large, %s > 1514\n", + txPacket->length); + +#ifndef NDEBUG + bool success = +#endif + txFifo.push(txPacket); + assert(success); + + /* + * this following section is not tqo spec, but + * functionally shouldn't be any different. normally, + * the chip will wait til the transmit has occurred + * before writing back the descriptor because it has + * to wait to see that it was successfully transmitted + * to decide whether to set CMDSTS_OK or not. + * however, in the simulator since it is always + * successfully transmitted, and writing it exactly to + * spec would complicate the code, we just do it here + */ + + cmdsts &= ~CMDSTS_OWN; + cmdsts |= CMDSTS_OK; + + DPRINTF(EthernetDesc, + "txDesc writeback: cmdsts=%08x extsts=%08x\n", + cmdsts, extsts); + + txDmaFree = dmaDescFree; + txDmaAddr = regs.txdp & 0x3fffffff; + txDmaData = &cmdsts; + if (is64bit) { + txDmaAddr += offsetof(ns_desc64, cmdsts); + txDmaLen = + sizeof(txDesc64.cmdsts) + sizeof(txDesc64.extsts); + } else { + txDmaAddr += offsetof(ns_desc32, cmdsts); + txDmaLen = + sizeof(txDesc32.cmdsts) + sizeof(txDesc32.extsts); + } + + descDmaWrites++; + descDmaWrBytes += txDmaLen; + + transmit(); + txPacket = 0; + + if (!txEnable) { + DPRINTF(EthernetSM, "halting TX state machine\n"); + txState = txIdle; + goto exit; + } else + txState = txAdvance; + + if (doTxDmaWrite()) + goto exit; + } + } else { + DPRINTF(EthernetSM, "this descriptor isn't done yet\n"); + if (!txFifo.full()) { + txState = txFragRead; + + /* + * The number of bytes transferred is either whatever + * is left in the descriptor (txDescCnt), or if there + * is not enough room in the fifo, just whatever room + * is left in the fifo + */ + txXferLen = min<uint32_t>(txDescCnt, txFifo.avail()); + + txDmaAddr = txFragPtr & 0x3fffffff; + txDmaData = txPacketBufPtr; + txDmaLen = txXferLen; + txDmaFree = dmaDataFree; + + if (doTxDmaRead()) + goto exit; + } else { + txState = txFifoBlock; + transmit(); + + goto exit; + } + + } + break; + + case txFragRead: + if (txDmaState != dmaIdle) + goto exit; + + txPacketBufPtr += txXferLen; + txFragPtr += txXferLen; + txDescCnt -= txXferLen; + txFifo.reserve(txXferLen); + + txState = txFifoBlock; + break; + + case txDescWrite: + if (txDmaState != dmaIdle) + goto exit; + + if (cmdsts & CMDSTS_INTR) + devIntrPost(ISR_TXDESC); + + if (!txEnable) { + DPRINTF(EthernetSM, "halting TX state machine\n"); + txState = txIdle; + goto exit; + } else + txState = txAdvance; + break; + + case txAdvance: + if (link == 0) { + devIntrPost(ISR_TXIDLE); + txState = txIdle; + goto exit; + } else { + if (txDmaState != dmaIdle) + goto exit; + txState = txDescRead; + regs.txdp = link; + CTDD = false; + + txDmaAddr = link & 0x3fffffff; + txDmaData = is64bit ? (void *)&txDesc64 : (void *)&txDesc32; + txDmaLen = is64bit ? sizeof(txDesc64) : sizeof(txDesc32); + txDmaFree = dmaDescFree; + + if (doTxDmaRead()) + goto exit; + } + break; + + default: + panic("invalid state"); + } + + DPRINTF(EthernetSM, "entering next txState=%s\n", + NsTxStateStrings[txState]); + goto next; + + exit: + /** + * @todo do we want to schedule a future kick? + */ + DPRINTF(EthernetSM, "tx state machine exited txState=%s\n", + NsTxStateStrings[txState]); + + if (clock && !txKickEvent.scheduled()) + txKickEvent.schedule(txKickTick); +} + +/** + * Advance the EEPROM state machine + * Called on rising edge of EEPROM clock bit in MEAR + */ +void +NSGigE::eepromKick() +{ + switch (eepromState) { + + case eepromStart: + + // Wait for start bit + if (regs.mear & MEAR_EEDI) { + // Set up to get 2 opcode bits + eepromState = eepromGetOpcode; + eepromBitsToRx = 2; + eepromOpcode = 0; + } + break; + + case eepromGetOpcode: + eepromOpcode <<= 1; + eepromOpcode += (regs.mear & MEAR_EEDI) ? 1 : 0; + --eepromBitsToRx; + + // Done getting opcode + if (eepromBitsToRx == 0) { + if (eepromOpcode != EEPROM_READ) + panic("only EEPROM reads are implemented!"); + + // Set up to get address + eepromState = eepromGetAddress; + eepromBitsToRx = 6; + eepromAddress = 0; + } + break; + + case eepromGetAddress: + eepromAddress <<= 1; + eepromAddress += (regs.mear & MEAR_EEDI) ? 1 : 0; + --eepromBitsToRx; + + // Done getting address + if (eepromBitsToRx == 0) { + + if (eepromAddress >= EEPROM_SIZE) + panic("EEPROM read access out of range!"); + + switch (eepromAddress) { + + case EEPROM_PMATCH2_ADDR: + eepromData = rom.perfectMatch[5]; + eepromData <<= 8; + eepromData += rom.perfectMatch[4]; + break; + + case EEPROM_PMATCH1_ADDR: + eepromData = rom.perfectMatch[3]; + eepromData <<= 8; + eepromData += rom.perfectMatch[2]; + break; + + case EEPROM_PMATCH0_ADDR: + eepromData = rom.perfectMatch[1]; + eepromData <<= 8; + eepromData += rom.perfectMatch[0]; + break; + + default: + panic("FreeBSD driver only uses EEPROM to read PMATCH!"); + } + // Set up to read data + eepromState = eepromRead; + eepromBitsToRx = 16; + + // Clear data in bit + regs.mear &= ~MEAR_EEDI; + } + break; + + case eepromRead: + // Clear Data Out bit + regs.mear &= ~MEAR_EEDO; + // Set bit to value of current EEPROM bit + regs.mear |= (eepromData & 0x8000) ? MEAR_EEDO : 0x0; + + eepromData <<= 1; + --eepromBitsToRx; + + // All done + if (eepromBitsToRx == 0) { + eepromState = eepromStart; + } + break; + + default: + panic("invalid EEPROM state"); + } + +} + +void +NSGigE::transferDone() +{ + if (txFifo.empty()) { + DPRINTF(Ethernet, "transfer complete: txFifo empty...nothing to do\n"); + return; + } + + DPRINTF(Ethernet, "transfer complete: data in txFifo...schedule xmit\n"); + + if (txEvent.scheduled()) + txEvent.reschedule(curTick + cycles(1)); + else + txEvent.schedule(curTick + cycles(1)); +} + +bool +NSGigE::rxFilter(const EthPacketPtr &packet) +{ + EthPtr eth = packet; + bool drop = true; + string type; + + const EthAddr &dst = eth->dst(); + if (dst.unicast()) { + // If we're accepting all unicast addresses + if (acceptUnicast) + drop = false; + + // If we make a perfect match + if (acceptPerfect && dst == rom.perfectMatch) + drop = false; + + if (acceptArp && eth->type() == ETH_TYPE_ARP) + drop = false; + + } else if (dst.broadcast()) { + // if we're accepting broadcasts + if (acceptBroadcast) + drop = false; + + } else if (dst.multicast()) { + // if we're accepting all multicasts + if (acceptMulticast) + drop = false; + + // Multicast hashing faked - all packets accepted + if (multicastHashEnable) + drop = false; + } + + if (drop) { + DPRINTF(Ethernet, "rxFilter drop\n"); + DDUMP(EthernetData, packet->data, packet->length); + } + + return drop; +} + +bool +NSGigE::recvPacket(EthPacketPtr packet) +{ + rxBytes += packet->length; + rxPackets++; + + DPRINTF(Ethernet, "Receiving packet from wire, rxFifoAvail=%d\n", + rxFifo.avail()); + + if (!rxEnable) { + DPRINTF(Ethernet, "receive disabled...packet dropped\n"); + return true; + } + + if (!rxFilterEnable) { + DPRINTF(Ethernet, + "receive packet filtering disabled . . . packet dropped\n"); + return true; + } + + if (rxFilter(packet)) { + DPRINTF(Ethernet, "packet filtered...dropped\n"); + return true; + } + + if (rxFifo.avail() < packet->length) { +#if TRACING_ON + IpPtr ip(packet); + TcpPtr tcp(ip); + if (ip) { + DPRINTF(Ethernet, + "packet won't fit in receive buffer...pkt ID %d dropped\n", + ip->id()); + if (tcp) { + DPRINTF(Ethernet, "Seq=%d\n", tcp->seq()); + } + } +#endif + droppedPackets++; + devIntrPost(ISR_RXORN); + return false; + } + + rxFifo.push(packet); + + rxKick(); + return true; +} + + +void +NSGigE::resume() +{ + SimObject::resume(); + + // During drain we could have left the state machines in a waiting state and + // they wouldn't get out until some other event occured to kick them. + // This way they'll get out immediately + txKick(); + rxKick(); +} + + +//===================================================================== +// +// +void +NSGigE::serialize(ostream &os) +{ + // Serialize the PciDev base class + PciDev::serialize(os); + + /* + * Finalize any DMA events now. + */ + // @todo will mem system save pending dma? + + /* + * Serialize the device registers + */ + SERIALIZE_SCALAR(regs.command); + SERIALIZE_SCALAR(regs.config); + SERIALIZE_SCALAR(regs.mear); + SERIALIZE_SCALAR(regs.ptscr); + SERIALIZE_SCALAR(regs.isr); + SERIALIZE_SCALAR(regs.imr); + SERIALIZE_SCALAR(regs.ier); + SERIALIZE_SCALAR(regs.ihr); + SERIALIZE_SCALAR(regs.txdp); + SERIALIZE_SCALAR(regs.txdp_hi); + SERIALIZE_SCALAR(regs.txcfg); + SERIALIZE_SCALAR(regs.gpior); + SERIALIZE_SCALAR(regs.rxdp); + SERIALIZE_SCALAR(regs.rxdp_hi); + SERIALIZE_SCALAR(regs.rxcfg); + SERIALIZE_SCALAR(regs.pqcr); + SERIALIZE_SCALAR(regs.wcsr); + SERIALIZE_SCALAR(regs.pcr); + SERIALIZE_SCALAR(regs.rfcr); + SERIALIZE_SCALAR(regs.rfdr); + SERIALIZE_SCALAR(regs.brar); + SERIALIZE_SCALAR(regs.brdr); + SERIALIZE_SCALAR(regs.srr); + SERIALIZE_SCALAR(regs.mibc); + SERIALIZE_SCALAR(regs.vrcr); + SERIALIZE_SCALAR(regs.vtcr); + SERIALIZE_SCALAR(regs.vdr); + SERIALIZE_SCALAR(regs.ccsr); + SERIALIZE_SCALAR(regs.tbicr); + SERIALIZE_SCALAR(regs.tbisr); + SERIALIZE_SCALAR(regs.tanar); + SERIALIZE_SCALAR(regs.tanlpar); + SERIALIZE_SCALAR(regs.taner); + SERIALIZE_SCALAR(regs.tesr); + + SERIALIZE_ARRAY(rom.perfectMatch, ETH_ADDR_LEN); + SERIALIZE_ARRAY(rom.filterHash, FHASH_SIZE); + + SERIALIZE_SCALAR(ioEnable); + + /* + * Serialize the data Fifos + */ + rxFifo.serialize("rxFifo", os); + txFifo.serialize("txFifo", os); + + /* + * Serialize the various helper variables + */ + bool txPacketExists = txPacket; + SERIALIZE_SCALAR(txPacketExists); + if (txPacketExists) { + txPacket->length = txPacketBufPtr - txPacket->data; + txPacket->serialize("txPacket", os); + uint32_t txPktBufPtr = (uint32_t) (txPacketBufPtr - txPacket->data); + SERIALIZE_SCALAR(txPktBufPtr); + } + + bool rxPacketExists = rxPacket; + SERIALIZE_SCALAR(rxPacketExists); + if (rxPacketExists) { + rxPacket->serialize("rxPacket", os); + uint32_t rxPktBufPtr = (uint32_t) (rxPacketBufPtr - rxPacket->data); + SERIALIZE_SCALAR(rxPktBufPtr); + } + + SERIALIZE_SCALAR(txXferLen); + SERIALIZE_SCALAR(rxXferLen); + + /* + * Serialize Cached Descriptors + */ + SERIALIZE_SCALAR(rxDesc64.link); + SERIALIZE_SCALAR(rxDesc64.bufptr); + SERIALIZE_SCALAR(rxDesc64.cmdsts); + SERIALIZE_SCALAR(rxDesc64.extsts); + SERIALIZE_SCALAR(txDesc64.link); + SERIALIZE_SCALAR(txDesc64.bufptr); + SERIALIZE_SCALAR(txDesc64.cmdsts); + SERIALIZE_SCALAR(txDesc64.extsts); + SERIALIZE_SCALAR(rxDesc32.link); + SERIALIZE_SCALAR(rxDesc32.bufptr); + SERIALIZE_SCALAR(rxDesc32.cmdsts); + SERIALIZE_SCALAR(rxDesc32.extsts); + SERIALIZE_SCALAR(txDesc32.link); + SERIALIZE_SCALAR(txDesc32.bufptr); + SERIALIZE_SCALAR(txDesc32.cmdsts); + SERIALIZE_SCALAR(txDesc32.extsts); + SERIALIZE_SCALAR(extstsEnable); + + /* + * Serialize tx state machine + */ + int txState = this->txState; + SERIALIZE_SCALAR(txState); + SERIALIZE_SCALAR(txEnable); + SERIALIZE_SCALAR(CTDD); + SERIALIZE_SCALAR(txFragPtr); + SERIALIZE_SCALAR(txDescCnt); + int txDmaState = this->txDmaState; + SERIALIZE_SCALAR(txDmaState); + SERIALIZE_SCALAR(txKickTick); + + /* + * Serialize rx state machine + */ + int rxState = this->rxState; + SERIALIZE_SCALAR(rxState); + SERIALIZE_SCALAR(rxEnable); + SERIALIZE_SCALAR(CRDD); + SERIALIZE_SCALAR(rxPktBytes); + SERIALIZE_SCALAR(rxFragPtr); + SERIALIZE_SCALAR(rxDescCnt); + int rxDmaState = this->rxDmaState; + SERIALIZE_SCALAR(rxDmaState); + SERIALIZE_SCALAR(rxKickTick); + + /* + * Serialize EEPROM state machine + */ + int eepromState = this->eepromState; + SERIALIZE_SCALAR(eepromState); + SERIALIZE_SCALAR(eepromClk); + SERIALIZE_SCALAR(eepromBitsToRx); + SERIALIZE_SCALAR(eepromOpcode); + SERIALIZE_SCALAR(eepromAddress); + SERIALIZE_SCALAR(eepromData); + + /* + * If there's a pending transmit, store the time so we can + * reschedule it later + */ + Tick transmitTick = txEvent.scheduled() ? txEvent.when() - curTick : 0; + SERIALIZE_SCALAR(transmitTick); + + /* + * receive address filter settings + */ + SERIALIZE_SCALAR(rxFilterEnable); + SERIALIZE_SCALAR(acceptBroadcast); + SERIALIZE_SCALAR(acceptMulticast); + SERIALIZE_SCALAR(acceptUnicast); + SERIALIZE_SCALAR(acceptPerfect); + SERIALIZE_SCALAR(acceptArp); + SERIALIZE_SCALAR(multicastHashEnable); + + /* + * Keep track of pending interrupt status. + */ + SERIALIZE_SCALAR(intrTick); + SERIALIZE_SCALAR(cpuPendingIntr); + Tick intrEventTick = 0; + if (intrEvent) + intrEventTick = intrEvent->when(); + SERIALIZE_SCALAR(intrEventTick); + +} + +void +NSGigE::unserialize(Checkpoint *cp, const std::string §ion) +{ + // Unserialize the PciDev base class + PciDev::unserialize(cp, section); + + UNSERIALIZE_SCALAR(regs.command); + UNSERIALIZE_SCALAR(regs.config); + UNSERIALIZE_SCALAR(regs.mear); + UNSERIALIZE_SCALAR(regs.ptscr); + UNSERIALIZE_SCALAR(regs.isr); + UNSERIALIZE_SCALAR(regs.imr); + UNSERIALIZE_SCALAR(regs.ier); + UNSERIALIZE_SCALAR(regs.ihr); + UNSERIALIZE_SCALAR(regs.txdp); + UNSERIALIZE_SCALAR(regs.txdp_hi); + UNSERIALIZE_SCALAR(regs.txcfg); + UNSERIALIZE_SCALAR(regs.gpior); + UNSERIALIZE_SCALAR(regs.rxdp); + UNSERIALIZE_SCALAR(regs.rxdp_hi); + UNSERIALIZE_SCALAR(regs.rxcfg); + UNSERIALIZE_SCALAR(regs.pqcr); + UNSERIALIZE_SCALAR(regs.wcsr); + UNSERIALIZE_SCALAR(regs.pcr); + UNSERIALIZE_SCALAR(regs.rfcr); + UNSERIALIZE_SCALAR(regs.rfdr); + UNSERIALIZE_SCALAR(regs.brar); + UNSERIALIZE_SCALAR(regs.brdr); + UNSERIALIZE_SCALAR(regs.srr); + UNSERIALIZE_SCALAR(regs.mibc); + UNSERIALIZE_SCALAR(regs.vrcr); + UNSERIALIZE_SCALAR(regs.vtcr); + UNSERIALIZE_SCALAR(regs.vdr); + UNSERIALIZE_SCALAR(regs.ccsr); + UNSERIALIZE_SCALAR(regs.tbicr); + UNSERIALIZE_SCALAR(regs.tbisr); + UNSERIALIZE_SCALAR(regs.tanar); + UNSERIALIZE_SCALAR(regs.tanlpar); + UNSERIALIZE_SCALAR(regs.taner); + UNSERIALIZE_SCALAR(regs.tesr); + + UNSERIALIZE_ARRAY(rom.perfectMatch, ETH_ADDR_LEN); + UNSERIALIZE_ARRAY(rom.filterHash, FHASH_SIZE); + + UNSERIALIZE_SCALAR(ioEnable); + + /* + * unserialize the data fifos + */ + rxFifo.unserialize("rxFifo", cp, section); + txFifo.unserialize("txFifo", cp, section); + + /* + * unserialize the various helper variables + */ + bool txPacketExists; + UNSERIALIZE_SCALAR(txPacketExists); + if (txPacketExists) { + txPacket = new EthPacketData(16384); + txPacket->unserialize("txPacket", cp, section); + uint32_t txPktBufPtr; + UNSERIALIZE_SCALAR(txPktBufPtr); + txPacketBufPtr = (uint8_t *) txPacket->data + txPktBufPtr; + } else + txPacket = 0; + + bool rxPacketExists; + UNSERIALIZE_SCALAR(rxPacketExists); + rxPacket = 0; + if (rxPacketExists) { + rxPacket = new EthPacketData(16384); + rxPacket->unserialize("rxPacket", cp, section); + uint32_t rxPktBufPtr; + UNSERIALIZE_SCALAR(rxPktBufPtr); + rxPacketBufPtr = (uint8_t *) rxPacket->data + rxPktBufPtr; + } else + rxPacket = 0; + + UNSERIALIZE_SCALAR(txXferLen); + UNSERIALIZE_SCALAR(rxXferLen); + + /* + * Unserialize Cached Descriptors + */ + UNSERIALIZE_SCALAR(rxDesc64.link); + UNSERIALIZE_SCALAR(rxDesc64.bufptr); + UNSERIALIZE_SCALAR(rxDesc64.cmdsts); + UNSERIALIZE_SCALAR(rxDesc64.extsts); + UNSERIALIZE_SCALAR(txDesc64.link); + UNSERIALIZE_SCALAR(txDesc64.bufptr); + UNSERIALIZE_SCALAR(txDesc64.cmdsts); + UNSERIALIZE_SCALAR(txDesc64.extsts); + UNSERIALIZE_SCALAR(rxDesc32.link); + UNSERIALIZE_SCALAR(rxDesc32.bufptr); + UNSERIALIZE_SCALAR(rxDesc32.cmdsts); + UNSERIALIZE_SCALAR(rxDesc32.extsts); + UNSERIALIZE_SCALAR(txDesc32.link); + UNSERIALIZE_SCALAR(txDesc32.bufptr); + UNSERIALIZE_SCALAR(txDesc32.cmdsts); + UNSERIALIZE_SCALAR(txDesc32.extsts); + UNSERIALIZE_SCALAR(extstsEnable); + + /* + * unserialize tx state machine + */ + int txState; + UNSERIALIZE_SCALAR(txState); + this->txState = (TxState) txState; + UNSERIALIZE_SCALAR(txEnable); + UNSERIALIZE_SCALAR(CTDD); + UNSERIALIZE_SCALAR(txFragPtr); + UNSERIALIZE_SCALAR(txDescCnt); + int txDmaState; + UNSERIALIZE_SCALAR(txDmaState); + this->txDmaState = (DmaState) txDmaState; + UNSERIALIZE_SCALAR(txKickTick); + if (txKickTick) + txKickEvent.schedule(txKickTick); + + /* + * unserialize rx state machine + */ + int rxState; + UNSERIALIZE_SCALAR(rxState); + this->rxState = (RxState) rxState; + UNSERIALIZE_SCALAR(rxEnable); + UNSERIALIZE_SCALAR(CRDD); + UNSERIALIZE_SCALAR(rxPktBytes); + UNSERIALIZE_SCALAR(rxFragPtr); + UNSERIALIZE_SCALAR(rxDescCnt); + int rxDmaState; + UNSERIALIZE_SCALAR(rxDmaState); + this->rxDmaState = (DmaState) rxDmaState; + UNSERIALIZE_SCALAR(rxKickTick); + if (rxKickTick) + rxKickEvent.schedule(rxKickTick); + + /* + * Unserialize EEPROM state machine + */ + int eepromState; + UNSERIALIZE_SCALAR(eepromState); + this->eepromState = (EEPROMState) eepromState; + UNSERIALIZE_SCALAR(eepromClk); + UNSERIALIZE_SCALAR(eepromBitsToRx); + UNSERIALIZE_SCALAR(eepromOpcode); + UNSERIALIZE_SCALAR(eepromAddress); + UNSERIALIZE_SCALAR(eepromData); + + /* + * If there's a pending transmit, reschedule it now + */ + Tick transmitTick; + UNSERIALIZE_SCALAR(transmitTick); + if (transmitTick) + txEvent.schedule(curTick + transmitTick); + + /* + * unserialize receive address filter settings + */ + UNSERIALIZE_SCALAR(rxFilterEnable); + UNSERIALIZE_SCALAR(acceptBroadcast); + UNSERIALIZE_SCALAR(acceptMulticast); + UNSERIALIZE_SCALAR(acceptUnicast); + UNSERIALIZE_SCALAR(acceptPerfect); + UNSERIALIZE_SCALAR(acceptArp); + UNSERIALIZE_SCALAR(multicastHashEnable); + + /* + * Keep track of pending interrupt status. + */ + UNSERIALIZE_SCALAR(intrTick); + UNSERIALIZE_SCALAR(cpuPendingIntr); + Tick intrEventTick; + UNSERIALIZE_SCALAR(intrEventTick); + if (intrEventTick) { + intrEvent = new IntrEvent(this, true); + intrEvent->schedule(intrEventTick); + } +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(NSGigEInt) + + SimObjectParam<EtherInt *> peer; + SimObjectParam<NSGigE *> device; + +END_DECLARE_SIM_OBJECT_PARAMS(NSGigEInt) + +BEGIN_INIT_SIM_OBJECT_PARAMS(NSGigEInt) + + INIT_PARAM_DFLT(peer, "peer interface", NULL), + INIT_PARAM(device, "Ethernet device of this interface") + +END_INIT_SIM_OBJECT_PARAMS(NSGigEInt) + +CREATE_SIM_OBJECT(NSGigEInt) +{ + NSGigEInt *dev_int = new NSGigEInt(getInstanceName(), device); + + EtherInt *p = (EtherInt *)peer; + if (p) { + dev_int->setPeer(p); + p->setPeer(dev_int); + } + + return dev_int; +} + +REGISTER_SIM_OBJECT("NSGigEInt", NSGigEInt) + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(NSGigE) + + SimObjectParam<System *> system; + SimObjectParam<Platform *> platform; + SimObjectParam<PciConfigData *> configdata; + Param<uint32_t> pci_bus; + Param<uint32_t> pci_dev; + Param<uint32_t> pci_func; + Param<Tick> pio_latency; + Param<Tick> config_latency; + + Param<Tick> clock; + Param<bool> dma_desc_free; + Param<bool> dma_data_free; + Param<Tick> dma_read_delay; + Param<Tick> dma_write_delay; + Param<Tick> dma_read_factor; + Param<Tick> dma_write_factor; + Param<bool> dma_no_allocate; + Param<Tick> intr_delay; + + Param<Tick> rx_delay; + Param<Tick> tx_delay; + Param<uint32_t> rx_fifo_size; + Param<uint32_t> tx_fifo_size; + + Param<bool> rx_filter; + Param<string> hardware_address; + Param<bool> rx_thread; + Param<bool> tx_thread; + Param<bool> rss; + +END_DECLARE_SIM_OBJECT_PARAMS(NSGigE) + +BEGIN_INIT_SIM_OBJECT_PARAMS(NSGigE) + + INIT_PARAM(system, "System pointer"), + INIT_PARAM(platform, "Platform pointer"), + INIT_PARAM(configdata, "PCI Config data"), + INIT_PARAM(pci_bus, "PCI bus ID"), + INIT_PARAM(pci_dev, "PCI device number"), + INIT_PARAM(pci_func, "PCI function code"), + INIT_PARAM_DFLT(pio_latency, "Programmed IO latency in bus cycles", 1), + INIT_PARAM(config_latency, "Number of cycles for a config read or write"), + INIT_PARAM(clock, "State machine cycle time"), + + INIT_PARAM(dma_desc_free, "DMA of Descriptors is free"), + INIT_PARAM(dma_data_free, "DMA of Data is free"), + INIT_PARAM(dma_read_delay, "fixed delay for dma reads"), + INIT_PARAM(dma_write_delay, "fixed delay for dma writes"), + INIT_PARAM(dma_read_factor, "multiplier for dma reads"), + INIT_PARAM(dma_write_factor, "multiplier for dma writes"), + INIT_PARAM(dma_no_allocate, "Should DMA reads allocate cache lines"), + INIT_PARAM(intr_delay, "Interrupt Delay in microseconds"), + + INIT_PARAM(rx_delay, "Receive Delay"), + INIT_PARAM(tx_delay, "Transmit Delay"), + INIT_PARAM(rx_fifo_size, "max size in bytes of rxFifo"), + INIT_PARAM(tx_fifo_size, "max size in bytes of txFifo"), + + INIT_PARAM(rx_filter, "Enable Receive Filter"), + INIT_PARAM(hardware_address, "Ethernet Hardware Address"), + INIT_PARAM(rx_thread, ""), + INIT_PARAM(tx_thread, ""), + INIT_PARAM(rss, "") + +END_INIT_SIM_OBJECT_PARAMS(NSGigE) + + +CREATE_SIM_OBJECT(NSGigE) +{ + NSGigE::Params *params = new NSGigE::Params; + + params->name = getInstanceName(); + params->platform = platform; + params->system = system; + params->configData = configdata; + params->busNum = pci_bus; + params->deviceNum = pci_dev; + params->functionNum = pci_func; + params->pio_delay = pio_latency; + params->config_delay = config_latency; + + params->clock = clock; + params->dma_desc_free = dma_desc_free; + params->dma_data_free = dma_data_free; + params->dma_read_delay = dma_read_delay; + params->dma_write_delay = dma_write_delay; + params->dma_read_factor = dma_read_factor; + params->dma_write_factor = dma_write_factor; + params->dma_no_allocate = dma_no_allocate; + params->pio_delay = pio_latency; + params->intr_delay = intr_delay; + + params->rx_delay = rx_delay; + params->tx_delay = tx_delay; + params->rx_fifo_size = rx_fifo_size; + params->tx_fifo_size = tx_fifo_size; + + params->rx_filter = rx_filter; + params->eaddr = hardware_address; + params->rx_thread = rx_thread; + params->tx_thread = tx_thread; + params->rss = rss; + + return new NSGigE(params); +} + +REGISTER_SIM_OBJECT("NSGigE", NSGigE) diff --git a/src/dev/ns_gige.hh b/src/dev/ns_gige.hh new file mode 100644 index 000000000..080c0b1f3 --- /dev/null +++ b/src/dev/ns_gige.hh @@ -0,0 +1,467 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu + */ + +/** @file + * Device module for modelling the National Semiconductor + * DP83820 ethernet controller + */ + +#ifndef __DEV_NS_GIGE_HH__ +#define __DEV_NS_GIGE_HH__ + +#include "base/inet.hh" +#include "base/statistics.hh" +#include "dev/etherint.hh" +#include "dev/etherpkt.hh" +#include "dev/io_device.hh" +#include "dev/ns_gige_reg.h" +#include "dev/pcidev.hh" +#include "dev/pktfifo.hh" +#include "sim/eventq.hh" + +// Hash filtering constants +const uint16_t FHASH_ADDR = 0x100; +const uint16_t FHASH_SIZE = 0x100; + +// EEPROM constants +const uint8_t EEPROM_READ = 0x2; +const uint8_t EEPROM_SIZE = 64; // Size in words of NSC93C46 EEPROM +const uint8_t EEPROM_PMATCH2_ADDR = 0xA; // EEPROM Address of PMATCH word 2 +const uint8_t EEPROM_PMATCH1_ADDR = 0xB; // EEPROM Address of PMATCH word 1 +const uint8_t EEPROM_PMATCH0_ADDR = 0xC; // EEPROM Address of PMATCH word 0 + +/** + * Ethernet device registers + */ +struct dp_regs { + uint32_t command; + uint32_t config; + uint32_t mear; + uint32_t ptscr; + uint32_t isr; + uint32_t imr; + uint32_t ier; + uint32_t ihr; + uint32_t txdp; + uint32_t txdp_hi; + uint32_t txcfg; + uint32_t gpior; + uint32_t rxdp; + uint32_t rxdp_hi; + uint32_t rxcfg; + uint32_t pqcr; + uint32_t wcsr; + uint32_t pcr; + uint32_t rfcr; + uint32_t rfdr; + uint32_t brar; + uint32_t brdr; + uint32_t srr; + uint32_t mibc; + uint32_t vrcr; + uint32_t vtcr; + uint32_t vdr; + uint32_t ccsr; + uint32_t tbicr; + uint32_t tbisr; + uint32_t tanar; + uint32_t tanlpar; + uint32_t taner; + uint32_t tesr; +}; + +struct dp_rom { + /** + * for perfect match memory. + * the linux driver doesn't use any other ROM + */ + uint8_t perfectMatch[ETH_ADDR_LEN]; + + /** + * for hash table memory. + * used by the freebsd driver + */ + uint8_t filterHash[FHASH_SIZE]; +}; + +class NSGigEInt; +class Packet; + +/** + * NS DP83820 Ethernet device model + */ +class NSGigE : public PciDev +{ + public: + /** Transmit State Machine states */ + enum TxState + { + txIdle, + txDescRefr, + txDescRead, + txFifoBlock, + txFragRead, + txDescWrite, + txAdvance + }; + + /** Receive State Machine States */ + enum RxState + { + rxIdle, + rxDescRefr, + rxDescRead, + rxFifoBlock, + rxFragWrite, + rxDescWrite, + rxAdvance + }; + + enum DmaState + { + dmaIdle, + dmaReading, + dmaWriting, + dmaReadWaiting, + dmaWriteWaiting + }; + + /** EEPROM State Machine States */ + enum EEPROMState + { + eepromStart, + eepromGetOpcode, + eepromGetAddress, + eepromRead + }; + + protected: + /** device register file */ + dp_regs regs; + dp_rom rom; + + /** pci settings */ + bool ioEnable; +#if 0 + bool memEnable; + bool bmEnable; +#endif + + /*** BASIC STRUCTURES FOR TX/RX ***/ + /* Data FIFOs */ + PacketFifo txFifo; + PacketFifo rxFifo; + + /** various helper vars */ + EthPacketPtr txPacket; + EthPacketPtr rxPacket; + uint8_t *txPacketBufPtr; + uint8_t *rxPacketBufPtr; + uint32_t txXferLen; + uint32_t rxXferLen; + bool rxDmaFree; + bool txDmaFree; + + /** DescCaches */ + ns_desc32 txDesc32; + ns_desc32 rxDesc32; + ns_desc64 txDesc64; + ns_desc64 rxDesc64; + + /* state machine cycle time */ + Tick clock; + inline Tick cycles(int numCycles) const { return numCycles * clock; } + + /* tx State Machine */ + TxState txState; + bool txEnable; + + /** Current Transmit Descriptor Done */ + bool CTDD; + /** halt the tx state machine after next packet */ + bool txHalt; + /** ptr to the next byte in the current fragment */ + Addr txFragPtr; + /** count of bytes remaining in the current descriptor */ + uint32_t txDescCnt; + DmaState txDmaState; + + /** rx State Machine */ + RxState rxState; + bool rxEnable; + + /** Current Receive Descriptor Done */ + bool CRDD; + /** num of bytes in the current packet being drained from rxDataFifo */ + uint32_t rxPktBytes; + /** halt the rx state machine after current packet */ + bool rxHalt; + /** ptr to the next byte in current fragment */ + Addr rxFragPtr; + /** count of bytes remaining in the current descriptor */ + uint32_t rxDescCnt; + DmaState rxDmaState; + + bool extstsEnable; + + /** EEPROM State Machine */ + EEPROMState eepromState; + bool eepromClk; + uint8_t eepromBitsToRx; + uint8_t eepromOpcode; + uint8_t eepromAddress; + uint16_t eepromData; + + protected: + Tick dmaReadDelay; + Tick dmaWriteDelay; + + Tick dmaReadFactor; + Tick dmaWriteFactor; + + void *rxDmaData; + Addr rxDmaAddr; + int rxDmaLen; + bool doRxDmaRead(); + bool doRxDmaWrite(); + + void *txDmaData; + Addr txDmaAddr; + int txDmaLen; + bool doTxDmaRead(); + bool doTxDmaWrite(); + + void rxDmaReadDone(); + friend class EventWrapper<NSGigE, &NSGigE::rxDmaReadDone>; + EventWrapper<NSGigE, &NSGigE::rxDmaReadDone> rxDmaReadEvent; + + void rxDmaWriteDone(); + friend class EventWrapper<NSGigE, &NSGigE::rxDmaWriteDone>; + EventWrapper<NSGigE, &NSGigE::rxDmaWriteDone> rxDmaWriteEvent; + + void txDmaReadDone(); + friend class EventWrapper<NSGigE, &NSGigE::txDmaReadDone>; + EventWrapper<NSGigE, &NSGigE::txDmaReadDone> txDmaReadEvent; + + void txDmaWriteDone(); + friend class EventWrapper<NSGigE, &NSGigE::txDmaWriteDone>; + EventWrapper<NSGigE, &NSGigE::txDmaWriteDone> txDmaWriteEvent; + + bool dmaDescFree; + bool dmaDataFree; + + protected: + Tick txDelay; + Tick rxDelay; + + void txReset(); + void rxReset(); + void regsReset(); + + void rxKick(); + Tick rxKickTick; + typedef EventWrapper<NSGigE, &NSGigE::rxKick> RxKickEvent; + friend void RxKickEvent::process(); + RxKickEvent rxKickEvent; + + void txKick(); + Tick txKickTick; + typedef EventWrapper<NSGigE, &NSGigE::txKick> TxKickEvent; + friend void TxKickEvent::process(); + TxKickEvent txKickEvent; + + void eepromKick(); + + /** + * Retransmit event + */ + void transmit(); + void txEventTransmit() + { + transmit(); + if (txState == txFifoBlock) + txKick(); + } + typedef EventWrapper<NSGigE, &NSGigE::txEventTransmit> TxEvent; + friend void TxEvent::process(); + TxEvent txEvent; + + void txDump() const; + void rxDump() const; + + /** + * receive address filter + */ + bool rxFilterEnable; + bool rxFilter(const EthPacketPtr &packet); + bool acceptBroadcast; + bool acceptMulticast; + bool acceptUnicast; + bool acceptPerfect; + bool acceptArp; + bool multicastHashEnable; + + /** + * Interrupt management + */ + void devIntrPost(uint32_t interrupts); + void devIntrClear(uint32_t interrupts); + void devIntrChangeMask(); + + Tick intrDelay; + Tick intrTick; + bool cpuPendingIntr; + void cpuIntrPost(Tick when); + void cpuInterrupt(); + void cpuIntrClear(); + + typedef EventWrapper<NSGigE, &NSGigE::cpuInterrupt> IntrEvent; + friend void IntrEvent::process(); + IntrEvent *intrEvent; + NSGigEInt *interface; + + public: + struct Params : public PciDev::Params + { + Tick clock; + Tick intr_delay; + Tick tx_delay; + Tick rx_delay; + bool dma_desc_free; + bool dma_data_free; + Tick dma_read_delay; + Tick dma_write_delay; + Tick dma_read_factor; + Tick dma_write_factor; + bool rx_filter; + Net::EthAddr eaddr; + uint32_t tx_fifo_size; + uint32_t rx_fifo_size; + bool rx_thread; + bool tx_thread; + bool rss; + bool dma_no_allocate; + }; + + NSGigE(Params *params); + ~NSGigE(); + const Params *params() const { return (const Params *)_params; } + + virtual Tick writeConfig(Packet *pkt); + + virtual Tick read(Packet *pkt); + virtual Tick write(Packet *pkt); + + bool cpuIntrPending() const; + void cpuIntrAck() { cpuIntrClear(); } + + bool recvPacket(EthPacketPtr packet); + void transferDone(); + + void setInterface(NSGigEInt *i) { assert(!interface); interface = i; } + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + virtual void resume(); + + public: + void regStats(); + + private: + Stats::Scalar<> txBytes; + Stats::Scalar<> rxBytes; + Stats::Scalar<> txPackets; + Stats::Scalar<> rxPackets; + Stats::Scalar<> txIpChecksums; + Stats::Scalar<> rxIpChecksums; + Stats::Scalar<> txTcpChecksums; + Stats::Scalar<> rxTcpChecksums; + Stats::Scalar<> txUdpChecksums; + Stats::Scalar<> rxUdpChecksums; + Stats::Scalar<> descDmaReads; + Stats::Scalar<> descDmaWrites; + Stats::Scalar<> descDmaRdBytes; + Stats::Scalar<> descDmaWrBytes; + Stats::Formula totBandwidth; + Stats::Formula totPackets; + Stats::Formula totBytes; + Stats::Formula totPacketRate; + Stats::Formula txBandwidth; + Stats::Formula rxBandwidth; + Stats::Formula txPacketRate; + Stats::Formula rxPacketRate; + Stats::Scalar<> postedSwi; + Stats::Formula coalescedSwi; + Stats::Scalar<> totalSwi; + Stats::Scalar<> postedRxIdle; + Stats::Formula coalescedRxIdle; + Stats::Scalar<> totalRxIdle; + Stats::Scalar<> postedRxOk; + Stats::Formula coalescedRxOk; + Stats::Scalar<> totalRxOk; + Stats::Scalar<> postedRxDesc; + Stats::Formula coalescedRxDesc; + Stats::Scalar<> totalRxDesc; + Stats::Scalar<> postedTxOk; + Stats::Formula coalescedTxOk; + Stats::Scalar<> totalTxOk; + Stats::Scalar<> postedTxIdle; + Stats::Formula coalescedTxIdle; + Stats::Scalar<> totalTxIdle; + Stats::Scalar<> postedTxDesc; + Stats::Formula coalescedTxDesc; + Stats::Scalar<> totalTxDesc; + Stats::Scalar<> postedRxOrn; + Stats::Formula coalescedRxOrn; + Stats::Scalar<> totalRxOrn; + Stats::Formula coalescedTotal; + Stats::Scalar<> postedInterrupts; + Stats::Scalar<> droppedPackets; +}; + +/* + * Ethernet Interface for an Ethernet Device + */ +class NSGigEInt : public EtherInt +{ + private: + NSGigE *dev; + + public: + NSGigEInt(const std::string &name, NSGigE *d) + : EtherInt(name), dev(d) { dev->setInterface(this); } + + virtual bool recvPacket(EthPacketPtr pkt) { return dev->recvPacket(pkt); } + virtual void sendDone() { dev->transferDone(); } +}; + +#endif // __DEV_NS_GIGE_HH__ diff --git a/src/dev/ns_gige_reg.h b/src/dev/ns_gige_reg.h new file mode 100644 index 000000000..80ac5800d --- /dev/null +++ b/src/dev/ns_gige_reg.h @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Lisa Hsu + */ + +/** @file + * Ethernet device register definitions for the National + * Semiconductor DP83820 Ethernet controller + */ + +#ifndef __DEV_NS_GIGE_REG_H__ +#define __DEV_NS_GIGE_REG_H__ + +/* Device Register Address Map */ +#define CR 0x00 +#define CFGR 0x04 +#define MEAR 0x08 +#define PTSCR 0x0c +#define ISR 0x10 +#define IMR 0x14 +#define IER 0x18 +#define IHR 0x1c +#define TXDP 0x20 +#define TXDP_HI 0x24 +#define TX_CFG 0x28 +#define GPIOR 0x2c +#define RXDP 0x30 +#define RXDP_HI 0x34 +#define RX_CFG 0x38 +#define PQCR 0x3c +#define WCSR 0x40 +#define PCR 0x44 +#define RFCR 0x48 +#define RFDR 0x4c +#define BRAR 0x50 +#define BRDR 0x54 +#define SRR 0x58 +#define MIBC 0x5c +#define MIB_START 0x60 +#define MIB_END 0x88 +#define VRCR 0xbc +#define VTCR 0xc0 +#define VDR 0xc4 +#define CCSR 0xcc +#define TBICR 0xe0 +#define TBISR 0xe4 +#define TANAR 0xe8 +#define TANLPAR 0xec +#define TANER 0xf0 +#define TESR 0xf4 +#define M5REG 0xf8 +#define LAST 0xf8 +#define RESERVED 0xfc + +/* Chip Command Register */ +#define CR_TXE 0x00000001 +#define CR_TXD 0x00000002 +#define CR_RXE 0x00000004 +#define CR_RXD 0x00000008 +#define CR_TXR 0x00000010 +#define CR_RXR 0x00000020 +#define CR_SWI 0x00000080 +#define CR_RST 0x00000100 + +/* configuration register */ +#define CFGR_LNKSTS 0x80000000 +#define CFGR_SPDSTS 0x60000000 +#define CFGR_SPDSTS1 0x40000000 +#define CFGR_SPDSTS0 0x20000000 +#define CFGR_DUPSTS 0x10000000 +#define CFGR_TBI_EN 0x01000000 +#define CFGR_RESERVED 0x0e000000 +#define CFGR_MODE_1000 0x00400000 +#define CFGR_AUTO_1000 0x00200000 +#define CFGR_PINT_CTL 0x001c0000 +#define CFGR_PINT_DUPSTS 0x00100000 +#define CFGR_PINT_LNKSTS 0x00080000 +#define CFGR_PINT_SPDSTS 0x00040000 +#define CFGR_TMRTEST 0x00020000 +#define CFGR_MRM_DIS 0x00010000 +#define CFGR_MWI_DIS 0x00008000 +#define CFGR_T64ADDR 0x00004000 +#define CFGR_PCI64_DET 0x00002000 +#define CFGR_DATA64_EN 0x00001000 +#define CFGR_M64ADDR 0x00000800 +#define CFGR_PHY_RST 0x00000400 +#define CFGR_PHY_DIS 0x00000200 +#define CFGR_EXTSTS_EN 0x00000100 +#define CFGR_REQALG 0x00000080 +#define CFGR_SB 0x00000040 +#define CFGR_POW 0x00000020 +#define CFGR_EXD 0x00000010 +#define CFGR_PESEL 0x00000008 +#define CFGR_BROM_DIS 0x00000004 +#define CFGR_EXT_125 0x00000002 +#define CFGR_BEM 0x00000001 + +/* EEPROM access register */ +#define MEAR_EEDI 0x00000001 +#define MEAR_EEDO 0x00000002 +#define MEAR_EECLK 0x00000004 +#define MEAR_EESEL 0x00000008 +#define MEAR_MDIO 0x00000010 +#define MEAR_MDDIR 0x00000020 +#define MEAR_MDC 0x00000040 + +/* PCI test control register */ +#define PTSCR_EEBIST_FAIL 0x00000001 +#define PTSCR_EEBIST_EN 0x00000002 +#define PTSCR_EELOAD_EN 0x00000004 +#define PTSCR_RBIST_FAIL 0x000001b8 +#define PTSCR_RBIST_DONE 0x00000200 +#define PTSCR_RBIST_EN 0x00000400 +#define PTSCR_RBIST_RST 0x00002000 +#define PTSCR_RBIST_RDONLY 0x000003f9 + +/* interrupt status register */ +#define ISR_RESERVE 0x80000000 +#define ISR_TXDESC3 0x40000000 +#define ISR_TXDESC2 0x20000000 +#define ISR_TXDESC1 0x10000000 +#define ISR_TXDESC0 0x08000000 +#define ISR_RXDESC3 0x04000000 +#define ISR_RXDESC2 0x02000000 +#define ISR_RXDESC1 0x01000000 +#define ISR_RXDESC0 0x00800000 +#define ISR_TXRCMP 0x00400000 +#define ISR_RXRCMP 0x00200000 +#define ISR_DPERR 0x00100000 +#define ISR_SSERR 0x00080000 +#define ISR_RMABT 0x00040000 +#define ISR_RTABT 0x00020000 +#define ISR_RXSOVR 0x00010000 +#define ISR_HIBINT 0x00008000 +#define ISR_PHY 0x00004000 +#define ISR_PME 0x00002000 +#define ISR_SWI 0x00001000 +#define ISR_MIB 0x00000800 +#define ISR_TXURN 0x00000400 +#define ISR_TXIDLE 0x00000200 +#define ISR_TXERR 0x00000100 +#define ISR_TXDESC 0x00000080 +#define ISR_TXOK 0x00000040 +#define ISR_RXORN 0x00000020 +#define ISR_RXIDLE 0x00000010 +#define ISR_RXEARLY 0x00000008 +#define ISR_RXERR 0x00000004 +#define ISR_RXDESC 0x00000002 +#define ISR_RXOK 0x00000001 +#define ISR_ALL 0x7FFFFFFF +#define ISR_DELAY (ISR_TXIDLE|ISR_TXDESC|ISR_TXOK| \ + ISR_RXIDLE|ISR_RXDESC|ISR_RXOK) +#define ISR_NODELAY (ISR_ALL & ~ISR_DELAY) +#define ISR_IMPL (ISR_SWI|ISR_TXIDLE|ISR_TXDESC|ISR_TXOK|ISR_RXORN| \ + ISR_RXIDLE|ISR_RXDESC|ISR_RXOK) +#define ISR_NOIMPL (ISR_ALL & ~ISR_IMPL) + +/* transmit configuration register */ +#define TX_CFG_CSI 0x80000000 +#define TX_CFG_HBI 0x40000000 +#define TX_CFG_MLB 0x20000000 +#define TX_CFG_ATP 0x10000000 +#define TX_CFG_ECRETRY 0x00800000 +#define TX_CFG_BRST_DIS 0x00080000 +#define TX_CFG_MXDMA1024 0x00000000 +#define TX_CFG_MXDMA512 0x00700000 +#define TX_CFG_MXDMA256 0x00600000 +#define TX_CFG_MXDMA128 0x00500000 +#define TX_CFG_MXDMA64 0x00400000 +#define TX_CFG_MXDMA32 0x00300000 +#define TX_CFG_MXDMA16 0x00200000 +#define TX_CFG_MXDMA8 0x00100000 +#define TX_CFG_MXDMA 0x00700000 + +#define TX_CFG_FLTH_MASK 0x0000ff00 +#define TX_CFG_DRTH_MASK 0x000000ff + +/*general purpose I/O control register */ +#define GPIOR_UNUSED 0xffff8000 +#define GPIOR_GP5_IN 0x00004000 +#define GPIOR_GP4_IN 0x00002000 +#define GPIOR_GP3_IN 0x00001000 +#define GPIOR_GP2_IN 0x00000800 +#define GPIOR_GP1_IN 0x00000400 +#define GPIOR_GP5_OE 0x00000200 +#define GPIOR_GP4_OE 0x00000100 +#define GPIOR_GP3_OE 0x00000080 +#define GPIOR_GP2_OE 0x00000040 +#define GPIOR_GP1_OE 0x00000020 +#define GPIOR_GP5_OUT 0x00000010 +#define GPIOR_GP4_OUT 0x00000008 +#define GPIOR_GP3_OUT 0x00000004 +#define GPIOR_GP2_OUT 0x00000002 +#define GPIOR_GP1_OUT 0x00000001 + +/* receive configuration register */ +#define RX_CFG_AEP 0x80000000 +#define RX_CFG_ARP 0x40000000 +#define RX_CFG_STRIPCRC 0x20000000 +#define RX_CFG_RX_FD 0x10000000 +#define RX_CFG_ALP 0x08000000 +#define RX_CFG_AIRL 0x04000000 +#define RX_CFG_MXDMA512 0x00700000 +#define RX_CFG_MXDMA 0x00700000 +#define RX_CFG_DRTH 0x0000003e +#define RX_CFG_DRTH0 0x00000002 + +/* pause control status register */ +#define PCR_PSEN (1 << 31) +#define PCR_PS_MCAST (1 << 30) +#define PCR_PS_DA (1 << 29) +#define PCR_STHI_8 (3 << 23) +#define PCR_STLO_4 (1 << 23) +#define PCR_FFHI_8K (3 << 21) +#define PCR_FFLO_4K (1 << 21) +#define PCR_PAUSE_CNT 0xFFFE + +/*receive filter/match control register */ +#define RFCR_RFEN 0x80000000 +#define RFCR_AAB 0x40000000 +#define RFCR_AAM 0x20000000 +#define RFCR_AAU 0x10000000 +#define RFCR_APM 0x08000000 +#define RFCR_APAT 0x07800000 +#define RFCR_APAT3 0x04000000 +#define RFCR_APAT2 0x02000000 +#define RFCR_APAT1 0x01000000 +#define RFCR_APAT0 0x00800000 +#define RFCR_AARP 0x00400000 +#define RFCR_MHEN 0x00200000 +#define RFCR_UHEN 0x00100000 +#define RFCR_ULM 0x00080000 +#define RFCR_RFADDR 0x000003ff + +/* receive filter/match data register */ +#define RFDR_BMASK 0x00030000 +#define RFDR_RFDATA0 0x000000ff +#define RFDR_RFDATA1 0x0000ff00 + +/* management information base control register */ +#define MIBC_MIBS 0x00000008 +#define MIBC_ACLR 0x00000004 +#define MIBC_FRZ 0x00000002 +#define MIBC_WRN 0x00000001 + +/* VLAN/IP receive control register */ +#define VRCR_RUDPE 0x00000080 +#define VRCR_RTCPE 0x00000040 +#define VRCR_RIPE 0x00000020 +#define VRCR_IPEN 0x00000010 +#define VRCR_DUTF 0x00000008 +#define VRCR_DVTF 0x00000004 +#define VRCR_VTREN 0x00000002 +#define VRCR_VTDEN 0x00000001 + +/* VLAN/IP transmit control register */ +#define VTCR_PPCHK 0x00000008 +#define VTCR_GCHK 0x00000004 +#define VTCR_VPPTI 0x00000002 +#define VTCR_VGTI 0x00000001 + +/* Clockrun Control/Status Register */ +#define CCSR_CLKRUN_EN 0x00000001 + +/* TBI control register */ +#define TBICR_MR_LOOPBACK 0x00004000 +#define TBICR_MR_AN_ENABLE 0x00001000 +#define TBICR_MR_RESTART_AN 0x00000200 + +/* TBI status register */ +#define TBISR_MR_LINK_STATUS 0x00000020 +#define TBISR_MR_AN_COMPLETE 0x00000004 + +/* TBI auto-negotiation advertisement register */ +#define TANAR_NP 0x00008000 +#define TANAR_RF2 0x00002000 +#define TANAR_RF1 0x00001000 +#define TANAR_PS2 0x00000100 +#define TANAR_PS1 0x00000080 +#define TANAR_HALF_DUP 0x00000040 +#define TANAR_FULL_DUP 0x00000020 +#define TANAR_UNUSED 0x00000E1F + +/* M5 control register */ +#define M5REG_RESERVED 0xfffffffc +#define M5REG_RSS 0x00000004 +#define M5REG_RX_THREAD 0x00000002 +#define M5REG_TX_THREAD 0x00000001 + +struct ns_desc32 { + uint32_t link; /* link field to next descriptor in linked list */ + uint32_t bufptr; /* pointer to the first fragment or buffer */ + uint32_t cmdsts; /* command/status field */ + uint32_t extsts; /* extended status field for VLAN and IP info */ +}; + +struct ns_desc64 { + uint64_t link; /* link field to next descriptor in linked list */ + uint64_t bufptr; /* pointer to the first fragment or buffer */ + uint32_t cmdsts; /* command/status field */ + uint32_t extsts; /* extended status field for VLAN and IP info */ +}; + +/* cmdsts flags for descriptors */ +#define CMDSTS_OWN 0x80000000 +#define CMDSTS_MORE 0x40000000 +#define CMDSTS_INTR 0x20000000 +#define CMDSTS_ERR 0x10000000 +#define CMDSTS_OK 0x08000000 +#define CMDSTS_LEN_MASK 0x0000ffff + +#define CMDSTS_DEST_MASK 0x01800000 +#define CMDSTS_DEST_SELF 0x00800000 +#define CMDSTS_DEST_MULTI 0x01000000 + +/* extended flags for descriptors */ +#define EXTSTS_UDPERR 0x00400000 +#define EXTSTS_UDPPKT 0x00200000 +#define EXTSTS_TCPERR 0x00100000 +#define EXTSTS_TCPPKT 0x00080000 +#define EXTSTS_IPERR 0x00040000 +#define EXTSTS_IPPKT 0x00020000 + + +/* speed status */ +#define SPDSTS_POLARITY (CFGR_SPDSTS1 | CFGR_SPDSTS0 | CFGR_DUPSTS | (lnksts ? CFGR_LNKSTS : 0)) + +#endif /* __DEV_NS_GIGE_REG_H__ */ diff --git a/src/dev/pciconfigall.cc b/src/dev/pciconfigall.cc new file mode 100644 index 000000000..68013eab8 --- /dev/null +++ b/src/dev/pciconfigall.cc @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Andrew Schultz + * Ali Saidi + */ + +/* @file + * PCI Configspace implementation + */ + +#include "base/trace.hh" +#include "dev/pciconfigall.hh" +#include "dev/pcireg.h" +#include "dev/platform.hh" +#include "mem/packet.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; + +PciConfigAll::PciConfigAll(Params *p) + : PioDevice(p) +{ + pioAddr = p->platform->calcConfigAddr(params()->bus,0,0); +} + + +Tick +PciConfigAll::read(Packet *pkt) +{ + assert(pkt->result == Packet::Unknown); + + pkt->allocate(); + + DPRINTF(PciConfigAll, "read va=%#x size=%d\n", pkt->getAddr(), + pkt->getSize()); + + switch (pkt->getSize()) { + case sizeof(uint32_t): + pkt->set<uint32_t>(0xFFFFFFFF); + break; + case sizeof(uint16_t): + pkt->set<uint16_t>(0xFFFF); + break; + case sizeof(uint8_t): + pkt->set<uint8_t>(0xFF); + break; + default: + panic("invalid access size(?) for PCI configspace!\n"); + } + pkt->result = Packet::Success; + return params()->pio_delay; +} + +Tick +PciConfigAll::write(Packet *pkt) +{ + assert(pkt->result == Packet::Unknown); + panic("Attempting to write to config space on non-existant device\n"); +} + +void +PciConfigAll::addressRanges(AddrRangeList &range_list) +{ + range_list.clear(); + range_list.push_back(RangeSize(pioAddr, params()->size)); +} + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(PciConfigAll) + + Param<Tick> pio_latency; + Param<int> bus; + Param<Addr> size; + SimObjectParam<Platform *> platform; + SimObjectParam<System *> system; + +END_DECLARE_SIM_OBJECT_PARAMS(PciConfigAll) + +BEGIN_INIT_SIM_OBJECT_PARAMS(PciConfigAll) + + INIT_PARAM(pio_latency, "Programmed IO latency"), + INIT_PARAM(bus, "Bus that this object handles config space for"), + INIT_PARAM(size, "The size of config space"), + INIT_PARAM(platform, "platform"), + INIT_PARAM(system, "system object") + +END_INIT_SIM_OBJECT_PARAMS(PciConfigAll) + +CREATE_SIM_OBJECT(PciConfigAll) +{ + PciConfigAll::Params *p = new PciConfigAll::Params; + p->pio_delay = pio_latency; + p->platform = platform; + p->system = system; + p->bus = bus; + p->size = size; + + return new PciConfigAll(p); +} + +REGISTER_SIM_OBJECT("PciConfigAll", PciConfigAll) + +#endif // DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/dev/pciconfigall.hh b/src/dev/pciconfigall.hh new file mode 100644 index 000000000..07eaf8112 --- /dev/null +++ b/src/dev/pciconfigall.hh @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Andrew Schultz + * Ali Saidi + */ + +/* + * @file + * PCI Config space implementation. + */ + +#ifndef __PCICONFIGALL_HH__ +#define __PCICONFIGALL_HH__ + +#include "dev/pcireg.h" +#include "base/range.hh" +#include "dev/io_device.hh" + + +/** + * PCI Config Space + * All of PCI config space needs to return -1 on Tsunami, except + * the devices that exist. This device maps the entire bus config + * space and passes the requests on to TsunamiPCIDev devices as + * appropriate. + */ +class PciConfigAll : public PioDevice +{ + public: + struct Params : public PioDevice::Params + { + Tick pio_delay; + Addr size; + int bus; + }; + const Params *params() const { return (const Params *)_params; } + + /** + * Constructor for PCIConfigAll + * @param p parameters structure + */ + PciConfigAll(Params *p); + + /** + * Read something in PCI config space. If the device does not exist + * -1 is returned, if the device does exist its PciDev::ReadConfig (or the + * virtual function that overrides) it is called. + * @param pkt Contains information about the read operation + * @return Amount of time to do the read + */ + virtual Tick read(Packet *pkt); + + /** + * Write to PCI config spcae. If the device does not exit the simulator + * panics. If it does it is passed on the PciDev::WriteConfig (or the virtual + * function that overrides it). + * @param pkt Contains information about the write operation + * @return Amount of time to do the read + */ + + virtual Tick write(Packet *pkt); + + void addressRanges(AddrRangeList &range_list); + + private: + Addr pioAddr; + +}; + +#endif // __PCICONFIGALL_HH__ diff --git a/src/dev/pcidev.cc b/src/dev/pcidev.cc new file mode 100644 index 000000000..c3b83f448 --- /dev/null +++ b/src/dev/pcidev.cc @@ -0,0 +1,435 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Andrew Schultz + * Miguel Serrano + */ + +/* @file + * A single PCI device configuration space entry. + */ + +#include <list> +#include <string> +#include <vector> + +#include "base/inifile.hh" +#include "base/intmath.hh" // for isPowerOf2( +#include "base/misc.hh" +#include "base/str.hh" // for to_number +#include "base/trace.hh" +#include "dev/pciconfigall.hh" +#include "dev/pcidev.hh" +#include "dev/tsunamireg.h" +#include "mem/packet.hh" +#include "sim/builder.hh" +#include "sim/byteswap.hh" +#include "sim/param.hh" +#include "sim/root.hh" + +using namespace std; + + +PciDev::PciConfigPort::PciConfigPort(PciDev *dev, int busid, int devid, + int funcid, Platform *p) + : SimpleTimingPort(dev->name() + "-pciconf"), device(dev), platform(p), + busId(busid), deviceId(devid), functionId(funcid) +{ + configAddr = platform->calcConfigAddr(busId, deviceId, functionId); +} + + +Tick +PciDev::PciConfigPort::recvAtomic(Packet *pkt) +{ + assert(pkt->result == Packet::Unknown); + assert(pkt->getAddr() >= configAddr && + pkt->getAddr() < configAddr + PCI_CONFIG_SIZE); + return pkt->isRead() ? device->readConfig(pkt) : device->writeConfig(pkt); +} + +void +PciDev::PciConfigPort::getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) +{ + snoop.clear(); + resp.push_back(RangeSize(configAddr, PCI_CONFIG_SIZE+1)); +} + + +PciDev::PciDev(Params *p) + : DmaDevice(p), plat(p->platform), configData(p->configData), + pioDelay(p->pio_delay), configDelay(p->config_delay), + configPort(NULL) +{ + // copy the config data from the PciConfigData object + if (configData) { + memcpy(config.data, configData->config.data, sizeof(config.data)); + memcpy(BARSize, configData->BARSize, sizeof(BARSize)); + } else + panic("NULL pointer to configuration data"); + + memset(BARAddrs, 0, sizeof(BARAddrs)); + + plat->registerPciDevice(0, p->deviceNum, p->functionNum, + letoh(configData->config.interruptLine)); +} + +void +PciDev::init() +{ + if (!configPort) + panic("pci config port not connected to anything!"); + configPort->sendStatusChange(Port::RangeChange); + PioDevice::init(); +} + +unsigned int +PciDev::drain(Event *de) +{ + unsigned int count; + count = pioPort->drain(de) + dmaPort->drain(de) + configPort->drain(de); + if (count) + changeState(Draining); + else + changeState(Drained); + return count; +} + +Tick +PciDev::readConfig(Packet *pkt) +{ + int offset = pkt->getAddr() & PCI_CONFIG_SIZE; + if (offset >= PCI_DEVICE_SPECIFIC) + panic("Device specific PCI config space not implemented!\n"); + + pkt->allocate(); + + switch (pkt->getSize()) { + case sizeof(uint8_t): + pkt->set<uint8_t>(config.data[offset]); + DPRINTF(PCIDEV, + "readConfig: dev %#x func %#x reg %#x 1 bytes: data = %#x\n", + params()->deviceNum, params()->functionNum, offset, + (uint32_t)pkt->get<uint8_t>()); + break; + case sizeof(uint16_t): + pkt->set<uint16_t>(*(uint16_t*)&config.data[offset]); + DPRINTF(PCIDEV, + "readConfig: dev %#x func %#x reg %#x 2 bytes: data = %#x\n", + params()->deviceNum, params()->functionNum, offset, + (uint32_t)pkt->get<uint16_t>()); + break; + case sizeof(uint32_t): + pkt->set<uint32_t>(*(uint32_t*)&config.data[offset]); + DPRINTF(PCIDEV, + "readConfig: dev %#x func %#x reg %#x 4 bytes: data = %#x\n", + params()->deviceNum, params()->functionNum, offset, + (uint32_t)pkt->get<uint32_t>()); + break; + default: + panic("invalid access size(?) for PCI configspace!\n"); + } + pkt->result = Packet::Success; + return configDelay; + +} + +void +PciDev::addressRanges(AddrRangeList &range_list) +{ + int x = 0; + range_list.clear(); + for (x = 0; x < 6; x++) + if (BARAddrs[x] != 0) + range_list.push_back(RangeSize(BARAddrs[x],BARSize[x])); +} + +Tick +PciDev::writeConfig(Packet *pkt) +{ + int offset = pkt->getAddr() & PCI_CONFIG_SIZE; + if (offset >= PCI_DEVICE_SPECIFIC) + panic("Device specific PCI config space not implemented!\n"); + + switch (pkt->getSize()) { + case sizeof(uint8_t): + switch (offset) { + case PCI0_INTERRUPT_LINE: + config.interruptLine = pkt->get<uint8_t>(); + case PCI_CACHE_LINE_SIZE: + config.cacheLineSize = pkt->get<uint8_t>(); + case PCI_LATENCY_TIMER: + config.latencyTimer = pkt->get<uint8_t>(); + break; + /* Do nothing for these read-only registers */ + case PCI0_INTERRUPT_PIN: + case PCI0_MINIMUM_GRANT: + case PCI0_MAXIMUM_LATENCY: + case PCI_CLASS_CODE: + case PCI_REVISION_ID: + break; + default: + panic("writing to a read only register"); + } + DPRINTF(PCIDEV, + "writeConfig: dev %#x func %#x reg %#x 1 bytes: data = %#x\n", + params()->deviceNum, params()->functionNum, offset, + (uint32_t)pkt->get<uint8_t>()); + break; + case sizeof(uint16_t): + switch (offset) { + case PCI_COMMAND: + config.command = pkt->get<uint8_t>(); + case PCI_STATUS: + config.status = pkt->get<uint8_t>(); + case PCI_CACHE_LINE_SIZE: + config.cacheLineSize = pkt->get<uint8_t>(); + break; + default: + panic("writing to a read only register"); + } + DPRINTF(PCIDEV, + "writeConfig: dev %#x func %#x reg %#x 2 bytes: data = %#x\n", + params()->deviceNum, params()->functionNum, offset, + (uint32_t)pkt->get<uint16_t>()); + break; + case sizeof(uint32_t): + switch (offset) { + case PCI0_BASE_ADDR0: + case PCI0_BASE_ADDR1: + case PCI0_BASE_ADDR2: + case PCI0_BASE_ADDR3: + case PCI0_BASE_ADDR4: + case PCI0_BASE_ADDR5: + { + int barnum = BAR_NUMBER(offset); + + // convert BAR values to host endianness + uint32_t he_old_bar = letoh(config.baseAddr[barnum]); + uint32_t he_new_bar = letoh(pkt->get<uint32_t>()); + + uint32_t bar_mask = + BAR_IO_SPACE(he_old_bar) ? BAR_IO_MASK : BAR_MEM_MASK; + + // Writing 0xffffffff to a BAR tells the card to set the + // value of the bar to a bitmask indicating the size of + // memory it needs + if (he_new_bar == 0xffffffff) { + he_new_bar = ~(BARSize[barnum] - 1); + } else { + // does it mean something special to write 0 to a BAR? + he_new_bar &= ~bar_mask; + if (he_new_bar) { + Addr space_base = BAR_IO_SPACE(he_old_bar) ? + TSUNAMI_PCI0_IO : TSUNAMI_PCI0_MEMORY; + BARAddrs[barnum] = he_new_bar + space_base; + pioPort->sendStatusChange(Port::RangeChange); + } + } + config.baseAddr[barnum] = htole((he_new_bar & ~bar_mask) | + (he_old_bar & bar_mask)); + } + break; + + case PCI0_ROM_BASE_ADDR: + if (letoh(pkt->get<uint32_t>()) == 0xfffffffe) + config.expansionROM = htole((uint32_t)0xffffffff); + else + config.expansionROM = pkt->get<uint32_t>(); + break; + + case PCI_COMMAND: + // This could also clear some of the error bits in the Status + // register. However they should never get set, so lets ignore + // it for now + config.command = pkt->get<uint32_t>(); + break; + + default: + DPRINTF(PCIDEV, "Writing to a read only register"); + } + DPRINTF(PCIDEV, + "writeConfig: dev %#x func %#x reg %#x 4 bytes: data = %#x\n", + params()->deviceNum, params()->functionNum, offset, + (uint32_t)pkt->get<uint32_t>()); + break; + default: + panic("invalid access size(?) for PCI configspace!\n"); + } + pkt->result = Packet::Success; + return configDelay; + +} + +void +PciDev::serialize(ostream &os) +{ + SERIALIZE_ARRAY(BARSize, sizeof(BARSize) / sizeof(BARSize[0])); + SERIALIZE_ARRAY(BARAddrs, sizeof(BARAddrs) / sizeof(BARAddrs[0])); + SERIALIZE_ARRAY(config.data, sizeof(config.data) / sizeof(config.data[0])); +} + +void +PciDev::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_ARRAY(BARSize, sizeof(BARSize) / sizeof(BARSize[0])); + UNSERIALIZE_ARRAY(BARAddrs, sizeof(BARAddrs) / sizeof(BARAddrs[0])); + UNSERIALIZE_ARRAY(config.data, + sizeof(config.data) / sizeof(config.data[0])); +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(PciConfigData) + + Param<uint16_t> VendorID; + Param<uint16_t> DeviceID; + Param<uint16_t> Command; + Param<uint16_t> Status; + Param<uint8_t> Revision; + Param<uint8_t> ProgIF; + Param<uint8_t> SubClassCode; + Param<uint8_t> ClassCode; + Param<uint8_t> CacheLineSize; + Param<uint8_t> LatencyTimer; + Param<uint8_t> HeaderType; + Param<uint8_t> BIST; + Param<uint32_t> BAR0; + Param<uint32_t> BAR1; + Param<uint32_t> BAR2; + Param<uint32_t> BAR3; + Param<uint32_t> BAR4; + Param<uint32_t> BAR5; + Param<uint32_t> CardbusCIS; + Param<uint16_t> SubsystemVendorID; + Param<uint16_t> SubsystemID; + Param<uint32_t> ExpansionROM; + Param<uint8_t> InterruptLine; + Param<uint8_t> InterruptPin; + Param<uint8_t> MinimumGrant; + Param<uint8_t> MaximumLatency; + Param<uint32_t> BAR0Size; + Param<uint32_t> BAR1Size; + Param<uint32_t> BAR2Size; + Param<uint32_t> BAR3Size; + Param<uint32_t> BAR4Size; + Param<uint32_t> BAR5Size; + +END_DECLARE_SIM_OBJECT_PARAMS(PciConfigData) + +BEGIN_INIT_SIM_OBJECT_PARAMS(PciConfigData) + + INIT_PARAM(VendorID, "Vendor ID"), + INIT_PARAM(DeviceID, "Device ID"), + INIT_PARAM_DFLT(Command, "Command Register", 0x00), + INIT_PARAM_DFLT(Status, "Status Register", 0x00), + INIT_PARAM_DFLT(Revision, "Device Revision", 0x00), + INIT_PARAM_DFLT(ProgIF, "Programming Interface", 0x00), + INIT_PARAM(SubClassCode, "Sub-Class Code"), + INIT_PARAM(ClassCode, "Class Code"), + INIT_PARAM_DFLT(CacheLineSize, "System Cacheline Size", 0x00), + INIT_PARAM_DFLT(LatencyTimer, "PCI Latency Timer", 0x00), + INIT_PARAM_DFLT(HeaderType, "PCI Header Type", 0x00), + INIT_PARAM_DFLT(BIST, "Built In Self Test", 0x00), + INIT_PARAM_DFLT(BAR0, "Base Address Register 0", 0x00), + INIT_PARAM_DFLT(BAR1, "Base Address Register 1", 0x00), + INIT_PARAM_DFLT(BAR2, "Base Address Register 2", 0x00), + INIT_PARAM_DFLT(BAR3, "Base Address Register 3", 0x00), + INIT_PARAM_DFLT(BAR4, "Base Address Register 4", 0x00), + INIT_PARAM_DFLT(BAR5, "Base Address Register 5", 0x00), + INIT_PARAM_DFLT(CardbusCIS, "Cardbus Card Information Structure", 0x00), + INIT_PARAM_DFLT(SubsystemVendorID, "Subsystem Vendor ID", 0x00), + INIT_PARAM_DFLT(SubsystemID, "Subsystem ID", 0x00), + INIT_PARAM_DFLT(ExpansionROM, "Expansion ROM Base Address Register", 0x00), + INIT_PARAM(InterruptLine, "Interrupt Line Register"), + INIT_PARAM(InterruptPin, "Interrupt Pin Register"), + INIT_PARAM_DFLT(MinimumGrant, "Minimum Grant", 0x00), + INIT_PARAM_DFLT(MaximumLatency, "Maximum Latency", 0x00), + INIT_PARAM_DFLT(BAR0Size, "Base Address Register 0 Size", 0x00), + INIT_PARAM_DFLT(BAR1Size, "Base Address Register 1 Size", 0x00), + INIT_PARAM_DFLT(BAR2Size, "Base Address Register 2 Size", 0x00), + INIT_PARAM_DFLT(BAR3Size, "Base Address Register 3 Size", 0x00), + INIT_PARAM_DFLT(BAR4Size, "Base Address Register 4 Size", 0x00), + INIT_PARAM_DFLT(BAR5Size, "Base Address Register 5 Size", 0x00) + +END_INIT_SIM_OBJECT_PARAMS(PciConfigData) + +CREATE_SIM_OBJECT(PciConfigData) +{ + PciConfigData *data = new PciConfigData(getInstanceName()); + + data->config.vendor = htole(VendorID); + data->config.device = htole(DeviceID); + data->config.command = htole(Command); + data->config.status = htole(Status); + data->config.revision = htole(Revision); + data->config.progIF = htole(ProgIF); + data->config.subClassCode = htole(SubClassCode); + data->config.classCode = htole(ClassCode); + data->config.cacheLineSize = htole(CacheLineSize); + data->config.latencyTimer = htole(LatencyTimer); + data->config.headerType = htole(HeaderType); + data->config.bist = htole(BIST); + + data->config.baseAddr[0] = htole(BAR0); + data->config.baseAddr[1] = htole(BAR1); + data->config.baseAddr[2] = htole(BAR2); + data->config.baseAddr[3] = htole(BAR3); + data->config.baseAddr[4] = htole(BAR4); + data->config.baseAddr[5] = htole(BAR5); + data->config.cardbusCIS = htole(CardbusCIS); + data->config.subsystemVendorID = htole(SubsystemVendorID); + data->config.subsystemID = htole(SubsystemID); + data->config.expansionROM = htole(ExpansionROM); + data->config.interruptLine = htole(InterruptLine); + data->config.interruptPin = htole(InterruptPin); + data->config.minimumGrant = htole(MinimumGrant); + data->config.maximumLatency = htole(MaximumLatency); + + data->BARSize[0] = BAR0Size; + data->BARSize[1] = BAR1Size; + data->BARSize[2] = BAR2Size; + data->BARSize[3] = BAR3Size; + data->BARSize[4] = BAR4Size; + data->BARSize[5] = BAR5Size; + + for (int i = 0; i < 6; ++i) { + uint32_t barsize = data->BARSize[i]; + if (barsize != 0 && !isPowerOf2(barsize)) { + fatal("%s: BAR %d size %d is not a power of 2\n", + getInstanceName(), i, data->BARSize[i]); + } + } + + return data; +} + +REGISTER_SIM_OBJECT("PciConfigData", PciConfigData) + +#endif // DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/dev/pcidev.hh b/src/dev/pcidev.hh new file mode 100644 index 000000000..22dd6296e --- /dev/null +++ b/src/dev/pcidev.hh @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Andrew Schultz + * Nathan Binkert + */ + +/* @file + * Interface for devices using PCI configuration + */ + +#ifndef __DEV_PCIDEV_HH__ +#define __DEV_PCIDEV_HH__ + +#include "dev/io_device.hh" +#include "dev/pcireg.h" +#include "dev/platform.hh" + +#define BAR_IO_MASK 0x3 +#define BAR_MEM_MASK 0xF +#define BAR_IO_SPACE_BIT 0x1 +#define BAR_IO_SPACE(x) ((x) & BAR_IO_SPACE_BIT) +#define BAR_NUMBER(x) (((x) - PCI0_BASE_ADDR0) >> 0x2); + + +/** + * This class encapulates the first 64 bytes of a singles PCI + * devices config space that in configured by the configuration file. + */ +class PciConfigData : public SimObject +{ + public: + /** + * Constructor to initialize the devices config space to 0. + */ + PciConfigData(const std::string &name) + : SimObject(name) + { + memset(config.data, 0, sizeof(config.data)); + memset(BARSize, 0, sizeof(BARSize)); + } + + /** The first 64 bytes */ + PCIConfig config; + + /** The size of the BARs */ + uint32_t BARSize[6]; +}; + + +/** + * PCI device, base implementation is only config space. + */ +class PciDev : public DmaDevice +{ + class PciConfigPort : public SimpleTimingPort + { + protected: + PciDev *device; + + virtual Tick recvAtomic(Packet *pkt); + + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop); + + Platform *platform; + + int busId; + int deviceId; + int functionId; + + Addr configAddr; + + public: + PciConfigPort(PciDev *dev, int busid, int devid, int funcid, + Platform *p); + }; + + public: + struct Params : public PioDevice::Params + { + /** + * A pointer to the object that contains the first 64 bytes of + * config space + */ + PciConfigData *configData; + + /** The bus number we are on */ + uint32_t busNum; + + /** The device number we have */ + uint32_t deviceNum; + + /** The function number */ + uint32_t functionNum; + + /** The latency for pio accesses. */ + Tick pio_delay; + + /** The latency for a config access. */ + Tick config_delay; + }; + + public: + const Params *params() const { return (const Params *)_params; } + + protected: + /** The current config space. Unlike the PciConfigData this is + * updated during simulation while continues to reflect what was + * in the config file. + */ + PCIConfig config; + + /** The size of the BARs */ + uint32_t BARSize[6]; + + /** The current address mapping of the BARs */ + Addr BARAddrs[6]; + + /** + * Does the given address lie within the space mapped by the given + * base address register? + */ + bool + isBAR(Addr addr, int bar) const + { + assert(bar >= 0 && bar < 6); + return BARAddrs[bar] <= addr && addr < BARAddrs[bar] + BARSize[bar]; + } + + /** + * Which base address register (if any) maps the given address? + * @return The BAR number (0-5 inclusive), or -1 if none. + */ + int + getBAR(Addr addr) + { + for (int i = 0; i <= 5; ++i) + if (isBAR(addr, i)) + return i; + + return -1; + } + + /** + * Which base address register (if any) maps the given address? + * @param addr The address to check. + * @retval bar The BAR number (0-5 inclusive), + * only valid if return value is true. + * @retval offs The offset from the base address, + * only valid if return value is true. + * @return True iff address maps to a base address register's region. + */ + bool + getBAR(Addr addr, int &bar, Addr &offs) + { + int b = getBAR(addr); + if (b < 0) + return false; + + offs = addr - BARAddrs[b]; + bar = b; + return true; + } + + protected: + Platform *plat; + PciConfigData *configData; + Tick pioDelay; + Tick configDelay; + PciConfigPort *configPort; + + /** + * Write to the PCI config space data that is stored locally. This may be + * overridden by the device but at some point it will eventually call this + * for normal operations that it does not need to override. + * @param pkt packet containing the write the offset into config space + */ + virtual Tick writeConfig(Packet *pkt); + + + /** + * Read from the PCI config space data that is stored locally. This may be + * overridden by the device but at some point it will eventually call this + * for normal operations that it does not need to override. + * @param pkt packet containing the write the offset into config space + */ + virtual Tick readConfig(Packet *pkt); + + public: + Addr pciToDma(Addr pciAddr) const + { return plat->pciToDma(pciAddr); } + + void + intrPost() + { plat->postPciInt(letoh(configData->config.interruptLine)); } + + void + intrClear() + { plat->clearPciInt(letoh(configData->config.interruptLine)); } + + uint8_t + interruptLine() + { return letoh(configData->config.interruptLine); } + + /** return the address ranges that this device responds to. + * @params range_list range list to populate with ranges + */ + void addressRanges(AddrRangeList &range_list); + + /** + * Constructor for PCI Dev. This function copies data from the + * config file object PCIConfigData and registers the device with + * a PciConfigAll object. + */ + PciDev(Params *params); + + virtual void init(); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); + + + virtual unsigned int drain(Event *de); + + virtual Port *getPort(const std::string &if_name, int idx = -1) + { + if (if_name == "config") { + if (configPort != NULL) + panic("pciconfig port already connected to."); + configPort = new PciConfigPort(this, params()->busNum, + params()->deviceNum, params()->functionNum, + params()->platform); + return configPort; + } + return DmaDevice::getPort(if_name, idx); + } + +}; +#endif // __DEV_PCIDEV_HH__ diff --git a/src/dev/pcireg.h b/src/dev/pcireg.h new file mode 100644 index 000000000..df57acdb0 --- /dev/null +++ b/src/dev/pcireg.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Miguel Serrano + */ + +/* @file + * Device register definitions for a device's PCI config space + */ + +#ifndef __PCIREG_H__ +#define __PCIREG_H__ + +#include <sys/types.h> + +union PCIConfig { + uint8_t data[64]; + + struct { + uint16_t vendor; + uint16_t device; + uint16_t command; + uint16_t status; + uint8_t revision; + uint8_t progIF; + uint8_t subClassCode; + uint8_t classCode; + uint8_t cacheLineSize; + uint8_t latencyTimer; + uint8_t headerType; + uint8_t bist; + uint32_t baseAddr[6]; + uint32_t cardbusCIS; + uint16_t subsystemVendorID; + uint16_t subsystemID; + uint32_t expansionROM; + uint32_t reserved0; + uint32_t reserved1; + uint8_t interruptLine; + uint8_t interruptPin; + uint8_t minimumGrant; + uint8_t maximumLatency; + }; +}; + +// Common PCI offsets +#define PCI_VENDOR_ID 0x00 // Vendor ID ro +#define PCI_DEVICE_ID 0x02 // Device ID ro +#define PCI_COMMAND 0x04 // Command rw +#define PCI_STATUS 0x06 // Status rw +#define PCI_REVISION_ID 0x08 // Revision ID ro +#define PCI_CLASS_CODE 0x09 // Class Code ro +#define PCI_SUB_CLASS_CODE 0x0A // Sub Class Code ro +#define PCI_BASE_CLASS_CODE 0x0B // Base Class Code ro +#define PCI_CACHE_LINE_SIZE 0x0C // Cache Line Size ro+ +#define PCI_LATENCY_TIMER 0x0D // Latency Timer ro+ +#define PCI_HEADER_TYPE 0x0E // Header Type ro +#define PCI_BIST 0x0F // Built in self test rw + +// some pci command reg bitfields +#define PCI_CMD_BME 0x04 // Bus master function enable +#define PCI_CMD_MSE 0x02 // Memory Space Access enable +#define PCI_CMD_IOSE 0x01 // I/O space enable + +// Type 0 PCI offsets +#define PCI0_BASE_ADDR0 0x10 // Base Address 0 rw +#define PCI0_BASE_ADDR1 0x14 // Base Address 1 rw +#define PCI0_BASE_ADDR2 0x18 // Base Address 2 rw +#define PCI0_BASE_ADDR3 0x1C // Base Address 3 rw +#define PCI0_BASE_ADDR4 0x20 // Base Address 4 rw +#define PCI0_BASE_ADDR5 0x24 // Base Address 5 rw +#define PCI0_CIS 0x28 // CardBus CIS Pointer ro +#define PCI0_SUB_VENDOR_ID 0x2C // Sub-Vendor ID ro +#define PCI0_SUB_SYSTEM_ID 0x2E // Sub-System ID ro +#define PCI0_ROM_BASE_ADDR 0x30 // Expansion ROM Base Address rw +#define PCI0_RESERVED0 0x34 +#define PCI0_RESERVED1 0x38 +#define PCI0_INTERRUPT_LINE 0x3C // Interrupt Line rw +#define PCI0_INTERRUPT_PIN 0x3D // Interrupt Pin ro +#define PCI0_MINIMUM_GRANT 0x3E // Maximum Grant ro +#define PCI0_MAXIMUM_LATENCY 0x3F // Maximum Latency ro + +// Type 1 PCI offsets +#define PCI1_BASE_ADDR0 0x10 // Base Address 0 rw +#define PCI1_BASE_ADDR1 0x14 // Base Address 1 rw +#define PCI1_PRI_BUS_NUM 0x18 // Primary Bus Number rw +#define PCI1_SEC_BUS_NUM 0x19 // Secondary Bus Number rw +#define PCI1_SUB_BUS_NUM 0x1A // Subordinate Bus Number rw +#define PCI1_SEC_LAT_TIMER 0x1B // Secondary Latency Timer ro+ +#define PCI1_IO_BASE 0x1C // I/O Base rw +#define PCI1_IO_LIMIT 0x1D // I/O Limit rw +#define PCI1_SECONDARY_STATUS 0x1E // Secondary Status rw +#define PCI1_MEM_BASE 0x20 // Memory Base rw +#define PCI1_MEM_LIMIT 0x22 // Memory Limit rw +#define PCI1_PRF_MEM_BASE 0x24 // Prefetchable Memory Base rw +#define PCI1_PRF_MEM_LIMIT 0x26 // Prefetchable Memory Limit rw +#define PCI1_PRF_BASE_UPPER 0x28 // Prefetchable Base Upper 32 rw +#define PCI1_PRF_LIMIT_UPPER 0x2C // Prefetchable Limit Upper 32 rw +#define PCI1_IO_BASE_UPPER 0x30 // I/O Base Upper 16 bits rw +#define PCI1_IO_LIMIT_UPPER 0x32 // I/O Limit Upper 16 bits rw +#define PCI1_RESERVED 0x34 // Reserved ro +#define PCI1_ROM_BASE_ADDR 0x38 // Expansion ROM Base Address rw +#define PCI1_INTR_LINE 0x3C // Interrupt Line rw +#define PCI1_INTR_PIN 0x3D // Interrupt Pin ro +#define PCI1_BRIDGE_CTRL 0x3E // Bridge Control rw + +// Device specific offsets +#define PCI_DEVICE_SPECIFIC 0x40 // 192 bytes +#define PCI_CONFIG_SIZE 0xFF + +// Some Vendor IDs +#define PCI_VENDOR_DEC 0x1011 +#define PCI_VENDOR_NCR 0x101A +#define PCI_VENDOR_QLOGIC 0x1077 +#define PCI_VENDOR_SIMOS 0x1291 + +// Some Product IDs +#define PCI_PRODUCT_DEC_PZA 0x0008 +#define PCI_PRODUCT_NCR_810 0x0001 +#define PCI_PRODUCT_QLOGIC_ISP1020 0x1020 +#define PCI_PRODUCT_SIMOS_SIMOS 0x1291 +#define PCI_PRODUCT_SIMOS_ETHER 0x1292 + +#endif // __PCIREG_H__ diff --git a/src/dev/pitreg.h b/src/dev/pitreg.h new file mode 100644 index 000000000..d42925a41 --- /dev/null +++ b/src/dev/pitreg.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Miguel Serrano + */ + +/* @file + * Device register definitions for a device's PCI config space + */ + +#ifndef __PITREG_H__ +#define __PITREG_H__ + +#include <sys/types.h> + +// Control Word Format + +#define PIT_SEL_SHFT 0x6 +#define PIT_RW_SHFT 0x4 +#define PIT_MODE_SHFT 0x1 +#define PIT_BCD_SHFT 0x0 + +#define PIT_SEL_MASK 0x3 +#define PIT_RW_MASK 0x3 +#define PIT_MODE_MASK 0x7 +#define PIT_BCD_MASK 0x1 + +#define GET_CTRL_FIELD(x, s, m) (((x) >> s) & m) +#define GET_CTRL_SEL(x) GET_CTRL_FIELD(x, PIT_SEL_SHFT, PIT_SEL_MASK) +#define GET_CTRL_RW(x) GET_CTRL_FIELD(x, PIT_RW_SHFT, PIT_RW_MASK) +#define GET_CTRL_MODE(x) GET_CTRL_FIELD(x, PIT_MODE_SHFT, PIT_MODE_MASK) +#define GET_CTRL_BCD(x) GET_CTRL_FIELD(x, PIT_BCD_SHFT, PIT_BCD_MASK) + +#define PIT_READ_BACK 0x3 + +#define PIT_RW_LATCH_COMMAND 0x0 +#define PIT_RW_LSB_ONLY 0x1 +#define PIT_RW_MSB_ONLY 0x2 +#define PIT_RW_16BIT 0x3 + +#define PIT_MODE_INTTC 0x0 +#define PIT_MODE_ONESHOT 0x1 +#define PIT_MODE_RATEGEN 0x2 +#define PIT_MODE_SQWAVE 0x3 +#define PIT_MODE_SWSTROBE 0x4 +#define PIT_MODE_HWSTROBE 0x5 + +#define PIT_BCD_FALSE 0x0 +#define PIT_BCD_TRUE 0x1 + +#endif // __PITREG_H__ diff --git a/src/dev/pktfifo.cc b/src/dev/pktfifo.cc new file mode 100644 index 000000000..37f7ff680 --- /dev/null +++ b/src/dev/pktfifo.cc @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include "base/misc.hh" +#include "dev/pktfifo.hh" + +using namespace std; + +bool +PacketFifo::copyout(void *dest, int offset, int len) +{ + char *data = (char *)dest; + if (offset + len >= size()) + return false; + + list<EthPacketPtr>::iterator p = fifo.begin(); + list<EthPacketPtr>::iterator end = fifo.end(); + while (len > 0) { + while (offset >= (*p)->length) { + offset -= (*p)->length; + ++p; + } + + if (p == end) + panic("invalid fifo"); + + int size = min((*p)->length - offset, len); + memcpy(data, (*p)->data, size); + offset = 0; + len -= size; + data += size; + ++p; + } + + return true; +} + + +void +PacketFifo::serialize(const string &base, ostream &os) +{ + paramOut(os, base + ".size", _size); + paramOut(os, base + ".maxsize", _maxsize); + paramOut(os, base + ".reserved", _reserved); + paramOut(os, base + ".packets", fifo.size()); + + int i = 0; + list<EthPacketPtr>::iterator p = fifo.begin(); + list<EthPacketPtr>::iterator end = fifo.end(); + while (p != end) { + (*p)->serialize(csprintf("%s.packet%d", base, i), os); + ++p; + ++i; + } +} + +void +PacketFifo::unserialize(const string &base, Checkpoint *cp, + const string §ion) +{ + paramIn(cp, section, base + ".size", _size); +// paramIn(cp, section, base + ".maxsize", _maxsize); + paramIn(cp, section, base + ".reserved", _reserved); + int fifosize; + paramIn(cp, section, base + ".packets", fifosize); + + fifo.clear(); + + for (int i = 0; i < fifosize; ++i) { + EthPacketPtr p = new EthPacketData(16384); + p->unserialize(csprintf("%s.packet%d", base, i), cp, section); + fifo.push_back(p); + } +} diff --git a/src/dev/pktfifo.hh b/src/dev/pktfifo.hh new file mode 100644 index 000000000..45157ba41 --- /dev/null +++ b/src/dev/pktfifo.hh @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __DEV_PKTFIFO_HH__ +#define __DEV_PKTFIFO_HH__ + +#include <iosfwd> +#include <list> +#include <string> + +#include "dev/etherpkt.hh" +#include "sim/serialize.hh" + +class Checkpoint; +class PacketFifo +{ + public: + typedef std::list<EthPacketPtr> fifo_list; + typedef fifo_list::iterator iterator; + + protected: + std::list<EthPacketPtr> fifo; + int _maxsize; + int _size; + int _reserved; + + public: + explicit PacketFifo(int max) : _maxsize(max), _size(0), _reserved(0) {} + virtual ~PacketFifo() {} + + int packets() const { return fifo.size(); } + int maxsize() const { return _maxsize; } + int size() const { return _size; } + int reserved() const { return _reserved; } + int avail() const { return _maxsize - _size - _reserved; } + bool empty() const { return size() <= 0; } + bool full() const { return avail() <= 0; } + + int reserve(int len = 0) + { + _reserved += len; + assert(avail() >= 0); + return _reserved; + } + + iterator begin() { return fifo.begin(); } + iterator end() { return fifo.end(); } + + EthPacketPtr front() { return fifo.front(); } + + bool push(EthPacketPtr ptr) + { + assert(ptr->length); + assert(_reserved <= ptr->length); + assert(ptr->slack == 0); + if (avail() < ptr->length - _reserved) + return false; + + _size += ptr->length; + fifo.push_back(ptr); + _reserved = 0; + return true; + } + + void pop() + { + if (empty()) + return; + + EthPacketPtr &packet = fifo.front(); + _size -= packet->length; + _size -= packet->slack; + packet->slack = 0; + packet = NULL; + fifo.pop_front(); + } + + void clear() + { + for (iterator i = begin(); i != end(); ++i) + (*i)->slack = 0; + fifo.clear(); + _size = 0; + _reserved = 0; + } + + void remove(iterator i) + { + EthPacketPtr &packet = *i; + if (i != fifo.begin()) { + iterator prev = i; + --prev; + assert(prev != fifo.end()); + (*prev)->slack += packet->length; + } else { + _size -= packet->length; + _size -= packet->slack; + } + + packet->slack = 0; + packet = NULL; + fifo.erase(i); + } + + bool copyout(void *dest, int offset, int len); + + int countPacketsBefore(iterator end) + { + iterator i = fifo.begin(); + int count = 0; + + while (i != end) { + ++count; + ++i; + } + + return count; + } + + int countPacketsAfter(iterator i) + { + iterator end = fifo.end(); + int count = 0; + + while (i != end) { + ++count; + ++i; + } + + return count; + } + + +/** + * Serialization stuff + */ + public: + void serialize(const std::string &base, std::ostream &os); + void unserialize(const std::string &base, + Checkpoint *cp, const std::string §ion); +}; + +#endif // __DEV_PKTFIFO_HH__ diff --git a/src/dev/platform.cc b/src/dev/platform.cc new file mode 100644 index 000000000..07288249c --- /dev/null +++ b/src/dev/platform.cc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Nathan Binkert + */ + +#include "base/misc.hh" +#include "dev/platform.hh" +#include "sim/builder.hh" +#include "sim/sim_exit.hh" + +using namespace std; +using namespace TheISA; + +Platform::Platform(const string &name, IntrControl *intctrl) + : SimObject(name), intrctrl(intctrl) +{ +} + +Platform::~Platform() +{ +} + +void +Platform::postPciInt(int line) +{ + panic("No PCI interrupt support in platform."); +} + +void +Platform::clearPciInt(int line) +{ + panic("No PCI interrupt support in platform."); +} + +Addr +Platform::pciToDma(Addr pciAddr) const +{ + panic("No PCI dma support in platform."); +} + +void +Platform::registerPciDevice(uint8_t bus, uint8_t dev, uint8_t func, uint8_t intr) +{ + uint32_t bdf = bus << 16 | dev << 8 | func << 0; + if (pciDevices.find(bdf) != pciDevices.end()) + fatal("Two PCI devices have same bus:device:function\n"); + + if (intLines.test(intr)) + fatal("Two PCI devices have same interrupt line: %d\n", intr); + + pciDevices.insert(bdf); + + intLines.set(intr); +} + + +DEFINE_SIM_OBJECT_CLASS_NAME("Platform", Platform) + diff --git a/src/dev/platform.hh b/src/dev/platform.hh new file mode 100644 index 000000000..1940dcad6 --- /dev/null +++ b/src/dev/platform.hh @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Andrew Schultz + * Nathan Binkert + */ + +/** + * @file + * Generic interface for platforms + */ + +#ifndef __DEV_PLATFORM_HH__ +#define __DEV_PLATFORM_HH__ + +#include <bitset> +#include <set> + +#include "sim/sim_object.hh" +#include "arch/isa_traits.hh" + +class PciConfigAll; +class IntrControl; +class SimConsole; +class Uart; +class System; + +class Platform : public SimObject +{ + public: + /** Pointer to the interrupt controller */ + IntrControl *intrctrl; + + /** Pointer to the UART, set by the uart */ + Uart *uart; + + /** Pointer to the system for info about the memory system. */ + System *system; + + public: + Platform(const std::string &name, IntrControl *intctrl); + virtual ~Platform(); + virtual void postConsoleInt() = 0; + virtual void clearConsoleInt() = 0; + virtual Tick intrFrequency() = 0; + virtual void postPciInt(int line); + virtual void clearPciInt(int line); + virtual Addr pciToDma(Addr pciAddr) const; + virtual Addr calcConfigAddr(int bus, int dev, int func) = 0; + virtual void registerPciDevice(uint8_t bus, uint8_t dev, uint8_t func, + uint8_t intr); + + private: + std::bitset<256> intLines; + std::set<uint32_t> pciDevices; + +}; + +#endif // __DEV_PLATFORM_HH__ diff --git a/src/dev/rtcreg.h b/src/dev/rtcreg.h new file mode 100644 index 000000000..37255777b --- /dev/null +++ b/src/dev/rtcreg.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Miguel Serrano + * Nathan Binkert + */ + +#define RTC_SEC 0x00 +#define RTC_SEC_ALRM 0x01 +#define RTC_MIN 0x02 +#define RTC_MIN_ALRM 0x03 +#define RTC_HR 0x04 +#define RTC_HR_ALRM 0x05 +#define RTC_DOW 0x06 +#define RTC_DOM 0x07 +#define RTC_MON 0x08 +#define RTC_YEAR 0x09 + +#define RTC_STAT_REGA 0x0A +#define RTCA_1024HZ 0x06 /* 1024Hz periodic interrupt frequency */ +#define RTCA_32768HZ 0x20 /* 22-stage divider, 32.768KHz timebase */ +#define RTCA_UIP 0x80 /* 1 = date and time update in progress */ + +#define RTC_STAT_REGB 0x0B +#define RTCB_DST 0x01 /* USA Daylight Savings Time enable */ +#define RTCB_24HR 0x02 /* 0 = 12 hours, 1 = 24 hours */ +#define RTCB_BIN 0x04 /* 0 = BCD, 1 = Binary coded time */ +#define RTCB_SQWE 0x08 /* 1 = output sqare wave at SQW pin */ +#define RTCB_UPDT_IE 0x10 /* 1 = enable update-ended interrupt */ +#define RTCB_ALRM_IE 0x20 /* 1 = enable alarm interrupt */ +#define RTCB_PRDC_IE 0x40 /* 1 = enable periodic clock interrupt */ +#define RTCB_NO_UPDT 0x80 /* stop clock updates */ + +#define RTC_STAT_REGC 0x0C +#define RTC_STAT_REGD 0x0D + diff --git a/src/dev/simconsole.cc b/src/dev/simconsole.cc new file mode 100644 index 000000000..77aafd9fa --- /dev/null +++ b/src/dev/simconsole.cc @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ali Saidi + */ + +/* @file + * Implements the user interface to a serial console + */ + +#include <sys/ioctl.h> +#include <sys/termios.h> +#include <sys/types.h> +#include <errno.h> +#include <poll.h> +#include <unistd.h> + +#include <iostream> +#include <fstream> +#include <sstream> +#include <string> + +#include "base/misc.hh" +#include "base/output.hh" +#include "base/socket.hh" +#include "base/trace.hh" +#include "dev/platform.hh" +#include "dev/simconsole.hh" +#include "dev/uart.hh" +#include "sim/builder.hh" + +using namespace std; + +//////////////////////////////////////////////////////////////////////// +// +// + +SimConsole::Event::Event(SimConsole *c, int fd, int e) + : PollEvent(fd, e), cons(c) +{ +} + +void +SimConsole::Event::process(int revent) +{ + if (revent & POLLIN) + cons->data(); + else if (revent & POLLNVAL) + cons->detach(); +} + +SimConsole::SimConsole(const string &name, ostream *os, int num) + : SimObject(name), event(NULL), number(num), in_fd(-1), out_fd(-1), + listener(NULL), txbuf(16384), rxbuf(16384), outfile(os) +#if TRACING_ON == 1 + , linebuf(16384) +#endif +{ + if (outfile) + outfile->setf(ios::unitbuf); +} + +SimConsole::~SimConsole() +{ + close(); +} + +void +SimConsole::close() +{ + if (in_fd != -1) + ::close(in_fd); + + if (out_fd != in_fd && out_fd != -1) + ::close(out_fd); +} + +void +SimConsole::attach(int in, int out, ConsoleListener *l) +{ + in_fd = in; + out_fd = out; + listener = l; + + event = new Event(this, in, POLLIN); + pollQueue.schedule(event); + + stringstream stream; + ccprintf(stream, "==== m5 slave console: Console %d ====", number); + + // we need an actual carriage return followed by a newline for the + // terminal + stream << "\r\n"; + + write((const uint8_t *)stream.str().c_str(), stream.str().size()); + + + DPRINTFN("attach console %d\n", number); + + txbuf.readall(out); +} + +void +SimConsole::detach() +{ + close(); + in_fd = -1; + out_fd = -1; + + pollQueue.remove(event); + + if (listener) { + listener->add(this); + listener = NULL; + } + + DPRINTFN("detach console %d\n", number); +} + +void +SimConsole::data() +{ + uint8_t buf[1024]; + int len; + + len = read(buf, sizeof(buf)); + if (len) { + rxbuf.write((char *)buf, len); + // Inform the UART there is data available + uart->dataAvailable(); + } +} + +size_t +SimConsole::read(uint8_t *buf, size_t len) +{ + if (in_fd < 0) + panic("Console not properly attached.\n"); + + size_t ret; + do { + ret = ::read(in_fd, buf, len); + } while (ret == -1 && errno == EINTR); + + + if (ret < 0) + DPRINTFN("Read failed.\n"); + + if (ret <= 0) { + detach(); + return 0; + } + + return ret; +} + +// Console output. +size_t +SimConsole::write(const uint8_t *buf, size_t len) +{ + if (out_fd < 0) + panic("Console not properly attached.\n"); + + size_t ret; + for (;;) { + ret = ::write(out_fd, buf, len); + + if (ret >= 0) + break; + + if (errno != EINTR) + detach(); + } + + return ret; +} + +#define MORE_PENDING (ULL(1) << 61) +#define RECEIVE_SUCCESS (ULL(0) << 62) +#define RECEIVE_NONE (ULL(2) << 62) +#define RECEIVE_ERROR (ULL(3) << 62) + +uint8_t +SimConsole::in() +{ + bool empty; + uint8_t c; + + empty = rxbuf.empty(); + assert(!empty); + rxbuf.read((char *)&c, 1); + empty = rxbuf.empty(); + + + DPRINTF(ConsoleVerbose, "in: \'%c\' %#02x more: %d\n", + isprint(c) ? c : ' ', c, !empty); + + return c; +} + +uint64_t +SimConsole::console_in() +{ + uint64_t value; + + if (dataAvailable()) { + value = RECEIVE_SUCCESS | in(); + if (!rxbuf.empty()) + value |= MORE_PENDING; + } else { + value = RECEIVE_NONE; + } + + DPRINTF(ConsoleVerbose, "console_in: return: %#x\n", value); + + return value; +} + +void +SimConsole::out(char c) +{ +#if TRACING_ON == 1 + if (DTRACE(Console)) { + static char last = '\0'; + + if (c != '\n' && c != '\r' || + last != '\n' && last != '\r') { + if (c == '\n' || c == '\r') { + int size = linebuf.size(); + char *buffer = new char[size + 1]; + linebuf.read(buffer, size); + buffer[size] = '\0'; + DPRINTF(Console, "%s\n", buffer); + delete [] buffer; + } else { + linebuf.write(c); + } + } + + last = c; + } +#endif + + txbuf.write(c); + + if (out_fd >= 0) + write(c); + + if (outfile) + outfile->write(&c, 1); + + DPRINTF(ConsoleVerbose, "out: \'%c\' %#02x\n", + isprint(c) ? c : ' ', (int)c); + +} + + +void +SimConsole::serialize(ostream &os) +{ +} + +void +SimConsole::unserialize(Checkpoint *cp, const std::string §ion) +{ +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimConsole) + + SimObjectParam<ConsoleListener *> listener; + SimObjectParam<IntrControl *> intr_control; + Param<string> output; + Param<bool> append_name; + Param<int> number; + +END_DECLARE_SIM_OBJECT_PARAMS(SimConsole) + +BEGIN_INIT_SIM_OBJECT_PARAMS(SimConsole) + + INIT_PARAM(listener, "console listener"), + INIT_PARAM(intr_control, "interrupt controller"), + INIT_PARAM(output, "file to dump output to"), + INIT_PARAM_DFLT(append_name, "append name() to filename", true), + INIT_PARAM_DFLT(number, "console number", 0) + +END_INIT_SIM_OBJECT_PARAMS(SimConsole) + +CREATE_SIM_OBJECT(SimConsole) +{ + string filename = output; + ostream *stream = NULL; + + if (!filename.empty()) { + if (append_name) + filename += "." + getInstanceName(); + stream = simout.find(filename); + } + + SimConsole *console = new SimConsole(getInstanceName(), stream, number); + ((ConsoleListener *)listener)->add(console); + + return console; +} + +REGISTER_SIM_OBJECT("SimConsole", SimConsole) + +//////////////////////////////////////////////////////////////////////// +// +// + +ConsoleListener::ConsoleListener(const string &name) + : SimObject(name), event(NULL) +{} + +ConsoleListener::~ConsoleListener() +{ + if (event) + delete event; +} + +void +ConsoleListener::Event::process(int revent) +{ + listener->accept(); +} + +/////////////////////////////////////////////////////////////////////// +// socket creation and console attach +// + +void +ConsoleListener::listen(int port) +{ + while (!listener.listen(port, true)) { + DPRINTF(Console, + ": can't bind address console port %d inuse PID %d\n", + port, getpid()); + port++; + } + + ccprintf(cerr, "Listening for console connection on port %d\n", port); + + event = new Event(this, listener.getfd(), POLLIN); + pollQueue.schedule(event); +} + +void +ConsoleListener::add(SimConsole *cons) +{ ConsoleList.push_back(cons);} + +void +ConsoleListener::accept() +{ + if (!listener.islistening()) + panic("%s: cannot accept a connection if not listening!", name()); + + int sfd = listener.accept(true); + if (sfd != -1) { + iter_t i = ConsoleList.begin(); + iter_t end = ConsoleList.end(); + if (i == end) { + close(sfd); + } else { + (*i)->attach(sfd, this); + i = ConsoleList.erase(i); + } + } +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(ConsoleListener) + + Param<int> port; + +END_DECLARE_SIM_OBJECT_PARAMS(ConsoleListener) + +BEGIN_INIT_SIM_OBJECT_PARAMS(ConsoleListener) + + INIT_PARAM_DFLT(port, "listen port", 3456) + +END_INIT_SIM_OBJECT_PARAMS(ConsoleListener) + +CREATE_SIM_OBJECT(ConsoleListener) +{ + ConsoleListener *listener = new ConsoleListener(getInstanceName()); + listener->listen(port); + + return listener; +} + +REGISTER_SIM_OBJECT("ConsoleListener", ConsoleListener) diff --git a/src/dev/simconsole.hh b/src/dev/simconsole.hh new file mode 100644 index 000000000..ec99c6028 --- /dev/null +++ b/src/dev/simconsole.hh @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ali Saidi + */ + +/* @file + * User Console Interface + */ + +#ifndef __CONSOLE_HH__ +#define __CONSOLE_HH__ + +#include <iostream> + +#include "base/circlebuf.hh" +#include "cpu/intr_control.hh" +#include "base/pollevent.hh" +#include "base/socket.hh" +#include "sim/sim_object.hh" + +class ConsoleListener; +class Uart; + +class SimConsole : public SimObject +{ + public: + Uart *uart; + + protected: + class Event : public PollEvent + { + protected: + SimConsole *cons; + + public: + Event(SimConsole *c, int fd, int e); + void process(int revent); + }; + + friend class Event; + Event *event; + + protected: + int number; + int in_fd; + int out_fd; + ConsoleListener *listener; + + public: + SimConsole(const std::string &name, std::ostream *os, int num); + ~SimConsole(); + + protected: + CircleBuf txbuf; + CircleBuf rxbuf; + std::ostream *outfile; +#if TRACING_ON == 1 + CircleBuf linebuf; +#endif + + public: + /////////////////////// + // Terminal Interface + + void attach(int fd, ConsoleListener *l = NULL) { attach(fd, fd, l); } + void attach(int in, int out, ConsoleListener *l = NULL); + void detach(); + + void data(); + + void close(); + void read(uint8_t &c) { read(&c, 1); } + size_t read(uint8_t *buf, size_t len); + void write(uint8_t c) { write(&c, 1); } + size_t write(const uint8_t *buf, size_t len); + + public: + ///////////////// + // OS interface + + // Get a character from the console. + uint8_t in(); + + // get a character from the console in the console specific format + // corresponds to GETC: + // retval<63:61> + // 000: success: character received + // 001: success: character received, more pending + // 100: failure: no character ready + // 110: failure: character received with error + // 111: failure: character received with error, more pending + // retval<31:0> + // character read from console + // + // Interrupts are cleared when the buffer is empty. + uint64_t console_in(); + + // Send a character to the console + void out(char c); + + //Ask the console if data is available + bool dataAvailable() { return !rxbuf.empty(); } + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +class ConsoleListener : public SimObject +{ + protected: + class Event : public PollEvent + { + protected: + ConsoleListener *listener; + + public: + Event(ConsoleListener *l, int fd, int e) + : PollEvent(fd, e), listener(l) {} + void process(int revent); + }; + + friend class Event; + Event *event; + + typedef std::list<SimConsole *> list_t; + typedef list_t::iterator iter_t; + list_t ConsoleList; + + protected: + ListenSocket listener; + + public: + ConsoleListener(const std::string &name); + ~ConsoleListener(); + + void add(SimConsole *cons); + + void accept(); + void listen(int port); +}; + +#endif // __CONSOLE_HH__ diff --git a/src/dev/simple_disk.cc b/src/dev/simple_disk.cc new file mode 100644 index 000000000..e29bfa726 --- /dev/null +++ b/src/dev/simple_disk.cc @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/* @file + * Simple disk interface for the system console + */ + +#include <sys/types.h> +#include <sys/uio.h> +#include <fcntl.h> +#include <unistd.h> + +#include <cstring> +#include <string> + +#include "base/misc.hh" +#include "base/trace.hh" +#include "dev/disk_image.hh" +#include "dev/simple_disk.hh" +#include "mem/port.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; + +SimpleDisk::SimpleDisk(const string &name, System *sys, DiskImage *img) + : SimObject(name), system(sys), image(img) +{} + +SimpleDisk::~SimpleDisk() +{} + + +void +SimpleDisk::read(Addr addr, baddr_t block, int count) const +{ + uint8_t *data = new uint8_t[SectorSize * count]; + + if (count & (SectorSize - 1)) + panic("Not reading a multiple of a sector (count = %d)", count); + + for (int i = 0, j = 0; i < count; i += SectorSize, j++) + image->read(data + i, block + j); + + system->functionalPort.writeBlob(addr, data, count); + + DPRINTF(SimpleDisk, "read block=%#x len=%d\n", (uint64_t)block, count); + DDUMP(SimpleDiskData, data, count); + + delete data; +} + +void +SimpleDisk::write(Addr addr, baddr_t block, int count) +{ + panic("unimplemented!\n"); + +#if 0 + uint8_t *data = physmem->dma_addr(addr, count); + if (!data) + panic("dma out of range! write addr=%#x count=%d\n", addr, count); + + image->write(data, block, count); +#endif +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(SimpleDisk) + + SimObjectParam<System *> system; + SimObjectParam<DiskImage *> disk; + +END_DECLARE_SIM_OBJECT_PARAMS(SimpleDisk) + +BEGIN_INIT_SIM_OBJECT_PARAMS(SimpleDisk) + + INIT_PARAM(system, "System pointer"), + INIT_PARAM(disk, "Disk Image") + +END_INIT_SIM_OBJECT_PARAMS(SimpleDisk) + +CREATE_SIM_OBJECT(SimpleDisk) +{ + return new SimpleDisk(getInstanceName(), system, disk); +} + +REGISTER_SIM_OBJECT("SimpleDisk", SimpleDisk) diff --git a/src/dev/simple_disk.hh b/src/dev/simple_disk.hh new file mode 100644 index 000000000..9f588cb95 --- /dev/null +++ b/src/dev/simple_disk.hh @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/* @file + * Simple disk interface for the system console + */ + +#ifndef __DEV_SIMPLE_DISK_HH__ +#define __DEV_SIMPLE_DISK_HH__ + +#include "sim/sim_object.hh" +#include "arch/isa_traits.hh" + +class DiskImage; +class System; + +/* + * Trivial interface to a disk image used by the System Console + */ +class SimpleDisk : public SimObject +{ + public: + typedef uint64_t baddr_t; + + protected: + System *system; + DiskImage *image; + + public: + SimpleDisk(const std::string &name, System *sys, DiskImage *img); + ~SimpleDisk(); + + void read(Addr addr, baddr_t block, int count) const; + void write(Addr addr, baddr_t block, int count); +}; + +#endif // __DEV_SIMPLE_DISK_HH__ diff --git a/src/dev/sinic.cc b/src/dev/sinic.cc new file mode 100644 index 000000000..61fb3aa24 --- /dev/null +++ b/src/dev/sinic.cc @@ -0,0 +1,1765 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <deque> +#include <limits> +#include <string> + +#include "base/inet.hh" +#include "cpu/thread_context.hh" +#include "cpu/intr_control.hh" +#include "dev/etherlink.hh" +#include "dev/sinic.hh" +#include "mem/packet.hh" +#include "sim/builder.hh" +#include "sim/debug.hh" +#include "sim/eventq.hh" +#include "sim/host.hh" +#include "sim/stats.hh" +#include "arch/vtophys.hh" + +using namespace Net; +using namespace TheISA; + +namespace Sinic { + +const char *RxStateStrings[] = +{ + "rxIdle", + "rxFifoBlock", + "rxBeginCopy", + "rxCopy", + "rxCopyDone" +}; + +const char *TxStateStrings[] = +{ + "txIdle", + "txFifoBlock", + "txBeginCopy", + "txCopy", + "txCopyDone" +}; + + +/////////////////////////////////////////////////////////////////////// +// +// Sinic PCI Device +// +Base::Base(Params *p) + : PciDev(p), rxEnable(false), txEnable(false), clock(p->clock), + intrDelay(p->intr_delay), intrTick(0), cpuIntrEnable(false), + cpuPendingIntr(false), intrEvent(0), interface(NULL) +{ +} + +Device::Device(Params *p) + : Base(p), rxUnique(0), txUnique(0), + virtualRegs(p->virtual_count < 1 ? 1 : p->virtual_count), + rxFifo(p->rx_fifo_size), txFifo(p->tx_fifo_size), + rxKickTick(0), txKickTick(0), + txEvent(this), rxDmaEvent(this), txDmaEvent(this), + dmaReadDelay(p->dma_read_delay), dmaReadFactor(p->dma_read_factor), + dmaWriteDelay(p->dma_write_delay), dmaWriteFactor(p->dma_write_factor) +{ + reset(); + +} + +Device::~Device() +{} + +void +Device::regStats() +{ + rxBytes + .name(name() + ".rxBytes") + .desc("Bytes Received") + .prereq(rxBytes) + ; + + rxBandwidth + .name(name() + ".rxBandwidth") + .desc("Receive Bandwidth (bits/s)") + .precision(0) + .prereq(rxBytes) + ; + + rxPackets + .name(name() + ".rxPackets") + .desc("Number of Packets Received") + .prereq(rxBytes) + ; + + rxPacketRate + .name(name() + ".rxPPS") + .desc("Packet Reception Rate (packets/s)") + .precision(0) + .prereq(rxBytes) + ; + + rxIpPackets + .name(name() + ".rxIpPackets") + .desc("Number of IP Packets Received") + .prereq(rxBytes) + ; + + rxTcpPackets + .name(name() + ".rxTcpPackets") + .desc("Number of Packets Received") + .prereq(rxBytes) + ; + + rxUdpPackets + .name(name() + ".rxUdpPackets") + .desc("Number of UDP Packets Received") + .prereq(rxBytes) + ; + + rxIpChecksums + .name(name() + ".rxIpChecksums") + .desc("Number of rx IP Checksums done by device") + .precision(0) + .prereq(rxBytes) + ; + + rxTcpChecksums + .name(name() + ".rxTcpChecksums") + .desc("Number of rx TCP Checksums done by device") + .precision(0) + .prereq(rxBytes) + ; + + rxUdpChecksums + .name(name() + ".rxUdpChecksums") + .desc("Number of rx UDP Checksums done by device") + .precision(0) + .prereq(rxBytes) + ; + + totBandwidth + .name(name() + ".totBandwidth") + .desc("Total Bandwidth (bits/s)") + .precision(0) + .prereq(totBytes) + ; + + totPackets + .name(name() + ".totPackets") + .desc("Total Packets") + .precision(0) + .prereq(totBytes) + ; + + totBytes + .name(name() + ".totBytes") + .desc("Total Bytes") + .precision(0) + .prereq(totBytes) + ; + + totPacketRate + .name(name() + ".totPPS") + .desc("Total Tranmission Rate (packets/s)") + .precision(0) + .prereq(totBytes) + ; + + txBytes + .name(name() + ".txBytes") + .desc("Bytes Transmitted") + .prereq(txBytes) + ; + + txBandwidth + .name(name() + ".txBandwidth") + .desc("Transmit Bandwidth (bits/s)") + .precision(0) + .prereq(txBytes) + ; + + txPackets + .name(name() + ".txPackets") + .desc("Number of Packets Transmitted") + .prereq(txBytes) + ; + + txPacketRate + .name(name() + ".txPPS") + .desc("Packet Tranmission Rate (packets/s)") + .precision(0) + .prereq(txBytes) + ; + + txIpPackets + .name(name() + ".txIpPackets") + .desc("Number of IP Packets Transmitted") + .prereq(txBytes) + ; + + txTcpPackets + .name(name() + ".txTcpPackets") + .desc("Number of TCP Packets Transmitted") + .prereq(txBytes) + ; + + txUdpPackets + .name(name() + ".txUdpPackets") + .desc("Number of Packets Transmitted") + .prereq(txBytes) + ; + + txIpChecksums + .name(name() + ".txIpChecksums") + .desc("Number of tx IP Checksums done by device") + .precision(0) + .prereq(txBytes) + ; + + txTcpChecksums + .name(name() + ".txTcpChecksums") + .desc("Number of tx TCP Checksums done by device") + .precision(0) + .prereq(txBytes) + ; + + txUdpChecksums + .name(name() + ".txUdpChecksums") + .desc("Number of tx UDP Checksums done by device") + .precision(0) + .prereq(txBytes) + ; + + txBandwidth = txBytes * Stats::constant(8) / simSeconds; + rxBandwidth = rxBytes * Stats::constant(8) / simSeconds; + totBandwidth = txBandwidth + rxBandwidth; + totBytes = txBytes + rxBytes; + totPackets = txPackets + rxPackets; + txPacketRate = txPackets / simSeconds; + rxPacketRate = rxPackets / simSeconds; +} + +void +Device::prepareIO(int cpu, int index) +{ + int size = virtualRegs.size(); + if (index > size) + panic("Trying to access a vnic that doesn't exist %d > %d\n", + index, size); +} + +void +Device::prepareRead(int cpu, int index) +{ + using namespace Regs; + prepareIO(cpu, index); + + VirtualReg &vnic = virtualRegs[index]; + + // update rx registers + uint64_t rxdone = vnic.RxDone; + rxdone = set_RxDone_Packets(rxdone, rxFifo.countPacketsAfter(rxFifoPtr)); + rxdone = set_RxDone_Empty(rxdone, rxFifo.empty()); + rxdone = set_RxDone_High(rxdone, rxFifo.size() > regs.RxFifoMark); + rxdone = set_RxDone_NotHigh(rxdone, rxLow); + regs.RxData = vnic.RxData; + regs.RxDone = rxdone; + regs.RxWait = rxdone; + + // update tx regsiters + uint64_t txdone = vnic.TxDone; + txdone = set_TxDone_Packets(txdone, txFifo.packets()); + txdone = set_TxDone_Full(txdone, txFifo.avail() < regs.TxMaxCopy); + txdone = set_TxDone_Low(txdone, txFifo.size() < regs.TxFifoMark); + regs.TxData = vnic.TxData; + regs.TxDone = txdone; + regs.TxWait = txdone; +} + +void +Device::prepareWrite(int cpu, int index) +{ + prepareIO(cpu, index); +} + +/** + * I/O read of device register + */ +Tick +Device::read(Packet *pkt) +{ + assert(config.command & PCI_CMD_MSE); + assert(pkt->getAddr() >= BARAddrs[0] && pkt->getSize() < BARSize[0]); + + int cpu = pkt->req->getCpuNum(); + Addr daddr = pkt->getAddr() - BARAddrs[0]; + Addr index = daddr >> Regs::VirtualShift; + Addr raddr = daddr & Regs::VirtualMask; + + pkt->allocate(); + + if (!regValid(raddr)) + panic("invalid register: cpu=%d vnic=%d da=%#x pa=%#x size=%d", + cpu, index, daddr, pkt->getAddr(), pkt->getSize()); + + const Regs::Info &info = regInfo(raddr); + if (!info.read) + panic("read %s (write only): " + "cpu=%d vnic=%d da=%#x pa=%#x size=%d", + info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize()); + + panic("read %s (invalid size): " + "cpu=%d vnic=%d da=%#x pa=%#x size=%d", + info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize()); + + prepareRead(cpu, index); + + uint64_t value = 0; + if (pkt->getSize() == 4) { + uint32_t reg = regData32(raddr); + pkt->set(reg); + value = reg; + } + + if (pkt->getSize() == 8) { + uint64_t reg = regData64(raddr); + pkt->set(reg); + value = reg; + } + + DPRINTF(EthernetPIO, + "read %s: cpu=%d vnic=%d da=%#x pa=%#x size=%d val=%#x\n", + info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize(), value); + + // reading the interrupt status register has the side effect of + // clearing it + if (raddr == Regs::IntrStatus) + devIntrClear(); + + return pioDelay; +} + +/** + * IPR read of device register + + Fault +Device::iprRead(Addr daddr, int cpu, uint64_t &result) +{ + if (!regValid(daddr)) + panic("invalid address: da=%#x", daddr); + + const Regs::Info &info = regInfo(daddr); + if (!info.read) + panic("reading %s (write only): cpu=%d da=%#x", info.name, cpu, daddr); + + DPRINTF(EthernetPIO, "IPR read %s: cpu=%d da=%#x\n", + info.name, cpu, daddr); + + prepareRead(cpu, 0); + + if (info.size == 4) + result = regData32(daddr); + + if (info.size == 8) + result = regData64(daddr); + + DPRINTF(EthernetPIO, "IPR read %s: cpu=%s da=%#x val=%#x\n", + info.name, cpu, result); + + return NoFault; +} +*/ +/** + * I/O write of device register + */ +Tick +Device::write(Packet *pkt) +{ + assert(config.command & PCI_CMD_MSE); + assert(pkt->getAddr() >= BARAddrs[0] && pkt->getSize() < BARSize[0]); + + int cpu = pkt->req->getCpuNum(); + Addr daddr = pkt->getAddr() - BARAddrs[0]; + Addr index = daddr >> Regs::VirtualShift; + Addr raddr = daddr & Regs::VirtualMask; + + if (!regValid(raddr)) + panic("invalid register: cpu=%d, da=%#x pa=%#x size=%d", + cpu, daddr, pkt->getAddr(), pkt->getSize()); + + const Regs::Info &info = regInfo(raddr); + if (!info.write) + panic("write %s (read only): " + "cpu=%d vnic=%d da=%#x pa=%#x size=%d", + info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize()); + + if (pkt->getSize() != info.size) + panic("write %s (invalid size): " + "cpu=%d vnic=%d da=%#x pa=%#x size=%d", + info.name, cpu, index, daddr, pkt->getAddr(), pkt->getSize()); + + VirtualReg &vnic = virtualRegs[index]; + + DPRINTF(EthernetPIO, + "write %s vnic %d: cpu=%d val=%#x da=%#x pa=%#x size=%d\n", + info.name, index, cpu, info.size == 4 ? pkt->get<uint32_t>() : + pkt->get<uint64_t>(), daddr, pkt->getAddr(), pkt->getSize()); + + prepareWrite(cpu, index); + + switch (raddr) { + case Regs::Config: + changeConfig(pkt->get<uint32_t>()); + break; + + case Regs::Command: + command(pkt->get<uint32_t>()); + break; + + case Regs::IntrStatus: + devIntrClear(regs.IntrStatus & pkt->get<uint32_t>()); + break; + + case Regs::IntrMask: + devIntrChangeMask(pkt->get<uint32_t>()); + break; + + case Regs::RxData: + if (Regs::get_RxDone_Busy(vnic.RxDone)) + panic("receive machine busy with another request! rxState=%s", + RxStateStrings[rxState]); + + vnic.rxUnique = rxUnique++; + vnic.RxDone = Regs::RxDone_Busy; + vnic.RxData = pkt->get<uint64_t>(); + + if (Regs::get_RxData_Vaddr(pkt->get<uint64_t>())) { + panic("vtophys not implemented in newmem"); +/* Addr vaddr = Regs::get_RxData_Addr(reg64); + Addr paddr = vtophys(req->xc, vaddr); + DPRINTF(EthernetPIO, "write RxData vnic %d (rxunique %d): " + "vaddr=%#x, paddr=%#x\n", + index, vnic.rxUnique, vaddr, paddr); + + vnic.RxData = Regs::set_RxData_Addr(vnic.RxData, paddr);*/ + } else { + DPRINTF(EthernetPIO, "write RxData vnic %d (rxunique %d)\n", + index, vnic.rxUnique); + } + + if (vnic.rxPacket == rxFifo.end()) { + DPRINTF(EthernetPIO, "request new packet...appending to rxList\n"); + rxList.push_back(index); + } else { + DPRINTF(EthernetPIO, "packet exists...appending to rxBusy\n"); + rxBusy.push_back(index); + } + + if (rxEnable && (rxState == rxIdle || rxState == rxFifoBlock)) { + rxState = rxFifoBlock; + rxKick(); + } + break; + + case Regs::TxData: + if (Regs::get_TxDone_Busy(vnic.TxDone)) + panic("transmit machine busy with another request! txState=%s", + TxStateStrings[txState]); + + vnic.txUnique = txUnique++; + vnic.TxDone = Regs::TxDone_Busy; + + if (Regs::get_TxData_Vaddr(pkt->get<uint64_t>())) { + panic("vtophys won't work here in newmem.\n"); + /*Addr vaddr = Regs::get_TxData_Addr(reg64); + Addr paddr = vtophys(req->xc, vaddr); + DPRINTF(EthernetPIO, "write TxData vnic %d (rxunique %d): " + "vaddr=%#x, paddr=%#x\n", + index, vnic.txUnique, vaddr, paddr); + + vnic.TxData = Regs::set_TxData_Addr(vnic.TxData, paddr);*/ + } else { + DPRINTF(EthernetPIO, "write TxData vnic %d (rxunique %d)\n", + index, vnic.txUnique); + } + + if (txList.empty() || txList.front() != index) + txList.push_back(index); + if (txEnable && txState == txIdle && txList.front() == index) { + txState = txFifoBlock; + txKick(); + } + break; + } + + return pioDelay; +} + +void +Device::devIntrPost(uint32_t interrupts) +{ + if ((interrupts & Regs::Intr_Res)) + panic("Cannot set a reserved interrupt"); + + regs.IntrStatus |= interrupts; + + DPRINTF(EthernetIntr, + "interrupt written to intStatus: intr=%#x status=%#x mask=%#x\n", + interrupts, regs.IntrStatus, regs.IntrMask); + + interrupts = regs.IntrStatus & regs.IntrMask; + + // Intr_RxHigh is special, we only signal it if we've emptied the fifo + // and then filled it above the high watermark + if (rxEmpty) + rxEmpty = false; + else + interrupts &= ~Regs::Intr_RxHigh; + + // Intr_TxLow is special, we only signal it if we've filled up the fifo + // and then dropped below the low watermark + if (txFull) + txFull = false; + else + interrupts &= ~Regs::Intr_TxLow; + + if (interrupts) { + Tick when = curTick; + if ((interrupts & Regs::Intr_NoDelay) == 0) + when += intrDelay; + cpuIntrPost(when); + } +} + +void +Device::devIntrClear(uint32_t interrupts) +{ + if ((interrupts & Regs::Intr_Res)) + panic("Cannot clear a reserved interrupt"); + + regs.IntrStatus &= ~interrupts; + + DPRINTF(EthernetIntr, + "interrupt cleared from intStatus: intr=%x status=%x mask=%x\n", + interrupts, regs.IntrStatus, regs.IntrMask); + + if (!(regs.IntrStatus & regs.IntrMask)) + cpuIntrClear(); +} + +void +Device::devIntrChangeMask(uint32_t newmask) +{ + if (regs.IntrMask == newmask) + return; + + regs.IntrMask = newmask; + + DPRINTF(EthernetIntr, + "interrupt mask changed: intStatus=%x intMask=%x masked=%x\n", + regs.IntrStatus, regs.IntrMask, regs.IntrStatus & regs.IntrMask); + + if (regs.IntrStatus & regs.IntrMask) + cpuIntrPost(curTick); + else + cpuIntrClear(); +} + +void +Base::cpuIntrPost(Tick when) +{ + // If the interrupt you want to post is later than an interrupt + // already scheduled, just let it post in the coming one and don't + // schedule another. + // HOWEVER, must be sure that the scheduled intrTick is in the + // future (this was formerly the source of a bug) + /** + * @todo this warning should be removed and the intrTick code should + * be fixed. + */ + assert(when >= curTick); + assert(intrTick >= curTick || intrTick == 0); + if (!cpuIntrEnable) { + DPRINTF(EthernetIntr, "interrupts not enabled.\n", + intrTick); + return; + } + + if (when > intrTick && intrTick != 0) { + DPRINTF(EthernetIntr, "don't need to schedule event...intrTick=%d\n", + intrTick); + return; + } + + intrTick = when; + if (intrTick < curTick) { + debug_break(); + intrTick = curTick; + } + + DPRINTF(EthernetIntr, "going to schedule an interrupt for intrTick=%d\n", + intrTick); + + if (intrEvent) + intrEvent->squash(); + intrEvent = new IntrEvent(this, true); + intrEvent->schedule(intrTick); +} + +void +Base::cpuInterrupt() +{ + assert(intrTick == curTick); + + // Whether or not there's a pending interrupt, we don't care about + // it anymore + intrEvent = 0; + intrTick = 0; + + // Don't send an interrupt if there's already one + if (cpuPendingIntr) { + DPRINTF(EthernetIntr, + "would send an interrupt now, but there's already pending\n"); + } else { + // Send interrupt + cpuPendingIntr = true; + + DPRINTF(EthernetIntr, "posting interrupt\n"); + intrPost(); + } +} + +void +Base::cpuIntrClear() +{ + if (!cpuPendingIntr) + return; + + if (intrEvent) { + intrEvent->squash(); + intrEvent = 0; + } + + intrTick = 0; + + cpuPendingIntr = false; + + DPRINTF(EthernetIntr, "clearing cchip interrupt\n"); + intrClear(); +} + +bool +Base::cpuIntrPending() const +{ return cpuPendingIntr; } + +void +Device::changeConfig(uint32_t newconf) +{ + uint32_t changed = regs.Config ^ newconf; + if (!changed) + return; + + regs.Config = newconf; + + if ((changed & Regs::Config_IntEn)) { + cpuIntrEnable = regs.Config & Regs::Config_IntEn; + if (cpuIntrEnable) { + if (regs.IntrStatus & regs.IntrMask) + cpuIntrPost(curTick); + } else { + cpuIntrClear(); + } + } + + if ((changed & Regs::Config_TxEn)) { + txEnable = regs.Config & Regs::Config_TxEn; + if (txEnable) + txKick(); + } + + if ((changed & Regs::Config_RxEn)) { + rxEnable = regs.Config & Regs::Config_RxEn; + if (rxEnable) + rxKick(); + } +} + +void +Device::command(uint32_t command) +{ + if (command & Regs::Command_Intr) + devIntrPost(Regs::Intr_Soft); + + if (command & Regs::Command_Reset) + reset(); +} + +void +Device::reset() +{ + using namespace Regs; + + memset(®s, 0, sizeof(regs)); + + regs.Config = 0; + if (params()->rx_thread) + regs.Config |= Config_RxThread; + if (params()->tx_thread) + regs.Config |= Config_TxThread; + if (params()->rss) + regs.Config |= Config_RSS; + if (params()->zero_copy) + regs.Config |= Config_ZeroCopy; + if (params()->delay_copy) + regs.Config |= Config_DelayCopy; + if (params()->virtual_addr) + regs.Config |= Config_Vaddr; + + if (params()->delay_copy && params()->zero_copy) + panic("Can't delay copy and zero copy"); + + regs.IntrMask = Intr_Soft | Intr_RxHigh | Intr_RxPacket | Intr_TxLow; + regs.RxMaxCopy = params()->rx_max_copy; + regs.TxMaxCopy = params()->tx_max_copy; + regs.RxMaxIntr = params()->rx_max_intr; + regs.VirtualCount = params()->virtual_count; + regs.RxFifoSize = params()->rx_fifo_size; + regs.TxFifoSize = params()->tx_fifo_size; + regs.RxFifoMark = params()->rx_fifo_threshold; + regs.TxFifoMark = params()->tx_fifo_threshold; + regs.HwAddr = params()->eaddr; + + rxList.clear(); + rxBusy.clear(); + rxActive = -1; + txList.clear(); + + rxState = rxIdle; + txState = txIdle; + + rxFifo.clear(); + rxFifoPtr = rxFifo.end(); + txFifo.clear(); + rxEmpty = false; + rxLow = true; + txFull = false; + + int size = virtualRegs.size(); + virtualRegs.clear(); + virtualRegs.resize(size); + for (int i = 0; i < size; ++i) + virtualRegs[i].rxPacket = rxFifo.end(); +} + +void +Device::rxDmaDone() +{ + assert(rxState == rxCopy); + rxState = rxCopyDone; + DPRINTF(EthernetDMA, "end rx dma write paddr=%#x len=%d\n", + rxDmaAddr, rxDmaLen); + DDUMP(EthernetData, rxDmaData, rxDmaLen); + + // If the transmit state machine has a pending DMA, let it go first + if (txState == txBeginCopy) + txKick(); + + rxKick(); +} + +void +Device::rxKick() +{ + VirtualReg *vnic = NULL; + + DPRINTF(EthernetSM, "rxKick: rxState=%s (rxFifo.size=%d)\n", + RxStateStrings[rxState], rxFifo.size()); + + if (rxKickTick > curTick) { + DPRINTF(EthernetSM, "rxKick: exiting, can't run till %d\n", + rxKickTick); + return; + } + + next: + if (rxState == rxIdle) + goto exit; + + if (rxActive == -1) { + if (rxState != rxFifoBlock) + panic("no active vnic while in state %s", RxStateStrings[rxState]); + + DPRINTF(EthernetSM, "processing rxState=%s\n", + RxStateStrings[rxState]); + } else { + vnic = &virtualRegs[rxActive]; + DPRINTF(EthernetSM, + "processing rxState=%s for vnic %d (rxunique %d)\n", + RxStateStrings[rxState], rxActive, vnic->rxUnique); + } + + switch (rxState) { + case rxFifoBlock: + if (DTRACE(EthernetSM)) { + PacketFifo::iterator end = rxFifo.end(); + int size = virtualRegs.size(); + for (int i = 0; i < size; ++i) { + VirtualReg *vn = &virtualRegs[i]; + if (vn->rxPacket != end && + !Regs::get_RxDone_Busy(vn->RxDone)) { + DPRINTF(EthernetSM, + "vnic %d (rxunique %d), has outstanding packet %d\n", + i, vn->rxUnique, + rxFifo.countPacketsBefore(vn->rxPacket)); + } + } + } + + if (!rxBusy.empty()) { + rxActive = rxBusy.front(); + rxBusy.pop_front(); + vnic = &virtualRegs[rxActive]; + + if (vnic->rxPacket == rxFifo.end()) + panic("continuing vnic without packet\n"); + + DPRINTF(EthernetSM, + "continue processing for vnic %d (rxunique %d)\n", + rxActive, vnic->rxUnique); + + rxState = rxBeginCopy; + + break; + } + + if (rxFifoPtr == rxFifo.end()) { + DPRINTF(EthernetSM, "receive waiting for data. Nothing to do.\n"); + goto exit; + } + + if (rxList.empty()) + panic("Not idle, but nothing to do!"); + + assert(!rxFifo.empty()); + + rxActive = rxList.front(); + rxList.pop_front(); + vnic = &virtualRegs[rxActive]; + + DPRINTF(EthernetSM, + "processing new packet for vnic %d (rxunique %d)\n", + rxActive, vnic->rxUnique); + + // Grab a new packet from the fifo. + vnic->rxPacket = rxFifoPtr++; + vnic->rxPacketOffset = 0; + vnic->rxPacketBytes = (*vnic->rxPacket)->length; + assert(vnic->rxPacketBytes); + + vnic->rxDoneData = 0; + /* scope for variables */ { + IpPtr ip(*vnic->rxPacket); + if (ip) { + DPRINTF(Ethernet, "ID is %d\n", ip->id()); + vnic->rxDoneData |= Regs::RxDone_IpPacket; + rxIpChecksums++; + if (cksum(ip) != 0) { + DPRINTF(EthernetCksum, "Rx IP Checksum Error\n"); + vnic->rxDoneData |= Regs::RxDone_IpError; + } + TcpPtr tcp(ip); + UdpPtr udp(ip); + if (tcp) { + DPRINTF(Ethernet, + "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n", + tcp->sport(), tcp->dport(), tcp->seq(), + tcp->ack()); + vnic->rxDoneData |= Regs::RxDone_TcpPacket; + rxTcpChecksums++; + if (cksum(tcp) != 0) { + DPRINTF(EthernetCksum, "Rx TCP Checksum Error\n"); + vnic->rxDoneData |= Regs::RxDone_TcpError; + } + } else if (udp) { + vnic->rxDoneData |= Regs::RxDone_UdpPacket; + rxUdpChecksums++; + if (cksum(udp) != 0) { + DPRINTF(EthernetCksum, "Rx UDP Checksum Error\n"); + vnic->rxDoneData |= Regs::RxDone_UdpError; + } + } + } + } + rxState = rxBeginCopy; + break; + + case rxBeginCopy: + if (dmaPending() || getState() != Running) + goto exit; + + rxDmaAddr = params()->platform->pciToDma( + Regs::get_RxData_Addr(vnic->RxData)); + rxDmaLen = std::min<int>(Regs::get_RxData_Len(vnic->RxData), + vnic->rxPacketBytes); + rxDmaData = (*vnic->rxPacket)->data + vnic->rxPacketOffset; + rxState = rxCopy; + if (rxDmaAddr == 1LL) { + rxState = rxCopyDone; + break; + } + + + dmaWrite(rxDmaAddr, rxDmaLen, &rxDmaEvent, rxDmaData); + break; + + case rxCopy: + DPRINTF(EthernetSM, "receive machine still copying\n"); + goto exit; + + case rxCopyDone: + vnic->RxDone = vnic->rxDoneData; + vnic->RxDone |= Regs::RxDone_Complete; + + if (vnic->rxPacketBytes == rxDmaLen) { + // Packet is complete. Indicate how many bytes were copied + vnic->RxDone = Regs::set_RxDone_CopyLen(vnic->RxDone, rxDmaLen); + + DPRINTF(EthernetSM, + "rxKick: packet complete on vnic %d (rxunique %d)\n", + rxActive, vnic->rxUnique); + rxFifo.remove(vnic->rxPacket); + vnic->rxPacket = rxFifo.end(); + } else { + vnic->rxPacketBytes -= rxDmaLen; + vnic->rxPacketOffset += rxDmaLen; + vnic->RxDone |= Regs::RxDone_More; + vnic->RxDone = Regs::set_RxDone_CopyLen(vnic->RxDone, + vnic->rxPacketBytes); + DPRINTF(EthernetSM, + "rxKick: packet not complete on vnic %d (rxunique %d): " + "%d bytes left\n", + rxActive, vnic->rxUnique, vnic->rxPacketBytes); + } + + rxActive = -1; + rxState = rxBusy.empty() && rxList.empty() ? rxIdle : rxFifoBlock; + + if (rxFifo.empty()) { + devIntrPost(Regs::Intr_RxEmpty); + rxEmpty = true; + } + + if (rxFifo.size() < params()->rx_fifo_low_mark) + rxLow = true; + + if (rxFifo.size() > params()->rx_fifo_threshold) + rxLow = false; + + devIntrPost(Regs::Intr_RxDMA); + break; + + default: + panic("Invalid rxState!"); + } + + DPRINTF(EthernetSM, "entering next rxState=%s\n", + RxStateStrings[rxState]); + + goto next; + + exit: + /** + * @todo do we want to schedule a future kick? + */ + DPRINTF(EthernetSM, "rx state machine exited rxState=%s\n", + RxStateStrings[rxState]); +} + +void +Device::txDmaDone() +{ + assert(txState == txCopy); + txState = txCopyDone; + DPRINTF(EthernetDMA, "tx dma read paddr=%#x len=%d\n", + txDmaAddr, txDmaLen); + DDUMP(EthernetData, txDmaData, txDmaLen); + + // If the receive state machine has a pending DMA, let it go first + if (rxState == rxBeginCopy) + rxKick(); + + txKick(); +} + +void +Device::transmit() +{ + if (txFifo.empty()) { + DPRINTF(Ethernet, "nothing to transmit\n"); + return; + } + + uint32_t interrupts; + EthPacketPtr packet = txFifo.front(); + if (!interface->sendPacket(packet)) { + DPRINTF(Ethernet, "Packet Transmit: failed txFifo available %d\n", + txFifo.avail()); + goto reschedule; + } + + txFifo.pop(); +#if TRACING_ON + if (DTRACE(Ethernet)) { + IpPtr ip(packet); + if (ip) { + DPRINTF(Ethernet, "ID is %d\n", ip->id()); + TcpPtr tcp(ip); + if (tcp) { + DPRINTF(Ethernet, + "Src Port=%d, Dest Port=%d, Seq=%d, Ack=%d\n", + tcp->sport(), tcp->dport(), tcp->seq(), + tcp->ack()); + } + } + } +#endif + + DDUMP(EthernetData, packet->data, packet->length); + txBytes += packet->length; + txPackets++; + + DPRINTF(Ethernet, "Packet Transmit: successful txFifo Available %d\n", + txFifo.avail()); + + interrupts = Regs::Intr_TxPacket; + if (txFifo.size() < regs.TxFifoMark) + interrupts |= Regs::Intr_TxLow; + devIntrPost(interrupts); + + reschedule: + if (!txFifo.empty() && !txEvent.scheduled()) { + DPRINTF(Ethernet, "reschedule transmit\n"); + txEvent.schedule(curTick + retryTime); + } +} + +void +Device::txKick() +{ + VirtualReg *vnic; + DPRINTF(EthernetSM, "txKick: txState=%s (txFifo.size=%d)\n", + TxStateStrings[txState], txFifo.size()); + + if (txKickTick > curTick) { + DPRINTF(EthernetSM, "txKick: exiting, can't run till %d\n", + txKickTick); + return; + } + + next: + if (txState == txIdle) + goto exit; + + assert(!txList.empty()); + vnic = &virtualRegs[txList.front()]; + + switch (txState) { + case txFifoBlock: + assert(Regs::get_TxDone_Busy(vnic->TxDone)); + if (!txPacket) { + // Grab a new packet from the fifo. + txPacket = new EthPacketData(16384); + txPacketOffset = 0; + } + + if (txFifo.avail() - txPacket->length < + Regs::get_TxData_Len(vnic->TxData)) { + DPRINTF(EthernetSM, "transmit fifo full. Nothing to do.\n"); + goto exit; + } + + txState = txBeginCopy; + break; + + case txBeginCopy: + if (dmaPending() || getState() != Running) + goto exit; + + txDmaAddr = params()->platform->pciToDma( + Regs::get_TxData_Addr(vnic->TxData)); + txDmaLen = Regs::get_TxData_Len(vnic->TxData); + txDmaData = txPacket->data + txPacketOffset; + txState = txCopy; + + dmaRead(txDmaAddr, txDmaLen, &txDmaEvent, txDmaData); + break; + + case txCopy: + DPRINTF(EthernetSM, "transmit machine still copying\n"); + goto exit; + + case txCopyDone: + vnic->TxDone = txDmaLen | Regs::TxDone_Complete; + txPacket->length += txDmaLen; + if ((vnic->TxData & Regs::TxData_More)) { + txPacketOffset += txDmaLen; + txState = txIdle; + devIntrPost(Regs::Intr_TxDMA); + break; + } + + assert(txPacket->length <= txFifo.avail()); + if ((vnic->TxData & Regs::TxData_Checksum)) { + IpPtr ip(txPacket); + if (ip) { + TcpPtr tcp(ip); + if (tcp) { + tcp->sum(0); + tcp->sum(cksum(tcp)); + txTcpChecksums++; + } + + UdpPtr udp(ip); + if (udp) { + udp->sum(0); + udp->sum(cksum(udp)); + txUdpChecksums++; + } + + ip->sum(0); + ip->sum(cksum(ip)); + txIpChecksums++; + } + } + + txFifo.push(txPacket); + if (txFifo.avail() < regs.TxMaxCopy) { + devIntrPost(Regs::Intr_TxFull); + txFull = true; + } + txPacket = 0; + transmit(); + txList.pop_front(); + txState = txList.empty() ? txIdle : txFifoBlock; + devIntrPost(Regs::Intr_TxDMA); + break; + + default: + panic("Invalid txState!"); + } + + DPRINTF(EthernetSM, "entering next txState=%s\n", + TxStateStrings[txState]); + + goto next; + + exit: + /** + * @todo do we want to schedule a future kick? + */ + DPRINTF(EthernetSM, "tx state machine exited txState=%s\n", + TxStateStrings[txState]); +} + +void +Device::transferDone() +{ + if (txFifo.empty()) { + DPRINTF(Ethernet, "transfer complete: txFifo empty...nothing to do\n"); + return; + } + + DPRINTF(Ethernet, "transfer complete: data in txFifo...schedule xmit\n"); + + if (txEvent.scheduled()) + txEvent.reschedule(curTick + cycles(1)); + else + txEvent.schedule(curTick + cycles(1)); +} + +bool +Device::rxFilter(const EthPacketPtr &packet) +{ + if (!Regs::get_Config_Filter(regs.Config)) + return false; + + panic("receive filter not implemented\n"); + bool drop = true; + +#if 0 + string type; + + EthHdr *eth = packet->eth(); + if (eth->unicast()) { + // If we're accepting all unicast addresses + if (acceptUnicast) + drop = false; + + // If we make a perfect match + if (acceptPerfect && params->eaddr == eth.dst()) + drop = false; + + if (acceptArp && eth->type() == ETH_TYPE_ARP) + drop = false; + + } else if (eth->broadcast()) { + // if we're accepting broadcasts + if (acceptBroadcast) + drop = false; + + } else if (eth->multicast()) { + // if we're accepting all multicasts + if (acceptMulticast) + drop = false; + + } + + if (drop) { + DPRINTF(Ethernet, "rxFilter drop\n"); + DDUMP(EthernetData, packet->data, packet->length); + } +#endif + return drop; +} + +bool +Device::recvPacket(EthPacketPtr packet) +{ + rxBytes += packet->length; + rxPackets++; + + DPRINTF(Ethernet, "Receiving packet from wire, rxFifo Available is %d\n", + rxFifo.avail()); + + if (!rxEnable) { + DPRINTF(Ethernet, "receive disabled...packet dropped\n"); + return true; + } + + if (rxFilter(packet)) { + DPRINTF(Ethernet, "packet filtered...dropped\n"); + return true; + } + + if (rxFifo.size() >= regs.RxFifoMark) + devIntrPost(Regs::Intr_RxHigh); + + if (!rxFifo.push(packet)) { + DPRINTF(Ethernet, + "packet will not fit in receive buffer...packet dropped\n"); + return false; + } + + // If we were at the last element, back up one ot go to the new + // last element of the list. + if (rxFifoPtr == rxFifo.end()) + --rxFifoPtr; + + devIntrPost(Regs::Intr_RxPacket); + rxKick(); + return true; +} + +void +Device::resume() +{ + SimObject::resume(); + + // During drain we could have left the state machines in a waiting state and + // they wouldn't get out until some other event occured to kick them. + // This way they'll get out immediately + txKick(); + rxKick(); +} + +//===================================================================== +// +// +void +Base::serialize(std::ostream &os) +{ + // Serialize the PciDev base class + PciDev::serialize(os); + + SERIALIZE_SCALAR(rxEnable); + SERIALIZE_SCALAR(txEnable); + SERIALIZE_SCALAR(cpuIntrEnable); + + /* + * Keep track of pending interrupt status. + */ + SERIALIZE_SCALAR(intrTick); + SERIALIZE_SCALAR(cpuPendingIntr); + Tick intrEventTick = 0; + if (intrEvent) + intrEventTick = intrEvent->when(); + SERIALIZE_SCALAR(intrEventTick); +} + +void +Base::unserialize(Checkpoint *cp, const std::string §ion) +{ + // Unserialize the PciDev base class + PciDev::unserialize(cp, section); + + UNSERIALIZE_SCALAR(rxEnable); + UNSERIALIZE_SCALAR(txEnable); + UNSERIALIZE_SCALAR(cpuIntrEnable); + + /* + * Keep track of pending interrupt status. + */ + UNSERIALIZE_SCALAR(intrTick); + UNSERIALIZE_SCALAR(cpuPendingIntr); + Tick intrEventTick; + UNSERIALIZE_SCALAR(intrEventTick); + if (intrEventTick) { + intrEvent = new IntrEvent(this, true); + intrEvent->schedule(intrEventTick); + } +} + +void +Device::serialize(std::ostream &os) +{ + int count; + + // Serialize the PciDev base class + Base::serialize(os); + + if (rxState == rxCopy) + panic("can't serialize with an in flight dma request rxState=%s", + RxStateStrings[rxState]); + + if (txState == txCopy) + panic("can't serialize with an in flight dma request txState=%s", + TxStateStrings[txState]); + + /* + * Serialize the device registers + */ + SERIALIZE_SCALAR(regs.Config); + SERIALIZE_SCALAR(regs.IntrStatus); + SERIALIZE_SCALAR(regs.IntrMask); + SERIALIZE_SCALAR(regs.RxMaxCopy); + SERIALIZE_SCALAR(regs.TxMaxCopy); + SERIALIZE_SCALAR(regs.RxMaxIntr); + SERIALIZE_SCALAR(regs.VirtualCount); + SERIALIZE_SCALAR(regs.RxData); + SERIALIZE_SCALAR(regs.RxDone); + SERIALIZE_SCALAR(regs.TxData); + SERIALIZE_SCALAR(regs.TxDone); + + /* + * Serialize the virtual nic state + */ + int virtualRegsSize = virtualRegs.size(); + SERIALIZE_SCALAR(virtualRegsSize); + for (int i = 0; i < virtualRegsSize; ++i) { + VirtualReg *vnic = &virtualRegs[i]; + + std::string reg = csprintf("vnic%d", i); + paramOut(os, reg + ".RxData", vnic->RxData); + paramOut(os, reg + ".RxDone", vnic->RxDone); + paramOut(os, reg + ".TxData", vnic->TxData); + paramOut(os, reg + ".TxDone", vnic->TxDone); + + bool rxPacketExists = vnic->rxPacket != rxFifo.end(); + paramOut(os, reg + ".rxPacketExists", rxPacketExists); + if (rxPacketExists) { + int rxPacket = 0; + PacketFifo::iterator i = rxFifo.begin(); + while (i != vnic->rxPacket) { + assert(i != rxFifo.end()); + ++i; + ++rxPacket; + } + + paramOut(os, reg + ".rxPacket", rxPacket); + paramOut(os, reg + ".rxPacketOffset", vnic->rxPacketOffset); + paramOut(os, reg + ".rxPacketBytes", vnic->rxPacketBytes); + } + paramOut(os, reg + ".rxDoneData", vnic->rxDoneData); + } + + int rxFifoPtr = rxFifo.countPacketsBefore(this->rxFifoPtr); + SERIALIZE_SCALAR(rxFifoPtr); + + SERIALIZE_SCALAR(rxActive); + + VirtualList::iterator i, end; + for (count = 0, i = rxList.begin(), end = rxList.end(); i != end; ++i) + paramOut(os, csprintf("rxList%d", count++), *i); + int rxListSize = count; + SERIALIZE_SCALAR(rxListSize); + + for (count = 0, i = rxBusy.begin(), end = rxBusy.end(); i != end; ++i) + paramOut(os, csprintf("rxBusy%d", count++), *i); + int rxBusySize = count; + SERIALIZE_SCALAR(rxBusySize); + + for (count = 0, i = txList.begin(), end = txList.end(); i != end; ++i) + paramOut(os, csprintf("txList%d", count++), *i); + int txListSize = count; + SERIALIZE_SCALAR(txListSize); + + /* + * Serialize rx state machine + */ + int rxState = this->rxState; + SERIALIZE_SCALAR(rxState); + SERIALIZE_SCALAR(rxEmpty); + SERIALIZE_SCALAR(rxLow); + rxFifo.serialize("rxFifo", os); + + /* + * Serialize tx state machine + */ + int txState = this->txState; + SERIALIZE_SCALAR(txState); + SERIALIZE_SCALAR(txFull); + txFifo.serialize("txFifo", os); + bool txPacketExists = txPacket; + SERIALIZE_SCALAR(txPacketExists); + if (txPacketExists) { + txPacket->serialize("txPacket", os); + SERIALIZE_SCALAR(txPacketOffset); + SERIALIZE_SCALAR(txPacketBytes); + } + + /* + * If there's a pending transmit, store the time so we can + * reschedule it later + */ + Tick transmitTick = txEvent.scheduled() ? txEvent.when() - curTick : 0; + SERIALIZE_SCALAR(transmitTick); +} + +void +Device::unserialize(Checkpoint *cp, const std::string §ion) +{ + // Unserialize the PciDev base class + Base::unserialize(cp, section); + + /* + * Unserialize the device registers + */ + UNSERIALIZE_SCALAR(regs.Config); + UNSERIALIZE_SCALAR(regs.IntrStatus); + UNSERIALIZE_SCALAR(regs.IntrMask); + UNSERIALIZE_SCALAR(regs.RxMaxCopy); + UNSERIALIZE_SCALAR(regs.TxMaxCopy); + UNSERIALIZE_SCALAR(regs.RxMaxIntr); + UNSERIALIZE_SCALAR(regs.VirtualCount); + UNSERIALIZE_SCALAR(regs.RxData); + UNSERIALIZE_SCALAR(regs.RxDone); + UNSERIALIZE_SCALAR(regs.TxData); + UNSERIALIZE_SCALAR(regs.TxDone); + + UNSERIALIZE_SCALAR(rxActive); + + int rxListSize; + UNSERIALIZE_SCALAR(rxListSize); + rxList.clear(); + for (int i = 0; i < rxListSize; ++i) { + int value; + paramIn(cp, section, csprintf("rxList%d", i), value); + rxList.push_back(value); + } + + int rxBusySize; + UNSERIALIZE_SCALAR(rxBusySize); + rxBusy.clear(); + for (int i = 0; i < rxBusySize; ++i) { + int value; + paramIn(cp, section, csprintf("rxBusy%d", i), value); + rxBusy.push_back(value); + } + + int txListSize; + UNSERIALIZE_SCALAR(txListSize); + txList.clear(); + for (int i = 0; i < txListSize; ++i) { + int value; + paramIn(cp, section, csprintf("txList%d", i), value); + txList.push_back(value); + } + + /* + * Unserialize rx state machine + */ + int rxState; + UNSERIALIZE_SCALAR(rxState); + UNSERIALIZE_SCALAR(rxEmpty); + UNSERIALIZE_SCALAR(rxLow); + this->rxState = (RxState) rxState; + rxFifo.unserialize("rxFifo", cp, section); + + int rxFifoPtr; + UNSERIALIZE_SCALAR(rxFifoPtr); + this->rxFifoPtr = rxFifo.begin(); + for (int i = 0; i < rxFifoPtr; ++i) + ++this->rxFifoPtr; + + /* + * Unserialize tx state machine + */ + int txState; + UNSERIALIZE_SCALAR(txState); + UNSERIALIZE_SCALAR(txFull); + this->txState = (TxState) txState; + txFifo.unserialize("txFifo", cp, section); + bool txPacketExists; + UNSERIALIZE_SCALAR(txPacketExists); + txPacket = 0; + if (txPacketExists) { + txPacket = new EthPacketData(16384); + txPacket->unserialize("txPacket", cp, section); + UNSERIALIZE_SCALAR(txPacketOffset); + UNSERIALIZE_SCALAR(txPacketBytes); + } + + /* + * unserialize the virtual nic registers/state + * + * this must be done after the unserialization of the rxFifo + * because the packet iterators depend on the fifo being populated + */ + int virtualRegsSize; + UNSERIALIZE_SCALAR(virtualRegsSize); + virtualRegs.clear(); + virtualRegs.resize(virtualRegsSize); + for (int i = 0; i < virtualRegsSize; ++i) { + VirtualReg *vnic = &virtualRegs[i]; + std::string reg = csprintf("vnic%d", i); + + paramIn(cp, section, reg + ".RxData", vnic->RxData); + paramIn(cp, section, reg + ".RxDone", vnic->RxDone); + paramIn(cp, section, reg + ".TxData", vnic->TxData); + paramIn(cp, section, reg + ".TxDone", vnic->TxDone); + + vnic->rxUnique = rxUnique++; + vnic->txUnique = txUnique++; + + bool rxPacketExists; + paramIn(cp, section, reg + ".rxPacketExists", rxPacketExists); + if (rxPacketExists) { + int rxPacket; + paramIn(cp, section, reg + ".rxPacket", rxPacket); + vnic->rxPacket = rxFifo.begin(); + while (rxPacket--) + ++vnic->rxPacket; + + paramIn(cp, section, reg + ".rxPacketOffset", + vnic->rxPacketOffset); + paramIn(cp, section, reg + ".rxPacketBytes", vnic->rxPacketBytes); + } else { + vnic->rxPacket = rxFifo.end(); + } + paramIn(cp, section, reg + ".rxDoneData", vnic->rxDoneData); + } + + /* + * If there's a pending transmit, reschedule it now + */ + Tick transmitTick; + UNSERIALIZE_SCALAR(transmitTick); + if (transmitTick) + txEvent.schedule(curTick + transmitTick); + + pioPort->sendStatusChange(Port::RangeChange); + +} + +/* namespace Sinic */ } + +BEGIN_DECLARE_SIM_OBJECT_PARAMS_WNS(Sinic, SinicInterface) + + SimObjectParam<EtherInt *> peer; + SimObjectParam<Sinic::Device *> device; +END_DECLARE_SIM_OBJECT_PARAMS_WNS(Sinic, SinicInterface) + +BEGIN_INIT_SIM_OBJECT_PARAMS_WNS(Sinic, SinicInterface) + + INIT_PARAM_DFLT(peer, "peer interface", NULL), + INIT_PARAM(device, "Ethernet device of this interface") + +END_INIT_SIM_OBJECT_PARAMS_WNS(Sinic, SinicInterface) + +CREATE_SIM_OBJECT_WNS(Sinic, SinicInterface) +{ + Sinic::Interface *dev_int = new Sinic::Interface(getInstanceName(), device); + + EtherInt *p = (EtherInt *)peer; + if (p) { + dev_int->setPeer(p); + p->setPeer(dev_int); + } + + return dev_int; +} + +REGISTER_SIM_OBJECT_WNS(Sinic, "SinicInt", SinicInterface) + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS_WNS(Sinic, SinicDevice) + + + SimObjectParam<System *> system; + SimObjectParam<Platform *> platform; + SimObjectParam<PciConfigData *> configdata; + Param<uint32_t> pci_bus; + Param<uint32_t> pci_dev; + Param<uint32_t> pci_func; + Param<Tick> pio_latency; + Param<Tick> config_latency; + Param<Tick> intr_delay; + + Param<Tick> clock; + Param<Tick> dma_read_delay; + Param<Tick> dma_read_factor; + Param<Tick> dma_write_delay; + Param<Tick> dma_write_factor; + + Param<Tick> rx_delay; + Param<Tick> tx_delay; + Param<uint32_t> rx_max_copy; + Param<uint32_t> tx_max_copy; + Param<uint32_t> rx_max_intr; + Param<uint32_t> rx_fifo_size; + Param<uint32_t> tx_fifo_size; + Param<uint32_t> rx_fifo_threshold; + Param<uint32_t> rx_fifo_low_mark; + Param<uint32_t> tx_fifo_high_mark; + Param<uint32_t> tx_fifo_threshold; + + Param<bool> rx_filter; + Param<std::string> hardware_address; + Param<bool> rx_thread; + Param<bool> tx_thread; + Param<bool> rss; + Param<uint32_t> virtual_count; + Param<bool> zero_copy; + Param<bool> delay_copy; + Param<bool> virtual_addr; + +END_DECLARE_SIM_OBJECT_PARAMS_WNS(Sinic, SinicDevice) + +BEGIN_INIT_SIM_OBJECT_PARAMS_WNS(Sinic, SinicDevice) + + + INIT_PARAM(system, "System pointer"), + INIT_PARAM(platform, "Platform pointer"), + INIT_PARAM(configdata, "PCI Config data"), + INIT_PARAM(pci_bus, "PCI bus ID"), + INIT_PARAM(pci_dev, "PCI device number"), + INIT_PARAM(pci_func, "PCI function code"), + INIT_PARAM_DFLT(pio_latency, "Programmed IO latency in bus cycles", 1), + INIT_PARAM(config_latency, "Number of cycles for a config read or write"), + INIT_PARAM(intr_delay, "Interrupt Delay"), + INIT_PARAM(clock, "State machine cycle time"), + + INIT_PARAM(dma_read_delay, "fixed delay for dma reads"), + INIT_PARAM(dma_read_factor, "multiplier for dma reads"), + INIT_PARAM(dma_write_delay, "fixed delay for dma writes"), + INIT_PARAM(dma_write_factor, "multiplier for dma writes"), + + INIT_PARAM(rx_delay, "Receive Delay"), + INIT_PARAM(tx_delay, "Transmit Delay"), + INIT_PARAM(rx_max_copy, "rx max copy"), + INIT_PARAM(tx_max_copy, "rx max copy"), + INIT_PARAM(rx_max_intr, "rx max intr"), + INIT_PARAM(rx_fifo_size, "max size in bytes of rxFifo"), + INIT_PARAM(tx_fifo_size, "max size in bytes of txFifo"), + INIT_PARAM(rx_fifo_threshold, "max size in bytes of rxFifo"), + INIT_PARAM(rx_fifo_low_mark, "max size in bytes of rxFifo"), + INIT_PARAM(tx_fifo_high_mark, "max size in bytes of txFifo"), + INIT_PARAM(tx_fifo_threshold, "max size in bytes of txFifo"), + + INIT_PARAM(rx_filter, "Enable Receive Filter"), + INIT_PARAM(hardware_address, "Ethernet Hardware Address"), + INIT_PARAM(rx_thread, ""), + INIT_PARAM(tx_thread, ""), + INIT_PARAM(rss, ""), + INIT_PARAM(virtual_count, ""), + INIT_PARAM(zero_copy, ""), + INIT_PARAM(delay_copy, ""), + INIT_PARAM(virtual_addr, "") + +END_INIT_SIM_OBJECT_PARAMS_WNS(Sinic, SinicDevice) + + +CREATE_SIM_OBJECT_WNS(Sinic, SinicDevice) +{ + Sinic::Sinic::Device::Params *params = new Device::Params; + params->name = getInstanceName(); + params->platform = platform; + params->system = system; + params->configData = configdata; + params->busNum = pci_bus; + params->deviceNum = pci_dev; + params->functionNum = pci_func; + params->pio_delay = pio_latency; + params->config_delay = config_latency; + params->intr_delay = intr_delay; + params->clock = clock; + + params->dma_read_delay = dma_read_delay; + params->dma_read_factor = dma_read_factor; + params->dma_write_delay = dma_write_delay; + params->dma_write_factor = dma_write_factor; + + params->tx_delay = tx_delay; + params->rx_delay = rx_delay; + params->rx_max_copy = rx_max_copy; + params->tx_max_copy = tx_max_copy; + params->rx_max_intr = rx_max_intr; + params->rx_fifo_size = rx_fifo_size; + params->tx_fifo_size = tx_fifo_size; + params->rx_fifo_threshold = rx_fifo_threshold; + params->rx_fifo_low_mark = rx_fifo_low_mark; + params->tx_fifo_high_mark = tx_fifo_high_mark; + params->tx_fifo_threshold = tx_fifo_threshold; + + params->rx_filter = rx_filter; + params->eaddr = hardware_address; + params->rx_thread = rx_thread; + params->tx_thread = tx_thread; + params->rss = rss; + params->virtual_count = virtual_count; + params->zero_copy = zero_copy; + params->delay_copy = delay_copy; + params->virtual_addr = virtual_addr; + + return new Sinic::Device(params); +} + +REGISTER_SIM_OBJECT_WNS(Sinic, "Sinic", SinicDevice) + diff --git a/src/dev/sinic.hh b/src/dev/sinic.hh new file mode 100644 index 000000000..eece4ba6b --- /dev/null +++ b/src/dev/sinic.hh @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __DEV_SINIC_HH__ +#define __DEV_SINIC_HH__ + +#include "base/inet.hh" +#include "base/statistics.hh" +#include "dev/etherint.hh" +#include "dev/etherpkt.hh" +#include "dev/io_device.hh" +#include "dev/pcidev.hh" +#include "dev/pktfifo.hh" +#include "dev/sinicreg.hh" +#include "sim/eventq.hh" + +namespace Sinic { + +class Interface; +class Base : public PciDev +{ + protected: + bool rxEnable; + bool txEnable; + Tick clock; + inline Tick cycles(int numCycles) const { return numCycles * clock; } + + protected: + Tick intrDelay; + Tick intrTick; + bool cpuIntrEnable; + bool cpuPendingIntr; + void cpuIntrPost(Tick when); + void cpuInterrupt(); + void cpuIntrClear(); + + typedef EventWrapper<Base, &Base::cpuInterrupt> IntrEvent; + friend void IntrEvent::process(); + IntrEvent *intrEvent; + Interface *interface; + + bool cpuIntrPending() const; + void cpuIntrAck() { cpuIntrClear(); } + +/** + * Serialization stuff + */ + public: + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +/** + * Construction/Destruction/Parameters + */ + public: + struct Params : public PciDev::Params + { + Tick clock; + Tick intr_delay; + }; + + Base(Params *p); +}; + +class Device : public Base +{ + protected: + /** Receive State Machine States */ + enum RxState { + rxIdle, + rxFifoBlock, + rxBeginCopy, + rxCopy, + rxCopyDone + }; + + /** Transmit State Machine states */ + enum TxState { + txIdle, + txFifoBlock, + txBeginCopy, + txCopy, + txCopyDone + }; + + /** device register file */ + struct { + uint32_t Config; // 0x00 + uint32_t Command; // 0x04 + uint32_t IntrStatus; // 0x08 + uint32_t IntrMask; // 0x0c + uint32_t RxMaxCopy; // 0x10 + uint32_t TxMaxCopy; // 0x14 + uint32_t RxMaxIntr; // 0x18 + uint32_t VirtualCount; // 0x1c + uint32_t RxFifoSize; // 0x20 + uint32_t TxFifoSize; // 0x24 + uint32_t RxFifoMark; // 0x28 + uint32_t TxFifoMark; // 0x2c + uint64_t RxData; // 0x30 + uint64_t RxDone; // 0x38 + uint64_t RxWait; // 0x40 + uint64_t TxData; // 0x48 + uint64_t TxDone; // 0x50 + uint64_t TxWait; // 0x58 + uint64_t HwAddr; // 0x60 + } regs; + + struct VirtualReg { + uint64_t RxData; + uint64_t RxDone; + uint64_t TxData; + uint64_t TxDone; + + PacketFifo::iterator rxPacket; + int rxPacketOffset; + int rxPacketBytes; + uint64_t rxDoneData; + + Counter rxUnique; + Counter txUnique; + + VirtualReg() + : RxData(0), RxDone(0), TxData(0), TxDone(0), + rxPacketOffset(0), rxPacketBytes(0), rxDoneData(0) + { } + }; + typedef std::vector<VirtualReg> VirtualRegs; + typedef std::list<int> VirtualList; + Counter rxUnique; + Counter txUnique; + VirtualRegs virtualRegs; + VirtualList rxList; + VirtualList rxBusy; + int rxActive; + VirtualList txList; + + uint8_t ®Data8(Addr daddr) { return *((uint8_t *)®s + daddr); } + uint32_t ®Data32(Addr daddr) { return *(uint32_t *)®Data8(daddr); } + uint64_t ®Data64(Addr daddr) { return *(uint64_t *)®Data8(daddr); } + + protected: + RxState rxState; + PacketFifo rxFifo; + PacketFifo::iterator rxFifoPtr; + bool rxEmpty; + bool rxLow; + Addr rxDmaAddr; + uint8_t *rxDmaData; + int rxDmaLen; + + TxState txState; + PacketFifo txFifo; + bool txFull; + EthPacketPtr txPacket; + int txPacketOffset; + int txPacketBytes; + Addr txDmaAddr; + uint8_t *txDmaData; + int txDmaLen; + + protected: + void reset(); + + void rxKick(); + Tick rxKickTick; + typedef EventWrapper<Device, &Device::rxKick> RxKickEvent; + friend void RxKickEvent::process(); + + void txKick(); + Tick txKickTick; + typedef EventWrapper<Device, &Device::txKick> TxKickEvent; + friend void TxKickEvent::process(); + + /** + * Retransmit event + */ + void transmit(); + void txEventTransmit() + { + transmit(); + if (txState == txFifoBlock) + txKick(); + } + typedef EventWrapper<Device, &Device::txEventTransmit> TxEvent; + friend void TxEvent::process(); + TxEvent txEvent; + + void txDump() const; + void rxDump() const; + + /** + * receive address filter + */ + bool rxFilter(const EthPacketPtr &packet); + +/** + * device configuration + */ + void changeConfig(uint32_t newconfig); + void command(uint32_t command); + +/** + * device ethernet interface + */ + public: + bool recvPacket(EthPacketPtr packet); + void transferDone(); + void setInterface(Interface *i) { assert(!interface); interface = i; } + +/** + * DMA parameters + */ + protected: + void rxDmaDone(); + friend class EventWrapper<Device, &Device::rxDmaDone>; + EventWrapper<Device, &Device::rxDmaDone> rxDmaEvent; + + void txDmaDone(); + friend class EventWrapper<Device, &Device::txDmaDone>; + EventWrapper<Device, &Device::txDmaDone> txDmaEvent; + + Tick dmaReadDelay; + Tick dmaReadFactor; + Tick dmaWriteDelay; + Tick dmaWriteFactor; + +/** + * Interrupt management + */ + protected: + void devIntrPost(uint32_t interrupts); + void devIntrClear(uint32_t interrupts = Regs::Intr_All); + void devIntrChangeMask(uint32_t newmask); + +/** + * Memory Interface + */ + public: + virtual Tick read(Packet *pkt); + virtual Tick write(Packet *pkt); + virtual void resume(); + + void prepareIO(int cpu, int index); + void prepareRead(int cpu, int index); + void prepareWrite(int cpu, int index); + // Fault iprRead(Addr daddr, int cpu, uint64_t &result); + +/** + * Statistics + */ + private: + Stats::Scalar<> rxBytes; + Stats::Formula rxBandwidth; + Stats::Scalar<> rxPackets; + Stats::Formula rxPacketRate; + Stats::Scalar<> rxIpPackets; + Stats::Scalar<> rxTcpPackets; + Stats::Scalar<> rxUdpPackets; + Stats::Scalar<> rxIpChecksums; + Stats::Scalar<> rxTcpChecksums; + Stats::Scalar<> rxUdpChecksums; + + Stats::Scalar<> txBytes; + Stats::Formula txBandwidth; + Stats::Formula totBandwidth; + Stats::Formula totPackets; + Stats::Formula totBytes; + Stats::Formula totPacketRate; + Stats::Scalar<> txPackets; + Stats::Formula txPacketRate; + Stats::Scalar<> txIpPackets; + Stats::Scalar<> txTcpPackets; + Stats::Scalar<> txUdpPackets; + Stats::Scalar<> txIpChecksums; + Stats::Scalar<> txTcpChecksums; + Stats::Scalar<> txUdpChecksums; + + public: + virtual void regStats(); + +/** + * Serialization stuff + */ + public: + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +/** + * Construction/Destruction/Parameters + */ + public: + struct Params : public Base::Params + { + Tick tx_delay; + Tick rx_delay; + bool rx_filter; + Net::EthAddr eaddr; + uint32_t rx_max_copy; + uint32_t tx_max_copy; + uint32_t rx_max_intr; + uint32_t rx_fifo_size; + uint32_t tx_fifo_size; + uint32_t rx_fifo_threshold; + uint32_t rx_fifo_low_mark; + uint32_t tx_fifo_high_mark; + uint32_t tx_fifo_threshold; + Tick dma_read_delay; + Tick dma_read_factor; + Tick dma_write_delay; + Tick dma_write_factor; + bool rx_thread; + bool tx_thread; + bool rss; + uint32_t virtual_count; + bool zero_copy; + bool delay_copy; + bool virtual_addr; + }; + + protected: + const Params *params() const { return (const Params *)_params; } + + public: + Device(Params *params); + ~Device(); +}; + +/* + * Ethernet Interface for an Ethernet Device + */ +class Interface : public EtherInt +{ + private: + Device *dev; + + public: + Interface(const std::string &name, Device *d) + : EtherInt(name), dev(d) { dev->setInterface(this); } + + virtual bool recvPacket(EthPacketPtr pkt) { return dev->recvPacket(pkt); } + virtual void sendDone() { dev->transferDone(); } +}; + +/* namespace Sinic */ } + +#endif // __DEV_SINIC_HH__ diff --git a/src/dev/sinicreg.hh b/src/dev/sinicreg.hh new file mode 100644 index 000000000..de4188145 --- /dev/null +++ b/src/dev/sinicreg.hh @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __DEV_SINICREG_HH__ +#define __DEV_SINICREG_HH__ + +#define __SINIC_REG32(NAME, VAL) static const uint32_t NAME = (VAL) +#define __SINIC_REG64(NAME, VAL) static const uint64_t NAME = (VAL) + +#define __SINIC_VAL32(NAME, OFFSET, WIDTH) \ + static const uint32_t NAME##_width = WIDTH; \ + static const uint32_t NAME##_offset = OFFSET; \ + static const uint32_t NAME##_mask = (1 << WIDTH) - 1; \ + static const uint32_t NAME = ((1 << WIDTH) - 1) << OFFSET; \ + static inline uint32_t get_##NAME(uint32_t reg) \ + { return (reg & NAME) >> OFFSET; } \ + static inline uint32_t set_##NAME(uint32_t reg, uint32_t val) \ + { return (reg & ~NAME) | ((val << OFFSET) & NAME); } + +#define __SINIC_VAL64(NAME, OFFSET, WIDTH) \ + static const uint64_t NAME##_width = WIDTH; \ + static const uint64_t NAME##_offset = OFFSET; \ + static const uint64_t NAME##_mask = (ULL(1) << WIDTH) - 1; \ + static const uint64_t NAME = ((ULL(1) << WIDTH) - 1) << OFFSET; \ + static inline uint64_t get_##NAME(uint64_t reg) \ + { return (reg & NAME) >> OFFSET; } \ + static inline uint64_t set_##NAME(uint64_t reg, uint64_t val) \ + { return (reg & ~NAME) | ((val << OFFSET) & NAME); } + +namespace Sinic { +namespace Regs { + +static const int VirtualShift = 8; +static const int VirtualMask = 0xff; + +// Registers +__SINIC_REG32(Config, 0x00); // 32: configuration register +__SINIC_REG32(Command, 0x04); // 32: command register +__SINIC_REG32(IntrStatus, 0x08); // 32: interrupt status +__SINIC_REG32(IntrMask, 0x0c); // 32: interrupt mask +__SINIC_REG32(RxMaxCopy, 0x10); // 32: max bytes per rx copy +__SINIC_REG32(TxMaxCopy, 0x14); // 32: max bytes per tx copy +__SINIC_REG32(RxMaxIntr, 0x18); // 32: max receives per interrupt +__SINIC_REG32(VirtualCount, 0x1c); // 32: number of virutal NICs +__SINIC_REG32(RxFifoSize, 0x20); // 32: rx fifo capacity in bytes +__SINIC_REG32(TxFifoSize, 0x24); // 32: tx fifo capacity in bytes +__SINIC_REG32(RxFifoMark, 0x28); // 32: rx fifo high watermark +__SINIC_REG32(TxFifoMark, 0x2c); // 32: tx fifo low watermark +__SINIC_REG32(RxData, 0x30); // 64: receive data +__SINIC_REG32(RxDone, 0x38); // 64: receive done +__SINIC_REG32(RxWait, 0x40); // 64: receive done (busy wait) +__SINIC_REG32(TxData, 0x48); // 64: transmit data +__SINIC_REG32(TxDone, 0x50); // 64: transmit done +__SINIC_REG32(TxWait, 0x58); // 64: transmit done (busy wait) +__SINIC_REG32(HwAddr, 0x60); // 64: mac address +__SINIC_REG32(Size, 0x68); // register addres space size + +// Config register bits +__SINIC_VAL32(Config_ZeroCopy, 12, 1); // enable zero copy +__SINIC_VAL32(Config_DelayCopy,11, 1); // enable delayed copy +__SINIC_VAL32(Config_RSS, 10, 1); // enable receive side scaling +__SINIC_VAL32(Config_RxThread, 9, 1); // enable receive threads +__SINIC_VAL32(Config_TxThread, 8, 1); // enable transmit thread +__SINIC_VAL32(Config_Filter, 7, 1); // enable receive filter +__SINIC_VAL32(Config_Vlan, 6, 1); // enable vlan tagging +__SINIC_VAL32(Config_Vaddr, 5, 1); // enable virtual addressing +__SINIC_VAL32(Config_Desc, 4, 1); // enable tx/rx descriptors +__SINIC_VAL32(Config_Poll, 3, 1); // enable polling +__SINIC_VAL32(Config_IntEn, 2, 1); // enable interrupts +__SINIC_VAL32(Config_TxEn, 1, 1); // enable transmit +__SINIC_VAL32(Config_RxEn, 0, 1); // enable receive + +// Command register bits +__SINIC_VAL32(Command_Intr, 1, 1); // software interrupt +__SINIC_VAL32(Command_Reset, 0, 1); // reset chip + +// Interrupt register bits +__SINIC_VAL32(Intr_Soft, 8, 1); // software interrupt +__SINIC_VAL32(Intr_TxLow, 7, 1); // tx fifo dropped below watermark +__SINIC_VAL32(Intr_TxFull, 6, 1); // tx fifo full +__SINIC_VAL32(Intr_TxDMA, 5, 1); // tx dma completed w/ interrupt +__SINIC_VAL32(Intr_TxPacket, 4, 1); // packet transmitted +__SINIC_VAL32(Intr_RxHigh, 3, 1); // rx fifo above high watermark +__SINIC_VAL32(Intr_RxEmpty, 2, 1); // rx fifo empty +__SINIC_VAL32(Intr_RxDMA, 1, 1); // rx dma completed w/ interrupt +__SINIC_VAL32(Intr_RxPacket, 0, 1); // packet received +__SINIC_REG32(Intr_All, 0x01ff); // all valid interrupts +__SINIC_REG32(Intr_NoDelay, 0x01cc); // interrupts that aren't coalesced +__SINIC_REG32(Intr_Res, ~0x01ff); // reserved interrupt bits + +// RX Data Description +__SINIC_VAL64(RxData_Vaddr, 60, 1); // Addr is virtual +__SINIC_VAL64(RxData_Len, 40, 20); // 0 - 256k +__SINIC_VAL64(RxData_Addr, 0, 40); // Address 1TB + +// TX Data Description +__SINIC_VAL64(TxData_More, 63, 1); // Packet not complete (will dma more) +__SINIC_VAL64(TxData_Checksum, 62, 1); // do checksum +__SINIC_VAL64(TxData_Vaddr, 60, 1); // Addr is virtual +__SINIC_VAL64(TxData_Len, 40, 20); // 0 - 256k +__SINIC_VAL64(TxData_Addr, 0, 40); // Address 1TB + +// RX Done/Busy Information +__SINIC_VAL64(RxDone_Packets, 32, 16); // number of packets in rx fifo +__SINIC_VAL64(RxDone_Busy, 31, 1); // receive dma busy copying +__SINIC_VAL64(RxDone_Complete, 30, 1); // valid data (packet complete) +__SINIC_VAL64(RxDone_More, 29, 1); // Packet has more data (dma again) +__SINIC_VAL64(RxDone_Empty, 28, 1); // rx fifo is empty +__SINIC_VAL64(RxDone_High, 27, 1); // rx fifo is above the watermark +__SINIC_VAL64(RxDone_NotHigh, 26, 1); // rxfifo never hit the high watermark +__SINIC_VAL64(RxDone_TcpError, 25, 1); // TCP packet error (bad checksum) +__SINIC_VAL64(RxDone_UdpError, 24, 1); // UDP packet error (bad checksum) +__SINIC_VAL64(RxDone_IpError, 23, 1); // IP packet error (bad checksum) +__SINIC_VAL64(RxDone_TcpPacket, 22, 1); // this is a TCP packet +__SINIC_VAL64(RxDone_UdpPacket, 21, 1); // this is a UDP packet +__SINIC_VAL64(RxDone_IpPacket, 20, 1); // this is an IP packet +__SINIC_VAL64(RxDone_CopyLen, 0, 20); // up to 256k + +// TX Done/Busy Information +__SINIC_VAL64(TxDone_Packets, 32, 16); // number of packets in tx fifo +__SINIC_VAL64(TxDone_Busy, 31, 1); // transmit dma busy copying +__SINIC_VAL64(TxDone_Complete, 30, 1); // valid data (packet complete) +__SINIC_VAL64(TxDone_Full, 29, 1); // tx fifo is full +__SINIC_VAL64(TxDone_Low, 28, 1); // tx fifo is below the watermark +__SINIC_VAL64(TxDone_Res0, 27, 1); // reserved +__SINIC_VAL64(TxDone_Res1, 26, 1); // reserved +__SINIC_VAL64(TxDone_Res2, 25, 1); // reserved +__SINIC_VAL64(TxDone_Res3, 24, 1); // reserved +__SINIC_VAL64(TxDone_Res4, 23, 1); // reserved +__SINIC_VAL64(TxDone_Res5, 22, 1); // reserved +__SINIC_VAL64(TxDone_Res6, 21, 1); // reserved +__SINIC_VAL64(TxDone_Res7, 20, 1); // reserved +__SINIC_VAL64(TxDone_CopyLen, 0, 20); // up to 256k + +struct Info +{ + uint8_t size; + bool read; + bool write; + const char *name; +}; + +/* namespace Regs */ } + +inline const Regs::Info& +regInfo(Addr daddr) +{ + static Regs::Info invalid = { 0, false, false, "invalid" }; + static Regs::Info info [] = { + { 4, true, true, "Config" }, + { 4, false, true, "Command" }, + { 4, true, true, "IntrStatus" }, + { 4, true, true, "IntrMask" }, + { 4, true, false, "RxMaxCopy" }, + { 4, true, false, "TxMaxCopy" }, + { 4, true, false, "RxMaxIntr" }, + { 4, true, false, "VirtualCount" }, + { 4, true, false, "RxFifoSize" }, + { 4, true, false, "TxFifoSize" }, + { 4, true, false, "RxFifoMark" }, + { 4, true, false, "TxFifoMark" }, + { 8, true, true, "RxData" }, + invalid, + { 8, true, false, "RxDone" }, + invalid, + { 8, true, false, "RxWait" }, + invalid, + { 8, true, true, "TxData" }, + invalid, + { 8, true, false, "TxDone" }, + invalid, + { 8, true, false, "TxWait" }, + invalid, + { 8, true, false, "HwAddr" }, + invalid, + }; + + return info[daddr / 4]; +} + +inline bool +regValid(Addr daddr) +{ + if (daddr > Regs::Size) + return false; + + if (regInfo(daddr).size == 0) + return false; + + return true; +} + +/* namespace Sinic */ } + +#endif // __DEV_SINICREG_HH__ diff --git a/src/dev/tsunami.cc b/src/dev/tsunami.cc new file mode 100644 index 000000000..8e740a72f --- /dev/null +++ b/src/dev/tsunami.cc @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** @file + * Implementation of Tsunami platform. + */ + +#include <deque> +#include <string> +#include <vector> + +#include "cpu/intr_control.hh" +#include "dev/simconsole.hh" +#include "dev/tsunami_cchip.hh" +#include "dev/tsunami_pchip.hh" +#include "dev/tsunami_io.hh" +#include "dev/tsunami.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; +//Should this be AlphaISA? +using namespace TheISA; + +Tsunami::Tsunami(const string &name, System *s, IntrControl *ic) + : Platform(name, ic), system(s) +{ + // set the back pointer from the system to myself + system->platform = this; + + for (int i = 0; i < Tsunami::Max_CPUs; i++) + intr_sum_type[i] = 0; +} + +Tick +Tsunami::intrFrequency() +{ + return io->frequency(); +} + +void +Tsunami::postConsoleInt() +{ + io->postPIC(0x10); +} + +void +Tsunami::clearConsoleInt() +{ + io->clearPIC(0x10); +} + +void +Tsunami::postPciInt(int line) +{ + cchip->postDRIR(line); +} + +void +Tsunami::clearPciInt(int line) +{ + cchip->clearDRIR(line); +} + +Addr +Tsunami::pciToDma(Addr pciAddr) const +{ + return pchip->translatePciToDma(pciAddr); +} + + +Addr +Tsunami::calcConfigAddr(int bus, int dev, int func) +{ + return pchip->calcConfigAddr(bus, dev, func); +} + +void +Tsunami::serialize(std::ostream &os) +{ + SERIALIZE_ARRAY(intr_sum_type, Tsunami::Max_CPUs); +} + +void +Tsunami::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_ARRAY(intr_sum_type, Tsunami::Max_CPUs); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(Tsunami) + + SimObjectParam<System *> system; + SimObjectParam<IntrControl *> intrctrl; + +END_DECLARE_SIM_OBJECT_PARAMS(Tsunami) + +BEGIN_INIT_SIM_OBJECT_PARAMS(Tsunami) + + INIT_PARAM(system, "system"), + INIT_PARAM(intrctrl, "interrupt controller") + +END_INIT_SIM_OBJECT_PARAMS(Tsunami) + +CREATE_SIM_OBJECT(Tsunami) +{ + return new Tsunami(getInstanceName(), system, intrctrl); +} + +REGISTER_SIM_OBJECT("Tsunami", Tsunami) diff --git a/src/dev/tsunami.hh b/src/dev/tsunami.hh new file mode 100644 index 000000000..6fbfac851 --- /dev/null +++ b/src/dev/tsunami.hh @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** + * @file + * Declaration of top level class for the Tsunami chipset. This class just + * retains pointers to all its children so the children can communicate. + */ + +#ifndef __DEV_TSUNAMI_HH__ +#define __DEV_TSUNAMI_HH__ + +#include "dev/platform.hh" + +class IdeController; +class TsunamiCChip; +class TsunamiPChip; +class TsunamiIO; +class System; + +/** + * Top level class for Tsunami Chipset emulation. + * This structure just contains pointers to all the + * children so the children can commnicate to do the + * read work + */ + +class Tsunami : public Platform +{ + public: + /** Max number of CPUs in a Tsunami */ + static const int Max_CPUs = 64; + + /** Pointer to the system */ + System *system; + + /** Pointer to the TsunamiIO device which has the RTC */ + TsunamiIO *io; + + /** Pointer to the Tsunami CChip. + * The chip contains some configuration information and + * all the interrupt mask and status registers + */ + TsunamiCChip *cchip; + + /** Pointer to the Tsunami PChip. + * The pchip is the interface to the PCI bus, in our case + * it does not have to do much. + */ + TsunamiPChip *pchip; + + int intr_sum_type[Tsunami::Max_CPUs]; + int ipi_pending[Tsunami::Max_CPUs]; + + public: + /** + * Constructor for the Tsunami Class. + * @param name name of the object + * @param s system the object belongs to + * @param intctrl pointer to the interrupt controller + */ + Tsunami(const std::string &name, System *s, IntrControl *intctrl); + + /** + * Return the interrupting frequency to AlphaAccess + * @return frequency of RTC interrupts + */ + virtual Tick intrFrequency(); + + /** + * Cause the cpu to post a serial interrupt to the CPU. + */ + virtual void postConsoleInt(); + + /** + * Clear a posted CPU interrupt (id=55) + */ + virtual void clearConsoleInt(); + + /** + * Cause the chipset to post a cpi interrupt to the CPU. + */ + virtual void postPciInt(int line); + + /** + * Clear a posted PCI->CPU interrupt + */ + virtual void clearPciInt(int line); + + + virtual Addr pciToDma(Addr pciAddr) const; + + /** + * Calculate the configuration address given a bus/dev/func. + */ + virtual Addr calcConfigAddr(int bus, int dev, int func); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +#endif // __DEV_TSUNAMI_HH__ diff --git a/src/dev/tsunami_cchip.cc b/src/dev/tsunami_cchip.cc new file mode 100644 index 000000000..3feb7439f --- /dev/null +++ b/src/dev/tsunami_cchip.cc @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Ron Dreslinski + */ + +/** @file + * Emulation of the Tsunami CChip CSRs + */ + +#include <deque> +#include <string> +#include <vector> + +#include "arch/alpha/ev5.hh" +#include "base/trace.hh" +#include "dev/tsunami_cchip.hh" +#include "dev/tsunamireg.h" +#include "dev/tsunami.hh" +#include "mem/port.hh" +#include "cpu/thread_context.hh" +#include "cpu/intr_control.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; +//Should this be AlphaISA? +using namespace TheISA; + +TsunamiCChip::TsunamiCChip(Params *p) + : BasicPioDevice(p), tsunami(p->tsunami) +{ + pioSize = 0xfffffff; + + drir = 0; + ipint = 0; + itint = 0; + + for (int x = 0; x < Tsunami::Max_CPUs; x++) + { + dim[x] = 0; + dir[x] = 0; + } + + //Put back pointer in tsunami + tsunami->cchip = this; +} + +Tick +TsunamiCChip::read(Packet *pkt) +{ + DPRINTF(Tsunami, "read va=%#x size=%d\n", pkt->getAddr(), pkt->getSize()); + + assert(pkt->result == Packet::Unknown); + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + + Addr regnum = (pkt->getAddr() - pioAddr) >> 6; + Addr daddr = (pkt->getAddr() - pioAddr); + + pkt->allocate(); + switch (pkt->getSize()) { + + case sizeof(uint64_t): + if (daddr & TSDEV_CC_BDIMS) + { + pkt->set(dim[(daddr >> 4) & 0x3F]); + break; + } + + if (daddr & TSDEV_CC_BDIRS) + { + pkt->set(dir[(daddr >> 4) & 0x3F]); + break; + } + + switch(regnum) { + case TSDEV_CC_CSR: + pkt->set(0x0); + break; + case TSDEV_CC_MTR: + panic("TSDEV_CC_MTR not implemeted\n"); + break; + case TSDEV_CC_MISC: + pkt->set((ipint << 8) & 0xF | (itint << 4) & 0xF | + (pkt->req->getCpuNum() & 0x3)); + break; + case TSDEV_CC_AAR0: + case TSDEV_CC_AAR1: + case TSDEV_CC_AAR2: + case TSDEV_CC_AAR3: + pkt->set(0); + break; + case TSDEV_CC_DIM0: + pkt->set(dim[0]); + break; + case TSDEV_CC_DIM1: + pkt->set(dim[1]); + break; + case TSDEV_CC_DIM2: + pkt->set(dim[2]); + break; + case TSDEV_CC_DIM3: + pkt->set(dim[3]); + break; + case TSDEV_CC_DIR0: + pkt->set(dir[0]); + break; + case TSDEV_CC_DIR1: + pkt->set(dir[1]); + break; + case TSDEV_CC_DIR2: + pkt->set(dir[2]); + break; + case TSDEV_CC_DIR3: + pkt->set(dir[3]); + break; + case TSDEV_CC_DRIR: + pkt->set(drir); + break; + case TSDEV_CC_PRBEN: + panic("TSDEV_CC_PRBEN not implemented\n"); + break; + case TSDEV_CC_IIC0: + case TSDEV_CC_IIC1: + case TSDEV_CC_IIC2: + case TSDEV_CC_IIC3: + panic("TSDEV_CC_IICx not implemented\n"); + break; + case TSDEV_CC_MPR0: + case TSDEV_CC_MPR1: + case TSDEV_CC_MPR2: + case TSDEV_CC_MPR3: + panic("TSDEV_CC_MPRx not implemented\n"); + break; + case TSDEV_CC_IPIR: + pkt->set(ipint); + break; + case TSDEV_CC_ITIR: + pkt->set(itint); + break; + default: + panic("default in cchip read reached, accessing 0x%x\n"); + } // uint64_t + + break; + case sizeof(uint32_t): + case sizeof(uint16_t): + case sizeof(uint8_t): + default: + panic("invalid access size(?) for tsunami register!\n"); + } + DPRINTF(Tsunami, "Tsunami CChip: read regnum=%#x size=%d data=%lld\n", + regnum, pkt->getSize(), pkt->get<uint64_t>()); + + pkt->result = Packet::Success; + return pioDelay; +} + +Tick +TsunamiCChip::write(Packet *pkt) +{ + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + Addr daddr = pkt->getAddr() - pioAddr; + Addr regnum = (pkt->getAddr() - pioAddr) >> 6 ; + + + assert(pkt->getSize() == sizeof(uint64_t)); + + DPRINTF(Tsunami, "write - addr=%#x value=%#x\n", pkt->getAddr(), pkt->get<uint64_t>()); + + bool supportedWrite = false; + + + if (daddr & TSDEV_CC_BDIMS) + { + int number = (daddr >> 4) & 0x3F; + + uint64_t bitvector; + uint64_t olddim; + uint64_t olddir; + + olddim = dim[number]; + olddir = dir[number]; + dim[number] = pkt->get<uint64_t>(); + dir[number] = dim[number] & drir; + for(int x = 0; x < Tsunami::Max_CPUs; x++) + { + bitvector = ULL(1) << x; + // Figure out which bits have changed + if ((dim[number] & bitvector) != (olddim & bitvector)) + { + // The bit is now set and it wasn't before (set) + if((dim[number] & bitvector) && (dir[number] & bitvector)) + { + tsunami->intrctrl->post(number, TheISA::INTLEVEL_IRQ1, x); + DPRINTF(Tsunami, "dim write resulting in posting dir" + " interrupt to cpu %d\n", number); + } + else if ((olddir & bitvector) && + !(dir[number] & bitvector)) + { + // The bit was set and now its now clear and + // we were interrupting on that bit before + tsunami->intrctrl->clear(number, TheISA::INTLEVEL_IRQ1, x); + DPRINTF(Tsunami, "dim write resulting in clear" + " dir interrupt to cpu %d\n", number); + + } + + + } + } + } else { + switch(regnum) { + case TSDEV_CC_CSR: + panic("TSDEV_CC_CSR write\n"); + case TSDEV_CC_MTR: + panic("TSDEV_CC_MTR write not implemented\n"); + case TSDEV_CC_MISC: + uint64_t ipreq; + ipreq = (pkt->get<uint64_t>() >> 12) & 0xF; + //If it is bit 12-15, this is an IPI post + if (ipreq) { + reqIPI(ipreq); + supportedWrite = true; + } + + //If it is bit 8-11, this is an IPI clear + uint64_t ipintr; + ipintr = (pkt->get<uint64_t>() >> 8) & 0xF; + if (ipintr) { + clearIPI(ipintr); + supportedWrite = true; + } + + //If it is the 4-7th bit, clear the RTC interrupt + uint64_t itintr; + itintr = (pkt->get<uint64_t>() >> 4) & 0xF; + if (itintr) { + clearITI(itintr); + supportedWrite = true; + } + + // ignore NXMs + if (pkt->get<uint64_t>() & 0x10000000) + supportedWrite = true; + + if(!supportedWrite) + panic("TSDEV_CC_MISC write not implemented\n"); + + break; + case TSDEV_CC_AAR0: + case TSDEV_CC_AAR1: + case TSDEV_CC_AAR2: + case TSDEV_CC_AAR3: + panic("TSDEV_CC_AARx write not implemeted\n"); + case TSDEV_CC_DIM0: + case TSDEV_CC_DIM1: + case TSDEV_CC_DIM2: + case TSDEV_CC_DIM3: + int number; + if(regnum == TSDEV_CC_DIM0) + number = 0; + else if(regnum == TSDEV_CC_DIM1) + number = 1; + else if(regnum == TSDEV_CC_DIM2) + number = 2; + else + number = 3; + + uint64_t bitvector; + uint64_t olddim; + uint64_t olddir; + + olddim = dim[number]; + olddir = dir[number]; + dim[number] = pkt->get<uint64_t>(); + dir[number] = dim[number] & drir; + for(int x = 0; x < 64; x++) + { + bitvector = ULL(1) << x; + // Figure out which bits have changed + if ((dim[number] & bitvector) != (olddim & bitvector)) + { + // The bit is now set and it wasn't before (set) + if((dim[number] & bitvector) && (dir[number] & bitvector)) + { + tsunami->intrctrl->post(number, TheISA::INTLEVEL_IRQ1, x); + DPRINTF(Tsunami, "posting dir interrupt to cpu 0\n"); + } + else if ((olddir & bitvector) && + !(dir[number] & bitvector)) + { + // The bit was set and now its now clear and + // we were interrupting on that bit before + tsunami->intrctrl->clear(number, TheISA::INTLEVEL_IRQ1, x); + DPRINTF(Tsunami, "dim write resulting in clear" + " dir interrupt to cpu %d\n", + x); + + } + + + } + } + break; + case TSDEV_CC_DIR0: + case TSDEV_CC_DIR1: + case TSDEV_CC_DIR2: + case TSDEV_CC_DIR3: + panic("TSDEV_CC_DIR write not implemented\n"); + case TSDEV_CC_DRIR: + panic("TSDEV_CC_DRIR write not implemented\n"); + case TSDEV_CC_PRBEN: + panic("TSDEV_CC_PRBEN write not implemented\n"); + case TSDEV_CC_IIC0: + case TSDEV_CC_IIC1: + case TSDEV_CC_IIC2: + case TSDEV_CC_IIC3: + panic("TSDEV_CC_IICx write not implemented\n"); + case TSDEV_CC_MPR0: + case TSDEV_CC_MPR1: + case TSDEV_CC_MPR2: + case TSDEV_CC_MPR3: + panic("TSDEV_CC_MPRx write not implemented\n"); + case TSDEV_CC_IPIR: + clearIPI(pkt->get<uint64_t>()); + break; + case TSDEV_CC_ITIR: + clearITI(pkt->get<uint64_t>()); + break; + case TSDEV_CC_IPIQ: + reqIPI(pkt->get<uint64_t>()); + break; + default: + panic("default in cchip read reached, accessing 0x%x\n"); + } // swtich(regnum) + } // not BIG_TSUNAMI write + pkt->result = Packet::Success; + return pioDelay; +} + +void +TsunamiCChip::clearIPI(uint64_t ipintr) +{ + int numcpus = tsunami->intrctrl->cpu->system->threadContexts.size(); + assert(numcpus <= Tsunami::Max_CPUs); + + if (ipintr) { + for (int cpunum=0; cpunum < numcpus; cpunum++) { + // Check each cpu bit + uint64_t cpumask = ULL(1) << cpunum; + if (ipintr & cpumask) { + // Check if there is a pending ipi + if (ipint & cpumask) { + ipint &= ~cpumask; + tsunami->intrctrl->clear(cpunum, TheISA::INTLEVEL_IRQ3, 0); + DPRINTF(IPI, "clear IPI IPI cpu=%d\n", cpunum); + } + else + warn("clear IPI for CPU=%d, but NO IPI\n", cpunum); + } + } + } + else + panic("Big IPI Clear, but not processors indicated\n"); +} + +void +TsunamiCChip::clearITI(uint64_t itintr) +{ + int numcpus = tsunami->intrctrl->cpu->system->threadContexts.size(); + assert(numcpus <= Tsunami::Max_CPUs); + + if (itintr) { + for (int i=0; i < numcpus; i++) { + uint64_t cpumask = ULL(1) << i; + if (itintr & cpumask & itint) { + tsunami->intrctrl->clear(i, TheISA::INTLEVEL_IRQ2, 0); + itint &= ~cpumask; + DPRINTF(Tsunami, "clearing rtc interrupt to cpu=%d\n", i); + } + } + } + else + panic("Big ITI Clear, but not processors indicated\n"); +} + +void +TsunamiCChip::reqIPI(uint64_t ipreq) +{ + int numcpus = tsunami->intrctrl->cpu->system->threadContexts.size(); + assert(numcpus <= Tsunami::Max_CPUs); + + if (ipreq) { + for (int cpunum=0; cpunum < numcpus; cpunum++) { + // Check each cpu bit + uint64_t cpumask = ULL(1) << cpunum; + if (ipreq & cpumask) { + // Check if there is already an ipi (bits 8:11) + if (!(ipint & cpumask)) { + ipint |= cpumask; + tsunami->intrctrl->post(cpunum, TheISA::INTLEVEL_IRQ3, 0); + DPRINTF(IPI, "send IPI cpu=%d\n", cpunum); + } + else + warn("post IPI for CPU=%d, but IPI already\n", cpunum); + } + } + } + else + panic("Big IPI Request, but not processors indicated\n"); +} + + +void +TsunamiCChip::postRTC() +{ + int size = tsunami->intrctrl->cpu->system->threadContexts.size(); + assert(size <= Tsunami::Max_CPUs); + + for (int i = 0; i < size; i++) { + uint64_t cpumask = ULL(1) << i; + if (!(cpumask & itint)) { + itint |= cpumask; + tsunami->intrctrl->post(i, TheISA::INTLEVEL_IRQ2, 0); + DPRINTF(Tsunami, "Posting RTC interrupt to cpu=%d", i); + } + } + +} + +void +TsunamiCChip::postDRIR(uint32_t interrupt) +{ + uint64_t bitvector = ULL(1) << interrupt; + uint64_t size = tsunami->intrctrl->cpu->system->threadContexts.size(); + assert(size <= Tsunami::Max_CPUs); + drir |= bitvector; + + for(int i=0; i < size; i++) { + dir[i] = dim[i] & drir; + if (dim[i] & bitvector) { + tsunami->intrctrl->post(i, TheISA::INTLEVEL_IRQ1, interrupt); + DPRINTF(Tsunami, "posting dir interrupt to cpu %d," + "interrupt %d\n",i, interrupt); + } + } +} + +void +TsunamiCChip::clearDRIR(uint32_t interrupt) +{ + uint64_t bitvector = ULL(1) << interrupt; + uint64_t size = tsunami->intrctrl->cpu->system->threadContexts.size(); + assert(size <= Tsunami::Max_CPUs); + + if (drir & bitvector) + { + drir &= ~bitvector; + for(int i=0; i < size; i++) { + if (dir[i] & bitvector) { + tsunami->intrctrl->clear(i, TheISA::INTLEVEL_IRQ1, interrupt); + DPRINTF(Tsunami, "clearing dir interrupt to cpu %d," + "interrupt %d\n",i, interrupt); + + } + dir[i] = dim[i] & drir; + } + } + else + DPRINTF(Tsunami, "Spurrious clear? interrupt %d\n", interrupt); +} + + +void +TsunamiCChip::serialize(std::ostream &os) +{ + SERIALIZE_ARRAY(dim, Tsunami::Max_CPUs); + SERIALIZE_ARRAY(dir, Tsunami::Max_CPUs); + SERIALIZE_SCALAR(ipint); + SERIALIZE_SCALAR(itint); + SERIALIZE_SCALAR(drir); +} + +void +TsunamiCChip::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_ARRAY(dim, Tsunami::Max_CPUs); + UNSERIALIZE_ARRAY(dir, Tsunami::Max_CPUs); + UNSERIALIZE_SCALAR(ipint); + UNSERIALIZE_SCALAR(itint); + UNSERIALIZE_SCALAR(drir); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(TsunamiCChip) + + Param<Addr> pio_addr; + Param<Tick> pio_latency; + SimObjectParam<Platform *> platform; + SimObjectParam<System *> system; + SimObjectParam<Tsunami *> tsunami; + +END_DECLARE_SIM_OBJECT_PARAMS(TsunamiCChip) + +BEGIN_INIT_SIM_OBJECT_PARAMS(TsunamiCChip) + + INIT_PARAM(pio_addr, "Device Address"), + INIT_PARAM(pio_latency, "Programmed IO latency"), + INIT_PARAM(platform, "platform"), + INIT_PARAM(system, "system object"), + INIT_PARAM(tsunami, "Tsunami") + +END_INIT_SIM_OBJECT_PARAMS(TsunamiCChip) + +CREATE_SIM_OBJECT(TsunamiCChip) +{ + TsunamiCChip::Params *p = new TsunamiCChip::Params; + p->name = getInstanceName(); + p->pio_addr = pio_addr; + p->pio_delay = pio_latency; + p->platform = platform; + p->system = system; + p->tsunami = tsunami; + return new TsunamiCChip(p); +} + +REGISTER_SIM_OBJECT("TsunamiCChip", TsunamiCChip) diff --git a/src/dev/tsunami_cchip.hh b/src/dev/tsunami_cchip.hh new file mode 100644 index 000000000..ef43f621f --- /dev/null +++ b/src/dev/tsunami_cchip.hh @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** @file + * Emulation of the Tsunami CChip CSRs + */ + +#ifndef __TSUNAMI_CCHIP_HH__ +#define __TSUNAMI_CCHIP_HH__ + +#include "dev/tsunami.hh" +#include "base/range.hh" +#include "dev/io_device.hh" + + +/** + * Tsunami CChip CSR Emulation. This device includes all the interrupt + * handling code for the chipset. + */ +class TsunamiCChip : public BasicPioDevice +{ + protected: + /** + * pointer to the tsunami object. + * This is our access to all the other tsunami + * devices. + */ + Tsunami *tsunami; + + /** + * The dims are device interrupt mask registers. + * One exists for each CPU, the DRIR X DIM = DIR + */ + uint64_t dim[Tsunami::Max_CPUs]; + + /** + * The dirs are device interrupt registers. + * One exists for each CPU, the DRIR X DIM = DIR + */ + uint64_t dir[Tsunami::Max_CPUs]; + + /** + * This register contains bits for each PCI interrupt + * that can occur. + */ + uint64_t drir; + + /** Indicator of which CPUs have an IPI interrupt */ + uint64_t ipint; + + /** Indicator of which CPUs have an RTC interrupt */ + uint64_t itint; + + public: + struct Params : public BasicPioDevice::Params + { + Tsunami *tsunami; + }; + protected: + const Params *params() const {return (const Params *)_params; } + + public: + /** + * Initialize the Tsunami CChip by setting all of the + * device register to 0. + * @param p params struct + */ + TsunamiCChip(Params *p); + + virtual Tick read(Packet *pkt); + + virtual Tick write(Packet *pkt); + + /** + * post an RTC interrupt to the CPU + */ + void postRTC(); + + /** + * post an interrupt to the CPU. + * @param interrupt the interrupt number to post (0-64) + */ + void postDRIR(uint32_t interrupt); + + /** + * clear an interrupt previously posted to the CPU. + * @param interrupt the interrupt number to post (0-64) + */ + void clearDRIR(uint32_t interrupt); + + /** + * post an ipi interrupt to the CPU. + * @param ipintr the cpu number to clear(bitvector) + */ + void clearIPI(uint64_t ipintr); + + /** + * clear a timer interrupt previously posted to the CPU. + * @param itintr the cpu number to clear(bitvector) + */ + void clearITI(uint64_t itintr); + + /** + * request an interrupt be posted to the CPU. + * @param ipreq the cpu number to interrupt(bitvector) + */ + void reqIPI(uint64_t ipreq); + + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +}; + +#endif // __TSUNAMI_CCHIP_HH__ diff --git a/src/dev/tsunami_io.cc b/src/dev/tsunami_io.cc new file mode 100644 index 000000000..e3da10eb5 --- /dev/null +++ b/src/dev/tsunami_io.cc @@ -0,0 +1,686 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Andrew Schultz + * Miguel Serrano + */ + +/** @file + * Tsunami I/O including PIC, PIT, RTC, DMA + */ + +#include <sys/time.h> + +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "dev/pitreg.h" +#include "dev/rtcreg.h" +#include "dev/tsunami_cchip.hh" +#include "dev/tsunami.hh" +#include "dev/tsunami_io.hh" +#include "dev/tsunamireg.h" +#include "mem/port.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; +//Should this be AlphaISA? +using namespace TheISA; + +TsunamiIO::RTC::RTC(const string &name, Tsunami* t, Tick i) + : _name(name), event(t, i), addr(0) +{ + memset(clock_data, 0, sizeof(clock_data)); + stat_regA = RTCA_32768HZ | RTCA_1024HZ; + stat_regB = RTCB_PRDC_IE |RTCB_BIN | RTCB_24HR; +} + +void +TsunamiIO::RTC::set_time(time_t t) +{ + struct tm tm; + gmtime_r(&t, &tm); + + sec = tm.tm_sec; + min = tm.tm_min; + hour = tm.tm_hour; + wday = tm.tm_wday + 1; + mday = tm.tm_mday; + mon = tm.tm_mon + 1; + year = tm.tm_year; + + DPRINTFN("Real-time clock set to %s", asctime(&tm)); +} + +void +TsunamiIO::RTC::writeAddr(const uint8_t data) +{ + if (data <= RTC_STAT_REGD) + addr = data; + else + panic("RTC addresses over 0xD are not implemented.\n"); +} + +void +TsunamiIO::RTC::writeData(const uint8_t data) +{ + if (addr < RTC_STAT_REGA) + clock_data[addr] = data; + else { + switch (addr) { + case RTC_STAT_REGA: + if (data != (RTCA_32768HZ | RTCA_1024HZ)) + panic("Unimplemented RTC register A value write!\n"); + stat_regA = data; + break; + case RTC_STAT_REGB: + if ((data & ~(RTCB_PRDC_IE | RTCB_SQWE)) != (RTCB_BIN | RTCB_24HR)) + panic("Write to RTC reg B bits that are not implemented!\n"); + + if (data & RTCB_PRDC_IE) { + if (!event.scheduled()) + event.scheduleIntr(); + } else { + if (event.scheduled()) + event.deschedule(); + } + stat_regB = data; + break; + case RTC_STAT_REGC: + case RTC_STAT_REGD: + panic("RTC status registers C and D are not implemented.\n"); + break; + } + } +} + +uint8_t +TsunamiIO::RTC::readData() +{ + if (addr < RTC_STAT_REGA) + return clock_data[addr]; + else { + switch (addr) { + case RTC_STAT_REGA: + // toggle UIP bit for linux + stat_regA ^= RTCA_UIP; + return stat_regA; + break; + case RTC_STAT_REGB: + return stat_regB; + break; + case RTC_STAT_REGC: + case RTC_STAT_REGD: + return 0x00; + break; + default: + panic("Shouldn't be here"); + } + } +} + +void +TsunamiIO::RTC::serialize(const string &base, ostream &os) +{ + paramOut(os, base + ".addr", addr); + arrayParamOut(os, base + ".clock_data", clock_data, sizeof(clock_data)); + paramOut(os, base + ".stat_regA", stat_regA); + paramOut(os, base + ".stat_regB", stat_regB); +} + +void +TsunamiIO::RTC::unserialize(const string &base, Checkpoint *cp, + const string §ion) +{ + paramIn(cp, section, base + ".addr", addr); + arrayParamIn(cp, section, base + ".clock_data", clock_data, + sizeof(clock_data)); + paramIn(cp, section, base + ".stat_regA", stat_regA); + paramIn(cp, section, base + ".stat_regB", stat_regB); + + // We're not unserializing the event here, but we need to + // rescehedule the event since curTick was moved forward by the + // checkpoint + event.reschedule(curTick + event.interval); +} + +TsunamiIO::RTC::RTCEvent::RTCEvent(Tsunami*t, Tick i) + : Event(&mainEventQueue), tsunami(t), interval(i) +{ + DPRINTF(MC146818, "RTC Event Initilizing\n"); + schedule(curTick + interval); +} + +void +TsunamiIO::RTC::RTCEvent::scheduleIntr() +{ + schedule(curTick + interval); +} + +void +TsunamiIO::RTC::RTCEvent::process() +{ + DPRINTF(MC146818, "RTC Timer Interrupt\n"); + schedule(curTick + interval); + //Actually interrupt the processor here + tsunami->cchip->postRTC(); +} + +const char * +TsunamiIO::RTC::RTCEvent::description() +{ + return "tsunami RTC interrupt"; +} + +TsunamiIO::PITimer::PITimer(const string &name) + : _name(name), counter0(name + ".counter0"), counter1(name + ".counter1"), + counter2(name + ".counter2") +{ + counter[0] = &counter0; + counter[1] = &counter0; + counter[2] = &counter0; +} + +void +TsunamiIO::PITimer::writeControl(const uint8_t data) +{ + int rw; + int sel; + + sel = GET_CTRL_SEL(data); + + if (sel == PIT_READ_BACK) + panic("PITimer Read-Back Command is not implemented.\n"); + + rw = GET_CTRL_RW(data); + + if (rw == PIT_RW_LATCH_COMMAND) + counter[sel]->latchCount(); + else { + counter[sel]->setRW(rw); + counter[sel]->setMode(GET_CTRL_MODE(data)); + counter[sel]->setBCD(GET_CTRL_BCD(data)); + } +} + +void +TsunamiIO::PITimer::serialize(const string &base, ostream &os) +{ + // serialize the counters + counter0.serialize(base + ".counter0", os); + counter1.serialize(base + ".counter1", os); + counter2.serialize(base + ".counter2", os); +} + +void +TsunamiIO::PITimer::unserialize(const string &base, Checkpoint *cp, + const string §ion) +{ + // unserialze the counters + counter0.unserialize(base + ".counter0", cp, section); + counter1.unserialize(base + ".counter1", cp, section); + counter2.unserialize(base + ".counter2", cp, section); +} + +TsunamiIO::PITimer::Counter::Counter(const string &name) + : _name(name), event(this), count(0), latched_count(0), period(0), + mode(0), output_high(false), latch_on(false), read_byte(LSB), + write_byte(LSB) +{ + +} + +void +TsunamiIO::PITimer::Counter::latchCount() +{ + // behave like a real latch + if(!latch_on) { + latch_on = true; + read_byte = LSB; + latched_count = count; + } +} + +uint8_t +TsunamiIO::PITimer::Counter::read() +{ + if (latch_on) { + switch (read_byte) { + case LSB: + read_byte = MSB; + return (uint8_t)latched_count; + break; + case MSB: + read_byte = LSB; + latch_on = false; + return latched_count >> 8; + break; + default: + panic("Shouldn't be here"); + } + } else { + switch (read_byte) { + case LSB: + read_byte = MSB; + return (uint8_t)count; + break; + case MSB: + read_byte = LSB; + return count >> 8; + break; + default: + panic("Shouldn't be here"); + } + } +} + +void +TsunamiIO::PITimer::Counter::write(const uint8_t data) +{ + switch (write_byte) { + case LSB: + count = (count & 0xFF00) | data; + + if (event.scheduled()) + event.deschedule(); + output_high = false; + write_byte = MSB; + break; + + case MSB: + count = (count & 0x00FF) | (data << 8); + period = count; + + if (period > 0) { + DPRINTF(Tsunami, "Timer set to curTick + %d\n", + count * event.interval); + event.schedule(curTick + count * event.interval); + } + write_byte = LSB; + break; + } +} + +void +TsunamiIO::PITimer::Counter::setRW(int rw_val) +{ + if (rw_val != PIT_RW_16BIT) + panic("Only LSB/MSB read/write is implemented.\n"); +} + +void +TsunamiIO::PITimer::Counter::setMode(int mode_val) +{ + if(mode_val != PIT_MODE_INTTC && mode_val != PIT_MODE_RATEGEN && + mode_val != PIT_MODE_SQWAVE) + panic("PIT mode %#x is not implemented: \n", mode_val); + + mode = mode_val; +} + +void +TsunamiIO::PITimer::Counter::setBCD(int bcd_val) +{ + if (bcd_val != PIT_BCD_FALSE) + panic("PITimer does not implement BCD counts.\n"); +} + +bool +TsunamiIO::PITimer::Counter::outputHigh() +{ + return output_high; +} + +void +TsunamiIO::PITimer::Counter::serialize(const string &base, ostream &os) +{ + paramOut(os, base + ".count", count); + paramOut(os, base + ".latched_count", latched_count); + paramOut(os, base + ".period", period); + paramOut(os, base + ".mode", mode); + paramOut(os, base + ".output_high", output_high); + paramOut(os, base + ".latch_on", latch_on); + paramOut(os, base + ".read_byte", read_byte); + paramOut(os, base + ".write_byte", write_byte); + + Tick event_tick = 0; + if (event.scheduled()) + event_tick = event.when(); + paramOut(os, base + ".event_tick", event_tick); +} + +void +TsunamiIO::PITimer::Counter::unserialize(const string &base, Checkpoint *cp, + const string §ion) +{ + paramIn(cp, section, base + ".count", count); + paramIn(cp, section, base + ".latched_count", latched_count); + paramIn(cp, section, base + ".period", period); + paramIn(cp, section, base + ".mode", mode); + paramIn(cp, section, base + ".output_high", output_high); + paramIn(cp, section, base + ".latch_on", latch_on); + paramIn(cp, section, base + ".read_byte", read_byte); + paramIn(cp, section, base + ".write_byte", write_byte); + + Tick event_tick; + paramIn(cp, section, base + ".event_tick", event_tick); + if (event_tick) + event.schedule(event_tick); +} + +TsunamiIO::PITimer::Counter::CounterEvent::CounterEvent(Counter* c_ptr) + : Event(&mainEventQueue) +{ + interval = (Tick)(Clock::Float::s / 1193180.0); + counter = c_ptr; +} + +void +TsunamiIO::PITimer::Counter::CounterEvent::process() +{ + DPRINTF(Tsunami, "Timer Interrupt\n"); + switch (counter->mode) { + case PIT_MODE_INTTC: + counter->output_high = true; + case PIT_MODE_RATEGEN: + case PIT_MODE_SQWAVE: + break; + default: + panic("Unimplemented PITimer mode.\n"); + } +} + +const char * +TsunamiIO::PITimer::Counter::CounterEvent::description() +{ + return "tsunami 8254 Interval timer"; +} + +TsunamiIO::TsunamiIO(Params *p) + : BasicPioDevice(p), tsunami(p->tsunami), pitimer(p->name + "pitimer"), + rtc(p->name + ".rtc", p->tsunami, p->frequency) +{ + pioSize = 0xff; + + // set the back pointer from tsunami to myself + tsunami->io = this; + + timerData = 0; + rtc.set_time(p->init_time == 0 ? time(NULL) : p->init_time); + picr = 0; + picInterrupting = false; +} + +Tick +TsunamiIO::frequency() const +{ + return Clock::Frequency / params()->frequency; +} + +Tick +TsunamiIO::read(Packet *pkt) +{ + assert(pkt->result == Packet::Unknown); + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + + Addr daddr = pkt->getAddr() - pioAddr; + + DPRINTF(Tsunami, "io read va=%#x size=%d IOPorrt=%#x\n", pkt->getAddr(), + pkt->getSize(), daddr); + + pkt->allocate(); + + if (pkt->getSize() == sizeof(uint8_t)) { + switch(daddr) { + // PIC1 mask read + case TSDEV_PIC1_MASK: + pkt->set(~mask1); + break; + case TSDEV_PIC2_MASK: + pkt->set(~mask2); + break; + case TSDEV_PIC1_ISR: + // !!! If this is modified 64bit case needs to be too + // Pal code has to do a 64 bit physical read because there is + // no load physical byte instruction + pkt->set(picr); + break; + case TSDEV_PIC2_ISR: + // PIC2 not implemnted... just return 0 + pkt->set(0x00); + break; + case TSDEV_TMR0_DATA: + pkt->set(pitimer.counter0.read()); + break; + case TSDEV_TMR1_DATA: + pkt->set(pitimer.counter1.read()); + break; + case TSDEV_TMR2_DATA: + pkt->set(pitimer.counter2.read()); + break; + case TSDEV_RTC_DATA: + pkt->set(rtc.readData()); + break; + case TSDEV_CTRL_PORTB: + if (pitimer.counter2.outputHigh()) + pkt->set(PORTB_SPKR_HIGH); + else + pkt->set(0x00); + break; + default: + panic("I/O Read - va%#x size %d\n", pkt->getAddr(), pkt->getSize()); + } + } else if (pkt->getSize() == sizeof(uint64_t)) { + if (daddr == TSDEV_PIC1_ISR) + pkt->set<uint64_t>(picr); + else + panic("I/O Read - invalid addr - va %#x size %d\n", + pkt->getAddr(), pkt->getSize()); + } else { + panic("I/O Read - invalid size - va %#x size %d\n", pkt->getAddr(), pkt->getSize()); + } + pkt->result = Packet::Success; + return pioDelay; +} + +Tick +TsunamiIO::write(Packet *pkt) +{ + assert(pkt->result == Packet::Unknown); + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + Addr daddr = pkt->getAddr() - pioAddr; + + DPRINTF(Tsunami, "io write - va=%#x size=%d IOPort=%#x Data=%#x\n", + pkt->getAddr(), pkt->getSize(), pkt->getAddr() & 0xfff, (uint32_t)pkt->get<uint8_t>()); + + assert(pkt->getSize() == sizeof(uint8_t)); + + switch(daddr) { + case TSDEV_PIC1_MASK: + mask1 = ~(pkt->get<uint8_t>()); + if ((picr & mask1) && !picInterrupting) { + picInterrupting = true; + tsunami->cchip->postDRIR(55); + DPRINTF(Tsunami, "posting pic interrupt to cchip\n"); + } + if ((!(picr & mask1)) && picInterrupting) { + picInterrupting = false; + tsunami->cchip->clearDRIR(55); + DPRINTF(Tsunami, "clearing pic interrupt\n"); + } + break; + case TSDEV_PIC2_MASK: + mask2 = pkt->get<uint8_t>(); + //PIC2 Not implemented to interrupt + break; + case TSDEV_PIC1_ACK: + // clear the interrupt on the PIC + picr &= ~(1 << (pkt->get<uint8_t>() & 0xF)); + if (!(picr & mask1)) + tsunami->cchip->clearDRIR(55); + break; + case TSDEV_DMA1_MODE: + mode1 = pkt->get<uint8_t>(); + break; + case TSDEV_DMA2_MODE: + mode2 = pkt->get<uint8_t>(); + break; + case TSDEV_TMR0_DATA: + pitimer.counter0.write(pkt->get<uint8_t>()); + break; + case TSDEV_TMR1_DATA: + pitimer.counter1.write(pkt->get<uint8_t>()); + break; + case TSDEV_TMR2_DATA: + pitimer.counter2.write(pkt->get<uint8_t>()); + break; + case TSDEV_TMR_CTRL: + pitimer.writeControl(pkt->get<uint8_t>()); + break; + case TSDEV_RTC_ADDR: + rtc.writeAddr(pkt->get<uint8_t>()); + break; + case TSDEV_RTC_DATA: + rtc.writeData(pkt->get<uint8_t>()); + break; + case TSDEV_KBD: + case TSDEV_DMA1_CMND: + case TSDEV_DMA2_CMND: + case TSDEV_DMA1_MMASK: + case TSDEV_DMA2_MMASK: + case TSDEV_PIC2_ACK: + case TSDEV_DMA1_RESET: + case TSDEV_DMA2_RESET: + case TSDEV_DMA1_MASK: + case TSDEV_DMA2_MASK: + case TSDEV_CTRL_PORTB: + break; + default: + panic("I/O Write - va%#x size %d data %#x\n", pkt->getAddr(), pkt->getSize(), pkt->get<uint8_t>()); + } + + pkt->result = Packet::Success; + return pioDelay; +} + +void +TsunamiIO::postPIC(uint8_t bitvector) +{ + //PIC2 Is not implemented, because nothing of interest there + picr |= bitvector; + if (picr & mask1) { + tsunami->cchip->postDRIR(55); + DPRINTF(Tsunami, "posting pic interrupt to cchip\n"); + } +} + +void +TsunamiIO::clearPIC(uint8_t bitvector) +{ + //PIC2 Is not implemented, because nothing of interest there + picr &= ~bitvector; + if (!(picr & mask1)) { + tsunami->cchip->clearDRIR(55); + DPRINTF(Tsunami, "clearing pic interrupt to cchip\n"); + } +} + +void +TsunamiIO::serialize(ostream &os) +{ + SERIALIZE_SCALAR(timerData); + SERIALIZE_SCALAR(mask1); + SERIALIZE_SCALAR(mask2); + SERIALIZE_SCALAR(mode1); + SERIALIZE_SCALAR(mode2); + SERIALIZE_SCALAR(picr); + SERIALIZE_SCALAR(picInterrupting); + + // Serialize the timers + pitimer.serialize("pitimer", os); + rtc.serialize("rtc", os); +} + +void +TsunamiIO::unserialize(Checkpoint *cp, const string §ion) +{ + UNSERIALIZE_SCALAR(timerData); + UNSERIALIZE_SCALAR(mask1); + UNSERIALIZE_SCALAR(mask2); + UNSERIALIZE_SCALAR(mode1); + UNSERIALIZE_SCALAR(mode2); + UNSERIALIZE_SCALAR(picr); + UNSERIALIZE_SCALAR(picInterrupting); + + // Unserialize the timers + pitimer.unserialize("pitimer", cp, section); + rtc.unserialize("rtc", cp, section); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(TsunamiIO) + + Param<Addr> pio_addr; + Param<Tick> pio_latency; + Param<Tick> frequency; + SimObjectParam<Platform *> platform; + SimObjectParam<System *> system; + Param<time_t> time; + SimObjectParam<Tsunami *> tsunami; + +END_DECLARE_SIM_OBJECT_PARAMS(TsunamiIO) + +BEGIN_INIT_SIM_OBJECT_PARAMS(TsunamiIO) + + INIT_PARAM(pio_addr, "Device Address"), + INIT_PARAM(pio_latency, "Programmed IO latency"), + INIT_PARAM(frequency, "clock interrupt frequency"), + INIT_PARAM(platform, "platform"), + INIT_PARAM(system, "system object"), + INIT_PARAM(time, "System time to use (0 for actual time"), + INIT_PARAM(tsunami, "Tsunami") + +END_INIT_SIM_OBJECT_PARAMS(TsunamiIO) + +CREATE_SIM_OBJECT(TsunamiIO) +{ + TsunamiIO::Params *p = new TsunamiIO::Params; + p->frequency = frequency; + p->name = getInstanceName(); + p->pio_addr = pio_addr; + p->pio_delay = pio_latency; + p->platform = platform; + p->system = system; + p->init_time = time; + p->tsunami = tsunami; + return new TsunamiIO(p); +} + +REGISTER_SIM_OBJECT("TsunamiIO", TsunamiIO) diff --git a/src/dev/tsunami_io.hh b/src/dev/tsunami_io.hh new file mode 100644 index 000000000..9084a1be8 --- /dev/null +++ b/src/dev/tsunami_io.hh @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Andrew Schultz + * Miguel Serrano + */ + +/** @file + * Tsunami I/O Space mapping including RTC/timer interrupts + */ + +#ifndef __DEV_TSUNAMI_IO_HH__ +#define __DEV_TSUNAMI_IO_HH__ + +#include "dev/io_device.hh" +#include "base/range.hh" +#include "dev/tsunami.hh" +#include "sim/eventq.hh" + +/** + * Tsunami I/O device is a catch all for all the south bridge stuff we care + * to implement. + */ +class TsunamiIO : public BasicPioDevice +{ + private: + struct tm tm; + + protected: + /** Real-Time Clock (MC146818) */ + class RTC + { + private: + /** Event for RTC periodic interrupt */ + struct RTCEvent : public Event + { + /** A pointer back to tsunami to create interrupt the processor. */ + Tsunami* tsunami; + Tick interval; + + RTCEvent(Tsunami* t, Tick i); + + /** Schedule the RTC periodic interrupt */ + void scheduleIntr(); + + /** Event process to occur at interrupt*/ + virtual void process(); + + /** Event description */ + virtual const char *description(); + }; + + private: + std::string _name; + const std::string &name() const { return _name; } + + /** RTC periodic interrupt event */ + RTCEvent event; + + /** Current RTC register address/index */ + int addr; + + /** Data for real-time clock function */ + union { + uint8_t clock_data[10]; + + struct { + uint8_t sec; + uint8_t sec_alrm; + uint8_t min; + uint8_t min_alrm; + uint8_t hour; + uint8_t hour_alrm; + uint8_t wday; + uint8_t mday; + uint8_t mon; + uint8_t year; + }; + }; + + /** RTC status register A */ + uint8_t stat_regA; + + /** RTC status register B */ + uint8_t stat_regB; + + public: + RTC(const std::string &name, Tsunami* t, Tick i); + + /** Set the initial RTC time/date */ + void set_time(time_t t); + + /** RTC address port: write address of RTC RAM data to access */ + void writeAddr(const uint8_t data); + + /** RTC write data */ + void writeData(const uint8_t data); + + /** RTC read data */ + uint8_t readData(); + + /** + * Serialize this object to the given output stream. + * @param base The base name of the counter object. + * @param os The stream to serialize to. + */ + void serialize(const std::string &base, std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param base The base name of the counter object. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + void unserialize(const std::string &base, Checkpoint *cp, + const std::string §ion); + }; + + /** Programmable Interval Timer (Intel 8254) */ + class PITimer + { + /** Counter element for PIT */ + class Counter + { + /** Event for counter interrupt */ + class CounterEvent : public Event + { + private: + /** Pointer back to Counter */ + Counter* counter; + Tick interval; + + public: + CounterEvent(Counter*); + + /** Event process */ + virtual void process(); + + /** Event description */ + virtual const char *description(); + + friend class Counter; + }; + + private: + std::string _name; + const std::string &name() const { return _name; } + + CounterEvent event; + + /** Current count value */ + uint16_t count; + + /** Latched count */ + uint16_t latched_count; + + /** Interrupt period */ + uint16_t period; + + /** Current mode of operation */ + uint8_t mode; + + /** Output goes high when the counter reaches zero */ + bool output_high; + + /** State of the count latch */ + bool latch_on; + + /** Set of values for read_byte and write_byte */ + enum {LSB, MSB}; + + /** Determine which byte of a 16-bit count value to read/write */ + uint8_t read_byte, write_byte; + + public: + Counter(const std::string &name); + + /** Latch the current count (if one is not already latched) */ + void latchCount(); + + /** Set the read/write mode */ + void setRW(int rw_val); + + /** Set operational mode */ + void setMode(int mode_val); + + /** Set count encoding */ + void setBCD(int bcd_val); + + /** Read a count byte */ + uint8_t read(); + + /** Write a count byte */ + void write(const uint8_t data); + + /** Is the output high? */ + bool outputHigh(); + + /** + * Serialize this object to the given output stream. + * @param base The base name of the counter object. + * @param os The stream to serialize to. + */ + void serialize(const std::string &base, std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param base The base name of the counter object. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + void unserialize(const std::string &base, Checkpoint *cp, + const std::string §ion); + }; + + private: + std::string _name; + const std::string &name() const { return _name; } + + /** PIT has three seperate counters */ + Counter *counter[3]; + + public: + /** Public way to access individual counters (avoid array accesses) */ + Counter counter0; + Counter counter1; + Counter counter2; + + PITimer(const std::string &name); + + /** Write control word */ + void writeControl(const uint8_t data); + + /** + * Serialize this object to the given output stream. + * @param base The base name of the counter object. + * @param os The stream to serialize to. + */ + void serialize(const std::string &base, std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param base The base name of the counter object. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + void unserialize(const std::string &base, Checkpoint *cp, + const std::string §ion); + }; + + /** Mask of the PIC1 */ + uint8_t mask1; + + /** Mask of the PIC2 */ + uint8_t mask2; + + /** Mode of PIC1. Not used for anything */ + uint8_t mode1; + + /** Mode of PIC2. Not used for anything */ + uint8_t mode2; + + /** Raw PIC interrupt register before masking */ + uint8_t picr; //Raw PIC interrput register + + /** Is the pic interrupting right now or not. */ + bool picInterrupting; + + /** A pointer to the Tsunami device which be belong to */ + Tsunami *tsunami; + + /** Intel 8253 Periodic Interval Timer */ + PITimer pitimer; + + RTC rtc; + + /** The interval is set via two writes to the PIT. + * This variable contains a flag as to how many writes have happened, and + * the time so far. + */ + uint16_t timerData; + + public: + /** + * Return the freqency of the RTC + * @return interrupt rate of the RTC + */ + Tick frequency() const; + + struct Params : public BasicPioDevice::Params + { + Tick frequency; + Tsunami *tsunami; + time_t init_time; + }; + protected: + const Params *params() const { return (const Params*)_params; } + + public: + /** + * Initialize all the data for devices supported by Tsunami I/O. + * @param p pointer to Params struct + */ + TsunamiIO(Params *p); + + virtual Tick read(Packet *pkt); + virtual Tick write(Packet *pkt); + + /** + * Post an PIC interrupt to the CPU via the CChip + * @param bitvector interrupt to post. + */ + void postPIC(uint8_t bitvector); + + /** + * Clear a posted interrupt + * @param bitvector interrupt to clear + */ + void clearPIC(uint8_t bitvector); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +}; + +#endif // __DEV_TSUNAMI_IO_HH__ diff --git a/src/dev/tsunami_pchip.cc b/src/dev/tsunami_pchip.cc new file mode 100644 index 000000000..8a542b9b0 --- /dev/null +++ b/src/dev/tsunami_pchip.cc @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Andrew Schultz + */ + +/** @file + * Tsunami PChip (pci) + */ + +#include <deque> +#include <string> +#include <vector> + +#include "base/trace.hh" +#include "dev/tsunami_pchip.hh" +#include "dev/tsunamireg.h" +#include "dev/tsunami.hh" +#include "mem/packet.hh" +#include "sim/builder.hh" +#include "sim/system.hh" + +using namespace std; +//Should this be AlphaISA? +using namespace TheISA; + +TsunamiPChip::TsunamiPChip(Params *p) +: BasicPioDevice(p) +{ + pioSize = 0xfff; + + for (int i = 0; i < 4; i++) { + wsba[i] = 0; + wsm[i] = 0; + tba[i] = 0; + } + + // initialize pchip control register + pctl = (ULL(0x1) << 20) | (ULL(0x1) << 32) | (ULL(0x2) << 36); + + //Set back pointer in tsunami + p->tsunami->pchip = this; +} + +Tick +TsunamiPChip::read(Packet *pkt) +{ + assert(pkt->result == Packet::Unknown); + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + + pkt->allocate(); + Addr daddr = (pkt->getAddr() - pioAddr) >> 6;; + assert(pkt->getSize() == sizeof(uint64_t)); + + + DPRINTF(Tsunami, "read va=%#x size=%d\n", pkt->getAddr(), pkt->getSize()); + + switch(daddr) { + case TSDEV_PC_WSBA0: + pkt->set(wsba[0]); + break; + case TSDEV_PC_WSBA1: + pkt->set(wsba[1]); + break; + case TSDEV_PC_WSBA2: + pkt->set(wsba[2]); + break; + case TSDEV_PC_WSBA3: + pkt->set(wsba[3]); + break; + case TSDEV_PC_WSM0: + pkt->set(wsm[0]); + break; + case TSDEV_PC_WSM1: + pkt->set(wsm[1]); + break; + case TSDEV_PC_WSM2: + pkt->set(wsm[2]); + break; + case TSDEV_PC_WSM3: + pkt->set(wsm[3]); + break; + case TSDEV_PC_TBA0: + pkt->set(tba[0]); + break; + case TSDEV_PC_TBA1: + pkt->set(tba[1]); + break; + case TSDEV_PC_TBA2: + pkt->set(tba[2]); + break; + case TSDEV_PC_TBA3: + pkt->set(tba[3]); + break; + case TSDEV_PC_PCTL: + pkt->set(pctl); + break; + case TSDEV_PC_PLAT: + panic("PC_PLAT not implemented\n"); + case TSDEV_PC_RES: + panic("PC_RES not implemented\n"); + case TSDEV_PC_PERROR: + pkt->set((uint64_t)0x00); + break; + case TSDEV_PC_PERRMASK: + pkt->set((uint64_t)0x00); + break; + case TSDEV_PC_PERRSET: + panic("PC_PERRSET not implemented\n"); + case TSDEV_PC_TLBIV: + panic("PC_TLBIV not implemented\n"); + case TSDEV_PC_TLBIA: + pkt->set((uint64_t)0x00); // shouldn't be readable, but linux + break; + case TSDEV_PC_PMONCTL: + panic("PC_PMONCTL not implemented\n"); + case TSDEV_PC_PMONCNT: + panic("PC_PMONCTN not implemented\n"); + default: + panic("Default in PChip Read reached reading 0x%x\n", daddr); + } + pkt->result = Packet::Success; + return pioDelay; + +} + +Tick +TsunamiPChip::write(Packet *pkt) +{ + assert(pkt->result == Packet::Unknown); + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + Addr daddr = (pkt->getAddr() - pioAddr) >> 6; + + assert(pkt->getSize() == sizeof(uint64_t)); + + DPRINTF(Tsunami, "write - va=%#x size=%d \n", pkt->getAddr(), pkt->getSize()); + + switch(daddr) { + case TSDEV_PC_WSBA0: + wsba[0] = pkt->get<uint64_t>(); + break; + case TSDEV_PC_WSBA1: + wsba[1] = pkt->get<uint64_t>(); + break; + case TSDEV_PC_WSBA2: + wsba[2] = pkt->get<uint64_t>(); + break; + case TSDEV_PC_WSBA3: + wsba[3] = pkt->get<uint64_t>(); + break; + case TSDEV_PC_WSM0: + wsm[0] = pkt->get<uint64_t>(); + break; + case TSDEV_PC_WSM1: + wsm[1] = pkt->get<uint64_t>(); + break; + case TSDEV_PC_WSM2: + wsm[2] = pkt->get<uint64_t>(); + break; + case TSDEV_PC_WSM3: + wsm[3] = pkt->get<uint64_t>(); + break; + case TSDEV_PC_TBA0: + tba[0] = pkt->get<uint64_t>(); + break; + case TSDEV_PC_TBA1: + tba[1] = pkt->get<uint64_t>(); + break; + case TSDEV_PC_TBA2: + tba[2] = pkt->get<uint64_t>(); + break; + case TSDEV_PC_TBA3: + tba[3] = pkt->get<uint64_t>(); + break; + case TSDEV_PC_PCTL: + pctl = pkt->get<uint64_t>(); + break; + case TSDEV_PC_PLAT: + panic("PC_PLAT not implemented\n"); + case TSDEV_PC_RES: + panic("PC_RES not implemented\n"); + case TSDEV_PC_PERROR: + break; + case TSDEV_PC_PERRMASK: + panic("PC_PERRMASK not implemented\n"); + case TSDEV_PC_PERRSET: + panic("PC_PERRSET not implemented\n"); + case TSDEV_PC_TLBIV: + panic("PC_TLBIV not implemented\n"); + case TSDEV_PC_TLBIA: + break; // value ignored, supposted to invalidate SG TLB + case TSDEV_PC_PMONCTL: + panic("PC_PMONCTL not implemented\n"); + case TSDEV_PC_PMONCNT: + panic("PC_PMONCTN not implemented\n"); + default: + panic("Default in PChip write reached reading 0x%x\n", daddr); + + } // uint64_t + + pkt->result = Packet::Success; + return pioDelay; +} + +#define DMA_ADDR_MASK ULL(0x3ffffffff) + +Addr +TsunamiPChip::translatePciToDma(Addr busAddr) +{ + // compare the address to the window base registers + uint64_t tbaMask = 0; + uint64_t baMask = 0; + + uint64_t windowMask = 0; + uint64_t windowBase = 0; + + uint64_t pteEntry = 0; + + Addr pteAddr; + Addr dmaAddr; + +#if 0 + DPRINTF(IdeDisk, "Translation for bus address: %#x\n", busAddr); + for (int i = 0; i < 4; i++) { + DPRINTF(IdeDisk, "(%d) base:%#x mask:%#x\n", + i, wsba[i], wsm[i]); + + windowBase = wsba[i]; + windowMask = ~wsm[i] & (ULL(0xfff) << 20); + + if ((busAddr & windowMask) == (windowBase & windowMask)) { + DPRINTF(IdeDisk, "Would have matched %d (wb:%#x wm:%#x --> ba&wm:%#x wb&wm:%#x)\n", + i, windowBase, windowMask, (busAddr & windowMask), + (windowBase & windowMask)); + } + } +#endif + + for (int i = 0; i < 4; i++) { + + windowBase = wsba[i]; + windowMask = ~wsm[i] & (ULL(0xfff) << 20); + + if ((busAddr & windowMask) == (windowBase & windowMask)) { + + if (wsba[i] & 0x1) { // see if enabled + if (wsba[i] & 0x2) { // see if SG bit is set + /** @todo + This currently is faked by just doing a direct + read from memory, however, to be realistic, this + needs to actually do a bus transaction. The process + is explained in the tsunami documentation on page + 10-12 and basically munges the address to look up a + PTE from a table in memory and then uses that mapping + to create an address for the SG page + */ + + tbaMask = ~(((wsm[i] & (ULL(0xfff) << 20)) >> 10) | ULL(0x3ff)); + baMask = (wsm[i] & (ULL(0xfff) << 20)) | (ULL(0x7f) << 13); + pteAddr = (tba[i] & tbaMask) | ((busAddr & baMask) >> 10); + + pioPort->readBlob(pteAddr, (uint8_t*)&pteEntry, sizeof(uint64_t)); + + dmaAddr = ((pteEntry & ~ULL(0x1)) << 12) | (busAddr & ULL(0x1fff)); + + } else { + baMask = (wsm[i] & (ULL(0xfff) << 20)) | ULL(0xfffff); + tbaMask = ~baMask; + dmaAddr = (tba[i] & tbaMask) | (busAddr & baMask); + } + + return (dmaAddr & DMA_ADDR_MASK); + } + } + } + + // if no match was found, then return the original address + return busAddr; +} +Addr +TsunamiPChip::calcConfigAddr(int bus, int dev, int func) +{ + assert(func < 8); + assert(dev < 32); + assert(bus == 0); + + return TsunamiPciBus0Config | (func << 8) | (dev << 11); +} + + + +void +TsunamiPChip::serialize(std::ostream &os) +{ + SERIALIZE_SCALAR(pctl); + SERIALIZE_ARRAY(wsba, 4); + SERIALIZE_ARRAY(wsm, 4); + SERIALIZE_ARRAY(tba, 4); +} + +void +TsunamiPChip::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(pctl); + UNSERIALIZE_ARRAY(wsba, 4); + UNSERIALIZE_ARRAY(wsm, 4); + UNSERIALIZE_ARRAY(tba, 4); +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(TsunamiPChip) + + Param<Addr> pio_addr; + Param<Tick> pio_latency; + SimObjectParam<Platform *> platform; + SimObjectParam<System *> system; + SimObjectParam<Tsunami *> tsunami; + +END_DECLARE_SIM_OBJECT_PARAMS(TsunamiPChip) + +BEGIN_INIT_SIM_OBJECT_PARAMS(TsunamiPChip) + + INIT_PARAM(pio_addr, "Device Address"), + INIT_PARAM(pio_latency, "Programmed IO latency"), + INIT_PARAM(platform, "platform"), + INIT_PARAM(system, "system object"), + INIT_PARAM(tsunami, "Tsunami") + +END_INIT_SIM_OBJECT_PARAMS(TsunamiPChip) + +CREATE_SIM_OBJECT(TsunamiPChip) +{ + TsunamiPChip::Params *p = new TsunamiPChip::Params; + p->name = getInstanceName(); + p->pio_addr = pio_addr; + p->pio_delay = pio_latency; + p->platform = platform; + p->system = system; + p->tsunami = tsunami; + return new TsunamiPChip(p); +} + +REGISTER_SIM_OBJECT("TsunamiPChip", TsunamiPChip) diff --git a/src/dev/tsunami_pchip.hh b/src/dev/tsunami_pchip.hh new file mode 100644 index 000000000..2c97a1fea --- /dev/null +++ b/src/dev/tsunami_pchip.hh @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** @file + * Tsunami PCI interface CSRs + */ + +#ifndef __TSUNAMI_PCHIP_HH__ +#define __TSUNAMI_PCHIP_HH__ + +#include "dev/tsunami.hh" +#include "base/range.hh" +#include "dev/io_device.hh" + +/** + * A very simple implementation of the Tsunami PCI interface chips. + */ +class TsunamiPChip : public BasicPioDevice +{ + protected: + + static const Addr TsunamiPciBus0Config = ULL(0x801fe000000); + + /** Pchip control register */ + uint64_t pctl; + + /** Window Base addresses */ + uint64_t wsba[4]; + + /** Window masks */ + uint64_t wsm[4]; + + /** Translated Base Addresses */ + uint64_t tba[4]; + + public: + struct Params : public BasicPioDevice::Params + { + Tsunami *tsunami; + }; + protected: + const Params *params() const { return (const Params*)_params; } + + public: + /** + * Register the PChip with the mmu and init all wsba, wsm, and tba to 0 + * @param p pointer to the parameters struct + */ + TsunamiPChip(Params *p); + + /** + * Translate a PCI bus address to a memory address for DMA. + * @todo Andrew says this needs to be fixed. What's wrong with it? + * @param busAddr PCI address to translate. + * @return memory system address + */ + Addr translatePciToDma(Addr busAddr); + + Addr calcConfigAddr(int bus, int dev, int func); + + virtual Tick read(Packet *pkt); + virtual Tick write(Packet *pkt); + + /** + * Serialize this object to the given output stream. + * @param os The stream to serialize to. + */ + virtual void serialize(std::ostream &os); + + /** + * Reconstruct the state of this object from a checkpoint. + * @param cp The checkpoint use. + * @param section The section name of this object + */ + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +#endif // __TSUNAMI_PCHIP_HH__ diff --git a/src/dev/tsunamireg.h b/src/dev/tsunamireg.h new file mode 100644 index 000000000..d603972be --- /dev/null +++ b/src/dev/tsunamireg.h @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** @file + * List of Tsunami CSRs + */ + +#ifndef __TSUNAMIREG_H__ +#define __TSUNAMIREG_H__ + +#define ALPHA_K0SEG_BASE ULL(0xfffffc0000000000) + +// CChip Registers +#define TSDEV_CC_CSR 0x00 +#define TSDEV_CC_MTR 0x01 +#define TSDEV_CC_MISC 0x02 + +#define TSDEV_CC_AAR0 0x04 +#define TSDEV_CC_AAR1 0x05 +#define TSDEV_CC_AAR2 0x06 +#define TSDEV_CC_AAR3 0x07 +#define TSDEV_CC_DIM0 0x08 +#define TSDEV_CC_DIM1 0x09 +#define TSDEV_CC_DIR0 0x0A +#define TSDEV_CC_DIR1 0x0B +#define TSDEV_CC_DRIR 0x0C +#define TSDEV_CC_PRBEN 0x0D +#define TSDEV_CC_IIC0 0x0E +#define TSDEV_CC_IIC1 0x0F +#define TSDEV_CC_MPR0 0x10 +#define TSDEV_CC_MPR1 0x11 +#define TSDEV_CC_MPR2 0x12 +#define TSDEV_CC_MPR3 0x13 + +#define TSDEV_CC_DIM2 0x18 +#define TSDEV_CC_DIM3 0x19 +#define TSDEV_CC_DIR2 0x1A +#define TSDEV_CC_DIR3 0x1B +#define TSDEV_CC_IIC2 0x1C +#define TSDEV_CC_IIC3 0x1D + +// BigTsunami Registers +#define TSDEV_CC_BDIMS 0x1000000 +#define TSDEV_CC_BDIRS 0x2000000 +#define TSDEV_CC_IPIQ 0x20 //0xf01a000800 +#define TSDEV_CC_IPIR 0x21 //0xf01a000840 +#define TSDEV_CC_ITIR 0x22 //0xf01a000880 + + +// PChip Registers +#define TSDEV_PC_WSBA0 0x00 +#define TSDEV_PC_WSBA1 0x01 +#define TSDEV_PC_WSBA2 0x02 +#define TSDEV_PC_WSBA3 0x03 +#define TSDEV_PC_WSM0 0x04 +#define TSDEV_PC_WSM1 0x05 +#define TSDEV_PC_WSM2 0x06 +#define TSDEV_PC_WSM3 0x07 +#define TSDEV_PC_TBA0 0x08 +#define TSDEV_PC_TBA1 0x09 +#define TSDEV_PC_TBA2 0x0A +#define TSDEV_PC_TBA3 0x0B +#define TSDEV_PC_PCTL 0x0C +#define TSDEV_PC_PLAT 0x0D +#define TSDEV_PC_RES 0x0E +#define TSDEV_PC_PERROR 0x0F +#define TSDEV_PC_PERRMASK 0x10 +#define TSDEV_PC_PERRSET 0x11 +#define TSDEV_PC_TLBIV 0x12 +#define TSDEV_PC_TLBIA 0x13 +#define TSDEV_PC_PMONCTL 0x14 +#define TSDEV_PC_PMONCNT 0x15 + +#define TSDEV_PC_SPST 0x20 + + +// DChip Registers +#define TSDEV_DC_DSC 0x20 +#define TSDEV_DC_STR 0x21 +#define TSDEV_DC_DREV 0x22 +#define TSDEV_DC_DSC2 0x23 + +// I/O Ports +#define TSDEV_PIC1_MASK 0x21 +#define TSDEV_PIC2_MASK 0xA1 +#define TSDEV_PIC1_ISR 0x20 +#define TSDEV_PIC2_ISR 0xA0 +#define TSDEV_PIC1_ACK 0x20 +#define TSDEV_PIC2_ACK 0xA0 +#define TSDEV_DMA1_RESET 0x0D +#define TSDEV_DMA2_RESET 0xDA +#define TSDEV_DMA1_MODE 0x0B +#define TSDEV_DMA2_MODE 0xD6 +#define TSDEV_DMA1_MASK 0x0A +#define TSDEV_DMA2_MASK 0xD4 +#define TSDEV_CTRL_PORTB 0x61 +#define TSDEV_TMR0_DATA 0x40 +#define TSDEV_TMR1_DATA 0x41 +#define TSDEV_TMR2_DATA 0x42 +#define TSDEV_TMR_CTRL 0x43 +#define TSDEV_KBD 0x64 +#define TSDEV_DMA1_CMND 0x08 +#define TSDEV_DMA1_STAT TSDEV_DMA1_CMND +#define TSDEV_DMA2_CMND 0xD0 +#define TSDEV_DMA2_STAT TSDEV_DMA2_CMND +#define TSDEV_DMA1_MMASK 0x0F +#define TSDEV_DMA2_MMASK 0xDE + +/* Added for keyboard accesses */ +#define TSDEV_KBD 0x64 + +/* Added for ATA PCI DMA */ +#define ATA_PCI_DMA 0x00 +#define ATA_PCI_DMA2 0x02 +#define ATA_PCI_DMA3 0x16 +#define ATA_PCI_DMA4 0x17 +#define ATA_PCI_DMA5 0x1a +#define ATA_PCI_DMA6 0x11 +#define ATA_PCI_DMA7 0x14 + +#define TSDEV_RTC_ADDR 0x70 +#define TSDEV_RTC_DATA 0x71 + +#define PCHIP_PCI0_MEMORY ULL(0x00000000000) +#define PCHIP_PCI0_IO ULL(0x001FC000000) +#define TSUNAMI_UNCACHABLE_BIT ULL(0x80000000000) +#define TSUNAMI_PCI0_MEMORY TSUNAMI_UNCACHABLE_BIT + PCHIP_PCI0_MEMORY +#define TSUNAMI_PCI0_IO TSUNAMI_UNCACHABLE_BIT + PCHIP_PCI0_IO + + +// UART Defines +#define UART_IER_RDI 0x01 +#define UART_IER_THRI 0x02 +#define UART_IER_RLSI 0x04 + + +#define UART_LSR_TEMT 0x40 +#define UART_LSR_THRE 0x20 +#define UART_LSR_DR 0x01 + +#define UART_MCR_LOOP 0x10 + +// System Control PortB Status Bits +#define PORTB_SPKR_HIGH 0x20 + +#endif // __TSUNAMIREG_H__ diff --git a/src/dev/uart.cc b/src/dev/uart.cc new file mode 100644 index 000000000..f769b720b --- /dev/null +++ b/src/dev/uart.cc @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** @file + * Implements a base class for UARTs + */ + +#include "dev/simconsole.hh" +#include "dev/uart.hh" +#include "dev/platform.hh" +#include "sim/builder.hh" + +using namespace std; + +Uart::Uart(Params *p) + : BasicPioDevice(p), platform(p->platform), cons(p->cons) +{ + + status = 0; + + // set back pointers + cons->uart = this; + platform->uart = this; +} + +DEFINE_SIM_OBJECT_CLASS_NAME("Uart", Uart) + diff --git a/src/dev/uart.hh b/src/dev/uart.hh new file mode 100644 index 000000000..f0ae0854c --- /dev/null +++ b/src/dev/uart.hh @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** @file + * Base class for UART + */ + +#ifndef __UART_HH__ +#define __UART_HH__ + +#include "base/range.hh" +#include "dev/io_device.hh" + +class SimConsole; +class Platform; + +const int RX_INT = 0x1; +const int TX_INT = 0x2; + + +class Uart : public BasicPioDevice +{ + + protected: + int status; + Platform *platform; + SimConsole *cons; + + public: + struct Params : public BasicPioDevice::Params + { + SimConsole *cons; + }; + + Uart(Params *p); + + /** + * Inform the uart that there is data available. + */ + virtual void dataAvailable() = 0; + + + /** + * Return if we have an interrupt pending + * @return interrupt status + */ + bool intStatus() { return status ? true : false; } + + protected: + const Params *params() const {return (const Params *)_params; } + +}; + +#endif // __UART_HH__ diff --git a/src/dev/uart8250.cc b/src/dev/uart8250.cc new file mode 100644 index 000000000..b92527b5a --- /dev/null +++ b/src/dev/uart8250.cc @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** @file + * Implements a 8250 UART + */ + +#include <string> +#include <vector> + +#include "arch/alpha/ev5.hh" +#include "base/inifile.hh" +#include "base/str.hh" // for to_number +#include "base/trace.hh" +#include "dev/simconsole.hh" +#include "dev/uart8250.hh" +#include "dev/platform.hh" +#include "sim/builder.hh" + +using namespace std; +using namespace TheISA; + +Uart8250::IntrEvent::IntrEvent(Uart8250 *u, int bit) + : Event(&mainEventQueue), uart(u) +{ + DPRINTF(Uart, "UART Interrupt Event Initilizing\n"); + intrBit = bit; +} + +const char * +Uart8250::IntrEvent::description() +{ + return "uart interrupt delay event"; +} + +void +Uart8250::IntrEvent::process() +{ + if (intrBit & uart->IER) { + DPRINTF(Uart, "UART InterEvent, interrupting\n"); + uart->platform->postConsoleInt(); + uart->status |= intrBit; + } + else + DPRINTF(Uart, "UART InterEvent, not interrupting\n"); + +} + +/* The linux serial driver (8250.c about line 1182) loops reading from + * the device until the device reports it has no more data to + * read. After a maximum of 255 iterations the code prints "serial8250 + * too much work for irq X," and breaks out of the loop. Since the + * simulated system is so much slower than the actual system, if a + * user is typing on the keyboard it is very easy for them to provide + * input at a fast enough rate to not allow the loop to exit and thus + * the error to be printed. This magic number provides a delay between + * the time the UART receives a character to send to the simulated + * system and the time it actually notifies the system it has a + * character to send to alleviate this problem. --Ali + */ +void +Uart8250::IntrEvent::scheduleIntr() +{ + static const Tick interval = (Tick)((Clock::Float::s / 2e9) * 450); + DPRINTF(Uart, "Scheduling IER interrupt for %#x, at cycle %lld\n", intrBit, + curTick + interval); + if (!scheduled()) + schedule(curTick + interval); + else + reschedule(curTick + interval); +} + + +Uart8250::Uart8250(Params *p) + : Uart(p), txIntrEvent(this, TX_INT), rxIntrEvent(this, RX_INT) +{ + pioSize = 8; + + IER = 0; + DLAB = 0; + LCR = 0; + MCR = 0; +} + +Tick +Uart8250::read(Packet *pkt) +{ + assert(pkt->result == Packet::Unknown); + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + assert(pkt->getSize() == 1); + + Addr daddr = pkt->getAddr() - pioAddr; + pkt->allocate(); + + DPRINTF(Uart, " read register %#x\n", daddr); + + switch (daddr) { + case 0x0: + if (!(LCR & 0x80)) { // read byte + if (cons->dataAvailable()) + pkt->set(cons->in()); + else { + pkt->set((uint8_t)0); + // A limited amount of these are ok. + DPRINTF(Uart, "empty read of RX register\n"); + } + status &= ~RX_INT; + platform->clearConsoleInt(); + + if (cons->dataAvailable() && (IER & UART_IER_RDI)) + rxIntrEvent.scheduleIntr(); + } else { // dll divisor latch + ; + } + break; + case 0x1: + if (!(LCR & 0x80)) { // Intr Enable Register(IER) + pkt->set(IER); + } else { // DLM divisor latch MSB + ; + } + break; + case 0x2: // Intr Identification Register (IIR) + DPRINTF(Uart, "IIR Read, status = %#x\n", (uint32_t)status); + + if (status & RX_INT) /* Rx data interrupt has a higher priority */ + pkt->set(IIR_RXID); + else if (status & TX_INT) + pkt->set(IIR_TXID); + else + pkt->set(IIR_NOPEND); + + //Tx interrupts are cleared on IIR reads + status &= ~TX_INT; + break; + case 0x3: // Line Control Register (LCR) + pkt->set(LCR); + break; + case 0x4: // Modem Control Register (MCR) + break; + case 0x5: // Line Status Register (LSR) + uint8_t lsr; + lsr = 0; + // check if there are any bytes to be read + if (cons->dataAvailable()) + lsr = UART_LSR_DR; + lsr |= UART_LSR_TEMT | UART_LSR_THRE; + pkt->set(lsr); + break; + case 0x6: // Modem Status Register (MSR) + pkt->set((uint8_t)0); + break; + case 0x7: // Scratch Register (SCR) + pkt->set((uint8_t)0); // doesn't exist with at 8250. + break; + default: + panic("Tried to access a UART port that doesn't exist\n"); + break; + } +/* uint32_t d32 = *data; + DPRINTF(Uart, "Register read to register %#x returned %#x\n", daddr, d32); +*/ + pkt->result = Packet::Success; + return pioDelay; +} + +Tick +Uart8250::write(Packet *pkt) +{ + + assert(pkt->result == Packet::Unknown); + assert(pkt->getAddr() >= pioAddr && pkt->getAddr() < pioAddr + pioSize); + assert(pkt->getSize() == 1); + + Addr daddr = pkt->getAddr() - pioAddr; + + DPRINTF(Uart, " write register %#x value %#x\n", daddr, pkt->get<uint8_t>()); + + switch (daddr) { + case 0x0: + if (!(LCR & 0x80)) { // write byte + cons->out(pkt->get<uint8_t>()); + platform->clearConsoleInt(); + status &= ~TX_INT; + if (UART_IER_THRI & IER) + txIntrEvent.scheduleIntr(); + } else { // dll divisor latch + ; + } + break; + case 0x1: + if (!(LCR & 0x80)) { // Intr Enable Register(IER) + IER = pkt->get<uint8_t>(); + if (UART_IER_THRI & IER) + { + DPRINTF(Uart, "IER: IER_THRI set, scheduling TX intrrupt\n"); + txIntrEvent.scheduleIntr(); + } + else + { + DPRINTF(Uart, "IER: IER_THRI cleared, descheduling TX intrrupt\n"); + if (txIntrEvent.scheduled()) + txIntrEvent.deschedule(); + if (status & TX_INT) + platform->clearConsoleInt(); + status &= ~TX_INT; + } + + if ((UART_IER_RDI & IER) && cons->dataAvailable()) { + DPRINTF(Uart, "IER: IER_RDI set, scheduling RX intrrupt\n"); + rxIntrEvent.scheduleIntr(); + } else { + DPRINTF(Uart, "IER: IER_RDI cleared, descheduling RX intrrupt\n"); + if (rxIntrEvent.scheduled()) + rxIntrEvent.deschedule(); + if (status & RX_INT) + platform->clearConsoleInt(); + status &= ~RX_INT; + } + } else { // DLM divisor latch MSB + ; + } + break; + case 0x2: // FIFO Control Register (FCR) + break; + case 0x3: // Line Control Register (LCR) + LCR = pkt->get<uint8_t>(); + break; + case 0x4: // Modem Control Register (MCR) + if (pkt->get<uint8_t>() == (UART_MCR_LOOP | 0x0A)) + MCR = 0x9A; + break; + case 0x7: // Scratch Register (SCR) + // We are emulating a 8250 so we don't have a scratch reg + break; + default: + panic("Tried to access a UART port that doesn't exist\n"); + break; + } + pkt->result = Packet::Success; + return pioDelay; +} + +void +Uart8250::dataAvailable() +{ + // if the kernel wants an interrupt when we have data + if (IER & UART_IER_RDI) + { + platform->postConsoleInt(); + status |= RX_INT; + } + +} + +void +Uart8250::addressRanges(AddrRangeList &range_list) +{ + assert(pioSize != 0); + range_list.clear(); + range_list.push_back(RangeSize(pioAddr, pioSize)); +} + + + +void +Uart8250::serialize(ostream &os) +{ + SERIALIZE_SCALAR(status); + SERIALIZE_SCALAR(IER); + SERIALIZE_SCALAR(DLAB); + SERIALIZE_SCALAR(LCR); + SERIALIZE_SCALAR(MCR); + Tick rxintrwhen; + if (rxIntrEvent.scheduled()) + rxintrwhen = rxIntrEvent.when(); + else + rxintrwhen = 0; + Tick txintrwhen; + if (txIntrEvent.scheduled()) + txintrwhen = txIntrEvent.when(); + else + txintrwhen = 0; + SERIALIZE_SCALAR(rxintrwhen); + SERIALIZE_SCALAR(txintrwhen); +} + +void +Uart8250::unserialize(Checkpoint *cp, const std::string §ion) +{ + UNSERIALIZE_SCALAR(status); + UNSERIALIZE_SCALAR(IER); + UNSERIALIZE_SCALAR(DLAB); + UNSERIALIZE_SCALAR(LCR); + UNSERIALIZE_SCALAR(MCR); + Tick rxintrwhen; + Tick txintrwhen; + UNSERIALIZE_SCALAR(rxintrwhen); + UNSERIALIZE_SCALAR(txintrwhen); + if (rxintrwhen != 0) + rxIntrEvent.schedule(rxintrwhen); + if (txintrwhen != 0) + txIntrEvent.schedule(txintrwhen); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(Uart8250) + + Param<Addr> pio_addr; + Param<Tick> pio_latency; + SimObjectParam<Platform *> platform; + SimObjectParam<SimConsole *> sim_console; + SimObjectParam<System *> system; + +END_DECLARE_SIM_OBJECT_PARAMS(Uart8250) + +BEGIN_INIT_SIM_OBJECT_PARAMS(Uart8250) + + INIT_PARAM(pio_addr, "Device Address"), + INIT_PARAM_DFLT(pio_latency, "Programmed IO latency", 1000), + INIT_PARAM(platform, "platform"), + INIT_PARAM(sim_console, "The Simulator Console"), + INIT_PARAM(system, "system object") + +END_INIT_SIM_OBJECT_PARAMS(Uart8250) + +CREATE_SIM_OBJECT(Uart8250) +{ + Uart8250::Params *p = new Uart8250::Params; + p->name = getInstanceName(); + p->pio_addr = pio_addr; + p->pio_delay = pio_latency; + p->platform = platform; + p->cons = sim_console; + p->system = system; + return new Uart8250(p); +} + +REGISTER_SIM_OBJECT("Uart8250", Uart8250) + diff --git a/src/dev/uart8250.hh b/src/dev/uart8250.hh new file mode 100644 index 000000000..44e90132f --- /dev/null +++ b/src/dev/uart8250.hh @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** @file + * Defines a 8250 UART + */ + +#ifndef __DEV_UART8250_HH__ +#define __DEV_UART8250_HH__ + +#include "dev/tsunamireg.h" +#include "base/range.hh" +#include "dev/io_device.hh" +#include "dev/uart.hh" + + +/* UART8250 Interrupt ID Register + * bit 0 Interrupt Pending 0 = true, 1 = false + * bit 2:1 ID of highest priority interrupt + * bit 7:3 zeroes + */ +const uint8_t IIR_NOPEND = 0x1; + +// Interrupt IDs +const uint8_t IIR_MODEM = 0x00; /* Modem Status (lowest priority) */ +const uint8_t IIR_TXID = 0x02; /* Tx Data */ +const uint8_t IIR_RXID = 0x04; /* Rx Data */ +const uint8_t IIR_LINE = 0x06; /* Rx Line Status (highest priority)*/ + +class SimConsole; +class Platform; + +class Uart8250 : public Uart +{ + + + protected: + uint8_t IER, DLAB, LCR, MCR; + + class IntrEvent : public Event + { + protected: + Uart8250 *uart; + int intrBit; + public: + IntrEvent(Uart8250 *u, int bit); + virtual void process(); + virtual const char *description(); + void scheduleIntr(); + }; + + IntrEvent txIntrEvent; + IntrEvent rxIntrEvent; + + public: + Uart8250(Params *p); + + virtual Tick read(Packet *pkt); + virtual Tick write(Packet *pkt); + virtual void addressRanges(AddrRangeList &range_list); + + + /** + * Inform the uart that there is data available. + */ + virtual void dataAvailable(); + + + /** + * Return if we have an interrupt pending + * @return interrupt status + */ + virtual bool intStatus() { return status ? true : false; } + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +}; + +#endif // __TSUNAMI_UART_HH__ diff --git a/src/doxygen/footer.html b/src/doxygen/footer.html new file mode 100644 index 000000000..6ef5293de --- /dev/null +++ b/src/doxygen/footer.html @@ -0,0 +1,5 @@ +<hr size="1"><address style="align: right;"><small> +Generated on $datetime for $projectname by <a href="http://www.doxygen.org/index.html"> doxygen</a> $doxygenversion</small></address> + +</body> +</html> diff --git a/src/doxygen/stl.hh b/src/doxygen/stl.hh new file mode 100644 index 000000000..fd9f68140 --- /dev/null +++ b/src/doxygen/stl.hh @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Nathan Binkert + */ + +/** + * @file + * Dummy definitions of STL classes to pick up relationships in doxygen. + */ + +namespace std { + +/** STL vector class*/ +template <class T> class vector { + public: + /** Dummy Item */ + T item; +}; + +/** STL deque class */ +template <class T> class deque { + public: + /** Dummy Item */ + T item; +}; + +/** STL list class */ +template <class T> class list { + public: + /** Dummy Item */ + T item; +}; + +/** STL pair class */ +template <class X, class Y> class pair { + public: + /** Dummy Item */ + X item1; + /** Dummy Item */ + Y item2; +}; + +} diff --git a/src/kern/kernel_stats.cc b/src/kern/kernel_stats.cc new file mode 100644 index 000000000..f7868b50f --- /dev/null +++ b/src/kern/kernel_stats.cc @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Lisa Hsu + * Nathan Binkert + */ + +#include <map> +#include <stack> +#include <string> + +#include "arch/alpha/osfpal.hh" +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "kern/kernel_stats.hh" +#include "kern/tru64/tru64_syscalls.hh" +#include "sim/system.hh" + +using namespace std; +using namespace Stats; + +namespace Kernel { + +const char *modestr[] = { "kernel", "user", "idle" }; + +Statistics::Statistics(System *system) + : idleProcess((Addr)-1), themode(kernel), lastModeTick(0), + iplLast(0), iplLastTick(0) +{ +} + +void +Statistics::regStats(const string &_name) +{ + myname = _name; + + _arm + .name(name() + ".inst.arm") + .desc("number of arm instructions executed") + ; + + _quiesce + .name(name() + ".inst.quiesce") + .desc("number of quiesce instructions executed") + ; + + _ivlb + .name(name() + ".inst.ivlb") + .desc("number of ivlb instructions executed") + ; + + _ivle + .name(name() + ".inst.ivle") + .desc("number of ivle instructions executed") + ; + + _hwrei + .name(name() + ".inst.hwrei") + .desc("number of hwrei instructions executed") + ; + + _iplCount + .init(32) + .name(name() + ".ipl_count") + .desc("number of times we switched to this ipl") + .flags(total | pdf | nozero | nonan) + ; + + _iplGood + .init(32) + .name(name() + ".ipl_good") + .desc("number of times we switched to this ipl from a different ipl") + .flags(total | pdf | nozero | nonan) + ; + + _iplTicks + .init(32) + .name(name() + ".ipl_ticks") + .desc("number of cycles we spent at this ipl") + .flags(total | pdf | nozero | nonan) + ; + + _iplUsed + .name(name() + ".ipl_used") + .desc("fraction of swpipl calls that actually changed the ipl") + .flags(total | nozero | nonan) + ; + + _iplUsed = _iplGood / _iplCount; + + _callpal + .init(256) + .name(name() + ".callpal") + .desc("number of callpals executed") + .flags(total | pdf | nozero | nonan) + ; + + for (int i = 0; i < PAL::NumCodes; ++i) { + const char *str = PAL::name(i); + if (str) + _callpal.subname(i, str); + } + + _syscall + .init(SystemCalls<Tru64>::Number) + .name(name() + ".syscall") + .desc("number of syscalls executed") + .flags(total | pdf | nozero | nonan) + ; + + for (int i = 0; i < SystemCalls<Tru64>::Number; ++i) { + const char *str = SystemCalls<Tru64>::name(i); + if (str) { + _syscall.subname(i, str); + } + } + + _mode + .init(cpu_mode_num) + .name(name() + ".mode_switch") + .desc("number of protection mode switches") + ; + + for (int i = 0; i < cpu_mode_num; ++i) + _mode.subname(i, modestr[i]); + + _modeGood + .init(cpu_mode_num) + .name(name() + ".mode_good") + ; + + for (int i = 0; i < cpu_mode_num; ++i) + _modeGood.subname(i, modestr[i]); + + _modeFraction + .name(name() + ".mode_switch_good") + .desc("fraction of useful protection mode switches") + .flags(total) + ; + + for (int i = 0; i < cpu_mode_num; ++i) + _modeFraction.subname(i, modestr[i]); + + _modeFraction = _modeGood / _mode; + + _modeTicks + .init(cpu_mode_num) + .name(name() + ".mode_ticks") + .desc("number of ticks spent at the given mode") + .flags(pdf) + ; + for (int i = 0; i < cpu_mode_num; ++i) + _modeTicks.subname(i, modestr[i]); + + _swap_context + .name(name() + ".swap_context") + .desc("number of times the context was actually changed") + ; +} + +void +Statistics::setIdleProcess(Addr idlepcbb, ThreadContext *tc) +{ + assert(themode == kernel); + idleProcess = idlepcbb; + themode = idle; + changeMode(themode, tc); +} + +void +Statistics::changeMode(cpu_mode newmode, ThreadContext *tc) +{ + _mode[newmode]++; + + if (newmode == themode) + return; + + DPRINTF(Context, "old mode=%-8s new mode=%-8s\n", + modestr[themode], modestr[newmode]); + + _modeGood[newmode]++; + _modeTicks[themode] += curTick - lastModeTick; + + lastModeTick = curTick; + themode = newmode; +} + +void +Statistics::swpipl(int ipl) +{ + assert(ipl >= 0 && ipl <= 0x1f && "invalid IPL\n"); + + _iplCount[ipl]++; + + if (ipl == iplLast) + return; + + _iplGood[ipl]++; + _iplTicks[iplLast] += curTick - iplLastTick; + iplLastTick = curTick; + iplLast = ipl; +} + +void +Statistics::mode(cpu_mode newmode, ThreadContext *tc) +{ + Addr pcbb = tc->readMiscReg(AlphaISA::IPR_PALtemp23); + + if (newmode == kernel && pcbb == idleProcess) + newmode = idle; + + changeMode(newmode, tc); +} + +void +Statistics::context(Addr oldpcbb, Addr newpcbb, ThreadContext *tc) +{ + assert(themode != user); + + _swap_context++; + changeMode(newpcbb == idleProcess ? idle : kernel, tc); +} + +void +Statistics::callpal(int code, ThreadContext *tc) +{ + if (!PAL::name(code)) + return; + + _callpal[code]++; + + switch (code) { + case PAL::callsys: { + int number = tc->readIntReg(0); + if (SystemCalls<Tru64>::validSyscallNumber(number)) { + int cvtnum = SystemCalls<Tru64>::convert(number); + _syscall[cvtnum]++; + } + } break; + } +} + +void +Statistics::serialize(ostream &os) +{ + int exemode = themode; + SERIALIZE_SCALAR(exemode); + SERIALIZE_SCALAR(idleProcess); + SERIALIZE_SCALAR(iplLast); + SERIALIZE_SCALAR(iplLastTick); + SERIALIZE_SCALAR(lastModeTick); +} + +void +Statistics::unserialize(Checkpoint *cp, const string §ion) +{ + int exemode; + UNSERIALIZE_SCALAR(exemode); + UNSERIALIZE_SCALAR(idleProcess); + UNSERIALIZE_SCALAR(iplLast); + UNSERIALIZE_SCALAR(iplLastTick); + UNSERIALIZE_SCALAR(lastModeTick); + themode = (cpu_mode)exemode; +} + +/* end namespace Kernel */ } diff --git a/src/kern/kernel_stats.hh b/src/kern/kernel_stats.hh new file mode 100644 index 000000000..c691ad8cf --- /dev/null +++ b/src/kern/kernel_stats.hh @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Lisa Hsu + * Nathan Binkert + */ + +#ifndef __KERNEL_STATS_HH__ +#define __KERNEL_STATS_HH__ + +#include <map> +#include <stack> +#include <string> +#include <vector> + +#include "cpu/static_inst.hh" + +class BaseCPU; +class ThreadContext; +class FnEvent; +// What does kernel stats expect is included? +class System; + +namespace Kernel { + +enum cpu_mode { kernel, user, idle, cpu_mode_num }; +extern const char *modestr[]; + +class Statistics : public Serializable +{ + private: + std::string myname; + + Addr idleProcess; + cpu_mode themode; + Tick lastModeTick; + + void changeMode(cpu_mode newmode, ThreadContext *tc); + + private: + Stats::Scalar<> _arm; + Stats::Scalar<> _quiesce; + Stats::Scalar<> _ivlb; + Stats::Scalar<> _ivle; + Stats::Scalar<> _hwrei; + + Stats::Vector<> _iplCount; + Stats::Vector<> _iplGood; + Stats::Vector<> _iplTicks; + Stats::Formula _iplUsed; + + Stats::Vector<> _callpal; + Stats::Vector<> _syscall; +// Stats::Vector<> _faults; + + Stats::Vector<> _mode; + Stats::Vector<> _modeGood; + Stats::Formula _modeFraction; + Stats::Vector<> _modeTicks; + + Stats::Scalar<> _swap_context; + + private: + int iplLast; + Tick iplLastTick; + + public: + Statistics(System *system); + + const std::string name() const { return myname; } + void regStats(const std::string &name); + + public: + void arm() { _arm++; } + void quiesce() { _quiesce++; } + void ivlb() { _ivlb++; } + void ivle() { _ivle++; } + void hwrei() { _hwrei++; } + void swpipl(int ipl); + void mode(cpu_mode newmode, ThreadContext *tc); + void context(Addr oldpcbb, Addr newpcbb, ThreadContext *tc); + void callpal(int code, ThreadContext *tc); + + void setIdleProcess(Addr idle, ThreadContext *tc); + + public: + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +/* end namespace Kernel */ } + +#endif // __KERNEL_STATS_HH__ diff --git a/src/kern/linux/events.cc b/src/kern/linux/events.cc new file mode 100644 index 000000000..289ece5ce --- /dev/null +++ b/src/kern/linux/events.cc @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ali Saidi + */ + +#include "arch/arguments.hh" +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "kern/linux/events.hh" +#include "kern/linux/printk.hh" +#include "kern/system_events.hh" +#include "sim/system.hh" + + +namespace Linux { + +void +DebugPrintkEvent::process(ThreadContext *tc) +{ + if (DTRACE(DebugPrintf)) { + if (!raw) { + StringWrap name(tc->getSystemPtr()->name() + ".dprintk"); + DPRINTFN(""); + } + + AlphaISA::AlphaArguments args(tc); + Printk(args); + SkipFuncEvent::process(tc); + } +} + +} // namespace linux diff --git a/src/kern/linux/events.hh b/src/kern/linux/events.hh new file mode 100644 index 000000000..b0510c18f --- /dev/null +++ b/src/kern/linux/events.hh @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2004-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ali Saidi + */ + +#ifndef __KERN_LINUX_EVENTS_HH__ +#define __KERN_LINUX_EVENTS_HH__ + +#include "kern/system_events.hh" + +namespace Linux { + +class DebugPrintkEvent : public SkipFuncEvent +{ + private: + bool raw; + + public: + DebugPrintkEvent(PCEventQueue *q, const std::string &desc, Addr addr, + bool r = false) + : SkipFuncEvent(q, desc, addr), raw(r) {} + virtual void process(ThreadContext *xc); +}; + +} + +#endif diff --git a/src/kern/linux/linux.hh b/src/kern/linux/linux.hh new file mode 100644 index 000000000..3a2677642 --- /dev/null +++ b/src/kern/linux/linux.hh @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#ifndef __LINUX_HH__ +#define __LINUX_HH__ +#include "config/full_system.hh" + +#if FULL_SYSTEM + +class Linux {}; + +#else //!FULL_SYSTEM + +#include <inttypes.h> + +#include "kern/operatingsystem.hh" + +/// +/// This class encapsulates the types, structures, constants, +/// functions, and syscall-number mappings specific to the Alpha Linux +/// syscall interface. +/// +class Linux : public OperatingSystem +{ + + public: + + //@{ + /// Basic Linux types. +/* typedef uint64_t size_t; + typedef uint64_t off_t; + typedef int64_t time_t; + typedef uint32_t uid_t; + typedef uint32_t gid_t;*/ + //@} + + /// Stat buffer. Note that we can't call it 'stat' since that + /// gets #defined to something else on some systems. This type + /// can be specialized by architecture specific "Linux" classes + typedef struct { + uint32_t st_dev; //!< device + uint32_t st_ino; //!< inode + uint32_t st_mode; //!< mode + uint32_t st_nlink; //!< link count + uint32_t st_uid; //!< owner's user ID + uint32_t st_gid; //!< owner's group ID + uint32_t st_rdev; //!< device number + int32_t _pad1; //!< for alignment + int64_t st_size; //!< file size in bytes + uint64_t st_atimeX; //!< time of last access + uint64_t st_mtimeX; //!< time of last modification + uint64_t st_ctimeX; //!< time of last status change + uint32_t st_blksize; //!< optimal I/O block size + int32_t st_blocks; //!< number of blocks allocated + uint32_t st_flags; //!< flags + uint32_t st_gen; //!< unknown + } tgt_stat; + + // same for stat64 + typedef struct { + uint64_t st_dev; + uint64_t st_ino; + uint64_t st_rdev; + int64_t st_size; + uint64_t st_blocks; + + uint32_t st_mode; + uint32_t st_uid; + uint32_t st_gid; + uint32_t st_blksize; + uint32_t st_nlink; + uint32_t __pad0; + + uint64_t st_atimeX; + uint64_t st_atime_nsec; + uint64_t st_mtimeX; + uint64_t st_mtime_nsec; + uint64_t st_ctimeX; + uint64_t st_ctime_nsec; + int64_t ___unused[3]; + } tgt_stat64; + + /// Length of strings in struct utsname (plus 1 for null char). + static const int _SYS_NMLN = 65; + + /// Interface struct for uname(). + struct utsname { + char sysname[_SYS_NMLN]; //!< System name. + char nodename[_SYS_NMLN]; //!< Node name. + char release[_SYS_NMLN]; //!< OS release. + char version[_SYS_NMLN]; //!< OS version. + char machine[_SYS_NMLN]; //!< Machine type. + }; + + /// Limit struct for getrlimit/setrlimit. + struct rlimit { + uint64_t rlim_cur; //!< soft limit + uint64_t rlim_max; //!< hard limit + }; + + /// For gettimeofday(). + struct timeval { + int64_t tv_sec; //!< seconds + int64_t tv_usec; //!< microseconds + }; + + // For writev/readv + struct tgt_iovec { + uint64_t iov_base; // void * + uint64_t iov_len; + }; + + + /// For getrusage(). + struct rusage { + struct timeval ru_utime; //!< user time used + struct timeval ru_stime; //!< system time used + int64_t ru_maxrss; //!< max rss + int64_t ru_ixrss; //!< integral shared memory size + int64_t ru_idrss; //!< integral unshared data " + int64_t ru_isrss; //!< integral unshared stack " + int64_t ru_minflt; //!< page reclaims - total vmfaults + int64_t ru_majflt; //!< page faults + int64_t ru_nswap; //!< swaps + int64_t ru_inblock; //!< block input operations + int64_t ru_oublock; //!< block output operations + int64_t ru_msgsnd; //!< messages sent + int64_t ru_msgrcv; //!< messages received + int64_t ru_nsignals; //!< signals received + int64_t ru_nvcsw; //!< voluntary context switches + int64_t ru_nivcsw; //!< involuntary " + }; + +}; // class Linux + + +#endif // FULL_SYSTEM + +#endif // __LINUX_HH__ diff --git a/src/kern/linux/linux_syscalls.cc b/src/kern/linux/linux_syscalls.cc new file mode 100644 index 000000000..7c2821dec --- /dev/null +++ b/src/kern/linux/linux_syscalls.cc @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#include "kern/linux/linux_syscalls.hh" + +namespace { + const char * + standard_strings[SystemCalls<Linux>::Number] = { + + + "llseek", //0 + "newselect", //1 + "sysctl", //2 + "access", //3 + "acct", //4 + "adjtimex", //5 + "afs_syscall", //6 + "alarm", //7 + "bdflush", //8 + "break", //9 + + + "brk", //10 + "capget", //11 + "capset", //12 + "chdir", //13 + "chmod", //14 + "chown", //15 + "chown32", //16 + "chroot", //17 + "clock_getres", //18 + "clock_gettime", //19 + + + "clock_nanosleep", //20 + "clock_settime", //21 + "clone", //22 + "close", //23 + "creat", //24 + "create_module", //25 + "delete_module", //26 + "dup", //27 + "dup2", //28 + "epoll_create", //29 + + + "epoll_ctl", //30 + "epoll_wait", //31 + "execve", //32 + "exit", //33 + "exit_group", //34 + "fadvise64", //35 + "fadvise64_64", //36 + "fchdir", //37 + "fchmod", //38 + "fchown", //39 + + + "fchown32", //40 + "fcntl", //41 + "fcntl64", //42 + "fdatasync", //43 + "fgetxattr", //44 + "flistxattr", //45 + "flock", //46 + "fork", //47 + "fremovexattr", //48 + "fsetxattr", //49 + + + "fstat", //50 + "fstat64", //51 + "fstatfs", //52 + "fstatfs64", //53 + "fsync", //54 + "ftime", //55 + "ftruncate", //56 + "ftruncate64", //57 + "futex", //58 + "get_kernel_syms", //59 + + + "get_thread_area", //60 + "getcwd", //61 + "getdents", //62 + "getdents64", //63 + "getegid", //64 + "getegid32", //65 + "geteuid", //66 + "geteuid32", //67 + "getgid", //68 + "getgid32", //69 + + + "getgroups", //70 + "getgroups32", //71 + "getitimer", //72 + "getpgid", //73 + "getpgrp", //74 + "getpid", //75 + "getpmsg", //76 + "getppid", //77 + "getpriority", //78 + "getresgid", //79 + + + "getresgid32", //80 + "getresuid", //81 + "getresuid32", //82 + "getrlimit", //83 + "getrusage", //84 + "getsid", //85 + "gettid", //86 + "gettimeofday", //87 + "getuid", //88 + "getuid32", //89 + + + "getxattr", //90 + "gtty", //91 + "idle", //92 + "init_module", //93 + "io_cancel", //94 + "io_destroy", //95 + "io_getevents", //96 + "io_setup", //97 + "io_submit", //98 + "ioctl", //99 + + + "ioperm", //100 + "iopl", //101 + "ipc", //102 + "kill", //103 + "lchown", //104 + "lchown32", //105 + "lgetxattr", //106 + "link", //107 + "listxattr", //108 + "llistxattr", //109 + + + "lock", //110 + "lookup_dcookie", //111 + "lremovexattr", //112 + "lseek", //113 + "lsetxattr", //114 + "lstat", //115 + "lstat64", //116 + "madvise", //117 + "madvise1", //118 + "mincore", //119 + + + "mkdir", //120 + "mknod", //121 + "mlock", //122 + "mlockall", //123 + "mmap", //124 + "mmap2", //125 + "modify_ldt", //126 + "mount", //127 + "mprotect", //128 + "mpx", //129 + + + "mremap", //130 + "msync", //131 + "munlock", //132 + "munlockall", //133 + "munmap", //134 + "nanosleep", //135 + "nfsservctl", //136 + "nice", //137 + "oldfstat", //138 + "oldlstat", //139 + + + "oldolduname", //140 + "oldstat", //141 + "olduname", //142 + "open", //143 + "pause", //144 + "personality", //145 + "pipe", //146 + "pivot_root", //147 + "poll", //148 + "prctl", //149 + + + "pread64", //150 + "prof", //151 + "profil", //152 + "ptrace", //153 + "putpmsg", //154 + "pwrite64", //155 + "query_module", //156 + "quotactl", //157 + "read", //158 + "readahead", //159 + + + "readdir", //160 + "readlink", //161 + "readv", //162 + "reboot", //163 + "remap_file_pages", //164 + "removexattr", //165 + "rename", //166 + "restart_syscall", //167 + "rmdir", //168 + "rt_sigaction", //169 + + + "rt_sigpending", //170 + "rt_sigprocmask", //171 + "rt_sigqueueinfo", //172 + "rt_sigreturn", //173 + "rt_sigsuspend", //174 + "rt_sigtimedwait", //175 + "sched_get_priority_max", //176 + "sched_get_priority_min", //177 + "sched_getaffinity", //178 + "sched_getparam", //179 + + + "sched_getscheduler", //180 + "sched_rr_get_interval", //181 + "sched_setaffinity", //182 + "sched_setparam", //183 + "sched_setscheduler", //184 + "sched_yield", //185 + "select", //186 + "sendfile", //187 + "sendfile64", //188 + "set_thread_area", //189 + + + "set_tid_address", //190 + "setdomainname", //191 + "setfsgid", //192 + "setfsgid32", //193 + "setfsuid", //194 + "setfsuid32", //195 + "setgid", //196 + "setgid32", //197 + "setgroups", //198 + "setgroups32", //199 + + + "sethostname", //200 + "setitimer", //201 + "setpgid", //202 + "setpriority", //203 + "setregid", //204 + "setregid32", //205 + "setresgid", //206 + "setresgid32", //207 + "setresuid", //208 + "setresuid32", //209 + + + "setreuid", //210 + "setreuid32", //211 + "setrlimit", //212 + "setsid", //213 + "settimeofday", //214 + "setuid", //215 + "setuid32", //216 + "setxattr", //217 + "sgetmask", //218 + "sigaction", //219 + + + "sigaltstack", //220 + "signal", //221 + "sigpending", //222 + "sigprocmask", //223 + "sigreturn", //224 + "sigsuspend", //225 + "socketcall", //226 + "ssetmask", //227 + "stat", //228 + "stat64", //229 + + + "statfs", //230 + "statfs64", //231 + "stime", //232 + "stty", //233 + "swapoff", //234 + "swapon", //235 + "symlink", //236 + "sync", //237 + "sysfs", //238 + "sysinfo", //239 + + + "syslog", //240 + "tgkill", //241 + "time", //242 + "timer_create", //243 + "timer_delete", //244 + "timer_getoverrun", //245 + "timer_gettime", //246 + "timer_settime", //247 + "times", //248 + "tkill", //249 + + + "truncate", //250 + "truncate64", //251 + "ugetrlimit", //252 + "ulimit", //253 + "umask", //254 + "umount", //255 + "umount2", //256 + "uname", //257 + "unlink", //258 + "uselib", //259 + + + "ustat", //260 + "utime", //261 + "utimes", //262 + "vfork", //263 + "vhangup", //264 + "vm86", //265 + "vm86old", //266 + "vserver", //267 + "wait4", //268 + "waitpid", //269 + + + "write", //270 + "writev", //271 + }; + + +} + +const char * +SystemCalls<Linux>::name(int num) +{ + if ((num >= 0) && (num < Number)) + return standard_strings[num]; + else + return 0; +} diff --git a/src/kern/linux/linux_syscalls.hh b/src/kern/linux/linux_syscalls.hh new file mode 100644 index 000000000..65616e862 --- /dev/null +++ b/src/kern/linux/linux_syscalls.hh @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#ifndef __KERN_LINUX_LINUX_SYSCALLS_HH__ +#define __KERN_LINUX_LINUX_SYSCALLS_HH__ + +#include "kern/linux/linux.hh" + +template <class OS> +class SystemCalls; + +template <> +class SystemCalls<Linux> +{ + public: + enum { + syscall = 0, + llseek = 1, + newselect = 2, + sysctl = 3, + access = 4, + acct = 5, + adjtimex = 6, + afs_syscall = 7, + alarm = 8, + bdflush = 9, + _break = 10, /*renamed from break*/ + brk = 11, + capget = 12, + capset = 13, + chdir = 14, + chmod = 15, + chown = 16, + chown32 = 17, + chroot = 18, + clock_getres = 19, + clock_gettime = 20, + clock_nanosleep = 21, + clock_settime = 22, + clone = 23, + close = 24, + creat = 25, + create_module = 26, + delete_module = 27, + dup = 28, + dup2 = 29, + epoll_create = 30, + epoll_ctl = 31, + epoll_wait = 32, + execve = 33, + exit = 34, + exit_group = 35, + fadvise64 = 36, + fadvise64_64 = 37, + fchdir = 38, + fchmod = 39, + fchown = 40, + fchown32 = 41, + fcntl = 42, + fcntl64 = 43, + fdatasync = 44, + fgetxattr = 45, + flistxattr = 46, + flock = 47, + fork = 48, + fremovexattr = 49, + fsetxattr = 50, + fstat = 51, + fstat64 = 52, + fstatfs = 53, + fstatfs64 = 54, + fsync = 55, + ftime = 56, + ftruncate = 57, + ftruncate64 = 58, + futex = 59, + get_kernel_syms = 60, + get_thread_area = 61, + getcwd = 62, + getdents = 63, + getdents64 = 64, + getegid = 65, + getegid32 = 66, + geteuid = 67, + geteuid32 = 68, + getgid = 69, + getgid32 = 70, + getgroups = 71, + getgroups32 = 72, + getitimer = 73, + getpgid = 74, + getpgrp = 75, + getpid = 76, + getpmsg = 77, + getppid = 78, + getpriority = 79, + getresgid = 80, + getresgid32 = 81, + getresuid = 82, + getresuid32 = 83, + getrlimit = 84, + getrusage = 85, + getsid = 86, + gettid = 87, + gettimeofday = 88, + getuid = 89, + getuid32 = 90, + getxattr = 91, + gtty = 92, + idle = 93, + init_module = 94, + io_cancel = 95, + io_destroy = 96, + io_getevents = 97, + io_setup = 98, + io_submit = 99, + ioctl = 100, + ioperm = 101, + iopl = 102, + ipc = 103, + kill = 104, + lchown = 105, + lchown32 = 106, + lgetxattr = 107, + link = 108, + listxattr = 109, + llistxattr = 110, + lock = 111, + lookup_dcookie = 112, + lremovexattr = 113, + lseek = 114, + lsetxattr = 115, + lstat = 116, + lstat64 = 117, + madvise = 118, + madvise1 = 119, + mincore = 120, + mkdir = 121, + mknod = 122, + mlock = 123, + mlockall = 124, + mmap = 125, + mmap2 = 126, + modify_ldt = 127, + mount = 128, + mprotect = 129, + mpx = 130, + mremap = 131, + msync = 132, + munlock = 133, + munlockall = 134, + munmap = 135, + nanosleep = 136, + nfsservctl = 137, + nice = 138, + oldfstat = 139, + oldlstat = 140, + oldolduname = 141, + oldstat = 142, + olduname = 143, + open = 144, + pause = 145, + personality = 146, + pipe = 147, + pivot_root = 148, + poll = 149, + prctl = 150, + pread64 = 151, + prof = 152, + profil = 153, + ptrace = 154, + putpmsg = 155, + pwrite64 = 156, + query_module = 157, + quotactl = 158, + read = 159, + readahead = 160, + readdir = 161, + readlink = 162, + readv = 163, + reboot = 164, + remap_file_pages = 165, + removexattr = 166, + rename = 167, + restart_syscall = 168, + rmdir = 169, + rt_sigaction = 170, + rt_sigpending = 171, + rt_sigprocmask = 172, + rt_sigqueueinfo = 173, + rt_sigreturn = 174, + rt_sigsuspend = 175, + rt_sigtimedwait = 176, + sched_get_priority_max = 177, + sched_get_priority_min = 178, + sched_getaffinity = 179, + sched_getparam = 180, + sched_getscheduler = 181, + sched_rr_get_interval = 182, + sched_setaffinity = 183, + sched_setparam = 184, + sched_setscheduler = 185, + sched_yield = 186, + select = 187, + sendfile = 188, + sendfile64 = 189, + set_thread_area = 190, + set_tid_address = 191, + setdomainname = 192, + setfsgid = 193, + setfsgid32 = 194, + setfsuid = 195, + setfsuid32 = 196, + setgid = 197, + setgid32 = 198, + setgroups = 199, + setgroups32 = 200, + sethostname = 201, + setitimer = 202, + setpgid = 203, + setpriority = 204, + setregid = 205, + setregid32 = 206, + setresgid = 207, + setresgid32 = 208, + setresuid = 209, + setresuid32 = 210, + setreuid = 211, + setreuid32 = 212, + setrlimit = 213, + setsid = 214, + settimeofday = 215, + setuid = 216, + setuid32 = 217, + setxattr = 218, + sgetmask = 219, + sigaction = 220, + sigaltstack = 221, + signal = 222, + sigpending = 223, + sigprocmask = 224, + sigreturn = 225, + sigsuspend = 226, + socketcall = 227, + ssetmask = 228, + stat = 229, + stat64 = 230, + statfs = 231, + statfs64 = 232, + stime = 233, + stty = 234, + swapoff = 235, + swapon = 236, + symlink = 237, + sync = 238, + sysfs = 239, + sysinfo = 240, + syslog = 241, + tgkill = 242, + time = 243, + timer_create = 244, + timer_delete = 245, + timer_getoverrun = 246, + timer_gettime = 247, + timer_settime = 248, + times = 249, + tkill = 250, + truncate = 251, + truncate64 = 252, + ugetrlimit = 253, + ulimit = 254, + umask = 255, + umount = 256, + umount2 = 257, + uname = 258, + unlink = 259, + uselib = 260, + ustat = 261, + utime = 262, + utimes = 263, + vfork = 264, + vhangup = 265, + vm86 = 266, + vm86old = 267, + vserver = 268, + wait4 = 269, + waitpid = 270, + write = 271, + writev = 272, + Number + }; + + static const char *name(int num); + + static bool validSyscallNumber(int num) { + return num < Number; + } + +}; + +#endif // __KERN_LINUX_LINUX_SYSCALLS_HH__ diff --git a/src/kern/linux/printk.cc b/src/kern/linux/printk.cc new file mode 100644 index 000000000..004d1be2f --- /dev/null +++ b/src/kern/linux/printk.cc @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ali Saidi + */ + +#include <sys/types.h> +#include <algorithm> + +#include "base/trace.hh" +#include "arch/arguments.hh" + +using namespace std; + + +void +Printk(AlphaISA::AlphaArguments args) +{ + char *p = (char *)args++; + + ios::fmtflags saved_flags = DebugOut().flags(); + char old_fill = DebugOut().fill(); + int old_precision = DebugOut().precision(); + + while (*p) { + switch (*p) { + case '%': { + bool more = true; + bool islong = false; + bool leftjustify = false; + bool format = false; + bool zero = false; + int width = 0; + while (more && *++p) { + switch (*p) { + case 'l': + case 'L': + islong = true; + break; + case '-': + leftjustify = true; + break; + case '#': + format = true; + break; + case '0': + if (width) + width *= 10; + else + zero = true; + break; + default: + if (*p >= '1' && *p <= '9') + width = 10 * width + *p - '0'; + else + more = false; + break; + } + } + + bool hexnum = false; + bool octal = false; + bool sign = false; + switch (*p) { + case 'X': + case 'x': + hexnum = true; + break; + case 'O': + case 'o': + octal = true; + break; + case 'D': + case 'd': + sign = true; + break; + case 'P': + format = true; + case 'p': + hexnum = true; + break; + } + + switch (*p) { + case 'D': + case 'd': + case 'U': + case 'u': + case 'X': + case 'x': + case 'O': + case 'o': + case 'P': + case 'p': { + if (hexnum) + DebugOut() << hex; + + if (octal) + DebugOut() << oct; + + if (format) { + if (!zero) + DebugOut().setf(ios::showbase); + else { + if (hexnum) { + DebugOut() << "0x"; + width -= 2; + } else if (octal) { + DebugOut() << "0"; + width -= 1; + } + } + } + + if (zero) + DebugOut().fill('0'); + + if (width > 0) + DebugOut().width(width); + + if (leftjustify && !zero) + DebugOut().setf(ios::left); + + if (sign) { + if (islong) + DebugOut() << (int64_t)args; + else + DebugOut() << (int32_t)args; + } else { + if (islong) + DebugOut() << (uint64_t)args; + else + DebugOut() << (uint32_t)args; + } + + if (zero) + DebugOut().fill(' '); + + if (width > 0) + DebugOut().width(0); + + DebugOut() << dec; + + ++args; + } + break; + + case 's': { + char *s = (char *)args; + if (!s) + s = "<NULL>"; + + if (width > 0) + DebugOut().width(width); + if (leftjustify) + DebugOut().setf(ios::left); + + DebugOut() << s; + ++args; + } + break; + case 'C': + case 'c': { + uint64_t mask = (*p == 'C') ? 0xffL : 0x7fL; + uint64_t num; + int width; + + if (islong) { + num = (uint64_t)args; + width = sizeof(uint64_t); + } else { + num = (uint32_t)args; + width = sizeof(uint32_t); + } + + while (width-- > 0) { + char c = (char)(num & mask); + if (c) + DebugOut() << c; + num >>= 8; + } + + ++args; + } + break; + case 'b': { + uint64_t n = (uint64_t)args++; + char *s = (char *)args++; + DebugOut() << s << ": " << n; + } + break; + case 'n': + case 'N': { + args += 2; +#if 0 + uint64_t n = (uint64_t)args++; + struct reg_values *rv = (struct reg_values *)args++; +#endif + } + break; + case 'r': + case 'R': { + args += 2; +#if 0 + uint64_t n = (uint64_t)args++; + struct reg_desc *rd = (struct reg_desc *)args++; +#endif + } + break; + case '%': + DebugOut() << '%'; + break; + } + ++p; + } + break; + case '\n': + DebugOut() << endl; + ++p; + break; + case '\r': + ++p; + if (*p != '\n') + DebugOut() << endl; + break; + + default: { + size_t len = strcspn(p, "%\n\r\0"); + DebugOut().write(p, len); + p += len; + } + } + } + + DebugOut().flags(saved_flags); + DebugOut().fill(old_fill); + DebugOut().precision(old_precision); +} + diff --git a/src/kern/linux/printk.hh b/src/kern/linux/printk.hh new file mode 100644 index 000000000..5ddf0a018 --- /dev/null +++ b/src/kern/linux/printk.hh @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ali Saidi + */ + +#ifndef __PRINTK_HH__ +#define __PRINTK_HH__ + +class AlphaISA::AlphaArguments; + +void Printk(AlphaISA::AlphaArguments args); + +#endif // __PRINTK_HH__ diff --git a/src/kern/linux/sched.hh b/src/kern/linux/sched.hh new file mode 100644 index 000000000..f849cee30 --- /dev/null +++ b/src/kern/linux/sched.hh @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Nathan Binkert + */ + +#ifndef __KERN_LINUX_SCHED_HH__ +#define __KERN_LINUX_SCHED_HH__ + +namespace Linux { + struct task_struct { + uint8_t junk1[0xf4]; + int32_t pid; + uint8_t junk2[0x190]; + uint64_ta start; + uint8_t junk3[0x5c]; + char name[16]; + }; + + +} + +#endif // __KERN_LINUX_SCHED_HH__ diff --git a/src/kern/operatingsystem.hh b/src/kern/operatingsystem.hh new file mode 100644 index 000000000..99358ae03 --- /dev/null +++ b/src/kern/operatingsystem.hh @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + */ + +#ifndef __KERN_OPERATINGSYSTEM_HH__ +#define __KERN_OPERATINGSYSTEM_HH__ + +#include "config/full_system.hh" + +#include <inttypes.h> + +#if FULL_SYSTEM + +class OperatingSystem {}; + +#else //!FULL_SYSTEM + +/// This struct is used to build an target-OS-dependent table that +/// maps the target's open() flags to the host open() flags. +struct OpenFlagTransTable { + int tgtFlag; //!< Target system flag value. + int hostFlag; //!< Corresponding host system flag value. +}; + + +/// +/// This class encapsulates the types, structures, constants, +/// functions, and syscall-number mappings specific to an operating system +/// syscall interface. +/// +class OperatingSystem { + + public: + + /// Stat buffer. Note that we can't call it 'stat' since that + /// gets #defined to something else on some systems. This type + /// can be specialized by architecture specific "Linux" classes + typedef void tgt_stat; + + // same for stat64 + typedef void tgt_stat64; + + /// Length of strings in struct utsname (plus 1 for null char). + static const int _SYS_NMLN = 65; + + /// Interface struct for uname(). + typedef struct { + char sysname[_SYS_NMLN]; //!< System name. + char nodename[_SYS_NMLN]; //!< Node name. + char release[_SYS_NMLN]; //!< OS release. + char version[_SYS_NMLN]; //!< OS version. + char machine[_SYS_NMLN]; //!< Machine type. + } utsname; + + /// Limit struct for getrlimit/setrlimit. + typedef struct { + uint64_t rlim_cur; //!< soft limit + uint64_t rlim_max; //!< hard limit + } rlimit; + + /// For gettimeofday(). + typedef struct { + int64_t tv_sec; //!< seconds + int64_t tv_usec; //!< microseconds + } timeval; + + // For writev/readv + typedef struct { + uint64_t iov_base; // void * + uint64_t iov_len; + } tgt_iovec; + + + /// For getrusage(). + typedef struct { + timeval ru_utime; //!< user time used + timeval ru_stime; //!< system time used + int64_t ru_maxrss; //!< max rss + int64_t ru_ixrss; //!< integral shared memory size + int64_t ru_idrss; //!< integral unshared data " + int64_t ru_isrss; //!< integral unshared stack " + int64_t ru_minflt; //!< page reclaims - total vmfaults + int64_t ru_majflt; //!< page faults + int64_t ru_nswap; //!< swaps + int64_t ru_inblock; //!< block input operations + int64_t ru_oublock; //!< block output operations + int64_t ru_msgsnd; //!< messages sent + int64_t ru_msgrcv; //!< messages received + int64_t ru_nsignals; //!< signals received + int64_t ru_nvcsw; //!< voluntary context switches + int64_t ru_nivcsw; //!< involuntary " + } rusage; + +}; // class OperatingSystem + + +#endif // FULL_SYSTEM + +#endif // __OPERATINGSYSTEM_HH__ diff --git a/src/kern/solaris/solaris.hh b/src/kern/solaris/solaris.hh new file mode 100644 index 000000000..a5ca41cdd --- /dev/null +++ b/src/kern/solaris/solaris.hh @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#ifndef __SOLARIS_HH__ +#define __SOLARIS_HH__ +#include "config/full_system.hh" + +#if FULL_SYSTEM + +class Solaris {}; + +#else //!FULL_SYSTEM + +#include <inttypes.h> + +#include "kern/operatingsystem.hh" + +class TranslatingPort; + +/// +/// This class encapsulates the types, structures, constants, +/// functions, and syscall-number mappings specific to the Solaris +/// syscall interface. +/// +class Solaris : public OperatingSystem +{ + + public: + + //@{ + /// Basic Solaris types. +/* typedef uint64_t size_t; + typedef uint64_t off_t; + typedef int64_t time_t; + typedef int32_t uid_t; + typedef int32_t gid_t; + typedef uint64_t rlim_t; + typedef uint64_t ino_t; + typedef uint64_t dev_t; + typedef uint32_t mode_t; + typedef uint32_t nlink_t;*/ + //@} + + struct tgt_timespec { + int64_t tv_sec; + int64_t tv_nsec; + }; + + /// Stat buffer. Note that we can't call it 'stat' since that + /// gets #defined to something else on some systems. + typedef struct { + uint64_t st_dev; //!< device + uint64_t st_ino; //!< inode + uint32_t st_mode; //!< mode + uint32_t st_nlink; //!< link count + int32_t st_uid; //!< owner's user ID + int32_t st_gid; //!< owner's group ID + uint64_t st_rdev; //!< device number + int64_t st_size; //!< file size in bytes + //struct tgt_timespec st_atimeX; //!< time of last access + //struct tgt_timespec st_mtimeX; //!< time of last modification + //struct tgt_timespec st_ctimeX; //!< time of last status change + int64_t st_atimeX, st_mtimeX, st_ctimeX; + int32_t st_blksize; //!< optimal I/O block size + int64_t st_blocks; //!< number of blocks allocated + char st_fstype[16]; + } tgt_stat; + + // same for stat64 + typedef struct { + uint64_t st_dev; //!< device + uint64_t st_ino; //!< inode + uint32_t st_mode; //!< mode + uint32_t st_nlink; //!< link count + int32_t st_uid; //!< owner's user ID + int32_t st_gid; //!< owner's group ID + uint64_t st_rdev; //!< device number + int64_t st_size; //!< file size in bytes + //struct tgt_timespec st_atimeX; //!< time of last access + //struct tgt_timespec st_mtimeX; //!< time of last modification + //struct tgt_timespec st_ctimeX; //!< time of last status change + int64_t st_atimeX, st_mtimeX, st_ctimeX; + int32_t st_blksize; //!< optimal I/O block size + int64_t st_blocks; //!< number of blocks allocated + char st_fstype[16]; + } tgt_stat64; + + /// Length of strings in struct utsname (plus 1 for null char). + static const int _SYS_NMLN = 257; + + /// Interface struct for uname(). + typedef struct utsname { + char sysname[_SYS_NMLN]; //!< System name. + char nodename[_SYS_NMLN]; //!< Node name. + char release[_SYS_NMLN]; //!< OS release. + char version[_SYS_NMLN]; //!< OS version. + char machine[_SYS_NMLN]; //!< Machine type. + } utsname; + +}; // class Solaris + + +#endif // FULL_SYSTEM + +#endif // __SOLARIS_HH__ diff --git a/src/kern/system_events.cc b/src/kern/system_events.cc new file mode 100644 index 000000000..177ce96d1 --- /dev/null +++ b/src/kern/system_events.cc @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Lisa Hsu + * Nathan Binkert + */ + +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "kern/kernel_stats.hh" +#include "kern/system_events.hh" +#include "sim/system.hh" + +using namespace TheISA; + +void +SkipFuncEvent::process(ThreadContext *tc) +{ + Addr newpc = tc->readIntReg(ReturnAddressReg); + + DPRINTF(PCEvent, "skipping %s: pc=%x, newpc=%x\n", description, + tc->readPC(), newpc); + + tc->setPC(newpc); + tc->setNextPC(tc->readPC() + sizeof(TheISA::MachInst)); +/* + BranchPred *bp = tc->getCpuPtr()->getBranchPred(); + if (bp != NULL) { + bp->popRAS(tc->getThreadNum()); + } +*/ +} + +void +IdleStartEvent::process(ThreadContext *tc) +{ + if (tc->getKernelStats()) + tc->getKernelStats()->setIdleProcess( + tc->readMiscReg(AlphaISA::IPR_PALtemp23), tc); + remove(); +} diff --git a/src/kern/system_events.hh b/src/kern/system_events.hh new file mode 100644 index 000000000..93b5eb528 --- /dev/null +++ b/src/kern/system_events.hh @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu + * Ali Saidi + */ + +#ifndef __SYSTEM_EVENTS_HH__ +#define __SYSTEM_EVENTS_HH__ + +#include "cpu/pc_event.hh" + +class System; + +class SkipFuncEvent : public PCEvent +{ + public: + SkipFuncEvent(PCEventQueue *q, const std::string &desc, Addr addr) + : PCEvent(q, desc, addr) + {} + virtual void process(ThreadContext *tc); +}; + +class IdleStartEvent : public PCEvent +{ + public: + IdleStartEvent(PCEventQueue *q, const std::string &desc, Addr addr) + : PCEvent(q, desc, addr) + {} + virtual void process(ThreadContext *tc); +}; + +#endif // __SYSTEM_EVENTS_HH__ diff --git a/src/kern/tru64/dump_mbuf.cc b/src/kern/tru64/dump_mbuf.cc new file mode 100644 index 000000000..8f88f8904 --- /dev/null +++ b/src/kern/tru64/dump_mbuf.cc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <sys/types.h> +#include <algorithm> + +#include "base/cprintf.hh" +#include "base/trace.hh" +#include "base/loader/symtab.hh" +#include "cpu/thread_context.hh" +#include "kern/tru64/mbuf.hh" +#include "sim/host.hh" +#include "sim/system.hh" +#include "arch/arguments.hh" +#include "arch/isa_traits.hh" +#include "arch/vtophys.hh" + +using namespace TheISA; + +namespace tru64 { + +void +DumpMbuf(AlphaArguments args) +{ + ThreadContext *tc = args.getThreadContext(); + Addr addr = (Addr)args; + struct mbuf m; + + CopyOut(tc, &m, addr, sizeof(m)); + + int count = m.m_pkthdr.len; + + ccprintf(DebugOut(), "m=%#lx, m->m_pkthdr.len=%#d\n", addr, + m.m_pkthdr.len); + + while (count > 0) { + ccprintf(DebugOut(), "m=%#lx, m->m_data=%#lx, m->m_len=%d\n", + addr, m.m_data, m.m_len); + char *buffer = new char[m.m_len]; + CopyOut(tc, buffer, m.m_data, m.m_len); + Trace::dataDump(curTick, tc->getSystemPtr()->name(), (uint8_t *)buffer, + m.m_len); + delete [] buffer; + + count -= m.m_len; + if (!m.m_next) + break; + + CopyOut(tc, &m, m.m_next, sizeof(m)); + } +} + +} // namespace Tru64 diff --git a/src/kern/tru64/dump_mbuf.hh b/src/kern/tru64/dump_mbuf.hh new file mode 100644 index 000000000..25c6fd31d --- /dev/null +++ b/src/kern/tru64/dump_mbuf.hh @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __DUMP_MBUF_HH__ +#define __DUMP_MBUF_HH__ + +#include "arch/arguments.hh" + +namespace tru64 { + void DumpMbuf(AlphaISA::AlphaArguments args); +} + +#endif // __DUMP_MBUF_HH__ diff --git a/src/kern/tru64/mbuf.hh b/src/kern/tru64/mbuf.hh new file mode 100644 index 000000000..cb5a84a7e --- /dev/null +++ b/src/kern/tru64/mbuf.hh @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __MBUF_HH__ +#define __MBUF_HH__ + +#include "sim/host.hh" +#include "arch/isa_traits.hh" + +namespace tru64 { + +struct m_hdr { + Addr mh_next; // 0x00 + Addr mh_nextpkt; // 0x08 + Addr mh_data; // 0x10 + int32_t mh_len; // 0x18 + int32_t mh_type; // 0x1C + int32_t mh_flags; // 0x20 + int32_t mh_pad0; // 0x24 + Addr mh_foo[4]; // 0x28, 0x30, 0x38, 0x40 +}; + +struct pkthdr { + int32_t len; + int32_t protocolSum; + Addr rcvif; +}; + +struct m_ext { + Addr ext_buf; // 0x00 + Addr ext_free; // 0x08 + uint32_t ext_size; // 0x10 + uint32_t ext_pad0; // 0x14 + Addr ext_arg; // 0x18 + struct ext_refq { + Addr forw, back; // 0x20, 0x28 + } ext_ref; + Addr uiomove_f; // 0x30 + int32_t protocolSum; // 0x38 + int32_t bytesSummed; // 0x3C + Addr checksum; // 0x40 +}; + +struct mbuf { + struct m_hdr m_hdr; + union { + struct { + struct pkthdr MH_pkthdr; + union { + struct m_ext MH_ext; + char MH_databuf[1]; + } MH_dat; + } MH; + char M_databuf[1]; + } M_dat; +}; + +#define m_attr m_hdr.mh_attr +#define m_next m_hdr.mh_next +#define m_len m_hdr.mh_len +#define m_data m_hdr.mh_data +#define m_type m_hdr.mh_type +#define m_flags m_hdr.mh_flags +#define m_nextpkt m_hdr.mh_nextpkt +#define m_act m_nextpkt +#define m_pkthdr M_dat.MH.MH_pkthdr +#define m_ext M_dat.MH.MH_dat.MH_ext +#define m_pktdat M_dat.MH.MH_dat.MH_databuf +#define m_dat M_dat.M_databuf + +} + +#endif // __MBUF_HH__ diff --git a/src/kern/tru64/printf.cc b/src/kern/tru64/printf.cc new file mode 100644 index 000000000..29dd443d2 --- /dev/null +++ b/src/kern/tru64/printf.cc @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <sys/types.h> +#include <algorithm> + +#include "base/cprintf.hh" +#include "base/trace.hh" +#include "sim/host.hh" +#include "arch/arguments.hh" +#include "arch/vtophys.hh" + +using namespace std; + +namespace tru64 { + +void +Printf(AlphaISA::AlphaArguments args) +{ + char *p = (char *)args++; + + ios::fmtflags saved_flags = DebugOut().flags(); + char old_fill = DebugOut().fill(); + int old_precision = DebugOut().precision(); + + while (*p) { + switch (*p) { + case '%': { + bool more = true; + bool islong = false; + bool leftjustify = false; + bool format = false; + bool zero = false; + int width = 0; + while (more && *++p) { + switch (*p) { + case 'l': + case 'L': + islong = true; + break; + case '-': + leftjustify = true; + break; + case '#': + format = true; + break; + case '0': + if (width) + width *= 10; + else + zero = true; + break; + default: + if (*p >= '1' && *p <= '9') + width = 10 * width + *p - '0'; + else + more = false; + break; + } + } + + bool hexnum = false; + bool octal = false; + bool sign = false; + switch (*p) { + case 'X': + case 'x': + hexnum = true; + break; + case 'O': + case 'o': + octal = true; + break; + case 'D': + case 'd': + sign = true; + break; + case 'P': + format = true; + case 'p': + hexnum = true; + break; + } + + switch (*p) { + case 'D': + case 'd': + case 'U': + case 'u': + case 'X': + case 'x': + case 'O': + case 'o': + case 'P': + case 'p': { + if (hexnum) + DebugOut() << hex; + + if (octal) + DebugOut() << oct; + + if (format) { + if (!zero) + DebugOut().setf(ios::showbase); + else { + if (hexnum) { + DebugOut() << "0x"; + width -= 2; + } else if (octal) { + DebugOut() << "0"; + width -= 1; + } + } + } + + if (zero) + DebugOut().fill('0'); + + if (width > 0) + DebugOut().width(width); + + if (leftjustify && !zero) + DebugOut().setf(ios::left); + + if (sign) { + if (islong) + DebugOut() << (int64_t)args; + else + DebugOut() << (int32_t)args; + } else { + if (islong) + DebugOut() << (uint64_t)args; + else + DebugOut() << (uint32_t)args; + } + + if (zero) + DebugOut().fill(' '); + + if (width > 0) + DebugOut().width(0); + + DebugOut() << dec; + + ++args; + } + break; + + case 's': { + char *s = (char *)args; + if (!s) + s = "<NULL>"; + + if (width > 0) + DebugOut().width(width); + if (leftjustify) + DebugOut().setf(ios::left); + + DebugOut() << s; + ++args; + } + break; + case 'C': + case 'c': { + uint64_t mask = (*p == 'C') ? 0xffL : 0x7fL; + uint64_t num; + int width; + + if (islong) { + num = (uint64_t)args; + width = sizeof(uint64_t); + } else { + num = (uint32_t)args; + width = sizeof(uint32_t); + } + + while (width-- > 0) { + char c = (char)(num & mask); + if (c) + DebugOut() << c; + num >>= 8; + } + + ++args; + } + break; + case 'b': { + uint64_t n = (uint64_t)args++; + char *s = (char *)args++; + DebugOut() << s << ": " << n; + } + break; + case 'n': + case 'N': { + args += 2; +#if 0 + uint64_t n = (uint64_t)args++; + struct reg_values *rv = (struct reg_values *)args++; +#endif + } + break; + case 'r': + case 'R': { + args += 2; +#if 0 + uint64_t n = (uint64_t)args++; + struct reg_desc *rd = (struct reg_desc *)args++; +#endif + } + break; + case '%': + DebugOut() << '%'; + break; + } + ++p; + } + break; + case '\n': + DebugOut() << endl; + ++p; + break; + case '\r': + ++p; + if (*p != '\n') + DebugOut() << endl; + break; + + default: { + size_t len = strcspn(p, "%\n\r\0"); + DebugOut().write(p, len); + p += len; + } + } + } + + DebugOut().flags(saved_flags); + DebugOut().fill(old_fill); + DebugOut().precision(old_precision); +} + +} // namespace Tru64 diff --git a/src/kern/tru64/printf.hh b/src/kern/tru64/printf.hh new file mode 100644 index 000000000..f6a4544ad --- /dev/null +++ b/src/kern/tru64/printf.hh @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __PRINTF_HH__ +#define __PRINTF_HH__ + +#include "arch/arguments.hh" + +namespace tru64 { + void Printf(AlphaISA::AlphaArguments args); +} + +#endif // __PRINTF_HH__ diff --git a/src/kern/tru64/tru64.hh b/src/kern/tru64/tru64.hh new file mode 100644 index 000000000..87ac88007 --- /dev/null +++ b/src/kern/tru64/tru64.hh @@ -0,0 +1,1250 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Ali Saidi + */ + +#ifndef __TRU64_HH__ +#define __TRU64_HH__ +#include "config/full_system.hh" +#include "kern/operatingsystem.hh" + +#if FULL_SYSTEM + +class Tru64 {}; + +#else //!FULL_SYSTEM + +#include <sys/types.h> +#include <sys/stat.h> +#if defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD__) +#include <sys/param.h> +#include <sys/mount.h> +#else +#include <sys/statfs.h> +#endif + +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> // for memset() +#include <unistd.h> + +#include "cpu/base.hh" +#include "sim/root.hh" +#include "sim/syscall_emul.hh" + +typedef struct stat global_stat; +typedef struct statfs global_statfs; +typedef struct dirent global_dirent; + +class TranslatingPort; + +/// +/// This class encapsulates the types, structures, constants, +/// functions, and syscall-number mappings specific to the Alpha Tru64 +/// syscall interface. +/// +class Tru64 : public OperatingSystem +{ + + public: + + //@{ + /// Basic Tru64 types. + typedef uint64_t size_t; + typedef uint64_t off_t; + typedef uint16_t nlink_t; + typedef int32_t dev_t; + typedef uint32_t uid_t; + typedef uint32_t gid_t; + typedef uint32_t time_t; + typedef uint32_t mode_t; + typedef uint32_t ino_t; + typedef struct { int val[2]; } quad; + typedef quad fsid_t; + //@} + + + /// For statfs(). + struct F64_statfs { + int16_t f_type; + int16_t f_flags; + int32_t f_retired1; + int32_t f_retired2; + int32_t f_retired3; + int32_t f_retired4; + int32_t f_retired5; + int32_t f_retired6; + int32_t f_retired7; + fsid_t f_fsid; + int32_t f_spare[9]; + char f_retired8[90]; + char f_retired9[90]; + uint64_t dummy[10]; // was union mount_info mount_info; + uint64_t f_flags2; + int64_t f_spare2[14]; + int64_t f_fsize; + int64_t f_bsize; + int64_t f_blocks; + int64_t f_bfree; + int64_t f_bavail; + int64_t f_files; + int64_t f_ffree; + char f_mntonname[1024]; + char f_mntfromname[1024]; + }; + + /// For old Tru64 v4.x statfs() + struct pre_F64_statfs { + int16_t f_type; + int16_t f_flags; + int32_t f_fsize; + int32_t f_bsize; + int32_t f_blocks; + int32_t f_bfree; + int32_t f_bavail; + int32_t f_files; + int32_t f_ffree; + fsid_t f_fsid; + int32_t f_spare[9]; + char f_mntonname[90]; + char f_mntfromname[90]; + uint64_t dummy[10]; // was union mount_info mount_info; + }; + + /// For getdirentries(). + struct dirent + { + ino_t d_ino; //!< file number of entry + uint16_t d_reclen; //!< length of this record + uint16_t d_namlen; //!< length of string in d_name + char d_name[256]; //!< dummy name length + }; + + + /// Length of strings in struct utsname (plus 1 for null char). + static const int _SYS_NMLN = 32; + + /// Interface struct for uname(). + struct utsname { + char sysname[_SYS_NMLN]; //!< System name. + char nodename[_SYS_NMLN]; //!< Node name. + char release[_SYS_NMLN]; //!< OS release. + char version[_SYS_NMLN]; //!< OS version. + char machine[_SYS_NMLN]; //!< Machine type. + }; + + /// Limit struct for getrlimit/setrlimit. + struct rlimit { + uint64_t rlim_cur; //!< soft limit + uint64_t rlim_max; //!< hard limit + }; + + + /// For getsysinfo() GSI_CPU_INFO option. + struct cpu_info { + uint32_t current_cpu; //!< current_cpu + uint32_t cpus_in_box; //!< cpus_in_box + uint32_t cpu_type; //!< cpu_type + uint32_t ncpus; //!< ncpus + uint64_t cpus_present; //!< cpus_present + uint64_t cpus_running; //!< cpus_running + uint64_t cpu_binding; //!< cpu_binding + uint64_t cpu_ex_binding; //!< cpu_ex_binding + uint32_t mhz; //!< mhz + uint32_t unused[3]; //!< future expansion + }; + + /// For gettimeofday. + struct timeval { + uint32_t tv_sec; //!< seconds + uint32_t tv_usec; //!< microseconds + }; + + /// For getrusage(). + struct rusage { + struct timeval ru_utime; //!< user time used + struct timeval ru_stime; //!< system time used + uint64_t ru_maxrss; //!< ru_maxrss + uint64_t ru_ixrss; //!< integral shared memory size + uint64_t ru_idrss; //!< integral unshared data " + uint64_t ru_isrss; //!< integral unshared stack " + uint64_t ru_minflt; //!< page reclaims - total vmfaults + uint64_t ru_majflt; //!< page faults + uint64_t ru_nswap; //!< swaps + uint64_t ru_inblock; //!< block input operations + uint64_t ru_oublock; //!< block output operations + uint64_t ru_msgsnd; //!< messages sent + uint64_t ru_msgrcv; //!< messages received + uint64_t ru_nsignals; //!< signals received + uint64_t ru_nvcsw; //!< voluntary context switches + uint64_t ru_nivcsw; //!< involuntary " + }; + + /// For sigreturn(). + struct sigcontext { + int64_t sc_onstack; //!< sigstack state to restore + int64_t sc_mask; //!< signal mask to restore + int64_t sc_pc; //!< pc at time of signal + int64_t sc_ps; //!< psl to retore + int64_t sc_regs[32]; //!< processor regs 0 to 31 + int64_t sc_ownedfp; //!< fp has been used + int64_t sc_fpregs[32]; //!< fp regs 0 to 31 + uint64_t sc_fpcr; //!< floating point control reg + uint64_t sc_fp_control; //!< software fpcr + int64_t sc_reserved1; //!< reserved for kernel + uint32_t sc_kreserved1; //!< reserved for kernel + uint32_t sc_kreserved2; //!< reserved for kernel + size_t sc_ssize; //!< stack size + caddr_t sc_sbase; //!< stack start + uint64_t sc_traparg_a0; //!< a0 argument to trap on exc + uint64_t sc_traparg_a1; //!< a1 argument to trap on exc + uint64_t sc_traparg_a2; //!< a2 argument to trap on exc + uint64_t sc_fp_trap_pc; //!< imprecise pc + uint64_t sc_fp_trigger_sum; //!< Exception summary at trigg + uint64_t sc_fp_trigger_inst; //!< Instruction at trigger pc + }; + + + + /// For table(). + struct tbl_sysinfo { + uint64_t si_user; //!< User time + uint64_t si_nice; //!< Nice time + uint64_t si_sys; //!< System time + uint64_t si_idle; //!< Idle time + uint64_t si_hz; //!< hz + uint64_t si_phz; //!< phz + uint64_t si_boottime; //!< Boot time in seconds + uint64_t wait; //!< Wait time + uint32_t si_max_procs; //!< rpb->rpb_numprocs + uint32_t pad; //!< padding + }; + + + /// For stack_create. + struct vm_stack { + // was void * + Addr address; //!< address hint + size_t rsize; //!< red zone size + size_t ysize; //!< yellow zone size + size_t gsize; //!< green zone size + size_t swap; //!< amount of swap to reserve + size_t incr; //!< growth increment + uint64_t align; //!< address alignment + uint64_t flags; //!< MAP_FIXED etc. + // was struct memalloc_attr * + Addr attr; //!< allocation policy + uint64_t reserved; //!< reserved + }; + + /// Return values for nxm calls. + enum { + KERN_NOT_RECEIVER = 7, + KERN_NOT_IN_SET = 12 + }; + + /// For nxm_task_init. + static const int NXM_TASK_INIT_VP = 2; //!< initial thread is VP + + /// Task attribute structure. + struct nxm_task_attr { + int64_t nxm_callback; //!< nxm_callback + unsigned int nxm_version; //!< nxm_version + unsigned short nxm_uniq_offset; //!< nxm_uniq_offset + unsigned short flags; //!< flags + int nxm_quantum; //!< nxm_quantum + int pad1; //!< pad1 + int64_t pad2; //!< pad2 + }; + + /// Signal set. + typedef uint64_t sigset_t; + + /// Thread state shared between user & kernel. + struct ushared_state { + sigset_t sigmask; //!< thread signal mask + sigset_t sig; //!< thread pending mask + // struct nxm_pth_state * + Addr pth_id; //!< out-of-line state + int flags; //!< shared flags +#define US_SIGSTACK 0x1 // thread called sigaltstack +#define US_ONSTACK 0x2 // thread is running on altstack +#define US_PROFILE 0x4 // thread called profil +#define US_SYSCALL 0x8 // thread in syscall +#define US_TRAP 0x10 // thread has trapped +#define US_YELLOW 0x20 // thread has mellowed yellow +#define US_YZONE 0x40 // thread has zoned out +#define US_FP_OWNED 0x80 // thread used floating point + + int cancel_state; //!< thread's cancelation state +#define US_CANCEL 0x1 // cancel pending +#define US_NOCANCEL 0X2 // synch cancel disabled +#define US_SYS_NOCANCEL 0x4 // syscall cancel disabled +#define US_ASYNC_NOCANCEL 0x8 // asynch cancel disabled +#define US_CANCEL_BITS (US_NOCANCEL|US_SYS_NOCANCEL|US_ASYNC_NOCANCEL) +#define US_CANCEL_MASK (US_CANCEL|US_NOCANCEL|US_SYS_NOCANCEL| \ + US_ASYNC_NOCANCEL) + + // These are semi-shared. They are always visible to + // the kernel but are never context-switched by the library. + + int nxm_ssig; //!< scheduler's synchronous signals + int reserved1; //!< reserved1 + int64_t nxm_active; //!< scheduler active + int64_t reserved2; //!< reserved2 + }; + + struct nxm_sched_state { + struct ushared_state nxm_u; //!< state own by user thread + unsigned int nxm_bits; //!< scheduler state / slot + int nxm_quantum; //!< quantum count-down value + int nxm_set_quantum; //!< quantum reset value + int nxm_sysevent; //!< syscall state + // struct nxm_upcall * + Addr nxm_uc_ret; //!< stack ptr of null thread + // void * + Addr nxm_tid; //!< scheduler's thread id + int64_t nxm_va; //!< page fault address + // struct nxm_pth_state * + Addr nxm_pthid; //!< id of null thread + uint64_t nxm_bound_pcs_count; //!< bound PCS thread count + int64_t pad[2]; //!< pad + }; + + /// nxm_shared. + struct nxm_shared { + int64_t nxm_callback; //!< address of upcall routine + unsigned int nxm_version; //!< version number + unsigned short nxm_uniq_offset; //!< correction factor for TEB + unsigned short pad1; //!< pad1 + int64_t space[2]; //!< future growth + struct nxm_sched_state nxm_ss[1]; //!< array of shared areas + }; + + /// nxm_slot_state_t. + enum nxm_slot_state_t { + NXM_SLOT_AVAIL, + NXM_SLOT_BOUND, + NXM_SLOT_UNBOUND, + NXM_SLOT_EMPTY + }; + + /// nxm_config_info + struct nxm_config_info { + int nxm_nslots_per_rad; //!< max number of VP slots per RAD + int nxm_nrads; //!< max number of RADs + // nxm_slot_state_t * + Addr nxm_slot_state; //!< per-VP slot state + // struct nxm_shared * + Addr nxm_rad[1]; //!< per-RAD shared areas + }; + + /// For nxm_thread_create. + enum nxm_thread_type { + NXM_TYPE_SCS = 0, + NXM_TYPE_VP = 1, + NXM_TYPE_MANAGER = 2 + }; + + /// Thread attributes. + struct nxm_thread_attr { + int version; //!< version + int type; //!< type + int cancel_flags; //!< cancel_flags + int priority; //!< priority + int policy; //!< policy + int signal_type; //!< signal_type + // void * + Addr pthid; //!< pthid + sigset_t sigmask; //!< sigmask + /// Initial register values. + struct { + uint64_t pc; //!< pc + uint64_t sp; //!< sp + uint64_t a0; //!< a0 + } registers; + uint64_t pad2[2]; //!< pad2 + }; + + /*/// Helper function to convert a host stat buffer to a target stat + /// buffer. Also copies the target buffer out to the simulated + /// memory space. Used by stat(), fstat(), and lstat(). + template <class T> + static void + copyOutStatBuf(TranslatingPort *mem, Addr addr, global_stat *host) + { + using namespace TheISA; + + TypedBufferArg<T> tgt(addr); + + tgt->st_dev = htog(host->st_dev); + tgt->st_ino = htog(host->st_ino); + tgt->st_mode = htog(host->st_mode); + tgt->st_nlink = htog(host->st_nlink); + tgt->st_uid = htog(host->st_uid); + tgt->st_gid = htog(host->st_gid); + tgt->st_rdev = htog(host->st_rdev); + tgt->st_size = htog(host->st_size); + tgt->st_atimeX = htog(host->st_atime); + tgt->st_mtimeX = htog(host->st_mtime); + tgt->st_ctimeX = htog(host->st_ctime); + tgt->st_blksize = htog(host->st_blksize); + tgt->st_blocks = htog(host->st_blocks); + + tgt.copyOut(mem); + }*/ + + /// Helper function to convert a host statfs buffer to a target statfs + /// buffer. Also copies the target buffer out to the simulated + /// memory space. Used by statfs() and fstatfs(). + template <class T> + static void + copyOutStatfsBuf(TranslatingPort *mem, Addr addr, global_statfs *host) + { + using namespace TheISA; + + TypedBufferArg<T> tgt(addr); + +#if defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD__) + tgt->f_type = 0; +#else + tgt->f_type = htog(host->f_type); +#endif + tgt->f_bsize = htog(host->f_bsize); + tgt->f_blocks = htog(host->f_blocks); + tgt->f_bfree = htog(host->f_bfree); + tgt->f_bavail = htog(host->f_bavail); + tgt->f_files = htog(host->f_files); + tgt->f_ffree = htog(host->f_ffree); + + // Is this as string normally? + memcpy(&tgt->f_fsid, &host->f_fsid, sizeof(host->f_fsid)); + + tgt.copyOut(mem); + } + +/* /// Helper function to convert a host stat buffer to an old pre-F64 + /// (4.x) target stat buffer. Also copies the target buffer out to + /// the simulated memory space. Used by pre_F64_stat(), + /// pre_F64_fstat(), and pre_F64_lstat(). + static void + copyOutPreF64StatBuf(TranslatingPort *mem, Addr addr, struct stat *host) + { + using namespace TheISA; + + TypedBufferArg<Tru64::pre_F64_stat> tgt(addr); + + tgt->st_dev = htog(host->st_dev); + tgt->st_ino = htog(host->st_ino); + tgt->st_mode = htog(host->st_mode); + tgt->st_nlink = htog(host->st_nlink); + tgt->st_uid = htog(host->st_uid); + tgt->st_gid = htog(host->st_gid); + tgt->st_rdev = htog(host->st_rdev); + tgt->st_size = htog(host->st_size); + tgt->st_atimeX = htog(host->st_atime); + tgt->st_mtimeX = htog(host->st_mtime); + tgt->st_ctimeX = htog(host->st_ctime); + tgt->st_blksize = htog(host->st_blksize); + tgt->st_blocks = htog(host->st_blocks); + + tgt.copyOut(mem); + }*/ + + + /// The target system's hostname. + static const char *hostname; + + + /// Target getdirentries() handler. + static SyscallReturn + getdirentriesFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + using namespace TheISA; + +#ifdef __CYGWIN__ + panic("getdirent not implemented on cygwin!"); +#else + int fd = process->sim_fd(tc->getSyscallArg(0)); + Addr tgt_buf = tc->getSyscallArg(1); + int tgt_nbytes = tc->getSyscallArg(2); + Addr tgt_basep = tc->getSyscallArg(3); + + char * const host_buf = new char[tgt_nbytes]; + + // just pass basep through uninterpreted. + TypedBufferArg<int64_t> basep(tgt_basep); + basep.copyIn(tc->getMemPort()); + long host_basep = (off_t)htog((int64_t)*basep); + int host_result = getdirentries(fd, host_buf, tgt_nbytes, &host_basep); + + // check for error + if (host_result < 0) { + delete [] host_buf; + return -errno; + } + + // no error: copy results back to target space + Addr tgt_buf_ptr = tgt_buf; + char *host_buf_ptr = host_buf; + char *host_buf_end = host_buf + host_result; + while (host_buf_ptr < host_buf_end) { + global_dirent *host_dp = (global_dirent *)host_buf_ptr; + int namelen = strlen(host_dp->d_name); + + // Actual size includes padded string rounded up for alignment. + // Subtract 256 for dummy char array in Tru64::dirent definition. + // Add 1 to namelen for terminating null char. + int tgt_bufsize = sizeof(Tru64::dirent) - 256 + roundUp(namelen+1, 8); + TypedBufferArg<Tru64::dirent> tgt_dp(tgt_buf_ptr, tgt_bufsize); + tgt_dp->d_ino = host_dp->d_ino; + tgt_dp->d_reclen = tgt_bufsize; + tgt_dp->d_namlen = namelen; + strcpy(tgt_dp->d_name, host_dp->d_name); + tgt_dp.copyOut(tc->getMemPort()); + + tgt_buf_ptr += tgt_bufsize; + host_buf_ptr += host_dp->d_reclen; + } + + delete [] host_buf; + + *basep = htog((int64_t)host_basep); + basep.copyOut(tc->getMemPort()); + + return tgt_buf_ptr - tgt_buf; +#endif + } + + /// Target sigreturn() handler. + static SyscallReturn + sigreturnFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + using namespace TheISA; + + using TheISA::RegFile; + TypedBufferArg<Tru64::sigcontext> sc(tc->getSyscallArg(0)); + + sc.copyIn(tc->getMemPort()); + + // Restore state from sigcontext structure. + // Note that we'll advance PC <- NPC before the end of the cycle, + // so we need to restore the desired PC into NPC. + // The current regs->pc will get clobbered. + tc->setNextPC(htog(sc->sc_pc)); + + for (int i = 0; i < 31; ++i) { + tc->setIntReg(i, htog(sc->sc_regs[i])); + tc->setFloatRegBits(i, htog(sc->sc_fpregs[i])); + } + + tc->setMiscReg(TheISA::Fpcr_DepTag, htog(sc->sc_fpcr)); + + return 0; + } + + + // + // Mach syscalls -- identified by negated syscall numbers + // + + /// Create a stack region for a thread. + static SyscallReturn + stack_createFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + using namespace TheISA; + + TypedBufferArg<Tru64::vm_stack> argp(tc->getSyscallArg(0)); + + argp.copyIn(tc->getMemPort()); + + // if the user chose an address, just let them have it. Otherwise + // pick one for them. + if (htog(argp->address) == 0) { + argp->address = htog(process->next_thread_stack_base); + int stack_size = (htog(argp->rsize) + htog(argp->ysize) + + htog(argp->gsize)); + process->next_thread_stack_base -= stack_size; + argp.copyOut(tc->getMemPort()); + } + + return 0; + } + + /// NXM library version stamp. + static + const int NXM_LIB_VERSION = 301003; + + /// This call sets up the interface between the user and kernel + /// schedulers by creating a shared-memory region. The shared memory + /// region has several structs, some global, some per-RAD, some per-VP. + static SyscallReturn + nxm_task_initFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + using namespace std; + using namespace TheISA; + + TypedBufferArg<Tru64::nxm_task_attr> attrp(tc->getSyscallArg(0)); + TypedBufferArg<Addr> configptr_ptr(tc->getSyscallArg(1)); + + attrp.copyIn(tc->getMemPort()); + + if (gtoh(attrp->nxm_version) != NXM_LIB_VERSION) { + cerr << "nxm_task_init: thread library version mismatch! " + << "got " << attrp->nxm_version + << ", expected " << NXM_LIB_VERSION << endl; + abort(); + } + + if (gtoh(attrp->flags) != Tru64::NXM_TASK_INIT_VP) { + cerr << "nxm_task_init: bad flag value " << attrp->flags + << " (expected " << Tru64::NXM_TASK_INIT_VP << ")" << endl; + abort(); + } + + const Addr base_addr = 0x12000; // was 0x3f0000000LL; + Addr cur_addr = base_addr; // next addresses to use + // first comes the config_info struct + Addr config_addr = cur_addr; + cur_addr += sizeof(Tru64::nxm_config_info); + // next comes the per-cpu state vector + Addr slot_state_addr = cur_addr; + int slot_state_size = + process->numCpus() * sizeof(Tru64::nxm_slot_state_t); + cur_addr += slot_state_size; + // now the per-RAD state struct (we only support one RAD) + cur_addr = 0x14000; // bump up addr for alignment + Addr rad_state_addr = cur_addr; + int rad_state_size = + (sizeof(Tru64::nxm_shared) + + (process->numCpus()-1) * sizeof(Tru64::nxm_sched_state)); + cur_addr += rad_state_size; + + // now initialize a config_info struct and copy it out to user space + TypedBufferArg<Tru64::nxm_config_info> config(config_addr); + + config->nxm_nslots_per_rad = htog(process->numCpus()); + config->nxm_nrads = htog(1); // only one RAD in our system! + config->nxm_slot_state = htog(slot_state_addr); + config->nxm_rad[0] = htog(rad_state_addr); + + config.copyOut(tc->getMemPort()); + + // initialize the slot_state array and copy it out + TypedBufferArg<Tru64::nxm_slot_state_t> slot_state(slot_state_addr, + slot_state_size); + for (int i = 0; i < process->numCpus(); ++i) { + // CPU 0 is bound to the calling process; all others are available + // XXX this code should have an endian conversion, but I don't think + // it works anyway + slot_state[i] = + (i == 0) ? Tru64::NXM_SLOT_BOUND : Tru64::NXM_SLOT_AVAIL; + } + + slot_state.copyOut(tc->getMemPort()); + + // same for the per-RAD "shared" struct. Note that we need to + // allocate extra bytes for the per-VP array which is embedded at + // the end. + TypedBufferArg<Tru64::nxm_shared> rad_state(rad_state_addr, + rad_state_size); + + rad_state->nxm_callback = attrp->nxm_callback; + rad_state->nxm_version = attrp->nxm_version; + rad_state->nxm_uniq_offset = attrp->nxm_uniq_offset; + for (int i = 0; i < process->numCpus(); ++i) { + Tru64::nxm_sched_state *ssp = &rad_state->nxm_ss[i]; + ssp->nxm_u.sigmask = htog(0); + ssp->nxm_u.sig = htog(0); + ssp->nxm_u.flags = htog(0); + ssp->nxm_u.cancel_state = htog(0); + ssp->nxm_u.nxm_ssig = 0; + ssp->nxm_bits = htog(0); + ssp->nxm_quantum = attrp->nxm_quantum; + ssp->nxm_set_quantum = attrp->nxm_quantum; + ssp->nxm_sysevent = htog(0); + + if (i == 0) { + uint64_t uniq = tc->readMiscReg(TheISA::Uniq_DepTag); + ssp->nxm_u.pth_id = htog(uniq + gtoh(attrp->nxm_uniq_offset)); + ssp->nxm_u.nxm_active = htog(uniq | 1); + } + else { + ssp->nxm_u.pth_id = htog(0); + ssp->nxm_u.nxm_active = htog(0); + } + } + + rad_state.copyOut(tc->getMemPort()); + + // + // copy pointer to shared config area out to user + // + *configptr_ptr = htog(config_addr); + configptr_ptr.copyOut(tc->getMemPort()); + + // Register this as a valid address range with the process + process->nxm_start = base_addr; + process->nxm_end = cur_addr; + + return 0; + } + + /// Initialize thread context. + static void + init_thread_context(ThreadContext *tc, + Tru64::nxm_thread_attr *attrp, uint64_t uniq_val) + { + using namespace TheISA; + + tc->clearArchRegs(); + + tc->setIntReg(TheISA::ArgumentReg0, gtoh(attrp->registers.a0)); + tc->setIntReg(27/*t12*/, gtoh(attrp->registers.pc)); + tc->setIntReg(TheISA::StackPointerReg, gtoh(attrp->registers.sp)); + tc->setMiscReg(TheISA::Uniq_DepTag, uniq_val); + + tc->setPC(gtoh(attrp->registers.pc)); + tc->setNextPC(gtoh(attrp->registers.pc) + sizeof(TheISA::MachInst)); + + tc->activate(); + } + + /// Create thread. + static SyscallReturn + nxm_thread_createFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + using namespace std; + using namespace TheISA; + + TypedBufferArg<Tru64::nxm_thread_attr> attrp(tc->getSyscallArg(0)); + TypedBufferArg<uint64_t> kidp(tc->getSyscallArg(1)); + int thread_index = tc->getSyscallArg(2); + + // get attribute args + attrp.copyIn(tc->getMemPort()); + + if (gtoh(attrp->version) != NXM_LIB_VERSION) { + cerr << "nxm_thread_create: thread library version mismatch! " + << "got " << attrp->version + << ", expected " << NXM_LIB_VERSION << endl; + abort(); + } + + if (thread_index < 0 | thread_index > process->numCpus()) { + cerr << "nxm_thread_create: bad thread index " << thread_index + << endl; + abort(); + } + + // On a real machine, the per-RAD shared structure is in + // shared memory, so both the user and kernel can get at it. + // We don't have that luxury, so we just copy it in and then + // back out again. + int rad_state_size = + (sizeof(Tru64::nxm_shared) + + (process->numCpus()-1) * sizeof(Tru64::nxm_sched_state)); + + TypedBufferArg<Tru64::nxm_shared> rad_state(0x14000, + rad_state_size); + rad_state.copyIn(tc->getMemPort()); + + uint64_t uniq_val = gtoh(attrp->pthid) - gtoh(rad_state->nxm_uniq_offset); + + if (gtoh(attrp->type) == Tru64::NXM_TYPE_MANAGER) { + // DEC pthreads seems to always create one of these (in + // addition to N application threads), but we don't use it, + // so don't bother creating it. + + // This is supposed to be a port number. Make something up. + *kidp = htog(99); + kidp.copyOut(tc->getMemPort()); + + return 0; + } else if (gtoh(attrp->type) == Tru64::NXM_TYPE_VP) { + // A real "virtual processor" kernel thread. Need to fork + // this thread on another CPU. + Tru64::nxm_sched_state *ssp = &rad_state->nxm_ss[thread_index]; + + if (gtoh(ssp->nxm_u.nxm_active) != 0) + return (int) Tru64::KERN_NOT_RECEIVER; + + ssp->nxm_u.pth_id = attrp->pthid; + ssp->nxm_u.nxm_active = htog(uniq_val | 1); + + rad_state.copyOut(tc->getMemPort()); + + Addr slot_state_addr = 0x12000 + sizeof(Tru64::nxm_config_info); + int slot_state_size = + process->numCpus() * sizeof(Tru64::nxm_slot_state_t); + + TypedBufferArg<Tru64::nxm_slot_state_t> + slot_state(slot_state_addr, + slot_state_size); + + slot_state.copyIn(tc->getMemPort()); + + if (slot_state[thread_index] != Tru64::NXM_SLOT_AVAIL) { + cerr << "nxm_thread_createFunc: requested VP slot " + << thread_index << " not available!" << endl; + fatal(""); + } + + // XXX This should have an endian conversion but I think this code + // doesn't work anyway + slot_state[thread_index] = Tru64::NXM_SLOT_BOUND; + + slot_state.copyOut(tc->getMemPort()); + + // Find a free simulator thread context. + for (int i = 0; i < process->numCpus(); ++i) { + ThreadContext *tc = process->threadContexts[i]; + + if (tc->status() == ThreadContext::Suspended) { + // inactive context... grab it + init_thread_context(tc, attrp, uniq_val); + + // This is supposed to be a port number, but we'll try + // and get away with just sticking the thread index + // here. + *kidp = htog(thread_index); + kidp.copyOut(tc->getMemPort()); + + return 0; + } + } + + // fell out of loop... no available inactive context + cerr << "nxm_thread_create: no idle contexts available." << endl; + abort(); + } else { + cerr << "nxm_thread_create: can't handle thread type " + << attrp->type << endl; + abort(); + } + + return 0; + } + + /// Thread idle call (like yield()). + static SyscallReturn + nxm_idleFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + return 0; + } + + /// Block thread. + static SyscallReturn + nxm_thread_blockFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + using namespace std; + + uint64_t tid = tc->getSyscallArg(0); + uint64_t secs = tc->getSyscallArg(1); + uint64_t flags = tc->getSyscallArg(2); + uint64_t action = tc->getSyscallArg(3); + uint64_t usecs = tc->getSyscallArg(4); + + cout << tc->getCpuPtr()->name() << ": nxm_thread_block " << tid << " " + << secs << " " << flags << " " << action << " " << usecs << endl; + + return 0; + } + + /// block. + static SyscallReturn + nxm_blockFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + using namespace std; + + Addr uaddr = tc->getSyscallArg(0); + uint64_t val = tc->getSyscallArg(1); + uint64_t secs = tc->getSyscallArg(2); + uint64_t usecs = tc->getSyscallArg(3); + uint64_t flags = tc->getSyscallArg(4); + + BaseCPU *cpu = tc->getCpuPtr(); + + cout << cpu->name() << ": nxm_block " + << hex << uaddr << dec << " " << val + << " " << secs << " " << usecs + << " " << flags << endl; + + return 0; + } + + /// Unblock thread. + static SyscallReturn + nxm_unblockFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + using namespace std; + + Addr uaddr = tc->getSyscallArg(0); + + cout << tc->getCpuPtr()->name() << ": nxm_unblock " + << hex << uaddr << dec << endl; + + return 0; + } + + /// Switch thread priority. + static SyscallReturn + swtch_priFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + // Attempts to switch to another runnable thread (if there is + // one). Returns false if there are no other threads to run + // (i.e., the thread can reasonably spin-wait) or true if there + // are other threads. + // + // Since we assume at most one "kernel" thread per CPU, it's + // always safe to return false here. + return 0; //false; + } + + + /// Activate thread context waiting on a channel. Just activate one + /// by default. + static int + activate_waiting_context(Addr uaddr, LiveProcess *process, + bool activate_all = false) + { + using namespace std; + + int num_activated = 0; + + list<Process::WaitRec>::iterator i = process->waitList.begin(); + list<Process::WaitRec>::iterator end = process->waitList.end(); + + while (i != end && (num_activated == 0 || activate_all)) { + if (i->waitChan == uaddr) { + // found waiting process: make it active + ThreadContext *newCtx = i->waitingContext; + assert(newCtx->status() == ThreadContext::Suspended); + newCtx->activate(); + + // get rid of this record + i = process->waitList.erase(i); + + ++num_activated; + } else { + ++i; + } + } + + return num_activated; + } + + /// M5 hacked-up lock acquire. + static void + m5_lock_mutex(Addr uaddr, LiveProcess *process, ThreadContext *tc) + { + using namespace TheISA; + + TypedBufferArg<uint64_t> lockp(uaddr); + + lockp.copyIn(tc->getMemPort()); + + if (gtoh(*lockp) == 0) { + // lock is free: grab it + *lockp = htog(1); + lockp.copyOut(tc->getMemPort()); + } else { + // lock is busy: disable until free + process->waitList.push_back(Process::WaitRec(uaddr, tc)); + tc->suspend(); + } + } + + /// M5 unlock call. + static void + m5_unlock_mutex(Addr uaddr, LiveProcess *process, ThreadContext *tc) + { + TypedBufferArg<uint64_t> lockp(uaddr); + + lockp.copyIn(tc->getMemPort()); + assert(*lockp != 0); + + // Check for a process waiting on the lock. + int num_waiting = activate_waiting_context(uaddr, process); + + // clear lock field if no waiting context is taking over the lock + if (num_waiting == 0) { + *lockp = 0; + lockp.copyOut(tc->getMemPort()); + } + } + + /// Lock acquire syscall handler. + static SyscallReturn + m5_mutex_lockFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + Addr uaddr = tc->getSyscallArg(0); + + m5_lock_mutex(uaddr, process, tc); + + // Return 0 since we will always return to the user with the lock + // acquired. We will just keep the context inactive until that is + // true. + return 0; + } + + /// Try lock (non-blocking). + static SyscallReturn + m5_mutex_trylockFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + using namespace TheISA; + + Addr uaddr = tc->getSyscallArg(0); + TypedBufferArg<uint64_t> lockp(uaddr); + + lockp.copyIn(tc->getMemPort()); + + if (gtoh(*lockp) == 0) { + // lock is free: grab it + *lockp = htog(1); + lockp.copyOut(tc->getMemPort()); + return 0; + } else { + return 1; + } + } + + /// Unlock syscall handler. + static SyscallReturn + m5_mutex_unlockFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + Addr uaddr = tc->getSyscallArg(0); + + m5_unlock_mutex(uaddr, process, tc); + + return 0; + } + + /// Signal ocndition. + static SyscallReturn + m5_cond_signalFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + Addr cond_addr = tc->getSyscallArg(0); + + // Wake up one process waiting on the condition variable. + activate_waiting_context(cond_addr, process); + + return 0; + } + + /// Wake up all processes waiting on the condition variable. + static SyscallReturn + m5_cond_broadcastFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + Addr cond_addr = tc->getSyscallArg(0); + + activate_waiting_context(cond_addr, process, true); + + return 0; + } + + /// Wait on a condition. + static SyscallReturn + m5_cond_waitFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + using namespace TheISA; + + Addr cond_addr = tc->getSyscallArg(0); + Addr lock_addr = tc->getSyscallArg(1); + TypedBufferArg<uint64_t> condp(cond_addr); + TypedBufferArg<uint64_t> lockp(lock_addr); + + // user is supposed to acquire lock before entering + lockp.copyIn(tc->getMemPort()); + assert(gtoh(*lockp) != 0); + + m5_unlock_mutex(lock_addr, process, tc); + + process->waitList.push_back(Process::WaitRec(cond_addr, tc)); + tc->suspend(); + + return 0; + } + + /// Thread exit. + static SyscallReturn + m5_thread_exitFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + assert(tc->status() == ThreadContext::Active); + tc->deallocate(); + + return 0; + } + + /// Indirect syscall invocation (call #0). + static SyscallReturn + indirectSyscallFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) + { + int new_callnum = tc->getSyscallArg(0); + + for (int i = 0; i < 5; ++i) + tc->setSyscallArg(i, tc->getSyscallArg(i+1)); + + + SyscallDesc *new_desc = process->getDesc(new_callnum); + if (desc == NULL) + fatal("Syscall %d out of range", callnum); + + new_desc->doSyscall(new_callnum, process, tc); + + return 0; + } + +}; // class Tru64 + +class Tru64_F64 : public Tru64 +{ + public: + + /// Stat buffer. Note that Tru64 v5.0+ use a new "F64" stat + /// structure, and a new set of syscall numbers for stat calls. + /// On some hosts (notably Linux) define st_atime, st_mtime, and + /// st_ctime as macros, so we append an X to get around this. + struct F64_stat { + dev_t st_dev; //!< st_dev + int32_t st_retired1; //!< st_retired1 + mode_t st_mode; //!< st_mode + nlink_t st_nlink; //!< st_nlink + uint16_t st_nlink_reserved; //!< st_nlink_reserved + uid_t st_uid; //!< st_uid + gid_t st_gid; //!< st_gid + dev_t st_rdev; //!< st_rdev + dev_t st_ldev; //!< st_ldev + off_t st_size; //!< st_size + time_t st_retired2; //!< st_retired2 + int32_t st_uatime; //!< st_uatime + time_t st_retired3; //!< st_retired3 + int32_t st_umtime; //!< st_umtime + time_t st_retired4; //!< st_retired4 + int32_t st_uctime; //!< st_uctime + int32_t st_retired5; //!< st_retired5 + int32_t st_retired6; //!< st_retired6 + uint32_t st_flags; //!< st_flags + uint32_t st_gen; //!< st_gen + uint64_t st_spare[4]; //!< st_spare[4] + ino_t st_ino; //!< st_ino + int32_t st_ino_reserved; //!< st_ino_reserved + time_t st_atimeX; //!< st_atime + int32_t st_atime_reserved; //!< st_atime_reserved + time_t st_mtimeX; //!< st_mtime + int32_t st_mtime_reserved; //!< st_mtime_reserved + time_t st_ctimeX; //!< st_ctime + int32_t st_ctime_reserved; //!< st_ctime_reserved + uint64_t st_blksize; //!< st_blksize + uint64_t st_blocks; //!< st_blocks + }; + + typedef F64_stat tgt_stat; +/* + static void copyOutStatBuf(TranslatingPort *mem, Addr addr, + global_stat *host) + { + Tru64::copyOutStatBuf<Tru64::F64_stat>(mem, addr, host); + }*/ + + static void copyOutStatfsBuf(TranslatingPort *mem, Addr addr, + global_statfs *host) + { + Tru64::copyOutStatfsBuf<Tru64::F64_statfs>(mem, addr, host); + } +}; + +class Tru64_PreF64 : public Tru64 +{ + public: + + /// Old Tru64 v4.x stat struct. + /// Tru64 maintains backwards compatibility with v4.x by + /// implementing another set of stat functions using the old + /// structure definition and binding them to the old syscall + /// numbers. + + struct pre_F64_stat { + dev_t st_dev; + ino_t st_ino; + mode_t st_mode; + nlink_t st_nlink; + uid_t st_uid __attribute__ ((aligned(sizeof(uid_t)))); + gid_t st_gid; + dev_t st_rdev; + off_t st_size __attribute__ ((aligned(sizeof(off_t)))); + time_t st_atimeX; + int32_t st_uatime; + time_t st_mtimeX; + int32_t st_umtime; + time_t st_ctimeX; + int32_t st_uctime; + uint32_t st_blksize; + int32_t st_blocks; + uint32_t st_flags; + uint32_t st_gen; + }; + + typedef pre_F64_stat tgt_stat; +/* + static void copyOutStatBuf(TranslatingPort *mem, Addr addr, + global_stat *host) + { + Tru64::copyOutStatBuf<Tru64::pre_F64_stat>(mem, addr, host); + }*/ + + static void copyOutStatfsBuf(TranslatingPort *mem, Addr addr, + global_statfs *host) + { + Tru64::copyOutStatfsBuf<Tru64::pre_F64_statfs>(mem, addr, host); + } +}; + +#endif // FULL_SYSTEM + +#endif // __TRU64_HH__ diff --git a/src/kern/tru64/tru64_events.cc b/src/kern/tru64/tru64_events.cc new file mode 100644 index 000000000..69638bde1 --- /dev/null +++ b/src/kern/tru64/tru64_events.cc @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu + */ + +#include "cpu/thread_context.hh" +#include "cpu/base.hh" +#include "kern/system_events.hh" +#include "kern/tru64/tru64_events.hh" +#include "kern/tru64/dump_mbuf.hh" +#include "kern/tru64/printf.hh" +#include "arch/alpha/ev5.hh" +#include "arch/arguments.hh" +#include "arch/isa_traits.hh" +#include "sim/system.hh" + +using namespace TheISA; + +//void SkipFuncEvent::process(ExecContext *tc); + +void +BadAddrEvent::process(ThreadContext *tc) +{ + // The following gross hack is the equivalent function to the + // annotation for vmunix::badaddr in: + // simos/simulation/apps/tcl/osf/tlaser.tcl + + uint64_t a0 = tc->readIntReg(ArgumentReg0); + + AddrRangeList resp; + AddrRangeList snoop; + AddrRangeIter iter; + bool found = false; + + tc->getPhysPort()->getPeerAddressRanges(resp, snoop); + for(iter = resp.begin(); iter != resp.end(); iter++) + { + if (*iter == (TheISA::K0Seg2Phys(a0) & EV5::PAddrImplMask)) + found = true; + } + + if (!TheISA::IsK0Seg(a0) || found ) { + + DPRINTF(BADADDR, "badaddr arg=%#x bad\n", a0); + tc->setIntReg(ReturnValueReg, 0x1); + SkipFuncEvent::process(tc); + } + else + DPRINTF(BADADDR, "badaddr arg=%#x good\n", a0); +} + +void +PrintfEvent::process(ThreadContext *tc) +{ + if (DTRACE(Printf)) { + DebugOut() << curTick << ": " << tc->getCpuPtr()->name() << ": "; + + AlphaArguments args(tc); + tru64::Printf(args); + } +} + +void +DebugPrintfEvent::process(ThreadContext *tc) +{ + if (DTRACE(DebugPrintf)) { + if (!raw) + DebugOut() << curTick << ": " << tc->getCpuPtr()->name() << ": "; + + AlphaArguments args(tc); + tru64::Printf(args); + } +} + +void +DumpMbufEvent::process(ThreadContext *tc) +{ + if (DTRACE(DebugPrintf)) { + AlphaArguments args(tc); + tru64::DumpMbuf(args); + } +} diff --git a/src/kern/tru64/tru64_events.hh b/src/kern/tru64/tru64_events.hh new file mode 100644 index 000000000..6a1ab2e51 --- /dev/null +++ b/src/kern/tru64/tru64_events.hh @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Lisa Hsu + */ + +#ifndef __TRU64_EVENTS_HH__ +#define __TRU64_EVENTS_HH__ + +#include <string> + +#include "cpu/pc_event.hh" +#include "kern/system_events.hh" + +class ThreadContext; + +class BadAddrEvent : public SkipFuncEvent +{ + public: + BadAddrEvent(PCEventQueue *q, const std::string &desc, Addr addr) + : SkipFuncEvent(q, desc, addr) {} + virtual void process(ThreadContext *tc); +}; + +class PrintfEvent : public PCEvent +{ + public: + PrintfEvent(PCEventQueue *q, const std::string &desc, Addr addr) + : PCEvent(q, desc, addr) {} + virtual void process(ThreadContext *tc); +}; + +class DebugPrintfEvent : public PCEvent +{ + private: + bool raw; + + public: + DebugPrintfEvent(PCEventQueue *q, const std::string &desc, Addr addr, + bool r = false) + : PCEvent(q, desc, addr), raw(r) {} + virtual void process(ThreadContext *tc); +}; + +class DebugPrintfrEvent : public DebugPrintfEvent +{ + public: + DebugPrintfrEvent(PCEventQueue *q, const std::string &desc, Addr addr) + : DebugPrintfEvent(q, desc, addr, true) + {} +}; + +class DumpMbufEvent : public PCEvent +{ + public: + DumpMbufEvent(PCEventQueue *q, const std::string &desc, Addr addr) + : PCEvent(q, desc, addr) {} + virtual void process(ThreadContext *tc); +}; + +#endif // __TRU64_EVENTS_HH__ diff --git a/src/kern/tru64/tru64_syscalls.cc b/src/kern/tru64/tru64_syscalls.cc new file mode 100644 index 000000000..8051b9efb --- /dev/null +++ b/src/kern/tru64/tru64_syscalls.cc @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include "kern/tru64/tru64_syscalls.hh" + +namespace { + const char * + standard_strings[SystemCalls<Tru64>::StandardNumber] = { + "syscall", // 0 + "exit", // 1 + "fork", // 2 + "read", // 3 + "write", // 4 + "old_open", // 5 + "close", // 6 + "wait4", // 7 + "old_creat", // 8 + "link", // 9 + + "unlink", // 10 + "execv", // 11 + "chdir", // 12 + "fchdir", // 13 + "mknod", // 14 + "chmod", // 15 + "chown", // 16 + "obreak", // 17 + "pre_F64_getfsstat", // 18 + "lseek", // 19 + + "getpid", // 20 + "mount", // 21 + "unmount", // 22 + "setuid", // 23 + "getuid", // 24 + "exec_with_loader", // 25 + "ptrace", // 26 + "recvmsg", // 27 + "sendmsg", // 28 + "recvfrom", // 29 + + "accept", // 30 + "getpeername", // 31 + "getsockname", // 32 + "access", // 33 + "chflags", // 34 + "fchflags", // 35 + "sync", // 36 + "kill", // 37 + "old_stat", // 38 + "setpgid", // 39 + + "old_lstat", // 40 + "dup", // 41 + "pipe", // 42 + "set_program_attributes", // 43 + "profil", // 44 + "open", // 45 + "obsolete_osigaction", // 46 + "getgid", // 47 + "sigprocmask", // 48 + "getlogin", // 49 + + "setlogin", // 50 + "acct", // 51 + "sigpending", // 52 + "classcntl", // 53 + "ioctl", // 54 + "reboot", // 55 + "revoke", // 56 + "symlink", // 57 + "readlink", // 58 + "execve", // 59 + + "umask", // 60 + "chroot", // 61 + "old_fstat", // 62 + "getpgrp", // 63 + "getpagesize", // 64 + "mremap", // 65 + "vfork", // 66 + "pre_F64_stat", // 67 + "pre_F64_lstat", // 68 + "sbrk", // 69 + + "sstk", // 70 + "mmap", // 71 + "ovadvise", // 72 + "munmap", // 73 + "mprotect", // 74 + "madvise", // 75 + "old_vhangup", // 76 + "kmodcall", // 77 + "mincore", // 78 + "getgroups", // 79 + + "setgroups", // 80 + "old_getpgrp", // 81 + "setpgrp", // 82 + "setitimer", // 83 + "old_wait", // 84 + "table", // 85 + "getitimer", // 86 + "gethostname", // 87 + "sethostname", // 88 + "getdtablesize", // 89 + + "dup2", // 90 + "pre_F64_fstat", // 91 + "fcntl", // 92 + "select", // 93 + "poll", // 94 + "fsync", // 95 + "setpriority", // 96 + "socket", // 97 + "connect", // 98 + "old_accept", // 99 + + "getpriority", // 100 + "old_send", // 101 + "old_recv", // 102 + "sigreturn", // 103 + "bind", // 104 + "setsockopt", // 105 + "listen", // 106 + "plock", // 107 + "old_sigvec", // 108 + "old_sigblock", // 109 + + "old_sigsetmask", // 110 + "sigsuspend", // 111 + "sigstack", // 112 + "old_recvmsg", // 113 + "old_sendmsg", // 114 + "obsolete_vtrcae", // 115 + "gettimeofday", // 116 + "getrusage", // 117 + "getsockopt", // 118 + "numa_syscalls", // 119 + + "readv", // 120 + "writev", // 121 + "settimeofday", // 122 + "fchown", // 123 + "fchmod", // 124 + "old_recvfrom", // 125 + "setreuid", // 126 + "setregid", // 127 + "rename", // 128 + "truncate", // 129 + + "ftruncate", // 130 + "flock", // 131 + "setgid", // 132 + "sendto", // 133 + "shutdown", // 134 + "socketpair", // 135 + "mkdir", // 136 + "rmdir", // 137 + "utimes", // 138 + "obsolete_42_sigreturn", // 139 + + "adjtime", // 140 + "old_getpeername", // 141 + "gethostid", // 142 + "sethostid", // 143 + "getrlimit", // 144 + "setrlimit", // 145 + "old_killpg", // 146 + "setsid", // 147 + "quotactl", // 148 + "oldquota", // 149 + + "old_getsockname", // 150 + "pread", // 151 + "pwrite", // 152 + "pid_block", // 153 + "pid_unblock", // 154 + "signal_urti", // 155 + "sigaction", // 156 + "sigwaitprim", // 157 + "nfssvc", // 158 + "getdirentries", // 159 + + "pre_F64_statfs", // 160 + "pre_F64_fstatfs", // 161 + 0, // 162 + "async_daemon", // 163 + "getfh", // 164 + "getdomainname", // 165 + "setdomainname", // 166 + 0, // 167 + 0, // 168 + "exportfs", // 169 + + 0, // 170 + 0, // 171 + 0, // 172 + 0, // 173 + 0, // 174 + 0, // 175 + 0, // 176 + 0, // 177 + 0, // 178 + 0, // 179 + + 0, // 180 + "alt_plock", // 181 + 0, // 182 + 0, // 183 + "getmnt", // 184 + 0, // 185 + 0, // 186 + "alt_sigpending", // 187 + "alt_setsid", // 188 + 0, // 189 + + 0, // 190 + 0, // 191 + 0, // 192 + 0, // 193 + 0, // 194 + 0, // 195 + 0, // 196 + 0, // 197 + 0, // 198 + "swapon", // 199 + + "msgctl", // 200 + "msgget", // 201 + "msgrcv", // 202 + "msgsnd", // 203 + "semctl", // 204 + "semget", // 205 + "semop", // 206 + "uname", // 207 + "lchown", // 208 + "shmat", // 209 + + "shmctl", // 210 + "shmdt", // 211 + "shmget", // 212 + "mvalid", // 213 + "getaddressconf", // 214 + "msleep", // 215 + "mwakeup", // 216 + "msync", // 217 + "signal", // 218 + "utc_gettime", // 219 + + "utc_adjtime", // 220 + 0, // 221 + "security", // 222 + "kloadcall", // 223 + "stat", // 224 + "lstat", // 225 + "fstat", // 226 + "statfs", // 227 + "fstatfs", // 228 + "getfsstat", // 229 + + "gettimeofday64", // 230 + "settimeofday64", // 231 + 0, // 232 + "getpgid", // 233 + "getsid", // 234 + "sigaltstack", // 235 + "waitid", // 236 + "priocntlset", // 237 + "sigsendset", // 238 + "set_speculative", // 239 + + "msfs_syscall", // 240 + "sysinfo", // 241 + "uadmin", // 242 + "fuser", // 243 + "proplist_syscall", // 244 + "ntp_adjtime", // 245 + "ntp_gettime", // 246 + "pathconf", // 247 + "fpathconf", // 248 + "sync2", // 249 + + "uswitch", // 250 + "usleep_thread", // 251 + "audcntl", // 252 + "audgen", // 253 + "sysfs", // 254 + "subsys_info", // 255 + "getsysinfo", // 256 + "setsysinfo", // 257 + "afs_syscall", // 258 + "swapctl", // 259 + + "memcntl", // 260 + "fdatasync", // 261 + "oflock", // 262 + "_F64_readv", // 263 + "_F64_writev", // 264 + "cdslxlate", // 265 + "sendfile", // 266 + }; + + const char * + mach_strings[SystemCalls<Tru64>::MachNumber] = { + 0, // 0 + 0, // 1 + 0, // 2 + 0, // 3 + 0, // 4 + 0, // 5 + 0, // 6 + 0, // 7 + 0, // 8 + 0, // 9 + + "task_self", // 10 + "thread_reply", // 11 + "task_notify", // 12 + "thread_self", // 13 + 0, // 14 + 0, // 15 + 0, // 16 + 0, // 17 + 0, // 18 + 0, // 19 + + "msg_send_trap", // 20 + "msg_receive_trap", // 21 + "msg_rpc_trap", // 22 + 0, // 23 + "nxm_block", // 24 + "nxm_unblock", // 25 + 0, // 26 + 0, // 27 + 0, // 28 + "nxm_thread_destroy", // 29 + + "lw_wire", // 30 + "lw_unwire", // 31 + "nxm_thread_create", // 32 + "nxm_task_init", // 33 + 0, // 34 + "nxm_idle", // 35 + "nxm_wakeup_idle", // 36 + "nxm_set_pthid", // 37 + "nxm_thread_kill", // 38 + "nxm_thread_block", // 39 + + "nxm_thread_wakeup", // 40 + "init_process", // 41 + "nxm_get_binding", // 42 + "map_fd", // 43 + "nxm_resched", // 44 + "nxm_set_cancel", // 45 + "nxm_set_binding", // 46 + "stack_create", // 47 + "nxm_get_state", // 48 + "nxm_thread_suspend", // 49 + + "nxm_thread_resume", // 50 + "nxm_signal_check", // 51 + "htg_unix_syscall", // 52 + 0, // 53 + 0, // 54 + "host_self", // 55 + "host_priv_self", // 56 + 0, // 57 + 0, // 58 + "swtch_pri", // 59 + + "swtch", // 60 + "thread_switch", // 61 + "semop_fast", // 62 + "nxm_pshared_init", // 63 + "nxm_pshared_block", // 64 + "nxm_pshared_unblock", // 65 + "nxm_pshared_destroy", // 66 + "nxm_swtch_pri", // 67 + "lw_syscall", // 68 + 0, // 69 + + "mach_sctimes_0", // 70 + "mach_sctimes_1", // 71 + "mach_sctimes_2", // 72 + "mach_sctimes_3", // 73 + "mach_sctimes_4", // 74 + "mach_sctimes_5", // 75 + "mach_sctimes_6", // 76 + "mach_sctimes_7", // 77 + "mach_sctimes_8", // 78 + "mach_sctimes_9", // 79 + + "mach_sctimes_10", // 80 + "mach_sctimes_11", // 81 + "mach_sctimes_port_alloc_dealloc", // 82 + }; +} + +const char * +SystemCalls<Tru64>::name(int num) +{ + if (num >= Number) + return 0; + else if (num >= StandardNumber) + return mach_strings[num - StandardNumber]; + else if (num >= 0) + return standard_strings[num]; + else if (num > -MachNumber) + return mach_strings[-num]; + else + return 0; +} diff --git a/src/kern/tru64/tru64_syscalls.hh b/src/kern/tru64/tru64_syscalls.hh new file mode 100644 index 000000000..66f5c2d39 --- /dev/null +++ b/src/kern/tru64/tru64_syscalls.hh @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __KERN_TRU64_TRU64_SYSCALLS_HH__ +#define __KERN_TRU64_TRU64_SYSCALLS_HH__ + +#include "kern/tru64/tru64.hh" + +template <class OS> +class SystemCalls; + +template <> +class SystemCalls<Tru64> +{ + public: + enum { + syscall = 0, + exit = 1, + fork = 2, + read = 3, + write = 4, + old_open = 5, + close = 6, + wait4 = 7, + old_creat = 8, + link = 9, + unlink = 10, + execv = 11, + chdir = 12, + fchdir = 13, + mknod = 14, + chmod = 15, + chown = 16, + obreak = 17, + pre_F64_getfsstat = 18, + lseek = 19, + getpid = 20, + mount = 21, + unmount = 22, + setuid = 23, + getuid = 24, + exec_with_loader = 25, + ptrace = 26, + recvmsg = 27, + sendmsg = 28, + recvfrom = 29, + accept = 30, + getpeername = 31, + getsockname = 32, + access = 33, + chflags = 34, + fchflags = 35, + sync = 36, + kill = 37, + old_stat = 38, + setpgid = 39, + old_lstat = 40, + dup = 41, + pipe = 42, + set_program_attributes = 43, + profil = 44, + open = 45, + obsolete_osigaction = 46, + getgid = 47, + sigprocmask = 48, + getlogin = 49, + setlogin = 50, + acct = 51, + sigpending = 52, + classcntl = 53, + ioctl = 54, + reboot = 55, + revoke = 56, + symlink = 57, + readlink = 58, + execve = 59, + umask = 60, + chroot = 61, + old_fstat = 62, + getpgrp = 63, + getpagesize = 64, + mremap = 65, + vfork = 66, + pre_F64_stat = 67, + pre_F64_lstat = 68, + sbrk = 69, + sstk = 70, + mmap = 71, + ovadvise = 72, + munmap = 73, + mprotect = 74, + madvise = 75, + old_vhangup = 76, + kmodcall = 77, + mincore = 78, + getgroups = 79, + setgroups = 80, + old_getpgrp = 81, + setpgrp = 82, + setitimer = 83, + old_wait = 84, + table = 85, + getitimer = 86, + gethostname = 87, + sethostname = 88, + getdtablesize = 89, + dup2 = 90, + pre_F64_fstat = 91, + fcntl = 92, + select = 93, + poll = 94, + fsync = 95, + setpriority = 96, + socket = 97, + connect = 98, + old_accept = 99, + getpriority = 100, + old_send = 101, + old_recv = 102, + sigreturn = 103, + bind = 104, + setsockopt = 105, + listen = 106, + plock = 107, + old_sigvec = 108, + old_sigblock = 109, + old_sigsetmask = 110, + sigsuspend = 111, + sigstack = 112, + old_recvmsg = 113, + old_sendmsg = 114, + obsolete_vtrcae = 115, + gettimeofday = 116, + getrusage = 117, + getsockopt = 118, + numa_syscalls = 119, + readv = 120, + writev = 121, + settimeofday = 122, + fchown = 123, + fchmod = 124, + old_recvfrom = 125, + setreuid = 126, + setregid = 127, + rename = 128, + truncate = 129, + ftruncate = 130, + flock = 131, + setgid = 132, + sendto = 133, + shutdown = 134, + socketpair = 135, + mkdir = 136, + rmdir = 137, + utimes = 138, + obsolete_42_sigreturn = 139, + adjtime = 140, + old_getpeername = 141, + gethostid = 142, + sethostid = 143, + getrlimit = 144, + setrlimit = 145, + old_killpg = 146, + setsid = 147, + quotactl = 148, + oldquota = 149, + old_getsockname = 150, + pread = 151, + pwrite = 152, + pid_block = 153, + pid_unblock = 154, + signal_urti = 155, + sigaction = 156, + sigwaitprim = 157, + nfssvc = 158, + getdirentries = 159, + pre_F64_statfs = 160, + pre_F64_fstatfs = 161, + async_daemon = 163, + getfh = 164, + getdomainname = 165, + setdomainname = 166, + exportfs = 169, + alt_plock = 181, + getmnt = 184, + alt_sigpending = 187, + alt_setsid = 188, + swapon = 199, + msgctl = 200, + msgget = 201, + msgrcv = 202, + msgsnd = 203, + semctl = 204, + semget = 205, + semop = 206, + uname = 207, + lchown = 208, + shmat = 209, + shmctl = 210, + shmdt = 211, + shmget = 212, + mvalid = 213, + getaddressconf = 214, + msleep = 215, + mwakeup = 216, + msync = 217, + signal = 218, + utc_gettime = 219, + utc_adjtime = 220, + security = 222, + kloadcall = 223, + stat = 224, + lstat = 225, + fstat = 226, + statfs = 227, + fstatfs = 228, + getfsstat = 229, + gettimeofday64 = 230, + settimeofday64 = 231, + getpgid = 233, + getsid = 234, + sigaltstack = 235, + waitid = 236, + priocntlset = 237, + sigsendset = 238, + set_speculative = 239, + msfs_syscall = 240, + sysinfo = 241, + uadmin = 242, + fuser = 243, + proplist_syscall = 244, + ntp_adjtime = 245, + ntp_gettime = 246, + pathconf = 247, + fpathconf = 248, + sync2 = 249, + uswitch = 250, + usleep_thread = 251, + audcntl = 252, + audgen = 253, + sysfs = 254, + subsys_info = 255, + getsysinfo = 256, + setsysinfo = 257, + afs_syscall = 258, + swapctl = 259, + memcntl = 260, + fdatasync = 261, + oflock = 262, + _F64_readv = 263, + _F64_writev = 264, + cdslxlate = 265, + sendfile = 266, + StandardNumber + }; + + enum { + task_self = 10, + thread_reply = 11, + task_notify = 12, + thread_self = 13, + msg_send_trap = 20, + msg_receive_trap = 21, + msg_rpc_trap = 22, + nxm_block = 24, + nxm_unblock = 25, + nxm_thread_destroy = 29, + lw_wire = 30, + lw_unwire = 31, + nxm_thread_create = 32, + nxm_task_init = 33, + nxm_idle = 35, + nxm_wakeup_idle = 36, + nxm_set_pthid = 37, + nxm_thread_kill = 38, + nxm_thread_block = 39, + nxm_thread_wakeup = 40, + init_process = 41, + nxm_get_binding = 42, + map_fd = 43, + nxm_resched = 44, + nxm_set_cancel = 45, + nxm_set_binding = 46, + stack_create = 47, + nxm_get_state = 48, + nxm_thread_suspend = 49, + nxm_thread_resume = 50, + nxm_signal_check = 51, + htg_unix_syscall = 52, + host_self = 55, + host_priv_self = 56, + swtch_pri = 59, + swtch = 60, + thread_switch = 61, + semop_fast = 62, + nxm_pshared_init = 63, + nxm_pshared_block = 64, + nxm_pshared_unblock = 65, + nxm_pshared_destroy = 66, + nxm_swtch_pri = 67, + lw_syscall = 68, + mach_sctimes_0 = 70, + mach_sctimes_1 = 71, + mach_sctimes_2 = 72, + mach_sctimes_3 = 73, + mach_sctimes_4 = 74, + mach_sctimes_5 = 75, + mach_sctimes_6 = 76, + mach_sctimes_7 = 77, + mach_sctimes_8 = 78, + mach_sctimes_9 = 79, + mach_sctimes_10 = 80, + mach_sctimes_11 = 81, + mach_sctimes_port_alloc_dealloc = 82, + MachNumber + }; + + static const int Number = StandardNumber + MachNumber; + + static const char *name(int num); + + static bool validSyscallNumber(int num) { + return -MachNumber < num && num < StandardNumber; + } + + static int convert(int syscall_num) { + if (!validSyscallNumber(syscall_num)) + return -1; + + return syscall_num < 0 ? StandardNumber - syscall_num : syscall_num; + } +}; + +#endif // __KERN_TRU64_TRU64_SYSCALLS_HH__ diff --git a/src/mem/bridge.cc b/src/mem/bridge.cc new file mode 100644 index 000000000..9c14e7ee2 --- /dev/null +++ b/src/mem/bridge.cc @@ -0,0 +1,262 @@ + +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Steve Reinhardt + */ + +/** + * @file + * Definition of a simple bus bridge without buffering. + */ + +#include <algorithm> + +#include "base/trace.hh" +#include "mem/bridge.hh" +#include "sim/builder.hh" + +Bridge::BridgePort::BridgePort(const std::string &_name, + Bridge *_bridge, BridgePort *_otherPort, + int _delay, int _queueLimit) + : Port(_name), bridge(_bridge), otherPort(_otherPort), + delay(_delay), outstandingResponses(0), + queueLimit(_queueLimit), sendEvent(this) +{ +} + +Bridge::Bridge(const std::string &n, int qsa, int qsb, + Tick _delay, int write_ack) + : MemObject(n), + portA(n + "-portA", this, &portB, _delay, qsa), + portB(n + "-portB", this, &portA, _delay, qsa), + ackWrites(write_ack) +{ +} + +Port * +Bridge::getPort(const std::string &if_name, int idx) +{ + BridgePort *port; + + if (if_name == "side_a") + port = &portA; + else if (if_name == "side_b") + port = &portB; + else + return NULL; + + if (port->getPeer() != NULL) + panic("bridge side %s already connected to.", if_name); + return port; +} + + +void +Bridge::init() +{ + // Make sure that both sides are connected to. + if (portA.getPeer() == NULL || portB.getPeer() == NULL) + panic("Both ports of bus bridge are not connected to a bus.\n"); +} + + +/** Function called by the port when the bus is receiving a Timing + * transaction.*/ +bool +Bridge::BridgePort::recvTiming(Packet *pkt) +{ + DPRINTF(BusBridge, "recvTiming: src %d dest %d addr 0x%x\n", + pkt->getSrc(), pkt->getDest(), pkt->getAddr()); + + return otherPort->queueForSendTiming(pkt); +} + + +bool +Bridge::BridgePort::queueForSendTiming(Packet *pkt) +{ + if (queueFull()) + return false; + + if (pkt->isResponse()) { + // This is a response for a request we forwarded earlier. The + // corresponding PacketBuffer should be stored in the packet's + // senderState field. + PacketBuffer *buf = dynamic_cast<PacketBuffer*>(pkt->senderState); + assert(buf != NULL); + // set up new packet dest & senderState based on values saved + // from original request + buf->fixResponse(pkt); + DPRINTF(BusBridge, "restoring sender state: %#X, from packet buffer: %#X\n", + pkt->senderState, buf); + DPRINTF(BusBridge, " is response, new dest %d\n", pkt->getDest()); + delete buf; + } + + Tick readyTime = curTick + delay; + PacketBuffer *buf = new PacketBuffer(pkt, readyTime); + DPRINTF(BusBridge, "old sender state: %#X, new sender state: %#X\n", + buf->origSenderState, buf); + + // If we're about to put this packet at the head of the queue, we + // need to schedule an event to do the transmit. Otherwise there + // should already be an event scheduled for sending the head + // packet. + if (sendQueue.empty()) { + sendEvent.schedule(readyTime); + } + + sendQueue.push_back(buf); + + return true; +} + +void +Bridge::BridgePort::trySend() +{ + assert(!sendQueue.empty()); + + bool was_full = queueFull(); + + PacketBuffer *buf = sendQueue.front(); + + assert(buf->ready <= curTick); + + Packet *pkt = buf->pkt; + + DPRINTF(BusBridge, "trySend: origSrc %d dest %d addr 0x%x\n", + buf->origSrc, pkt->getDest(), pkt->getAddr()); + + if (sendTiming(pkt)) { + // send successful + sendQueue.pop_front(); + buf->pkt = NULL; // we no longer own packet, so it's not safe to look at it + + if (buf->expectResponse) { + // Must wait for response. We just need to count outstanding + // responses (in case we want to cap them); PacketBuffer + // pointer will be recovered on response. + ++outstandingResponses; + DPRINTF(BusBridge, " successful: awaiting response (%d)\n", + outstandingResponses); + } else { + // no response expected... deallocate packet buffer now. + DPRINTF(BusBridge, " successful: no response expected\n"); + delete buf; + } + + // If there are more packets to send, schedule event to try again. + if (!sendQueue.empty()) { + buf = sendQueue.front(); + sendEvent.schedule(std::max(buf->ready, curTick + 1)); + } + // Let things start sending again + if (was_full) { + DPRINTF(BusBridge, "Queue was full, sending retry\n"); + otherPort->sendRetry(); + } + + } else { + DPRINTF(BusBridge, " unsuccessful\n"); + } +} + + +void +Bridge::BridgePort::recvRetry() +{ + trySend(); +} + +/** Function called by the port when the bus is receiving a Atomic + * transaction.*/ +Tick +Bridge::BridgePort::recvAtomic(Packet *pkt) +{ + return otherPort->sendAtomic(pkt) + delay; +} + +/** Function called by the port when the bus is receiving a Functional + * transaction.*/ +void +Bridge::BridgePort::recvFunctional(Packet *pkt) +{ + std::list<PacketBuffer*>::iterator i; + bool pktContinue = true; + + for (i = sendQueue.begin(); i != sendQueue.end(); ++i) { + if (pkt->intersect((*i)->pkt)) { + pktContinue &= fixPacket(pkt, (*i)->pkt); + } + } + + if (pktContinue) { + otherPort->sendFunctional(pkt); + } +} + +/** Function called by the port when the bus is receiving a status change.*/ +void +Bridge::BridgePort::recvStatusChange(Port::Status status) +{ + otherPort->sendStatusChange(status); +} + +void +Bridge::BridgePort::getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) +{ + otherPort->getPeerAddressRanges(resp, snoop); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(Bridge) + + Param<int> queue_size_a; + Param<int> queue_size_b; + Param<Tick> delay; + Param<bool> write_ack; + +END_DECLARE_SIM_OBJECT_PARAMS(Bridge) + +BEGIN_INIT_SIM_OBJECT_PARAMS(Bridge) + + INIT_PARAM(queue_size_a, "The size of the queue for data coming into side a"), + INIT_PARAM(queue_size_b, "The size of the queue for data coming into side b"), + INIT_PARAM(delay, "The miminum delay to cross this bridge"), + INIT_PARAM(write_ack, "Acknowledge any writes that are received.") + +END_INIT_SIM_OBJECT_PARAMS(Bridge) + +CREATE_SIM_OBJECT(Bridge) +{ + return new Bridge(getInstanceName(), queue_size_a, queue_size_b, delay, + write_ack); +} + +REGISTER_SIM_OBJECT("Bridge", Bridge) diff --git a/src/mem/bridge.hh b/src/mem/bridge.hh new file mode 100644 index 000000000..2ab9799c7 --- /dev/null +++ b/src/mem/bridge.hh @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Steve Reinhardt + */ + +/** + * @file + * Declaration of a simple bus bridge object with no buffering + */ + +#ifndef __MEM_BRIDGE_HH__ +#define __MEM_BRIDGE_HH__ + +#include <string> +#include <list> +#include <inttypes.h> +#include <queue> + +#include "mem/mem_object.hh" +#include "mem/packet.hh" +#include "mem/port.hh" +#include "sim/eventq.hh" + +class Bridge : public MemObject +{ + protected: + /** Declaration of the buses port type, one will be instantiated for each + of the interfaces connecting to the bus. */ + class BridgePort : public Port + { + /** A pointer to the bridge to which this port belongs. */ + Bridge *bridge; + + /** + * Pointer to the port on the other side of the bridge + * (connected to the other bus). + */ + BridgePort *otherPort; + + /** Minimum delay though this bridge. */ + Tick delay; + + class PacketBuffer : public Packet::SenderState { + + public: + Tick ready; + Packet *pkt; + Packet::SenderState *origSenderState; + short origSrc; + bool expectResponse; + + PacketBuffer(Packet *_pkt, Tick t) + : ready(t), pkt(_pkt), + origSenderState(_pkt->senderState), origSrc(_pkt->getSrc()), + expectResponse(_pkt->needsResponse()) + { + if (!pkt->isResponse()) + pkt->senderState = this; + } + + void fixResponse(Packet *pkt) + { + assert(pkt->senderState == this); + pkt->setDest(origSrc); + pkt->senderState = origSenderState; + } + }; + + /** + * Outbound packet queue. Packets are held in this queue for a + * specified delay to model the processing delay of the + * bridge. + */ + std::list<PacketBuffer*> sendQueue; + + int outstandingResponses; + + /** Max queue size for outbound packets */ + int queueLimit; + + /** + * Is this side blocked from accepting outbound packets? + */ + bool queueFull() { return (sendQueue.size() == queueLimit); } + + bool queueForSendTiming(Packet *pkt); + + void finishSend(PacketBuffer *buf); + + /** + * Handle send event, scheduled when the packet at the head of + * the outbound queue is ready to transmit (for timing + * accesses only). + */ + void trySend(); + + class SendEvent : public Event + { + BridgePort *port; + + public: + SendEvent(BridgePort *p) + : Event(&mainEventQueue), port(p) {} + + virtual void process() { port->trySend(); } + + virtual const char *description() { return "bridge send event"; } + }; + + SendEvent sendEvent; + + public: + + /** Constructor for the BusPort.*/ + BridgePort(const std::string &_name, + Bridge *_bridge, BridgePort *_otherPort, + int _delay, int _queueLimit); + + protected: + + /** When receiving a timing request from the peer port, + pass it to the bridge. */ + virtual bool recvTiming(Packet *pkt); + + /** When receiving a retry request from the peer port, + pass it to the bridge. */ + virtual void recvRetry(); + + /** When receiving a Atomic requestfrom the peer port, + pass it to the bridge. */ + virtual Tick recvAtomic(Packet *pkt); + + /** When receiving a Functional request from the peer port, + pass it to the bridge. */ + virtual void recvFunctional(Packet *pkt); + + /** When receiving a status changefrom the peer port, + pass it to the bridge. */ + virtual void recvStatusChange(Status status); + + /** When receiving a address range request the peer port, + pass it to the bridge. */ + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop); + }; + + BridgePort portA, portB; + + /** If this bridge should acknowledge writes. */ + bool ackWrites; + + public: + + /** A function used to return the port associated with this bus object. */ + virtual Port *getPort(const std::string &if_name, int idx = -1); + + virtual void init(); + + Bridge(const std::string &n, int qsa, int qsb, Tick _delay, int write_ack); +}; + +#endif //__MEM_BUS_HH__ diff --git a/src/mem/bus.cc b/src/mem/bus.cc new file mode 100644 index 000000000..cf9e54e62 --- /dev/null +++ b/src/mem/bus.cc @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** + * @file + * Definition of a bus object. + */ + + +#include "base/misc.hh" +#include "base/trace.hh" +#include "mem/bus.hh" +#include "sim/builder.hh" + +Port * +Bus::getPort(const std::string &if_name, int idx) +{ + if (if_name == "default") + if (defaultPort == NULL) { + defaultPort = new BusPort(csprintf("%s-default",name()), this, + defaultId); + return defaultPort; + } else + fatal("Default port already set\n"); + + // if_name ignored? forced to be empty? + int id = interfaces.size(); + BusPort *bp = new BusPort(csprintf("%s-p%d", name(), id), this, id); + interfaces.push_back(bp); + return bp; +} + +/** Get the ranges of anyone other buses that we are connected to. */ +void +Bus::init() +{ + std::vector<Port*>::iterator intIter; + + for (intIter = interfaces.begin(); intIter != interfaces.end(); intIter++) + (*intIter)->sendStatusChange(Port::RangeChange); +} + + +/** Function called by the port when the bus is receiving a Timing + * transaction.*/ +bool +Bus::recvTiming(Packet *pkt) +{ + Port *port; + DPRINTF(Bus, "recvTiming: packet src %d dest %d addr 0x%x cmd %s\n", + pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); + + short dest = pkt->getDest(); + if (dest == Packet::Broadcast) { + if ( timingSnoopPhase1(pkt) ) + { + timingSnoopPhase2(pkt); + port = findPort(pkt->getAddr(), pkt->getSrc()); + } + else + { + //Snoop didn't succeed + retryList.push_back(interfaces[pkt->getSrc()]); + return false; + } + } else { + assert(dest >= 0 && dest < interfaces.size()); + assert(dest != pkt->getSrc()); // catch infinite loops + port = interfaces[dest]; + } + if (port->sendTiming(pkt)) { + // packet was successfully sent, just return true. + return true; + } + + // packet not successfully sent + retryList.push_back(interfaces[pkt->getSrc()]); + return false; +} + +void +Bus::recvRetry(int id) +{ + // Go through all the elements on the list calling sendRetry on each + // This is not very efficient at all but it works. Ultimately we should end + // up with something that is more intelligent. + int initialSize = retryList.size(); + int i; + Port *p; + + for (i = 0; i < initialSize; i++) { + assert(retryList.size() > 0); + p = retryList.front(); + retryList.pop_front(); + p->sendRetry(); + } +} + + +Port * +Bus::findPort(Addr addr, int id) +{ + /* An interval tree would be a better way to do this. --ali. */ + int dest_id = -1; + int i = 0; + bool found = false; + AddrRangeIter iter; + + while (i < portList.size() && !found) + { + if (portList[i].range == addr) { + dest_id = portList[i].portId; + found = true; + DPRINTF(Bus, " found addr %#llx on device %d\n", addr, dest_id); + } + i++; + } + + // Check if this matches the default range + if (dest_id == -1) { + for (iter = defaultRange.begin(); iter != defaultRange.end(); iter++) { + if (*iter == addr) { + DPRINTF(Bus, " found addr %#llx on default\n", addr); + return defaultPort; + } + } + panic("Unable to find destination for addr: %#llx", addr); + } + + + // we shouldn't be sending this back to where it came from + assert(dest_id != id); + + return interfaces[dest_id]; +} + +std::vector<int> +Bus::findSnoopPorts(Addr addr, int id) +{ + int i = 0; + AddrRangeIter iter; + std::vector<int> ports; + + while (i < portSnoopList.size()) + { + if (portSnoopList[i].range == addr && portSnoopList[i].portId != id) { + //Careful to not overlap ranges + //or snoop will be called more than once on the port + ports.push_back(portSnoopList[i].portId); + DPRINTF(Bus, " found snoop addr %#llx on device%d\n", addr, + portSnoopList[i].portId); + } + i++; + } + return ports; +} + +void +Bus::atomicSnoop(Packet *pkt) +{ + std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc()); + + while (!ports.empty()) + { + interfaces[ports.back()]->sendAtomic(pkt); + ports.pop_back(); + } +} + +bool +Bus::timingSnoopPhase1(Packet *pkt) +{ + std::vector<int> ports = findSnoopPorts(pkt->getAddr(), pkt->getSrc()); + bool success = true; + + while (!ports.empty() && success) + { + snoopCallbacks.push_back(ports.back()); + success = interfaces[ports.back()]->sendTiming(pkt); + ports.pop_back(); + } + if (!success) + { + while (!snoopCallbacks.empty()) + { + interfaces[snoopCallbacks.back()]->sendStatusChange(Port::SnoopSquash); + snoopCallbacks.pop_back(); + } + return false; + } + return true; +} + +void +Bus::timingSnoopPhase2(Packet *pkt) +{ + bool success; + pkt->flags |= SNOOP_COMMIT; + while (!snoopCallbacks.empty()) + { + success = interfaces[snoopCallbacks.back()]->sendTiming(pkt); + //We should not fail on snoop callbacks + assert(success); + snoopCallbacks.pop_back(); + } +} + +/** Function called by the port when the bus is receiving a Atomic + * transaction.*/ +Tick +Bus::recvAtomic(Packet *pkt) +{ + DPRINTF(Bus, "recvAtomic: packet src %d dest %d addr 0x%x cmd %s\n", + pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); + assert(pkt->getDest() == Packet::Broadcast); + atomicSnoop(pkt); + return findPort(pkt->getAddr(), pkt->getSrc())->sendAtomic(pkt); +} + +/** Function called by the port when the bus is receiving a Functional + * transaction.*/ +void +Bus::recvFunctional(Packet *pkt) +{ + DPRINTF(Bus, "recvFunctional: packet src %d dest %d addr 0x%x cmd %s\n", + pkt->getSrc(), pkt->getDest(), pkt->getAddr(), pkt->cmdString()); + assert(pkt->getDest() == Packet::Broadcast); + findPort(pkt->getAddr(), pkt->getSrc())->sendFunctional(pkt); +} + +/** Function called by the port when the bus is receiving a status change.*/ +void +Bus::recvStatusChange(Port::Status status, int id) +{ + AddrRangeList ranges; + AddrRangeList snoops; + int x; + AddrRangeIter iter; + + assert(status == Port::RangeChange && + "The other statuses need to be implemented."); + + DPRINTF(BusAddrRanges, "received RangeChange from device id %d\n", id); + + if (id == defaultId) { + defaultRange.clear(); + defaultPort->getPeerAddressRanges(ranges, snoops); + assert(snoops.size() == 0); + for(iter = ranges.begin(); iter != ranges.end(); iter++) { + defaultRange.push_back(*iter); + DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for default range\n", + iter->start, iter->end); + } + } else { + + assert((id < interfaces.size() && id >= 0) || id == -1); + Port *port = interfaces[id]; + std::vector<DevMap>::iterator portIter; + std::vector<DevMap>::iterator snoopIter; + + // Clean out any previously existent ids + for (portIter = portList.begin(); portIter != portList.end(); ) { + if (portIter->portId == id) + portIter = portList.erase(portIter); + else + portIter++; + } + + for (snoopIter = portSnoopList.begin(); snoopIter != portSnoopList.end(); ) { + if (snoopIter->portId == id) + snoopIter = portSnoopList.erase(snoopIter); + else + snoopIter++; + } + + port->getPeerAddressRanges(ranges, snoops); + + for(iter = snoops.begin(); iter != snoops.end(); iter++) { + DevMap dm; + dm.portId = id; + dm.range = *iter; + + DPRINTF(BusAddrRanges, "Adding snoop range %#llx - %#llx for id %d\n", + dm.range.start, dm.range.end, id); + portSnoopList.push_back(dm); + } + + for(iter = ranges.begin(); iter != ranges.end(); iter++) { + DevMap dm; + dm.portId = id; + dm.range = *iter; + + DPRINTF(BusAddrRanges, "Adding range %#llx - %#llx for id %d\n", + dm.range.start, dm.range.end, id); + portList.push_back(dm); + } + } + DPRINTF(MMU, "port list has %d entries\n", portList.size()); + + // tell all our peers that our address range has changed. + // Don't tell the device that caused this change, it already knows + for (x = 0; x < interfaces.size(); x++) + if (x != id) + interfaces[x]->sendStatusChange(Port::RangeChange); + + if (id != defaultId && defaultPort) + defaultPort->sendStatusChange(Port::RangeChange); +} + +void +Bus::addressRanges(AddrRangeList &resp, AddrRangeList &snoop, int id) +{ + std::vector<DevMap>::iterator portIter; + AddrRangeIter dflt_iter; + bool subset; + + resp.clear(); + snoop.clear(); + + DPRINTF(BusAddrRanges, "received address range request, returning:\n"); + + for (dflt_iter = defaultRange.begin(); dflt_iter != defaultRange.end(); + dflt_iter++) { + resp.push_back(*dflt_iter); + DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n",dflt_iter->start, + dflt_iter->end); + } + for (portIter = portList.begin(); portIter != portList.end(); portIter++) { + subset = false; + for (dflt_iter = defaultRange.begin(); dflt_iter != defaultRange.end(); + dflt_iter++) { + if ((portIter->range.start < dflt_iter->start && + portIter->range.end >= dflt_iter->start) || + (portIter->range.start < dflt_iter->end && + portIter->range.end >= dflt_iter->end)) + fatal("Devices can not set ranges that itersect the default set\ + but are not a subset of the default set.\n"); + if (portIter->range.start >= dflt_iter->start && + portIter->range.end <= dflt_iter->end) { + subset = true; + DPRINTF(BusAddrRanges, " -- %#llx : %#llx is a SUBSET\n", + portIter->range.start, portIter->range.end); + } + } + if (portIter->portId != id && !subset) { + resp.push_back(portIter->range); + DPRINTF(BusAddrRanges, " -- %#llx : %#llx\n", + portIter->range.start, portIter->range.end); + } + } +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(Bus) + + Param<int> bus_id; + +END_DECLARE_SIM_OBJECT_PARAMS(Bus) + +BEGIN_INIT_SIM_OBJECT_PARAMS(Bus) + INIT_PARAM(bus_id, "a globally unique bus id") +END_INIT_SIM_OBJECT_PARAMS(Bus) + +CREATE_SIM_OBJECT(Bus) +{ + return new Bus(getInstanceName(), bus_id); +} + +REGISTER_SIM_OBJECT("Bus", Bus) diff --git a/src/mem/bus.hh b/src/mem/bus.hh new file mode 100644 index 000000000..941389296 --- /dev/null +++ b/src/mem/bus.hh @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + * Ali Saidi + */ + +/** + * @file + * Declaration of a bus object. + */ + +#ifndef __MEM_BUS_HH__ +#define __MEM_BUS_HH__ + +#include <string> +#include <list> +#include <inttypes.h> + +#include "base/range.hh" +#include "mem/mem_object.hh" +#include "mem/packet.hh" +#include "mem/port.hh" +#include "mem/request.hh" + +class Bus : public MemObject +{ + /** a globally unique id for this bus. */ + int busId; + + static const int defaultId = -1; + + struct DevMap { + int portId; + Range<Addr> range; + }; + std::vector<DevMap> portList; + AddrRangeList defaultRange; + std::vector<DevMap> portSnoopList; + + std::vector<int> snoopCallbacks; + + + /** Function called by the port when the bus is recieving a Timing + transaction.*/ + bool recvTiming(Packet *pkt); + + /** Function called by the port when the bus is recieving a Atomic + transaction.*/ + Tick recvAtomic(Packet *pkt); + + /** Function called by the port when the bus is recieving a Functional + transaction.*/ + void recvFunctional(Packet *pkt); + + /** Timing function called by port when it is once again able to process + * requests. */ + void recvRetry(int id); + + /** Function called by the port when the bus is recieving a status change.*/ + void recvStatusChange(Port::Status status, int id); + + /** Find which port connected to this bus (if any) should be given a packet + * with this address. + * @param addr Address to find port for. + * @param id Id of the port this packet was received from (to prevent + * loops) + * @return pointer to port that the packet should be sent out of. + */ + Port *findPort(Addr addr, int id); + + /** Find all ports with a matching snoop range, except src port. Keep in mind + * that the ranges shouldn't overlap or you will get a double snoop to the same + * interface.and the cache will assert out. + * @param addr Address to find snoop prts for. + * @param id Id of the src port of the request to avoid calling snoop on src + * @return vector of IDs to snoop on + */ + std::vector<int> findSnoopPorts(Addr addr, int id); + + /** Snoop all relevant ports atomicly. */ + void atomicSnoop(Packet *pkt); + + /** Snoop for NACK and Blocked in phase 1 + * @return True if succeds. + */ + bool timingSnoopPhase1(Packet *pkt); + + /** @todo Don't need to commit all snoops just those that need it + *(register somehow). */ + /** Commit all snoops now that we know if any of them would have blocked. + */ + void timingSnoopPhase2(Packet *pkt); + + /** Process address range request. + * @param resp addresses that we can respond to + * @param snoop addresses that we would like to snoop + * @param id ide of the busport that made the request. + */ + void addressRanges(AddrRangeList &resp, AddrRangeList &snoop, int id); + + + /** Declaration of the buses port type, one will be instantiated for each + of the interfaces connecting to the bus. */ + class BusPort : public Port + { + /** A pointer to the bus to which this port belongs. */ + Bus *bus; + + /** A id to keep track of the intercafe ID this port is connected to. */ + int id; + + public: + + /** Constructor for the BusPort.*/ + BusPort(const std::string &_name, Bus *_bus, int _id) + : Port(_name), bus(_bus), id(_id) + { } + + protected: + + /** When reciving a timing request from the peer port (at id), + pass it to the bus. */ + virtual bool recvTiming(Packet *pkt) + { pkt->setSrc(id); return bus->recvTiming(pkt); } + + /** When reciving a Atomic requestfrom the peer port (at id), + pass it to the bus. */ + virtual Tick recvAtomic(Packet *pkt) + { pkt->setSrc(id); return bus->recvAtomic(pkt); } + + /** When reciving a Functional requestfrom the peer port (at id), + pass it to the bus. */ + virtual void recvFunctional(Packet *pkt) + { pkt->setSrc(id); bus->recvFunctional(pkt); } + + /** When reciving a status changefrom the peer port (at id), + pass it to the bus. */ + virtual void recvStatusChange(Status status) + { bus->recvStatusChange(status, id); } + + /** When reciving a retry from the peer port (at id), + pass it to the bus. */ + virtual void recvRetry() + { bus->recvRetry(id); } + + // This should return all the 'owned' addresses that are + // downstream from this bus, yes? That is, the union of all + // the 'owned' address ranges of all the other interfaces on + // this bus... + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { bus->addressRanges(resp, snoop, id); } + + // Hack to make translating port work without changes + virtual int deviceBlockSize() { return 32; } + + }; + + /** An array of pointers to the peer port interfaces + connected to this bus.*/ + std::vector<Port*> interfaces; + + /** An array of pointers to ports that retry should be called on because the + * original send failed for whatever reason.*/ + std::list<Port*> retryList; + + /** Port that handles requests that don't match any of the interfaces.*/ + Port *defaultPort; + + public: + + /** A function used to return the port associated with this bus object. */ + virtual Port *getPort(const std::string &if_name, int idx = -1); + + virtual void init(); + + Bus(const std::string &n, int bus_id) + : MemObject(n), busId(bus_id), defaultPort(NULL) {} + +}; + +#endif //__MEM_BUS_HH__ diff --git a/src/mem/cache/base_cache.cc b/src/mem/cache/base_cache.cc new file mode 100644 index 000000000..a172847df --- /dev/null +++ b/src/mem/cache/base_cache.cc @@ -0,0 +1,466 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definition of BaseCache functions. + */ + +#include "mem/cache/base_cache.hh" +#include "cpu/smt.hh" +#include "cpu/base.hh" + +using namespace std; + +BaseCache::CachePort::CachePort(const std::string &_name, BaseCache *_cache, + bool _isCpuSide) + : Port(_name), cache(_cache), isCpuSide(_isCpuSide) +{ + blocked = false; + //Start ports at null if more than one is created we should panic + //cpuSidePort = NULL; + //memSidePort = NULL; +} + +void +BaseCache::CachePort::recvStatusChange(Port::Status status) +{ + cache->recvStatusChange(status, isCpuSide); +} + +void +BaseCache::CachePort::getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) +{ + cache->getAddressRanges(resp, snoop, isCpuSide); +} + +int +BaseCache::CachePort::deviceBlockSize() +{ + return cache->getBlockSize(); +} + +bool +BaseCache::CachePort::recvTiming(Packet *pkt) +{ + if (blocked) + { + DPRINTF(Cache,"Scheduling a retry while blocked\n"); + mustSendRetry = true; + return false; + } + return cache->doTimingAccess(pkt, this, isCpuSide); +} + +Tick +BaseCache::CachePort::recvAtomic(Packet *pkt) +{ + return cache->doAtomicAccess(pkt, isCpuSide); +} + +void +BaseCache::CachePort::recvFunctional(Packet *pkt) +{ + cache->doFunctionalAccess(pkt, isCpuSide); +} + +void +BaseCache::CachePort::recvRetry() +{ + Packet *pkt; + + if (!isCpuSide) + { + pkt = cache->getPacket(); + bool success = sendTiming(pkt); + DPRINTF(Cache, "Address %x was %s in sending the timing request\n", + pkt->getAddr(), success ? "succesful" : "unsuccesful"); + cache->sendResult(pkt, success); + if (success && cache->doMasterRequest()) + { + //Still more to issue, rerequest in 1 cycle + pkt = NULL; + BaseCache::CacheEvent * reqCpu = new BaseCache::CacheEvent(this); + reqCpu->schedule(curTick + 1); + } + } + else + { + pkt = cache->getCoherencePacket(); + bool success = sendTiming(pkt); + if (success && cache->doSlaveRequest()) + { + //Still more to issue, rerequest in 1 cycle + pkt = NULL; + BaseCache::CacheEvent * reqCpu = new BaseCache::CacheEvent(this); + reqCpu->schedule(curTick + 1); + } + + } + return; +} +void +BaseCache::CachePort::setBlocked() +{ + assert(!blocked); + DPRINTF(Cache, "Cache Blocking\n"); + blocked = true; + //Clear the retry flag + mustSendRetry = false; +} + +void +BaseCache::CachePort::clearBlocked() +{ + assert(blocked); + DPRINTF(Cache, "Cache Unblocking\n"); + blocked = false; + if (mustSendRetry) + { + DPRINTF(Cache, "Cache Sending Retry\n"); + mustSendRetry = false; + sendRetry(); + } +} + +BaseCache::CacheEvent::CacheEvent(CachePort *_cachePort) + : Event(&mainEventQueue, CPU_Tick_Pri), cachePort(_cachePort) +{ + this->setFlags(AutoDelete); + pkt = NULL; +} + +BaseCache::CacheEvent::CacheEvent(CachePort *_cachePort, Packet *_pkt) + : Event(&mainEventQueue, CPU_Tick_Pri), cachePort(_cachePort), pkt(_pkt) +{ + this->setFlags(AutoDelete); +} + +void +BaseCache::CacheEvent::process() +{ + if (!pkt) + { + if (!cachePort->isCpuSide) + { + //MSHR + pkt = cachePort->cache->getPacket(); + bool success = cachePort->sendTiming(pkt); + DPRINTF(Cache, "Address %x was %s in sending the timing request\n", + pkt->getAddr(), success ? "succesful" : "unsuccesful"); + cachePort->cache->sendResult(pkt, success); + if (success && cachePort->cache->doMasterRequest()) + { + //Still more to issue, rerequest in 1 cycle + pkt = NULL; + this->schedule(curTick+1); + } + } + else + { + //CSHR + pkt = cachePort->cache->getCoherencePacket(); + bool success = cachePort->sendTiming(pkt); + if (success && cachePort->cache->doSlaveRequest()) + { + //Still more to issue, rerequest in 1 cycle + pkt = NULL; + this->schedule(curTick+1); + } + } + return; + } + //Response + //Know the packet to send, no need to mark in service (must succed) + assert(cachePort->sendTiming(pkt)); +} + +const char * +BaseCache::CacheEvent::description() +{ + return "timing event\n"; +} + +Port* +BaseCache::getPort(const std::string &if_name, int idx) +{ + if (if_name == "") + { + if(cpuSidePort == NULL) + cpuSidePort = new CachePort(name() + "-cpu_side_port", this, true); + return cpuSidePort; + } + else if (if_name == "functional") + { + if(cpuSidePort == NULL) + cpuSidePort = new CachePort(name() + "-cpu_side_port", this, true); + return cpuSidePort; + } + else if (if_name == "cpu_side") + { + if(cpuSidePort == NULL) + cpuSidePort = new CachePort(name() + "-cpu_side_port", this, true); + return cpuSidePort; + } + else if (if_name == "mem_side") + { + if (memSidePort != NULL) + panic("Already have a mem side for this cache\n"); + memSidePort = new CachePort(name() + "-mem_side_port", this, false); + return memSidePort; + } + else panic("Port name %s unrecognized\n", if_name); +} + +void +BaseCache::init() +{ + if (!cpuSidePort || !memSidePort) + panic("Cache not hooked up on both sides\n"); + cpuSidePort->sendStatusChange(Port::RangeChange); +} + +void +BaseCache::regStats() +{ + Request temp_req((Addr) NULL, 4, 0); + Packet::Command temp_cmd = Packet::ReadReq; + Packet temp_pkt(&temp_req, temp_cmd, 0); //@todo FIx command strings so this isn't neccessary + temp_pkt.allocate(); //Temp allocate, all need data + + using namespace Stats; + + // Hit statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + hits[access_idx] + .init(maxThreadsPerCPU) + .name(name() + "." + cstr + "_hits") + .desc("number of " + cstr + " hits") + .flags(total | nozero | nonan) + ; + } + + demandHits + .name(name() + ".demand_hits") + .desc("number of demand (read+write) hits") + .flags(total) + ; + demandHits = hits[Packet::ReadReq] + hits[Packet::WriteReq]; + + overallHits + .name(name() + ".overall_hits") + .desc("number of overall hits") + .flags(total) + ; + overallHits = demandHits + hits[Packet::SoftPFReq] + hits[Packet::HardPFReq] + + hits[Packet::Writeback]; + + // Miss statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + misses[access_idx] + .init(maxThreadsPerCPU) + .name(name() + "." + cstr + "_misses") + .desc("number of " + cstr + " misses") + .flags(total | nozero | nonan) + ; + } + + demandMisses + .name(name() + ".demand_misses") + .desc("number of demand (read+write) misses") + .flags(total) + ; + demandMisses = misses[Packet::ReadReq] + misses[Packet::WriteReq]; + + overallMisses + .name(name() + ".overall_misses") + .desc("number of overall misses") + .flags(total) + ; + overallMisses = demandMisses + misses[Packet::SoftPFReq] + + misses[Packet::HardPFReq] + misses[Packet::Writeback]; + + // Miss latency statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + missLatency[access_idx] + .init(maxThreadsPerCPU) + .name(name() + "." + cstr + "_miss_latency") + .desc("number of " + cstr + " miss cycles") + .flags(total | nozero | nonan) + ; + } + + demandMissLatency + .name(name() + ".demand_miss_latency") + .desc("number of demand (read+write) miss cycles") + .flags(total) + ; + demandMissLatency = missLatency[Packet::ReadReq] + missLatency[Packet::WriteReq]; + + overallMissLatency + .name(name() + ".overall_miss_latency") + .desc("number of overall miss cycles") + .flags(total) + ; + overallMissLatency = demandMissLatency + missLatency[Packet::SoftPFReq] + + missLatency[Packet::HardPFReq]; + + // access formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + accesses[access_idx] + .name(name() + "." + cstr + "_accesses") + .desc("number of " + cstr + " accesses(hits+misses)") + .flags(total | nozero | nonan) + ; + + accesses[access_idx] = hits[access_idx] + misses[access_idx]; + } + + demandAccesses + .name(name() + ".demand_accesses") + .desc("number of demand (read+write) accesses") + .flags(total) + ; + demandAccesses = demandHits + demandMisses; + + overallAccesses + .name(name() + ".overall_accesses") + .desc("number of overall (read+write) accesses") + .flags(total) + ; + overallAccesses = overallHits + overallMisses; + + // miss rate formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + missRate[access_idx] + .name(name() + "." + cstr + "_miss_rate") + .desc("miss rate for " + cstr + " accesses") + .flags(total | nozero | nonan) + ; + + missRate[access_idx] = misses[access_idx] / accesses[access_idx]; + } + + demandMissRate + .name(name() + ".demand_miss_rate") + .desc("miss rate for demand accesses") + .flags(total) + ; + demandMissRate = demandMisses / demandAccesses; + + overallMissRate + .name(name() + ".overall_miss_rate") + .desc("miss rate for overall accesses") + .flags(total) + ; + overallMissRate = overallMisses / overallAccesses; + + // miss latency formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + avgMissLatency[access_idx] + .name(name() + "." + cstr + "_avg_miss_latency") + .desc("average " + cstr + " miss latency") + .flags(total | nozero | nonan) + ; + + avgMissLatency[access_idx] = + missLatency[access_idx] / misses[access_idx]; + } + + demandAvgMissLatency + .name(name() + ".demand_avg_miss_latency") + .desc("average overall miss latency") + .flags(total) + ; + demandAvgMissLatency = demandMissLatency / demandMisses; + + overallAvgMissLatency + .name(name() + ".overall_avg_miss_latency") + .desc("average overall miss latency") + .flags(total) + ; + overallAvgMissLatency = overallMissLatency / overallMisses; + + blocked_cycles.init(NUM_BLOCKED_CAUSES); + blocked_cycles + .name(name() + ".blocked_cycles") + .desc("number of cycles access was blocked") + .subname(Blocked_NoMSHRs, "no_mshrs") + .subname(Blocked_NoTargets, "no_targets") + ; + + + blocked_causes.init(NUM_BLOCKED_CAUSES); + blocked_causes + .name(name() + ".blocked") + .desc("number of cycles access was blocked") + .subname(Blocked_NoMSHRs, "no_mshrs") + .subname(Blocked_NoTargets, "no_targets") + ; + + avg_blocked + .name(name() + ".avg_blocked_cycles") + .desc("average number of cycles each access was blocked") + .subname(Blocked_NoMSHRs, "no_mshrs") + .subname(Blocked_NoTargets, "no_targets") + ; + + avg_blocked = blocked_cycles / blocked_causes; + + fastWrites + .name(name() + ".fast_writes") + .desc("number of fast writes performed") + ; + + cacheCopies + .name(name() + ".cache_copies") + .desc("number of cache copies performed") + ; + +} diff --git a/src/mem/cache/base_cache.hh b/src/mem/cache/base_cache.hh new file mode 100644 index 000000000..069dbab58 --- /dev/null +++ b/src/mem/cache/base_cache.hh @@ -0,0 +1,559 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declares a basic cache interface BaseCache. + */ + +#ifndef __BASE_CACHE_HH__ +#define __BASE_CACHE_HH__ + +#include <vector> +#include <string> +#include <list> +#include <inttypes.h> + +#include "base/misc.hh" +#include "base/statistics.hh" +#include "base/trace.hh" +#include "mem/mem_object.hh" +#include "mem/packet.hh" +#include "mem/port.hh" +#include "mem/request.hh" +#include "sim/eventq.hh" + +/** + * Reasons for Caches to be Blocked. + */ +enum BlockedCause{ + Blocked_NoMSHRs, + Blocked_NoTargets, + Blocked_NoWBBuffers, + Blocked_Coherence, + Blocked_Copy, + NUM_BLOCKED_CAUSES +}; + +/** + * Reasons for cache to request a bus. + */ +enum RequestCause{ + Request_MSHR, + Request_WB, + Request_Coherence, + Request_PF +}; + +/** + * A basic cache interface. Implements some common functions for speed. + */ +class BaseCache : public MemObject +{ + class CachePort : public Port + { + public: + BaseCache *cache; + + CachePort(const std::string &_name, BaseCache *_cache, bool _isCpuSide); + + protected: + virtual bool recvTiming(Packet *pkt); + + virtual Tick recvAtomic(Packet *pkt); + + virtual void recvFunctional(Packet *pkt); + + virtual void recvStatusChange(Status status); + + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop); + + virtual int deviceBlockSize(); + + virtual void recvRetry(); + + public: + void setBlocked(); + + void clearBlocked(); + + bool blocked; + + bool mustSendRetry; + + bool isCpuSide; + }; + + struct CacheEvent : public Event + { + CachePort *cachePort; + Packet *pkt; + + CacheEvent(CachePort *_cachePort); + CacheEvent(CachePort *_cachePort, Packet *_pkt); + void process(); + const char *description(); + }; + + protected: + CachePort *cpuSidePort; + CachePort *memSidePort; + + public: + virtual Port *getPort(const std::string &if_name, int idx = -1); + + private: + //To be defined in cache_impl.hh not in base class + virtual bool doTimingAccess(Packet *pkt, CachePort *cachePort, bool isCpuSide) + { + fatal("No implementation"); + } + + virtual Tick doAtomicAccess(Packet *pkt, bool isCpuSide) + { + fatal("No implementation"); + } + + virtual void doFunctionalAccess(Packet *pkt, bool isCpuSide) + { + fatal("No implementation"); + } + + void recvStatusChange(Port::Status status, bool isCpuSide) + { + if (status == Port::RangeChange) + { + if (!isCpuSide) + { + cpuSidePort->sendStatusChange(Port::RangeChange); + } + else + { + memSidePort->sendStatusChange(Port::RangeChange); + } + } + } + + virtual Packet *getPacket() + { + fatal("No implementation"); + } + + virtual Packet *getCoherencePacket() + { + fatal("No implementation"); + } + + virtual void sendResult(Packet* &pkt, bool success) + { + + fatal("No implementation"); + } + + /** + * Bit vector of the blocking reasons for the access path. + * @sa #BlockedCause + */ + uint8_t blocked; + + /** + * Bit vector for the blocking reasons for the snoop path. + * @sa #BlockedCause + */ + uint8_t blockedSnoop; + + /** + * Bit vector for the outstanding requests for the master interface. + */ + uint8_t masterRequests; + + /** + * Bit vector for the outstanding requests for the slave interface. + */ + uint8_t slaveRequests; + + protected: + + /** True if this cache is connected to the CPU. */ + bool topLevelCache; + + /** Stores time the cache blocked for statistics. */ + Tick blockedCycle; + + /** Block size of this cache */ + const int blkSize; + + /** The number of misses to trigger an exit event. */ + Counter missCount; + + public: + // Statistics + /** + * @addtogroup CacheStatistics + * @{ + */ + + /** Number of hits per thread for each type of command. @sa Packet::Command */ + Stats::Vector<> hits[NUM_MEM_CMDS]; + /** Number of hits for demand accesses. */ + Stats::Formula demandHits; + /** Number of hit for all accesses. */ + Stats::Formula overallHits; + + /** Number of misses per thread for each type of command. @sa Packet::Command */ + Stats::Vector<> misses[NUM_MEM_CMDS]; + /** Number of misses for demand accesses. */ + Stats::Formula demandMisses; + /** Number of misses for all accesses. */ + Stats::Formula overallMisses; + + /** + * Total number of cycles per thread/command spent waiting for a miss. + * Used to calculate the average miss latency. + */ + Stats::Vector<> missLatency[NUM_MEM_CMDS]; + /** Total number of cycles spent waiting for demand misses. */ + Stats::Formula demandMissLatency; + /** Total number of cycles spent waiting for all misses. */ + Stats::Formula overallMissLatency; + + /** The number of accesses per command and thread. */ + Stats::Formula accesses[NUM_MEM_CMDS]; + /** The number of demand accesses. */ + Stats::Formula demandAccesses; + /** The number of overall accesses. */ + Stats::Formula overallAccesses; + + /** The miss rate per command and thread. */ + Stats::Formula missRate[NUM_MEM_CMDS]; + /** The miss rate of all demand accesses. */ + Stats::Formula demandMissRate; + /** The miss rate for all accesses. */ + Stats::Formula overallMissRate; + + /** The average miss latency per command and thread. */ + Stats::Formula avgMissLatency[NUM_MEM_CMDS]; + /** The average miss latency for demand misses. */ + Stats::Formula demandAvgMissLatency; + /** The average miss latency for all misses. */ + Stats::Formula overallAvgMissLatency; + + /** The total number of cycles blocked for each blocked cause. */ + Stats::Vector<> blocked_cycles; + /** The number of times this cache blocked for each blocked cause. */ + Stats::Vector<> blocked_causes; + + /** The average number of cycles blocked for each blocked cause. */ + Stats::Formula avg_blocked; + + /** The number of fast writes (WH64) performed. */ + Stats::Scalar<> fastWrites; + + /** The number of cache copies performed. */ + Stats::Scalar<> cacheCopies; + + /** + * @} + */ + + /** + * Register stats for this object. + */ + virtual void regStats(); + + public: + + class Params + { + public: + /** List of address ranges of this cache. */ + std::vector<Range<Addr> > addrRange; + /** The hit latency for this cache. */ + int hitLatency; + /** The block size of this cache. */ + int blkSize; + /** + * The maximum number of misses this cache should handle before + * ending the simulation. + */ + Counter maxMisses; + + /** + * Construct an instance of this parameter class. + */ + Params(std::vector<Range<Addr> > addr_range, + int hit_latency, int _blkSize, Counter max_misses) + : addrRange(addr_range), hitLatency(hit_latency), blkSize(_blkSize), + maxMisses(max_misses) + { + } + }; + + /** + * Create and initialize a basic cache object. + * @param name The name of this cache. + * @param hier_params Pointer to the HierParams object for this hierarchy + * of this cache. + * @param params The parameter object for this BaseCache. + */ + BaseCache(const std::string &name, Params ¶ms) + : MemObject(name), blocked(0), blockedSnoop(0), masterRequests(0), + slaveRequests(0), topLevelCache(false), blkSize(params.blkSize), + missCount(params.maxMisses) + { + //Start ports at null if more than one is created we should panic + cpuSidePort = NULL; + memSidePort = NULL; + } + + virtual void init(); + + /** + * Query block size of a cache. + * @return The block size + */ + int getBlockSize() const + { + return blkSize; + } + + /** + * Returns true if this cache is connect to the CPU. + * @return True if this is a L1 cache. + */ + bool isTopLevel() + { + return topLevelCache; + } + + /** + * Returns true if the cache is blocked for accesses. + */ + bool isBlocked() + { + return blocked != 0; + } + + /** + * Returns true if the cache is blocked for snoops. + */ + bool isBlockedForSnoop() + { + return blockedSnoop != 0; + } + + /** + * Marks the access path of the cache as blocked for the given cause. This + * also sets the blocked flag in the slave interface. + * @param cause The reason for the cache blocking. + */ + void setBlocked(BlockedCause cause) + { + uint8_t flag = 1 << cause; + if (blocked == 0) { + blocked_causes[cause]++; + blockedCycle = curTick; + } + blocked |= flag; + DPRINTF(Cache,"Blocking for cause %s\n", cause); + cpuSidePort->setBlocked(); + } + + /** + * Marks the snoop path of the cache as blocked for the given cause. This + * also sets the blocked flag in the master interface. + * @param cause The reason to block the snoop path. + */ + void setBlockedForSnoop(BlockedCause cause) + { + uint8_t flag = 1 << cause; + blockedSnoop |= flag; + memSidePort->setBlocked(); + } + + /** + * Marks the cache as unblocked for the given cause. This also clears the + * blocked flags in the appropriate interfaces. + * @param cause The newly unblocked cause. + * @warning Calling this function can cause a blocked request on the bus to + * access the cache. The cache must be in a state to handle that request. + */ + void clearBlocked(BlockedCause cause) + { + uint8_t flag = 1 << cause; + DPRINTF(Cache,"Unblocking for cause %s, causes left=%i\n", + cause, blocked); + if (blocked & flag) + { + blocked &= ~flag; + if (!isBlocked()) { + blocked_cycles[cause] += curTick - blockedCycle; + DPRINTF(Cache,"Unblocking from all causes\n"); + cpuSidePort->clearBlocked(); + } + } + if (blockedSnoop & flag) + { + blockedSnoop &= ~flag; + if (!isBlockedForSnoop()) { + memSidePort->clearBlocked(); + } + } + } + + /** + * True if the master bus should be requested. + * @return True if there are outstanding requests for the master bus. + */ + bool doMasterRequest() + { + return masterRequests != 0; + } + + /** + * Request the master bus for the given cause and time. + * @param cause The reason for the request. + * @param time The time to make the request. + */ + void setMasterRequest(RequestCause cause, Tick time) + { + if (!doMasterRequest()) + { + BaseCache::CacheEvent * reqCpu = new BaseCache::CacheEvent(memSidePort); + reqCpu->schedule(time); + } + uint8_t flag = 1<<cause; + masterRequests |= flag; + } + + /** + * Clear the master bus request for the given cause. + * @param cause The request reason to clear. + */ + void clearMasterRequest(RequestCause cause) + { + uint8_t flag = 1<<cause; + masterRequests &= ~flag; + } + + /** + * Return true if the slave bus should be requested. + * @return True if there are outstanding requests for the slave bus. + */ + bool doSlaveRequest() + { + return slaveRequests != 0; + } + + /** + * Request the slave bus for the given reason and time. + * @param cause The reason for the request. + * @param time The time to make the request. + */ + void setSlaveRequest(RequestCause cause, Tick time) + { + uint8_t flag = 1<<cause; + slaveRequests |= flag; + assert("Implement\n" && 0); +// si->pktuest(time); + } + + /** + * Clear the slave bus request for the given reason. + * @param cause The request reason to clear. + */ + void clearSlaveRequest(RequestCause cause) + { + uint8_t flag = 1<<cause; + slaveRequests &= ~flag; + } + + /** + * Send a response to the slave interface. + * @param pkt The request being responded to. + * @param time The time the response is ready. + */ + void respond(Packet *pkt, Tick time) + { + pkt->makeTimingResponse(); + pkt->result = Packet::Success; + CacheEvent *reqCpu = new CacheEvent(cpuSidePort, pkt); + reqCpu->schedule(time); + } + + /** + * Send a reponse to the slave interface and calculate miss latency. + * @param pkt The request to respond to. + * @param time The time the response is ready. + */ + void respondToMiss(Packet *pkt, Tick time) + { + if (!pkt->req->isUncacheable()) { + missLatency[pkt->cmdToIndex()][pkt->req->getThreadNum()] += time - pkt->time; + } + pkt->makeTimingResponse(); + pkt->result = Packet::Success; + CacheEvent *reqCpu = new CacheEvent(cpuSidePort, pkt); + reqCpu->schedule(time); + } + + /** + * Suppliess the data if cache to cache transfers are enabled. + * @param pkt The bus transaction to fulfill. + */ + void respondToSnoop(Packet *pkt) + { + assert("Implement\n" && 0); +// mi->respond(pkt,curTick + hitLatency); + } + + /** + * Notification from master interface that a address range changed. Nothing + * to do for a cache. + */ + void rangeChange() {} + + void getAddressRanges(AddrRangeList &resp, AddrRangeList &snoop, bool isCpuSide) + { + if (isCpuSide) + { + AddrRangeList dummy; + memSidePort->getPeerAddressRanges(resp, dummy); + } + else + { + //This is where snoops get updated + return; + } + } +}; + +#endif //__BASE_CACHE_HH__ diff --git a/src/mem/cache/cache.cc b/src/mem/cache/cache.cc new file mode 100644 index 000000000..db66c096e --- /dev/null +++ b/src/mem/cache/cache.cc @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Steve Reinhardt + * Lisa Hsu + * Kevin Lim + */ + +/** + * @file + * Cache template instantiations. + */ + +#include "mem/config/cache.hh" +#include "mem/config/compression.hh" + +#include "mem/cache/tags/cache_tags.hh" + +#if defined(USE_CACHE_LRU) +#include "mem/cache/tags/lru.hh" +#endif + +#if defined(USE_CACHE_FALRU) +#include "mem/cache/tags/fa_lru.hh" +#endif + +#if defined(USE_CACHE_IIC) +#include "mem/cache/tags/iic.hh" +#endif + +#if defined(USE_CACHE_SPLIT) +#include "mem/cache/tags/split.hh" +#endif + +#if defined(USE_CACHE_SPLIT_LIFO) +#include "mem/cache/tags/split_lifo.hh" +#endif + +#include "base/compression/null_compression.hh" +#if defined(USE_LZSS_COMPRESSION) +#include "base/compression/lzss_compression.hh" +#endif + +#include "mem/cache/miss/miss_queue.hh" +#include "mem/cache/miss/blocking_buffer.hh" + +#include "mem/cache/coherence/uni_coherence.hh" +#include "mem/cache/coherence/simple_coherence.hh" + +#include "mem/cache/cache_impl.hh" + +// Template Instantiations +#ifndef DOXYGEN_SHOULD_SKIP_THIS + + +#if defined(USE_CACHE_FALRU) +template class Cache<CacheTags<FALRU,NullCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<FALRU,NullCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<FALRU,NullCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<FALRU,NullCompression>, MissQueue, UniCoherence>; +#if defined(USE_LZSS_COMPRESSION) +template class Cache<CacheTags<FALRU,LZSSCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<FALRU,LZSSCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<FALRU,LZSSCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<FALRU,LZSSCompression>, MissQueue, UniCoherence>; +#endif +#endif + +#if defined(USE_CACHE_IIC) +template class Cache<CacheTags<IIC,NullCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<IIC,NullCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<IIC,NullCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<IIC,NullCompression>, MissQueue, UniCoherence>; +#if defined(USE_LZSS_COMPRESSION) +template class Cache<CacheTags<IIC,LZSSCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<IIC,LZSSCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<IIC,LZSSCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<IIC,LZSSCompression>, MissQueue, UniCoherence>; +#endif +#endif + +#if defined(USE_CACHE_LRU) +template class Cache<CacheTags<LRU,NullCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<LRU,NullCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<LRU,NullCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<LRU,NullCompression>, MissQueue, UniCoherence>; +#if defined(USE_LZSS_COMPRESSION) +template class Cache<CacheTags<LRU,LZSSCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<LRU,LZSSCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<LRU,LZSSCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<LRU,LZSSCompression>, MissQueue, UniCoherence>; +#endif +#endif + +#if defined(USE_CACHE_SPLIT) +template class Cache<CacheTags<Split,NullCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<Split,NullCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<Split,NullCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<Split,NullCompression>, MissQueue, UniCoherence>; +#if defined(USE_LZSS_COMPRESSION) +template class Cache<CacheTags<Split,LZSSCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<Split,LZSSCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<Split,LZSSCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<Split,LZSSCompression>, MissQueue, UniCoherence>; +#endif +#endif + +#if defined(USE_CACHE_SPLIT_LIFO) +template class Cache<CacheTags<SplitLIFO,NullCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<SplitLIFO,NullCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<SplitLIFO,NullCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<SplitLIFO,NullCompression>, MissQueue, UniCoherence>; +#if defined(USE_LZSS_COMPRESSION) +template class Cache<CacheTags<SplitLIFO,LZSSCompression>, BlockingBuffer, SimpleCoherence>; +template class Cache<CacheTags<SplitLIFO,LZSSCompression>, BlockingBuffer, UniCoherence>; +template class Cache<CacheTags<SplitLIFO,LZSSCompression>, MissQueue, SimpleCoherence>; +template class Cache<CacheTags<SplitLIFO,LZSSCompression>, MissQueue, UniCoherence>; +#endif +#endif + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/cache.hh b/src/mem/cache/cache.hh new file mode 100644 index 000000000..989b8743e --- /dev/null +++ b/src/mem/cache/cache.hh @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Dave Greene + * Steve Reinhardt + */ + +/** + * @file + * Describes a cache based on template policies. + */ + +#ifndef __CACHE_HH__ +#define __CACHE_HH__ + +#include "base/misc.hh" // fatal, panic, and warn +#include "cpu/smt.hh" // SMT_MAX_THREADS + +#include "mem/cache/base_cache.hh" +#include "mem/cache/prefetch/prefetcher.hh" + +//Forward decleration +class MSHR; + + +/** + * A template-policy based cache. The behavior of the cache can be altered by + * supplying different template policies. TagStore handles all tag and data + * storage @sa TagStore. Buffering handles all misses and writes/writebacks + * @sa MissQueue. Coherence handles all coherence policy details @sa + * UniCoherence, SimpleMultiCoherence. + */ +template <class TagStore, class Buffering, class Coherence> +class Cache : public BaseCache +{ + public: + /** Define the type of cache block to use. */ + typedef typename TagStore::BlkType BlkType; + + bool prefetchAccess; + protected: + + /** Tag and data Storage */ + TagStore *tags; + /** Miss and Writeback handler */ + Buffering *missQueue; + /** Coherence protocol. */ + Coherence *coherence; + + /** Prefetcher */ + Prefetcher<TagStore, Buffering> *prefetcher; + + /** Do fast copies in this cache. */ + bool doCopy; + + /** Block on a delayed copy. */ + bool blockOnCopy; + + /** + * The clock ratio of the outgoing bus. + * Used for calculating critical word first. + */ + int busRatio; + + /** + * The bus width in bytes of the outgoing bus. + * Used for calculating critical word first. + */ + int busWidth; + + /** + * The latency of a hit in this device. + */ + int hitLatency; + + /** + * A permanent mem req to always be used to cause invalidations. + * Used to append to target list, to cause an invalidation. + */ + Packet * invalidatePkt; + + /** + * Temporarily move a block into a MSHR. + * @todo Remove this when LSQ/SB are fixed and implemented in memtest. + */ + void pseudoFill(Addr addr); + + /** + * Temporarily move a block into an existing MSHR. + * @todo Remove this when LSQ/SB are fixed and implemented in memtest. + */ + void pseudoFill(MSHR *mshr); + + public: + + class Params + { + public: + TagStore *tags; + Buffering *missQueue; + Coherence *coherence; + bool doCopy; + bool blockOnCopy; + BaseCache::Params baseParams; + Prefetcher<TagStore, Buffering> *prefetcher; + bool prefetchAccess; + int hitLatency; + + Params(TagStore *_tags, Buffering *mq, Coherence *coh, + bool do_copy, BaseCache::Params params, + Prefetcher<TagStore, Buffering> *_prefetcher, + bool prefetch_access, int hit_latency) + : tags(_tags), missQueue(mq), coherence(coh), doCopy(do_copy), + blockOnCopy(false), baseParams(params), + prefetcher(_prefetcher), prefetchAccess(prefetch_access), + hitLatency(hit_latency) + { + } + }; + + /** Instantiates a basic cache object. */ + Cache(const std::string &_name, Params ¶ms); + + virtual bool doTimingAccess(Packet *pkt, CachePort *cachePort, + bool isCpuSide); + + virtual Tick doAtomicAccess(Packet *pkt, bool isCpuSide); + + virtual void doFunctionalAccess(Packet *pkt, bool isCpuSide); + + virtual void recvStatusChange(Port::Status status, bool isCpuSide); + + void regStats(); + + /** + * Performs the access specified by the request. + * @param pkt The request to perform. + * @return The result of the access. + */ + bool access(Packet * &pkt); + + /** + * Selects a request to send on the bus. + * @return The memory request to service. + */ + virtual Packet * getPacket(); + + /** + * Was the request was sent successfully? + * @param pkt The request. + * @param success True if the request was sent successfully. + */ + virtual void sendResult(Packet * &pkt, bool success); + + /** + * Handles a response (cache line fill/write ack) from the bus. + * @param pkt The request being responded to. + */ + void handleResponse(Packet * &pkt); + + /** + * Start handling a copy transaction. + * @param pkt The copy request to perform. + */ + void startCopy(Packet * &pkt); + + /** + * Handle a delayed copy transaction. + * @param pkt The delayed copy request to continue. + * @param addr The address being responded to. + * @param blk The block of the current response. + * @param mshr The mshr being handled. + */ + void handleCopy(Packet * &pkt, Addr addr, BlkType *blk, MSHR *mshr); + + /** + * Selects a coherence message to forward to lower levels of the hierarchy. + * @return The coherence message to forward. + */ + virtual Packet * getCoherencePacket(); + + /** + * Snoops bus transactions to maintain coherence. + * @param pkt The current bus transaction. + */ + void snoop(Packet * &pkt); + + void snoopResponse(Packet * &pkt); + + /** + * Invalidates the block containing address if found. + * @param addr The address to look for. + * @param asid The address space ID of the address. + * @todo Is this function necessary? + */ + void invalidateBlk(Addr addr); + + /** + * Squash all requests associated with specified thread. + * intended for use by I-cache. + * @param threadNum The thread to squash. + */ + void squash(int threadNum) + { + missQueue->squash(threadNum); + } + + /** + * Return the number of outstanding misses in a Cache. + * Default returns 0. + * + * @retval unsigned The number of missing still outstanding. + */ + unsigned outstandingMisses() const + { + return missQueue->getMisses(); + } + + /** + * Perform the access specified in the request and return the estimated + * time of completion. This function can either update the hierarchy state + * or just perform the access wherever the data is found depending on the + * state of the update flag. + * @param pkt The memory request to satisfy + * @param update If true, update the hierarchy, otherwise just perform the + * request. + * @return The estimated completion time. + */ + Tick probe(Packet * &pkt, bool update); + + /** + * Snoop for the provided request in the cache and return the estimated + * time of completion. + * @todo Can a snoop probe not change state? + * @param pkt The memory request to satisfy + * @param update If true, update the hierarchy, otherwise just perform the + * request. + * @return The estimated completion time. + */ + Tick snoopProbe(Packet * &pkt, bool update); +}; + +#endif // __CACHE_HH__ diff --git a/src/mem/cache/cache_blk.hh b/src/mem/cache/cache_blk.hh new file mode 100644 index 000000000..a75c9611d --- /dev/null +++ b/src/mem/cache/cache_blk.hh @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** @file + * Definitions of a simple cache block class. + */ + +#ifndef __CACHE_BLK_HH__ +#define __CACHE_BLK_HH__ + +#include "sim/root.hh" // for Tick +#include "arch/isa_traits.hh" // for Addr + +#include <iostream> + +/** + * Cache block status bit assignments + */ +enum CacheBlkStatusBits { + /** valid, readable */ + BlkValid = 0x01, + /** write permission */ + BlkWritable = 0x02, + /** dirty (modified) */ + BlkDirty = 0x04, + /** compressed */ + BlkCompressed = 0x08, + /** block was referenced */ + BlkReferenced = 0x10, + /** block was a hardware prefetch yet unaccessed*/ + BlkHWPrefetched = 0x20 +}; + +/** + * A Basic Cache block. + * Contains the tag, status, and a pointer to data. + */ +class CacheBlk +{ + public: + /** The address space ID of this block. */ + int asid; + /** Data block tag value. */ + Addr tag; + /** + * Contains a copy of the data in this block for easy access. This is used + * for efficient execution when the data could be actually stored in + * another format (COW, compressed, sub-blocked, etc). In all cases the + * data stored here should be kept consistant with the actual data + * referenced by this block. + */ + uint8_t *data; + /** the number of bytes stored in this block. */ + int size; + + /** block state: OR of CacheBlkStatusBit */ + typedef unsigned State; + + /** The current status of this block. @sa CacheBlockStatusBits */ + State status; + + /** Which curTick will this block be accessable */ + Tick whenReady; + + /** + * The set this block belongs to. + * @todo Move this into subclasses when we fix CacheTags to use them. + */ + int set; + + /** Number of references to this block since it was brought in. */ + int refCount; + + CacheBlk() + : asid(-1), tag(0), data(0) ,size(0), status(0), whenReady(0), + set(-1), refCount(0) + {} + + /** + * Copy the state of the given block into this one. + * @param rhs The block to copy. + * @return a const reference to this block. + */ + const CacheBlk& operator=(const CacheBlk& rhs) + { + asid = rhs.asid; + tag = rhs.tag; + data = rhs.data; + size = rhs.size; + status = rhs.status; + whenReady = rhs.whenReady; + set = rhs.set; + refCount = rhs.refCount; + return *this; + } + + /** + * Checks the write permissions of this block. + * @return True if the block is writable. + */ + bool isWritable() const + { + const int needed_bits = BlkWritable | BlkValid; + return (status & needed_bits) == needed_bits; + } + + /** + * Checks that a block is valid (readable). + * @return True if the block is valid. + */ + bool isValid() const + { + return (status & BlkValid) != 0; + } + + /** + * Check to see if a block has been written. + * @return True if the block is dirty. + */ + bool isModified() const + { + return (status & BlkDirty) != 0; + } + + /** + * Check to see if this block contains compressed data. + * @return True iF the block's data is compressed. + */ + bool isCompressed() const + { + return (status & BlkCompressed) != 0; + } + + /** + * Check if this block has been referenced. + * @return True if the block has been referenced. + */ + bool isReferenced() const + { + return (status & BlkReferenced) != 0; + } + + /** + * Check if this block was the result of a hardware prefetch, yet to + * be touched. + * @return True if the block was a hardware prefetch, unaccesed. + */ + bool isPrefetch() const + { + return (status & BlkHWPrefetched) != 0; + } + + +}; + +/** + * Output a CacheBlk to the given ostream. + * @param out The stream for the output. + * @param blk The cache block to print. + * + * @return The output stream. + */ +inline std::ostream & +operator<<(std::ostream &out, const CacheBlk &blk) +{ + out << std::hex << std::endl; + out << " Tag: " << blk.tag << std::endl; + out << " Status: " << blk.status << std::endl; + + return(out << std::dec); +} + +#endif //__CACHE_BLK_HH__ diff --git a/src/mem/cache/cache_builder.cc b/src/mem/cache/cache_builder.cc new file mode 100644 index 000000000..05a149a1c --- /dev/null +++ b/src/mem/cache/cache_builder.cc @@ -0,0 +1,480 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Nathan Binkert + */ + +/** + * @file + * Simobject instatiation of caches. + */ +#include <vector> + +// Must be included first to determine which caches we want +#include "mem/config/cache.hh" +#include "mem/config/compression.hh" +#include "mem/config/prefetch.hh" + +#include "mem/cache/base_cache.hh" +#include "mem/cache/cache.hh" +#include "mem/bus.hh" +#include "mem/cache/coherence/coherence_protocol.hh" +#include "sim/builder.hh" + +// Tag Templates +#if defined(USE_CACHE_LRU) +#include "mem/cache/tags/lru.hh" +#endif + +#if defined(USE_CACHE_FALRU) +#include "mem/cache/tags/fa_lru.hh" +#endif + +#if defined(USE_CACHE_IIC) +#include "mem/cache/tags/iic.hh" +#endif + +#if defined(USE_CACHE_SPLIT) +#include "mem/cache/tags/split.hh" +#endif + +#if defined(USE_CACHE_SPLIT_LIFO) +#include "mem/cache/tags/split_lifo.hh" +#endif + +// Compression Templates +#include "base/compression/null_compression.hh" +#if defined(USE_LZSS_COMPRESSION) +#include "base/compression/lzss_compression.hh" +#endif + +// CacheTags Templates +#include "mem/cache/tags/cache_tags.hh" + +// MissQueue Templates +#include "mem/cache/miss/miss_queue.hh" +#include "mem/cache/miss/blocking_buffer.hh" + +// Coherence Templates +#include "mem/cache/coherence/uni_coherence.hh" +#include "mem/cache/coherence/simple_coherence.hh" + +//Prefetcher Headers +#if defined(USE_GHB) +#include "mem/cache/prefetch/ghb_prefetcher.hh" +#endif +#if defined(USE_TAGGED) +#include "mem/cache/prefetch/tagged_prefetcher.hh" +#endif +#if defined(USE_STRIDED) +#include "mem/cache/prefetch/stride_prefetcher.hh" +#endif + + +using namespace std; +using namespace TheISA; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(BaseCache) + + Param<int> size; + Param<int> assoc; + Param<int> block_size; + Param<int> latency; + Param<int> mshrs; + Param<int> tgts_per_mshr; + Param<int> write_buffers; + Param<bool> prioritizeRequests; +// SimObjectParam<Bus *> in_bus; +// SimObjectParam<Bus *> out_bus; + Param<bool> do_copy; + SimObjectParam<CoherenceProtocol *> protocol; + Param<Addr> trace_addr; + Param<int> hash_delay; +#if defined(USE_CACHE_IIC) + SimObjectParam<Repl *> repl; +#endif + Param<bool> compressed_bus; + Param<bool> store_compressed; + Param<bool> adaptive_compression; + Param<int> compression_latency; + Param<int> subblock_size; + Param<Counter> max_miss_count; +// SimObjectParam<HierParams *> hier; + VectorParam<Range<Addr> > addr_range; +// SimObjectParam<MemTraceWriter *> mem_trace; + Param<bool> split; + Param<int> split_size; + Param<bool> lifo; + Param<bool> two_queue; + Param<bool> prefetch_miss; + Param<bool> prefetch_access; + Param<int> prefetcher_size; + Param<bool> prefetch_past_page; + Param<bool> prefetch_serial_squash; + Param<Tick> prefetch_latency; + Param<int> prefetch_degree; + Param<string> prefetch_policy; + Param<bool> prefetch_cache_check_push; + Param<bool> prefetch_use_cpu_id; + Param<bool> prefetch_data_accesses_only; + Param<int> hit_latency; + +END_DECLARE_SIM_OBJECT_PARAMS(BaseCache) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(BaseCache) + + INIT_PARAM(size, "capacity in bytes"), + INIT_PARAM(assoc, "associativity"), + INIT_PARAM(block_size, "block size in bytes"), + INIT_PARAM(latency, "hit latency in CPU cycles"), + INIT_PARAM(mshrs, "number of MSHRs (max outstanding requests)"), + INIT_PARAM(tgts_per_mshr, "max number of accesses per MSHR"), + INIT_PARAM_DFLT(write_buffers, "number of write buffers", 8), + INIT_PARAM_DFLT(prioritizeRequests, "always service demand misses first", + false), +/* INIT_PARAM_DFLT(in_bus, "incoming bus object", NULL), + INIT_PARAM(out_bus, "outgoing bus object"), +*/ + INIT_PARAM_DFLT(do_copy, "perform fast copies in the cache", false), + INIT_PARAM_DFLT(protocol, "coherence protocol to use in the cache", NULL), + INIT_PARAM_DFLT(trace_addr, "address to trace", 0), + + INIT_PARAM_DFLT(hash_delay, "time in cycles of hash access",1), +#if defined(USE_CACHE_IIC) + INIT_PARAM_DFLT(repl, "replacement policy",NULL), +#endif + INIT_PARAM_DFLT(compressed_bus, + "This cache connects to a compressed memory", + false), + INIT_PARAM_DFLT(store_compressed, "Store compressed data in the cache", + false), + INIT_PARAM_DFLT(adaptive_compression, "Use an adaptive compression scheme", + false), + INIT_PARAM_DFLT(compression_latency, + "Latency in cycles of compression algorithm", + 0), + INIT_PARAM_DFLT(subblock_size, + "Size of subblock in IIC used for compression", + 0), + INIT_PARAM_DFLT(max_miss_count, + "The number of misses to handle before calling exit", + 0), +/* INIT_PARAM_DFLT(hier, + "Hierarchy global variables", + &defaultHierParams), +*/ + INIT_PARAM_DFLT(addr_range, "The address range in bytes", + vector<Range<Addr> >(1,RangeIn((Addr)0, MaxAddr))), +// INIT_PARAM_DFLT(mem_trace, "Memory trace to write accesses to", NULL), + INIT_PARAM_DFLT(split, "Whether this is a partitioned cache", false), + INIT_PARAM_DFLT(split_size, "the number of \"ways\" belonging to the LRU partition", 0), + INIT_PARAM_DFLT(lifo, "whether you are using a LIFO repl. policy", false), + INIT_PARAM_DFLT(two_queue, "whether the lifo should have two queue replacement", false), + INIT_PARAM_DFLT(prefetch_miss, "wheter you are using the hardware prefetcher from Miss stream", false), + INIT_PARAM_DFLT(prefetch_access, "wheter you are using the hardware prefetcher from Access stream", false), + INIT_PARAM_DFLT(prefetcher_size, "Number of entries in the harware prefetch queue", 100), + INIT_PARAM_DFLT(prefetch_past_page, "Allow prefetches to cross virtual page boundaries", false), + INIT_PARAM_DFLT(prefetch_serial_squash, "Squash prefetches with a later time on a subsequent miss", false), + INIT_PARAM_DFLT(prefetch_latency, "Latency of the prefetcher", 10), + INIT_PARAM_DFLT(prefetch_degree, "Degree of the prefetch depth", 1), + INIT_PARAM_DFLT(prefetch_policy, "Type of prefetcher to use", "none"), + INIT_PARAM_DFLT(prefetch_cache_check_push, "Check if in cash on push or pop of prefetch queue", true), + INIT_PARAM_DFLT(prefetch_use_cpu_id, "Use the CPU ID to seperate calculations of prefetches", true), + INIT_PARAM_DFLT(prefetch_data_accesses_only, "Only prefetch on data not on instruction accesses", false), + INIT_PARAM_DFLT(hit_latency, "Hit Latecny for a succesful access", 1) +END_INIT_SIM_OBJECT_PARAMS(BaseCache) + + +#define BUILD_CACHE(t, comp, b, c) do { \ + Prefetcher<CacheTags<t, comp>, b> *pf; \ + if (pf_policy == "tagged") { \ + BUILD_TAGGED_PREFETCHER(t, comp, b); \ + } \ + else if (pf_policy == "stride") { \ + BUILD_STRIDED_PREFETCHER(t, comp, b); \ + } \ + else if (pf_policy == "ghb") { \ + BUILD_GHB_PREFETCHER(t, comp, b); \ + } \ + else { \ + BUILD_NULL_PREFETCHER(t, comp, b); \ + } \ + Cache<CacheTags<t, comp>, b, c>::Params params(tagStore, mq, coh, \ + do_copy, base_params, \ + /*in_bus, out_bus,*/ pf, \ + prefetch_access, hit_latency); \ + Cache<CacheTags<t, comp>, b, c> *retval = \ + new Cache<CacheTags<t, comp>, b, c>(getInstanceName(), /*hier,*/ \ + params); \ +/* if (in_bus == NULL) { \ + retval->setSlaveInterface(new MemoryInterface<Cache<CacheTags<t, comp>, b, c> >(getInstanceName(), hier, retval, mem_trace)); \ + } else { \ + retval->setSlaveInterface(new SlaveInterface<Cache<CacheTags<t, comp>, b, c>, Bus>(getInstanceName(), hier, retval, in_bus, mem_trace)); \ + } \ + retval->setMasterInterface(new MasterInterface<Cache<CacheTags<t, comp>, b, c>, Bus>(getInstanceName(), hier, retval, out_bus)); \ + out_bus->rangeChange(); \ + return retval; \ +*/return retval; \ + } while (0) + +#define BUILD_CACHE_PANIC(x) do { \ + panic("%s not compiled into M5", x); \ + } while (0) + +#if defined(USE_LZSS_COMPRESSION) +#define BUILD_COMPRESSED_CACHE(TAGS, tags, b, c) do { \ + if (compressed_bus || store_compressed){ \ + CacheTags<TAGS, LZSSCompression> *tagStore = \ + new CacheTags<TAGS, LZSSCompression>(tags, \ + compression_latency, \ + true, store_compressed, \ + adaptive_compression, \ + prefetch_miss); \ + BUILD_CACHE(TAGS, LZSSCompression, b, c); \ + } else { \ + CacheTags<TAGS, NullCompression> *tagStore = \ + new CacheTags<TAGS, NullCompression>(tags, \ + compression_latency, \ + true, store_compressed, \ + adaptive_compression, \ + prefetch_miss); \ + BUILD_CACHE(TAGS, NullCompression, b, c); \ + } \ + } while (0) +#else +#define BUILD_COMPRESSED_CACHE(TAGS, tags, b, c) do { \ + if (compressed_bus || store_compressed){ \ + BUILD_CACHE_PANIC("compressed caches"); \ + } else { \ + CacheTags<TAGS, NullCompression> *tagStore = \ + new CacheTags<TAGS, NullCompression>(tags, \ + compression_latency, \ + true, store_compressed, \ + adaptive_compression \ + prefetch_miss); \ + BUILD_CACHE(TAGS, NullCompression, b, c); \ + } \ + } while (0) +#endif + +#if defined(USE_CACHE_FALRU) +#define BUILD_FALRU_CACHE(b,c) do { \ + FALRU *tags = new FALRU(block_size, size, latency); \ + BUILD_COMPRESSED_CACHE(FALRU, tags, b, c); \ + } while (0) +#else +#define BUILD_FALRU_CACHE(b, c) BUILD_CACHE_PANIC("falru cache") +#endif + +#if defined(USE_CACHE_LRU) +#define BUILD_LRU_CACHE(b, c) do { \ + LRU *tags = new LRU(numSets, block_size, assoc, latency); \ + BUILD_COMPRESSED_CACHE(LRU, tags, b, c); \ + } while (0) +#else +#define BUILD_LRU_CACHE(b, c) BUILD_CACHE_PANIC("lru cache") +#endif + +#if defined(USE_CACHE_SPLIT) +#define BUILD_SPLIT_CACHE(b, c) do { \ + Split *tags = new Split(numSets, block_size, assoc, split_size, lifo, \ + two_queue, latency); \ + BUILD_COMPRESSED_CACHE(Split, tags, b, c); \ + } while (0) +#else +#define BUILD_SPLIT_CACHE(b, c) BUILD_CACHE_PANIC("split cache") +#endif + +#if defined(USE_CACHE_SPLIT_LIFO) +#define BUILD_SPLIT_LIFO_CACHE(b, c) do { \ + SplitLIFO *tags = new SplitLIFO(block_size, size, assoc, \ + latency, two_queue, -1); \ + BUILD_COMPRESSED_CACHE(SplitLIFO, tags, b, c); \ + } while (0) +#else +#define BUILD_SPLIT_LIFO_CACHE(b, c) BUILD_CACHE_PANIC("lifo cache") +#endif + +#if defined(USE_CACHE_IIC) +#define BUILD_IIC_CACHE(b ,c) do { \ + IIC *tags = new IIC(iic_params); \ + BUILD_COMPRESSED_CACHE(IIC, tags, b, c); \ + } while (0) +#else +#define BUILD_IIC_CACHE(b, c) BUILD_CACHE_PANIC("iic") +#endif + +#define BUILD_CACHES(b, c) do { \ + if (repl == NULL) { \ + if (numSets == 1) { \ + BUILD_FALRU_CACHE(b, c); \ + } else { \ + if (split == true) { \ + BUILD_SPLIT_CACHE(b, c); \ + } else if (lifo == true) { \ + BUILD_SPLIT_LIFO_CACHE(b, c); \ + } else { \ + BUILD_LRU_CACHE(b, c); \ + } \ + } \ + } else { \ + BUILD_IIC_CACHE(b, c); \ + } \ + } while (0) + +#define BUILD_COHERENCE(b) do { \ + if (protocol == NULL) { \ + UniCoherence *coh = new UniCoherence(); \ + BUILD_CACHES(b, UniCoherence); \ + } else { \ + SimpleCoherence *coh = new SimpleCoherence(protocol); \ + BUILD_CACHES(b, SimpleCoherence); \ + } \ + } while (0) + +#if defined(USE_TAGGED) +#define BUILD_TAGGED_PREFETCHER(t, comp, b) pf = new \ + TaggedPrefetcher<CacheTags<t, comp>, b>(prefetcher_size, \ + !prefetch_past_page, \ + prefetch_serial_squash, \ + prefetch_cache_check_push, \ + prefetch_data_accesses_only, \ + prefetch_latency, \ + prefetch_degree) +#else +#define BUILD_TAGGED_PREFETCHER(t, comp, b) BUILD_CACHE_PANIC("Tagged Prefetcher") +#endif + +#if defined(USE_STRIDED) +#define BUILD_STRIDED_PREFETCHER(t, comp, b) pf = new \ + StridePrefetcher<CacheTags<t, comp>, b>(prefetcher_size, \ + !prefetch_past_page, \ + prefetch_serial_squash, \ + prefetch_cache_check_push, \ + prefetch_data_accesses_only, \ + prefetch_latency, \ + prefetch_degree, \ + prefetch_use_cpu_id) +#else +#define BUILD_STRIDED_PREFETCHER(t, comp, b) BUILD_CACHE_PANIC("Stride Prefetcher") +#endif + +#if defined(USE_GHB) +#define BUILD_GHB_PREFETCHER(t, comp, b) pf = new \ + GHBPrefetcher<CacheTags<t, comp>, b>(prefetcher_size, \ + !prefetch_past_page, \ + prefetch_serial_squash, \ + prefetch_cache_check_push, \ + prefetch_data_accesses_only, \ + prefetch_latency, \ + prefetch_degree, \ + prefetch_use_cpu_id) +#else +#define BUILD_GHB_PREFETCHER(t, comp, b) BUILD_CACHE_PANIC("GHB Prefetcher") +#endif + +#if defined(USE_TAGGED) +#define BUILD_NULL_PREFETCHER(t, comp, b) pf = new \ + TaggedPrefetcher<CacheTags<t, comp>, b>(prefetcher_size, \ + !prefetch_past_page, \ + prefetch_serial_squash, \ + prefetch_cache_check_push, \ + prefetch_data_accesses_only, \ + prefetch_latency, \ + prefetch_degree) +#else +#define BUILD_NULL_PREFETCHER(t, comp, b) BUILD_CACHE_PANIC("NULL Prefetcher (uses Tagged)") +#endif + +CREATE_SIM_OBJECT(BaseCache) +{ + string name = getInstanceName(); + int numSets = size / (assoc * block_size); + string pf_policy = prefetch_policy; + if (subblock_size == 0) { + subblock_size = block_size; + } + + // Build BaseCache param object + BaseCache::Params base_params(addr_range, latency, + block_size, max_miss_count); + + //Warnings about prefetcher policy + if (pf_policy == "none" && (prefetch_miss || prefetch_access)) { + panic("With no prefetcher, you shouldn't prefetch from" + " either miss or access stream\n"); + } + if ((pf_policy == "tagged" || pf_policy == "stride" || + pf_policy == "ghb") && !(prefetch_miss || prefetch_access)) { + warn("With this prefetcher you should chose a prefetch" + " stream (miss or access)\nNo Prefetching will occur\n"); + } + if ((pf_policy == "tagged" || pf_policy == "stride" || + pf_policy == "ghb") && prefetch_miss && prefetch_access) { + panic("Can't do prefetches from both miss and access" + " stream\n"); + } + if (pf_policy != "tagged" && pf_policy != "stride" && + pf_policy != "ghb" && pf_policy != "none") { + panic("Unrecognized form of a prefetcher: %s, try using" + "['none','stride','tagged','ghb']\n", pf_policy); + } + +#if defined(USE_CACHE_IIC) + // Build IIC params + IIC::Params iic_params; + iic_params.size = size; + iic_params.numSets = numSets; + iic_params.blkSize = block_size; + iic_params.assoc = assoc; + iic_params.hashDelay = hash_delay; + iic_params.hitLatency = latency; + iic_params.rp = repl; + iic_params.subblockSize = subblock_size; +#else + const void *repl = NULL; +#endif + + if (mshrs == 1 /*|| out_bus->doEvents() == false*/) { + BlockingBuffer *mq = new BlockingBuffer(true); + BUILD_COHERENCE(BlockingBuffer); + } else { + MissQueue *mq = new MissQueue(mshrs, tgts_per_mshr, write_buffers, + true, prefetch_miss); + BUILD_COHERENCE(MissQueue); + } + return NULL; +} + +REGISTER_SIM_OBJECT("BaseCache", BaseCache) + + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/cache_impl.hh b/src/mem/cache/cache_impl.hh new file mode 100644 index 000000000..11cd84e88 --- /dev/null +++ b/src/mem/cache/cache_impl.hh @@ -0,0 +1,672 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Dave Greene + * Nathan Binkert + */ + +/** + * @file + * Cache definitions. + */ + +#include <assert.h> +#include <math.h> + +#include <cassert> +#include <iostream> +#include <string> + +#include "sim/host.hh" +#include "base/misc.hh" +#include "cpu/smt.hh" + +#include "mem/cache/cache.hh" +#include "mem/cache/cache_blk.hh" +#include "mem/cache/miss/mshr.hh" +#include "mem/cache/prefetch/prefetcher.hh" + +#include "sim/sim_events.hh" // for SimExitEvent + +template<class TagStore, class Buffering, class Coherence> +bool +Cache<TagStore,Buffering,Coherence>:: +doTimingAccess(Packet *pkt, CachePort *cachePort, bool isCpuSide) +{ + if (isCpuSide) + { + if (pkt->isWrite() && (pkt->req->getFlags() & LOCKED)) { + pkt->req->setScResult(1); + } + access(pkt); + } + else + { + if (pkt->isResponse()) + handleResponse(pkt); + else + snoop(pkt); + } + return true; +} + +template<class TagStore, class Buffering, class Coherence> +Tick +Cache<TagStore,Buffering,Coherence>:: +doAtomicAccess(Packet *pkt, bool isCpuSide) +{ + if (isCpuSide) + { + //Temporary solution to LL/SC + if (pkt->isWrite() && (pkt->req->getFlags() & LOCKED)) { + pkt->req->setScResult(1); + } + + probe(pkt, true); + //TEMP ALWAYS SUCCES FOR NOW + pkt->result = Packet::Success; + } + else + { + if (pkt->isResponse()) + handleResponse(pkt); + else + snoopProbe(pkt, true); + } + //Fix this timing info + return hitLatency; +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>:: +doFunctionalAccess(Packet *pkt, bool isCpuSide) +{ + if (isCpuSide) + { + //TEMP USE CPU?THREAD 0 0 + pkt->req->setThreadContext(0,0); + + //Temporary solution to LL/SC + if (pkt->isWrite() && (pkt->req->getFlags() & LOCKED)) { + assert("Can't handle LL/SC on functional path\n"); + } + + probe(pkt, true); + //TEMP ALWAYS SUCCESFUL FOR NOW + pkt->result = Packet::Success; + } + else + { + if (pkt->isResponse()) + handleResponse(pkt); + else + snoopProbe(pkt, true); + } +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>:: +recvStatusChange(Port::Status status, bool isCpuSide) +{ + +} + + +template<class TagStore, class Buffering, class Coherence> +Cache<TagStore,Buffering,Coherence>:: +Cache(const std::string &_name, + Cache<TagStore,Buffering,Coherence>::Params ¶ms) + : BaseCache(_name, params.baseParams), + prefetchAccess(params.prefetchAccess), + tags(params.tags), missQueue(params.missQueue), + coherence(params.coherence), prefetcher(params.prefetcher), + doCopy(params.doCopy), blockOnCopy(params.blockOnCopy) +{ +//FIX BUS POINTERS +// if (params.in == NULL) { + topLevelCache = true; +// } +//PLEASE FIX THIS, BUS SIZES NOT BEING USED + tags->setCache(this, blkSize, 1/*params.out->width, params.out->clockRate*/); + tags->setPrefetcher(prefetcher); + missQueue->setCache(this); + missQueue->setPrefetcher(prefetcher); + coherence->setCache(this); + prefetcher->setCache(this); + prefetcher->setTags(tags); + prefetcher->setBuffer(missQueue); +#if 0 + invalidatePkt = new Packet; + invalidatePkt->cmd = Packet::InvalidateReq; +#endif +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::regStats() +{ + BaseCache::regStats(); + tags->regStats(name()); + missQueue->regStats(name()); + coherence->regStats(name()); + prefetcher->regStats(name()); +} + +template<class TagStore, class Buffering, class Coherence> +bool +Cache<TagStore,Buffering,Coherence>::access(PacketPtr &pkt) +{ +//@todo Add back in MemDebug Calls +// MemDebug::cacheAccess(pkt); + BlkType *blk = NULL; + PacketList writebacks; + int size = blkSize; + int lat = hitLatency; + if (prefetchAccess) { + //We are determining prefetches on access stream, call prefetcher + prefetcher->handleMiss(pkt, curTick); + } + if (!pkt->req->isUncacheable()) { + if (pkt->isInvalidate() && !pkt->isRead() + && !pkt->isWrite()) { + //Upgrade or Invalidate + //Look into what happens if two slave caches on bus + DPRINTF(Cache, "%s %x ? blk_addr: %x\n", pkt->cmdString(), + pkt->getAddr() & (((ULL(1))<<48)-1), + pkt->getAddr() & ~((Addr)blkSize - 1)); + + //@todo Should this return latency have the hit latency in it? +// respond(pkt,curTick+lat); + pkt->flags |= SATISFIED; +// return MA_HIT; //@todo, return values + return true; + } + blk = tags->handleAccess(pkt, lat, writebacks); + } else { + size = pkt->getSize(); + } + // If this is a block size write/hint (WH64) allocate the block here + // if the coherence protocol allows it. + /** @todo make the fast write alloc (wh64) work with coherence. */ + /** @todo Do we want to do fast writes for writebacks as well? */ + if (!blk && pkt->getSize() >= blkSize && coherence->allowFastWrites() && + (pkt->cmd == Packet::WriteReq || pkt->cmd == Packet::WriteInvalidateReq) ) { + // not outstanding misses, can do this + MSHR* outstanding_miss = missQueue->findMSHR(pkt->getAddr()); + if (pkt->cmd == Packet::WriteInvalidateReq || !outstanding_miss) { + if (outstanding_miss) { + warn("WriteInv doing a fastallocate" + "with an outstanding miss to the same address\n"); + } + blk = tags->handleFill(NULL, pkt, BlkValid | BlkWritable, + writebacks); + ++fastWrites; + } + } + while (!writebacks.empty()) { + missQueue->doWriteback(writebacks.front()); + writebacks.pop_front(); + } + DPRINTF(Cache, "%s %x %s blk_addr: %x pc %x\n", pkt->cmdString(), + pkt->getAddr() & (((ULL(1))<<48)-1), (blk) ? "hit" : "miss", + pkt->getAddr() & ~((Addr)blkSize - 1), pkt->req->getPC()); + if (blk) { + // Hit + hits[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + // clear dirty bit if write through + if (pkt->needsResponse()) + respond(pkt, curTick+lat); +// return MA_HIT; + return true; + } + + // Miss + if (!pkt->req->isUncacheable()) { + misses[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + /** @todo Move miss count code into BaseCache */ + if (missCount) { + --missCount; + if (missCount == 0) + new SimLoopExitEvent(curTick, "A cache reached the maximum miss count"); + } + } + missQueue->handleMiss(pkt, size, curTick + hitLatency); +// return MA_CACHE_MISS; + return true; +} + + +template<class TagStore, class Buffering, class Coherence> +Packet * +Cache<TagStore,Buffering,Coherence>::getPacket() +{ + Packet * pkt = missQueue->getPacket(); + if (pkt) { + if (!pkt->req->isUncacheable()) { + if (pkt->cmd == Packet::HardPFReq) misses[Packet::HardPFReq][pkt->req->getThreadNum()]++; + BlkType *blk = tags->findBlock(pkt); + Packet::Command cmd = coherence->getBusCmd(pkt->cmd, + (blk)? blk->status : 0); + missQueue->setBusCmd(pkt, cmd); + } + } + + assert(!doMasterRequest() || missQueue->havePending()); + assert(!pkt || pkt->time <= curTick); + return pkt; +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::sendResult(PacketPtr &pkt, bool success) +{ + if (success) { + missQueue->markInService(pkt); + //Temp Hack for UPGRADES + if (pkt->cmd == Packet::UpgradeReq) { + handleResponse(pkt); + } + } else if (pkt && !pkt->req->isUncacheable()) { + missQueue->restoreOrigCmd(pkt); + } +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::handleResponse(Packet * &pkt) +{ + BlkType *blk = NULL; + if (pkt->senderState) { +// MemDebug::cacheResponse(pkt); + DPRINTF(Cache, "Handling reponse to %x, blk addr: %x\n",pkt->getAddr(), + pkt->getAddr() & (((ULL(1))<<48)-1)); + + if (pkt->isCacheFill() && !pkt->isNoAllocate()) { + blk = tags->findBlock(pkt); + CacheBlk::State old_state = (blk) ? blk->status : 0; + PacketList writebacks; + blk = tags->handleFill(blk, (MSHR*)pkt->senderState, + coherence->getNewState(pkt,old_state), + writebacks); + while (!writebacks.empty()) { + missQueue->doWriteback(writebacks.front()); + } + } + missQueue->handleResponse(pkt, curTick + hitLatency); + } +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::pseudoFill(Addr addr) +{ + // Need to temporarily move this blk into MSHRs + MSHR *mshr = missQueue->allocateTargetList(addr); + int lat; + PacketList dummy; + // Read the data into the mshr + BlkType *blk = tags->handleAccess(mshr->pkt, lat, dummy, false); + assert(dummy.empty()); + assert(mshr->pkt->flags & SATISFIED); + // can overload order since it isn't used on non pending blocks + mshr->order = blk->status; + // temporarily remove the block from the cache. + tags->invalidateBlk(addr); +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::pseudoFill(MSHR *mshr) +{ + // Need to temporarily move this blk into MSHRs + assert(mshr->pkt->cmd == Packet::ReadReq); + int lat; + PacketList dummy; + // Read the data into the mshr + BlkType *blk = tags->handleAccess(mshr->pkt, lat, dummy, false); + assert(dummy.empty()); + assert(mshr->pkt->flags & SATISFIED); + // can overload order since it isn't used on non pending blocks + mshr->order = blk->status; + // temporarily remove the block from the cache. + tags->invalidateBlk(mshr->pkt->getAddr()); +} + + +template<class TagStore, class Buffering, class Coherence> +Packet * +Cache<TagStore,Buffering,Coherence>::getCoherencePacket() +{ + return coherence->getPacket(); +} + + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::snoop(Packet * &pkt) +{ + + Addr blk_addr = pkt->getAddr() & ~(Addr(blkSize-1)); + BlkType *blk = tags->findBlock(pkt); + MSHR *mshr = missQueue->findMSHR(blk_addr); + if (isTopLevel() && coherence->hasProtocol()) { //@todo Move this into handle bus req + //If we find an mshr, and it is in service, we need to NACK or invalidate + if (mshr) { + if (mshr->inService) { + if ((mshr->pkt->isInvalidate() || !mshr->pkt->isCacheFill()) + && (pkt->cmd != Packet::InvalidateReq && pkt->cmd != Packet::WriteInvalidateReq)) { + //If the outstanding request was an invalidate (upgrade,readex,..) + //Then we need to ACK the request until we get the data + //Also NACK if the outstanding request is not a cachefill (writeback) + pkt->flags |= NACKED_LINE; + return; + } + else { + //The supplier will be someone else, because we are waiting for + //the data. This should cause this cache to be forced to go to + //the shared state, not the exclusive even though the shared line + //won't be asserted. But for now we will just invlidate ourselves + //and allow the other cache to go into the exclusive state. + //@todo Make it so a read to a pending read doesn't invalidate. + //@todo Make it so that a read to a pending read can't be exclusive now. + + //Set the address so find match works + invalidatePkt->addrOverride(pkt->getAddr()); + + //Append the invalidate on + missQueue->addTarget(mshr,invalidatePkt); + DPRINTF(Cache, "Appending Invalidate to blk_addr: %x\n", pkt->getAddr() & (((ULL(1))<<48)-1)); + return; + } + } + } + //We also need to check the writeback buffers and handle those + std::vector<MSHR *> writebacks; + if (missQueue->findWrites(blk_addr, writebacks)) { + DPRINTF(Cache, "Snoop hit in writeback to blk_addr: %x\n", pkt->getAddr() & (((ULL(1))<<48)-1)); + + //Look through writebacks for any non-uncachable writes, use that + for (int i=0; i<writebacks.size(); i++) { + mshr = writebacks[i]; + + if (!mshr->pkt->req->isUncacheable()) { + if (pkt->isRead()) { + //Only Upgrades don't get here + //Supply the data + pkt->flags |= SATISFIED; + + //If we are in an exclusive protocol, make it ask again + //to get write permissions (upgrade), signal shared + pkt->flags |= SHARED_LINE; + + assert(pkt->isRead()); + Addr offset = pkt->getAddr() & ~(blkSize - 1); + assert(offset < blkSize); + assert(pkt->getSize() <= blkSize); + assert(offset + pkt->getSize() <=blkSize); + memcpy(pkt->getPtr<uint8_t>(), mshr->pkt->getPtr<uint8_t>() + offset, pkt->getSize()); + + respondToSnoop(pkt); + } + + if (pkt->isInvalidate()) { + //This must be an upgrade or other cache will take ownership + missQueue->markInService(mshr->pkt); + } + return; + } + } + } + } + CacheBlk::State new_state; + bool satisfy = coherence->handleBusRequest(pkt,blk,mshr, new_state); + if (satisfy) { + tags->handleSnoop(blk, new_state, pkt); + respondToSnoop(pkt); + return; + } + tags->handleSnoop(blk, new_state); +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::snoopResponse(Packet * &pkt) +{ + //Need to handle the response, if NACKED + if (pkt->flags & NACKED_LINE) { + //Need to mark it as not in service, and retry for bus + assert(0); //Yeah, we saw a NACK come through + + //For now this should never get called, we return false when we see a NACK + //instead, by doing this we allow the bus_blocked mechanism to handle the retry + //For now it retrys in just 2 cycles, need to figure out how to change that + //Eventually we will want to also have success come in as a parameter + //Need to make sure that we handle the functionality that happens on successufl + //return of the sendAddr function + } +} + +template<class TagStore, class Buffering, class Coherence> +void +Cache<TagStore,Buffering,Coherence>::invalidateBlk(Addr addr) +{ + tags->invalidateBlk(addr); +} + + +/** + * @todo Fix to not assume write allocate + */ +template<class TagStore, class Buffering, class Coherence> +Tick +Cache<TagStore,Buffering,Coherence>::probe(Packet * &pkt, bool update) +{ +// MemDebug::cacheProbe(pkt); + if (!pkt->req->isUncacheable()) { + if (pkt->isInvalidate() && !pkt->isRead() + && !pkt->isWrite()) { + //Upgrade or Invalidate, satisfy it, don't forward + DPRINTF(Cache, "%s %x ? blk_addr: %x\n", pkt->cmdString(), + pkt->getAddr() & (((ULL(1))<<48)-1), + pkt->getAddr() & ~((Addr)blkSize - 1)); + pkt->flags |= SATISFIED; + return 0; + } + } + + PacketList writebacks; + int lat; + BlkType *blk = tags->handleAccess(pkt, lat, writebacks, update); + + if (!blk) { + // Need to check for outstanding misses and writes + Addr blk_addr = pkt->getAddr() & ~(blkSize - 1); + + // There can only be one matching outstanding miss. + MSHR* mshr = missQueue->findMSHR(blk_addr); + + // There can be many matching outstanding writes. + std::vector<MSHR*> writes; + missQueue->findWrites(blk_addr, writes); + + if (!update) { + memSidePort->sendFunctional(pkt); + // Check for data in MSHR and writebuffer. + if (mshr) { + warn("Found outstanding miss on an non-update probe"); + MSHR::TargetList *targets = mshr->getTargetList(); + MSHR::TargetList::iterator i = targets->begin(); + MSHR::TargetList::iterator end = targets->end(); + for (; i != end; ++i) { + Packet * target = *i; + // If the target contains data, and it overlaps the + // probed request, need to update data + if (target->isWrite() && target->intersect(pkt)) { + uint8_t* pkt_data; + uint8_t* write_data; + int data_size; + if (target->getAddr() < pkt->getAddr()) { + int offset = pkt->getAddr() - target->getAddr(); + pkt_data = pkt->getPtr<uint8_t>(); + write_data = target->getPtr<uint8_t>() + offset; + data_size = target->getSize() - offset; + assert(data_size > 0); + if (data_size > pkt->getSize()) + data_size = pkt->getSize(); + } else { + int offset = target->getAddr() - pkt->getAddr(); + pkt_data = pkt->getPtr<uint8_t>() + offset; + write_data = target->getPtr<uint8_t>(); + data_size = pkt->getSize() - offset; + assert(data_size > pkt->getSize()); + if (data_size > target->getSize()) + data_size = target->getSize(); + } + + if (pkt->isWrite()) { + memcpy(pkt_data, write_data, data_size); + } else { + memcpy(write_data, pkt_data, data_size); + } + } + } + } + for (int i = 0; i < writes.size(); ++i) { + Packet * write = writes[i]->pkt; + if (write->intersect(pkt)) { + warn("Found outstanding write on an non-update probe"); + uint8_t* pkt_data; + uint8_t* write_data; + int data_size; + if (write->getAddr() < pkt->getAddr()) { + int offset = pkt->getAddr() - write->getAddr(); + pkt_data = pkt->getPtr<uint8_t>(); + write_data = write->getPtr<uint8_t>() + offset; + data_size = write->getSize() - offset; + assert(data_size > 0); + if (data_size > pkt->getSize()) + data_size = pkt->getSize(); + } else { + int offset = write->getAddr() - pkt->getAddr(); + pkt_data = pkt->getPtr<uint8_t>() + offset; + write_data = write->getPtr<uint8_t>(); + data_size = pkt->getSize() - offset; + assert(data_size > pkt->getSize()); + if (data_size > write->getSize()) + data_size = write->getSize(); + } + + if (pkt->isWrite()) { + memcpy(pkt_data, write_data, data_size); + } else { + memcpy(write_data, pkt_data, data_size); + } + + } + } + return 0; + } else { + // update the cache state and statistics + if (mshr || !writes.empty()){ + // Can't handle it, return pktuest unsatisfied. + return 0; + } + if (!pkt->req->isUncacheable()) { + // Fetch the cache block to fill + BlkType *blk = tags->findBlock(pkt); + Packet::Command temp_cmd = coherence->getBusCmd(pkt->cmd, + (blk)? blk->status : 0); + + Packet * busPkt = new Packet(pkt->req,temp_cmd, -1, blkSize); + + busPkt->allocate(); + + busPkt->time = curTick; + + lat = memSidePort->sendAtomic(busPkt); + +/* if (!(busPkt->flags & SATISFIED)) { + // blocked at a higher level, just return + return 0; + } + +*/ misses[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + + CacheBlk::State old_state = (blk) ? blk->status : 0; + tags->handleFill(blk, busPkt, + coherence->getNewState(busPkt, old_state), + writebacks, pkt); + // Handle writebacks if needed + while (!writebacks.empty()){ + memSidePort->sendAtomic(writebacks.front()); + writebacks.pop_front(); + } + return lat + hitLatency; + } else { + return memSidePort->sendAtomic(pkt); + } + } + } else { + // There was a cache hit. + // Handle writebacks if needed + while (!writebacks.empty()){ + memSidePort->sendAtomic(writebacks.front()); + writebacks.pop_front(); + } + + if (update) { + hits[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + } else if (pkt->isWrite()) { + // Still need to change data in all locations. + return memSidePort->sendAtomic(pkt); + } + return curTick + lat; + } + fatal("Probe not handled.\n"); + return 0; +} + +template<class TagStore, class Buffering, class Coherence> +Tick +Cache<TagStore,Buffering,Coherence>::snoopProbe(PacketPtr &pkt, bool update) +{ + Addr blk_addr = pkt->getAddr() & ~(Addr(blkSize-1)); + BlkType *blk = tags->findBlock(pkt); + MSHR *mshr = missQueue->findMSHR(blk_addr); + CacheBlk::State new_state = 0; + bool satisfy = coherence->handleBusRequest(pkt,blk,mshr, new_state); + if (satisfy) { + tags->handleSnoop(blk, new_state, pkt); + return hitLatency; + } + tags->handleSnoop(blk, new_state); + return 0; +} + diff --git a/src/mem/cache/coherence/coherence_protocol.cc b/src/mem/cache/coherence/coherence_protocol.cc new file mode 100644 index 000000000..bcf3ce9c5 --- /dev/null +++ b/src/mem/cache/coherence/coherence_protocol.cc @@ -0,0 +1,567 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Steve Reinhardt + * Ron Dreslinski + */ + +/** + * @file + * Definitions of CoherenceProtocol. + */ + +#include <string> + +#include "base/misc.hh" +#include "mem/cache/miss/mshr.hh" +#include "mem/cache/cache.hh" +#include "mem/cache/coherence/coherence_protocol.hh" +#include "sim/builder.hh" + +using namespace std; + + +CoherenceProtocol::StateTransition::StateTransition() + : busCmd(Packet::InvalidCmd), newState(-1), snoopFunc(invalidTransition) +{ +} + + +void +CoherenceProtocol::regStats() +{ + // Even though we count all the possible transitions in the + // requestCount and snoopCount arrays, most of these are invalid, + // so we just select the interesting ones to print here. + + requestCount[Invalid][Packet::ReadReq] + .name(name() + ".read_invalid") + .desc("read misses to invalid blocks") + ; + + requestCount[Invalid][Packet::WriteReq] + .name(name() +".write_invalid") + .desc("write misses to invalid blocks") + ; + + requestCount[Invalid][Packet::SoftPFReq] + .name(name() +".swpf_invalid") + .desc("soft prefetch misses to invalid blocks") + ; + + requestCount[Invalid][Packet::HardPFReq] + .name(name() +".hwpf_invalid") + .desc("hard prefetch misses to invalid blocks") + ; + + requestCount[Shared][Packet::WriteReq] + .name(name() + ".write_shared") + .desc("write misses to shared blocks") + ; + + requestCount[Owned][Packet::WriteReq] + .name(name() + ".write_owned") + .desc("write misses to owned blocks") + ; + + snoopCount[Shared][Packet::ReadReq] + .name(name() + ".snoop_read_shared") + .desc("read snoops on shared blocks") + ; + + snoopCount[Shared][Packet::ReadExReq] + .name(name() + ".snoop_readex_shared") + .desc("readEx snoops on shared blocks") + ; + + snoopCount[Shared][Packet::UpgradeReq] + .name(name() + ".snoop_upgrade_shared") + .desc("upgradee snoops on shared blocks") + ; + + snoopCount[Modified][Packet::ReadReq] + .name(name() + ".snoop_read_modified") + .desc("read snoops on modified blocks") + ; + + snoopCount[Modified][Packet::ReadExReq] + .name(name() + ".snoop_readex_modified") + .desc("readEx snoops on modified blocks") + ; + + snoopCount[Owned][Packet::ReadReq] + .name(name() + ".snoop_read_owned") + .desc("read snoops on owned blocks") + ; + + snoopCount[Owned][Packet::ReadExReq] + .name(name() + ".snoop_readex_owned") + .desc("readEx snoops on owned blocks") + ; + + snoopCount[Owned][Packet::UpgradeReq] + .name(name() + ".snoop_upgrade_owned") + .desc("upgrade snoops on owned blocks") + ; + + snoopCount[Exclusive][Packet::ReadReq] + .name(name() + ".snoop_read_exclusive") + .desc("read snoops on exclusive blocks") + ; + + snoopCount[Exclusive][Packet::ReadExReq] + .name(name() + ".snoop_readex_exclusive") + .desc("readEx snoops on exclusive blocks") + ; + + snoopCount[Shared][Packet::InvalidateReq] + .name(name() + ".snoop_inv_shared") + .desc("Invalidate snoops on shared blocks") + ; + + snoopCount[Owned][Packet::InvalidateReq] + .name(name() + ".snoop_inv_owned") + .desc("Invalidate snoops on owned blocks") + ; + + snoopCount[Exclusive][Packet::InvalidateReq] + .name(name() + ".snoop_inv_exclusive") + .desc("Invalidate snoops on exclusive blocks") + ; + + snoopCount[Modified][Packet::InvalidateReq] + .name(name() + ".snoop_inv_modified") + .desc("Invalidate snoops on modified blocks") + ; + + snoopCount[Invalid][Packet::InvalidateReq] + .name(name() + ".snoop_inv_invalid") + .desc("Invalidate snoops on invalid blocks") + ; + + snoopCount[Shared][Packet::WriteInvalidateReq] + .name(name() + ".snoop_writeinv_shared") + .desc("WriteInvalidate snoops on shared blocks") + ; + + snoopCount[Owned][Packet::WriteInvalidateReq] + .name(name() + ".snoop_writeinv_owned") + .desc("WriteInvalidate snoops on owned blocks") + ; + + snoopCount[Exclusive][Packet::WriteInvalidateReq] + .name(name() + ".snoop_writeinv_exclusive") + .desc("WriteInvalidate snoops on exclusive blocks") + ; + + snoopCount[Modified][Packet::WriteInvalidateReq] + .name(name() + ".snoop_writeinv_modified") + .desc("WriteInvalidate snoops on modified blocks") + ; + + snoopCount[Invalid][Packet::WriteInvalidateReq] + .name(name() + ".snoop_writeinv_invalid") + .desc("WriteInvalidate snoops on invalid blocks") + ; +} + + +bool +CoherenceProtocol::invalidateTrans(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, MSHR *mshr, + CacheBlk::State & new_state) +{ + // invalidate the block + new_state = (blk->status & ~stateMask) | Invalid; + return false; +} + + +bool +CoherenceProtocol::supplyTrans(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, + MSHR *mshr, + CacheBlk::State & new_state + ) +{ + return true; +} + + +bool +CoherenceProtocol::supplyAndGotoSharedTrans(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, + MSHR *mshr, + CacheBlk::State & new_state) +{ + new_state = (blk->status & ~stateMask) | Shared; + pkt->flags |= SHARED_LINE; + return supplyTrans(cache, pkt, blk, mshr, new_state); +} + + +bool +CoherenceProtocol::supplyAndGotoOwnedTrans(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, + MSHR *mshr, + CacheBlk::State & new_state) +{ + new_state = (blk->status & ~stateMask) | Owned; + pkt->flags |= SHARED_LINE; + return supplyTrans(cache, pkt, blk, mshr, new_state); +} + + +bool +CoherenceProtocol::supplyAndInvalidateTrans(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, + MSHR *mshr, + CacheBlk::State & new_state) +{ + new_state = (blk->status & ~stateMask) | Invalid; + return supplyTrans(cache, pkt, blk, mshr, new_state); +} + +bool +CoherenceProtocol::assertShared(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, + MSHR *mshr, + CacheBlk::State & new_state) +{ + new_state = (blk->status & ~stateMask) | Shared; + pkt->flags |= SHARED_LINE; + return false; +} + +CoherenceProtocol::CoherenceProtocol(const string &name, + const string &protocol, + const bool doUpgrades) + : SimObject(name) +{ + if ((protocol == "mosi" || protocol == "moesi") && !doUpgrades) { + cerr << "CoherenceProtocol: ownership protocols require upgrade transactions" + << "(write miss on owned block generates ReadExcl, which will clobber dirty block)" + << endl; + fatal(""); + } + + Packet::Command writeToSharedCmd = doUpgrades ? Packet::UpgradeReq : Packet::ReadExReq; + Packet::Command writeToSharedResp = doUpgrades ? Packet::UpgradeResp : Packet::ReadExResp; + +//@todo add in hardware prefetch to this list + if (protocol == "msi") { + // incoming requests: specify outgoing bus request + transitionTable[Invalid][Packet::ReadReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::WriteReq].onRequest(Packet::ReadExReq); + transitionTable[Shared][Packet::WriteReq].onRequest(writeToSharedCmd); + //Prefetching causes a read + transitionTable[Invalid][Packet::SoftPFReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::HardPFReq].onRequest(Packet::ReadReq); + + // on response to given request: specify new state + transitionTable[Invalid][Packet::ReadResp].onResponse(Shared); + transitionTable[Invalid][Packet::ReadExResp].onResponse(Modified); + transitionTable[Shared][writeToSharedResp].onResponse(Modified); + + // bus snoop transition functions + transitionTable[Invalid][Packet::ReadReq].onSnoop(nullTransition); + transitionTable[Invalid][Packet::ReadExReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::ReadReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::ReadExReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::ReadExReq].onSnoop(supplyAndInvalidateTrans); + transitionTable[Modified][Packet::ReadReq].onSnoop(supplyAndGotoSharedTrans); + //Tansitions on seeing a DMA (writeInv(samelevel) or DMAInv) + transitionTable[Invalid][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Invalid][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + + if (doUpgrades) { + transitionTable[Invalid][Packet::UpgradeReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::UpgradeReq].onSnoop(invalidateTrans); + } + } + + else if(protocol == "mesi") { + // incoming requests: specify outgoing bus request + transitionTable[Invalid][Packet::ReadReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::WriteReq].onRequest(Packet::ReadExReq); + transitionTable[Shared][Packet::WriteReq].onRequest(writeToSharedCmd); + //Prefetching causes a read + transitionTable[Invalid][Packet::SoftPFReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::HardPFReq].onRequest(Packet::ReadReq); + + // on response to given request: specify new state + transitionTable[Invalid][Packet::ReadResp].onResponse(Exclusive); + //It will move into shared if the shared line is asserted in the + //getNewState function + transitionTable[Invalid][Packet::ReadExResp].onResponse(Modified); + transitionTable[Shared][writeToSharedResp].onResponse(Modified); + + // bus snoop transition functions + transitionTable[Invalid][Packet::ReadReq].onSnoop(nullTransition); + transitionTable[Invalid][Packet::ReadExReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::ReadReq].onSnoop(assertShared); + transitionTable[Shared][Packet::ReadExReq].onSnoop(invalidateTrans); + transitionTable[Exclusive][Packet::ReadReq].onSnoop(assertShared); + transitionTable[Exclusive][Packet::ReadExReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::ReadExReq].onSnoop(supplyAndInvalidateTrans); + transitionTable[Modified][Packet::ReadReq].onSnoop(supplyAndGotoSharedTrans); + //Tansitions on seeing a DMA (writeInv(samelevel) or DMAInv) + transitionTable[Invalid][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Exclusive][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Invalid][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Exclusive][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + + if (doUpgrades) { + transitionTable[Invalid][Packet::UpgradeReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::UpgradeReq].onSnoop(invalidateTrans); + } + } + + else if(protocol == "mosi") { + // incoming requests: specify outgoing bus request + transitionTable[Invalid][Packet::ReadReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::WriteReq].onRequest(Packet::ReadExReq); + transitionTable[Shared][Packet::WriteReq].onRequest(writeToSharedCmd); + transitionTable[Owned][Packet::WriteReq].onRequest(writeToSharedCmd); + //Prefetching causes a read + transitionTable[Invalid][Packet::SoftPFReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::HardPFReq].onRequest(Packet::ReadReq); + + // on response to given request: specify new state + transitionTable[Invalid][Packet::ReadResp].onResponse(Shared); + transitionTable[Invalid][Packet::ReadExResp].onResponse(Modified); + transitionTable[Shared][writeToSharedResp].onResponse(Modified); + transitionTable[Owned][writeToSharedResp].onResponse(Modified); + + // bus snoop transition functions + transitionTable[Invalid][Packet::ReadReq].onSnoop(nullTransition); + transitionTable[Invalid][Packet::ReadExReq].onSnoop(nullTransition); + transitionTable[Invalid][Packet::UpgradeReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::ReadReq].onSnoop(assertShared); + transitionTable[Shared][Packet::ReadExReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::UpgradeReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::ReadExReq].onSnoop(supplyAndInvalidateTrans); + transitionTable[Modified][Packet::ReadReq].onSnoop(supplyAndGotoOwnedTrans); + transitionTable[Owned][Packet::ReadReq].onSnoop(supplyAndGotoOwnedTrans); + transitionTable[Owned][Packet::ReadExReq].onSnoop(supplyAndInvalidateTrans); + transitionTable[Owned][Packet::UpgradeReq].onSnoop(invalidateTrans); + //Tansitions on seeing a DMA (writeInv(samelevel) or DMAInv) + transitionTable[Invalid][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Owned][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Invalid][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Owned][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + } + + else if(protocol == "moesi") { + // incoming requests: specify outgoing bus request + transitionTable[Invalid][Packet::ReadReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::WriteReq].onRequest(Packet::ReadExReq); + transitionTable[Shared][Packet::WriteReq].onRequest(writeToSharedCmd); + transitionTable[Owned][Packet::WriteReq].onRequest(writeToSharedCmd); + //Prefetching causes a read + transitionTable[Invalid][Packet::SoftPFReq].onRequest(Packet::ReadReq); + transitionTable[Invalid][Packet::HardPFReq].onRequest(Packet::ReadReq); + + // on response to given request: specify new state + transitionTable[Invalid][Packet::ReadResp].onResponse(Exclusive); + //It will move into shared if the shared line is asserted in the + //getNewState function + transitionTable[Invalid][Packet::ReadExResp].onResponse(Modified); + transitionTable[Shared][writeToSharedResp].onResponse(Modified); + transitionTable[Owned][writeToSharedResp].onResponse(Modified); + + // bus snoop transition functions + transitionTable[Invalid][Packet::ReadReq].onSnoop(nullTransition); + transitionTable[Invalid][Packet::ReadExReq].onSnoop(nullTransition); + transitionTable[Invalid][Packet::UpgradeReq].onSnoop(nullTransition); + transitionTable[Shared][Packet::ReadReq].onSnoop(assertShared); + transitionTable[Shared][Packet::ReadExReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::UpgradeReq].onSnoop(invalidateTrans); + transitionTable[Exclusive][Packet::ReadReq].onSnoop(assertShared); + transitionTable[Exclusive][Packet::ReadExReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::ReadReq].onSnoop(supplyAndGotoOwnedTrans); + transitionTable[Modified][Packet::ReadExReq].onSnoop(supplyAndInvalidateTrans); + transitionTable[Owned][Packet::ReadReq].onSnoop(supplyAndGotoOwnedTrans); + transitionTable[Owned][Packet::ReadExReq].onSnoop(supplyAndInvalidateTrans); + transitionTable[Owned][Packet::UpgradeReq].onSnoop(invalidateTrans); + //Transitions on seeing a DMA (writeInv(samelevel) or DMAInv) + transitionTable[Invalid][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Exclusive][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Owned][Packet::InvalidateReq].onSnoop(invalidateTrans); + transitionTable[Invalid][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Shared][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Exclusive][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Modified][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + transitionTable[Owned][Packet::WriteInvalidateReq].onSnoop(invalidateTrans); + } + + else { + cerr << "CoherenceProtocol: unrecognized protocol " << protocol + << endl; + fatal(""); + } +} + + +Packet::Command +CoherenceProtocol::getBusCmd(Packet::Command cmdIn, CacheBlk::State state, + MSHR *mshr) +{ + state &= stateMask; + int cmd_idx = (int) cmdIn; + + assert(0 <= state && state <= stateMax); + assert(0 <= cmd_idx && cmd_idx < NUM_MEM_CMDS); + + Packet::Command cmdOut = transitionTable[state][cmd_idx].busCmd; + + assert(cmdOut != Packet::InvalidCmd); + + ++requestCount[state][cmd_idx]; + + return cmdOut; +} + + +CacheBlk::State +CoherenceProtocol::getNewState(Packet * &pkt, CacheBlk::State oldState) +{ + CacheBlk::State state = oldState & stateMask; + int cmd_idx = pkt->cmdToIndex(); + + assert(0 <= state && state <= stateMax); + assert(0 <= cmd_idx && cmd_idx < NUM_MEM_CMDS); + + CacheBlk::State newState = transitionTable[state][cmd_idx].newState; + + //Check if it's exclusive and the shared line was asserted, + //then goto shared instead + if (newState == Exclusive && (pkt->flags & SHARED_LINE)) { + newState = Shared; + } + + assert(newState != -1); + + //Make sure not to loose any other state information + newState = (oldState & ~stateMask) | newState; + return newState; +} + + +bool +CoherenceProtocol::handleBusRequest(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, + MSHR *mshr, + CacheBlk::State & new_state) +{ + if (blk == NULL) { + // nothing to do if we don't have a block + return false; + } + + CacheBlk::State state = blk->status & stateMask; + int cmd_idx = pkt->cmdToIndex(); + + assert(0 <= state && state <= stateMax); + assert(0 <= cmd_idx && cmd_idx < NUM_MEM_CMDS); + +// assert(mshr == NULL); // can't currently handle outstanding requests + //Check first if MSHR, and also insure, if there is one, that it is not in service + assert(!mshr || mshr->inService == 0); + ++snoopCount[state][cmd_idx]; + + bool ret = transitionTable[state][cmd_idx].snoopFunc(cache, pkt, blk, mshr, + new_state); + + + + return ret; +} + +bool +CoherenceProtocol::nullTransition(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, MSHR *mshr, + CacheBlk::State & new_state) +{ + // do nothing + if (blk) + new_state = blk->status; + return false; +} + + +bool +CoherenceProtocol::invalidTransition(BaseCache *cache, Packet * &pkt, + CacheBlk *blk, MSHR *mshr, + CacheBlk::State & new_state) +{ + panic("Invalid transition"); + return false; +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(CoherenceProtocol) + + Param<string> protocol; + Param<bool> do_upgrades; + +END_DECLARE_SIM_OBJECT_PARAMS(CoherenceProtocol) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(CoherenceProtocol) + + INIT_PARAM(protocol, "name of coherence protocol"), + INIT_PARAM_DFLT(do_upgrades, "use upgrade transactions?", true) + +END_INIT_SIM_OBJECT_PARAMS(CoherenceProtocol) + + +CREATE_SIM_OBJECT(CoherenceProtocol) +{ + return new CoherenceProtocol(getInstanceName(), protocol, + do_upgrades); +} + +REGISTER_SIM_OBJECT("CoherenceProtocol", CoherenceProtocol) + +#endif // DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/coherence/coherence_protocol.hh b/src/mem/cache/coherence/coherence_protocol.hh new file mode 100644 index 000000000..b5d7d80aa --- /dev/null +++ b/src/mem/cache/coherence/coherence_protocol.hh @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Ron Dreslinski + * Steve Reinhardt + */ + +/** + * @file + * Declaration of CoherenceProcotol a basic coherence policy. + */ +#ifndef __COHERENCE_PROTOCOL_HH__ +#define __COHERENCE_PROTOCOL_HH__ + +#include <string> + +#include "sim/sim_object.hh" +#include "mem/packet.hh" +#include "mem/cache/cache_blk.hh" +#include "base/statistics.hh" + +class BaseCache; +class MSHR; + +/** + * A simple coherence policy for the memory hierarchy. Currently implements + * MSI, MESI, and MOESI protocols. + */ +class CoherenceProtocol : public SimObject +{ + public: + /** + * Contruct and initialize this policy. + * @param name The name of this policy. + * @param protocol The string representation of the protocol to use. + * @param doUpgrades True if bus upgrades should be used. + */ + CoherenceProtocol(const std::string &name, const std::string &protocol, + const bool doUpgrades); + + /** + * Destructor. + */ + virtual ~CoherenceProtocol() {}; + + /** + * Register statistics + */ + virtual void regStats(); + + /** + * Get the proper bus command for the given command and status. + * @param cmd The request's command. + * @param status The current state of the cache block. + * @param mshr The MSHR matching the request. + * @return The proper bus command, as determined by the protocol. + */ + Packet::Command getBusCmd(Packet::Command cmd, CacheBlk::State status, + MSHR *mshr = NULL); + + /** + * Return the proper state given the current state and the bus response. + * @param pkt The bus response. + * @param oldState The current block state. + * @return The new state. + */ + CacheBlk::State getNewState(Packet * &pkt, + CacheBlk::State oldState); + + /** + * Handle snooped bus requests. + * @param cache The cache that snooped the request. + * @param pkt The snooped bus request. + * @param blk The cache block corresponding to the request, if any. + * @param mshr The MSHR corresponding to the request, if any. + * @param new_state The new coherence state of the block. + * @return True if the request should be satisfied locally. + */ + bool handleBusRequest(BaseCache *cache, Packet * &pkt, CacheBlk *blk, + MSHR *mshr, CacheBlk::State &new_state); + + protected: + /** Snoop function type. */ + typedef bool (*SnoopFuncType)(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + // + // Standard snoop transition functions + // + + /** + * Do nothing transition. + */ + static bool nullTransition(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Invalid transition, basically panic. + */ + static bool invalidTransition(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Invalidate block, move to Invalid state. + */ + static bool invalidateTrans(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Supply data, no state transition. + */ + static bool supplyTrans(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Supply data and go to Shared state. + */ + static bool supplyAndGotoSharedTrans(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Supply data and go to Owned state. + */ + static bool supplyAndGotoOwnedTrans(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Invalidate block, supply data, and go to Invalid state. + */ + static bool supplyAndInvalidateTrans(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Assert the shared line for a block that is shared/exclusive. + */ + static bool assertShared(BaseCache *, Packet *&, CacheBlk *, + MSHR *, CacheBlk::State&); + + /** + * Definition of protocol state transitions. + */ + class StateTransition + { + friend class CoherenceProtocol; + + /** The bus command of this transition. */ + Packet::Command busCmd; + /** The state to transition to. */ + int newState; + /** The snoop function for this transition. */ + SnoopFuncType snoopFunc; + + /** + * Constructor, defaults to invalid transition. + */ + StateTransition(); + + /** + * Initialize bus command. + * @param cmd The bus command to use. + */ + void onRequest(Packet::Command cmd) + { + busCmd = cmd; + } + + /** + * Set the transition state. + * @param s The new state. + */ + void onResponse(CacheBlk::State s) + { + newState = s; + } + + /** + * Initialize the snoop function. + * @param f The new snoop function. + */ + void onSnoop(SnoopFuncType f) + { + snoopFunc = f; + } + }; + + friend class CoherenceProtocol::StateTransition; + + /** Mask to select status bits relevant to coherence protocol. */ + const static CacheBlk::State + stateMask = BlkValid | BlkWritable | BlkDirty; + + /** The Modified (M) state. */ + const static CacheBlk::State + Modified = BlkValid | BlkWritable | BlkDirty; + /** The Owned (O) state. */ + const static CacheBlk::State + Owned = BlkValid | BlkDirty; + /** The Exclusive (E) state. */ + const static CacheBlk::State + Exclusive = BlkValid | BlkWritable; + /** The Shared (S) state. */ + const static CacheBlk::State + Shared = BlkValid; + /** The Invalid (I) state. */ + const static CacheBlk::State + Invalid = 0; + + /** + * Maximum state encoding value (used to size transition lookup + * table). Could be more than number of states, depends on + * encoding of status bits. + */ + const static int stateMax = stateMask; + + /** + * The table of all possible transitions, organized by starting state and + * request command. + */ + StateTransition transitionTable[stateMax+1][NUM_MEM_CMDS]; + + /** + * @addtogroup CoherenceStatistics + * @{ + */ + /** + * State accesses from parent cache. + */ + Stats::Scalar<> requestCount[stateMax+1][NUM_MEM_CMDS]; + /** + * State accesses from snooped requests. + */ + Stats::Scalar<> snoopCount[stateMax+1][NUM_MEM_CMDS]; + /** + * @} + */ +}; + +#endif // __COHERENCE_PROTOCOL_HH__ diff --git a/src/mem/cache/coherence/simple_coherence.hh b/src/mem/cache/coherence/simple_coherence.hh new file mode 100644 index 000000000..71d8f36f4 --- /dev/null +++ b/src/mem/cache/coherence/simple_coherence.hh @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Ron Dreslinski + */ + +/** + * @file + * Declaration of a simple coherence policy. + */ + +#ifndef __SIMPLE_COHERENCE_HH__ +#define __SIMPLE_COHERENCE_HH__ + +#include <string> + +#include "mem/packet.hh" +#include "mem/cache/cache_blk.hh" +#include "mem/cache/miss/mshr_queue.hh" +#include "mem/cache/coherence/coherence_protocol.hh" + +class BaseCache; + +/** + * A simple MP coherence policy. This policy assumes an atomic bus and only one + * level of cache. + */ +class SimpleCoherence +{ + protected: + /** Pointer to the parent cache. */ + BaseCache *cache; + /** Pointer to the coherence protocol. */ + CoherenceProtocol *protocol; + + public: + /** + * Construct and initialize this coherence policy. + * @param _protocol The coherence protocol to use. + */ + SimpleCoherence(CoherenceProtocol *_protocol) + : protocol(_protocol) + { + } + + /** + * Set the pointer to the parent cache. + * @param _cache The parent cache. + */ + void setCache(BaseCache *_cache) + { + cache = _cache; + } + + /** + * Register statistics. + * @param name The name to prepend to stat descriptions. + */ + void regStats(const std::string &name) + { + } + + /** + * This policy does not forward invalidates, return NULL. + * @return NULL. + */ + Packet * getPacket() + { + return NULL; + } + + /** + * Return the proper state given the current state and the bus response. + * @param pkt The bus response. + * @param current The current block state. + * @return The new state. + */ + CacheBlk::State getNewState(Packet * &pkt, CacheBlk::State current) + { + return protocol->getNewState(pkt, current); + } + + /** + * Handle snooped bus requests. + * @param pkt The snooped bus request. + * @param blk The cache block corresponding to the request, if any. + * @param mshr The MSHR corresponding to the request, if any. + * @param new_state Return the new state for the block. + */ + bool handleBusRequest(Packet * &pkt, CacheBlk *blk, MSHR *mshr, + CacheBlk::State &new_state) + { +// assert(mshr == NULL); +//Got rid of, there could be an MSHR, but it can't be in service + if (blk != NULL) + { + if (pkt->cmd != Packet::Writeback) { + return protocol->handleBusRequest(cache, pkt, blk, mshr, + new_state); + } + else { //It is a writeback, must be ownership protocol, just keep state + new_state = blk->status; + } + } + return false; + } + + /** + * Get the proper bus command for the given command and status. + * @param cmd The request's command. + * @param state The current state of the cache block. + * @return The proper bus command, as determined by the protocol. + */ + Packet::Command getBusCmd(Packet::Command &cmd, CacheBlk::State state) + { + if (cmd == Packet::Writeback) return Packet::Writeback; + return protocol->getBusCmd(cmd, state); + } + + /** + * Return true if this coherence policy can handle fast cache writes. + */ + bool allowFastWrites() { return false; } + + bool hasProtocol() { return true; } +}; + +#endif //__SIMPLE_COHERENCE_HH__ + + + + + + + + diff --git a/src/mem/cache/coherence/uni_coherence.cc b/src/mem/cache/coherence/uni_coherence.cc new file mode 100644 index 000000000..5ab706269 --- /dev/null +++ b/src/mem/cache/coherence/uni_coherence.cc @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +#include "mem/cache/coherence/uni_coherence.hh" +#include "mem/cache/base_cache.hh" + +#include "base/trace.hh" + +using namespace std; + +UniCoherence::UniCoherence() + : cshrs(50) +{ +} + +Packet * +UniCoherence::getPacket() +{ + bool unblock = cshrs.isFull(); + Packet* pkt = cshrs.getReq(); + cshrs.markInService((MSHR*)pkt->senderState); + if (!cshrs.havePending()) { + cache->clearSlaveRequest(Request_Coherence); + } + if (unblock) { + //since CSHRs are always used as buffers, should always get rid of one + assert(!cshrs.isFull()); + cache->clearBlocked(Blocked_Coherence); + } + return pkt; +} + +/** + * @todo add support for returning slave requests, not doing them here. + */ +bool +UniCoherence::handleBusRequest(Packet * &pkt, CacheBlk *blk, MSHR *mshr, + CacheBlk::State &new_state) +{ + new_state = 0; + if (pkt->isInvalidate()) { + DPRINTF(Cache, "snoop inval on blk %x (blk ptr %x)\n", + pkt->getAddr(), blk); + if (!cache->isTopLevel()) { + // Forward to other caches + Packet * tmp = new Packet(pkt->req, Packet::InvalidateReq, -1); + cshrs.allocate(tmp); + cache->setSlaveRequest(Request_Coherence, curTick); + if (cshrs.isFull()) { + cache->setBlockedForSnoop(Blocked_Coherence); + } + } + } else { + if (blk) { + new_state = blk->status; + } + } + return false; +} diff --git a/src/mem/cache/coherence/uni_coherence.hh b/src/mem/cache/coherence/uni_coherence.hh new file mode 100644 index 000000000..27b6c7fb5 --- /dev/null +++ b/src/mem/cache/coherence/uni_coherence.hh @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +#ifndef __UNI_COHERENCE_HH__ +#define __UNI_COHERENCE_HH__ + +#include "base/trace.hh" +#include "base/misc.hh" +#include "mem/cache/cache_blk.hh" +#include "mem/cache/miss/mshr_queue.hh" +#include "mem/packet.hh" + +class BaseCache; + +class UniCoherence +{ + protected: + /** Buffers to hold forwarded invalidates. */ + MSHRQueue cshrs; + /** Pointer to the parent cache. */ + BaseCache *cache; + + public: + /** + * Construct and initialize this coherence policy. + */ + UniCoherence(); + + /** + * Set the pointer to the parent cache. + * @param _cache The parent cache. + */ + void setCache(BaseCache *_cache) + { + cache = _cache; + } + + /** + * Register statistics. + * @param name The name to prepend to stat descriptions. + */ + void regStats(const std::string &name) + { + } + + /** + * Return Read. + * @param cmd The request's command. + * @param state The current state of the cache block. + * @return The proper bus command, as determined by the protocol. + * @todo Make changes so writebacks don't get here. + */ + Packet::Command getBusCmd(Packet::Command &cmd, CacheBlk::State state) + { + if (cmd == Packet::HardPFReq && state) + warn("Trying to issue a prefetch to a block we already have\n"); + if (cmd == Packet::Writeback) + return Packet::Writeback; + return Packet::ReadReq; + } + + /** + * Just return readable and writeable. + * @param pkt The bus response. + * @param current The current block state. + * @return The new state. + */ + CacheBlk::State getNewState(Packet * &pkt, CacheBlk::State current) + { + if (pkt->senderState) //Blocking Buffers don't get mshrs + { + if (((MSHR *)(pkt->senderState))->originalCmd == Packet::HardPFReq) { + DPRINTF(HWPrefetch, "Marking a hardware prefetch as such in the state\n"); + return BlkHWPrefetched | BlkValid | BlkWritable; + } + else { + return BlkValid | BlkWritable; + } + } + //@todo What about prefetching with blocking buffers + else + return BlkValid | BlkWritable; + } + /** + * Return outstanding invalidate to forward. + * @return The next invalidate to forward to lower levels of cache. + */ + Packet * getPacket(); + + /** + * Handle snooped bus requests. + * @param pkt The snooped bus request. + * @param blk The cache block corresponding to the request, if any. + * @param mshr The MSHR corresponding to the request, if any. + * @param new_state The new coherence state of the block. + * @return True if the request should be satisfied locally. + */ + bool handleBusRequest(Packet * &pkt, CacheBlk *blk, MSHR *mshr, + CacheBlk::State &new_state); + + /** + * Return true if this coherence policy can handle fast cache writes. + */ + bool allowFastWrites() { return true; } + + bool hasProtocol() { return false; } +}; + +#endif //__UNI_COHERENCE_HH__ diff --git a/src/mem/cache/miss/blocking_buffer.cc b/src/mem/cache/miss/blocking_buffer.cc new file mode 100644 index 000000000..67fc7ae56 --- /dev/null +++ b/src/mem/cache/miss/blocking_buffer.cc @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definitions of a simple buffer for a blocking cache. + */ + +#include "cpu/smt.hh" //for maxThreadsPerCPU +#include "mem/cache/base_cache.hh" +#include "mem/cache/miss/blocking_buffer.hh" +#include "mem/cache/prefetch/base_prefetcher.hh" +#include "sim/eventq.hh" // for Event declaration. +#include "mem/request.hh" + +/** + * @todo Move writebacks into shared BaseBuffer class. + */ +void +BlockingBuffer::regStats(const std::string &name) +{ + using namespace Stats; + writebacks + .init(maxThreadsPerCPU) + .name(name + ".writebacks") + .desc("number of writebacks") + .flags(total) + ; +} + +void +BlockingBuffer::setCache(BaseCache *_cache) +{ + cache = _cache; + blkSize = cache->getBlockSize(); +} + +void +BlockingBuffer::setPrefetcher(BasePrefetcher *_prefetcher) +{ + prefetcher = _prefetcher; +} +void +BlockingBuffer::handleMiss(Packet * &pkt, int blk_size, Tick time) +{ + Addr blk_addr = pkt->getAddr() & ~(Addr)(blk_size - 1); + if (pkt->isWrite() && (pkt->req->isUncacheable() || !writeAllocate || + !pkt->needsResponse())) { + if (!pkt->needsResponse()) { + wb.allocateAsBuffer(pkt); + } else { + wb.allocate(pkt->cmd, blk_addr, blk_size, pkt); + } + + memcpy(wb.pkt->getPtr<uint8_t>(), pkt->getPtr<uint8_t>(), blk_size); + + cache->setBlocked(Blocked_NoWBBuffers); + cache->setMasterRequest(Request_WB, time); + return; + } + + if (!pkt->needsResponse()) { + miss.allocateAsBuffer(pkt); + } else { + miss.allocate(pkt->cmd, blk_addr, blk_size, pkt); + } + if (!pkt->req->isUncacheable()) { + miss.pkt->flags |= CACHE_LINE_FILL; + } + cache->setBlocked(Blocked_NoMSHRs); + cache->setMasterRequest(Request_MSHR, time); +} + +Packet * +BlockingBuffer::getPacket() +{ + if (miss.pkt && !miss.inService) { + return miss.pkt; + } + return wb.pkt; +} + +void +BlockingBuffer::setBusCmd(Packet * &pkt, Packet::Command cmd) +{ + MSHR *mshr = (MSHR*) pkt->senderState; + mshr->originalCmd = pkt->cmd; + if (pkt->isCacheFill()) + pkt->cmdOverride(cmd); +} + +void +BlockingBuffer::restoreOrigCmd(Packet * &pkt) +{ + pkt->cmdOverride(((MSHR*)(pkt->senderState))->originalCmd); +} + +void +BlockingBuffer::markInService(Packet * &pkt) +{ + if (!pkt->isCacheFill() && pkt->isWrite()) { + // Forwarding a write/ writeback, don't need to change + // the command + assert((MSHR*)pkt->senderState == &wb); + cache->clearMasterRequest(Request_WB); + if (!pkt->needsResponse()) { + assert(wb.getNumTargets() == 0); + wb.deallocate(); + cache->clearBlocked(Blocked_NoWBBuffers); + } else { + wb.inService = true; + } + } else { + assert((MSHR*)pkt->senderState == &miss); + cache->clearMasterRequest(Request_MSHR); + if (!pkt->needsResponse()) { + assert(miss.getNumTargets() == 0); + miss.deallocate(); + cache->clearBlocked(Blocked_NoMSHRs); + } else { + //mark in service + miss.inService = true; + } + } +} + +void +BlockingBuffer::handleResponse(Packet * &pkt, Tick time) +{ + if (pkt->isCacheFill()) { + // targets were handled in the cache tags + assert((MSHR*)pkt->senderState == &miss); + miss.deallocate(); + cache->clearBlocked(Blocked_NoMSHRs); + } else { + if (((MSHR*)(pkt->senderState))->hasTargets()) { + // Should only have 1 target if we had any + assert(((MSHR*)(pkt->senderState))->getNumTargets() == 1); + Packet * target = ((MSHR*)(pkt->senderState))->getTarget(); + ((MSHR*)(pkt->senderState))->popTarget(); + if (pkt->isRead()) { + memcpy(target->getPtr<uint8_t>(), pkt->getPtr<uint8_t>(), target->getSize()); + } + cache->respond(target, time); + assert(!((MSHR*)(pkt->senderState))->hasTargets()); + } + + if (pkt->isWrite()) { + assert(((MSHR*)(pkt->senderState)) == &wb); + wb.deallocate(); + cache->clearBlocked(Blocked_NoWBBuffers); + } else { + miss.deallocate(); + cache->clearBlocked(Blocked_NoMSHRs); + } + } +} + +void +BlockingBuffer::squash(int threadNum) +{ + if (miss.threadNum == threadNum) { + Packet * target = miss.getTarget(); + miss.popTarget(); + assert(target->req->getThreadNum() == threadNum); + target = NULL; + assert(!miss.hasTargets()); + miss.ntargets=0; + if (!miss.inService) { + miss.deallocate(); + cache->clearBlocked(Blocked_NoMSHRs); + cache->clearMasterRequest(Request_MSHR); + } + } +} + +void +BlockingBuffer::doWriteback(Addr addr, + int size, uint8_t *data, bool compressed) +{ + // Generate request + Request * req = new Request(addr, size, 0); + Packet * pkt = new Packet(req, Packet::Writeback, -1); + pkt->allocate(); + if (data) { + memcpy(pkt->getPtr<uint8_t>(), data, size); + } + + if (compressed) { + pkt->flags |= COMPRESSED; + } + + ///All writebacks charged to same thread @todo figure this out + writebacks[pkt->req->getThreadNum()]++; + + wb.allocateAsBuffer(pkt); + cache->setMasterRequest(Request_WB, curTick); + cache->setBlocked(Blocked_NoWBBuffers); +} + + + +void +BlockingBuffer::doWriteback(Packet * &pkt) +{ + writebacks[pkt->req->getThreadNum()]++; + + wb.allocateAsBuffer(pkt); + + // Since allocate as buffer copies the request, + // need to copy data here. + memcpy(wb.pkt->getPtr<uint8_t>(), pkt->getPtr<uint8_t>(), pkt->getSize()); + + cache->setBlocked(Blocked_NoWBBuffers); + cache->setMasterRequest(Request_WB, curTick); +} diff --git a/src/mem/cache/miss/blocking_buffer.hh b/src/mem/cache/miss/blocking_buffer.hh new file mode 100644 index 000000000..641d5a798 --- /dev/null +++ b/src/mem/cache/miss/blocking_buffer.hh @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of a simple buffer for a blocking cache. + */ + +#ifndef __BLOCKING_BUFFER_HH__ +#define __BLOCKING_BUFFER_HH__ + +#include <vector> + +#include "mem/cache/miss/mshr.hh" +#include "base/statistics.hh" + +class BaseCache; +class BasePrefetcher; + +/** + * Miss and writeback storage for a blocking cache. + */ +class BlockingBuffer +{ +protected: + /** Miss storage. */ + MSHR miss; + /** WB storage. */ + MSHR wb; + + //Params + + /** Allocate on write misses. */ + const bool writeAllocate; + + /** Pointer to the parent cache. */ + BaseCache* cache; + + BasePrefetcher* prefetcher; + + /** Block size of the parent cache. */ + int blkSize; + + // Statistics + /** + * @addtogroup CacheStatistics + * @{ + */ + /** Number of blocks written back per thread. */ + Stats::Vector<> writebacks; + + /** + * @} + */ + +public: + /** + * Builds and initializes this buffer. + * @param write_allocate If true, treat write misses the same as reads. + */ + BlockingBuffer(bool write_allocate) + : writeAllocate(write_allocate) + { + } + + /** + * Register statistics for this object. + * @param name The name of the parent cache. + */ + void regStats(const std::string &name); + + /** + * Called by the parent cache to set the back pointer. + * @param _cache A pointer to the parent cache. + */ + void setCache(BaseCache *_cache); + + void setPrefetcher(BasePrefetcher *_prefetcher); + + /** + * Handle a cache miss properly. Requests the bus and marks the cache as + * blocked. + * @param pkt The request that missed in the cache. + * @param blk_size The block size of the cache. + * @param time The time the miss is detected. + */ + void handleMiss(Packet * &pkt, int blk_size, Tick time); + + /** + * Fetch the block for the given address and buffer the given target. + * @param addr The address to fetch. + * @param asid The address space of the address. + * @param blk_size The block size of the cache. + * @param time The time the miss is detected. + * @param target The target for the fetch. + */ + MSHR* fetchBlock(Addr addr, int blk_size, Tick time, + Packet * &target) + { + fatal("Unimplemented"); + } + + /** + * Selects a outstanding pktuest to service. + * @return The pktuest to service, NULL if none found. + */ + Packet * getPacket(); + + /** + * Set the command to the given bus command. + * @param pkt The request to update. + * @param cmd The bus command to use. + */ + void setBusCmd(Packet * &pkt, Packet::Command cmd); + + /** + * Restore the original command in case of a bus transmission error. + * @param pkt The request to reset. + */ + void restoreOrigCmd(Packet * &pkt); + + /** + * Marks a pktuest as in service (sent on the bus). This can have side + * effect since storage for no response commands is deallocated once they + * are successfully sent. + * @param pkt The request that was sent on the bus. + */ + void markInService(Packet * &pkt); + + /** + * Frees the resources of the pktuest and unblock the cache. + * @param pkt The request that has been satisfied. + * @param time The time when the pktuest is satisfied. + */ + void handleResponse(Packet * &pkt, Tick time); + + /** + * Removes all outstanding pktuests for a given thread number. If a request + * has been sent to the bus, this function removes all of its targets. + * @param threadNum The thread number of the requests to squash. + */ + void squash(int threadNum); + + /** + * Return the current number of outstanding misses. + * @return the number of outstanding misses. + */ + int getMisses() + { + return miss.getNumTargets(); + } + + /** + * Searches for the supplied address in the miss "queue". + * @param addr The address to look for. + * @param asid The address space id. + * @return A pointer to miss if it matches. + */ + MSHR* findMSHR(Addr addr) + { + if (miss.addr == addr && miss.pkt) + return &miss; + return NULL; + } + + /** + * Searches for the supplied address in the write buffer. + * @param addr The address to look for. + * @param asid The address space id. + * @param writes List of pointers to the matching writes. + * @return True if there is a matching write. + */ + bool findWrites(Addr addr, std::vector<MSHR*>& writes) + { + if (wb.addr == addr && wb.pkt) { + writes.push_back(&wb); + return true; + } + return false; + } + + + + /** + * Perform a writeback of dirty data to the given address. + * @param addr The address to write to. + * @param asid The address space id. + * @param size The number of bytes to write. + * @param data The data to write, can be NULL. + * @param compressed True if the data is compressed. + */ + void doWriteback(Addr addr, + int size, uint8_t *data, bool compressed); + + /** + * Perform a writeback pktuest. + * @param pkt The writeback request. + */ + void doWriteback(Packet * &pkt); + + /** + * Returns true if there are outstanding pktuests. + * @return True if there are outstanding pktuests. + */ + bool havePending() + { + return !miss.inService || !wb.inService; + } + + /** + * Add a target to the given MSHR. This assumes it is in the miss queue. + * @param mshr The mshr to add a target to. + * @param pkt The target to add. + */ + void addTarget(MSHR *mshr, Packet * &pkt) + { + fatal("Shouldn't call this on a blocking buffer."); + } + + /** + * Dummy implmentation. + */ + MSHR* allocateTargetList(Addr addr) + { + fatal("Unimplemented"); + } +}; + +#endif // __BLOCKING_BUFFER_HH__ diff --git a/src/mem/cache/miss/miss_queue.cc b/src/mem/cache/miss/miss_queue.cc new file mode 100644 index 000000000..76fb25716 --- /dev/null +++ b/src/mem/cache/miss/miss_queue.cc @@ -0,0 +1,757 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Ron Dreslinski + */ + +/** + * @file + * Miss and writeback queue definitions. + */ + +#include "cpu/smt.hh" //for maxThreadsPerCPU +#include "mem/cache/base_cache.hh" +#include "mem/cache/miss/miss_queue.hh" +#include "mem/cache/prefetch/base_prefetcher.hh" + +using namespace std; + +// simple constructor +/** + * @todo Remove the +16 from the write buffer constructor once we handle + * stalling on writebacks do to compression writes. + */ +MissQueue::MissQueue(int numMSHRs, int numTargets, int write_buffers, + bool write_allocate, bool prefetch_miss) + : mq(numMSHRs, 4), wb(write_buffers,numMSHRs+1000), numMSHR(numMSHRs), + numTarget(numTargets), writeBuffers(write_buffers), + writeAllocate(write_allocate), order(0), prefetchMiss(prefetch_miss) +{ + noTargetMSHR = NULL; +} + +void +MissQueue::regStats(const string &name) +{ + Request temp_req((Addr) NULL, 4, 0); + Packet::Command temp_cmd = Packet::ReadReq; + Packet temp_pkt(&temp_req, temp_cmd, 0); //@todo FIx command strings so this isn't neccessary + temp_pkt.allocate(); + + using namespace Stats; + + writebacks + .init(maxThreadsPerCPU) + .name(name + ".writebacks") + .desc("number of writebacks") + .flags(total) + ; + + // MSHR hit statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshr_hits[access_idx] + .init(maxThreadsPerCPU) + .name(name + "." + cstr + "_mshr_hits") + .desc("number of " + cstr + " MSHR hits") + .flags(total | nozero | nonan) + ; + } + + demandMshrHits + .name(name + ".demand_mshr_hits") + .desc("number of demand (read+write) MSHR hits") + .flags(total) + ; + demandMshrHits = mshr_hits[Packet::ReadReq] + mshr_hits[Packet::WriteReq]; + + overallMshrHits + .name(name + ".overall_mshr_hits") + .desc("number of overall MSHR hits") + .flags(total) + ; + overallMshrHits = demandMshrHits + mshr_hits[Packet::SoftPFReq] + + mshr_hits[Packet::HardPFReq]; + + // MSHR miss statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshr_misses[access_idx] + .init(maxThreadsPerCPU) + .name(name + "." + cstr + "_mshr_misses") + .desc("number of " + cstr + " MSHR misses") + .flags(total | nozero | nonan) + ; + } + + demandMshrMisses + .name(name + ".demand_mshr_misses") + .desc("number of demand (read+write) MSHR misses") + .flags(total) + ; + demandMshrMisses = mshr_misses[Packet::ReadReq] + mshr_misses[Packet::WriteReq]; + + overallMshrMisses + .name(name + ".overall_mshr_misses") + .desc("number of overall MSHR misses") + .flags(total) + ; + overallMshrMisses = demandMshrMisses + mshr_misses[Packet::SoftPFReq] + + mshr_misses[Packet::HardPFReq]; + + // MSHR miss latency statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshr_miss_latency[access_idx] + .init(maxThreadsPerCPU) + .name(name + "." + cstr + "_mshr_miss_latency") + .desc("number of " + cstr + " MSHR miss cycles") + .flags(total | nozero | nonan) + ; + } + + demandMshrMissLatency + .name(name + ".demand_mshr_miss_latency") + .desc("number of demand (read+write) MSHR miss cycles") + .flags(total) + ; + demandMshrMissLatency = mshr_miss_latency[Packet::ReadReq] + + mshr_miss_latency[Packet::WriteReq]; + + overallMshrMissLatency + .name(name + ".overall_mshr_miss_latency") + .desc("number of overall MSHR miss cycles") + .flags(total) + ; + overallMshrMissLatency = demandMshrMissLatency + + mshr_miss_latency[Packet::SoftPFReq] + mshr_miss_latency[Packet::HardPFReq]; + + // MSHR uncacheable statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshr_uncacheable[access_idx] + .init(maxThreadsPerCPU) + .name(name + "." + cstr + "_mshr_uncacheable") + .desc("number of " + cstr + " MSHR uncacheable") + .flags(total | nozero | nonan) + ; + } + + overallMshrUncacheable + .name(name + ".overall_mshr_uncacheable_misses") + .desc("number of overall MSHR uncacheable misses") + .flags(total) + ; + overallMshrUncacheable = mshr_uncacheable[Packet::ReadReq] + + mshr_uncacheable[Packet::WriteReq] + mshr_uncacheable[Packet::SoftPFReq] + + mshr_uncacheable[Packet::HardPFReq]; + + // MSHR miss latency statistics + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshr_uncacheable_lat[access_idx] + .init(maxThreadsPerCPU) + .name(name + "." + cstr + "_mshr_uncacheable_latency") + .desc("number of " + cstr + " MSHR uncacheable cycles") + .flags(total | nozero | nonan) + ; + } + + overallMshrUncacheableLatency + .name(name + ".overall_mshr_uncacheable_latency") + .desc("number of overall MSHR uncacheable cycles") + .flags(total) + ; + overallMshrUncacheableLatency = mshr_uncacheable_lat[Packet::ReadReq] + + mshr_uncacheable_lat[Packet::WriteReq] + + mshr_uncacheable_lat[Packet::SoftPFReq] + + mshr_uncacheable_lat[Packet::HardPFReq]; + +#if 0 + // MSHR access formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshrAccesses[access_idx] + .name(name + "." + cstr + "_mshr_accesses") + .desc("number of " + cstr + " mshr accesses(hits+misses)") + .flags(total | nozero | nonan) + ; + mshrAccesses[access_idx] = + mshr_hits[access_idx] + mshr_misses[access_idx] + + mshr_uncacheable[access_idx]; + } + + demandMshrAccesses + .name(name + ".demand_mshr_accesses") + .desc("number of demand (read+write) mshr accesses") + .flags(total | nozero | nonan) + ; + demandMshrAccesses = demandMshrHits + demandMshrMisses; + + overallMshrAccesses + .name(name + ".overall_mshr_accesses") + .desc("number of overall (read+write) mshr accesses") + .flags(total | nozero | nonan) + ; + overallMshrAccesses = overallMshrHits + overallMshrMisses + + overallMshrUncacheable; +#endif + + // MSHR miss rate formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + mshrMissRate[access_idx] + .name(name + "." + cstr + "_mshr_miss_rate") + .desc("mshr miss rate for " + cstr + " accesses") + .flags(total | nozero | nonan) + ; + + mshrMissRate[access_idx] = + mshr_misses[access_idx] / cache->accesses[access_idx]; + } + + demandMshrMissRate + .name(name + ".demand_mshr_miss_rate") + .desc("mshr miss rate for demand accesses") + .flags(total) + ; + demandMshrMissRate = demandMshrMisses / cache->demandAccesses; + + overallMshrMissRate + .name(name + ".overall_mshr_miss_rate") + .desc("mshr miss rate for overall accesses") + .flags(total) + ; + overallMshrMissRate = overallMshrMisses / cache->overallAccesses; + + // mshrMiss latency formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + avgMshrMissLatency[access_idx] + .name(name + "." + cstr + "_avg_mshr_miss_latency") + .desc("average " + cstr + " mshr miss latency") + .flags(total | nozero | nonan) + ; + + avgMshrMissLatency[access_idx] = + mshr_miss_latency[access_idx] / mshr_misses[access_idx]; + } + + demandAvgMshrMissLatency + .name(name + ".demand_avg_mshr_miss_latency") + .desc("average overall mshr miss latency") + .flags(total) + ; + demandAvgMshrMissLatency = demandMshrMissLatency / demandMshrMisses; + + overallAvgMshrMissLatency + .name(name + ".overall_avg_mshr_miss_latency") + .desc("average overall mshr miss latency") + .flags(total) + ; + overallAvgMshrMissLatency = overallMshrMissLatency / overallMshrMisses; + + // mshrUncacheable latency formulas + for (int access_idx = 0; access_idx < NUM_MEM_CMDS; ++access_idx) { + Packet::Command cmd = (Packet::Command)access_idx; + const string &cstr = temp_pkt.cmdIdxToString(cmd); + + avgMshrUncacheableLatency[access_idx] + .name(name + "." + cstr + "_avg_mshr_uncacheable_latency") + .desc("average " + cstr + " mshr uncacheable latency") + .flags(total | nozero | nonan) + ; + + avgMshrUncacheableLatency[access_idx] = + mshr_uncacheable_lat[access_idx] / mshr_uncacheable[access_idx]; + } + + overallAvgMshrUncacheableLatency + .name(name + ".overall_avg_mshr_uncacheable_latency") + .desc("average overall mshr uncacheable latency") + .flags(total) + ; + overallAvgMshrUncacheableLatency = overallMshrUncacheableLatency / overallMshrUncacheable; + + mshr_cap_events + .init(maxThreadsPerCPU) + .name(name + ".mshr_cap_events") + .desc("number of times MSHR cap was activated") + .flags(total) + ; + + //software prefetching stats + soft_prefetch_mshr_full + .init(maxThreadsPerCPU) + .name(name + ".soft_prefetch_mshr_full") + .desc("number of mshr full events for SW prefetching instrutions") + .flags(total) + ; + + mshr_no_allocate_misses + .name(name +".no_allocate_misses") + .desc("Number of misses that were no-allocate") + ; + +} + +void +MissQueue::setCache(BaseCache *_cache) +{ + cache = _cache; + blkSize = cache->getBlockSize(); +} + +void +MissQueue::setPrefetcher(BasePrefetcher *_prefetcher) +{ + prefetcher = _prefetcher; +} + +MSHR* +MissQueue::allocateMiss(Packet * &pkt, int size, Tick time) +{ + MSHR* mshr = mq.allocate(pkt, blkSize); + mshr->order = order++; + if (!pkt->req->isUncacheable() ){//&& !pkt->isNoAllocate()) { + // Mark this as a cache line fill + mshr->pkt->flags |= CACHE_LINE_FILL; + } + if (mq.isFull()) { + cache->setBlocked(Blocked_NoMSHRs); + } + if (pkt->cmd != Packet::HardPFReq) { + //If we need to request the bus (not on HW prefetch), do so + cache->setMasterRequest(Request_MSHR, time); + } + return mshr; +} + + +MSHR* +MissQueue::allocateWrite(Packet * &pkt, int size, Tick time) +{ + MSHR* mshr = wb.allocate(pkt,blkSize); + mshr->order = order++; + +//REMOVING COMPRESSION FOR NOW +#if 0 + if (pkt->isCompressed()) { + mshr->pkt->deleteData(); + mshr->pkt->actualSize = pkt->actualSize; + mshr->pkt->data = new uint8_t[pkt->actualSize]; + memcpy(mshr->pkt->data, pkt->data, pkt->actualSize); + } else { +#endif + memcpy(mshr->pkt->getPtr<uint8_t>(), pkt->getPtr<uint8_t>(), pkt->getSize()); + //{ + + if (wb.isFull()) { + cache->setBlocked(Blocked_NoWBBuffers); + } + + cache->setMasterRequest(Request_WB, time); + + return mshr; +} + + +/** + * @todo Remove SW prefetches on mshr hits. + */ +void +MissQueue::handleMiss(Packet * &pkt, int blkSize, Tick time) +{ +// if (!cache->isTopLevel()) + if (prefetchMiss) prefetcher->handleMiss(pkt, time); + + int size = blkSize; + Addr blkAddr = pkt->getAddr() & ~(Addr)(blkSize-1); + MSHR* mshr = NULL; + if (!pkt->req->isUncacheable()) { + mshr = mq.findMatch(blkAddr); + if (mshr) { + //@todo remove hw_pf here + mshr_hits[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + if (mshr->threadNum != pkt->req->getThreadNum()) { + mshr->threadNum = -1; + } + mq.allocateTarget(mshr, pkt); + if (mshr->pkt->isNoAllocate() && !pkt->isNoAllocate()) { + //We are adding an allocate after a no-allocate + mshr->pkt->flags &= ~NO_ALLOCATE; + } + if (mshr->getNumTargets() == numTarget) { + noTargetMSHR = mshr; + cache->setBlocked(Blocked_NoTargets); + mq.moveToFront(mshr); + } + return; + } + if (pkt->isNoAllocate()) { + //Count no-allocate requests differently + mshr_no_allocate_misses++; + } + else { + mshr_misses[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + } + } else { + //Count uncacheable accesses + mshr_uncacheable[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + size = pkt->getSize(); + } + if (pkt->isWrite() && (pkt->req->isUncacheable() || !writeAllocate || + !pkt->needsResponse())) { + /** + * @todo Add write merging here. + */ + mshr = allocateWrite(pkt, blkSize, time); + return; + } + + mshr = allocateMiss(pkt, blkSize, time); +} + +MSHR* +MissQueue::fetchBlock(Addr addr, int blk_size, Tick time, + Packet * &target) +{ + Addr blkAddr = addr & ~(Addr)(blk_size - 1); + assert(mq.findMatch(addr) == NULL); + MSHR *mshr = mq.allocateFetch(blkAddr, blk_size, target); + mshr->order = order++; + mshr->pkt->flags |= CACHE_LINE_FILL; + if (mq.isFull()) { + cache->setBlocked(Blocked_NoMSHRs); + } + cache->setMasterRequest(Request_MSHR, time); + return mshr; +} + +Packet * +MissQueue::getPacket() +{ + Packet * pkt = mq.getReq(); + if (((wb.isFull() && wb.inServiceMSHRs == 0) || !pkt || + pkt->time > curTick) && wb.havePending()) { + pkt = wb.getReq(); + // Need to search for earlier miss. + MSHR *mshr = mq.findPending(pkt); + if (mshr && mshr->order < ((MSHR*)(pkt->senderState))->order) { + // Service misses in order until conflict is cleared. + return mq.getReq(); + } + } + if (pkt) { + MSHR* mshr = wb.findPending(pkt); + if (mshr /*&& mshr->order < pkt->senderState->order*/) { + // The only way this happens is if we are + // doing a write and we didn't have permissions + // then subsequently saw a writeback(owned got evicted) + // We need to make sure to perform the writeback first + // To preserve the dirty data, then we can issue the write + return wb.getReq(); + } + } + else if (!mq.isFull()){ + //If we have a miss queue slot, we can try a prefetch + pkt = prefetcher->getPacket(); + if (pkt) { + //Update statistic on number of prefetches issued (hwpf_mshr_misses) + mshr_misses[pkt->cmdToIndex()][pkt->req->getThreadNum()]++; + //It will request the bus for the future, but should clear that immedieatley + allocateMiss(pkt, pkt->getSize(), curTick); + pkt = mq.getReq(); + assert(pkt); //We should get back a req b/c we just put one in + } + } + return pkt; +} + +void +MissQueue::setBusCmd(Packet * &pkt, Packet::Command cmd) +{ + assert(pkt->senderState != 0); + MSHR * mshr = (MSHR*)pkt->senderState; + mshr->originalCmd = pkt->cmd; + if (pkt->isCacheFill() || pkt->isNoAllocate()) + pkt->cmd = cmd; +} + +void +MissQueue::restoreOrigCmd(Packet * &pkt) +{ + pkt->cmd = ((MSHR*)(pkt->senderState))->originalCmd; +} + +void +MissQueue::markInService(Packet * &pkt) +{ + assert(pkt->senderState != 0); + bool unblock = false; + BlockedCause cause = NUM_BLOCKED_CAUSES; + + /** + * @todo Should include MSHRQueue pointer in MSHR to select the correct + * one. + */ + if ((!pkt->isCacheFill() && pkt->isWrite())) { + // Forwarding a write/ writeback, don't need to change + // the command + unblock = wb.isFull(); + wb.markInService((MSHR*)pkt->senderState); + if (!wb.havePending()){ + cache->clearMasterRequest(Request_WB); + } + if (unblock) { + // Do we really unblock? + unblock = !wb.isFull(); + cause = Blocked_NoWBBuffers; + } + } else { + unblock = mq.isFull(); + mq.markInService((MSHR*)pkt->senderState); + if (!mq.havePending()){ + cache->clearMasterRequest(Request_MSHR); + } + if (((MSHR*)(pkt->senderState))->originalCmd == Packet::HardPFReq) { + DPRINTF(HWPrefetch, "%s:Marking a HW_PF in service\n", + cache->name()); + //Also clear pending if need be + if (!prefetcher->havePending()) + { + cache->clearMasterRequest(Request_PF); + } + } + if (unblock) { + unblock = !mq.isFull(); + cause = Blocked_NoMSHRs; + } + } + if (unblock) { + cache->clearBlocked(cause); + } +} + + +void +MissQueue::handleResponse(Packet * &pkt, Tick time) +{ + MSHR* mshr = (MSHR*)pkt->senderState; + if (((MSHR*)(pkt->senderState))->originalCmd == Packet::HardPFReq) { + DPRINTF(HWPrefetch, "%s:Handling the response to a HW_PF\n", + cache->name()); + } +#ifndef NDEBUG + int num_targets = mshr->getNumTargets(); +#endif + + bool unblock = false; + bool unblock_target = false; + BlockedCause cause = NUM_BLOCKED_CAUSES; + + if (pkt->isCacheFill() && !pkt->isNoAllocate()) { + mshr_miss_latency[mshr->originalCmd][pkt->req->getThreadNum()] += + curTick - pkt->time; + // targets were handled in the cache tags + if (mshr == noTargetMSHR) { + // we always clear at least one target + unblock_target = true; + cause = Blocked_NoTargets; + noTargetMSHR = NULL; + } + + if (mshr->hasTargets()) { + // Didn't satisfy all the targets, need to resend + Packet::Command cmd = mshr->getTarget()->cmd; + mq.markPending(mshr, cmd); + mshr->order = order++; + cache->setMasterRequest(Request_MSHR, time); + } + else { + unblock = mq.isFull(); + mq.deallocate(mshr); + if (unblock) { + unblock = !mq.isFull(); + cause = Blocked_NoMSHRs; + } + } + } else { + if (pkt->req->isUncacheable()) { + mshr_uncacheable_lat[pkt->cmd][pkt->req->getThreadNum()] += + curTick - pkt->time; + } + if (mshr->hasTargets() && pkt->req->isUncacheable()) { + // Should only have 1 target if we had any + assert(num_targets == 1); + Packet * target = mshr->getTarget(); + mshr->popTarget(); + if (pkt->isRead()) { + memcpy(target->getPtr<uint8_t>(), pkt->getPtr<uint8_t>(), + target->getSize()); + } + cache->respond(target, time); + assert(!mshr->hasTargets()); + } + else if (mshr->hasTargets()) { + //Must be a no_allocate with possibly more than one target + assert(mshr->pkt->isNoAllocate()); + while (mshr->hasTargets()) { + Packet * target = mshr->getTarget(); + mshr->popTarget(); + if (pkt->isRead()) { + memcpy(target->getPtr<uint8_t>(), pkt->getPtr<uint8_t>(), + target->getSize()); + } + cache->respond(target, time); + } + } + + if (pkt->isWrite()) { + // If the wrtie buffer is full, we might unblock now + unblock = wb.isFull(); + wb.deallocate(mshr); + if (unblock) { + // Did we really unblock? + unblock = !wb.isFull(); + cause = Blocked_NoWBBuffers; + } + } else { + unblock = mq.isFull(); + mq.deallocate(mshr); + if (unblock) { + unblock = !mq.isFull(); + cause = Blocked_NoMSHRs; + } + } + } + if (unblock || unblock_target) { + cache->clearBlocked(cause); + } +} + +void +MissQueue::squash(int threadNum) +{ + bool unblock = false; + BlockedCause cause = NUM_BLOCKED_CAUSES; + + if (noTargetMSHR && noTargetMSHR->threadNum == threadNum) { + noTargetMSHR = NULL; + unblock = true; + cause = Blocked_NoTargets; + } + if (mq.isFull()) { + unblock = true; + cause = Blocked_NoMSHRs; + } + mq.squash(threadNum); + if (!mq.havePending()) { + cache->clearMasterRequest(Request_MSHR); + } + if (unblock && !mq.isFull()) { + cache->clearBlocked(cause); + } + +} + +MSHR* +MissQueue::findMSHR(Addr addr) const +{ + return mq.findMatch(addr); +} + +bool +MissQueue::findWrites(Addr addr, vector<MSHR*> &writes) const +{ + return wb.findMatches(addr,writes); +} + +void +MissQueue::doWriteback(Addr addr, + int size, uint8_t *data, bool compressed) +{ + // Generate request + Request * req = new Request(addr, size, 0); + Packet * pkt = new Packet(req, Packet::Writeback, -1); + pkt->allocate(); + if (data) { + memcpy(pkt->getPtr<uint8_t>(), data, size); + } + + if (compressed) { + pkt->flags |= COMPRESSED; + } + + ///All writebacks charged to same thread @todo figure this out + writebacks[pkt->req->getThreadNum()]++; + + allocateWrite(pkt, 0, curTick); +} + + +void +MissQueue::doWriteback(Packet * &pkt) +{ + writebacks[pkt->req->getThreadNum()]++; + allocateWrite(pkt, 0, curTick); +} + + +MSHR* +MissQueue::allocateTargetList(Addr addr) +{ + MSHR* mshr = mq.allocateTargetList(addr, blkSize); + mshr->pkt->flags |= CACHE_LINE_FILL; + if (mq.isFull()) { + cache->setBlocked(Blocked_NoMSHRs); + } + return mshr; +} + +bool +MissQueue::havePending() +{ + return mq.havePending() || wb.havePending() || prefetcher->havePending(); +} diff --git a/src/mem/cache/miss/miss_queue.hh b/src/mem/cache/miss/miss_queue.hh new file mode 100644 index 000000000..505d1f90c --- /dev/null +++ b/src/mem/cache/miss/miss_queue.hh @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Miss and writeback queue declarations. + */ + +#ifndef __MISS_QUEUE_HH__ +#define __MISS_QUEUE_HH__ + +#include <vector> + +#include "mem/cache/miss/mshr.hh" +#include "mem/cache/miss/mshr_queue.hh" +#include "base/statistics.hh" + +class BaseCache; +class BasePrefetcher; +/** + * Manages cache misses and writebacks. Contains MSHRs to store miss data + * and the writebuffer for writes/writebacks. + * @todo need to handle data on writes better (encapsulate). + * @todo need to make replacements/writebacks happen in Cache::access + */ +class MissQueue +{ + protected: + /** The MSHRs. */ + MSHRQueue mq; + /** Write Buffer. */ + MSHRQueue wb; + + // PARAMTERS + + /** The number of MSHRs in the miss queue. */ + const int numMSHR; + /** The number of targets for each MSHR. */ + const int numTarget; + /** The number of write buffers. */ + const int writeBuffers; + /** True if the cache should allocate on a write miss. */ + const bool writeAllocate; + /** Pointer to the parent cache. */ + BaseCache* cache; + + /** The Prefetcher */ + BasePrefetcher *prefetcher; + + /** The block size of the parent cache. */ + int blkSize; + + /** Increasing order number assigned to each incoming pktuest. */ + uint64_t order; + + bool prefetchMiss; + + // Statistics + /** + * @addtogroup CacheStatistics + * @{ + */ + /** Number of blocks written back per thread. */ + Stats::Vector<> writebacks; + + /** Number of misses that hit in the MSHRs per command and thread. */ + Stats::Vector<> mshr_hits[NUM_MEM_CMDS]; + /** Demand misses that hit in the MSHRs. */ + Stats::Formula demandMshrHits; + /** Total number of misses that hit in the MSHRs. */ + Stats::Formula overallMshrHits; + + /** Number of misses that miss in the MSHRs, per command and thread. */ + Stats::Vector<> mshr_misses[NUM_MEM_CMDS]; + /** Demand misses that miss in the MSHRs. */ + Stats::Formula demandMshrMisses; + /** Total number of misses that miss in the MSHRs. */ + Stats::Formula overallMshrMisses; + + /** Number of misses that miss in the MSHRs, per command and thread. */ + Stats::Vector<> mshr_uncacheable[NUM_MEM_CMDS]; + /** Total number of misses that miss in the MSHRs. */ + Stats::Formula overallMshrUncacheable; + + /** Total cycle latency of each MSHR miss, per command and thread. */ + Stats::Vector<> mshr_miss_latency[NUM_MEM_CMDS]; + /** Total cycle latency of demand MSHR misses. */ + Stats::Formula demandMshrMissLatency; + /** Total cycle latency of overall MSHR misses. */ + Stats::Formula overallMshrMissLatency; + + /** Total cycle latency of each MSHR miss, per command and thread. */ + Stats::Vector<> mshr_uncacheable_lat[NUM_MEM_CMDS]; + /** Total cycle latency of overall MSHR misses. */ + Stats::Formula overallMshrUncacheableLatency; + + /** The total number of MSHR accesses per command and thread. */ + Stats::Formula mshrAccesses[NUM_MEM_CMDS]; + /** The total number of demand MSHR accesses. */ + Stats::Formula demandMshrAccesses; + /** The total number of MSHR accesses. */ + Stats::Formula overallMshrAccesses; + + /** The miss rate in the MSHRs pre command and thread. */ + Stats::Formula mshrMissRate[NUM_MEM_CMDS]; + /** The demand miss rate in the MSHRs. */ + Stats::Formula demandMshrMissRate; + /** The overall miss rate in the MSHRs. */ + Stats::Formula overallMshrMissRate; + + /** The average latency of an MSHR miss, per command and thread. */ + Stats::Formula avgMshrMissLatency[NUM_MEM_CMDS]; + /** The average latency of a demand MSHR miss. */ + Stats::Formula demandAvgMshrMissLatency; + /** The average overall latency of an MSHR miss. */ + Stats::Formula overallAvgMshrMissLatency; + + /** The average latency of an MSHR miss, per command and thread. */ + Stats::Formula avgMshrUncacheableLatency[NUM_MEM_CMDS]; + /** The average overall latency of an MSHR miss. */ + Stats::Formula overallAvgMshrUncacheableLatency; + + /** The number of times a thread hit its MSHR cap. */ + Stats::Vector<> mshr_cap_events; + /** The number of times software prefetches caused the MSHR to block. */ + Stats::Vector<> soft_prefetch_mshr_full; + + Stats::Scalar<> mshr_no_allocate_misses; + + /** + * @} + */ + + private: + /** Pointer to the MSHR that has no targets. */ + MSHR* noTargetMSHR; + + /** + * Allocate a new MSHR to handle the provided miss. + * @param pkt The miss to buffer. + * @param size The number of bytes to fetch. + * @param time The time the miss occurs. + * @return A pointer to the new MSHR. + */ + MSHR* allocateMiss(Packet * &pkt, int size, Tick time); + + /** + * Allocate a new WriteBuffer to handle the provided write. + * @param pkt The write to handle. + * @param size The number of bytes to write. + * @param time The time the write occurs. + * @return A pointer to the new write buffer. + */ + MSHR* allocateWrite(Packet * &pkt, int size, Tick time); + + public: + /** + * Simple Constructor. Initializes all needed internal storage and sets + * parameters. + * @param numMSHRs The number of outstanding misses to handle. + * @param numTargets The number of outstanding targets to each miss. + * @param write_buffers The number of outstanding writes to handle. + * @param write_allocate If true, treat write misses the same as reads. + */ + MissQueue(int numMSHRs, int numTargets, int write_buffers, + bool write_allocate, bool prefetch_miss); + + /** + * Deletes all allocated internal storage. + */ + ~MissQueue(); + + /** + * Register statistics for this object. + * @param name The name of the parent cache. + */ + void regStats(const std::string &name); + + /** + * Called by the parent cache to set the back pointer. + * @param _cache A pointer to the parent cache. + */ + void setCache(BaseCache *_cache); + + void setPrefetcher(BasePrefetcher *_prefetcher); + + /** + * Handle a cache miss properly. Either allocate an MSHR for the pktuest, + * or forward it through the write buffer. + * @param pkt The request that missed in the cache. + * @param blk_size The block size of the cache. + * @param time The time the miss is detected. + */ + void handleMiss(Packet * &pkt, int blk_size, Tick time); + + /** + * Fetch the block for the given address and buffer the given target. + * @param addr The address to fetch. + * @param asid The address space of the address. + * @param blk_size The block size of the cache. + * @param time The time the miss is detected. + * @param target The target for the fetch. + */ + MSHR* fetchBlock(Addr addr, int blk_size, Tick time, + Packet * &target); + + /** + * Selects a outstanding pktuest to service. + * @return The pktuest to service, NULL if none found. + */ + Packet * getPacket(); + + /** + * Set the command to the given bus command. + * @param pkt The request to update. + * @param cmd The bus command to use. + */ + void setBusCmd(Packet * &pkt, Packet::Command cmd); + + /** + * Restore the original command in case of a bus transmission error. + * @param pkt The request to reset. + */ + void restoreOrigCmd(Packet * &pkt); + + /** + * Marks a pktuest as in service (sent on the bus). This can have side + * effect since storage for no response commands is deallocated once they + * are successfully sent. + * @param pkt The request that was sent on the bus. + */ + void markInService(Packet * &pkt); + + /** + * Collect statistics and free resources of a satisfied pktuest. + * @param pkt The request that has been satisfied. + * @param time The time when the pktuest is satisfied. + */ + void handleResponse(Packet * &pkt, Tick time); + + /** + * Removes all outstanding pktuests for a given thread number. If a request + * has been sent to the bus, this function removes all of its targets. + * @param threadNum The thread number of the requests to squash. + */ + void squash(int threadNum); + + /** + * Return the current number of outstanding misses. + * @return the number of outstanding misses. + */ + int getMisses() + { + return mq.getAllocatedTargets(); + } + + /** + * Searches for the supplied address in the miss queue. + * @param addr The address to look for. + * @param asid The address space id. + * @return The MSHR that contains the address, NULL if not found. + * @warning Currently only searches the miss queue. If non write allocate + * might need to search the write buffer for coherence. + */ + MSHR* findMSHR(Addr addr) const; + + /** + * Searches for the supplied address in the write buffer. + * @param addr The address to look for. + * @param asid The address space id. + * @param writes The list of writes that match the address. + * @return True if any writes are found + */ + bool findWrites(Addr addr, std::vector<MSHR*>& writes) const; + + /** + * Perform a writeback of dirty data to the given address. + * @param addr The address to write to. + * @param asid The address space id. + * @param xc The execution context of the address space. + * @param size The number of bytes to write. + * @param data The data to write, can be NULL. + * @param compressed True if the data is compressed. + */ + void doWriteback(Addr addr, + int size, uint8_t *data, bool compressed); + + /** + * Perform the given writeback pktuest. + * @param pkt The writeback request. + */ + void doWriteback(Packet * &pkt); + + /** + * Returns true if there are outstanding pktuests. + * @return True if there are outstanding pktuests. + */ + bool havePending(); + + /** + * Add a target to the given MSHR. This assumes it is in the miss queue. + * @param mshr The mshr to add a target to. + * @param pkt The target to add. + */ + void addTarget(MSHR *mshr, Packet * &pkt) + { + mq.allocateTarget(mshr, pkt); + } + + /** + * Allocate a MSHR to hold a list of targets to a block involved in a copy. + * If the block is marked done then the MSHR already holds the data to + * fill the block. Otherwise the block needs to be fetched. + * @param addr The address to buffer. + * @param asid The address space ID. + * @return A pointer to the allocated MSHR. + */ + MSHR* allocateTargetList(Addr addr); + +}; + +#endif //__MISS_QUEUE_HH__ diff --git a/src/mem/cache/miss/mshr.cc b/src/mem/cache/miss/mshr.cc new file mode 100644 index 000000000..519ec5ebd --- /dev/null +++ b/src/mem/cache/miss/mshr.cc @@ -0,0 +1,181 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Dave Greene + */ + +/** + * @file + * Miss Status and Handling Register (MSHR) definitions. + */ + +#include <assert.h> +#include <string> +#include <vector> + +#include "mem/cache/miss/mshr.hh" +#include "sim/root.hh" // for curTick +#include "sim/host.hh" +#include "base/misc.hh" +#include "mem/cache/cache.hh" + +using namespace std; + +MSHR::MSHR() +{ + inService = false; + ntargets = 0; + threadNum = -1; +} + +void +MSHR::allocate(Packet::Command cmd, Addr _addr, int size, + Packet * &target) +{ + addr = _addr; + if (target) + { + //Have a request, just use it + pkt = new Packet(target->req, cmd, Packet::Broadcast, size); + pkt->time = curTick; + pkt->allocate(); + pkt->senderState = (Packet::SenderState *)this; + allocateTarget(target); + } + else + { + //need a request first + Request * req = new Request(); + req->setPhys(addr, size, 0); + //Thread context?? + pkt = new Packet(req, cmd, Packet::Broadcast, size); + pkt->time = curTick; + pkt->allocate(); + pkt->senderState = (Packet::SenderState *)this; + } +} + +// Since we aren't sure if data is being used, don't copy here. +/** + * @todo When we have a "global" data flag, might want to copy data here. + */ +void +MSHR::allocateAsBuffer(Packet * &target) +{ + addr = target->getAddr(); + threadNum = target->req->getThreadNum(); + pkt = new Packet(target->req, target->cmd, -1); + pkt->allocate(); + pkt->senderState = (Packet::SenderState*)this; + pkt->time = curTick; +} + +void +MSHR::deallocate() +{ + assert(targets.empty()); + assert(ntargets == 0); + pkt = NULL; + inService = false; + //allocIter = NULL; + //readyIter = NULL; +} + +/* + * Adds a target to an MSHR + */ +void +MSHR::allocateTarget(Packet * &target) +{ + //If we append an invalidate and we issued a read to the bus, + //but now have some pending writes, we need to move + //the invalidate to before the first non-read + if (inService && pkt->isRead() && target->isInvalidate()) { + std::list<Packet *> temp; + + while (!targets.empty()) { + if (!targets.front()->isRead()) break; + //Place on top of temp stack + temp.push_front(targets.front()); + //Remove from targets + targets.pop_front(); + } + + //Now that we have all the reads off until first non-read, we can + //place the invalidate on + targets.push_front(target); + + //Now we pop off the temp_stack and put them back + while (!temp.empty()) { + targets.push_front(temp.front()); + temp.pop_front(); + } + } + else { + targets.push_back(target); + } + + ++ntargets; + assert(targets.size() == ntargets); + /** + * @todo really prioritize the target commands. + */ + + if (!inService && target->isWrite()) { + pkt->cmd = Packet::WriteReq; + } +} + + + +void +MSHR::dump() +{ + ccprintf(cerr, + "inService: %d thread: %d\n" + "Addr: %x ntargets %d\n" + "Targets:\n", + inService, threadNum, addr, ntargets); + + TargetListIterator tar_it = targets.begin(); + for (int i = 0; i < ntargets; i++) { + assert(tar_it != targets.end()); + + ccprintf(cerr, "\t%d: Addr: %x cmd: %d\n", + i, (*tar_it)->getAddr(), (*tar_it)->cmdToIndex()); + + tar_it++; + } + ccprintf(cerr, "\n"); +} + +MSHR::~MSHR() +{ + if (pkt) + pkt = NULL; +} diff --git a/src/mem/cache/miss/mshr.hh b/src/mem/cache/miss/mshr.hh new file mode 100644 index 000000000..028259b35 --- /dev/null +++ b/src/mem/cache/miss/mshr.hh @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Miss Status and Handling Register (MSHR) declaration. + */ + +#ifndef __MSHR_HH__ +#define __MSHR_HH__ + +#include "mem/packet.hh" +#include <list> +#include <deque> + +class MSHR; + +/** + * Miss Status and handling Register. This class keeps all the information + * needed to handle a cache miss including a list of target pktuests. + */ +class MSHR { + public: + /** Defines the Data structure of the MSHR targetlist. */ + typedef std::list<Packet *> TargetList; + /** Target list iterator. */ + typedef std::list<Packet *>::iterator TargetListIterator; + /** A list of MSHRs. */ + typedef std::list<MSHR *> List; + /** MSHR list iterator. */ + typedef List::iterator Iterator; + /** MSHR list const_iterator. */ + typedef List::const_iterator ConstIterator; + + /** Address of the miss. */ + Addr addr; + /** Adress space id of the miss. */ + short asid; + /** True if the pktuest has been sent to the bus. */ + bool inService; + /** Thread number of the miss. */ + int threadNum; + /** The pktuest that is forwarded to the next level of the hierarchy. */ + Packet * pkt; + /** The number of currently allocated targets. */ + short ntargets; + /** The original pktuesting command. */ + Packet::Command originalCmd; + /** Order number of assigned by the miss queue. */ + uint64_t order; + + /** + * Pointer to this MSHR on the ready list. + * @sa MissQueue, MSHRQueue::readyList + */ + Iterator readyIter; + /** + * Pointer to this MSHR on the allocated list. + * @sa MissQueue, MSHRQueue::allocatedList + */ + Iterator allocIter; + +private: + /** List of all pktuests that match the address */ + TargetList targets; + +public: + /** + * Allocate a miss to this MSHR. + * @param cmd The pktuesting command. + * @param addr The address of the miss. + * @param asid The address space id of the miss. + * @param size The number of bytes to pktuest. + * @param pkt The original miss. + */ + void allocate(Packet::Command cmd, Addr addr, int size, + Packet * &pkt); + + /** + * Allocate this MSHR as a buffer for the given pktuest. + * @param target The memory pktuest to buffer. + */ + void allocateAsBuffer(Packet * &target); + + /** + * Mark this MSHR as free. + */ + void deallocate(); + + /** + * Add a pktuest to the list of targets. + * @param target The target. + */ + void allocateTarget(Packet * &target); + + /** A simple constructor. */ + MSHR(); + /** A simple destructor. */ + ~MSHR(); + + /** + * Returns the current number of allocated targets. + * @return The current number of allocated targets. + */ + int getNumTargets() + { + return(ntargets); + } + + /** + * Returns a pointer to the target list. + * @return a pointer to the target list. + */ + TargetList* getTargetList() + { + return &targets; + } + + /** + * Returns a reference to the first target. + * @return A pointer to the first target. + */ + Packet * getTarget() + { + return targets.front(); + } + + /** + * Pop first target. + */ + void popTarget() + { + --ntargets; + targets.pop_front(); + } + + /** + * Returns true if there are targets left. + * @return true if there are targets + */ + bool hasTargets() + { + return !targets.empty(); + } + + /** + * Prints the contents of this MSHR to stderr. + */ + void dump(); +}; + +#endif //__MSHR_HH__ diff --git a/src/mem/cache/miss/mshr_queue.cc b/src/mem/cache/miss/mshr_queue.cc new file mode 100644 index 000000000..97a56119f --- /dev/null +++ b/src/mem/cache/miss/mshr_queue.cc @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** @file + * Definition of the MSHRQueue. + */ + +#include "mem/cache/miss/mshr_queue.hh" +#include "sim/eventq.hh" + +using namespace std; + +MSHRQueue::MSHRQueue(int num_mshrs, int reserve) + : numMSHRs(num_mshrs + reserve - 1), numReserve(reserve) +{ + allocated = 0; + inServiceMSHRs = 0; + allocatedTargets = 0; + registers = new MSHR[numMSHRs]; + for (int i = 0; i < numMSHRs; ++i) { + freeList.push_back(®isters[i]); + } +} + +MSHRQueue::~MSHRQueue() +{ + delete [] registers; +} + +MSHR* +MSHRQueue::findMatch(Addr addr) const +{ + MSHR::ConstIterator i = allocatedList.begin(); + MSHR::ConstIterator end = allocatedList.end(); + for (; i != end; ++i) { + MSHR *mshr = *i; + if (mshr->addr == addr) { + return mshr; + } + } + return NULL; +} + +bool +MSHRQueue::findMatches(Addr addr, vector<MSHR*>& matches) const +{ + // Need an empty vector + assert(matches.empty()); + bool retval = false; + MSHR::ConstIterator i = allocatedList.begin(); + MSHR::ConstIterator end = allocatedList.end(); + for (; i != end; ++i) { + MSHR *mshr = *i; + if (mshr->addr == addr) { + retval = true; + matches.push_back(mshr); + } + } + return retval; + +} + +MSHR* +MSHRQueue::findPending(Packet * &pkt) const +{ + MSHR::ConstIterator i = pendingList.begin(); + MSHR::ConstIterator end = pendingList.end(); + for (; i != end; ++i) { + MSHR *mshr = *i; + if (mshr->addr < pkt->getAddr()) { + if (mshr->addr + mshr->pkt->getSize() > pkt->getAddr()) { + return mshr; + } + } else { + if (pkt->getAddr() + pkt->getSize() > mshr->addr) { + return mshr; + } + } + + //need to check destination address for copies. + //TEMP NOT DOING COPIES +#if 0 + if (mshr->pkt->cmd == Copy) { + Addr dest = mshr->pkt->dest; + if (dest < pkt->addr) { + if (dest + mshr->pkt->size > pkt->addr) { + return mshr; + } + } else { + if (pkt->addr + pkt->size > dest) { + return mshr; + } + } + } +#endif + } + return NULL; +} + +MSHR* +MSHRQueue::allocate(Packet * &pkt, int size) +{ + Addr aligned_addr = pkt->getAddr() & ~((Addr)size - 1); + MSHR *mshr = freeList.front(); + assert(mshr->getNumTargets() == 0); + freeList.pop_front(); + + if (!pkt->needsResponse()) { + mshr->allocateAsBuffer(pkt); + } else { + assert(size !=0); + mshr->allocate(pkt->cmd, aligned_addr, size, pkt); + allocatedTargets += 1; + } + mshr->allocIter = allocatedList.insert(allocatedList.end(), mshr); + mshr->readyIter = pendingList.insert(pendingList.end(), mshr); + + allocated += 1; + return mshr; +} + +MSHR* +MSHRQueue::allocateFetch(Addr addr, int size, Packet * &target) +{ + MSHR *mshr = freeList.front(); + assert(mshr->getNumTargets() == 0); + freeList.pop_front(); + mshr->allocate(Packet::ReadReq, addr, size, target); + mshr->allocIter = allocatedList.insert(allocatedList.end(), mshr); + mshr->readyIter = pendingList.insert(pendingList.end(), mshr); + + allocated += 1; + return mshr; +} + +MSHR* +MSHRQueue::allocateTargetList(Addr addr, int size) +{ + MSHR *mshr = freeList.front(); + assert(mshr->getNumTargets() == 0); + freeList.pop_front(); + Packet * dummy; + mshr->allocate(Packet::ReadReq, addr, size, dummy); + mshr->allocIter = allocatedList.insert(allocatedList.end(), mshr); + mshr->inService = true; + ++inServiceMSHRs; + ++allocated; + return mshr; +} + + +void +MSHRQueue::deallocate(MSHR* mshr) +{ + deallocateOne(mshr); +} + +MSHR::Iterator +MSHRQueue::deallocateOne(MSHR* mshr) +{ + MSHR::Iterator retval = allocatedList.erase(mshr->allocIter); + freeList.push_front(mshr); + allocated--; + allocatedTargets -= mshr->getNumTargets(); + if (mshr->inService) { + inServiceMSHRs--; + } else { + pendingList.erase(mshr->readyIter); + } + mshr->deallocate(); + return retval; +} + +void +MSHRQueue::moveToFront(MSHR *mshr) +{ + if (!mshr->inService) { + assert(mshr == *(mshr->readyIter)); + pendingList.erase(mshr->readyIter); + mshr->readyIter = pendingList.insert(pendingList.begin(), mshr); + } +} + +void +MSHRQueue::markInService(MSHR* mshr) +{ + //assert(mshr == pendingList.front()); + if (!mshr->pkt->needsResponse()) { + assert(mshr->getNumTargets() == 0); + deallocate(mshr); + return; + } + mshr->inService = true; + pendingList.erase(mshr->readyIter); + //mshr->readyIter = NULL; + inServiceMSHRs += 1; + //pendingList.pop_front(); +} + +void +MSHRQueue::markPending(MSHR* mshr, Packet::Command cmd) +{ + //assert(mshr->readyIter == NULL); + mshr->pkt->cmd = cmd; + mshr->pkt->flags &= ~SATISFIED; + mshr->inService = false; + --inServiceMSHRs; + /** + * @ todo might want to add rerequests to front of pending list for + * performance. + */ + mshr->readyIter = pendingList.insert(pendingList.end(), mshr); +} + +void +MSHRQueue::squash(int threadNum) +{ + MSHR::Iterator i = allocatedList.begin(); + MSHR::Iterator end = allocatedList.end(); + for (; i != end;) { + MSHR *mshr = *i; + if (mshr->threadNum == threadNum) { + while (mshr->hasTargets()) { + Packet * target = mshr->getTarget(); + mshr->popTarget(); + + assert(target->req->getThreadNum() == threadNum); + target = NULL; + } + assert(!mshr->hasTargets()); + assert(mshr->ntargets==0); + if (!mshr->inService) { + i = deallocateOne(mshr); + } else { + //mshr->pkt->flags &= ~CACHE_LINE_FILL; + ++i; + } + } else { + ++i; + } + } +} diff --git a/src/mem/cache/miss/mshr_queue.hh b/src/mem/cache/miss/mshr_queue.hh new file mode 100644 index 000000000..ea5f101b7 --- /dev/null +++ b/src/mem/cache/miss/mshr_queue.hh @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** @file + * Declaration of a structure to manage MSHRs. + */ + +#ifndef __MSHR_QUEUE_HH__ +#define __MSHR_QUEUE_HH__ + +#include <vector> +#include "mem/cache/miss/mshr.hh" + +/** + * A Class for maintaining a list of pending and allocated memory pktuests. + */ +class MSHRQueue { + private: + /** MSHR storage. */ + MSHR* registers; + /** Holds pointers to all allocated MSHRs. */ + MSHR::List allocatedList; + /** Holds pointers to MSHRs that haven't been sent to the bus. */ + MSHR::List pendingList; + /** Holds non allocated MSHRs. */ + MSHR::List freeList; + + // Parameters + /** + * The total number of MSHRs in this queue. This number is set as the + * number of MSHRs pktuested plus (numReserve - 1). This allows for + * the same number of effective MSHRs while still maintaining the reserve. + */ + const int numMSHRs; + + /** + * The number of MSHRs to hold in reserve. This is needed because copy + * operations can allocate upto 4 MSHRs at one time. + */ + const int numReserve; + + public: + /** The number of allocated MSHRs. */ + int allocated; + /** The number of MSHRs that have been forwarded to the bus. */ + int inServiceMSHRs; + /** The number of targets waiting for response. */ + int allocatedTargets; + + /** + * Create a queue with a given number of MSHRs. + * @param num_mshrs The number of MSHRs in this queue. + * @param reserve The minimum number of MSHRs needed to satisfy any access. + */ + MSHRQueue(int num_mshrs, int reserve = 1); + + /** Destructor */ + ~MSHRQueue(); + + /** + * Find the first MSHR that matches the provide address and asid. + * @param addr The address to find. + * @param asid The address space id. + * @return Pointer to the matching MSHR, null if not found. + */ + MSHR* findMatch(Addr addr) const; + + /** + * Find and return all the matching MSHRs in the provided vector. + * @param addr The address to find. + * @param asid The address space ID. + * @param matches The vector to return pointers to the matching MSHRs. + * @return True if any matches are found, false otherwise. + * @todo Typedef the vector?? + */ + bool findMatches(Addr addr, std::vector<MSHR*>& matches) const; + + /** + * Find any pending pktuests that overlap the given request. + * @param pkt The request to find. + * @return A pointer to the earliest matching MSHR. + */ + MSHR* findPending(Packet * &pkt) const; + + /** + * Allocates a new MSHR for the pktuest and size. This places the request + * as the first target in the MSHR. + * @param pkt The request to handle. + * @param size The number in bytes to fetch from memory. + * @return The a pointer to the MSHR allocated. + * + * @pre There are free MSHRs. + */ + MSHR* allocate(Packet * &pkt, int size = 0); + + /** + * Allocate a read pktuest for the given address, and places the given + * target on the target list. + * @param addr The address to fetch. + * @param asid The address space for the fetch. + * @param size The number of bytes to pktuest. + * @param target The first target for the pktuest. + * @return Pointer to the new MSHR. + */ + MSHR* allocateFetch(Addr addr, int size, Packet * &target); + + /** + * Allocate a target list for the given address. + * @param addr The address to fetch. + * @param asid The address space for the fetch. + * @param size The number of bytes to pktuest. + * @return Pointer to the new MSHR. + */ + MSHR* allocateTargetList(Addr addr, int size); + + /** + * Removes the given MSHR from the queue. This places the MSHR on the + * free list. + * @param mshr + */ + void deallocate(MSHR* mshr); + + /** + * Allocates a target to the given MSHR. Used to keep track of the number + * of outstanding targets. + * @param mshr The MSHR to allocate the target to. + * @param pkt The target request. + */ + void allocateTarget(MSHR* mshr, Packet * &pkt) + { + mshr->allocateTarget(pkt); + allocatedTargets += 1; + } + + /** + * Remove a MSHR from the queue. Returns an iterator into the allocatedList + * for faster squash implementation. + * @param mshr The MSHR to remove. + * @return An iterator to the next entry in the allocatedList. + */ + MSHR::Iterator deallocateOne(MSHR* mshr); + + /** + * Moves the MSHR to the front of the pending list if it is not in service. + * @param mshr The mshr to move. + */ + void moveToFront(MSHR *mshr); + + /** + * Mark the given MSHR as in service. This removes the MSHR from the + * pendingList. Deallocates the MSHR if it does not expect a response. + * @param mshr The MSHR to mark in service. + */ + void markInService(MSHR* mshr); + + /** + * Mark an in service mshr as pending, used to resend a pktuest. + * @param mshr The MSHR to resend. + * @param cmd The command to resend. + */ + void markPending(MSHR* mshr, Packet::Command cmd); + + /** + * Squash outstanding pktuests with the given thread number. If a request + * is in service, just squashes the targets. + * @param threadNum The thread to squash. + */ + void squash(int threadNum); + + /** + * Returns true if the pending list is not empty. + * @return True if there are outstanding pktuests. + */ + bool havePending() const + { + return !pendingList.empty(); + } + + /** + * Returns true if there are no free MSHRs. + * @return True if this queue is full. + */ + bool isFull() const + { + return (allocated > numMSHRs - numReserve); + } + + /** + * Returns the pktuest at the head of the pendingList. + * @return The next pktuest to service. + */ + Packet * getReq() const + { + if (pendingList.empty()) { + return NULL; + } + MSHR* mshr = pendingList.front(); + return mshr->pkt; + } + + /** + * Returns the number of outstanding targets. + * @return the number of allocated targets. + */ + int getAllocatedTargets() const + { + return allocatedTargets; + } + +}; + +#endif //__MSHR_QUEUE_HH__ diff --git a/src/mem/cache/prefetch/base_prefetcher.cc b/src/mem/cache/prefetch/base_prefetcher.cc new file mode 100644 index 000000000..5e50c48bd --- /dev/null +++ b/src/mem/cache/prefetch/base_prefetcher.cc @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Hardware Prefetcher Definition. + */ + +#include "base/trace.hh" +#include "mem/cache/base_cache.hh" +#include "mem/cache/prefetch/base_prefetcher.hh" +#include "mem/request.hh" +#include <list> + +BasePrefetcher::BasePrefetcher(int size, bool pageStop, bool serialSquash, + bool cacheCheckPush, bool onlyData) + :size(size), pageStop(pageStop), serialSquash(serialSquash), + cacheCheckPush(cacheCheckPush), only_data(onlyData) +{ +} + +void +BasePrefetcher::setCache(BaseCache *_cache) +{ + cache = _cache; + blkSize = cache->getBlockSize(); +} + +void +BasePrefetcher::regStats(const std::string &name) +{ + pfIdentified + .name(name + ".prefetcher.num_hwpf_identified") + .desc("number of hwpf identified") + ; + + pfMSHRHit + .name(name + ".prefetcher.num_hwpf_already_in_mshr") + .desc("number of hwpf that were already in mshr") + ; + + pfCacheHit + .name(name + ".prefetcher.num_hwpf_already_in_cache") + .desc("number of hwpf that were already in the cache") + ; + + pfBufferHit + .name(name + ".prefetcher.num_hwpf_already_in_prefetcher") + .desc("number of hwpf that were already in the prefetch queue") + ; + + pfRemovedFull + .name(name + ".prefetcher.num_hwpf_evicted") + .desc("number of hwpf removed due to no buffer left") + ; + + pfRemovedMSHR + .name(name + ".prefetcher.num_hwpf_removed_MSHR_hit") + .desc("number of hwpf removed because MSHR allocated") + ; + + pfIssued + .name(name + ".prefetcher.num_hwpf_issued") + .desc("number of hwpf issued") + ; + + pfSpanPage + .name(name + ".prefetcher.num_hwpf_span_page") + .desc("number of hwpf spanning a virtual page") + ; + + pfSquashed + .name(name + ".prefetcher.num_hwpf_squashed_from_miss") + .desc("number of hwpf that got squashed due to a miss aborting calculation time") + ; +} + +Packet * +BasePrefetcher::getPacket() +{ + DPRINTF(HWPrefetch, "%s:Requesting a hw_pf to issue\n", cache->name()); + + if (pf.empty()) { + DPRINTF(HWPrefetch, "%s:No HW_PF found\n", cache->name()); + return NULL; + } + + Packet * pkt; + bool keepTrying = false; + do { + pkt = *pf.begin(); + pf.pop_front(); + if (!cacheCheckPush) { + keepTrying = inCache(pkt); + } + if (pf.empty()) { + cache->clearMasterRequest(Request_PF); + if (keepTrying) return NULL; //None left, all were in cache + } + } while (keepTrying); + + pfIssued++; + return pkt; +} + +void +BasePrefetcher::handleMiss(Packet * &pkt, Tick time) +{ + if (!pkt->req->isUncacheable() && !(pkt->req->isInstRead() && only_data)) + { + //Calculate the blk address + Addr blkAddr = pkt->getAddr() & ~(Addr)(blkSize-1); + + //Check if miss is in pfq, if so remove it + std::list<Packet *>::iterator iter = inPrefetch(blkAddr); + if (iter != pf.end()) { + DPRINTF(HWPrefetch, "%s:Saw a miss to a queued prefetch, removing it\n", cache->name()); + pfRemovedMSHR++; + pf.erase(iter); + if (pf.empty()) + cache->clearMasterRequest(Request_PF); + } + + //Remove anything in queue with delay older than time + //since everything is inserted in time order, start from end + //and work until pf.empty() or time is earlier + //This is done to emulate Aborting the previous work on a new miss + //Needed for serial calculators like GHB + if (serialSquash) { + iter = pf.end(); + iter--; + while (!pf.empty() && ((*iter)->time >= time)) { + pfSquashed++; + pf.pop_back(); + iter--; + } + if (pf.empty()) + cache->clearMasterRequest(Request_PF); + } + + + std::list<Addr> addresses; + std::list<Tick> delays; + calculatePrefetch(pkt, addresses, delays); + + std::list<Addr>::iterator addr = addresses.begin(); + std::list<Tick>::iterator delay = delays.begin(); + while (addr != addresses.end()) + { + DPRINTF(HWPrefetch, "%s:Found a pf canidate, inserting into prefetch queue\n", cache->name()); + //temp calc this here... + pfIdentified++; + //create a prefetch memreq + Request * prefetchReq = new Request(*addr, blkSize, 0); + Packet * prefetch; + prefetch = new Packet(prefetchReq, Packet::HardPFReq, -1); + prefetch->allocate(); + prefetch->req->setThreadContext(pkt->req->getCpuNum(), + pkt->req->getThreadNum()); + + prefetch->time = time + (*delay); //@todo ADD LATENCY HERE + //... initialize + + //Check if it is already in the cache + if (cacheCheckPush) { + if (inCache(prefetch)) { + addr++; + delay++; + continue; + } + } + + //Check if it is already in the miss_queue + if (inMissQueue(prefetch->getAddr())) { + addr++; + delay++; + continue; + } + + //Check if it is already in the pf buffer + if (inPrefetch(prefetch->getAddr()) != pf.end()) { + pfBufferHit++; + addr++; + delay++; + continue; + } + + //We just remove the head if we are full + if (pf.size() == size) + { + DPRINTF(HWPrefetch, "%s:Inserting into prefetch queue, it was full removing oldest\n", cache->name()); + pfRemovedFull++; + pf.pop_front(); + } + + pf.push_back(prefetch); + prefetch->flags |= CACHE_LINE_FILL; + + //Make sure to request the bus, with proper delay + cache->setMasterRequest(Request_PF, prefetch->time); + + //Increment through the list + addr++; + delay++; + } + } +} + +std::list<Packet *>::iterator +BasePrefetcher::inPrefetch(Addr address) +{ + //Guaranteed to only be one match, we always check before inserting + std::list<Packet *>::iterator iter; + for (iter=pf.begin(); iter != pf.end(); iter++) { + if (((*iter)->getAddr() & ~(Addr)(blkSize-1)) == address) { + return iter; + } + } + return pf.end(); +} + + diff --git a/src/mem/cache/prefetch/base_prefetcher.hh b/src/mem/cache/prefetch/base_prefetcher.hh new file mode 100644 index 000000000..d7ea41961 --- /dev/null +++ b/src/mem/cache/prefetch/base_prefetcher.hh @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Miss and writeback queue declarations. + */ + +#ifndef __MEM_CACHE_PREFETCH_BASE_PREFETCHER_HH__ +#define __MEM_CACHE_PREFETCH_BASE_PREFETCHER_HH__ + +#include "mem/packet.hh" +#include <list> + +class BaseCache; +class BasePrefetcher +{ + protected: + + /** The Prefetch Queue. */ + std::list<Packet *> pf; + + // PARAMETERS + + /** The number of MSHRs in the Prefetch Queue. */ + const int size; + + /** Pointr to the parent cache. */ + BaseCache* cache; + + /** The block size of the parent cache. */ + int blkSize; + + /** Do we prefetch across page boundaries. */ + bool pageStop; + + /** Do we remove prefetches with later times than a new miss.*/ + bool serialSquash; + + /** Do we check if it is in the cache when inserting into buffer, + or removing.*/ + bool cacheCheckPush; + + /** Do we prefetch on only data reads, or on inst reads as well. */ + bool only_data; + + public: + + Stats::Scalar<> pfIdentified; + Stats::Scalar<> pfMSHRHit; + Stats::Scalar<> pfCacheHit; + Stats::Scalar<> pfBufferHit; + Stats::Scalar<> pfRemovedFull; + Stats::Scalar<> pfRemovedMSHR; + Stats::Scalar<> pfIssued; + Stats::Scalar<> pfSpanPage; + Stats::Scalar<> pfSquashed; + + void regStats(const std::string &name); + + public: + BasePrefetcher(int numMSHRS, bool pageStop, bool serialSquash, + bool cacheCheckPush, bool onlyData); + + virtual ~BasePrefetcher() {} + + void setCache(BaseCache *_cache); + + void handleMiss(Packet * &pkt, Tick time); + + Packet * getPacket(); + + bool havePending() + { + return !pf.empty(); + } + + virtual void calculatePrefetch(Packet * &pkt, + std::list<Addr> &addresses, + std::list<Tick> &delays) = 0; + + virtual bool inCache(Packet * &pkt) = 0; + + virtual bool inMissQueue(Addr address) = 0; + + std::list<Packet *>::iterator inPrefetch(Addr address); +}; + + +#endif //__MEM_CACHE_PREFETCH_BASE_PREFETCHER_HH__ diff --git a/src/mem/cache/prefetch/ghb_prefetcher.cc b/src/mem/cache/prefetch/ghb_prefetcher.cc new file mode 100644 index 000000000..247ec6e8b --- /dev/null +++ b/src/mem/cache/prefetch/ghb_prefetcher.cc @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + * Steve Reinhardt + */ + +/** + * @file + * GHB Prefetcher template instantiations. + */ + +#include "mem/cache/tags/cache_tags.hh" + +#include "mem/cache/tags/lru.hh" + +#include "base/compression/null_compression.hh" + +#include "mem/cache/miss/miss_queue.hh" +#include "mem/cache/miss/blocking_buffer.hh" + +#include "mem/cache/prefetch/ghb_prefetcher.hh" + +// Template Instantiations +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template class GHBPrefetcher<CacheTags<LRU,NullCompression>, MissQueue>; +template class GHBPrefetcher<CacheTags<LRU,NullCompression>, BlockingBuffer>; + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/prefetch/ghb_prefetcher.hh b/src/mem/cache/prefetch/ghb_prefetcher.hh new file mode 100644 index 000000000..c22b763d1 --- /dev/null +++ b/src/mem/cache/prefetch/ghb_prefetcher.hh @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Describes a ghb prefetcher based on template policies. + */ + +#ifndef __MEM_CACHE_PREFETCH_GHB_PREFETCHER_HH__ +#define __MEM_CACHE_PREFETCH_GHB_PREFETCHER_HH__ + +#include "base/misc.hh" // fatal, panic, and warn + +#include "mem/cache/prefetch/prefetcher.hh" + +/** + * A template-policy based cache. The behavior of the cache can be altered by + * supplying different template policies. TagStore handles all tag and data + * storage @sa TagStore. Buffering handles all misses and writes/writebacks + * @sa MissQueue. Coherence handles all coherence policy details @sa + * UniCoherence, SimpleMultiCoherence. + */ +template <class TagStore, class Buffering> +class GHBPrefetcher : public Prefetcher<TagStore, Buffering> +{ + protected: + + Buffering* mq; + TagStore* tags; + + Addr second_last_miss_addr[64/*MAX_CPUS*/]; + Addr last_miss_addr[64/*MAX_CPUS*/]; + + Tick latency; + int degree; + bool useCPUId; + + public: + + GHBPrefetcher(int size, bool pageStop, bool serialSquash, + bool cacheCheckPush, bool onlyData, + Tick latency, int degree, bool useCPUId) + :Prefetcher<TagStore, Buffering>(size, pageStop, serialSquash, + cacheCheckPush, onlyData), + latency(latency), degree(degree), useCPUId(useCPUId) + { + } + + ~GHBPrefetcher() {} + + void calculatePrefetch(Packet * &pkt, std::list<Addr> &addresses, + std::list<Tick> &delays) + { + Addr blkAddr = pkt->getAddr() & ~(Addr)(this->blkSize-1); + int cpuID = pkt->req->getCpuNum(); + if (!useCPUId) cpuID = 0; + + + int new_stride = blkAddr - last_miss_addr[cpuID]; + int old_stride = last_miss_addr[cpuID] - + second_last_miss_addr[cpuID]; + + second_last_miss_addr[cpuID] = last_miss_addr[cpuID]; + last_miss_addr[cpuID] = blkAddr; + + if (new_stride == old_stride) { + for (int d=1; d <= degree; d++) { + Addr newAddr = blkAddr + d * new_stride; + if (this->pageStop && + (blkAddr & ~(TheISA::VMPageSize - 1)) != + (newAddr & ~(TheISA::VMPageSize - 1))) + { + //Spanned the page, so now stop + this->pfSpanPage += degree - d + 1; + return; + } + else + { + addresses.push_back(newAddr); + delays.push_back(latency); + } + } + } + } +}; + +#endif // __MEM_CACHE_PREFETCH_GHB_PREFETCHER_HH__ diff --git a/src/mem/cache/prefetch/stride_prefetcher.cc b/src/mem/cache/prefetch/stride_prefetcher.cc new file mode 100644 index 000000000..93a096468 --- /dev/null +++ b/src/mem/cache/prefetch/stride_prefetcher.cc @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + * Steve Reinhardt + */ + +/** + * @file + * Stride Prefetcher template instantiations. + */ + +#include "mem/cache/tags/cache_tags.hh" + +#include "mem/cache/tags/lru.hh" + +#include "base/compression/null_compression.hh" + +#include "mem/cache/miss/miss_queue.hh" +#include "mem/cache/miss/blocking_buffer.hh" + +#include "mem/cache/prefetch/stride_prefetcher.hh" + +// Template Instantiations +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +template class StridePrefetcher<CacheTags<LRU,NullCompression>, MissQueue>; +template class StridePrefetcher<CacheTags<LRU,NullCompression>, BlockingBuffer>; + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/prefetch/stride_prefetcher.hh b/src/mem/cache/prefetch/stride_prefetcher.hh new file mode 100644 index 000000000..4a8ee7de4 --- /dev/null +++ b/src/mem/cache/prefetch/stride_prefetcher.hh @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Describes a strided prefetcher based on template policies. + */ + +#ifndef __MEM_CACHE_PREFETCH_STRIDE_PREFETCHER_HH__ +#define __MEM_CACHE_PREFETCH_STRIDE_PREFETCHER_HH__ + +#include "base/misc.hh" // fatal, panic, and warn + +#include "mem/cache/prefetch/prefetcher.hh" + +/** + * A template-policy based cache. The behavior of the cache can be altered by + * supplying different template policies. TagStore handles all tag and data + * storage @sa TagStore. Buffering handles all misses and writes/writebacks + * @sa MissQueue. Coherence handles all coherence policy details @sa + * UniCoherence, SimpleMultiCoherence. + */ +template <class TagStore, class Buffering> +class StridePrefetcher : public Prefetcher<TagStore, Buffering> +{ + protected: + + Buffering* mq; + TagStore* tags; + + class strideEntry + { + public: + Addr IAddr; + Addr MAddr; + int stride; + int64_t confidence; + +/* bool operator < (strideEntry a,strideEntry b) + { + if (a.confidence == b.confidence) { + return true; //?????? + } + else return a.confidence < b.confidence; + }*/ + }; + Addr* lastMissAddr[64/*MAX_CPUS*/]; + + std::list<strideEntry*> table[64/*MAX_CPUS*/]; + Tick latency; + int degree; + bool useCPUId; + + + public: + + StridePrefetcher(int size, bool pageStop, bool serialSquash, + bool cacheCheckPush, bool onlyData, + Tick latency, int degree, bool useCPUId) + :Prefetcher<TagStore, Buffering>(size, pageStop, serialSquash, + cacheCheckPush, onlyData), + latency(latency), degree(degree), useCPUId(useCPUId) + { + } + + ~StridePrefetcher() {} + + void calculatePrefetch(Packet * &pkt, std::list<Addr> &addresses, + std::list<Tick> &delays) + { +// Addr blkAddr = pkt->paddr & ~(Addr)(this->blkSize-1); + int cpuID = pkt->req->getCpuNum(); + if (!useCPUId) cpuID = 0; + + /* Scan Table for IAddr Match */ +/* std::list<strideEntry*>::iterator iter; + for (iter=table[cpuID].begin(); + iter !=table[cpuID].end(); + iter++) { + if ((*iter)->IAddr == pkt->pc) break; + } + + if (iter != table[cpuID].end()) { + //Hit in table + + int newStride = blkAddr - (*iter)->MAddr; + if (newStride == (*iter)->stride) { + (*iter)->confidence++; + } + else { + (*iter)->stride = newStride; + (*iter)->confidence--; + } + + (*iter)->MAddr = blkAddr; + + for (int d=1; d <= degree; d++) { + Addr newAddr = blkAddr + d * newStride; + if (this->pageStop && + (blkAddr & ~(TheISA::VMPageSize - 1)) != + (newAddr & ~(TheISA::VMPageSize - 1))) + { + //Spanned the page, so now stop + this->pfSpanPage += degree - d + 1; + return; + } + else + { + addresses.push_back(newAddr); + delays.push_back(latency); + } + } + } + else { + //Miss in table + //Find lowest confidence and replace + + } +*/ } +}; + +#endif // __MEM_CACHE_PREFETCH_STRIDE_PREFETCHER_HH__ diff --git a/src/mem/cache/prefetch/tagged_prefetcher.hh b/src/mem/cache/prefetch/tagged_prefetcher.hh new file mode 100644 index 000000000..17f500dd8 --- /dev/null +++ b/src/mem/cache/prefetch/tagged_prefetcher.hh @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Describes a tagged prefetcher based on template policies. + */ + +#ifndef __MEM_CACHE_PREFETCH_TAGGED_PREFETCHER_HH__ +#define __MEM_CACHE_PREFETCH_TAGGED_PREFETCHER_HH__ + +#include "mem/cache/prefetch/prefetcher.hh" + +/** + * A template-policy based cache. The behavior of the cache can be altered by + * supplying different template policies. TagStore handles all tag and data + * storage @sa TagStore. Buffering handles all misses and writes/writebacks + * @sa MissQueue. Coherence handles all coherence policy details @sa + * UniCoherence, SimpleMultiCoherence. + */ +template <class TagStore, class Buffering> +class TaggedPrefetcher : public Prefetcher<TagStore, Buffering> +{ + protected: + + Buffering* mq; + TagStore* tags; + + Tick latency; + int degree; + + public: + + TaggedPrefetcher(int size, bool pageStop, bool serialSquash, + bool cacheCheckPush, bool onlyData, + Tick latency, int degree); + + ~TaggedPrefetcher() {} + + void calculatePrefetch(Packet * &pkt, std::list<Addr> &addresses, + std::list<Tick> &delays); +}; + +#endif // __MEM_CACHE_PREFETCH_TAGGED_PREFETCHER_HH__ diff --git a/src/mem/cache/prefetch/tagged_prefetcher_impl.hh b/src/mem/cache/prefetch/tagged_prefetcher_impl.hh new file mode 100644 index 000000000..e554b3cec --- /dev/null +++ b/src/mem/cache/prefetch/tagged_prefetcher_impl.hh @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Describes a tagged prefetcher based on template policies. + */ + +#include "arch/isa_traits.hh" +#include "mem/cache/prefetch/tagged_prefetcher.hh" + +template <class TagStore, class Buffering> +TaggedPrefetcher<TagStore, Buffering>:: +TaggedPrefetcher(int size, bool pageStop, bool serialSquash, + bool cacheCheckPush, bool onlyData, + Tick latency, int degree) + :Prefetcher<TagStore, Buffering>(size, pageStop, serialSquash, + cacheCheckPush, onlyData), + latency(latency), degree(degree) +{ +} + +template <class TagStore, class Buffering> +void +TaggedPrefetcher<TagStore, Buffering>:: +calculatePrefetch(Packet * &pkt, std::list<Addr> &addresses, + std::list<Tick> &delays) +{ + Addr blkAddr = pkt->getAddr() & ~(Addr)(this->blkSize-1); + + for (int d=1; d <= degree; d++) { + Addr newAddr = blkAddr + d*(this->blkSize); + if (this->pageStop && + (blkAddr & ~(TheISA::VMPageSize - 1)) != + (newAddr & ~(TheISA::VMPageSize - 1))) + { + //Spanned the page, so now stop + this->pfSpanPage += degree - d + 1; + return; + } + else + { + addresses.push_back(newAddr); + delays.push_back(latency); + } + } +} + + diff --git a/src/mem/cache/tags/base_tags.cc b/src/mem/cache/tags/base_tags.cc new file mode 100644 index 000000000..153737300 --- /dev/null +++ b/src/mem/cache/tags/base_tags.cc @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Ron Dreslinski + */ + +/** + * @file + * Definitions of BaseTags. + */ + +#include "mem/cache/tags/base_tags.hh" + +#include "mem/cache/base_cache.hh" +#include "cpu/smt.hh" //maxThreadsPerCPU +#include "sim/sim_exit.hh" + +using namespace std; + +void +BaseTags::setCache(BaseCache *_cache) +{ + cache = _cache; + objName = cache->name(); +} + +void +BaseTags::regStats(const string &name) +{ + using namespace Stats; + replacements + .init(maxThreadsPerCPU) + .name(name + ".replacements") + .desc("number of replacements") + .flags(total) + ; + + tagsInUse + .name(name + ".tagsinuse") + .desc("Cycle average of tags in use") + ; + + totalRefs + .name(name + ".total_refs") + .desc("Total number of references to valid blocks.") + ; + + sampledRefs + .name(name + ".sampled_refs") + .desc("Sample count of references to valid blocks.") + ; + + avgRefs + .name(name + ".avg_refs") + .desc("Average number of references to valid blocks.") + ; + + avgRefs = totalRefs/sampledRefs; + + warmupCycle + .name(name + ".warmup_cycle") + .desc("Cycle when the warmup percentage was hit.") + ; + + registerExitCallback(new BaseTagsCallback(this)); +} diff --git a/src/mem/cache/tags/base_tags.hh b/src/mem/cache/tags/base_tags.hh new file mode 100644 index 000000000..b7b0c7ef0 --- /dev/null +++ b/src/mem/cache/tags/base_tags.hh @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Ron Dreslinski + */ + +/** + * @file + * Declaration of a common base class for cache tagstore objects. + */ + +#ifndef __BASE_TAGS_HH__ +#define __BASE_TAGS_HH__ + +#include <string> +#include "base/statistics.hh" +#include "base/callback.hh" + +class BaseCache; + +/** + * A common base class of Cache tagstore objects. + */ +class BaseTags +{ + protected: + /** Pointer to the parent cache. */ + BaseCache *cache; + + /** Local copy of the parent cache name. Used for DPRINTF. */ + std::string objName; + + /** + * The number of tags that need to be touched to meet the warmup + * percentage. + */ + int warmupBound; + /** Marked true when the cache is warmed up. */ + bool warmedUp; + + // Statistics + /** + * @addtogroup CacheStatistics + * @{ + */ + + /** Number of replacements of valid blocks per thread. */ + Stats::Vector<> replacements; + /** Per cycle average of the number of tags that hold valid data. */ + Stats::Average<> tagsInUse; + + /** The total number of references to a block before it is replaced. */ + Stats::Scalar<> totalRefs; + + /** + * The number of reference counts sampled. This is different from + * replacements because we sample all the valid blocks when the simulator + * exits. + */ + Stats::Scalar<> sampledRefs; + + /** + * Average number of references to a block before is was replaced. + * @todo This should change to an average stat once we have them. + */ + Stats::Formula avgRefs; + + /** The cycle that the warmup percentage was hit. */ + Stats::Scalar<> warmupCycle; + /** + * @} + */ + + public: + + /** + * Destructor. + */ + virtual ~BaseTags() {} + + /** + * Set the parent cache back pointer. Also copies the cache name to + * objName. + * @param _cache Pointer to parent cache. + */ + void setCache(BaseCache *_cache); + + /** + * Return the parent cache name. + * @return the parent cache name. + */ + const std::string &name() const + { + return objName; + } + + /** + * Register local statistics. + * @param name The name to preceed each statistic name. + */ + void regStats(const std::string &name); + + /** + * Average in the reference count for valid blocks when the simulation + * exits. + */ + virtual void cleanupRefs() {} +}; + +class BaseTagsCallback : public Callback +{ + BaseTags *tags; + public: + BaseTagsCallback(BaseTags *t) : tags(t) {} + virtual void process() { tags->cleanupRefs(); }; +}; + +#endif //__BASE_TAGS_HH__ diff --git a/src/mem/cache/tags/fa_lru.cc b/src/mem/cache/tags/fa_lru.cc new file mode 100644 index 000000000..784ba1311 --- /dev/null +++ b/src/mem/cache/tags/fa_lru.cc @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definitions a fully associative LRU tagstore. + */ + +#include <sstream> + +#include <assert.h> + +#include "mem/cache/tags/fa_lru.hh" +#include "base/intmath.hh" +#include "base/misc.hh" + +using namespace std; + +FALRU::FALRU(int _blkSize, int _size, int hit_latency) + : blkSize(_blkSize), size(_size), + numBlks(size/blkSize), hitLatency(hit_latency) +{ + if (!isPowerOf2(blkSize)) + fatal("cache block size (in bytes) `%d' must be a power of two", + blkSize); + if (!(hitLatency > 0)) + fatal("Access latency in cycles must be at least one cycle"); + if (!isPowerOf2(size)) + fatal("Cache Size must be power of 2 for now"); + + // Track all cache sizes from 128K up by powers of 2 + numCaches = floorLog2(size) - 17; + if (numCaches >0){ + cacheBoundaries = new FALRUBlk *[numCaches]; + cacheMask = (1 << numCaches) - 1; + } else { + cacheMask = 0; + } + + warmedUp = false; + warmupBound = size/blkSize; + + blks = new FALRUBlk[numBlks]; + head = &(blks[0]); + tail = &(blks[numBlks-1]); + + head->prev = NULL; + head->next = &(blks[1]); + head->inCache = cacheMask; + + tail->prev = &(blks[numBlks-2]); + tail->next = NULL; + tail->inCache = 0; + + int index = (1 << 17) / blkSize; + int j = 0; + int flags = cacheMask; + for (int i = 1; i < numBlks-1; i++) { + blks[i].inCache = flags; + if (i == index - 1){ + cacheBoundaries[j] = &(blks[i]); + flags &= ~ (1<<j); + ++j; + index = index << 1; + } + blks[i].prev = &(blks[i-1]); + blks[i].next = &(blks[i+1]); + blks[i].isTouched = false; + } + assert(j == numCaches); + assert(index == numBlks); + //assert(check()); +} + +void +FALRU::regStats(const string &name) +{ + using namespace Stats; + BaseTags::regStats(name); + hits + .init(numCaches+1) + .name(name + ".falru_hits") + .desc("The number of hits in each cache size.") + ; + misses + .init(numCaches+1) + .name(name + ".falru_misses") + .desc("The number of misses in each cache size.") + ; + accesses + .name(name + ".falru_accesses") + .desc("The number of accesses to the FA LRU cache.") + ; + + for (int i = 0; i < numCaches+1; ++i) { + stringstream size_str; + if (i < 3){ + size_str << (1<<(i+7)) <<"K"; + } else { + size_str << (1<<(i-3)) <<"M"; + } + + hits.subname(i, size_str.str()); + hits.subdesc(i, "Hits in a " + size_str.str() +" cache"); + misses.subname(i, size_str.str()); + misses.subdesc(i, "Misses in a " + size_str.str() +" cache"); + } +} + +FALRUBlk * +FALRU::hashLookup(Addr addr) const +{ + tagIterator iter = tagHash.find(addr); + if (iter != tagHash.end()) { + return (*iter).second; + } + return NULL; +} + +bool +FALRU::probe(Addr addr) const +{ + Addr blkAddr = blkAlign(addr); + FALRUBlk* blk = hashLookup(blkAddr); + return blk && blk->tag == blkAddr && blk->isValid(); +} + +void +FALRU::invalidateBlk(Addr addr) +{ + Addr blkAddr = blkAlign(addr); + FALRUBlk* blk = (*tagHash.find(blkAddr)).second; + if (blk) { + assert(blk->tag == blkAddr); + blk->status = 0; + blk->isTouched = false; + tagsInUse--; + } +} + +FALRUBlk* +FALRU::findBlock(Addr addr, int &lat, int *inCache) +{ + accesses++; + int tmp_in_cache = 0; + Addr blkAddr = blkAlign(addr); + FALRUBlk* blk = hashLookup(blkAddr); + + if (blk && blk->isValid()) { + assert(blk->tag == blkAddr); + tmp_in_cache = blk->inCache; + for (int i = 0; i < numCaches; i++) { + if (1<<i & blk->inCache) { + hits[i]++; + } else { + misses[i]++; + } + } + hits[numCaches]++; + if (blk != head){ + moveToHead(blk); + } + } else { + blk = NULL; + for (int i = 0; i < numCaches+1; ++i) { + misses[i]++; + } + } + if (inCache) { + *inCache = tmp_in_cache; + } + + lat = hitLatency; + //assert(check()); + return blk; +} + +FALRUBlk* +FALRU::findBlock(Packet * &pkt, int &lat, int *inCache) +{ + Addr addr = pkt->getAddr(); + + accesses++; + int tmp_in_cache = 0; + Addr blkAddr = blkAlign(addr); + FALRUBlk* blk = hashLookup(blkAddr); + + if (blk && blk->isValid()) { + assert(blk->tag == blkAddr); + tmp_in_cache = blk->inCache; + for (int i = 0; i < numCaches; i++) { + if (1<<i & blk->inCache) { + hits[i]++; + } else { + misses[i]++; + } + } + hits[numCaches]++; + if (blk != head){ + moveToHead(blk); + } + } else { + blk = NULL; + for (int i = 0; i < numCaches+1; ++i) { + misses[i]++; + } + } + if (inCache) { + *inCache = tmp_in_cache; + } + + lat = hitLatency; + //assert(check()); + return blk; +} + +FALRUBlk* +FALRU::findBlock(Addr addr) const +{ + Addr blkAddr = blkAlign(addr); + FALRUBlk* blk = hashLookup(blkAddr); + + if (blk && blk->isValid()) { + assert(blk->tag == blkAddr); + } else { + blk = NULL; + } + return blk; +} + +FALRUBlk* +FALRU::findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks) +{ + FALRUBlk * blk = tail; + assert(blk->inCache == 0); + moveToHead(blk); + tagHash.erase(blk->tag); + tagHash[blkAlign(pkt->getAddr())] = blk; + if (blk->isValid()) { + replacements[0]++; + } else { + tagsInUse++; + blk->isTouched = true; + if (!warmedUp && tagsInUse.value() >= warmupBound) { + warmedUp = true; + warmupCycle = curTick; + } + } + //assert(check()); + return blk; +} + +void +FALRU::moveToHead(FALRUBlk *blk) +{ + int updateMask = blk->inCache ^ cacheMask; + for (int i = 0; i < numCaches; i++){ + if ((1<<i) & updateMask) { + cacheBoundaries[i]->inCache &= ~(1<<i); + cacheBoundaries[i] = cacheBoundaries[i]->prev; + } else if (cacheBoundaries[i] == blk) { + cacheBoundaries[i] = blk->prev; + } + } + blk->inCache = cacheMask; + if (blk != head) { + if (blk == tail){ + assert(blk->next == NULL); + tail = blk->prev; + tail->next = NULL; + } else { + blk->prev->next = blk->next; + blk->next->prev = blk->prev; + } + blk->next = head; + blk->prev = NULL; + head->prev = blk; + head = blk; + } +} + +bool +FALRU::check() +{ + FALRUBlk* blk = head; + int size = 0; + int boundary = 1<<17; + int j = 0; + int flags = cacheMask; + while (blk) { + size += blkSize; + if (blk->inCache != flags) { + return false; + } + if (size == boundary && blk != tail) { + if (cacheBoundaries[j] != blk) { + return false; + } + flags &=~(1 << j); + boundary = boundary<<1; + ++j; + } + blk = blk->next; + } + return true; +} diff --git a/src/mem/cache/tags/fa_lru.hh b/src/mem/cache/tags/fa_lru.hh new file mode 100644 index 000000000..f9d4d7109 --- /dev/null +++ b/src/mem/cache/tags/fa_lru.hh @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of a fully associative LRU tag store. + */ + +#ifndef __FA_LRU_HH__ +#define __FA_LRU_HH__ + +#include <list> + +#include "mem/cache/cache_blk.hh" +#include "mem/packet.hh" +#include "base/hashmap.hh" +#include "mem/cache/tags/base_tags.hh" + +/** + * A fully associative cache block. + */ +class FALRUBlk : public CacheBlk +{ +public: + /** The previous block in LRU order. */ + FALRUBlk *prev; + /** The next block in LRU order. */ + FALRUBlk *next; + /** Has this block been touched? */ + bool isTouched; + + /** + * A bit mask of the sizes of cache that this block is resident in. + * Each bit represents a power of 2 in MB size cache. + * If bit 0 is set, this block is in a 1MB cache + * If bit 2 is set, this block is in a 4MB cache, etc. + * There is one bit for each cache smaller than the full size (default + * 16MB). + */ + int inCache; +}; + +/** + * A fully associative LRU cache. Keeps statistics for accesses to a number of + * cache sizes at once. + */ +class FALRU : public BaseTags +{ + public: + /** Typedef the block type used in this class. */ + typedef FALRUBlk BlkType; + /** Typedef a list of pointers to the local block type. */ + typedef std::list<FALRUBlk*> BlkList; + protected: + /** The block size of the cache. */ + const int blkSize; + /** The size of the cache. */ + const int size; + /** The number of blocks in the cache. */ + const int numBlks; // calculated internally + /** The hit latency of the cache. */ + const int hitLatency; + + /** Array of pointers to blocks at the cache size boundaries. */ + FALRUBlk **cacheBoundaries; + /** A mask for the FALRUBlk::inCache bits. */ + int cacheMask; + /** The number of different size caches being tracked. */ + int numCaches; + + /** The cache blocks. */ + FALRUBlk *blks; + + /** The MRU block. */ + FALRUBlk *head; + /** The LRU block. */ + FALRUBlk *tail; + + /** Hash table type mapping addresses to cache block pointers. */ + typedef m5::hash_map<Addr, FALRUBlk *, m5::hash<Addr> > hash_t; + /** Iterator into the address hash table. */ + typedef hash_t::const_iterator tagIterator; + + /** The address hash table. */ + hash_t tagHash; + + /** + * Find the cache block for the given address. + * @param addr The address to find. + * @return The cache block of the address, if any. + */ + FALRUBlk * hashLookup(Addr addr) const; + + /** + * Move a cache block to the MRU position. + * @param blk The block to promote. + */ + void moveToHead(FALRUBlk *blk); + + /** + * Check to make sure all the cache boundaries are still where they should + * be. Used for debugging. + * @return True if everything is correct. + */ + bool check(); + + /** + * @defgroup FALRUStats Fully Associative LRU specific statistics + * The FA lru stack lets us track multiple cache sizes at once. These + * statistics track the hits and misses for different cache sizes. + * @{ + */ + + /** Hits in each cache size >= 128K. */ + Stats::Vector<> hits; + /** Misses in each cache size >= 128K. */ + Stats::Vector<> misses; + /** Total number of accesses. */ + Stats::Scalar<> accesses; + + /** + * @} + */ + +public: + /** + * Construct and initialize this cache tagstore. + * @param blkSize The block size of the cache. + * @param size The size of the cache. + * @param hit_latency The hit latency of the cache. + */ + FALRU(int blkSize, int size, int hit_latency); + + /** + * Register the stats for this object. + * @param name The name to prepend to the stats name. + */ + void regStats(const std::string &name); + + /** + * Return true if the address is found in the cache. + * @param asid The address space ID. + * @param addr The address to look for. + * @return True if the address is in the cache. + */ + bool probe(Addr addr) const; + + /** + * Invalidate the cache block that contains the given addr. + * @param asid The address space ID. + * @param addr The address to invalidate. + */ + void invalidateBlk(Addr addr); + + /** + * Find the block in the cache and update the replacement data. Returns + * the access latency and the in cache flags as a side effect + * @param addr The address to look for. + * @param asid The address space ID. + * @param lat The latency of the access. + * @param inCache The FALRUBlk::inCache flags. + * @return Pointer to the cache block. + */ + FALRUBlk* findBlock(Addr addr, int &lat, int *inCache = 0); + + /** + * Find the block in the cache and update the replacement data. Returns + * the access latency and the in cache flags as a side effect + * @param pkt The req whose block to find + * @param lat The latency of the access. + * @param inCache The FALRUBlk::inCache flags. + * @return Pointer to the cache block. + */ + FALRUBlk* findBlock(Packet * &pkt, int &lat, int *inCache = 0); + + /** + * Find the block in the cache, do not update the replacement data. + * @param addr The address to look for. + * @param asid The address space ID. + * @return Pointer to the cache block. + */ + FALRUBlk* findBlock(Addr addr) const; + + /** + * Find a replacement block for the address provided. + * @param pkt The request to a find a replacement candidate for. + * @param writebacks List for any writebacks to be performed. + * @param compress_blocks List of blocks to compress, for adaptive comp. + * @return The block to place the replacement in. + */ + FALRUBlk* findReplacement(Packet * &pkt, PacketList & writebacks, + BlkList &compress_blocks); + + /** + * Return the hit latency of this cache. + * @return The hit latency. + */ + int getHitLatency() const + { + return hitLatency; + } + + /** + * Return the block size of this cache. + * @return The block size. + */ + int getBlockSize() + { + return blkSize; + } + + /** + * Return the subblock size of this cache, always the block size. + * @return The block size. + */ + int getSubBlockSize() + { + return blkSize; + } + + /** + * Align an address to the block size. + * @param addr the address to align. + * @return The aligned address. + */ + Addr blkAlign(Addr addr) const + { + return (addr & ~(Addr)(blkSize-1)); + } + + /** + * Generate the tag from the addres. For fully associative this is just the + * block address. + * @param addr The address to get the tag from. + * @param blk ignored here + * @return The tag. + */ + Addr extractTag(Addr addr, FALRUBlk *blk) const + { + return blkAlign(addr); + } + + /** + * Return the set of an address. Only one set in a fully associative cache. + * @param addr The address to get the set from. + * @return 0. + */ + int extractSet(Addr addr) const + { + return 0; + } + + /** + * Calculate the block offset of an address. + * @param addr the address to get the offset of. + * @return the block offset. + */ + int extractBlkOffset(Addr addr) const + { + return (addr & (Addr)(blkSize-1)); + } + + /** + * Regenerate the block address from the tag and the set. + * @param tag The tag of the block. + * @param set The set the block belongs to. + * @return the block address. + */ + Addr regenerateBlkAddr(Addr tag, int set) const + { + return (tag); + } + + /** + * Read the data out of the internal storage of a cache block. FALRU + * currently doesn't support data storage. + * @param blk The cache block to read. + * @param data The buffer to read the data into. + * @return The data from the cache block. + */ + void readData(FALRUBlk *blk, uint8_t *data) + { + } + + /** + * Write data into the internal storage of a cache block. FALRU + * currently doesn't support data storage. + * @param blk The cache block to be written. + * @param data The data to write. + * @param size The number of bytes to write. + * @param writebacks A list for any writebacks to be performed. May be + * needed when writing to a compressed block. + */ + void writeData(FALRUBlk *blk, uint8_t *data, int size, + PacketList &writebacks) + { + } + + /** + * Unimplemented. Perform a cache block copy from block aligned addresses. + * @param source The block aligned source address. + * @param dest The block aligned destination adddress. + * @param asid The address space ID. + * @param writebacks List for any generated writeback pktuests. + */ + void doCopy(Addr source, Addr dest, PacketList &writebacks) + { + } + + /** + * Unimplemented. + */ + void fixCopy(Packet * &pkt, PacketList &writebacks) + { + } + +}; + +#endif diff --git a/src/mem/cache/tags/iic.cc b/src/mem/cache/tags/iic.cc new file mode 100644 index 000000000..1377c8613 --- /dev/null +++ b/src/mem/cache/tags/iic.cc @@ -0,0 +1,877 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definitions of the Indirect Index Cache tagstore. + */ + +#include <algorithm> +#include <string> +#include <vector> + +#include <math.h> + +#include "mem/cache/base_cache.hh" +#include "mem/cache/tags/iic.hh" +#include "base/intmath.hh" +#include "sim/root.hh" // for curTick + +#include "base/trace.hh" // for DPRINTF + + +using namespace std; + +/** Track the number of accesses to each cache set. */ +#define PROFILE_IIC 1 + +IIC::IIC(IIC::Params ¶ms) : + hashSets(params.numSets), blkSize(params.blkSize), assoc(params.assoc), + hitLatency(params.hitLatency), subSize(params.subblockSize), + numSub(blkSize/subSize), + trivialSize((floorLog2(params.size/subSize)*numSub)/8), + tagShift(floorLog2(blkSize)), blkMask(blkSize - 1), + subShift(floorLog2(subSize)), subMask(numSub - 1), + hashDelay(params.hashDelay), + numBlocks(params.size/subSize), + numTags(hashSets * assoc + params.size/blkSize -1), + numSecondary(params.size/blkSize), + tagNull(numTags), + primaryBound(hashSets * assoc) +{ + int i; + + // Check parameters + if (blkSize < 4 || !isPowerOf2(blkSize)) { + fatal("Block size must be at least 4 and a power of 2"); + } + if (hashSets <= 0 || !isPowerOf2(hashSets)) { + fatal("# of hashsets must be non-zero and a power of 2"); + } + if (assoc <= 0) { + fatal("associativity must be greater than zero"); + } + if (hitLatency <= 0) { + fatal("access latency must be greater than zero"); + } + if (numSub*subSize != blkSize) { + fatal("blocksize must be evenly divisible by subblock size"); + } + + // debug stuff + freeSecond = numSecondary; + + warmedUp = false; + warmupBound = params.size/blkSize; + + // Replacement Policy Initialization + repl = params.rp; + repl->setIIC(this); + + //last_miss_time = 0 + + // allocate data reference counters + dataReferenceCount = new int[numBlocks]; + memset(dataReferenceCount, 0, numBlocks*sizeof(int)); + + // Allocate storage for both internal data and block fast access data. + // We allocate it as one large chunk to reduce overhead and to make + // deletion easier. + int data_index = 0; + dataStore = new uint8_t[(numBlocks + numTags) * blkSize]; + dataBlks = new uint8_t*[numBlocks]; + for (i = 0; i < numBlocks; ++i) { + dataBlks[i] = &dataStore[data_index]; + freeDataBlock(i); + data_index += subSize; + } + + assert(data_index == numBlocks * subSize); + + // allocate and init tag store + tagStore = new IICTag[numTags]; + + int blkIndex = 0; + // allocate and init sets + sets = new IICSet[hashSets]; + for (i = 0; i < hashSets; ++i) { + sets[i].assoc = assoc; + sets[i].tags = new IICTag*[assoc]; + sets[i].chain_ptr = tagNull; + + for (int j = 0; j < assoc; ++j) { + IICTag *tag = &tagStore[blkIndex++]; + tag->chain_ptr = tagNull; + tag->data_ptr.resize(numSub); + tag->size = blkSize; + tag->trivialData = new uint8_t[trivialSize]; + tag->numData = 0; + sets[i].tags[j] = tag; + tag->set = i; + tag->data = &dataStore[data_index]; + data_index += blkSize; + } + } + + assert(blkIndex == primaryBound); + + for (i = primaryBound; i < tagNull; i++) { + tagStore[i].chain_ptr = i+1; + //setup data ptrs to subblocks + tagStore[i].data_ptr.resize(numSub); + tagStore[i].size = blkSize; + tagStore[i].trivialData = new uint8_t[trivialSize]; + tagStore[i].numData = 0; + tagStore[i].set = 0; + tagStore[i].data = &dataStore[data_index]; + data_index += blkSize; + } + freelist = primaryBound; +} + +IIC::~IIC() +{ + delete [] dataReferenceCount; + delete [] dataStore; + delete [] tagStore; + delete [] sets; +} + +/* register cache stats */ +void +IIC::regStats(const string &name) +{ + using namespace Stats; + + BaseTags::regStats(name); + + hitHashDepth.init(0, 20, 1); + missHashDepth.init(0, 20, 1); + setAccess.init(0, hashSets, 1); + + /** IIC Statistics */ + hitHashDepth + .name(name + ".hit_hash_depth_dist") + .desc("Dist. of Hash lookup depths") + .flags(pdf) + ; + + missHashDepth + .name(name + ".miss_hash_depth_dist") + .desc("Dist. of Hash lookup depths") + .flags(pdf) + ; + + repl->regStats(name); + + if (PROFILE_IIC) + setAccess + .name(name + ".set_access_dist") + .desc("Dist. of Accesses across sets") + .flags(pdf) + ; + + missDepthTotal + .name(name + ".miss_depth_total") + .desc("Total of miss depths") + ; + + hashMiss + .name(name + ".hash_miss") + .desc("Total of misses in hash table") + ; + + hitDepthTotal + .name(name + ".hit_depth_total") + .desc("Total of hit depths") + ; + + hashHit + .name(name + ".hash_hit") + .desc("Total of hites in hash table") + ; +} + +// probe cache for presence of given block. +bool +IIC::probe(Addr addr) const +{ + return (findBlock(addr) != NULL); +} + +IICTag* +IIC::findBlock(Addr addr, int &lat) +{ + Addr tag = extractTag(addr); + unsigned set = hash(addr); + int set_lat; + + unsigned long chain_ptr; + + if (PROFILE_IIC) + setAccess.sample(set); + + IICTag *tag_ptr = sets[set].findTag(tag, chain_ptr); + set_lat = 1; + if (tag_ptr == NULL && chain_ptr != tagNull) { + int secondary_depth; + tag_ptr = secondaryChain(tag, chain_ptr, &secondary_depth); + set_lat += secondary_depth; + // set depth for statistics fix this later!!! egh + sets[set].depth = set_lat; + + if (tag_ptr != NULL) { + /* need to move tag into primary table */ + // need to preserve chain: fix this egh + sets[set].tags[assoc-1]->chain_ptr = tag_ptr->chain_ptr; + tagSwap(tag_ptr - tagStore, sets[set].tags[assoc-1] - tagStore); + tag_ptr = sets[set].findTag(tag, chain_ptr); + assert(tag_ptr!=NULL); + } + + } + set_lat = set_lat * hashDelay + hitLatency; + if (tag_ptr != NULL) { + // IIC replacement: if this is not the first element of + // list, reorder + sets[set].moveToHead(tag_ptr); + + hitHashDepth.sample(sets[set].depth); + hashHit++; + hitDepthTotal += sets[set].depth; + tag_ptr->status |= BlkReferenced; + lat = set_lat; + if (tag_ptr->whenReady > curTick && tag_ptr->whenReady - curTick > set_lat) { + lat = tag_ptr->whenReady - curTick; + } + + tag_ptr->refCount += 1; + } + else { + // fall through: cache block not found, not a hit... + missHashDepth.sample(sets[set].depth); + hashMiss++; + missDepthTotal += sets[set].depth; + lat = set_lat; + } + return tag_ptr; +} + +IICTag* +IIC::findBlock(Packet * &pkt, int &lat) +{ + Addr addr = pkt->getAddr(); + + Addr tag = extractTag(addr); + unsigned set = hash(addr); + int set_lat; + + unsigned long chain_ptr; + + if (PROFILE_IIC) + setAccess.sample(set); + + IICTag *tag_ptr = sets[set].findTag(tag, chain_ptr); + set_lat = 1; + if (tag_ptr == NULL && chain_ptr != tagNull) { + int secondary_depth; + tag_ptr = secondaryChain(tag, chain_ptr, &secondary_depth); + set_lat += secondary_depth; + // set depth for statistics fix this later!!! egh + sets[set].depth = set_lat; + + if (tag_ptr != NULL) { + /* need to move tag into primary table */ + // need to preserve chain: fix this egh + sets[set].tags[assoc-1]->chain_ptr = tag_ptr->chain_ptr; + tagSwap(tag_ptr - tagStore, sets[set].tags[assoc-1] - tagStore); + tag_ptr = sets[set].findTag(tag, chain_ptr); + assert(tag_ptr!=NULL); + } + + } + set_lat = set_lat * hashDelay + hitLatency; + if (tag_ptr != NULL) { + // IIC replacement: if this is not the first element of + // list, reorder + sets[set].moveToHead(tag_ptr); + + hitHashDepth.sample(sets[set].depth); + hashHit++; + hitDepthTotal += sets[set].depth; + tag_ptr->status |= BlkReferenced; + lat = set_lat; + if (tag_ptr->whenReady > curTick && tag_ptr->whenReady - curTick > set_lat) { + lat = tag_ptr->whenReady - curTick; + } + + tag_ptr->refCount += 1; + } + else { + // fall through: cache block not found, not a hit... + missHashDepth.sample(sets[set].depth); + hashMiss++; + missDepthTotal += sets[set].depth; + lat = set_lat; + } + return tag_ptr; +} + +IICTag* +IIC::findBlock(Addr addr) const +{ + Addr tag = extractTag(addr); + unsigned set = hash(addr); + + unsigned long chain_ptr; + + IICTag *tag_ptr = sets[set].findTag(tag, chain_ptr); + if (tag_ptr == NULL && chain_ptr != tagNull) { + int secondary_depth; + tag_ptr = secondaryChain(tag, chain_ptr, &secondary_depth); + } + return tag_ptr; +} + + +IICTag* +IIC::findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks) +{ + DPRINTF(IIC, "Finding Replacement for %x\n", pkt->getAddr()); + unsigned set = hash(pkt->getAddr()); + IICTag *tag_ptr; + unsigned long *tmp_data = new unsigned long[numSub]; + + // Get a enough subblocks for a full cache line + for (int i = 0; i < numSub; ++i){ + tmp_data[i] = getFreeDataBlock(writebacks); + assert(dataReferenceCount[tmp_data[i]]==0); + } + + tag_ptr = getFreeTag(set, writebacks); + + tag_ptr->set = set; + for (int i=0; i< numSub; ++i) { + tag_ptr->data_ptr[i] = tmp_data[i]; + dataReferenceCount[tag_ptr->data_ptr[i]]++; + } + tag_ptr->numData = numSub; + assert(tag_ptr - tagStore < primaryBound); // make sure it is in primary + tag_ptr->chain_ptr = tagNull; + sets[set].moveToHead(tag_ptr); + delete [] tmp_data; + + list<unsigned long> tag_indexes; + repl->doAdvance(tag_indexes); + while (!tag_indexes.empty()) { + if (!tagStore[tag_indexes.front()].isCompressed()) { + compress_blocks.push_back(&tagStore[tag_indexes.front()]); + } + tag_indexes.pop_front(); + } + + tag_ptr->re = (void*)repl->add(tag_ptr-tagStore); + + return tag_ptr; +} + +void +IIC::freeReplacementBlock(PacketList & writebacks) +{ + IICTag *tag_ptr; + unsigned long data_ptr; + /* consult replacement policy */ + tag_ptr = &tagStore[repl->getRepl()]; + assert(tag_ptr->isValid()); + + DPRINTF(Cache, "Replacing %x in IIC: %s\n", + regenerateBlkAddr(tag_ptr->tag,0), + tag_ptr->isModified() ? "writeback" : "clean"); + /* write back replaced block data */ + if (tag_ptr && (tag_ptr->isValid())) { + replacements[0]++; + totalRefs += tag_ptr->refCount; + ++sampledRefs; + tag_ptr->refCount = 0; + + if (tag_ptr->isModified()) { +/* Packet * writeback = + buildWritebackReq(regenerateBlkAddr(tag_ptr->tag, 0), + tag_ptr->req->asid, tag_ptr->xc, blkSize, + tag_ptr->data, + tag_ptr->size); +*/ + Request *writebackReq = new Request(regenerateBlkAddr(tag_ptr->tag, 0), + blkSize, 0); + Packet *writeback = new Packet(writebackReq, Packet::Writeback, -1); + writeback->allocate(); + memcpy(writeback->getPtr<uint8_t>(), tag_ptr->data, blkSize); + + writebacks.push_back(writeback); + } + } + + // free the data blocks + for (int i = 0; i < tag_ptr->numData; ++i) { + data_ptr = tag_ptr->data_ptr[i]; + assert(dataReferenceCount[data_ptr]>0); + if (--dataReferenceCount[data_ptr] == 0) { + freeDataBlock(data_ptr); + } + } + freeTag(tag_ptr); +} + +unsigned long +IIC::getFreeDataBlock(PacketList & writebacks) +{ + struct IICTag *tag_ptr; + unsigned long data_ptr; + + tag_ptr = NULL; + /* find data block */ + while (blkFreelist.empty()) { + freeReplacementBlock(writebacks); + } + + data_ptr = blkFreelist.front(); + blkFreelist.pop_front(); + DPRINTF(IICMore,"Found free data at %d\n",data_ptr); + return data_ptr; +} + + + +IICTag* +IIC::getFreeTag(int set, PacketList & writebacks) +{ + unsigned long tag_index; + IICTag *tag_ptr; + // Add new tag + tag_ptr = sets[set].findFree(); + // if no free in primary, and secondary exists + if (!tag_ptr && numSecondary) { + // need to spill a tag into secondary storage + while (freelist == tagNull) { + // get replacements until one is in secondary + freeReplacementBlock(writebacks); + } + + tag_index = freelist; + freelist = tagStore[freelist].chain_ptr; + freeSecond--; + + assert(tag_index != tagNull); + tagSwap(tag_index, sets[set].tags[assoc-1] - tagStore); + tagStore[tag_index].chain_ptr = sets[set].chain_ptr; + sets[set].chain_ptr = tag_index; + + tag_ptr = sets[set].tags[assoc-1]; + } + DPRINTF(IICMore,"Found free tag at %d\n",tag_ptr - tagStore); + tagsInUse++; + if (!warmedUp && tagsInUse.value() >= warmupBound) { + warmedUp = true; + warmupCycle = curTick; + } + + return tag_ptr; +} + +void +IIC::freeTag(IICTag *tag_ptr) +{ + unsigned long tag_index, tmp_index; + // Fix tag_ptr + if (tag_ptr) { + // we have a tag to clear + DPRINTF(IICMore,"Freeing Tag for %x\n", + regenerateBlkAddr(tag_ptr->tag,0)); + tagsInUse--; + tag_ptr->status = 0; + tag_ptr->numData = 0; + tag_ptr->re = NULL; + tag_index = tag_ptr - tagStore; + if (tag_index >= primaryBound) { + // tag_ptr points to secondary store + assert(tag_index < tagNull); // remove this?? egh + if (tag_ptr->chain_ptr == tagNull) { + // need to fix chain list + unsigned tmp_set = hash(tag_ptr->tag << tagShift); + if (sets[tmp_set].chain_ptr == tag_index) { + sets[tmp_set].chain_ptr = tagNull; + } else { + tmp_index = sets[tmp_set].chain_ptr; + while (tmp_index != tagNull + && tagStore[tmp_index].chain_ptr != tag_index) { + tmp_index = tagStore[tmp_index].chain_ptr; + } + assert(tmp_index != tagNull); + tagStore[tmp_index].chain_ptr = tagNull; + } + tag_ptr->chain_ptr = freelist; + freelist = tag_index; + freeSecond++; + } else { + // copy next chained entry to this tag location + tmp_index = tag_ptr->chain_ptr; + tagSwap(tmp_index, tag_index); + tagStore[tmp_index].chain_ptr = freelist; + freelist = tmp_index; + freeSecond++; + } + } else { + // tag_ptr in primary hash table + assert(tag_index < primaryBound); + tag_ptr->status = 0; + unsigned tmp_set = hash(tag_ptr->tag << tagShift); + if (sets[tmp_set].chain_ptr != tagNull) { // collapse chain + tmp_index = sets[tmp_set].chain_ptr; + tagSwap(tag_index, tmp_index); + tagStore[tmp_index].chain_ptr = freelist; + freelist = tmp_index; + freeSecond++; + sets[tmp_set].chain_ptr = tag_ptr->chain_ptr; + sets[tmp_set].moveToTail(tag_ptr); + } + } + } +} + +void +IIC::freeDataBlock(unsigned long data_ptr) +{ + assert(dataReferenceCount[data_ptr] == 0); + DPRINTF(IICMore, "Freeing data at %d\n", data_ptr); + blkFreelist.push_front(data_ptr); +} + +/** Use a simple modulo hash. */ +#define SIMPLE_HASH 0 + +unsigned +IIC::hash(Addr addr) const { +#if SIMPLE_HASH + return extractTag(addr) % iic_hash_size; +#else + Addr tag, mask, x, y; + tag = extractTag(addr); + mask = hashSets-1; /* assumes iic_hash_size is a power of 2 */ + x = tag & mask; + y = (tag >> (int)(::log(hashSets)/::log(2))) & mask; + assert (x < hashSets && y < hashSets); + return x ^ y; +#endif +} + + +void +IICSet::moveToHead(IICTag *tag) +{ + if (tags[0] == tag) + return; + + // write 'next' block into blks[i], moving up from MRU toward LRU + // until we overwrite the block we moved to head. + + // start by setting up to write 'blk' into blks[0] + int i = 0; + IICTag *next = tag; + + do { + assert(i < assoc); + // swap blks[i] and next + IICTag *tmp = tags[i]; + tags[i] = next; + next = tmp; + ++i; + } while (next != tag); +} + +void +IICSet::moveToTail(IICTag *tag) +{ + if (tags[assoc-1] == tag) + return; + + // write 'next' block into blks[i], moving up from MRU toward LRU + // until we overwrite the block we moved to head. + + // start by setting up to write 'blk' into blks[0] + int i = assoc - 1; + IICTag *next = tag; + + do { + assert(i >= 0); + // swap blks[i] and next + IICTag *tmp = tags[i]; + tags[i] = next; + next = tmp; + --i; + } while (next != tag); +} + +void +IIC::tagSwap(unsigned long index1, unsigned long index2) +{ + DPRINTF(IIC,"Swapping tag[%d]=%x for tag[%d]=%x\n",index1, + tagStore[index1].tag<<tagShift, index2, + tagStore[index2].tag<<tagShift); + IICTag tmp_tag; + tmp_tag = tagStore[index1]; + tagStore[index1] = tagStore[index2]; + tagStore[index2] = tmp_tag; + if (tagStore[index1].isValid()) + repl->fixTag(tagStore[index1].re, index2, index1); + if (tagStore[index2].isValid()) + repl->fixTag(tagStore[index2].re, index1, index2); +} + + +IICTag * +IIC::secondaryChain(Addr tag, unsigned long chain_ptr, + int *_depth) const +{ + int depth = 0; + while (chain_ptr != tagNull) { + DPRINTF(IIC,"Searching secondary at %d for %x\n", chain_ptr, + tag<<tagShift); + if (tagStore[chain_ptr].tag == tag && + (tagStore[chain_ptr].isValid())) { + *_depth = depth; + return &tagStore[chain_ptr]; + } + depth++; + chain_ptr = tagStore[chain_ptr].chain_ptr; + } + *_depth = depth; + return NULL; +} + +void +IIC::decompressBlock(unsigned long index) +{ + IICTag *tag_ptr = &tagStore[index]; + if (tag_ptr->isCompressed()) { + // decompress the data here. + } +} + +void +IIC::compressBlock(unsigned long index) +{ + IICTag *tag_ptr = &tagStore[index]; + if (!tag_ptr->isCompressed()) { + // Compress the data here. + } +} + +void +IIC::invalidateBlk(Addr addr) +{ + IICTag* tag_ptr = findBlock(addr); + if (tag_ptr) { + for (int i = 0; i < tag_ptr->numData; ++i) { + dataReferenceCount[tag_ptr->data_ptr[i]]--; + if (dataReferenceCount[tag_ptr->data_ptr[i]] == 0) { + freeDataBlock(tag_ptr->data_ptr[i]); + } + } + repl->removeEntry(tag_ptr->re); + freeTag(tag_ptr); + } +} + +void +IIC::readData(IICTag *blk, uint8_t *data){ +// assert(cache->doData()); + assert(blk->size <= trivialSize || blk->numData > 0); + int data_size = blk->size; + if (data_size > trivialSize) { + for (int i = 0; i < blk->numData; ++i){ + memcpy(data+i*subSize, + &(dataBlks[blk->data_ptr[i]][0]), + (data_size>subSize)?subSize:data_size); + data_size -= subSize; + } + } else { + memcpy(data,blk->trivialData,data_size); + } +} + +void +IIC::writeData(IICTag *blk, uint8_t *write_data, int size, + PacketList & writebacks){ +// assert(cache->doData()); + assert(size < blkSize || !blk->isCompressed()); + DPRINTF(IIC, "Writing %d bytes to %x\n", size, + blk->tag<<tagShift); + // Find the number of subblocks needed, (round up) + int num_subs = (size + (subSize -1))/subSize; + if (size <= trivialSize) { + num_subs = 0; + } + assert(num_subs <= numSub); + if (num_subs > blk->numData) { + // need to allocate more data blocks + for (int i = blk->numData; i < num_subs; ++i){ + blk->data_ptr[i] = getFreeDataBlock(writebacks); + dataReferenceCount[blk->data_ptr[i]] += 1; + } + } else if (num_subs < blk->numData){ + // can free data blocks + for (int i=num_subs; i < blk->numData; ++i){ + // decrement reference count and compare to zero + /** + * @todo + * Make this work with copying. + */ + if (--dataReferenceCount[blk->data_ptr[i]] == 0) { + freeDataBlock(blk->data_ptr[i]); + } + } + } + + blk->numData = num_subs; + blk->size = size; + assert(size <= trivialSize || blk->numData > 0); + if (size > trivialSize){ + for (int i = 0; i < blk->numData; ++i){ + memcpy(&dataBlks[blk->data_ptr[i]][0], write_data + i*subSize, + (size>subSize)?subSize:size); + size -= subSize; + } + } else { + memcpy(blk->trivialData,write_data,size); + } +} + + +/** + * @todo This code can break if the src is evicted to get a tag for the dest. + */ +void +IIC::doCopy(Addr source, Addr dest, PacketList &writebacks) +{ +//Copy unsuported now +#if 0 + IICTag *dest_tag = findBlock(dest); + + if (dest_tag) { + for (int i = 0; i < dest_tag->numData; ++i) { + if (--dataReferenceCount[dest_tag->data_ptr[i]] == 0) { + freeDataBlock(dest_tag->data_ptr[i]); + } + } + // Reset replacement entry + } else { + dest_tag = getFreeTag(hash(dest), writebacks); + dest_tag->re = (void*) repl->add(dest_tag - tagStore); + dest_tag->set = hash(dest); + dest_tag->tag = extractTag(dest); + dest_tag->status = BlkValid | BlkWritable; + } + // Find the source tag here since it might move if we need to find a + // tag for the destination. + IICTag *src_tag = findBlock(source); + assert(src_tag); + assert(!cache->doData() || src_tag->size <= trivialSize + || src_tag->numData > 0); + // point dest to source data and inc counter + for (int i = 0; i < src_tag->numData; ++i) { + dest_tag->data_ptr[i] = src_tag->data_ptr[i]; + ++dataReferenceCount[dest_tag->data_ptr[i]]; + } + + // Maintain fast access data. + memcpy(dest_tag->data, src_tag->data, blkSize); + + dest_tag->xc = src_tag->xc; + dest_tag->size = src_tag->size; + dest_tag->numData = src_tag->numData; + if (src_tag->numData == 0) { + // Data is stored in the trivial data, just copy it. + memcpy(dest_tag->trivialData, src_tag->trivialData, src_tag->size); + } + + dest_tag->status |= BlkDirty; + if (dest_tag->size < blkSize) { + dest_tag->status |= BlkCompressed; + } else { + dest_tag->status &= ~BlkCompressed; + } +#endif +} + +void +IIC::fixCopy(Packet * &pkt, PacketList &writebacks) +{ +#if 0 + // if reference counter is greater than 1, do copy + // else do write + Addr blk_addr = blkAlign(pkt->getAddr); + IICTag* blk = findBlock(blk_addr); + + if (blk->numData > 0 && dataReferenceCount[blk->data_ptr[0]] != 1) { + // copy the data + // Mark the block as referenced so it doesn't get replaced. + blk->status |= BlkReferenced; + for (int i = 0; i < blk->numData; ++i){ + unsigned long new_data = getFreeDataBlock(writebacks); + // Need to refresh pointer + /** + * @todo Remove this refetch once we change IIC to pointer based + */ + blk = findBlock(blk_addr); + assert(blk); + if (cache->doData()) { + memcpy(&(dataBlks[new_data][0]), + &(dataBlks[blk->data_ptr[i]][0]), + subSize); + } + dataReferenceCount[blk->data_ptr[i]]--; + dataReferenceCount[new_data]++; + blk->data_ptr[i] = new_data; + } + } +#endif +} + +void +IIC::cleanupRefs() +{ + for (int i = 0; i < numTags; ++i) { + if (tagStore[i].isValid()) { + totalRefs += tagStore[i].refCount; + ++sampledRefs; + } + } +} diff --git a/src/mem/cache/tags/iic.hh b/src/mem/cache/tags/iic.hh new file mode 100644 index 000000000..2357bdce3 --- /dev/null +++ b/src/mem/cache/tags/iic.hh @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of the Indirect Index Cache (IIC) tags store. + */ + +#ifndef __IIC_HH__ +#define __IIC_HH__ + +#include <list> +#include <vector> + +#include "mem/cache/cache_blk.hh" +#include "mem/cache/tags/repl/repl.hh" +#include "mem/packet.hh" +#include "base/statistics.hh" +#include "mem/cache/tags/base_tags.hh" + +class BaseCache; // Forward declaration + +/** + * IIC cache blk. + */ +class IICTag : public CacheBlk +{ + public: + /** + * Copy the contents of the given IICTag into this one. + * @param rhs The tag to copy. + * @return const reference to this tag. + */ + const IICTag& operator=(const IICTag& rhs) + { + CacheBlk::operator=(rhs); + chain_ptr = rhs.chain_ptr; + re = rhs.re; + set = rhs.set; + trivialData = rhs.trivialData; + numData = rhs.numData; + data_ptr.clear(); + for (int i = 0; i < rhs.numData; ++i) { + data_ptr.push_back(rhs.data_ptr[i]); + } + return *this; + } + + /** Hash chain pointer into secondary store. */ + unsigned long chain_ptr; + /** Data array pointers for each subblock. */ + std::vector<unsigned long> data_ptr; + /** Replacement Entry pointer. */ + void *re; + /** + * An array to store small compressed data. Conceputally the same size + * as the unsused data array pointers. + */ + uint8_t *trivialData; + /** + * The number of allocated subblocks. + */ + int numData; +}; + +/** + * A hash set for the IIC primary lookup table. + */ +class IICSet{ + public: + /** The associativity of the primary table. */ + int assoc; + + /** The number of hash chains followed when finding the last block. */ + int depth; + /** The current number of blocks on the chain. */ + int size; + + /** Tag pointer into the secondary tag storage. */ + unsigned long chain_ptr; + + /** The LRU list of the primary table. MRU is at 0 index. */ + IICTag ** tags; + + /** + * Find the addr in this set, return the chain pointer to the secondary if + * it isn't found. + * @param asid The address space ID. + * @param tag The address to find. + * @param chain_ptr The chain pointer to start the search of the secondary + * @return Pointer to the tag, NULL if not found. + */ + IICTag* findTag( Addr tag, unsigned long &chain_ptr) + { + depth = 1; + for (int i = 0; i < assoc; ++i) { + if (tags[i]->tag == tag && tags[i]->isValid()) { + return tags[i]; + } + } + chain_ptr = this->chain_ptr; + return 0; + } + + /** + * Find an usused tag in this set. + * @return Pointer to the unused tag, NULL if none are free. + */ + IICTag* findFree() + { + for (int i = 0; i < assoc; ++i) { + if (!tags[i]->isValid()) { + return tags[i]; + } + } + return 0; + } + + /** + * Move a tag to the head of the LRU list + * @param tag The tag to move. + */ + void moveToHead(IICTag *tag); + + /** + * Move a tag to the tail (LRU) of the LRU list + * @param tag The tag to move. + */ + void moveToTail(IICTag *tag); +}; + +/** + * The IIC tag store. This is a hardware-realizable, fully-associative tag + * store that uses software replacement, e.g. Gen. + */ +class IIC : public BaseTags +{ + public: + /** Typedef of the block type used in this class. */ + typedef IICTag BlkType; + /** Typedef for list of pointers to the local block type. */ + typedef std::list<IICTag*> BlkList; + protected: + /** The number of set in the primary table. */ + const int hashSets; + /** The block size in bytes. */ + const int blkSize; + /** The associativity of the primary table. */ + const int assoc; + /** The base hit latency. */ + const int hitLatency; + /** The subblock size, used for compression. */ + const int subSize; + + /** The number of subblocks */ + const int numSub; + /** The number of bytes used by data pointers */ + const int trivialSize; + + /** The amount to shift address to get the tag. */ + const int tagShift; + /** The mask to get block offset bits. */ + const unsigned blkMask; + + /** The amount to shift to get the subblock number. */ + const int subShift; + /** The mask to get the correct subblock number. */ + const unsigned subMask; + + /** The latency of a hash lookup. */ + const int hashDelay; + /** The number of data blocks. */ + const int numBlocks; + /** The total number of tags in primary and secondary. */ + const int numTags; + /** The number of tags in the secondary tag store. */ + const int numSecondary; + + /** The Null tag pointer. */ + const int tagNull; + /** The last tag in the primary table. */ + const int primaryBound; + + /** All of the tags */ + IICTag *tagStore; + /** + * Pointer to the head of the secondary freelist (maintained with chain + * pointers. + */ + unsigned long freelist; + /** + * The data block freelist. + */ + std::list<unsigned long> blkFreelist; + + /** The primary table. */ + IICSet *sets; + + /** The replacement policy. */ + Repl *repl; + + /** An array of data reference counters. */ + int *dataReferenceCount; + + /** The data blocks. */ + uint8_t *dataStore; + + /** Storage for the fast access data of each cache block. */ + uint8_t **dataBlks; + + /** + * Count of the current number of free secondary tags. + * Used for debugging. + */ + int freeSecond; + + // IIC Statistics + /** + * @addtogroup IICStatistics IIC Statistics + * @{ + */ + + /** Hash hit depth of cache hits. */ + Stats::Distribution<> hitHashDepth; + /** Hash depth for cache misses. */ + Stats::Distribution<> missHashDepth; + /** Count of accesses to each hash set. */ + Stats::Distribution<> setAccess; + + /** The total hash depth for every miss. */ + Stats::Scalar<> missDepthTotal; + /** The total hash depth for all hits. */ + Stats::Scalar<> hitDepthTotal; + /** The number of hash misses. */ + Stats::Scalar<> hashMiss; + /** The number of hash hits. */ + Stats::Scalar<> hashHit; + /** @} */ + + public: + /** + * Collection of parameters for the IIC. + */ + class Params { + public: + /** The size in bytes of the cache. */ + int size; + /** The number of sets in the primary table. */ + int numSets; + /** The block size in bytes. */ + int blkSize; + /** The associativity of the primary table. */ + int assoc; + /** The number of cycles for each hash lookup. */ + int hashDelay; + /** The number of cycles to read the data. */ + int hitLatency; + /** The replacement policy. */ + Repl *rp; + /** The subblock size in bytes. */ + int subblockSize; + }; + + /** + * Construct and initialize this tag store. + * @param params The IIC parameters. + * @todo + * Should make a way to have less tags in the primary than blks in the + * cache. Also should be able to specify number of secondary blks. + */ + IIC(Params ¶ms); + + /** + * Destructor. + */ + virtual ~IIC(); + + /** + * Register the statistics. + * @param name The name to prepend to the statistic descriptions. + */ + void regStats(const std::string &name); + + /** + * Regenerate the block address from the tag. + * @param tag The tag of the block. + * @param set Not needed for the iic. + * @return The block address. + */ + Addr regenerateBlkAddr(Addr tag, int set) { + return (((Addr)tag << tagShift)); + } + + /** + * Return the block size. + * @return The block size. + */ + int getBlockSize() + { + return blkSize; + } + + /** + * Return the subblock size. + * @return The subblock size. + */ + int getSubBlockSize() + { + return subSize; + } + + /** + * Return the hit latency. + * @return the hit latency. + */ + int getHitLatency() const + { + return hitLatency; + } + + /** + * Generate the tag from the address. + * @param addr The address to a get a tag for. + * @param blk Ignored here. + * @return the tag. + */ + Addr extractTag(Addr addr, IICTag *blk) const + { + return (addr >> tagShift); + } + + /** + * Generate the tag from the address. + * @param addr The address to a get a tag for. + * @return the tag. + */ + Addr extractTag(Addr addr) const + { + return (addr >> tagShift); + } + + /** + * Return the set, always 0 for IIC. + * @return 0. + */ + int extractSet(Addr addr) const + { + return 0; + } + + /** + * Get the block offset of an address. + * @param addr The address to get the offset of. + * @return the block offset of the address. + */ + int extractBlkOffset(Addr addr) const + { + return (addr & blkMask); + } + + /** + * Align an address to the block size. + * @param addr the address to align. + * @return The block address. + */ + Addr blkAlign(Addr addr) const + { + return (addr & ~(Addr)blkMask); + } + + /** + * Check for the address in the tagstore. + * @param asid The address space ID. + * @param addr The address to find. + * @return true if it is found. + */ + bool probe(Addr addr) const; + + /** + * Swap the position of two tags. + * @param index1 The first tag location. + * @param index2 The second tag location. + */ + void tagSwap(unsigned long index1, unsigned long index2); + + /** + * Clear the reference bit of the tag and return its old value. + * @param index The pointer of the tag to manipulate. + * @return The previous state of the reference bit. + */ + bool clearRef(unsigned long index) + { + bool tmp = tagStore[index].isReferenced(); + tagStore[index].status &= ~BlkReferenced; + return tmp; + } + + /** + * Decompress a block if it is compressed. + * @param index The tag store index for the block to uncompress. + */ + void decompressBlock(unsigned long index); + + /** + * Try and compress a block if it is not already compressed. + * @param index The tag store index for the block to compress. + */ + void compressBlock(unsigned long index); + + /** + * Invalidate the block containing the address. + * @param asid The address space ID. + * @param addr The address to invalidate. + */ + void invalidateBlk(Addr addr); + + /** + * Find the block and update the replacement data. This call also returns + * the access latency as a side effect. + * @param addr The address to find. + * @param asid The address space ID. + * @param lat The access latency. + * @return A pointer to the block found, if any. + */ + IICTag* findBlock(Addr addr, int &lat); + + /** + * Find the block and update the replacement data. This call also returns + * the access latency as a side effect. + * @param pkt The req whose block to find + * @param lat The access latency. + * @return A pointer to the block found, if any. + */ + IICTag* findBlock(Packet * &pkt, int &lat); + + /** + * Find the block, do not update the replacement data. + * @param addr The address to find. + * @param asid The address space ID. + * @return A pointer to the block found, if any. + */ + IICTag* findBlock(Addr addr) const; + + /** + * Find a replacement block for the address provided. + * @param pkt The request to a find a replacement candidate for. + * @param writebacks List for any writebacks to be performed. + * @param compress_blocks List of blocks to compress, for adaptive comp. + * @return The block to place the replacement in. + */ + IICTag* findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks); + + /** + * Read the data from the internal storage of the given cache block. + * @param blk The block to read the data from. + * @param data The buffer to read the data into. + * @return The cache block's data. + */ + void readData(IICTag *blk, uint8_t *data); + + /** + * Write the data into the internal storage of the given cache block. + * @param blk The block to write to. + * @param data The data to write. + * @param size The number of bytes to write. + * @param writebacks A list for any writebacks to be performed. May be + * needed when writing to a compressed block. + */ + void writeData(IICTag *blk, uint8_t *data, int size, + PacketList & writebacks); + + /** + * Perform a block aligned copy from the source address to the destination. + * @param source The block-aligned source address. + * @param dest The block-aligned destination address. + * @param asid The address space DI. + * @param writebacks List for any generated writeback pktuests. + */ + void doCopy(Addr source, Addr dest, PacketList &writebacks); + + /** + * If a block is currently marked copy on write, copy it before writing. + * @param pkt The write request. + * @param writebacks List for any generated writeback pktuests. + */ + void fixCopy(Packet * &pkt, PacketList &writebacks); + + /** + * Called at end of simulation to complete average block reference stats. + */ + virtual void cleanupRefs(); +private: + /** + * Return the hash of the address. + * @param addr The address to hash. + * @return the hash of the address. + */ + unsigned hash(Addr addr) const; + + /** + * Search for a block in the secondary tag store. Returns the number of + * hash lookups as a side effect. + * @param asid The address space ID. + * @param tag The tag to match. + * @param chain_ptr The first entry to search. + * @param depth The number of hash lookups made while searching. + * @return A pointer to the block if found. + */ + IICTag *secondaryChain(Addr tag, unsigned long chain_ptr, + int *depth) const; + + /** + * Free the resources associated with the next replacement block. + * @param writebacks A list of any writebacks to perform. + */ + void freeReplacementBlock(PacketList & writebacks); + + /** + * Return the pointer to a free data block. + * @param writebacks A list of any writebacks to perform. + * @return A pointer to a free data block. + */ + unsigned long getFreeDataBlock(PacketList & writebacks); + + /** + * Get a free tag in the given hash set. + * @param set The hash set to search. + * @param writebacks A list of any writebacks to perform. + * @return a pointer to a free tag. + */ + IICTag* getFreeTag(int set, PacketList & writebacks); + + /** + * Free the resources associated with the given tag. + * @param tag_ptr The tag to free. + */ + void freeTag(IICTag *tag_ptr); + + /** + * Mark the given data block as being available. + * @param data_ptr The data block to free. + */ + void freeDataBlock(unsigned long data_ptr); +}; +#endif // __IIC_HH__ + diff --git a/src/mem/cache/tags/lru.cc b/src/mem/cache/tags/lru.cc new file mode 100644 index 000000000..976bbeff2 --- /dev/null +++ b/src/mem/cache/tags/lru.cc @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Definitions of LRU tag store. + */ + +#include <string> + +#include "mem/cache/base_cache.hh" +#include "base/intmath.hh" +#include "mem/cache/tags/lru.hh" +#include "sim/root.hh" + +using namespace std; + +LRUBlk* +CacheSet::findBlk(Addr tag) const +{ + for (int i = 0; i < assoc; ++i) { + if (blks[i]->tag == tag && blks[i]->isValid()) { + return blks[i]; + } + } + return 0; +} + + +void +CacheSet::moveToHead(LRUBlk *blk) +{ + // nothing to do if blk is already head + if (blks[0] == blk) + return; + + // write 'next' block into blks[i], moving up from MRU toward LRU + // until we overwrite the block we moved to head. + + // start by setting up to write 'blk' into blks[0] + int i = 0; + LRUBlk *next = blk; + + do { + assert(i < assoc); + // swap blks[i] and next + LRUBlk *tmp = blks[i]; + blks[i] = next; + next = tmp; + ++i; + } while (next != blk); +} + + +// create and initialize a LRU/MRU cache structure +LRU::LRU(int _numSets, int _blkSize, int _assoc, int _hit_latency) : + numSets(_numSets), blkSize(_blkSize), assoc(_assoc), hitLatency(_hit_latency) +{ + // Check parameters + if (blkSize < 4 || !isPowerOf2(blkSize)) { + fatal("Block size must be at least 4 and a power of 2"); + } + if (numSets <= 0 || !isPowerOf2(numSets)) { + fatal("# of sets must be non-zero and a power of 2"); + } + if (assoc <= 0) { + fatal("associativity must be greater than zero"); + } + if (hitLatency <= 0) { + fatal("access latency must be greater than zero"); + } + + LRUBlk *blk; + int i, j, blkIndex; + + blkMask = blkSize - 1; + setShift = floorLog2(blkSize); + setMask = numSets - 1; + tagShift = setShift + floorLog2(numSets); + warmedUp = false; + /** @todo Make warmup percentage a parameter. */ + warmupBound = numSets * assoc; + + sets = new CacheSet[numSets]; + blks = new LRUBlk[numSets * assoc]; + // allocate data storage in one big chunk + dataBlks = new uint8_t[numSets*assoc*blkSize]; + + blkIndex = 0; // index into blks array + for (i = 0; i < numSets; ++i) { + sets[i].assoc = assoc; + + sets[i].blks = new LRUBlk*[assoc]; + + // link in the data blocks + for (j = 0; j < assoc; ++j) { + // locate next cache block + blk = &blks[blkIndex]; + blk->data = &dataBlks[blkSize*blkIndex]; + ++blkIndex; + + // invalidate new cache block + blk->status = 0; + + //EGH Fix Me : do we need to initialize blk? + + // Setting the tag to j is just to prevent long chains in the hash + // table; won't matter because the block is invalid + blk->tag = j; + blk->whenReady = 0; + blk->isTouched = false; + blk->size = blkSize; + sets[i].blks[j]=blk; + blk->set = i; + } + } +} + +LRU::~LRU() +{ + delete [] dataBlks; + delete [] blks; + delete [] sets; +} + +// probe cache for presence of given block. +bool +LRU::probe(Addr addr) const +{ + // return(findBlock(Read, addr, asid) != 0); + Addr tag = extractTag(addr); + unsigned myset = extractSet(addr); + + LRUBlk *blk = sets[myset].findBlk(tag); + + return (blk != NULL); // true if in cache +} + +LRUBlk* +LRU::findBlock(Addr addr, int &lat) +{ + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + LRUBlk *blk = sets[set].findBlk(tag); + lat = hitLatency; + if (blk != NULL) { + // move this block to head of the MRU list + sets[set].moveToHead(blk); + if (blk->whenReady > curTick + && blk->whenReady - curTick > hitLatency) { + lat = blk->whenReady - curTick; + } + blk->refCount += 1; + } + + return blk; +} + +LRUBlk* +LRU::findBlock(Packet * &pkt, int &lat) +{ + Addr addr = pkt->getAddr(); + + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + LRUBlk *blk = sets[set].findBlk(tag); + lat = hitLatency; + if (blk != NULL) { + // move this block to head of the MRU list + sets[set].moveToHead(blk); + if (blk->whenReady > curTick + && blk->whenReady - curTick > hitLatency) { + lat = blk->whenReady - curTick; + } + blk->refCount += 1; + } + + return blk; +} + +LRUBlk* +LRU::findBlock(Addr addr) const +{ + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + LRUBlk *blk = sets[set].findBlk(tag); + return blk; +} + +LRUBlk* +LRU::findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks) +{ + unsigned set = extractSet(pkt->getAddr()); + // grab a replacement candidate + LRUBlk *blk = sets[set].blks[assoc-1]; + sets[set].moveToHead(blk); + if (blk->isValid()) { + replacements[0]++; + totalRefs += blk->refCount; + ++sampledRefs; + blk->refCount = 0; + } else if (!blk->isTouched) { + tagsInUse++; + blk->isTouched = true; + if (!warmedUp && tagsInUse.value() >= warmupBound) { + warmedUp = true; + warmupCycle = curTick; + } + } + + return blk; +} + +void +LRU::invalidateBlk(Addr addr) +{ + LRUBlk *blk = findBlock(addr); + if (blk) { + blk->status = 0; + blk->isTouched = false; + tagsInUse--; + } +} + +void +LRU::doCopy(Addr source, Addr dest, PacketList &writebacks) +{ + assert(source == blkAlign(source)); + assert(dest == blkAlign(dest)); + LRUBlk *source_blk = findBlock(source); + assert(source_blk); + LRUBlk *dest_blk = findBlock(dest); + if (dest_blk == NULL) { + // Need to do a replacement + Request *search = new Request(dest,1,0); + Packet * pkt = new Packet(search, Packet::ReadReq, -1); + BlkList dummy_list; + dest_blk = findReplacement(pkt, writebacks, dummy_list); + if (dest_blk->isValid() && dest_blk->isModified()) { + // Need to writeback data. +/* pkt = buildWritebackReq(regenerateBlkAddr(dest_blk->tag, + dest_blk->set), + dest_blk->req->asid, + dest_blk->xc, + blkSize, + dest_blk->data, + dest_blk->size); +*/ + Request *writebackReq = new Request(regenerateBlkAddr(dest_blk->tag, + dest_blk->set), + blkSize, 0); + Packet *writeback = new Packet(writebackReq, Packet::Writeback, -1); + writeback->allocate(); + memcpy(writeback->getPtr<uint8_t>(),dest_blk->data, blkSize); + writebacks.push_back(writeback); + } + dest_blk->tag = extractTag(dest); + delete search; + delete pkt; + } + /** + * @todo Can't assume the status once we have coherence on copies. + */ + + // Set this block as readable, writeable, and dirty. + dest_blk->status = 7; + memcpy(dest_blk->data, source_blk->data, blkSize); +} + +void +LRU::cleanupRefs() +{ + for (int i = 0; i < numSets*assoc; ++i) { + if (blks[i].isValid()) { + totalRefs += blks[i].refCount; + ++sampledRefs; + } + } +} diff --git a/src/mem/cache/tags/lru.hh b/src/mem/cache/tags/lru.hh new file mode 100644 index 000000000..a3a56a0e6 --- /dev/null +++ b/src/mem/cache/tags/lru.hh @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declaration of a LRU tag store. + */ + +#ifndef __LRU_HH__ +#define __LRU_HH__ + +#include <list> + +#include "mem/cache/cache_blk.hh" // base class +#include "mem/packet.hh" // for inlined functions +#include <assert.h> +#include "mem/cache/tags/base_tags.hh" + +class BaseCache; + +/** + * LRU cache block. + */ +class LRUBlk : public CacheBlk { + public: + /** Has this block been touched? Used to aid calculation of warmup time. */ + bool isTouched; +}; + +/** + * An associative set of cache blocks. + */ +class CacheSet +{ + public: + /** The associativity of this set. */ + int assoc; + + /** Cache blocks in this set, maintained in LRU order 0 = MRU. */ + LRUBlk **blks; + + /** + * Find a block matching the tag in this set. + * @param asid The address space ID. + * @param tag The Tag to find. + * @return Pointer to the block if found. + */ + LRUBlk* findBlk(Addr tag) const; + + /** + * Move the given block to the head of the list. + * @param blk The block to move. + */ + void moveToHead(LRUBlk *blk); +}; + +/** + * A LRU cache tag store. + */ +class LRU : public BaseTags +{ + public: + /** Typedef the block type used in this tag store. */ + typedef LRUBlk BlkType; + /** Typedef for a list of pointers to the local block class. */ + typedef std::list<LRUBlk*> BlkList; + protected: + /** The number of sets in the cache. */ + const int numSets; + /** The number of bytes in a block. */ + const int blkSize; + /** The associativity of the cache. */ + const int assoc; + /** The hit latency. */ + const int hitLatency; + + /** The cache sets. */ + CacheSet *sets; + + /** The cache blocks. */ + LRUBlk *blks; + /** The data blocks, 1 per cache block. */ + uint8_t *dataBlks; + + /** The amount to shift the address to get the set. */ + int setShift; + /** The amount to shift the address to get the tag. */ + int tagShift; + /** Mask out all bits that aren't part of the set index. */ + unsigned setMask; + /** Mask out all bits that aren't part of the block offset. */ + unsigned blkMask; + +public: + /** + * Construct and initialize this tag store. + * @param _numSets The number of sets in the cache. + * @param _blkSize The number of bytes in a block. + * @param _assoc The associativity of the cache. + * @param _hit_latency The latency in cycles for a hit. + */ + LRU(int _numSets, int _blkSize, int _assoc, int _hit_latency); + + /** + * Destructor + */ + virtual ~LRU(); + + /** + * Return the block size. + * @return the block size. + */ + int getBlockSize() + { + return blkSize; + } + + /** + * Return the subblock size. In the case of LRU it is always the block + * size. + * @return The block size. + */ + int getSubBlockSize() + { + return blkSize; + } + + /** + * Search for the address in the cache. + * @param asid The address space ID. + * @param addr The address to find. + * @return True if the address is in the cache. + */ + bool probe(Addr addr) const; + + /** + * Invalidate the block containing the given address. + * @param asid The address space ID. + * @param addr The address to invalidate. + */ + void invalidateBlk(Addr addr); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param pkt The request whose block to find. + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + LRUBlk* findBlock(Packet * &pkt, int &lat); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param addr The address to find. + * @param asid The address space ID. + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + LRUBlk* findBlock(Addr addr, int &lat); + + /** + * Finds the given address in the cache, do not update replacement data. + * @param addr The address to find. + * @param asid The address space ID. + * @return Pointer to the cache block if found. + */ + LRUBlk* findBlock(Addr addr) const; + + /** + * Find a replacement block for the address provided. + * @param pkt The request to a find a replacement candidate for. + * @param writebacks List for any writebacks to be performed. + * @param compress_blocks List of blocks to compress, for adaptive comp. + * @return The block to place the replacement in. + */ + LRUBlk* findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks); + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @return The tag of the address. + */ + Addr extractTag(Addr addr) const + { + return (addr >> tagShift); + } + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @param blk Ignored. + * @return The tag of the address. + */ + Addr extractTag(Addr addr, LRUBlk *blk) const + { + return (addr >> tagShift); + } + + /** + * Calculate the set index from the address. + * @param addr The address to get the set from. + * @return The set index of the address. + */ + int extractSet(Addr addr) const + { + return ((addr >> setShift) & setMask); + } + + /** + * Get the block offset from an address. + * @param addr The address to get the offset of. + * @return The block offset. + */ + int extractBlkOffset(Addr addr) const + { + return (addr & blkMask); + } + + /** + * Align an address to the block size. + * @param addr the address to align. + * @return The block address. + */ + Addr blkAlign(Addr addr) const + { + return (addr & ~(Addr)blkMask); + } + + /** + * Regenerate the block address from the tag. + * @param tag The tag of the block. + * @param set The set of the block. + * @return The block address. + */ + Addr regenerateBlkAddr(Addr tag, unsigned set) const + { + return ((tag << tagShift) | ((Addr)set << setShift)); + } + + /** + * Return the hit latency. + * @return the hit latency. + */ + int getHitLatency() const + { + return hitLatency; + } + + /** + * Read the data out of the internal storage of the given cache block. + * @param blk The cache block to read. + * @param data The buffer to read the data into. + * @return The cache block's data. + */ + void readData(LRUBlk *blk, uint8_t *data) + { + memcpy(data, blk->data, blk->size); + } + + /** + * Write data into the internal storage of the given cache block. Since in + * LRU does not store data differently this just needs to update the size. + * @param blk The cache block to write. + * @param data The data to write. + * @param size The number of bytes to write. + * @param writebacks A list for any writebacks to be performed. May be + * needed when writing to a compressed block. + */ + void writeData(LRUBlk *blk, uint8_t *data, int size, + PacketList & writebacks) + { + assert(size <= blkSize); + blk->size = size; + } + + /** + * Perform a block aligned copy from the source address to the destination. + * @param source The block-aligned source address. + * @param dest The block-aligned destination address. + * @param asid The address space DI. + * @param writebacks List for any generated writeback pktuests. + */ + void doCopy(Addr source, Addr dest, PacketList &writebacks); + + /** + * No impl. + */ + void fixCopy(Packet * &pkt, PacketList &writebacks) + { + } + + /** + * Called at end of simulation to complete average block reference stats. + */ + virtual void cleanupRefs(); +}; + +#endif diff --git a/src/mem/cache/tags/repl/gen.cc b/src/mem/cache/tags/repl/gen.cc new file mode 100644 index 000000000..ec1c2aaf3 --- /dev/null +++ b/src/mem/cache/tags/repl/gen.cc @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Steve Reinhardt + */ + +/** + * @file + * Definitions of the Generational replacement policy. + */ + +#include <string> + +#include "base/misc.hh" +#include "mem/cache/tags/iic.hh" +#include "mem/cache/tags/repl/gen.hh" +#include "sim/builder.hh" +#include "sim/host.hh" + +using namespace std; + +GenRepl::GenRepl(const string &_name, + int _num_pools, + int _fresh_res, + int _pool_res) // fix this, should be set by cache + : Repl(_name) +{ + num_pools = _num_pools; + fresh_res = _fresh_res; + pool_res = _pool_res; + num_entries = 0; + num_pool_entries = 0; + misses = 0; + pools = new GenPool[num_pools+1]; +} + +GenRepl::~GenRepl() +{ + delete [] pools; +} + +unsigned long +GenRepl::getRepl() +{ + unsigned long tmp; + GenReplEntry *re; + int i; + int num_seen = 0; + if (!(num_pool_entries>0)) { + fatal("No blks available to replace"); + } + num_entries--; + num_pool_entries--; + for (i = 0; i < num_pools; i++) { + while ((re = pools[i].pop())) { + num_seen++; + // Remove invalidated entries + if (!re->valid) { + delete re; + continue; + } + if (iic->clearRef(re->tag_ptr)) { + pools[(((i+1)== num_pools)? i :i+1)].push(re, misses); + } + else { + tmp = re->tag_ptr; + delete re; + + repl_pool.sample(i); + + return tmp; + } + } + } + fatal("No replacement found"); + return 0xffffffff; +} + +unsigned long * +GenRepl::getNRepl(int n) +{ + unsigned long *tmp; + GenReplEntry *re; + int i; + if (!(num_pool_entries>(n-1))) { + fatal("Not enough blks available to replace"); + } + num_entries -= n; + num_pool_entries -= n; + tmp = new unsigned long[n]; /* array of cache_blk pointers */ + int blk_index = 0; + for (i = 0; i < num_pools && blk_index < n; i++) { + while (blk_index < n && (re = pools[i].pop())) { + // Remove invalidated entries + if (!re->valid) { + delete re; + continue; + } + if (iic->clearRef(re->tag_ptr)) { + pools[(((i+1)== num_pools)? i :i+1)].push(re, misses); + } + else { + tmp[blk_index] = re->tag_ptr; + blk_index++; + delete re; + repl_pool.sample(i); + } + } + } + if (blk_index >= n) + return tmp; + /* search the fresh pool */ + + fatal("No N replacements found"); + return NULL; +} + +void +GenRepl::doAdvance(std::list<unsigned long> &demoted) +{ + int i; + int num_seen = 0; + GenReplEntry *re; + misses++; + for (i=0; i<num_pools; i++) { + while (misses-pools[i].oldest > pool_res && (re = pools[i].pop())!=NULL) { + if (iic->clearRef(re->tag_ptr)) { + pools[(((i+1)== num_pools)? i :i+1)].push(re, misses); + /** @todo Not really demoted, but use it for now. */ + demoted.push_back(re->tag_ptr); + advance_pool.sample(i); + } + else { + pools[(((i-1)<0)?i:i-1)].push(re, misses); + demoted.push_back(re->tag_ptr); + demote_pool.sample(i); + } + } + num_seen += pools[i].size; + } + while (misses-pools[num_pools].oldest > fresh_res + && (re = pools[num_pools].pop())!=NULL) { + num_pool_entries++; + if (iic->clearRef(re->tag_ptr)) { + pools[num_pools/2].push(re, misses); + /** @todo Not really demoted, but use it for now. */ + demoted.push_back(re->tag_ptr); + advance_pool.sample(num_pools); + } + else { + pools[num_pools/2-1].push(re, misses); + demoted.push_back(re->tag_ptr); + demote_pool.sample(num_pools); + } + } +} + +void* +GenRepl::add(unsigned long tag_index) +{ + GenReplEntry *re = new GenReplEntry; + re->tag_ptr = tag_index; + re->valid = true; + pools[num_pools].push(re, misses); + num_entries++; + return (void*)re; +} + +void +GenRepl::regStats(const string name) +{ + using namespace Stats; + + /** GEN statistics */ + repl_pool + .init(0, 16, 1) + .name(name + ".repl_pool_dist") + .desc("Dist. of Repl. across pools") + .flags(pdf) + ; + + advance_pool + .init(0, 16, 1) + .name(name + ".advance_pool_dist") + .desc("Dist. of Repl. across pools") + .flags(pdf) + ; + + demote_pool + .init(0, 16, 1) + .name(name + ".demote_pool_dist") + .desc("Dist. of Repl. across pools") + .flags(pdf) + ; +} + +int +GenRepl::fixTag(void* _re, unsigned long old_index, unsigned long new_index) +{ + GenReplEntry *re = (GenReplEntry*)_re; + assert(re->valid); + if (re->tag_ptr == old_index) { + re->tag_ptr = new_index; + return 1; + } + fatal("Repl entry: tag ptrs do not match"); + return 0; +} + +bool +GenRepl::findTagPtr(unsigned long index) +{ + for (int i = 0; i < num_pools + 1; ++i) { + list<GenReplEntry*>::const_iterator iter = pools[i].entries.begin(); + list<GenReplEntry*>::const_iterator end = pools[i].entries.end(); + for (; iter != end; ++iter) { + if ((*iter)->valid && (*iter)->tag_ptr == index) { + return true; + } + } + } + return false; +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(GenRepl) + + Param<int> num_pools; + Param<int> fresh_res; + Param<int> pool_res; + +END_DECLARE_SIM_OBJECT_PARAMS(GenRepl) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(GenRepl) + + INIT_PARAM(num_pools, "capacity in bytes"), + INIT_PARAM(fresh_res, "associativity"), + INIT_PARAM(pool_res, "block size in bytes") + +END_INIT_SIM_OBJECT_PARAMS(GenRepl) + + +CREATE_SIM_OBJECT(GenRepl) +{ + return new GenRepl(getInstanceName(), num_pools, fresh_res, pool_res); +} + +REGISTER_SIM_OBJECT("GenRepl", GenRepl) + +#endif // DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/tags/repl/gen.hh b/src/mem/cache/tags/repl/gen.hh new file mode 100644 index 000000000..c1ceb3f4e --- /dev/null +++ b/src/mem/cache/tags/repl/gen.hh @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + */ + +/** + * @file + * Declarations of generational replacement policy + */ + +#ifndef ___GEN_HH__ +#define __GEN_HH__ + +#include <list> + +#include "base/statistics.hh" +#include "mem/cache/tags/repl/repl.hh" + +/** + * Generational Replacement entry. + */ +class GenReplEntry +{ + public: + /** Valid flag, used to quickly invalidate bogus entries. */ + bool valid; + /** The difference between this entry and the previous in the pool. */ + int delta; + /** Pointer to the corresponding tag in the IIC. */ + unsigned long tag_ptr; +}; + +/** + * Generational replacement pool + */ +class GenPool +{ + public: + /** The time the last entry was added. */ + Tick newest; + /** The time the oldest entry was added. */ + Tick oldest; + /** List of the replacement entries in this pool. */ + std::list<GenReplEntry*> entries; + + /** The number of entries in this pool. */ + int size; + + /** + * Simple constructor. + */ + GenPool() { + newest = 0; + oldest = 0; + size = 0; + } + + /** + * Add an entry to this pool. + * @param re The entry to add. + * @param now The current time. + */ + void push(GenReplEntry *re, Tick now) { + ++size; + if (!entries.empty()) { + re->delta = now - newest; + newest = now; + } else { + re->delta = 0; + newest = oldest = now; + } + entries.push_back(re); + } + + /** + * Remove an entry from the pool. + * @return The entry at the front of the list. + */ + GenReplEntry* pop() { + GenReplEntry *tmp = NULL; + if (!entries.empty()) { + --size; + tmp = entries.front(); + entries.pop_front(); + oldest += tmp->delta; + } + return tmp; + } + + /** + * Return the entry at the front of the list. + * @return the entry at the front of the list. + */ + GenReplEntry* top() { + return entries.front(); + } + + /** + * Destructor. + */ + ~GenPool() { + while (!entries.empty()) { + GenReplEntry *tmp = entries.front(); + entries.pop_front(); + delete tmp; + } + } +}; + +/** + * Generational replacement policy for use with the IIC. + * @todo update to use STL and for efficiency + */ +class GenRepl : public Repl +{ + public: + /** The array of pools. */ + GenPool *pools; + /** The number of pools. */ + int num_pools; + /** The amount of time to stay in the fresh pool. */ + int fresh_res; + /** The amount of time to stay in the normal pools. */ + int pool_res; + /** The maximum number of entries */ + int num_entries; + /** The number of entries currently in the pools. */ + int num_pool_entries; + /** The number of misses. Used as the internal time. */ + Tick misses; + + // Statistics + + /** + * @addtogroup CacheStatistics + * @{ + */ + /** The number of replacements from each pool. */ + Stats::Distribution<> repl_pool; + /** The number of advances out of each pool. */ + Stats::Distribution<> advance_pool; + /** The number of demotions from each pool. */ + Stats::Distribution<> demote_pool; + /** + * @} + */ + + /** + * Constructs and initializes this replacement policy. + * @param name The name of the policy. + * @param num_pools The number of pools to use. + * @param fresh_res The amount of time to wait in the fresh pool. + * @param pool_res The amount of time to wait in the normal pools. + */ + GenRepl(const std::string &name, int num_pools, + int fresh_res, int pool_res); + + /** + * Destructor. + */ + ~GenRepl(); + + /** + * Returns the tag pointer of the cache block to replace. + * @return The tag to replace. + */ + virtual unsigned long getRepl(); + + /** + * Return an array of N tag pointers to replace. + * @param n The number of tag pointer to return. + * @return An array of tag pointers to replace. + */ + virtual unsigned long *getNRepl(int n); + + /** + * Update replacement data + */ + virtual void doAdvance(std::list<unsigned long> &demoted); + + /** + * Add a tag to the replacement policy and return a pointer to the + * replacement entry. + * @param tag_index The tag to add. + * @return The replacement entry. + */ + virtual void* add(unsigned long tag_index); + + /** + * Register statistics. + * @param name The name to prepend to statistic descriptions. + */ + virtual void regStats(const std::string name); + + /** + * Update the tag pointer to when the tag moves. + * @param re The replacement entry of the tag. + * @param old_index The old tag pointer. + * @param new_index The new tag pointer. + * @return 1 if successful, 0 otherwise. + */ + virtual int fixTag(void *re, unsigned long old_index, + unsigned long new_index); + + /** + * Remove this entry from the replacement policy. + * @param re The replacement entry to remove + */ + virtual void removeEntry(void *re) + { + ((GenReplEntry*)re)->valid = false; + } + + protected: + /** + * Debug function to verify that there is only one repl entry per tag. + * @param index The tag index to check. + */ + bool findTagPtr(unsigned long index); +}; + +#endif /* __GEN_HH__ */ diff --git a/src/mem/cache/tags/repl/repl.cc b/src/mem/cache/tags/repl/repl.cc new file mode 100644 index 000000000..ce781eb9f --- /dev/null +++ b/src/mem/cache/tags/repl/repl.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Nathan Binkert + */ + +/** + * Definitions of the base replacement class. + */ + +#include "sim/param.hh" +#include "mem/cache/tags/repl/repl.hh" + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +DEFINE_SIM_OBJECT_CLASS_NAME("Repl", Repl) + +#endif //DOXYGEN_SHOULD_SKIP_THIS diff --git a/src/mem/cache/tags/repl/repl.hh b/src/mem/cache/tags/repl/repl.hh new file mode 100644 index 000000000..7c289a5c1 --- /dev/null +++ b/src/mem/cache/tags/repl/repl.hh @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Erik Hallnor + * Steve Reinhardt + * Nathan Binkert + */ + +/** + * @file + * Declaration of a base replacement policy class. + */ + +#ifndef __REPL_HH__ +#define __REPL_HH__ + +#include <string> +#include <list> + +#include "cpu/smt.hh" +#include "sim/host.hh" +#include "sim/sim_object.hh" + + +class IIC; + +/** + * A pure virtual base class that defines the interface of a replacement + * policy. + */ +class Repl : public SimObject +{ + public: + /** Pointer to the IIC using this policy. */ + IIC *iic; + + /** + * Construct and initialize this polixy. + * @param name The instance name of this policy. + */ + Repl (const std::string &name) + : SimObject(name) + { + iic = NULL; + } + + /** + * Set the back pointer to the IIC. + * @param iic_ptr Pointer to the IIC. + */ + void setIIC(IIC *iic_ptr) + { + iic = iic_ptr; + } + + /** + * Returns the tag pointer of the cache block to replace. + * @return The tag to replace. + */ + virtual unsigned long getRepl() = 0; + + /** + * Return an array of N tag pointers to replace. + * @param n The number of tag pointer to return. + * @return An array of tag pointers to replace. + */ + virtual unsigned long *getNRepl(int n) = 0; + + /** + * Update replacement data + */ + virtual void doAdvance(std::list<unsigned long> &demoted) = 0; + + /** + * Add a tag to the replacement policy and return a pointer to the + * replacement entry. + * @param tag_index The tag to add. + * @return The replacement entry. + */ + virtual void* add(unsigned long tag_index) = 0; + + /** + * Register statistics. + * @param name The name to prepend to statistic descriptions. + */ + virtual void regStats(const std::string name) = 0; + + /** + * Update the tag pointer to when the tag moves. + * @param re The replacement entry of the tag. + * @param old_index The old tag pointer. + * @param new_index The new tag pointer. + * @return 1 if successful, 0 otherwise. + */ + virtual int fixTag(void *re, unsigned long old_index, + unsigned long new_index) = 0; + + /** + * Remove this entry from the replacement policy. + * @param re The replacement entry to remove + */ + virtual void removeEntry(void *re) = 0; +}; + +#endif /* SMT_REPL_HH */ diff --git a/src/mem/cache/tags/split.cc b/src/mem/cache/tags/split.cc new file mode 100644 index 000000000..690eea22e --- /dev/null +++ b/src/mem/cache/tags/split.cc @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Definitions of split cache tag store. + */ + +#include <string> +#include <iostream> +#include <fstream> + +#include "base/cprintf.hh" +#include "base/intmath.hh" +#include "base/output.hh" +#include "base/trace.hh" +#include "mem/cache/base_cache.hh" +#include "mem/cache/tags/split.hh" +#include "mem/cache/tags/split_lifo.hh" +#include "mem/cache/tags/split_lru.hh" + + +using namespace std; +using namespace TheISA; + +// create and initialize a partitioned cache structure +Split::Split(int _numSets, int _blkSize, int total_ways, int LRU1_assoc, + bool _lifo, bool _two_queue, int _hit_latency) : + numSets(_numSets), blkSize(_blkSize), lifo(_lifo), hitLatency(_hit_latency) +{ + DPRINTF(Split, "new split cache!!\n"); + + DPRINTF(Split, "lru has %d numSets, %d blkSize, %d assoc, and %d hit_latency\n", + numSets, blkSize, LRU1_assoc, hitLatency); + + lru = new SplitLRU(_numSets, _blkSize, LRU1_assoc, _hit_latency, 1); + + if (total_ways - LRU1_assoc == 0) { + lifo_net = NULL; + lru_net = NULL; + } else { + if (lifo) { + DPRINTF(Split, "Other partition is a LIFO with size %d in bytes. it gets %d ways\n", + (total_ways - LRU1_assoc)*_numSets*_blkSize, (total_ways - LRU1_assoc)); + lifo_net = new SplitLIFO(_blkSize, (total_ways - LRU1_assoc)*_numSets*_blkSize, + (total_ways - LRU1_assoc), _hit_latency, _two_queue, 2); + lru_net = NULL; + } + else { + DPRINTF(Split, "other LRU gets %d ways\n", total_ways - LRU1_assoc); + lru_net = new SplitLRU(_numSets, _blkSize, total_ways - LRU1_assoc, _hit_latency, 2); + lifo_net = NULL; + } + } + + blkMask = blkSize - 1; + + if (!isPowerOf2(total_ways)) + warn("total cache ways/columns %d should be power of 2", + total_ways); + + warmedUp = false; + /** @todo Make warmup percentage a parameter. */ + warmupBound = numSets * total_ways; + +} + +Split::~Split() +{ + delete lru; + if (lifo) + delete lifo_net; + else + delete lru_net; +} + +void +Split::regStats(const string &name) +{ + using namespace Stats; + + BaseTags::regStats(name); + + usedEvictDist.init(0,3000,40); + unusedEvictDist.init(0,3000,40); + useByCPUCycleDist.init(0,35,1); + + nic_repl + .name(name + ".nic_repl") + .desc("number of replacements in the nic partition") + .precision(0) + ; + + cpu_repl + .name(name + ".cpu_repl") + .desc("number of replacements in the cpu partition") + .precision(0) + ; + + lru->regStats(name + ".lru"); + + if (lifo && lifo_net) { + lifo_net->regStats(name + ".lifo_net"); + } else if (lru_net) { + lru_net->regStats(name + ".lru_net"); + } + + nicUsedWhenEvicted + .name(name + ".nicUsedWhenEvicted") + .desc("number of NIC blks that were used before evicted") + ; + + nicUsedTotLatency + .name(name + ".nicUsedTotLatency") + .desc("total cycles before eviction of used NIC blks") + ; + + nicUsedTotEvicted + .name(name + ".nicUsedTotEvicted") + .desc("total number of used NIC blks evicted") + ; + + nicUsedAvgLatency + .name(name + ".nicUsedAvgLatency") + .desc("avg number of cycles a used NIC blk is in cache") + .precision(0) + ; + nicUsedAvgLatency = nicUsedTotLatency / nicUsedTotEvicted; + + usedEvictDist + .name(name + ".usedEvictDist") + .desc("distribution of used NIC blk eviction times") + .flags(pdf | cdf) + ; + + nicUnusedWhenEvicted + .name(name + ".nicUnusedWhenEvicted") + .desc("number of NIC blks that were unused when evicted") + ; + + nicUnusedTotLatency + .name(name + ".nicUnusedTotLatency") + .desc("total cycles before eviction of unused NIC blks") + ; + + nicUnusedTotEvicted + .name(name + ".nicUnusedTotEvicted") + .desc("total number of unused NIC blks evicted") + ; + + nicUnusedAvgLatency + .name(name + ".nicUnusedAvgLatency") + .desc("avg number of cycles an unused NIC blk is in cache") + .precision(0) + ; + nicUnusedAvgLatency = nicUnusedTotLatency / nicUnusedTotEvicted; + + unusedEvictDist + .name(name + ".unusedEvictDist") + .desc("distribution of unused NIC blk eviction times") + .flags(pdf | cdf) + ; + + nicUseByCPUCycleTotal + .name(name + ".nicUseByCPUCycleTotal") + .desc("total latency of NIC blks til usage time") + ; + + nicBlksUsedByCPU + .name(name + ".nicBlksUsedByCPU") + .desc("total number of NIC blks used") + ; + + nicAvgUsageByCPULatency + .name(name + ".nicAvgUsageByCPULatency") + .desc("average number of cycles before a NIC blk that is used gets used") + .precision(0) + ; + nicAvgUsageByCPULatency = nicUseByCPUCycleTotal / nicBlksUsedByCPU; + + useByCPUCycleDist + .name(name + ".useByCPUCycleDist") + .desc("the distribution of cycle time in cache before NIC blk is used") + .flags(pdf | cdf) + ; + + cpuUsedBlks + .name(name + ".cpuUsedBlks") + .desc("number of cpu blks that were used before evicted") + ; + + cpuUnusedBlks + .name(name + ".cpuUnusedBlks") + .desc("number of cpu blks that were unused before evicted") + ; + + nicAvgLatency + .name(name + ".nicAvgLatency") + .desc("avg number of cycles a NIC blk is in cache before evicted") + .precision(0) + ; + nicAvgLatency = (nicUnusedTotLatency + nicUsedTotLatency) / + (nicUnusedTotEvicted + nicUsedTotEvicted); + + NR_CP_hits + .name(name + ".NR_CP_hits") + .desc("NIC requests hitting in CPU Partition") + ; + + NR_NP_hits + .name(name + ".NR_NP_hits") + .desc("NIC requests hitting in NIC Partition") + ; + + CR_CP_hits + .name(name + ".CR_CP_hits") + .desc("CPU requests hitting in CPU partition") + ; + + CR_NP_hits + .name(name + ".CR_NP_hits") + .desc("CPU requests hitting in NIC partition") + ; + +} + +// probe cache for presence of given block. +bool +Split::probe(Addr addr) const +{ + bool success = lru->probe(addr); + if (!success) { + if (lifo && lifo_net) + success = lifo_net->probe(addr); + else if (lru_net) + success = lru_net->probe(addr); + } + + return success; +} + +SplitBlk* +Split::findBlock(Packet * &pkt, int &lat) +{ + + Addr aligned = blkAlign(pkt->getAddr()); + + if (memHash.count(aligned)) { + memHash[aligned]++; + } else if (pkt->nic_pkt()) { + memHash[aligned] = 1; + } + + SplitBlk *blk = lru->findBlock(pkt->getAddr(), lat); + if (blk) { + if (pkt->nic_pkt()) { + NR_CP_hits++; + } else { + CR_CP_hits++; + } + } else { + if (lifo && lifo_net) { + blk = lifo_net->findBlock(pkt->getAddr(), lat); + + } else if (lru_net) { + blk = lru_net->findBlock(pkt->getAddr(), lat); + } + if (blk) { + if (pkt->nic_pkt()) { + NR_NP_hits++; + } else { + CR_NP_hits++; + } + } + } + + if (blk) { + Tick latency = curTick - blk->ts; + if (blk->isNIC) { + if (!blk->isUsed && !pkt->nic_pkt()) { + useByCPUCycleDist.sample(latency); + nicUseByCPUCycleTotal += latency; + nicBlksUsedByCPU++; + } + } + blk->isUsed = true; + + if (pkt->nic_pkt()) { + DPRINTF(Split, "found block in partition %d\n", blk->part); + } + } + return blk; +} + +SplitBlk* +Split::findBlock(Addr addr, int &lat) +{ + SplitBlk *blk = lru->findBlock(addr, lat); + if (!blk) { + if (lifo && lifo_net) { + blk = lifo_net->findBlock(addr, lat); + } else if (lru_net) { + blk = lru_net->findBlock(addr, lat); + } + } + + return blk; +} + +SplitBlk* +Split::findBlock(Addr addr) const +{ + SplitBlk *blk = lru->findBlock(addr); + if (!blk) { + if (lifo && lifo_net) { + blk = lifo_net->findBlock(addr); + } else if (lru_net) { + blk = lru_net->findBlock(addr); + } + } + + return blk; +} + +SplitBlk* +Split::findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks) +{ + SplitBlk *blk; + + if (pkt->nic_pkt()) { + DPRINTF(Split, "finding a replacement for nic_req\n"); + nic_repl++; + if (lifo && lifo_net) + blk = lifo_net->findReplacement(pkt, writebacks, + compress_blocks); + else if (lru_net) + blk = lru_net->findReplacement(pkt, writebacks, + compress_blocks); + // in this case, this is an LRU only cache, it's non partitioned + else + blk = lru->findReplacement(pkt, writebacks, compress_blocks); + } else { + DPRINTF(Split, "finding replacement for cpu_req\n"); + blk = lru->findReplacement(pkt, writebacks, + compress_blocks); + cpu_repl++; + } + + Tick latency = curTick - blk->ts; + if (blk->isNIC) { + if (blk->isUsed) { + nicUsedWhenEvicted++; + usedEvictDist.sample(latency); + nicUsedTotLatency += latency; + nicUsedTotEvicted++; + } else { + nicUnusedWhenEvicted++; + unusedEvictDist.sample(latency); + nicUnusedTotLatency += latency; + nicUnusedTotEvicted++; + } + } else { + if (blk->isUsed) { + cpuUsedBlks++; + } else { + cpuUnusedBlks++; + } + } + + // blk attributes for the new blk coming IN + blk->ts = curTick; + blk->isNIC = (pkt->nic_pkt()) ? true : false; + + return blk; +} + +void +Split::invalidateBlk(Addr addr) +{ + SplitBlk *blk = lru->findBlock(addr); + if (!blk) { + if (lifo && lifo_net) + blk = lifo_net->findBlock(addr); + else if (lru_net) + blk = lru_net->findBlock(addr); + + if (!blk) + return; + } + + blk->status = 0; + blk->isTouched = false; + tagsInUse--; +} + +void +Split::doCopy(Addr source, Addr dest, PacketList &writebacks) +{ + if (lru->probe( source)) + lru->doCopy(source, dest, writebacks); + else { + if (lifo && lifo_net) + lifo_net->doCopy(source, dest, writebacks); + else if (lru_net) + lru_net->doCopy(source, dest, writebacks); + } +} + +void +Split::cleanupRefs() +{ + lru->cleanupRefs(); + if (lifo && lifo_net) + lifo_net->cleanupRefs(); + else if (lru_net) + lru_net->cleanupRefs(); + + ofstream memPrint(simout.resolve("memory_footprint.txt").c_str(), + ios::trunc); + + // this shouldn't be here but it happens at the end, which is what i want + memIter end = memHash.end(); + for (memIter iter = memHash.begin(); iter != end; ++iter) { + ccprintf(memPrint, "%8x\t%d\n", (*iter).first, (*iter).second); + } +} + +Addr +Split::regenerateBlkAddr(Addr tag, int set) const +{ + if (lifo_net) + return lifo_net->regenerateBlkAddr(tag, set); + else + return lru->regenerateBlkAddr(tag, set); +} + +Addr +Split::extractTag(Addr addr, SplitBlk *blk) const +{ + if (blk->part == 2) { + if (lifo_net) + return lifo_net->extractTag(addr); + else if (lru_net) + return lru_net->extractTag(addr); + else + panic("this shouldn't happen"); + } else + return lru->extractTag(addr); +} + diff --git a/src/mem/cache/tags/split.hh b/src/mem/cache/tags/split.hh new file mode 100644 index 000000000..f0091e971 --- /dev/null +++ b/src/mem/cache/tags/split.hh @@ -0,0 +1,335 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Declaration of a split/partitioned tag store. + */ + +#ifndef __SPLIT_HH__ +#define __SPLIT_HH__ + +#include <list> + +#include "mem/cache/cache_blk.hh" // base class +#include "mem/cache/tags/split_blk.hh" +#include "mem/packet.hh" // for inlined functions +#include <assert.h> +#include "mem/cache/tags/base_tags.hh" +#include "base/hashmap.hh" + +class BaseCache; +class SplitLRU; +class SplitLIFO; + +/** + * A cache tag store. + */ +class Split : public BaseTags +{ + public: + /** Typedef the block type used in this tag store. */ + typedef SplitBlk BlkType; + /** Typedef for a list of pointers to the local block class. */ + typedef std::list<SplitBlk*> BlkList; + protected: + /** The number of sets in the cache. */ + const int numSets; + /** The number of bytes in a block. */ + const int blkSize; + /** Whether the 2nd partition (for the nic) is LIFO or not */ + const bool lifo; + /** The hit latency. */ + const int hitLatency; + + Addr blkMask; + + /** Number of NIC pktuests that hit in the NIC partition */ + Stats::Scalar<> NR_NP_hits; + /** Number of NIC pktuests that hit in the CPU partition */ + Stats::Scalar<> NR_CP_hits; + /** Number of CPU pktuests that hit in the NIC partition */ + Stats::Scalar<> CR_NP_hits; + /** Number of CPU pktuests that hit in the CPU partition */ + Stats::Scalar<> CR_CP_hits; + /** The number of nic replacements (i.e. misses) */ + Stats::Scalar<> nic_repl; + /** The number of cpu replacements (i.e. misses) */ + Stats::Scalar<> cpu_repl; + + //For latency studies + /** the number of NIC blks that were used before evicted */ + Stats::Scalar<> nicUsedWhenEvicted; + /** the total latency of used NIC blocks in the cache */ + Stats::Scalar<> nicUsedTotLatency; + /** the total number of used NIC blocks evicted */ + Stats::Scalar<> nicUsedTotEvicted; + /** the average number of cycles a used NIC blk is in the cache */ + Stats::Formula nicUsedAvgLatency; + /** the Distribution of used NIC blk eviction times */ + Stats::Distribution<> usedEvictDist; + + /** the number of NIC blks that were unused before evicted */ + Stats::Scalar<> nicUnusedWhenEvicted; + /** the total latency of unused NIC blks in the cache */ + Stats::Scalar<> nicUnusedTotLatency; + /** the total number of unused NIC blocks evicted */ + Stats::Scalar<> nicUnusedTotEvicted; + /** the average number of cycles an unused NIC blk is in the cache */ + Stats::Formula nicUnusedAvgLatency; + /** the Distribution of unused NIC blk eviction times */ + Stats::Distribution<> unusedEvictDist; + + /** The total latency of NIC blocks to 1st usage time by CPU */ + Stats::Scalar<> nicUseByCPUCycleTotal; + /** The total number of NIC blocks used */ + Stats::Scalar<> nicBlksUsedByCPU; + /** the average number of cycles before a NIC blk that is used gets used by CPU */ + Stats::Formula nicAvgUsageByCPULatency; + /** the Distribution of cycles time before a NIC blk is used by CPU*/ + Stats::Distribution<> useByCPUCycleDist; + + /** the number of CPU blks that were used before evicted */ + Stats::Scalar<> cpuUsedBlks; + /** the number of CPU blks that were unused before evicted */ + Stats::Scalar<> cpuUnusedBlks; + + /** the avg number of cycles before a NIC blk is evicted */ + Stats::Formula nicAvgLatency; + + typedef m5::hash_map<Addr, int, m5::hash<Addr> > hash_t; + typedef hash_t::const_iterator memIter; + hash_t memHash; + + + private: + SplitLRU *lru; + SplitLRU *lru_net; + SplitLIFO *lifo_net; + + public: + /** + * Construct and initialize this tag store. + * @param _numSets The number of sets in the cache. + * @param _blkSize The number of bytes in a block. + * @param _assoc The associativity of the cache. + * @param _hit_latency The latency in cycles for a hit. + */ + Split(int _numSets, int _blkSize, int total_ways, int LRU1_assoc, + bool _lifo, bool _two_queue, int _hit_latency); + + /** + * Destructor + */ + virtual ~Split(); + + /** + * Register the stats for this object + * @param name The name to prepend to the stats name. + */ + void regStats(const std::string &name); + + /** + * Return the block size. + * @return the block size. + */ + int getBlockSize() + { + return blkSize; + } + + /** + * Return the subblock size. In the case of Split it is always the block + * size. + * @return The block size. + */ + int getSubBlockSize() + { + return blkSize; + } + + /** + * Search for the address in the cache. + * @param asid The address space ID. + * @param addr The address to find. + * @return True if the address is in the cache. + */ + bool probe(Addr addr) const; + + /** + * Invalidate the block containing the given address. + * @param asid The address space ID. + * @param addr The address to invalidate. + */ + void invalidateBlk(Addr addr); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param addr The address to find. + * @param asid The address space ID. + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Addr addr, int &lat); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param pkt The memory request whose block to find + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Packet * &pkt, int &lat); + + /** + * Finds the given address in the cache, do not update replacement data. + * @param addr The address to find. + * @param asid The address space ID. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Addr addr) const; + + /** + * Find a replacement block for the address provided. + * @param pkt The request to a find a replacement candidate for. + * @param writebacks List for any writebacks to be performed. + * @param compress_blocks List of blocks to compress, for adaptive comp. + * @return The block to place the replacement in. + */ + SplitBlk* findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks); + + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @param blk The block to find the partition it's in + * @return The tag of the address. + */ + Addr extractTag(Addr addr, SplitBlk *blk) const; + + /** + * Calculate the set index from the address. + * @param addr The address to get the set from. + * @return The set index of the address. + */ + int extractSet(Addr addr) const + { + panic("should never call this!\n"); + } + + /** + * Get the block offset from an address. + * @param addr The address to get the offset of. + * @return The block offset. + */ + int extractBlkOffset(Addr addr) const + { + return (addr & blkMask); + } + + /** + * Align an address to the block size. + * @param addr the address to align. + * @return The block address. + */ + Addr blkAlign(Addr addr) const + { + return (addr & ~(Addr) (blkMask)); + } + + /** + * Regenerate the block address from the tag. + * @param tag The tag of the block. + * @param set The set of the block. + * @return The block address. + */ + Addr regenerateBlkAddr(Addr tag, int set) const; + + /** + * Return the hit latency. + * @return the hit latency. + */ + int getHitLatency() const + { + return hitLatency; + } + + /** + * Read the data out of the internal storage of the given cache block. + * @param blk The cache block to read. + * @param data The buffer to read the data into. + * @return The cache block's data. + */ + void readData(SplitBlk *blk, uint8_t *data) + { + memcpy(data, blk->data, blk->size); + } + + /** + * Write data into the internal storage of the given cache block. Since in + * Split does not store data differently this just needs to update the size. + * @param blk The cache block to write. + * @param data The data to write. + * @param size The number of bytes to write. + * @param writebacks A list for any writebacks to be performed. May be + * needed when writing to a compressed block. + */ + void writeData(SplitBlk *blk, uint8_t *data, int size, + PacketList & writebacks) + { + assert(size <= blkSize); + blk->size = size; + } + + /** + * Perform a block aligned copy from the source address to the destination. + * @param source The block-aligned source address. + * @param dest The block-aligned destination address. + * @param asid The address space DI. + * @param writebacks List for any generated writeback pktuests. + */ + void doCopy(Addr source, Addr dest, PacketList &writebacks); + + /** + * No impl. + */ + void fixCopy(Packet * &pkt, PacketList &writebacks) + { + } + + /** + * Called at end of simulation to complete average block reference stats. + */ + virtual void cleanupRefs(); +}; + +#endif diff --git a/src/mem/cache/tags/split_blk.hh b/src/mem/cache/tags/split_blk.hh new file mode 100644 index 000000000..64d903579 --- /dev/null +++ b/src/mem/cache/tags/split_blk.hh @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Declaration of partitioned tag store cache block class. + */ + +#ifndef __SPLIT_BLK_HH__ +#define __SPLIT_BLK_HH__ + +#include "mem/cache/cache_blk.hh" // base class + +/** + * Split cache block. + */ +class SplitBlk : public CacheBlk { + public: + /** Has this block been touched? Used to aid calculation of warmup time. */ + bool isTouched; + /** Has this block been used after being brought in? (for LIFO partition) */ + bool isUsed; + /** is this blk a NIC block? (i.e. pktuested by the NIC) */ + bool isNIC; + /** timestamp of the arrival of this block into the cache */ + Tick ts; + /** the previous block in the LIFO partition (brought in before than me) */ + SplitBlk *prev; + /** the next block in the LIFO partition (brought in later than me) */ + SplitBlk *next; + /** which partition this block is in */ + int part; + + SplitBlk() + : isTouched(false), isUsed(false), isNIC(false), ts(0), prev(NULL), next(NULL), + part(0) + {} +}; + +#endif + diff --git a/src/mem/cache/tags/split_lifo.cc b/src/mem/cache/tags/split_lifo.cc new file mode 100644 index 000000000..6fcbf3597 --- /dev/null +++ b/src/mem/cache/tags/split_lifo.cc @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Definitions of LIFO tag store usable in a partitioned cache. + */ + +#include <string> + +#include "mem/cache/base_cache.hh" +#include "base/intmath.hh" +#include "mem/cache/tags/split_lifo.hh" +#include "sim/root.hh" +#include "base/trace.hh" + +using namespace std; + +SplitBlk* +LIFOSet::findBlk(Addr tag) const +{ + for (SplitBlk *blk = firstIn; blk != NULL; blk = blk->next) { + if (blk->tag == tag && blk->isValid()) { + return blk; + } + } + return NULL; +} + +void +LIFOSet::moveToLastIn(SplitBlk *blk) +{ + if (blk == lastIn) + return; + + if (blk == firstIn) { + blk->next->prev = NULL; + } else { + blk->prev->next = blk->next; + blk->next->prev = blk->prev; + } + blk->next = NULL; + blk->prev = lastIn; + lastIn->next = blk; + + lastIn = blk; +} + +void +LIFOSet::moveToFirstIn(SplitBlk *blk) +{ + if (blk == firstIn) + return; + + if (blk == lastIn) { + blk->prev->next = NULL; + } else { + blk->next->prev = blk->prev; + blk->prev->next = blk->next; + } + + blk->prev = NULL; + blk->next = firstIn; + firstIn->prev = blk; + + firstIn = blk; +} + +// create and initialize a LIFO cache structure +SplitLIFO::SplitLIFO(int _blkSize, int _size, int _ways, int _hit_latency, bool two_Queue, int _part) : + blkSize(_blkSize), size(_size), numBlks(_size/_blkSize), numSets((_size/_ways)/_blkSize), ways(_ways), + hitLatency(_hit_latency), twoQueue(two_Queue), part(_part) +{ + if (!isPowerOf2(blkSize)) + fatal("cache block size (in bytes) must be a power of 2"); + if (!(hitLatency > 0)) + fatal("access latency in cycles must be at least on cycle"); + if (_ways == 0) + fatal("if instantiating a splitLIFO, needs non-zero size!"); + + + SplitBlk *blk; + int i, j, blkIndex; + + setShift = floorLog2(blkSize); + blkMask = blkSize - 1; + setMask = numSets - 1; + tagShift = setShift + floorLog2(numSets); + + warmedUp = false; + /** @todo Make warmup percentage a parameter. */ + warmupBound = size/blkSize; + + // allocate data blocks + blks = new SplitBlk[numBlks]; + sets = new LIFOSet[numSets]; + dataBlks = new uint8_t[size]; + +/* + // these start off point to same blk + top = &(blks[0]); + head = top; +*/ + + blkIndex = 0; + for (i=0; i < numSets; ++i) { + sets[i].ways = ways; + sets[i].lastIn = &blks[blkIndex]; + sets[i].firstIn = &blks[blkIndex + ways - 1]; + + /* 3 cases: if there is 1 way, if there are 2 ways, or if there are 3+. + in the case of 1 way, last in and first out point to the same blocks, + and the next and prev pointers need to be assigned specially. and so on + */ + /* deal with the first way */ + blk = &blks[blkIndex]; + blk->prev = &blks[blkIndex + 1]; + blk->next = NULL; + blk->data = &dataBlks[blkSize*blkIndex]; + blk->size = blkSize; + blk->part = part; + blk->set = i; + ++blkIndex; + + /* if there are "middle" ways, do them here */ + if (ways > 2) { + for (j=1; j < ways-1; ++j) { + blk = &blks[blkIndex]; + blk->data = &dataBlks[blkSize*blkIndex]; + blk->prev = &blks[blkIndex+1]; + blk->next = &blks[blkIndex-1]; + blk->data = &(dataBlks[blkSize*blkIndex]); + blk->size = blkSize; + blk->part = part; + blk->set = i; + ++blkIndex; + } + } + + /* do the final way here, depending on whether the final way is the only + way or not + */ + if (ways > 1) { + blk = &blks[blkIndex]; + blk->prev = NULL; + blk->next = &blks[blkIndex - 1]; + blk->data = &dataBlks[blkSize*blkIndex]; + blk->size = blkSize; + blk->part = part; + blk->set = i; + ++blkIndex; + } else { + blk->prev = NULL; + } + } + assert(blkIndex == numBlks); +} + +SplitLIFO::~SplitLIFO() +{ + delete [] blks; + delete [] sets; + delete [] dataBlks; +} + +void +SplitLIFO::regStats(const std::string &name) +{ + BaseTags::regStats(name); + + hits + .name(name + ".hits") + .desc("number of hits on this partition") + .precision(0) + ; + + misses + .name(name + ".misses") + .desc("number of misses in this partition") + .precision(0) + ; + + invalidations + .name(name + ".invalidations") + .desc("number of invalidations in this partition") + .precision(0) + ; +} + +// probe cache for presence of given block. +bool +SplitLIFO::probe(Addr addr) const +{ + Addr tag = extractTag(addr); + unsigned myset = extractSet(addr); + + SplitBlk* blk = sets[myset].findBlk(tag); + return (blk != NULL); +} + +SplitBlk* +SplitLIFO::findBlock(Addr addr, int &lat) +{ + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + SplitBlk *blk = sets[set].findBlk(tag); + + lat = hitLatency; + + if (blk) { + DPRINTF(Split, "Found LIFO blk %#x in set %d, with tag %#x\n", + addr, set, tag); + hits++; + + if (blk->whenReady > curTick && blk->whenReady - curTick > hitLatency) + lat = blk->whenReady - curTick; + blk->refCount +=1; + + if (twoQueue) { + blk->isUsed = true; + sets[set].moveToFirstIn(blk); + } else { + sets[set].moveToLastIn(blk); + } + } + + return blk; +} + +SplitBlk* +SplitLIFO::findBlock(Packet * &pkt, int &lat) +{ + Addr addr = pkt->getAddr(); + + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + SplitBlk *blk = sets[set].findBlk(tag); + + if (blk) { + DPRINTF(Split, "Found LIFO blk %#x in set %d, with tag %#x\n", + addr, set, tag); + hits++; + + if (twoQueue) { + blk->isUsed = true; + sets[set].moveToFirstIn(blk); + } else { + sets[set].moveToLastIn(blk); + } + } + lat = hitLatency; + + return blk; +} + +SplitBlk* +SplitLIFO::findBlock(Addr addr) const +{ + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + SplitBlk *blk = sets[set].findBlk(tag); + + return blk; +} + +SplitBlk* +SplitLIFO::findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks) +{ + unsigned set = extractSet(pkt->getAddr()); + + SplitBlk *firstIn = sets[set].firstIn; + SplitBlk *lastIn = sets[set].lastIn; + + SplitBlk *blk; + if (twoQueue && firstIn->isUsed) { + blk = firstIn; + blk->isUsed = false; + sets[set].moveToLastIn(blk); + } else { + int withValue = sets[set].withValue; + if (withValue == ways) { + blk = lastIn; + } else { + blk = &(sets[set].firstIn[ways - ++withValue]); + } + } + + DPRINTF(Split, "just assigned %#x addr into LIFO, replacing %#x status %#x\n", + pkt->getAddr(), regenerateBlkAddr(blk->tag, set), blk->status); + if (blk->isValid()) { + replacements[0]++; + totalRefs += blk->refCount; + ++sampledRefs; + blk->refCount = 0; + } else { + tagsInUse++; + blk->isTouched = true; + if (!warmedUp && tagsInUse.value() >= warmupBound) { + warmedUp = true; + warmupCycle = curTick; + } + } + + misses++; + + return blk; +} + +void +SplitLIFO::invalidateBlk(Addr addr) +{ + SplitBlk *blk = findBlock(addr); + if (blk) { + blk->status = 0; + blk->isTouched = false; + tagsInUse--; + invalidations++; + } +} + +void +SplitLIFO::doCopy(Addr source, Addr dest, PacketList &writebacks) +{ +//Copy Unsuported for now +#if 0 + assert(source == blkAlign(source)); + assert(dest == blkAlign(dest)); + SplitBlk *source_blk = findBlock(source); + assert(source_blk); + SplitBlk *dest_blk = findBlock(dest); + if (dest_blk == NULL) { + // Need to do a replacement + Packet * pkt = new Packet(); + pkt->paddr = dest; + BlkList dummy_list; + dest_blk = findReplacement(pkt, writebacks, dummy_list); + if (dest_blk->isValid() && dest_blk->isModified()) { + // Need to writeback data. + pkt = buildWritebackReq(regenerateBlkAddr(dest_blk->tag, + dest_blk->set), + dest_blk->xc, + blkSize, + (cache->doData())?dest_blk->data:0, + dest_blk->size); + writebacks.push_back(pkt); + } + dest_blk->tag = extractTag(dest); + /** + * @todo Do we need to pass in the execution context, or can we + * assume its the same? + */ + assert(source_blk->xc); + dest_blk->xc = source_blk->xc; + } + /** + * @todo Can't assume the status once we have coherence on copies. + */ + + // Set this block as readable, writeable, and dirty. + dest_blk->status = 7; + if (cache->doData()) { + memcpy(dest_blk->data, source_blk->data, blkSize); + } +#endif +} + +void +SplitLIFO::cleanupRefs() +{ + for (int i = 0; i < numBlks; ++i) { + if (blks[i].isValid()) { + totalRefs += blks[i].refCount; + ++sampledRefs; + } + } +} diff --git a/src/mem/cache/tags/split_lifo.hh b/src/mem/cache/tags/split_lifo.hh new file mode 100644 index 000000000..355a66162 --- /dev/null +++ b/src/mem/cache/tags/split_lifo.hh @@ -0,0 +1,350 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Declaration of a LIFO tag store usable in a partitioned cache. + */ + +#ifndef __SPLIT_LIFO_HH__ +#define __SPLIT_LIFO_HH__ + +#include <list> + +#include "mem/cache/cache_blk.hh" // base class +#include "mem/cache/tags/split_blk.hh" +#include "mem/packet.hh" // for inlined functions +#include "base/hashmap.hh" +#include <assert.h> +#include "mem/cache/tags/base_tags.hh" + +class BaseCache; + +/** + * A LIFO set of cache blks + */ +class LIFOSet { + public: + /** the number of blocks in this set */ + int ways; + + /** Cache blocks in this set, maintained in LIFO order where + 0 = Last in (head) */ + SplitBlk *lastIn; + SplitBlk *firstIn; + + /** has the initial "filling" of this set finished? i.e., have you had + * 'ways' number of compulsory misses in this set yet? if withValue == ways, + * then yes. withValue is meant to be the number of blocks in the set that have + * gone through their first compulsory miss. + */ + int withValue; + + /** + * Find a block matching the tag in this set. + * @param asid The address space ID. + * @param tag the Tag you are looking for + * @return Pointer to the block, if found, NULL otherwise + */ + SplitBlk* findBlk(Addr tag) const; + + void moveToLastIn(SplitBlk *blk); + void moveToFirstIn(SplitBlk *blk); + + LIFOSet() + : ways(-1), lastIn(NULL), firstIn(NULL), withValue(0) + {} +}; + +/** + * A LIFO cache tag store. + */ +class SplitLIFO : public BaseTags +{ + public: + /** Typedef the block type used in this tag store. */ + typedef SplitBlk BlkType; + /** Typedef for a list of pointers to the local block class. */ + typedef std::list<SplitBlk*> BlkList; + protected: + /** The number of bytes in a block. */ + const int blkSize; + /** the size of the cache in bytes */ + const int size; + /** the number of blocks in the cache */ + const int numBlks; + /** the number of sets in the cache */ + const int numSets; + /** the number of ways in the cache */ + const int ways; + /** The hit latency. */ + const int hitLatency; + /** whether this is a "2 queue" replacement @sa moveToLastIn @sa moveToFirstIn */ + const bool twoQueue; + /** indicator for which partition this is */ + const int part; + + /** The cache blocks. */ + SplitBlk *blks; + /** The Cache sets */ + LIFOSet *sets; + /** The data blocks, 1 per cache block. */ + uint8_t *dataBlks; + + /** The amount to shift the address to get the set. */ + int setShift; + /** The amount to shift the address to get the tag. */ + int tagShift; + /** Mask out all bits that aren't part of the set index. */ + unsigned setMask; + /** Mask out all bits that aren't part of the block offset. */ + unsigned blkMask; + + + /** the number of hit in this partition */ + Stats::Scalar<> hits; + /** the number of blocks brought into this partition (i.e. misses) */ + Stats::Scalar<> misses; + /** the number of invalidations in this partition */ + Stats::Scalar<> invalidations; + +public: + /** + * Construct and initialize this tag store. + * @param _numSets The number of sets in the cache. + * @param _blkSize The number of bytes in a block. + * @param _assoc The associativity of the cache. + * @param _hit_latency The latency in cycles for a hit. + */ + SplitLIFO(int _blkSize, int _size, int _ways, int _hit_latency, bool twoQueue, int _part); + + /** + * Destructor + */ + virtual ~SplitLIFO(); + + /** + * Register the statistics for this object + * @param name The name to precede the stat + */ + void regStats(const std::string &name); + + /** + * Return the block size. + * @return the block size. + */ + int getBlockSize() + { + return blkSize; + } + + /** + * Return the subblock size. In the case of LIFO it is always the block + * size. + * @return The block size. + */ + int getSubBlockSize() + { + return blkSize; + } + + /** + * Search for the address in the cache. + * @param asid The address space ID. + * @param addr The address to find. + * @return True if the address is in the cache. + */ + bool probe( Addr addr) const; + + /** + * Invalidate the block containing the given address. + * @param asid The address space ID. + * @param addr The address to invalidate. + */ + void invalidateBlk(Addr addr); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param addr The address to find. + * @param asid The address space ID. + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Addr addr, int &lat); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param pkt The req whose block to find + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Packet * &pkt, int &lat); + + /** + * Finds the given address in the cache, do not update replacement data. + * @param addr The address to find. + * @param asid The address space ID. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Addr addr) const; + + /** + * Find a replacement block for the address provided. + * @param pkt The request to a find a replacement candidate for. + * @param writebacks List for any writebacks to be performed. + * @param compress_blocks List of blocks to compress, for adaptive comp. + * @return The block to place the replacement in. + */ + SplitBlk* findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks); + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @return The tag of the address. + */ + Addr extractTag(Addr addr) const + { + return (addr >> tagShift); + } + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @param blk Ignored + * @return The tag of the address. + */ + Addr extractTag(Addr addr, SplitBlk *blk) const + { + return (addr >> tagShift); + } + + /** + * Calculate the set index from the address. + * @param addr The address to get the set from. + * @return The set index of the address. + */ + int extractSet(Addr addr) const + { + return ((addr >> setShift) & setMask); + } + + /** + * Get the block offset from an address. + * @param addr The address to get the offset of. + * @return The block offset. + */ + int extractBlkOffset(Addr addr) const + { + return (addr & blkMask); + } + + /** + * Align an address to the block size. + * @param addr the address to align. + * @return The block address. + */ + Addr blkAlign(Addr addr) const + { + return (addr & ~(Addr)blkMask); + } + + /** + * Regenerate the block address from the tag. + * @param tag The tag of the block. + * @param set The set of the block. + * @return The block address. + */ + Addr regenerateBlkAddr(Addr tag, unsigned set) const + { + return ((tag << tagShift) | ((Addr)set << setShift)); + } + + /** + * Return the hit latency. + * @return the hit latency. + */ + int getHitLatency() const + { + return hitLatency; + } + + /** + * Read the data out of the internal storage of the given cache block. + * @param blk The cache block to read. + * @param data The buffer to read the data into. + * @return The cache block's data. + */ + void readData(SplitBlk *blk, uint8_t *data) + { + memcpy(data, blk->data, blk->size); + } + + /** + * Write data into the internal storage of the given cache block. Since in + * LIFO does not store data differently this just needs to update the size. + * @param blk The cache block to write. + * @param data The data to write. + * @param size The number of bytes to write. + * @param writebacks A list for any writebacks to be performed. May be + * needed when writing to a compressed block. + */ + void writeData(SplitBlk *blk, uint8_t *data, int size, + PacketList & writebacks) + { + assert(size <= blkSize); + blk->size = size; + } + + /** + * Perform a block aligned copy from the source address to the destination. + * @param source The block-aligned source address. + * @param dest The block-aligned destination address. + * @param asid The address space DI. + * @param writebacks List for any generated writeback pktuests. + */ + void doCopy(Addr source, Addr dest, PacketList &writebacks); + + /** + * No impl. + */ + void fixCopy(Packet * &pkt, PacketList &writebacks) + { + } + + /** + * Called at end of simulation to complete average block reference stats. + */ + virtual void cleanupRefs(); +}; + +#endif diff --git a/src/mem/cache/tags/split_lru.cc b/src/mem/cache/tags/split_lru.cc new file mode 100644 index 000000000..4381923bd --- /dev/null +++ b/src/mem/cache/tags/split_lru.cc @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Definitions of LRU tag store for a partitioned cache. + */ + +#include <string> + +#include "mem/cache/base_cache.hh" +#include "base/intmath.hh" +#include "mem/cache/tags/split_lru.hh" +#include "sim/root.hh" + +using namespace std; + +SplitBlk* +SplitCacheSet::findBlk(Addr tag) const +{ + for (int i = 0; i < assoc; ++i) { + if (blks[i]->tag == tag && blks[i]->isValid()) { + return blks[i]; + } + } + return 0; +} + + +void +SplitCacheSet::moveToHead(SplitBlk *blk) +{ + // nothing to do if blk is already head + if (blks[0] == blk) + return; + + // write 'next' block into blks[i], moving up from MRU toward LRU + // until we overwrite the block we moved to head. + + // start by setting up to write 'blk' into blks[0] + int i = 0; + SplitBlk *next = blk; + + do { + assert(i < assoc); + // swap blks[i] and next + SplitBlk *tmp = blks[i]; + blks[i] = next; + next = tmp; + ++i; + } while (next != blk); +} + + +// create and initialize a LRU/MRU cache structure +SplitLRU::SplitLRU(int _numSets, int _blkSize, int _assoc, int _hit_latency, int _part) : + numSets(_numSets), blkSize(_blkSize), assoc(_assoc), hitLatency(_hit_latency), part(_part) +{ + // Check parameters + if (blkSize < 4 || !isPowerOf2(blkSize)) { + fatal("Block size must be at least 4 and a power of 2"); + } + if (numSets <= 0 || !isPowerOf2(numSets)) { + fatal("# of sets must be non-zero and a power of 2"); + } + if (assoc <= 0) { + fatal("associativity must be greater than zero"); + } + if (hitLatency <= 0) { + fatal("access latency must be greater than zero"); + } + + SplitBlk *blk; + int i, j, blkIndex; + + blkMask = blkSize - 1; + setShift = floorLog2(blkSize); + setMask = numSets - 1; + tagShift = setShift + floorLog2(numSets); + warmedUp = false; + /** @todo Make warmup percentage a parameter. */ + warmupBound = numSets * assoc; + + sets = new SplitCacheSet[numSets]; + blks = new SplitBlk[numSets * assoc]; + // allocate data storage in one big chunk + dataBlks = new uint8_t[numSets*assoc*blkSize]; + + blkIndex = 0; // index into blks array + for (i = 0; i < numSets; ++i) { + sets[i].assoc = assoc; + + sets[i].blks = new SplitBlk*[assoc]; + + // link in the data blocks + for (j = 0; j < assoc; ++j) { + // locate next cache block + blk = &blks[blkIndex]; + blk->data = &dataBlks[blkSize*blkIndex]; + ++blkIndex; + + // invalidate new cache block + blk->status = 0; + + //EGH Fix Me : do we need to initialize blk? + + // Setting the tag to j is just to prevent long chains in the hash + // table; won't matter because the block is invalid + blk->tag = j; + blk->whenReady = 0; + blk->isTouched = false; + blk->size = blkSize; + sets[i].blks[j]=blk; + blk->set = i; + blk->part = part; + } + } +} + +SplitLRU::~SplitLRU() +{ + delete [] dataBlks; + delete [] blks; + delete [] sets; +} + +void +SplitLRU::regStats(const std::string &name) +{ + BaseTags::regStats(name); + + hits + .name(name + ".hits") + .desc("number of hits on this partition") + .precision(0) + ; + + misses + .name(name + ".misses") + .desc("number of misses in this partition") + .precision(0) + ; +} + +// probe cache for presence of given block. +bool +SplitLRU::probe(Addr addr) const +{ + // return(findBlock(Read, addr, asid) != 0); + Addr tag = extractTag(addr); + unsigned myset = extractSet(addr); + + SplitBlk *blk = sets[myset].findBlk(tag); + + return (blk != NULL); // true if in cache +} + +SplitBlk* +SplitLRU::findBlock(Addr addr, int &lat) +{ + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + SplitBlk *blk = sets[set].findBlk(tag); + lat = hitLatency; + if (blk != NULL) { + // move this block to head of the MRU list + sets[set].moveToHead(blk); + if (blk->whenReady > curTick && blk->whenReady - curTick > hitLatency){ + lat = blk->whenReady - curTick; + } + blk->refCount += 1; + hits++; + } + + return blk; +} + +SplitBlk* +SplitLRU::findBlock(Packet * &pkt, int &lat) +{ + Addr addr = pkt->getAddr(); + + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + SplitBlk *blk = sets[set].findBlk(tag); + lat = hitLatency; + if (blk != NULL) { + // move this block to head of the MRU list + sets[set].moveToHead(blk); + if (blk->whenReady > curTick && blk->whenReady - curTick > hitLatency){ + lat = blk->whenReady - curTick; + } + blk->refCount += 1; + hits++; + } + + return blk; +} + +SplitBlk* +SplitLRU::findBlock(Addr addr) const +{ + Addr tag = extractTag(addr); + unsigned set = extractSet(addr); + SplitBlk *blk = sets[set].findBlk(tag); + return blk; +} + +SplitBlk* +SplitLRU::findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks) +{ + unsigned set = extractSet(pkt->getAddr()); + // grab a replacement candidate + SplitBlk *blk = sets[set].blks[assoc-1]; + sets[set].moveToHead(blk); + if (blk->isValid()) { + replacements[0]++; + totalRefs += blk->refCount; + ++sampledRefs; + blk->refCount = 0; + } else if (!blk->isTouched) { + tagsInUse++; + blk->isTouched = true; + if (!warmedUp && tagsInUse.value() >= warmupBound) { + warmedUp = true; + warmupCycle = curTick; + } + } + + misses++; + + return blk; +} + +void +SplitLRU::invalidateBlk(Addr addr) +{ + SplitBlk *blk = findBlock(addr); + if (blk) { + blk->status = 0; + blk->isTouched = false; + tagsInUse--; + } +} + +void +SplitLRU::doCopy(Addr source, Addr dest, PacketList &writebacks) +{ +//Copy not supported for now +#if 0 + assert(source == blkAlign(source)); + assert(dest == blkAlign(dest)); + SplitBlk *source_blk = findBlock(source); + assert(source_blk); + SplitBlk *dest_blk = findBlock(dest); + if (dest_blk == NULL) { + // Need to do a replacement + Packet * pkt = new Packet(); + pkt->paddr = dest; + BlkList dummy_list; + dest_blk = findReplacement(pkt, writebacks, dummy_list); + if (dest_blk->isValid() && dest_blk->isModified()) { + // Need to writeback data. + pkt = buildWritebackReq(regenerateBlkAddr(dest_blk->tag, + dest_blk->set), + dest_blk->xc, + blkSize, + (cache->doData())?dest_blk->data:0, + dest_blk->size); + writebacks.push_back(pkt); + } + dest_blk->tag = extractTag(dest); + /** + * @todo Do we need to pass in the execution context, or can we + * assume its the same? + */ + assert(source_blk->xc); + dest_blk->xc = source_blk->xc; + } + /** + * @todo Can't assume the status once we have coherence on copies. + */ + + // Set this block as readable, writeable, and dirty. + dest_blk->status = 7; + if (cache->doData()) { + memcpy(dest_blk->data, source_blk->data, blkSize); + } +#endif +} + +void +SplitLRU::cleanupRefs() +{ + for (int i = 0; i < numSets*assoc; ++i) { + if (blks[i].isValid()) { + totalRefs += blks[i].refCount; + ++sampledRefs; + } + } +} diff --git a/src/mem/cache/tags/split_lru.hh b/src/mem/cache/tags/split_lru.hh new file mode 100644 index 000000000..72aebac9c --- /dev/null +++ b/src/mem/cache/tags/split_lru.hh @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Lisa Hsu + */ + +/** + * @file + * Declaration of a LRU tag store for a partitioned cache. + */ + +#ifndef __SPLIT_LRU_HH__ +#define __SPLIT_LRU_HH__ + +#include <list> + +#include "mem/cache/cache_blk.hh" // base class +#include "mem/cache/tags/split_blk.hh" +#include "mem/packet.hh" // for inlined functions +#include <assert.h> +#include "mem/cache/tags/base_tags.hh" + +class BaseCache; + +/** + * An associative set of cache blocks. + */ + +class SplitCacheSet +{ + public: + /** The associativity of this set. */ + int assoc; + + /** Cache blocks in this set, maintained in LRU order 0 = MRU. */ + SplitBlk **blks; + + /** + * Find a block matching the tag in this set. + * @param asid The address space ID. + * @param tag The Tag to find. + * @return Pointer to the block if found. + */ + SplitBlk* findBlk(Addr tag) const; + + /** + * Move the given block to the head of the list. + * @param blk The block to move. + */ + void moveToHead(SplitBlk *blk); +}; + +/** + * A LRU cache tag store. + */ +class SplitLRU : public BaseTags +{ + public: + /** Typedef the block type used in this tag store. */ + typedef SplitBlk BlkType; + /** Typedef for a list of pointers to the local block class. */ + typedef std::list<SplitBlk*> BlkList; + protected: + /** The number of sets in the cache. */ + const int numSets; + /** The number of bytes in a block. */ + const int blkSize; + /** The associativity of the cache. */ + const int assoc; + /** The hit latency. */ + const int hitLatency; + /** indicator for which partition this is */ + const int part; + + /** The cache sets. */ + SplitCacheSet *sets; + + /** The cache blocks. */ + SplitBlk *blks; + /** The data blocks, 1 per cache block. */ + uint8_t *dataBlks; + + /** The amount to shift the address to get the set. */ + int setShift; + /** The amount to shift the address to get the tag. */ + int tagShift; + /** Mask out all bits that aren't part of the set index. */ + unsigned setMask; + /** Mask out all bits that aren't part of the block offset. */ + unsigned blkMask; + + /** number of hits in this partition */ + Stats::Scalar<> hits; + /** number of blocks brought into this partition (i.e. misses) */ + Stats::Scalar<> misses; + +public: + /** + * Construct and initialize this tag store. + * @param _numSets The number of sets in the cache. + * @param _blkSize The number of bytes in a block. + * @param _assoc The associativity of the cache. + * @param _hit_latency The latency in cycles for a hit. + */ + SplitLRU(int _numSets, int _blkSize, int _assoc, int _hit_latency, int _part); + + /** + * Destructor + */ + virtual ~SplitLRU(); + + /** + * Register the statistics for this object + * @param name The name to precede the stat + */ + void regStats(const std::string &name); + + /** + * Return the block size. + * @return the block size. + */ + int getBlockSize() + { + return blkSize; + } + + /** + * Return the subblock size. In the case of LRU it is always the block + * size. + * @return The block size. + */ + int getSubBlockSize() + { + return blkSize; + } + + /** + * Search for the address in the cache. + * @param asid The address space ID. + * @param addr The address to find. + * @return True if the address is in the cache. + */ + bool probe(Addr addr) const; + + /** + * Invalidate the block containing the given address. + * @param asid The address space ID. + * @param addr The address to invalidate. + */ + void invalidateBlk(Addr addr); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param addr The address to find. + * @param asid The address space ID. + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Addr addr, int &lat); + + /** + * Finds the given address in the cache and update replacement data. + * Returns the access latency as a side effect. + * @param pkt The req whose block to find. + * @param lat The access latency. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Packet * &pkt, int &lat); + + /** + * Finds the given address in the cache, do not update replacement data. + * @param addr The address to find. + * @param asid The address space ID. + * @return Pointer to the cache block if found. + */ + SplitBlk* findBlock(Addr addr) const; + + /** + * Find a replacement block for the address provided. + * @param pkt The request to a find a replacement candidate for. + * @param writebacks List for any writebacks to be performed. + * @param compress_blocks List of blocks to compress, for adaptive comp. + * @return The block to place the replacement in. + */ + SplitBlk* findReplacement(Packet * &pkt, PacketList &writebacks, + BlkList &compress_blocks); + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @return The tag of the address. + */ + Addr extractTag(Addr addr) const + { + return (addr >> tagShift); + } + + /** + * Generate the tag from the given address. + * @param addr The address to get the tag from. + * @param blk Ignored. + * @return The tag of the address. + */ + Addr extractTag(Addr addr, SplitBlk *blk) const + { + return (addr >> tagShift); + } + + /** + * Calculate the set index from the address. + * @param addr The address to get the set from. + * @return The set index of the address. + */ + int extractSet(Addr addr) const + { + return ((addr >> setShift) & setMask); + } + + /** + * Get the block offset from an address. + * @param addr The address to get the offset of. + * @return The block offset. + */ + int extractBlkOffset(Addr addr) const + { + return (addr & blkMask); + } + + /** + * Align an address to the block size. + * @param addr the address to align. + * @return The block address. + */ + Addr blkAlign(Addr addr) const + { + return (addr & ~(Addr)blkMask); + } + + /** + * Regenerate the block address from the tag. + * @param tag The tag of the block. + * @param set The set of the block. + * @return The block address. + */ + Addr regenerateBlkAddr(Addr tag, unsigned set) const + { + return ((tag << tagShift) | ((Addr)set << setShift)); + } + + /** + * Return the hit latency. + * @return the hit latency. + */ + int getHitLatency() const + { + return hitLatency; + } + + /** + * Read the data out of the internal storage of the given cache block. + * @param blk The cache block to read. + * @param data The buffer to read the data into. + * @return The cache block's data. + */ + void readData(SplitBlk *blk, uint8_t *data) + { + memcpy(data, blk->data, blk->size); + } + + /** + * Write data into the internal storage of the given cache block. Since in + * LRU does not store data differently this just needs to update the size. + * @param blk The cache block to write. + * @param data The data to write. + * @param size The number of bytes to write. + * @param writebacks A list for any writebacks to be performed. May be + * needed when writing to a compressed block. + */ + void writeData(SplitBlk *blk, uint8_t *data, int size, + PacketList & writebacks) + { + assert(size <= blkSize); + blk->size = size; + } + + /** + * Perform a block aligned copy from the source address to the destination. + * @param source The block-aligned source address. + * @param dest The block-aligned destination address. + * @param asid The address space DI. + * @param writebacks List for any generated writeback pktuests. + */ + void doCopy(Addr source, Addr dest, PacketList &writebacks); + + /** + * No impl. + */ + void fixCopy(Packet * &pkt, PacketList &writebacks) + { + } + + /** + * Called at end of simulation to complete average block reference stats. + */ + virtual void cleanupRefs(); +}; + +#endif diff --git a/src/mem/config/cache.hh b/src/mem/config/cache.hh new file mode 100644 index 000000000..24da04021 --- /dev/null +++ b/src/mem/config/cache.hh @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/** + * @file + * Central location to configure which cache types we want to build + * into the simulator. In the future, this should probably be + * autogenerated by some sort of configuration script. + */ +#define USE_CACHE_LRU 1 +#define USE_CACHE_FALRU 1 +// #define USE_CACHE_SPLIT 1 +// #define USE_CACHE_SPLIT_LIFO 1 +#define USE_CACHE_IIC 1 + diff --git a/src/mem/config/prefetch.hh b/src/mem/config/prefetch.hh new file mode 100644 index 000000000..d24db79da --- /dev/null +++ b/src/mem/config/prefetch.hh @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Central location to configure which prefetch types we want to build + * into the simulator. In the future, this should probably be + * autogenerated by some sort of configuration script. + */ +#define USE_TAGGED 1 //Be sure not to turn this off, it is also used for no + //prefetching case unless you always want to use a + //different prefetcher +//#define USE_STRIDED 1 +//#define USE_GHB 1 diff --git a/src/mem/dram.cc b/src/mem/dram.cc new file mode 100644 index 000000000..d7b955975 --- /dev/null +++ b/src/mem/dram.cc @@ -0,0 +1,2746 @@ +/* + * Copyright (c) 2004 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Ron Dreslinski + */ + +/* + Copyright (c) 2000 Computer Engineering and Communication Networks Lab (TIK) + Swiss Federal Institute of Technology (ETH) Zurich, Switzerland + + All rights reserved. + Permission is hereby granted, without written agreement and without + license or royalty fees, to use, copy, modify, and distribute this + software and its documentation for any purpose, provided that the above + copyright notice and the following two paragraphs appear in all copies + of this software. + + IN NO EVENT SHALL THE TIK OR THE ETH ZURICH BE LIABLE TO ANY PARTY + FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF + THE TIK OR THE ETH ZURICH HAVE BEEN ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + + THE TIK AND THE ETH ZURICH SPECIFICALLY DISCLAIM ANY WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE + PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND TIK AND THE ETH ZURICH + HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, + ENHANCEMENTS, OR MODIFICATIONS. +*/ + +/* authors: Andreas Romer 4/99 - 7/99 + Matthias Gries 4/99 - 2/01 + + +References: http://www.tik.ee.ethz.ch/ +====================================== +-> Publications http://www.tik.ee.ethz.ch/db/tik/publications/form_search_publications.php3 + + +Matthias Gries: A Survey of Synchronous RAM Architectures. +TIK Report Nr. 71, Computer Engineering and Networks Lab (TIK), +Swiss Federal Institute of Technology (ETH) Zurich, April, 1999 + +-> DRAM survey + + +Matthias Gries, Andreas Romer: Performance Evaluation of Recent +DRAM Architectures for Embedded Systems. +TIK Report Nr. 82, Computer Engineering and Networks Lab (TIK), +Swiss Federal Institute of Technology (ETH) Zurich, November, 1999. + +-> description of the DRAM and controller models for SimpleScalar in the appendix +(note that the current software version additionally supports overlapping in + closed-page mode with slightly modified timing) + + +Matthias Gries: The Impact of Recent DRAM Architectures on Embedded Systems Performance. +Euromicro2000, Symposium on Digital Systems Design, IEEE Computer, Maastricht, Netherlands, +Vol. 1, pages 282-289, September, 2000. + +-> performance study with SimpleScalar + + +Matthias Gries: Modeling a Memory Subsystem with Petri Nets: a Case Study. +A. Yakovlev, L. Gomes, and L. Lavagno (Eds), Hardware Design and Petri Nets, +Kluwer Academic, pages 291-310, March, 2000. + +-> SDRAM + controller performance model as a high-level Petri net +*/ + +/** + * @file + * Definition of a DRAM like main memory. + */ + + +#include "mem/dram.hh" +#include "sim/builder.hh" + +#include <string> + +extern int maxThreadsPerCPU; + +/* SDRAM system: PC100/PC133 2-2-2 DIMM timing according to + PC SDRAM Specification, Rev. 1.7, Intel Corp, Nov. 1999. + + 64 bit DIMM consists of four 16x organized 256 Mbit SDRAMs, 128 MByte of main memory in total.*/ +/* the settings above must be changed if another memory is used */ +/* DRDRAM system: 16 bit channel, four chips (single RIMM), 128 MByte of main memory in total. + Timing: Rambus Inc, Direct RDRAM, preliminary information, 256/288Mbit: 40-800 timing */ + + +#define DR_STACK_BASE 0x8000000 /* total size of memory: 128 Mbyte */ +#define DR_BANK_SIZE 0x100000 /* size of a bank : 1 Mbyte */ +#define DR_ROW_SIZE 0x800 /* size of a row : 2 Kbyte */ +#define DR_NUM_BANKS (DR_STACK_BASE/DR_BANK_SIZE) /* number of banks : 128 */ +#define DR_NUM_ROWS (DR_BANK_SIZE/DR_ROW_SIZE) /* number of rows per bank: 512 */ +#define DR_DATA_BASE 0x4000000 /* Size of textsegment : 64 Mbyte */ +#define DR_NUM_BYTE_MEM 16 /* data packet capacity: 16 byte */ +#define DR_NUM_DEVS 4 /* number of devices along channel */ +#define DR_BANK_SAMP 16 /* 16 banks are together in one group in each device: bank 15 and 16 have no shared SAMPs */ +#define DR_T_PACKET 4 /* number of cycles (in 400 MHz) the memory needs to deliver a data packet */ +#define DR_T_RCD 7 /* RAS to CAS delay */ +#define DR_T_CAC 8 /* read access delay: number of cylces from read to data (trailing to leading edge of packet!) */ +#define DR_T_CWD 6 /* Write delay: number of cylces from write to write data (trailing to leading edge of packet!) */ +#define DR_T_RP 8 /* row precharge delay */ +#define DR_T_RTR 8 /* retire delay*/ +#define DR_T_RDP 4 /* min delay from read to precharge in cycles */ +#define DR_T_PP 8 /* precharge to precharge time to any bank in the same device */ +#define DR_T_RAS 20 /* minimal row active time */ +/*the settings above need to be changed if the memory is altered*/ +#define DR_DYNAMIC_SIZE (DR_STACK_BASE - DR_DATA_BASE) /* size of the heap and stack at most: 64 Mbyte */ +// #define DR_NUM_BANKS (DR_STACK_BASE/DR_BANK_SIZE) /* number of banks : 128 */ +// #define DR_NUM_ROWS (DR_BANK_SIZE/DR_ROW_SIZE) /* number of rows per bank: 512 */ +#define DR_T_OWR (DR_T_CWD + DR_T_PACKET - DR_T_RTR) /* overlap after write retire */ +#define DR_T_HELP (DR_T_CAC+DR_T_PACKET-DR_T_RDP+DR_T_PACKET) /* used for read after read with precharge */ +/*delays until data is ready/written to the memory for the DRDRAM*/ +#define DR_T_READ_READ_SROW (DR_T_CAC + DR_T_PACKET) /* RAR, row hit, current bank */ +#define DR_T_READ_WRITE_SROW (DR_T_CAC + DR_T_PACKET) /* RAW, row hit, current bank */ +#define DR_T_WRITE_READ_SROW (DR_T_CWD + DR_T_PACKET) /* WAR, row hit, current bank */ +#define DR_T_WRITE_WRITE_SROW (DR_T_CWD + DR_T_PACKET) /* WAW, row hit, current bank */ +#define DR_T_READ_READ_SBANK (DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET) /* RAR, row miss, current bank */ +#define DR_T_READ_WRITE_SBANK (DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET) /* RAW, row miss, current bank */ +#define DR_T_WRITE_READ_SBANK (DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET) /* WAR, row miss, current bank */ +#define DR_T_WRITE_WRITE_SBANK (DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET) /* WAR, row miss, current bank */ +#define DR_T_READ_READ_OBANK (DR_T_PP+DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET) /* RAR, row miss, another bank */ +#define DR_T_READ_WRITE_OBANK (DR_T_PP+DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET) /* RAW, row miss, another bank */ +#define DR_T_WRITE_READ_OBANK (DR_T_PP+DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET) /* WAR, row miss, another bank */ +#define DR_T_WRITE_WRITE_OBANK (DR_T_PP+DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET) /* WAR, row miss, another bank */ +/* best-case latencies (due to overlap / row hits in another bank) */ +#define DR_BEST_T_READ_READ_SROW 0 /* RAR, row hit, current bank */ +#define DR_BEST_T_READ_WRITE_SROW (DR_T_CAC+DR_T_PACKET-DR_T_OWR) /* RAW, row hit, current bank */ +#define DR_BEST_T_WRITE_READ_SROW 0 /* WAR, row hit, current bank */ +#define DR_BEST_T_WRITE_WRITE_SROW (DR_T_CWD+DR_T_PACKET-DR_T_OWR) /* WAR, row hit, current bank */ +#define DR_BEST_T_READ_READ_SBANK (DR_T_RCD + DR_T_CAC) /* RAR, row miss, current bank */ +#define DR_BEST_T_READ_WRITE_SBANK (DR_T_RP-DR_T_OWR+DR_T_RCD+DR_T_CAC+DR_T_PACKET) /* RAW, row miss, current bank */ +#define DR_BEST_T_WRITE_READ_SBANK (DR_T_RCD+DR_T_CWD) /* WAR, row miss, current bank */ +#define DR_BEST_T_WRITE_WRITE_SBANK (DR_T_RP-DR_T_OWR+DR_T_RCD+DR_T_CWD+DR_T_PACKET) /* WAW, row miss, current bank */ +#define DR_BEST_T_READ_READ_OBANK 0 /* RAR, row miss/hit, another bank */ +#define DR_BEST_T_READ_WRITE_OBANK (DR_T_PACKET+DR_T_CAC-DR_T_OWR) /* RAW, row miss/hit, another bank */ +#define DR_BEST_T_WRITE_READ_OBANK 0 /* WAR, row miss/hit, another bank */ +#define DR_BEST_T_WRITE_WRITE_OBANK 0 /* WAW, row miss/hit, another bank */ +#define DR_BEST_T_READ_WRITE_ODEV (DR_T_CAC-DR_T_CWD) /* RAW, row miss/hit, another device */ + + +#define MIN(a,b) ((a<b) ? a : b) +#define SD_ROW_SIZE 0x1000 /* size of a row : 4 Kbyte */ + + + +DRAMMemory::DRAMMemory(Params *p) + : PhysicalMemory(p), cpu_ratio(p->cpu_ratio), bus_width(p->bus_width), + mem_type(p->mem_type), mem_actpolicy(p->mem_actpolicy), + memctrladdr_type(p->memctrladdr_type), act_lat(p->act_lat), + cas_lat(p->cas_lat), war_lat(p->war_lat), + pre_lat(p->pre_lat), dpl_lat(p->dpl_lat), + trc_lat(p->trc_lat), num_banks(p->num_banks), + num_cpus(p->num_cpus), last_dev(DR_NUM_DEVS+1), + lastCmdIsRead(true), precharge(0), same_row_read_access(0), srr_after_read(0), + srr_after_write(0), same_row_write_access(0), srw_after_read(0), + srw_after_write(0), same_bank_read_access(0), sbr_after_read(0), + sbr_after_write(0), same_bank_write_access(0), sbw_after_read(0), + sbw_after_write(0), other_bank_read_access_hit(0), obr_after_read_hit(0), + obr_after_write_hit(0), other_bank_write_access_hit(0), + obw_after_read_hit(0), obw_after_write_hit(0), obr_after_read_miss(0), + obr_after_write_miss(0), + obw_after_read_miss(0), obw_after_write_miss(0), total_access(0), + adjacent_access(0), adjacent_read(0), adjacent_write(0), + command_overlapping(0), best_case(0), in_between_case(0), worst_case(0), + full_overlapping(0), partial_overlapping(0), mem_access_details(false), + memctrlpipe_enable(false), time_last_access(0) +{ + warn("This DRAM module has not been tested with the new memory system at all!"); + bank_size = (params()->addrRange.size() + 1) / num_banks; + num_rows = bank_size / SD_ROW_SIZE; /* 0x1000 size of row 4Kbtye */ + active_row = new int[num_banks]; + last_bank = num_banks+1; + last_row = num_rows; + busy_until = new Tick[num_banks]; + memset(busy_until,0,sizeof(Tick)*num_banks); /* initiliaze */ + +} + +void +DRAMMemory::regStats() +{ + using namespace Stats; + + accesses + .init(maxThreadsPerCPU) + .name(name() + ".accesses") + .desc("total number of accesses") + .flags(total) + ; + + bytesRequested + .init(maxThreadsPerCPU) + .name(name() + ".bytes_requested") + .desc("total number of bytes requested") + .flags(total) + ; + + bytesSent + .init(maxThreadsPerCPU) + .name(name() + ".bytes_sent") + .desc("total number of bytes sent") + .flags(total) + ; + + compressedAccesses + .init(maxThreadsPerCPU) + .name(name() + ".compressed_responses") + .desc("total number of accesses that are compressed") + .flags(total) + ; + + // stats for power modelling + cycles_nCKE + .init(1) + .name(name() + ".cycles_nCKE") + .desc("cycles when CKE is low") + .flags(total) + ; + + cycles_all_precharge_CKE + .init(1) + .name(name() + ".cycles_all_precharge_CKE") + .desc("cycles when all banks precharged") + .flags(total) + ; + + cycles_all_precharge_nCKE + .init(1) + .name(name() + ".cycles_all_precharge_nCKE") + .desc("cycles when all banks precharged and CKE is low") + .flags(total) + ; + + cycles_bank_active_nCKE + .init(1) + .name(name() + ".cycles_bank_active_nCKE") + .desc("cycles when banks active and CKE low") + .flags(total) + ; + + // we derive this from other stats later + // so DR TODO for now this counter is unused + cycles_avg_ACT + .init(1) + .name(name() + ".cycles_avg_ACT") + .desc("avg cycles between ACT commands") + .flags(total) + ; + + cycles_read_out + .init(1) + .name(name() + ".cycles_read_out") + .desc("cycles outputting read data") + .flags(total) + ; + + cycles_write_in + .init(1) + .name(name() + ".cycles_write_in") + .desc("cycles inputting write data") + .flags(total) + ; + + cycles_between_misses + .init(1) + .name(name() + ".cycles_between_misses") + .desc("cycles between open page misses") + .flags(total) + ; + + other_bank_read_access_miss + .init(1) + .name(name() + ".other_bank_read_access_miss") + .desc("read miss count") + .flags(total) + ; + + other_bank_write_access_miss + .init(1) + .name(name() + ".other_bank_write_access_miss") + .desc("write miss count") + .flags(total) + ; + + // DR TODO for now, only output stats which are involved in power equations + total_latency + .name(name() + ".total_latency") + .desc("total DRAM latency") + ; + + total_arb_latency + .name(name() + ".total_arb_latency") + .desc("total arbitration latency") + ; + + avg_latency + .name(name() + ".avg_latency") + .desc("average DRAM latency") + ; + + avg_arb_latency + .name(name() + ".avg_arb_latency") + .desc("average arbitration DRAM latency") + ; + + bank_access_profile + .init(num_banks,num_cpus) + .name(name() + "[cpu][bank]") + .desc("DRAM bank access profile") + ; + + total_icache_req + .name(name() + ".total_icache_req") + .desc("total number of requests from icache") + ; + + avg_latency = total_latency / accesses; + avg_arb_latency = total_arb_latency / accesses; +} + + + + +// DR DEBUG: assume we have a 500 MHz CPU and 100 MHz RAM +// static float cpu_ratio = 5; // ratio between CPU speed and memory bus speed +// DR TODO: get this parameter from the simulation + +static char *mem_access_output=NULL; + /* latency of access [CPU cycles]*/ +Tick +DRAMMemory::calculateLatency(Packet *pkt) +{ + + bool cmdIsRead = pkt->isRead(); + + int lat=0, temp=0, current_bank=0; + int current_row=0, current_device=0; + + int was_miss = 0; // determines if there was an active row miss this access + + //md_addr_t physic_address; /* linear memory address to be accessed */ + Addr physic_address; /* linear memory address to be accessed */ + + int num_blocks=0; + int corrected_overlap, /* overlap of consecutive accesses [CPU cycles] */ + overlap=0; /* overlap of consecutive accesses [mem bus cycles] */ + int adjacent=0; /* 1 indicates that current bank is adjacent to the last accessed one*/ + + int chunks = (pkt->getSize() + (bus_width - 1)) / bus_width; // burst length + assert(chunks >0); + physic_address = pkt->getAddr(); + + /////////////////////////////////////////////////////////////////////////// + // DR added more stats for power modelling + // NOTE: + // for DRAM closed-page, automatic precharge after read or write, + // i.e. whenever idle + + + // count number of cycles where dram is not busy, use for CKE low signal + // calculate as percentage of all clock cycles + // if busy, do not add to idle count. Else add cycles since last access +/* #define SD_NUM_BANKS (SD_STACK_BASE/SD_BANK_SIZE) */ /* number of banks */ +/* #define SD_NUM_ROWS (SD_BANK_SIZE/SD_ROW_SIZE) */ /* number of rows per bank */ +/*delays until data is ready/written to the memory for the SDRAM*/ + int SD_T_READ_READ_SROW = cas_lat; /* RAR, row hit, current bank */ + int SD_T_READ_WRITE_SROW = cas_lat; /* RAW, row hit, current bank */ + int SD_T_WRITE_READ_SROW = war_lat-1; /* WAR, row hit, current bank */ + int SD_T_WRITE_WRITE_SROW = 0; /* WAW, row hit, current bank */ + int SD_T_READ_READ_SBANK = (pre_lat+act_lat+cas_lat); /* RAR, row miss, current bank */ + int SD_T_READ_WRITE_SBANK = (pre_lat+act_lat+cas_lat+(dpl_lat-1)); /* RAW, row miss, current bank */ + int SD_T_WRITE_READ_SBANK = (pre_lat+act_lat); /* WAR, row miss, current bank */ + int SD_T_WRITE_WRITE_SBANK = (pre_lat+act_lat+(dpl_lat-1)); /* WAW, row miss, current bank */ + int SD_T_READ_READ_OBANK = (pre_lat+act_lat+cas_lat); /* RAR, row miss, another bank */ + int SD_T_READ_WRITE_OBANK = (pre_lat+act_lat+cas_lat); /* RAW, row miss, another bank */ + int SD_T_WRITE_READ_OBANK = (pre_lat+act_lat); /* WAR, row miss, another bank */ + int SD_T_WRITE_WRITE_OBANK = (pre_lat+act_lat); /* WAW, row miss, another bank */ +/* best-case latencies (due to overlap / row hits in another bank) */ + int SD_BEST_T_READ_READ_SROW = 0; /* RAR, row hit, current bank */ + int SD_BEST_T_READ_READ_SBANK = (act_lat+cas_lat); /* RAR, row miss, current bank */ + int SD_BEST_T_WRITE_READ_SBANK = (act_lat); /* WAR, row miss, current bank */ + int SD_BEST_T_READ_READ_OBANK = 0; /* RAR, row miss/hit, another bank */ + int SD_BEST_T_READ_WRITE_OBANK = cas_lat; /* RAW, row miss/hit, another bank */ + int SD_BEST_T_WRITE_READ_OBANK = (war_lat -1); /* WAR, row miss/hit, another bank */ + int SD_BEST_T_WRITE_WRITE_OBANK = 0; /* WAW, row miss/hit, another bank */ + + Tick time_since_last_access = curTick-time_last_access; + Tick time_last_miss = 0; // used for keeping track of times between activations (page misses) + //int was_idle = (curTick > busy_until); + bool srow_flag = false; + int timing_correction = 0; + + int was_idle = (curTick > busy_until[current_bank]); + cycles_nCKE[0] += was_idle ? MIN(curTick-busy_until[current_bank], time_since_last_access) : 0; + + // bank is precharged + //active_row[current_bank] == DR_NUM_ROWS + int all_precharged = 1; + int bank_max = num_banks; + int row_max = num_rows; + + if( (mem_type == "SDRAM") && (mem_actpolicy == "closed") ) { + // SDRAM does not use the active_row array in closed_page mode + // TODO: handle closed page operation + + } else { // DRDRAM uses the active_row array + for( int i = 0; i < bank_max; i++ ) { + if( (active_row[current_bank] != row_max)) all_precharged = 0; + } + } + + if(all_precharged) { + if(was_idle) { + cycles_all_precharge_nCKE[0] += MIN(curTick-busy_until[current_bank], time_since_last_access); + cycles_all_precharge_CKE[0] += MIN(0, busy_until[current_bank]-time_last_access); + } + else { + cycles_all_precharge_CKE[0] += time_since_last_access; + } + } else { // some bank is active + if(was_idle) { + cycles_bank_active_nCKE[0] += MIN(curTick-busy_until[current_bank], time_since_last_access); + } + else { + } + } + + if( cmdIsRead ) { + cycles_read_out[0] += chunks; + } else { + cycles_write_in[0] += chunks; + } + + + time_last_access = curTick; + //////////////////////////////////////////////////////////////////////////// + + if ((mem_type == "SDRAM") && (mem_actpolicy == "open")) + { + /* Split transaction on m5 makes it challenging to */ + /* model the DRAM. A single cycle latency is assumed */ + /* for dequeueing an address bus request. In response to */ + /* that, the current DRAM implementation assumes that a */ + /* seperate DRAM command generator / controller exists per */ + /* bank and the dequeued addresses are queued to these */ + /* controllers. We can view this as an ideal scenario for */ + /* a shared DRAM command generator / controller with */ + /* support for overlapping DRAM commands. */ + /* Compare DRAM PRE,ACT,CAS etc. latencies, DRAM clock */ + /* frequency and the number of banks to determine whether */ + /* the ideal scenario with a shared DRAM command generator */ + /* is equivalent to having multiple DRAM command generators */ + /* per bank */ + if ((memctrladdr_type != "interleaved"))/* i.e. mc_type is linear */ + { + current_bank=physic_address/bank_size; + temp=physic_address-current_bank*bank_size;/*address in bank*/ + current_row=temp/SD_ROW_SIZE; + } + else/* mc_type interleaved */ + /* This memory controller maps the addresses differently + * depending on the row_size, every row is mapped to another + * bank. Thus, the text segment uses half of every bank, the heap + * the next quarter of each bank, and the stack the rest. + */ + + { + num_blocks = physic_address/SD_ROW_SIZE; /* row number */ + current_bank=num_blocks%num_banks; + current_row=num_blocks/num_banks; + } + + if (mem_access_details == true) + { + // DR TODO + //fprintf(mem_accessfd," %09u %4d %3d\n",physic_address,current_row,current_bank); + } + else + { + if (mem_access_output!=0) + { + //fprintf(mem_accessfd,"\n"); + } + } + total_access++; + + if (memctrlpipe_enable == true) + { + overlap=(int)(busy_until[current_bank] - curTick); + } + else overlap = 0; + + if (cpu_ratio < 1.0) + { + corrected_overlap = overlap*((int)(1/cpu_ratio)); /* floor */ + } + else + { + corrected_overlap = (int) (overlap/cpu_ratio); + } + + /*fprintf(stderr,"%10.0f %10.0f %4d %4d ",(double)busy_until, (double)curTick, overlap, corrected_overlap); debugging*/ + + if (cmdIsRead == lastCmdIsRead)/*same command*/ + { + if (current_bank == last_bank)/*same bank*/ + { + if (current_row == last_row)/*same row*/ + { + /* Page Hit */ + if (cmdIsRead) + { + if (corrected_overlap > 0)/*overlapping*/ + { + /*best case*/ + if (corrected_overlap >= cas_lat) + { + lat=SD_BEST_T_READ_READ_SROW; + srow_flag = true; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = cas_lat-corrected_overlap; + srow_flag = true; + in_between_case++; + partial_overlapping++; + } + } + else + { + /*worst case*/ + lat = SD_T_READ_READ_SROW; + srow_flag = true; + worst_case++; + } + same_row_read_access++; + srr_after_read++; + } + else/*write*/ + {/*no option case*/ + lat = SD_T_WRITE_WRITE_SROW; + srow_flag = true; + same_row_write_access++; + srw_after_write++; + worst_case++; + } + } + else /*other row in same bank*/ + { + /* Page miss */ + if (cmdIsRead) + { + if (corrected_overlap > 0)/*overlapping*/ + { + if (corrected_overlap >= pre_lat)/*best case*/ + { + lat = SD_BEST_T_READ_READ_SBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = SD_T_READ_READ_SBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = SD_T_READ_READ_SBANK; + worst_case++; + } + same_bank_read_access++; + sbr_after_read++; + } + else/*write*/ + {/*no option case*/ + lat = SD_T_WRITE_WRITE_SBANK; + same_bank_write_access++; + sbw_after_write++; + worst_case++; + } + } + } + else /*other bank*/ + { + if (cmdIsRead) + { + /* Page empty */ + if (current_row == active_row[current_bank])/*row is still active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= pre_lat)/*best case*/ + { + lat = SD_BEST_T_READ_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = SD_T_READ_READ_OBANK - corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*in between case*/ + { + lat = SD_T_READ_READ_OBANK; + in_between_case++; + } + other_bank_read_access_hit++; + obr_after_read_hit++; + } + else/*row is not active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= SD_T_READ_READ_OBANK )/*best case*/ + { + lat = SD_BEST_T_READ_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = SD_T_READ_READ_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = SD_T_READ_READ_OBANK; + worst_case++; + } + + // DR keep track of time between misses + was_miss = 1; + + other_bank_read_access_miss[0]++; + obr_after_read_miss++; + } + } + else/*write*/ + { + if (current_row == active_row[current_bank])/*row is still active*/ + { /*best_case*/ + lat = SD_BEST_T_WRITE_WRITE_OBANK; + best_case++; + other_bank_write_access_hit++; + obw_after_write_hit++; + } + else/*row is not active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >=SD_T_WRITE_WRITE_OBANK)/*best case*/ + { + lat = SD_BEST_T_WRITE_WRITE_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = SD_T_WRITE_WRITE_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = SD_T_WRITE_WRITE_OBANK; + worst_case++; + } + + // DR keep track of time between misses + was_miss = 1; + + other_bank_write_access_miss[0]++; + obw_after_write_miss++; + + } + } + } + } + else /*lastCmdIsRead != cmdIsRead*/ + { + if (current_bank == last_bank)/*same bank*/ + { + if (current_row == last_row)/*same row*/ + { + /* Page Hit */ + if (cmdIsRead) + {/*worst case*/ + lat = SD_T_READ_WRITE_SROW; + srow_flag = true; + same_row_read_access++; + srr_after_write++; + worst_case++; + } + else/*write*/ + {/*worst case*/ + lat = SD_T_WRITE_READ_SROW; + srow_flag = true; + same_row_write_access++; + srw_after_read++; + worst_case++; + } + } + else /*other row in same bank*/ + { + /* Page Miss */ + if (cmdIsRead) + {/*worst case*/ + lat = SD_T_READ_WRITE_SBANK; + same_bank_read_access++; + sbr_after_write++; + worst_case++; + } + else/*write*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if (corrected_overlap >= pre_lat)/*best case*/ + { + lat = SD_BEST_T_WRITE_READ_SBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = SD_T_WRITE_READ_SBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = SD_T_WRITE_READ_OBANK; + worst_case++; + } + same_bank_write_access++; + sbw_after_read++; + } + } + } + else /*other bank*/ + { + /* Page empty */ + if (cmdIsRead) + { + if (current_row == active_row[current_bank])/*row is still active*/ + { /*best case*/ + lat = SD_BEST_T_READ_WRITE_OBANK; + best_case++; + other_bank_read_access_hit++; + obr_after_write_hit++; + } + else/*row is not active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= (pre_lat+act_lat))/*best case*/ + { + lat = SD_BEST_T_READ_WRITE_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = SD_T_READ_WRITE_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = SD_T_READ_WRITE_OBANK; + worst_case++; + } + // DR keep track of time between misses + was_miss = 1; + + other_bank_read_access_miss[0]++; + obr_after_write_miss++; + } + } + else/*write*/ + { + if (current_row == active_row[current_bank])/*row is still active*/ + { /*best case*/ + lat = SD_BEST_T_WRITE_READ_OBANK; + best_case++; + other_bank_write_access_hit++; + obw_after_read_hit++; + } + else/*row is not active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if (corrected_overlap >= (SD_T_WRITE_READ_OBANK-SD_BEST_T_WRITE_READ_OBANK))/*best case*/ + {/*best case*/ + lat = SD_BEST_T_WRITE_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = SD_T_WRITE_READ_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = SD_T_WRITE_READ_OBANK; + worst_case++; + } + + // DR keep track of time between misses + was_miss = 1; + + other_bank_write_access_miss[0]++; + obw_after_read_miss++; + } + } + } + } + /*fprintf(stderr,"%4d %4d ",lat,active_row[current_bank]);debugging*/ + + lat += chunks; /* burst length added*/ + if(srow_flag == false) + timing_correction = cpu_ratio*(trc_lat - pre_lat - act_lat - cas_lat - 1); + + + /*fprintf(stderr,"%4d ",lat);debugging*/ + + active_row[current_bank]=current_row; /* open-page (hit) register */ + lastCmdIsRead = cmdIsRead; + last_bank = current_bank; + last_row = current_row; + + if (cpu_ratio < 1.0) + { + lat = (lat+((int)(1/cpu_ratio)-1))/(int)(1/cpu_ratio); + } + else + { + temp = (int)(lat*cpu_ratio); + lat = (lat*cpu_ratio == temp)?temp:(temp+1); /*round up*/ + } + + /*fprintf(stderr,"%4d \n",lat);debugging*/ + + if (overlap <= 0) /*memory interface is not busy*/ + { + if (memctrlpipe_enable == true) + { + busy_until[current_bank]=curTick+lat+ + timing_correction; + } + else + { + if (busy_until[current_bank] >= curTick) + { + busy_until[current_bank]+=(lat+ + timing_correction); + total_arb_latency += (busy_until[current_bank] + - curTick - lat + - timing_correction); + lat=busy_until[current_bank] - curTick; + } + else busy_until[current_bank]=curTick+lat+ + timing_correction; + } + } + else/*the memory request will be satisfied temp cycles after curTick*/ + { + busy_until[current_bank] +=(lat+ + timing_correction); + command_overlapping++; + lat+=overlap; + total_arb_latency += overlap; + } + + // DR for power stats + if( was_miss ) { + cycles_between_misses[0] += (busy_until[current_bank] - time_last_miss); + time_last_miss = busy_until[current_bank]; + } + // cout <<"cpu id = " << _cpu_num << "current_bank = " << current_bank << endl; + // if((_cpu_num < num_cpus) && (_cpu_num >= 0)) + // bank_access_profile[_cpu_num][current_bank]++; + + return lat; + } + + + /***********************************************************/ + /******************** DRDRAM ***********************/ + /***********************************************************/ + + else if ((mem_type == "DRDRAM") && (mem_actpolicy == "open"))/*DRDRAM*/ /*a closed bank has an activ_row number of DR_NUM_ROWS: highest +1*/ + { + if ((memctrladdr_type != "interleaved"))/* i.e. mc_type is linear */ + { + current_bank=physic_address/DR_BANK_SIZE; + temp=physic_address-current_bank*DR_BANK_SIZE;/*address in bank*/ + current_row=temp/DR_ROW_SIZE; + current_device=current_bank/(DR_NUM_BANKS/DR_NUM_DEVS); + } + + else/*mc_type interleaved*/ + /* This memory controller maps the addresses differently + * depending on the row-size, every row is mapped to another + * bank. So the text segment uses half of every bank. The heap + * the next quarter of each bank and the stack the rest. + */ + + { + num_blocks = physic_address/DR_ROW_SIZE; /* row number */ + current_bank=(num_blocks%DR_NUM_BANKS)*2; /*every 'second' bank will be used*/ + /*banks above DR_NUM_BANKS are the uneven banks*/ + current_bank = ((current_bank < DR_NUM_BANKS) ? current_bank:(current_bank - DR_NUM_BANKS+1)); + current_row=num_blocks/DR_NUM_BANKS; + current_device=current_bank/(DR_NUM_BANKS/DR_NUM_DEVS); + } + if (abs(current_bank-last_bank)==1)/*access to an adjacent bank*/ + { + if (!((current_bank%DR_BANK_SAMP == (DR_BANK_SAMP-1))&&(last_bank%DR_BANK_SAMP == 0))/*not 15/16 (current/last)*/ + &&(!((last_bank%DR_BANK_SAMP == (DR_BANK_SAMP-1))&&(current_bank%DR_BANK_SAMP == 0))))/*not 16/15(current/last)*/ + { + adjacent_access++; + adjacent=1;/*an adjacent bank is accessed*/ + if (cmdIsRead) + adjacent_read++; + else + adjacent_write++; + } + } + precharge=0;/*at this moment no bank needs to be precharged*/ + if (active_row[current_bank] == DR_NUM_ROWS)/*bank is precharged*/ + { + if (prechargeBanksAround(current_bank)> 0)/*a bank next to the current is activated*/ + { + if ((adjacent==1)&&(precharge==1)) + { + /*since adjacent banks share SAMPs, this access would be the same as (in terms of latency) + *an access to another row in the same bank if only one adjacent bank was active*/ + last_bank = current_bank; + last_row = current_row+1; + precharge=0;/*set to 0 for next memory access*/ + } + } + } + if (mem_access_details == true) + { + //fprintf(mem_accessfd," %09u %4d %3d %15d\n",physic_address,current_row,current_bank,(int)adjacent_access); + } + else + { + if (mem_access_output!=NULL) + { + //fprintf(mem_accessfd,"\n"); + } + } + total_access++; + + if (memctrlpipe_enable == true) + { + overlap=(int)(busy_until[current_bank] - curTick); + } + else overlap=0; + + if (cpu_ratio < 1.0) + { + corrected_overlap = overlap*((int)(1/cpu_ratio)); /* floor */ + } + else + { + corrected_overlap = (int) (overlap/cpu_ratio); + } + + /*fprintf(stderr,"%10.0f %10.0f %6d %6d %2d %2d ",(double)busy_until, (double)curTick, overlap, corrected_overlap,precharge,adjacent);debugging*/ + + if (cmdIsRead == lastCmdIsRead)/*same command*/ + { + if (current_bank == last_bank)/*same bank*/ + { + if (current_row == last_row)/*same row*/ + { + if (cmdIsRead) + { + if (corrected_overlap > 0)/*overlapping*/ + { + /*best case*/ + if (corrected_overlap >= DR_T_READ_READ_SROW) + { + lat=DR_BEST_T_READ_READ_SROW; + srow_flag = true; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_READ_READ_SROW-corrected_overlap; + srow_flag = true; + in_between_case++; + partial_overlapping++; + } + } + else + { + /*worst case*/ + lat = DR_T_READ_READ_SROW; + srow_flag = true; + worst_case++; + } + same_row_read_access++; + srr_after_read++; + } + else/*write, always retire the previous data*/ + { + if (corrected_overlap > 0)/*overlapping*/ + { + /*best case*/ + if (corrected_overlap >= DR_T_OWR) + { + lat=DR_BEST_T_WRITE_WRITE_SROW; + srow_flag = true; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_WRITE_WRITE_SROW-corrected_overlap; + srow_flag = true; + in_between_case++; + partial_overlapping++; + } + } + else + { + /*worst case*/ + lat = DR_T_WRITE_WRITE_SROW; + srow_flag = true; + worst_case++; + } + same_row_write_access++; + srw_after_write++; + } + } + else /*other row in same bank*/ + { + if (cmdIsRead) + { + if (corrected_overlap > 0)/*overlapping*/ + { + if (corrected_overlap >= DR_T_HELP)/*best case*/ + { + lat = DR_BEST_T_READ_READ_SBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_READ_READ_SBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_READ_READ_SBANK; + worst_case++; + } + same_bank_read_access++; + sbr_after_read++; + } + else/*write*/ + { + if (corrected_overlap > 0)/*overlapping*/ + { + if (corrected_overlap >= DR_T_OWR)/*best case*/ + { + lat = DR_BEST_T_WRITE_WRITE_SBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_WRITE_WRITE_SBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_WRITE_WRITE_SBANK; + worst_case++; + } + same_bank_write_access++; + sbw_after_write++; + } + } + } + else /*other bank*/ + { + if (cmdIsRead) + { + if (current_row == active_row[current_bank])/*row is still active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= (DR_T_CAC+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_READ_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_CAC+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*in between case*/ + { + lat = DR_T_CAC+DR_T_PACKET; + in_between_case++; + } + other_bank_read_access_hit++; + obr_after_read_hit++; + } + else/*row is not active or bank is precharged/not active*/ + { + if (active_row[current_bank]!=DR_NUM_ROWS)/*row is not active, but bank is active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= (DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_READ_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET; + in_between_case++; + } + } + else/*current_row == DR_NUM_ROWS:precharged or inactive*/ + { + if(precharge == 0)/*no adjacent bank is active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= (DR_T_RCD+DR_T_CAC+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_READ_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_RCD+DR_T_CAC+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_RCD+DR_T_CAC+DR_T_PACKET; + in_between_case++; + } + } + else/*one ore two adjacent banks are active*/ + { + if (precharge == 1) + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= (DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_READ_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = (DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET)-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = (DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET); + in_between_case++; + } + } + else /*precharge ==2: two rows must be precharged*/ + { + if (adjacent == 1)/*these banks are adjacent*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= DR_T_PP+2*DR_T_PACKET-DR_T_RDP+DR_T_CAC)/*best case*/ + { + lat = DR_T_RDP+DR_T_RP+DR_T_RCD-DR_T_PACKET; + in_between_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_READ_READ_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_READ_READ_OBANK; + worst_case++; + } + } + else/*adjacent == 0: two not adjacent banks need to be precharged*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= DR_T_READ_READ_OBANK)/*best case*/ + { + lat = DR_BEST_T_READ_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_READ_READ_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_READ_READ_OBANK; + worst_case++; + } + } + } + } + } + other_bank_read_access_miss[0]++; + obr_after_read_miss++; + } + } + else/*write*/ + { + if (current_row == active_row[current_bank])/*row is still active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= (DR_T_CWD+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_WRITE_WRITE_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_CWD+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_CWD+DR_T_PACKET; + in_between_case++; + } + other_bank_write_access_hit++; + obw_after_write_hit++; + } + else/*row is not active or bank is precharged/not active*/ + { + if (active_row[current_bank] != DR_NUM_ROWS)/*row is not active,but bank is active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= (DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_WRITE_WRITE_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET; + in_between_case++; + } + } + else/*current_row == DR_NUM_ROWS:precharged or inactive*/ + { + if(precharge == 0)/*no adjacent bank is active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= (DR_T_RCD+DR_T_CWD+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_WRITE_WRITE_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_RCD+DR_T_CWD+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_RCD+DR_T_CWD+DR_T_PACKET; + in_between_case++; + } + } + else/*one ore two adjacent banks are active*/ + { + if (precharge == 1)/*last_bank is no adjacent*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= (DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_WRITE_WRITE_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = (DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET)-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = (DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET); + in_between_case++; + } + } + else /*precharge ==2: two rows have to be precharged*/ + { + if (adjacent == 1)/*these banks are adjacent*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= DR_T_OWR+DR_T_PP)/*best case*/ + { + lat = DR_T_WRITE_WRITE_OBANK-DR_T_OWR-DR_T_PP; + in_between_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_WRITE_WRITE_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_WRITE_WRITE_OBANK; + worst_case++; + } + } + else/*adjacent == 0: two not adjacent banks need to be precharged*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= DR_T_WRITE_WRITE_OBANK)/*best case*/ + { + lat = DR_BEST_T_WRITE_WRITE_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_WRITE_WRITE_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_WRITE_WRITE_OBANK; + worst_case++; + } + } + } + } + } + other_bank_write_access_miss[0]++; + obw_after_write_miss++; + } + } + } + } + else /*lastCmdIsRead != cmdIsRead*/ + { + if (current_bank == last_bank)/*same bank*/ + { + if (current_row == last_row)/*same row*/ + { + if (cmdIsRead) + { + if (corrected_overlap > 0)/*overlapping*/ + { + /*best case*/ + if (corrected_overlap >= DR_T_OWR) + { + lat=DR_BEST_T_READ_WRITE_SROW; + srow_flag = true; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_READ_WRITE_SROW-corrected_overlap; + srow_flag = true; + in_between_case++; + partial_overlapping++; + } + } + else + { + /*worst case*/ + lat = DR_T_READ_WRITE_SROW; + srow_flag = true; + worst_case++; + } + same_row_read_access++; + srr_after_write++; + } + else/*write*/ + { + if (corrected_overlap > 0)/*overlapping*/ + { + /*best case*/ + if (corrected_overlap >= DR_T_WRITE_READ_SROW) + { + lat=DR_BEST_T_WRITE_READ_SROW; + srow_flag = true; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_WRITE_READ_SROW-corrected_overlap; + srow_flag = true; + in_between_case++; + partial_overlapping++; + } + } + else + { + /*worst case*/ + lat = DR_T_WRITE_READ_SROW; + srow_flag = true; + worst_case++; + } + same_row_write_access++; + srw_after_read++; + } + } + else /*other row in same bank*/ + { + if (cmdIsRead) + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if (corrected_overlap >= DR_T_OWR)/*best case*/ + { + lat = DR_BEST_T_READ_WRITE_SBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_READ_WRITE_SBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_READ_WRITE_SBANK; + worst_case++; + } + same_bank_read_access++; + sbr_after_write++; + } + else/*write*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if (corrected_overlap >= DR_T_HELP)/*best case*/ + { + lat = DR_BEST_T_WRITE_READ_SBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_WRITE_READ_SBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_WRITE_READ_SBANK; + worst_case++; + } + same_bank_write_access++; + sbw_after_read++; + } + } + } + else /*other bank*/ + { + if (cmdIsRead) + { + if (current_row == active_row[current_bank])/*row is still active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(last_dev != current_device) + { + if(corrected_overlap >= DR_T_CWD+DR_T_PACKET)/*best case*/ + { + lat = DR_BEST_T_READ_WRITE_ODEV; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_CAC+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else /* same device */ + { + if(corrected_overlap >= DR_T_OWR)/*best case*/ + { + lat = DR_BEST_T_READ_WRITE_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_CAC+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + } + else/*in between case, no overlap*/ + { + lat = DR_T_CAC+DR_T_PACKET; + in_between_case++; + } + other_bank_read_access_hit++; + obr_after_write_hit++; + } + + else/*row is not active or bank is precharged/not active*/ + { + if (active_row[current_bank] != DR_NUM_ROWS)/*row is not active,but bank is active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if (last_dev != current_device) + { + if(corrected_overlap >= (DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_READ_WRITE_ODEV; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else /* same device */ + { + if(corrected_overlap >= (DR_T_RP+DR_T_RCD+DR_T_OWR))/*best case*/ + { + lat = DR_BEST_T_READ_WRITE_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + } + else/*worst case*/ + { + lat = DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET; + in_between_case++; + } + } + else/*current_row == DR_NUM_ROWS:precharged or inactive*/ + { + if(precharge == 0)/*no adjacent bank is active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(last_dev != current_device) + { + if(corrected_overlap >= (DR_T_RCD+DR_T_CWD+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_READ_WRITE_ODEV; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_RCD+DR_T_CAC+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else /* same device */ + { + if(corrected_overlap >= (DR_T_RCD+DR_T_OWR))/*best case*/ + { + lat = DR_BEST_T_READ_WRITE_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_RCD+DR_T_CAC+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + } + else/*worst case*/ + { + lat = DR_T_RCD+DR_T_CAC+DR_T_PACKET; + in_between_case++; + } + } + else/*one or two adjacent banks are active*/ + { + if (precharge == 1)/*an adjacent bank (!=last_bank) needs to be precharged*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(last_dev != current_device) + { + if(corrected_overlap >= (DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_READ_WRITE_ODEV; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = (DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET)-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else /* same device */ + { + if(corrected_overlap >= (DR_T_RP+DR_T_RCD+DR_T_OWR))/*best case*/ + { + lat = DR_BEST_T_READ_WRITE_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = (DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET)-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + } + else/*worst case*/ + { + lat = (DR_T_RP+DR_T_RCD+DR_T_CAC+DR_T_PACKET); + in_between_case++; + } + } + else /*precharge ==2: two rows have to be precharged*/ + { + if (adjacent == 1) /* the banks are adjacent */ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= DR_T_OWR + DR_T_PP)/*best case*/ + { + lat = DR_T_READ_WRITE_OBANK-DR_T_OWR - DR_T_PP; + in_between_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_READ_WRITE_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_READ_WRITE_OBANK; + worst_case++; + } + } + else/*adjacent == 0: two not adjacent banks need to be precharged*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if (last_dev != current_device) + { + if(corrected_overlap >= (DR_T_PP+DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_READ_WRITE_ODEV; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_READ_WRITE_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else /* same device */ + { + if(corrected_overlap >= (DR_T_PP+DR_T_RP+DR_T_RCD+DR_T_OWR))/*best case*/ + { + lat = DR_BEST_T_READ_WRITE_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_READ_WRITE_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + } + else/*worst case*/ + { + lat = DR_T_READ_WRITE_OBANK; + worst_case++; + } + } + } + } + } + other_bank_read_access_miss[0]++; + obr_after_write_miss++; + } + } + else/*write*/ + { + if (current_row == active_row[current_bank])/*row is still active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= DR_T_CWD+DR_T_PACKET)/*best case*/ + { + lat = DR_BEST_T_WRITE_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_CWD+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*in between case*/ + { + lat = DR_T_CWD+DR_T_PACKET; + in_between_case++; + } + other_bank_write_access_hit++; + obw_after_read_hit++; + } + else/*row is not active or bank is precharged/not active*/ + { + if (active_row[current_bank] != DR_NUM_ROWS)/*row is not active,but bank is active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= (DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_WRITE_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET; + in_between_case++; + } + } + else/*current_row == DR_NUM_ROWS:precharged or inactive*/ + { + if(precharge == 0)/*no adjacent bank is active*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= (DR_T_RCD+DR_T_CWD+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_WRITE_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_RCD+DR_T_CWD+DR_T_PACKET-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_RCD+DR_T_CWD+DR_T_PACKET; + in_between_case++; + } + } + else/*one or two adjacent banks are active*/ + { + if (precharge == 1)/*an adjacent bank (!=last_bank) needs to be precharged first*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= (DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET))/*best case*/ + { + lat = DR_BEST_T_WRITE_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = (DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET)-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = (DR_T_RP+DR_T_RCD+DR_T_CWD+DR_T_PACKET); + in_between_case++; + } + } + else /*precharge ==2: two rows have to be precharged*/ + { + if (adjacent == 1)/*these banks are adjacent*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= DR_T_PP-DR_T_RDP+2*DR_T_PACKET+DR_T_CAC)/*best case*/ + { + lat = DR_T_WRITE_READ_OBANK-(DR_T_PP-DR_T_RDP+2*DR_T_PACKET+DR_T_CAC); + in_between_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_WRITE_READ_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_WRITE_READ_OBANK; + worst_case++; + } + } + else/*adjacent == 0: two not adjacent banks need to be precharged*/ + { + if (corrected_overlap > 0 )/*overlapping*/ + { + if(corrected_overlap >= DR_T_WRITE_READ_OBANK)/*best case*/ + { + lat = DR_BEST_T_WRITE_READ_OBANK; + best_case++; + full_overlapping++; + } + else/*in between case*/ + { + lat = DR_T_WRITE_READ_OBANK-corrected_overlap; + in_between_case++; + partial_overlapping++; + } + } + else/*worst case*/ + { + lat = DR_T_WRITE_READ_OBANK; + worst_case++; + } + } + } + } + } + other_bank_write_access_miss[0]++; + obw_after_read_miss++; + } + } + } + } + /*fprintf(stderr,"%4d %4d ",lat,active_row[current_bank]);debugging*/ + + lat += chunks * DR_T_PACKET; /*every 128 bit need DR_NUM_CYCLES*/ + + /*fprintf(stderr,"%4d ",lat);debugging*/ + + if (cpu_ratio < 1.0) + { + lat = (lat+((int)(1/cpu_ratio)-1))/(int)(1/cpu_ratio); + } + else + { + temp = (int)(lat*cpu_ratio); + lat = (lat*cpu_ratio == temp)?temp:(temp+1); /*round up*/ + } + + active_row[current_bank]=current_row; + lastCmdIsRead = cmdIsRead; + last_bank=current_bank; + last_row = current_row; + last_dev = current_device; + + /*fprintf(stderr,"%4d \n",lat);debugging*/ + + if (overlap <= 0) /*memory interface is not busy*/ + { + if (memctrlpipe_enable == true) + { + busy_until[current_bank] =curTick+lat; + } + else + { + if (busy_until[current_bank] >= curTick) + { + busy_until[current_bank] +=lat; + lat=busy_until[current_bank] - curTick; + } + else busy_until[current_bank] = curTick+lat; + } + } + else/*the memory request will be satisfied temp cycles after curTick*/ + { + busy_until[current_bank] +=lat; + command_overlapping++; + lat+=overlap; + } + + // if((_cpu_num < num_cpus) && (_cpu_num >= 0)) + // cout <<"cpu id = " << _cpu_num << "current_bank = " << current_bank << endl; + // bank_access_profile[_cpu_num][current_bank]++; + return lat; + } + + /******************************************************************/ + + else if ((mem_type== "SDRAM") && (mem_actpolicy == "closed") && (memctrlpipe_enable == true)) + /* SDRAM closed-page with overlap, 2/00 MG */ + { + if ((memctrladdr_type != "interleaved"))/* i.e. mc_type is linear*/ + { + // current_bank=physic_address/SD_BANK_SIZE; + current_bank=physic_address/bank_size; + } + else/*mc_type interleaved*/ + /* This memory management unit maps the addresses different + * depending on the row_size, every row is mapped to another + * bank. So the text segment uses half of every bank. The heap + * the next quarter of each bank and the stack the rest. + */ + { + num_blocks = physic_address/SD_ROW_SIZE; /* row number */ + // current_bank=num_blocks%SD_NUM_BANKS; + current_bank=num_blocks%num_banks; + } + + if (mem_access_details == true) + { + //fprintf(mem_accessfd," %09u %3d\n",physic_address,current_bank); + } + else + { + if (mem_access_output!=NULL) + { + //fprintf(mem_accessfd,"\n"); + } + } + total_access++; + + overlap=(int)(busy_until[current_bank] - curTick); + + if (current_bank == last_bank)/*same bank*/ + { + if ((lastCmdIsRead == cmdIsRead) && (cmdIsRead))/* RAR */ + { + lat = act_lat + cas_lat; + } + else if ((lastCmdIsRead == cmdIsRead) && (!cmdIsRead)) /* WAW */ + { + lat = act_lat; + } + else if ((lastCmdIsRead != cmdIsRead) && (cmdIsRead)) /* RAW */ + { + lat = act_lat + cas_lat; + } + else /* WAR */ + { + lat = act_lat; + } + } + else /* other bank */ + { + if (cpu_ratio < 1.0) + { + corrected_overlap = overlap*((int)(1/cpu_ratio)); /* floor */ + } + else + { + corrected_overlap = (int) (overlap/cpu_ratio); + } + + if ((lastCmdIsRead == cmdIsRead) && (cmdIsRead))/* RAR */ + { + if (corrected_overlap > act_lat + cas_lat) + { + lat = 0; + best_case++; + full_overlapping++; + } + else if (corrected_overlap > 0) + { + lat = act_lat + cas_lat - corrected_overlap; + in_between_case++; + partial_overlapping++; + } + else + { + lat = act_lat + cas_lat; + worst_case++; + } + } + else if ((lastCmdIsRead == cmdIsRead) && (!cmdIsRead)) /* WAW */ + { + if (corrected_overlap > act_lat + pre_lat + (dpl_lat-1)) + { + lat = - pre_lat - dpl_lat +1; + best_case++; + full_overlapping++; + } + else if (corrected_overlap > 0) + { + lat = act_lat - corrected_overlap; + in_between_case++; + partial_overlapping++; + } + else + { + lat = act_lat; + worst_case++; + } + } + else if ((lastCmdIsRead != cmdIsRead) && (cmdIsRead)) /* RAW */ + { + if (corrected_overlap > cas_lat + pre_lat + dpl_lat - 1 ) + { + lat = act_lat - (pre_lat + dpl_lat - 1); + best_case++; + partial_overlapping++; + } + else if (corrected_overlap > 0) + { + lat = act_lat + cas_lat - corrected_overlap; + in_between_case++; + partial_overlapping++; + } + else + { + lat = act_lat + cas_lat; + worst_case++; + } + } + else /* WAR */ + { + if (corrected_overlap > act_lat - (dpl_lat-1)) + { + lat = dpl_lat-1; + best_case++; + partial_overlapping++; + } + else if (corrected_overlap > 0) + { + lat = act_lat - corrected_overlap; + in_between_case++; + partial_overlapping++; + } + else + { + lat = act_lat; + worst_case++; + } + } + } + lastCmdIsRead = cmdIsRead; + last_bank=current_bank; + last_row = current_row; + + lat += chunks; + + if (cpu_ratio < 1.0) + { + lat = (lat+((int)(1/cpu_ratio)-1))/(int)(1/cpu_ratio); + } + else + { + temp = (int)(lat*cpu_ratio); + lat = (lat*cpu_ratio == temp)?temp:(temp+1); /*round up*/ + } + + /*fprintf(stderr,"%4d \n",lat); debugging */ + + if (overlap <= 0) /*memory interface is not busy*/ + { + busy_until[current_bank] = curTick+lat; + } + else /*the memory request will be satisfied temp cycles after curTick*/ + { + busy_until[current_bank] +=lat; + command_overlapping++; + total_arb_latency += overlap; + lat+=overlap; + } + if (!cmdIsRead) + { + temp = (int)(((dpl_lat-1) + pre_lat)*cpu_ratio); + busy_until[current_bank] += (((dpl_lat-1) + pre_lat)*cpu_ratio == temp)?temp:(temp+1); + } + + + + /*fprintf(stderr,"%10.0f %10.0f %4d %4d \n",(double)busy_until, (double)curTick, overlap, lat);debug*/ + // if((_cpu_num < num_cpus) && (_cpu_num >= 0)) + // cout <<"cpu id = " << _cpu_num << "current_bank = " << current_bank << endl; + // bank_access_profile[_cpu_num][current_bank]++; + return lat; + } + + /******************************************************************/ + + else if ((mem_type == "DRDRAM") && (mem_actpolicy == "closed") && (memctrlpipe_enable == true)) + /* DRDRAM closed-page with overlap*/ + { + + if ((memctrladdr_type != "interleaved"))/*i.e. mc_type is linear*/ + { + current_bank=physic_address/DR_BANK_SIZE; + current_device=current_bank/(DR_NUM_BANKS/DR_NUM_DEVS); + } + else/*mc_type interleaved*/ + /* This memory management unit maps the addresses different + * depending on the row-size, every row is mapped to another + * bank. So the text segment uses half of every bank. The heap + * the next quarter of each bank and the stack the rest. + */ + { + num_blocks = physic_address/DR_ROW_SIZE; /* row number */ + current_bank=(num_blocks%DR_NUM_BANKS)*2; /*every 'second' bank will be used*/ + /*banks above DR_NUM_BANKS are the uneven banks*/ + current_bank = ((current_bank < DR_NUM_BANKS) ? current_bank:(current_bank - DR_NUM_BANKS+1)); + current_device=current_bank/(DR_NUM_BANKS/DR_NUM_DEVS); + } + + + if (mem_access_details == true) + { + //fprintf(mem_accessfd," %09u %3d \n",physic_address,current_bank); + } + else + { + if (mem_access_output!=NULL) + { + //fprintf(mem_accessfd,"\n"); + } + } + total_access++; + + overlap=(int)(busy_until[current_bank] - curTick); + + if (cpu_ratio < 1.0) + { + corrected_overlap = overlap*((int)(1/cpu_ratio)); /* floor */ + } + else + { + corrected_overlap = (int) (overlap/cpu_ratio); + } + + if (current_bank == last_bank)/*same bank*/ + { + if ((lastCmdIsRead == cmdIsRead) && (cmdIsRead))/* RAR */ + { + lat = DR_T_RCD + DR_T_CAC + DR_T_PACKET; + worst_case++; + } + else if ((lastCmdIsRead == cmdIsRead) && (!cmdIsRead)) /* WAW */ + { + lat = DR_T_RCD + DR_T_CWD + DR_T_PACKET; + worst_case++; + } + else if ((lastCmdIsRead != cmdIsRead) && (cmdIsRead)) /* RAW */ + { + lat = DR_T_RCD + DR_T_CAC + DR_T_PACKET; + worst_case++; + } + else /* WAR */ + { + lat = DR_T_RCD + DR_T_CWD + DR_T_PACKET; + worst_case++; + } + } + else /* other bank */ + { + if ((lastCmdIsRead == cmdIsRead) && (cmdIsRead))/* RAR */ + { + if (corrected_overlap > DR_T_RAS + DR_T_RP - 2 * DR_T_PACKET) + { + lat = - DR_T_RAS + DR_T_RCD + DR_T_PACKET + DR_T_CAC; + best_case++; + full_overlapping++; + } + else if (corrected_overlap > 0) + { + lat = DR_T_RCD + DR_T_CAC + DR_T_PACKET - corrected_overlap; + in_between_case++; + partial_overlapping++; + } + else + { + lat = DR_T_RCD + DR_T_CAC + DR_T_PACKET; + worst_case++; + } + } + else if ((lastCmdIsRead == cmdIsRead) && (!cmdIsRead)) /* WAW */ + { + if (corrected_overlap > DR_T_RCD + DR_T_RTR + DR_T_RP) + { + lat = - DR_T_CWD - 2 * DR_T_PACKET + DR_T_RTR; + best_case++; + full_overlapping++; + } + else if (corrected_overlap > 0) + { + lat = DR_T_RCD + DR_T_CWD + DR_T_PACKET - corrected_overlap; + in_between_case++; + partial_overlapping++; + } + else + { + lat = DR_T_RCD + DR_T_CWD + DR_T_PACKET; + worst_case++; + } + } + else if ((lastCmdIsRead != cmdIsRead) && (cmdIsRead)) /* RAW */ + { + if (current_device == last_dev) /* same device */ + { + if (corrected_overlap > DR_T_RCD + DR_T_RP) + { + lat = DR_T_PACKET + DR_T_CAC - DR_T_RP; + best_case++; + partial_overlapping++; + } + else if (corrected_overlap > 0) + { + lat = DR_T_RCD + DR_T_CAC + DR_T_PACKET - corrected_overlap; + in_between_case++; + partial_overlapping++; + } + else + { + lat = DR_T_RCD + DR_T_CAC + DR_T_PACKET; + worst_case++; + } + } + else /* other device */ + { + if (corrected_overlap > DR_T_RCD + DR_T_RP + 2 * DR_T_PACKET) + { + lat = - DR_T_PACKET + DR_T_CAC - DR_T_RP; + best_case++; + partial_overlapping++; + } + else if (corrected_overlap > 0) + { + lat = DR_T_RCD + DR_T_CAC + DR_T_PACKET - corrected_overlap; + in_between_case++; + partial_overlapping++; + } + else + { + lat = DR_T_RCD + DR_T_CAC + DR_T_PACKET; + worst_case++; + } + } + } + else /* WAR */ + { + if (corrected_overlap > DR_T_RAS + DR_T_RP - 2 * DR_T_PACKET - (DR_T_CAC - DR_T_CWD)) + { + lat = - DR_T_RAS + DR_T_RCD + DR_T_PACKET + DR_T_CAC; + best_case++; + full_overlapping++; + } + else if (corrected_overlap > 0) + { + lat = DR_T_RCD + DR_T_CWD + DR_T_PACKET - corrected_overlap; + in_between_case++; + partial_overlapping++; + } + else + { + lat = DR_T_RCD + DR_T_CWD + DR_T_PACKET; + worst_case++; + } + } + } + + lat += chunks * DR_T_PACKET; /*every 128 bit need DR_NUM_CYCLES*/ + + /*fprintf(stderr,"%4d ",lat);debugging*/ + + if (cpu_ratio < 1.0) + { + lat = (lat+((int)(1/cpu_ratio)-1))/(int)(1/cpu_ratio); + } + else + { + temp = (int)(lat*cpu_ratio); + lat = (lat*cpu_ratio == temp)?temp:(temp+1); /*round up*/ + } + + lastCmdIsRead=cmdIsRead; + last_bank=current_bank; + last_dev = current_device; + + /*fprintf(stderr,"%4d \n",lat);debugging*/ + + if (overlap <= 0) /*memory interface is not busy*/ + { + busy_until[current_bank] = curTick+lat; + } + else/*the memory request will be satisfied temp cycles after curTick*/ + { + busy_until[current_bank] +=lat; + command_overlapping++; + lat+=overlap; + } + + /*fprintf(stderr,"%10.0f %10.0f %4d %4d \n",(double)busy_until, (double)curTick, overlap, lat);*/ + + if (cmdIsRead) + { + if (cpu_ratio < 1.0) + { + busy_until[current_bank] += abs(((DR_T_RAS - (DR_T_RCD + DR_T_PACKET + DR_T_CAC)) + 1)/((int)(1/cpu_ratio))); /* CPU clock cycles */ + } + else + { + busy_until[current_bank] += (int) abs(((DR_T_RAS - (DR_T_RCD + DR_T_PACKET + DR_T_CAC)) + 1)*cpu_ratio); /* CPU clock cycles */ + } + } + else /* !cmdIsRead */ + { + if (cpu_ratio < 1.0) + { + busy_until[current_bank] += abs((-DR_T_PACKET + DR_T_RTR + DR_T_RP - DR_T_CWD + 1)/((int)(1/cpu_ratio))); /* CPU clock cycles */ + } + else + { + busy_until[current_bank] += (int) abs((-DR_T_PACKET + DR_T_RTR + DR_T_RP - DR_T_CWD + 1)*cpu_ratio); /* CPU clock cycles */ + } + } + + // if((_cpu_num < num_cpus) && (_cpu_num >= 0)) + // cout <<"cpu id = " << _cpu_num << "current_bank = " << current_bank << endl; + // bank_access_profile[_cpu_num][current_bank]++; + return lat; + } + + /******************************************************************/ + + else if ((mem_type == "SDRAM") && (mem_actpolicy == "closed") && (memctrlpipe_enable == false)) + /* SDRAM closed-page without overlap, 7/99 MG */ + { + if (mem_access_output != NULL) + { + //fprintf(mem_accessfd,"\n"); + } + assert(chunks >0); + + if (cmdIsRead) + { + lat = act_lat + cas_lat; + } + else /* !cmdIsRead */ + { + lat = act_lat; + } + total_access++; + lat += chunks; + + overlap=(int)(busy_until[current_bank] - curTick); + lastCmdIsRead=cmdIsRead; + + if (cpu_ratio < 1.0) + { + lat = (lat+((int)(1/cpu_ratio)-1))/(int)(1/cpu_ratio); + } + else + { + temp = (int)(lat*cpu_ratio); + lat = (lat*cpu_ratio == temp)?temp:(temp+1); /*round up*/ + } + + if (overlap <= 0) /*memory interface is not busy*/ + { + busy_until[current_bank] = curTick+lat; + } + else/*the memory request will be satisfied temp cycles after curTick*/ + { + busy_until[current_bank] +=lat; + command_overlapping++; + lat+=overlap; + } + if (!cmdIsRead) + { + temp = (int)(((dpl_lat-1) + pre_lat)*cpu_ratio); + busy_until[current_bank] += (((dpl_lat-1) + pre_lat)*cpu_ratio == temp)?temp:(temp+1); + } + + // if((_cpu_num < num_cpus) && (_cpu_num >= 0)) + // cout <<"cpu id = " << _cpu_num << "current_bank = " << current_bank << endl; + // bank_access_profile[_cpu_num][current_bank]++; + return lat; + } + + /******************************************************************/ + + else if ((mem_type == "DRDRAM") && (mem_actpolicy == "closed") && (memctrlpipe_enable == false)) + /* DRDRAM closed-page without overlap */ + { + if (cmdIsRead) + { + lat = DR_T_RCD + DR_T_CAC + DR_T_PACKET; /* DR_T_RP + */ + } + else /* !cmdIsRead */ + { + lat = DR_T_RCD + DR_T_CWD + DR_T_PACKET; /* DR_T_RP + */ + } + total_access++; + overlap=(int)(busy_until[current_bank] - curTick); + lat += chunks * DR_T_PACKET; /*every 128 bit need DR_NUM_CYCLES*/ + + if (cpu_ratio < 1.0) + { + lat = (lat+((int)(1/cpu_ratio)-1))/(int)(1/cpu_ratio); + } + else + { + temp = (int)(lat*cpu_ratio); + lat = (lat*cpu_ratio == temp)?temp:(temp+1); /*round up*/ + } + + lastCmdIsRead=cmdIsRead; + + if (overlap <= 0) /*memory interface is not busy*/ + { + busy_until[current_bank] = curTick+lat; + } + else/*the memory request will be satisfied temp cycles after curTick*/ + { + busy_until[current_bank] += lat; + command_overlapping++; + lat+=overlap; + } + + if (cmdIsRead) + { + if (cpu_ratio < 1.0) + { + busy_until[current_bank] += abs(((DR_T_RAS - (DR_T_RCD + DR_T_PACKET + DR_T_CAC)) + 1)/((int)(1/cpu_ratio))); /* CPU clock cycles */ + } + else + { + busy_until[current_bank] += (int) abs(((DR_T_RAS - (DR_T_RCD + DR_T_PACKET + DR_T_CAC)) + 1)*cpu_ratio); /* CPU clock cycles */ + } + } + else /* !cmdIsRead */ + { + if (cpu_ratio < 1.0) + { + busy_until[current_bank] += abs((-DR_T_PACKET + DR_T_RTR + DR_T_RP - DR_T_CWD + 1)/((int)(1/cpu_ratio))); /* CPU clock cycles */ + } + else + { + busy_until[current_bank] += (int) abs((-DR_T_PACKET + DR_T_RTR + DR_T_RP - DR_T_CWD + 1)*cpu_ratio); /* CPU clock cycles */ + } + } + // if((_cpu_num < num_cpus) && (_cpu_num >= 0)) + // cout <<"cpu id = " << _cpu_num << "current_bank = " << current_bank << endl; + // bank_access_profile[_cpu_num][current_bank]++; + return lat; + } + + /******************************************************************/ + + else /*STD*/ + { + if (mem_access_output != NULL) + { + //fprintf(mem_accessfd,"\n"); + } + assert(chunks >0); + // if((_cpu_num < num_cpus) && (_cpu_num >= 0)) + // cout <<"cpu id = " << _cpu_num << "current_bank = " << current_bank << endl; + // bank_access_profile[_cpu_num][current_bank]++; + return(/* first chunk latency */act_lat + + (/* remainder chunk latency */cas_lat * (chunks - 1))); + } + +} + +/*end added by ar, MG*/ + +/*begin added by ar, MG*/ + +/* ================ helper functions ========================= */ + +/****** DRDRAM specific: shared sense amplifiers ******/ +/* precharges the adjacent banks and returns the number of them (1 or 2)*/ +int /*number of precharged banks*/ +DRAMMemory::prechargeBanksAround(int bank)/*access to bank */ +{ +int temp; + +temp=bank%DR_BANK_SAMP; + +if (temp == 0) /*bank 0, 16,32 ....*/ + { + if (active_row[bank+1]!=DR_NUM_ROWS) + { + precharge++; + active_row[bank+1]=DR_NUM_ROWS; + } + } +else + { + if (temp==DR_BANK_SAMP-1)/*banks 15,31 ...*/ + { + if (active_row[bank-1]!=DR_NUM_ROWS) + { + precharge++; + active_row[bank-1]=DR_NUM_ROWS; + } + } + else + { + if (active_row[bank-1]!=DR_NUM_ROWS) + { + precharge++; + active_row[bank-1]=DR_NUM_ROWS; + } + if (active_row[bank+1]!=DR_NUM_ROWS) + { + precharge++; + active_row[bank+1]=DR_NUM_ROWS; + } + } + } +return precharge; +} + + +#ifndef DOXYGEN_SHOULD_SKIP_THIS + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(DRAMMemory) + + Param<std::string> file; + Param<Range<Addr> > range; + Param<Tick> latency; + /* additional params for dram protocol*/ + Param<int> cpu_ratio; + Param<std::string> mem_type; + Param<std::string> mem_actpolicy; + Param<std::string> memctrladdr_type; + Param<int> bus_width; + Param<int> act_lat; + Param<int> cas_lat; + Param<int> war_lat; + Param<int> pre_lat; + Param<int> dpl_lat; + Param<int> trc_lat; + Param<int> num_banks; + Param<int> num_cpus; + +END_DECLARE_SIM_OBJECT_PARAMS(DRAMMemory) + +BEGIN_INIT_SIM_OBJECT_PARAMS(DRAMMemory) + + INIT_PARAM_DFLT(file, "memory mapped file", ""), + INIT_PARAM(range, "Device Address Range"), + INIT_PARAM(latency, "Memory access latency"), + + /* additional params for dram protocol*/ + INIT_PARAM_DFLT(cpu_ratio,"ratio between CPU speed and memory bus speed",5), + INIT_PARAM_DFLT(mem_type,"type of DRAM","SDRAM"), + INIT_PARAM_DFLT(mem_actpolicy,"open / closed page policy","open"), + INIT_PARAM_DFLT(memctrladdr_type,"interleaved or direct mapping","interleaved"), + INIT_PARAM_DFLT(bus_width,"memory access bus width",16), + INIT_PARAM_DFLT(act_lat,"RAS to CAS delay",2), + INIT_PARAM_DFLT(cas_lat,"CAS delay",1), + INIT_PARAM_DFLT(war_lat,"write after read delay",2), + INIT_PARAM_DFLT(pre_lat,"precharge delay",2), + INIT_PARAM_DFLT(dpl_lat,"data in to precharge delay",2), + INIT_PARAM_DFLT(trc_lat,"row cycle delay",6), + INIT_PARAM_DFLT(num_banks,"Number of Banks",4), + INIT_PARAM_DFLT(num_cpus,"Number of CPUs connected to DRAM",4) + +END_INIT_SIM_OBJECT_PARAMS(DRAMMemory) + +CREATE_SIM_OBJECT(DRAMMemory) +{ + DRAMMemory::Params *p = new DRAMMemory::Params; + p->name = getInstanceName(); + p->addrRange = range; + p->latency = latency; + + /* additional params for dram */ + p->cpu_ratio = cpu_ratio; + p->bus_width = bus_width; + p->mem_type = mem_type; + p->mem_actpolicy = mem_actpolicy; + p->memctrladdr_type = memctrladdr_type; + p->act_lat = act_lat; + p->cas_lat = cas_lat; + p->war_lat = war_lat; + p->pre_lat = pre_lat; + p->dpl_lat = dpl_lat; + p->trc_lat = trc_lat; + p->num_banks = num_banks; + p->num_cpus = num_cpus; + + return new DRAMMemory(p); +} + +REGISTER_SIM_OBJECT("DRAMMemory", DRAMMemory) + +#endif // DOXYGEN_SHOULD_SKIP_THIS + + diff --git a/src/mem/dram.hh b/src/mem/dram.hh new file mode 100644 index 000000000..32d117596 --- /dev/null +++ b/src/mem/dram.hh @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2003-2004 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + * Ali Saidi + */ + +/** + * @file + * Derrive a class from PhysicalMemory that support DRAM like timing access. + */ +#ifndef __MEM_DRAM_HH__ +#define __MEM_DRAM_HH__ + +#include "base/statistics.hh" +#include "mem/physical.hh" + +class DRAMMemory : public PhysicalMemory +{ + protected: + /* added for dram support */ + const int cpu_ratio; // ratio between CPU speed and memory bus speed + const int bus_width; // memory access bus width (in bytes) + /* memory access latency (<first_chunk> <inter_chunk>) */ + const std::string mem_type; + const std::string mem_actpolicy; + const std::string memctrladdr_type; + const int act_lat; + const int cas_lat; + const int war_lat; + const int pre_lat; + const int dpl_lat; + const int trc_lat; + const int num_banks; + const int num_cpus; + + int bank_size; + int num_rows; + int *active_row; + int last_bank; + int last_row; + Tick *busy_until; + int last_dev; + bool lastCmdIsRead; + int precharge; + + /* memory access statistics */ + int same_row_read_access; + int srr_after_read; + int srr_after_write; + int same_row_write_access; + int srw_after_read; + int srw_after_write; + + int same_bank_read_access; + int sbr_after_read; + int sbr_after_write; + int same_bank_write_access; + int sbw_after_read; + int sbw_after_write; + + int other_bank_read_access_hit; + int obr_after_read_hit; + int obr_after_write_hit; + int other_bank_write_access_hit; + int obw_after_read_hit; + int obw_after_write_hit; + // DR + // int other_bank_read_access_miss; + int obr_after_read_miss; + int obr_after_write_miss; + // DR + // int other_bank_write_access_miss; + int obw_after_read_miss; + int obw_after_write_miss; + + int total_access; + + int adjacent_access; + int adjacent_read; + int adjacent_write; + int command_overlapping; + int best_case; + int in_between_case; + int worst_case; + int full_overlapping; + int partial_overlapping; + + int mem_access_details; + int memctrlpipe_enable; + + Tick time_last_access; + + + Stats::Vector<> accesses; + Stats::Vector<> bytesRequested; + Stats::Vector<> bytesSent; + Stats::Vector<> compressedAccesses; + + Stats::Vector<> cycles_nCKE; + Stats::Vector<> cycles_all_precharge_CKE; + Stats::Vector<> cycles_all_precharge_nCKE; + Stats::Vector<> cycles_bank_active_nCKE; + Stats::Vector<> cycles_avg_ACT; + Stats::Vector<> cycles_read_out; + Stats::Vector<> cycles_write_in; + Stats::Vector<> cycles_between_misses; + Stats::Vector<> other_bank_read_access_miss; + Stats::Vector<> other_bank_write_access_miss; + Stats::Scalar<> total_latency; + Stats::Scalar<> total_icache_req; + Stats::Scalar<> total_arb_latency; + Stats::Formula avg_latency; + Stats::Formula avg_arb_latency; + Stats::Vector2d<> bank_access_profile; + + + protected: + Tick calculateLatency(Packet *pkt); + int prechargeBanksAround(int bank); + + public: + struct Params : public PhysicalMemory::Params + { + /* additional params for dram protocol*/ + int cpu_ratio; + int bus_width; + + std::string mem_type; /* DRDRAM, SDRAM */ + std::string mem_actpolicy; /* closed, open */ + std::string memctrladdr_type; /* interleaved, anythingelse */ + + int act_lat; + int cas_lat; + int war_lat; + int pre_lat; + int dpl_lat; + int trc_lat; + int num_banks; + int num_cpus; + + }; + virtual void regStats(); + DRAMMemory(Params *p); +}; + +#endif// __MEM_DRAM_HH__ + diff --git a/src/mem/mem_object.cc b/src/mem/mem_object.cc new file mode 100644 index 000000000..d4d3fd283 --- /dev/null +++ b/src/mem/mem_object.cc @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#include "mem/mem_object.hh" +#include "sim/param.hh" + +MemObject::MemObject(const std::string &name) + : SimObject(name) +{ +} + +DEFINE_SIM_OBJECT_CLASS_NAME("MemObject", MemObject) diff --git a/src/mem/mem_object.hh b/src/mem/mem_object.hh new file mode 100644 index 000000000..d12eeffe0 --- /dev/null +++ b/src/mem/mem_object.hh @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Base Memory Object declaration. + */ + +#ifndef __MEM_MEM_OBJECT_HH__ +#define __MEM_MEM_OBJECT_HH__ + +#include "sim/sim_object.hh" +#include "mem/port.hh" + +/** + * The base MemoryObject class, allows for an accesor function to a + * simobj that returns the Port. + */ +class MemObject : public SimObject +{ + public: + MemObject(const std::string &name); + + public: + /** Additional function to return the Port of a memory object. */ + virtual Port *getPort(const std::string &if_name, int idx = -1) = 0; +}; + +#endif //__MEM_MEM_OBJECT_HH__ diff --git a/src/mem/packet.cc b/src/mem/packet.cc new file mode 100644 index 000000000..91298df8c --- /dev/null +++ b/src/mem/packet.cc @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + * Steve Reinhardt + */ + +/** + * @file + * Definition of the Packet Class, a packet is a transaction occuring + * between a single level of the memory heirarchy (ie L1->L2). + */ +#include "base/misc.hh" +#include "mem/packet.hh" + +static const std::string ReadReqString("ReadReq"); +static const std::string WriteReqString("WriteReq"); +static const std::string WriteReqNoAckString("WriteReqNoAck"); +static const std::string ReadRespString("ReadResp"); +static const std::string WriteRespString("WriteResp"); +static const std::string OtherCmdString("<other>"); + +const std::string & +Packet::cmdString() const +{ + switch (cmd) { + case ReadReq: return ReadReqString; + case WriteReq: return WriteReqString; + case WriteReqNoAck: return WriteReqNoAckString; + case ReadResp: return ReadRespString; + case WriteResp: return WriteRespString; + default: return OtherCmdString; + } +} + +const std::string & +Packet::cmdIdxToString(Packet::Command idx) +{ + switch (idx) { + case ReadReq: return ReadReqString; + case WriteReq: return WriteReqString; + case WriteReqNoAck: return WriteReqNoAckString; + case ReadResp: return ReadRespString; + case WriteResp: return WriteRespString; + default: return OtherCmdString; + } +} + +/** delete the data pointed to in the data pointer. Ok to call to matter how + * data was allocted. */ +void +Packet::deleteData() +{ + assert(staticData || dynamicData); + if (staticData) + return; + + if (arrayData) + delete [] data; + else + delete data; +} + +/** If there isn't data in the packet, allocate some. */ +void +Packet::allocate() +{ + if (data) + return; + assert(!staticData); + dynamicData = true; + arrayData = true; + data = new uint8_t[getSize()]; +} + +/** Do the packet modify the same addresses. */ +bool +Packet::intersect(Packet *p) +{ + Addr s1 = getAddr(); + Addr e1 = getAddr() + getSize(); + Addr s2 = p->getAddr(); + Addr e2 = p->getAddr() + p->getSize(); + + if (s1 >= s2 && s1 < e2) + return true; + if (e1 >= s2 && e1 < e2) + return true; + return false; +} + +bool +fixPacket(Packet *func, Packet *timing) +{ + panic("Need to implement!"); +} diff --git a/src/mem/packet.hh b/src/mem/packet.hh new file mode 100644 index 000000000..c7d28010c --- /dev/null +++ b/src/mem/packet.hh @@ -0,0 +1,375 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + * Steve Reinhardt + * Ali Saidi + */ + +/** + * @file + * Declaration of the Packet class. + */ + +#ifndef __MEM_PACKET_HH__ +#define __MEM_PACKET_HH__ + +#include "mem/request.hh" +#include "sim/host.hh" +#include "sim/root.hh" +#include <list> +#include <cassert> + +struct Packet; +typedef Packet* PacketPtr; +typedef uint8_t* PacketDataPtr; +typedef std::list<PacketPtr> PacketList; + +//Coherence Flags +#define NACKED_LINE 1 << 0 +#define SATISFIED 1 << 1 +#define SHARED_LINE 1 << 2 +#define CACHE_LINE_FILL 1 << 3 +#define COMPRESSED 1 << 4 +#define NO_ALLOCATE 1 << 5 +#define SNOOP_COMMIT 1 << 6 + +//For statistics we need max number of commands, hard code it at +//20 for now. @todo fix later +#define NUM_MEM_CMDS 1 << 9 + +/** + * A Packet is used to encapsulate a transfer between two objects in + * the memory system (e.g., the L1 and L2 cache). (In contrast, a + * single Request travels all the way from the requester to the + * ultimate destination and back, possibly being conveyed by several + * different Packets along the way.) + */ +class Packet +{ + public: + /** Temporary FLAGS field until cache gets working, this should be in coherence/sender state. */ + uint64_t flags; + + private: + /** A pointer to the data being transfered. It can be differnt + * sizes at each level of the heirarchy so it belongs in the + * packet, not request. This may or may not be populated when a + * responder recieves the packet. If not populated it memory + * should be allocated. + */ + PacketDataPtr data; + + /** Is the data pointer set to a value that shouldn't be freed + * when the packet is destroyed? */ + bool staticData; + /** The data pointer points to a value that should be freed when + * the packet is destroyed. */ + bool dynamicData; + /** the data pointer points to an array (thus delete [] ) needs to + * be called on it rather than simply delete.*/ + bool arrayData; + + + /** The address of the request. This address could be virtual or + * physical, depending on the system configuration. */ + Addr addr; + + /** The size of the request or transfer. */ + int size; + + /** Device address (e.g., bus ID) of the source of the + * transaction. The source is not responsible for setting this + * field; it is set implicitly by the interconnect when the + * packet * is first sent. */ + short src; + + /** Device address (e.g., bus ID) of the destination of the + * transaction. The special value Broadcast indicates that the + * packet should be routed based on its address. This field is + * initialized in the constructor and is thus always valid + * (unlike * addr, size, and src). */ + short dest; + + /** Are the 'addr' and 'size' fields valid? */ + bool addrSizeValid; + /** Is the 'src' field valid? */ + bool srcValid; + + + public: + + /** Used to calculate latencies for each packet.*/ + Tick time; + + /** The special destination address indicating that the packet + * should be routed based on its address. */ + static const short Broadcast = -1; + + /** A pointer to the original request. */ + RequestPtr req; + + /** A virtual base opaque structure used to hold coherence-related + * state. A specific subclass would be derived from this to + * carry state specific to a particular coherence protocol. */ + class CoherenceState { + public: + virtual ~CoherenceState() {} + }; + + /** This packet's coherence state. Caches should use + * dynamic_cast<> to cast to the state appropriate for the + * system's coherence protocol. */ + CoherenceState *coherence; + + /** A virtual base opaque structure used to hold state associated + * with the packet but specific to the sending device (e.g., an + * MSHR). A pointer to this state is returned in the packet's + * response so that the sender can quickly look up the state + * needed to process it. A specific subclass would be derived + * from this to carry state specific to a particular sending + * device. */ + class SenderState { + public: + virtual ~SenderState() {} + }; + + /** This packet's sender state. Devices should use dynamic_cast<> + * to cast to the state appropriate to the sender. */ + SenderState *senderState; + + private: + /** List of command attributes. */ + enum CommandAttribute + { + IsRead = 1 << 0, + IsWrite = 1 << 1, + IsPrefetch = 1 << 2, + IsInvalidate = 1 << 3, + IsRequest = 1 << 4, + IsResponse = 1 << 5, + NeedsResponse = 1 << 6, + IsSWPrefetch = 1 << 7, + IsHWPrefetch = 1 << 8 + }; + + public: + /** List of all commands associated with a packet. */ + enum Command + { + InvalidCmd = 0, + ReadReq = IsRead | IsRequest | NeedsResponse, + WriteReq = IsWrite | IsRequest | NeedsResponse, + WriteReqNoAck = IsWrite | IsRequest, + ReadResp = IsRead | IsResponse | NeedsResponse, + WriteResp = IsWrite | IsResponse | NeedsResponse, + Writeback = IsWrite | IsRequest, + SoftPFReq = IsRead | IsRequest | IsSWPrefetch | NeedsResponse, + HardPFReq = IsRead | IsRequest | IsHWPrefetch | NeedsResponse, + SoftPFResp = IsRead | IsResponse | IsSWPrefetch | NeedsResponse, + HardPFResp = IsRead | IsResponse | IsHWPrefetch | NeedsResponse, + InvalidateReq = IsInvalidate | IsRequest, + WriteInvalidateReq = IsWrite | IsInvalidate | IsRequest, + UpgradeReq = IsInvalidate | IsRequest | NeedsResponse, + UpgradeResp = IsInvalidate | IsResponse | NeedsResponse, + ReadExReq = IsRead | IsInvalidate | IsRequest | NeedsResponse, + ReadExResp = IsRead | IsInvalidate | IsResponse | NeedsResponse + }; + + /** Return the string name of the cmd field (for debugging and + * tracing). */ + const std::string &cmdString() const; + + /** Reutrn the string to a cmd given by idx. */ + const std::string &cmdIdxToString(Command idx); + + /** Return the index of this command. */ + inline int cmdToIndex() const { return (int) cmd; } + + /** The command field of the packet. */ + Command cmd; + + bool isRead() { return (cmd & IsRead) != 0; } + bool isWrite() { return (cmd & IsWrite) != 0; } + bool isRequest() { return (cmd & IsRequest) != 0; } + bool isResponse() { return (cmd & IsResponse) != 0; } + bool needsResponse() { return (cmd & NeedsResponse) != 0; } + bool isInvalidate() { return (cmd & IsInvalidate) != 0; } + + bool isCacheFill() { return (flags & CACHE_LINE_FILL) != 0; } + bool isNoAllocate() { return (flags & NO_ALLOCATE) != 0; } + bool isCompressed() { return (flags & COMPRESSED) != 0; } + + bool nic_pkt() { assert("Unimplemented\n" && 0); return false; } + + /** Possible results of a packet's request. */ + enum Result + { + Success, + BadAddress, + Nacked, + Unknown + }; + + /** The result of this packet's request. */ + Result result; + + /** Accessor function that returns the source index of the packet. */ + short getSrc() const { assert(srcValid); return src; } + void setSrc(short _src) { src = _src; srcValid = true; } + + /** Accessor function that returns the destination index of + the packet. */ + short getDest() const { return dest; } + void setDest(short _dest) { dest = _dest; } + + Addr getAddr() const { assert(addrSizeValid); return addr; } + int getSize() const { assert(addrSizeValid); return size; } + Addr getOffset(int blkSize) const { return addr & (Addr)(blkSize - 1); } + + void addrOverride(Addr newAddr) { assert(addrSizeValid); addr = newAddr; } + void cmdOverride(Command newCmd) { cmd = newCmd; } + + /** Constructor. Note that a Request object must be constructed + * first, but the Requests's physical address and size fields + * need not be valid. The command and destination addresses + * must be supplied. */ + Packet(Request *_req, Command _cmd, short _dest) + : data(NULL), staticData(false), dynamicData(false), arrayData(false), + addr(_req->paddr), size(_req->size), dest(_dest), + addrSizeValid(_req->validPaddr), + srcValid(false), + req(_req), coherence(NULL), senderState(NULL), cmd(_cmd), + result(Unknown) + { + flags = 0; + time = curTick; + } + + /** Alternate constructor if you are trying to create a packet with + * a request that is for a whole block, not the address from the req. + * this allows for overriding the size/addr of the req.*/ + Packet(Request *_req, Command _cmd, short _dest, int _blkSize) + : data(NULL), staticData(false), dynamicData(false), arrayData(false), + addr(_req->paddr & ~(_blkSize - 1)), size(_blkSize), + dest(_dest), + addrSizeValid(_req->validPaddr), srcValid(false), + req(_req), coherence(NULL), senderState(NULL), cmd(_cmd), + result(Unknown) + { + flags = 0; + time = curTick; + } + + /** Destructor. */ + ~Packet() + { deleteData(); } + + /** Reinitialize packet address and size from the associated + * Request object, and reset other fields that may have been + * modified by a previous transaction. Typically called when a + * statically allocated Request/Packet pair is reused for + * multiple transactions. */ + void reinitFromRequest() { + assert(req->validPaddr); + addr = req->paddr; + size = req->size; + time = req->time; + addrSizeValid = true; + result = Unknown; + if (dynamicData) { + deleteData(); + dynamicData = false; + arrayData = false; + } + } + + /** Take a request packet and modify it in place to be suitable + * for returning as a response to that request. Used for timing + * accesses only. For atomic and functional accesses, the + * request packet is always implicitly passed back *without* + * modifying the command or destination fields, so this function + * should not be called. */ + void makeTimingResponse() { + assert(needsResponse()); + assert(isRequest()); + int icmd = (int)cmd; + icmd &= ~(IsRequest); + icmd |= IsResponse; + cmd = (Command)icmd; + dest = src; + srcValid = false; + } + + /** Take a request packet that has been returned as NACKED and modify it so + * that it can be sent out again. Only packets that need a response can be + * NACKED, so verify that that is true. */ + void reinitNacked() { + assert(needsResponse() && result == Nacked); + dest = Broadcast; + result = Unknown; + } + + + /** Set the data pointer to the following value that should not be freed. */ + template <typename T> + void dataStatic(T *p); + + /** Set the data pointer to a value that should have delete [] called on it. + */ + template <typename T> + void dataDynamicArray(T *p); + + /** set the data pointer to a value that should have delete called on it. */ + template <typename T> + void dataDynamic(T *p); + + /** return the value of what is pointed to in the packet. */ + template <typename T> + T get(); + + /** get a pointer to the data ptr. */ + template <typename T> + T* getPtr(); + + /** set the value in the data pointer to v. */ + template <typename T> + void set(T v); + + /** delete the data pointed to in the data pointer. Ok to call to matter how + * data was allocted. */ + void deleteData(); + + /** If there isn't data in the packet, allocate some. */ + void allocate(); + + /** Do the packet modify the same addresses. */ + bool intersect(Packet *p); +}; + +bool fixPacket(Packet *func, Packet *timing); +#endif //__MEM_PACKET_HH diff --git a/src/mem/page_table.cc b/src/mem/page_table.cc new file mode 100644 index 000000000..2b460306f --- /dev/null +++ b/src/mem/page_table.cc @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2003 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Ron Dreslinski + */ + +/** + * @file + * Definitions of page table. + */ +#include <string> +#include <map> +#include <fstream> + +#include "arch/faults.hh" +#include "base/bitfield.hh" +#include "base/intmath.hh" +#include "base/trace.hh" +#include "mem/page_table.hh" +#include "sim/builder.hh" +#include "sim/sim_object.hh" +#include "sim/system.hh" + +using namespace std; +using namespace TheISA; + +PageTable::PageTable(System *_system, Addr _pageSize) + : pageSize(_pageSize), offsetMask(mask(floorLog2(_pageSize))), + system(_system) +{ + assert(isPowerOf2(pageSize)); + pTableCache[0].vaddr = 0; + pTableCache[1].vaddr = 0; + pTableCache[2].vaddr = 0; +} + +PageTable::~PageTable() +{ +} + +Fault +PageTable::page_check(Addr addr, int64_t size) const +{ + if (size < sizeof(uint64_t)) { + if (!isPowerOf2(size)) { + panic("Invalid request size!\n"); + return genMachineCheckFault(); + } + + if ((size - 1) & addr) + return genAlignmentFault(); + } + else { + if ((addr & (VMPageSize - 1)) + size > VMPageSize) { + panic("Invalid request size!\n"); + return genMachineCheckFault(); + } + + if ((sizeof(uint64_t) - 1) & addr) + return genAlignmentFault(); + } + + return NoFault; +} + + + + +void +PageTable::allocate(Addr vaddr, int64_t size) +{ + // starting address must be page aligned + assert(pageOffset(vaddr) == 0); + + for (; size > 0; size -= pageSize, vaddr += pageSize) { + m5::hash_map<Addr,Addr>::iterator iter = pTable.find(vaddr); + + if (iter != pTable.end()) { + // already mapped + fatal("PageTable::allocate: address 0x%x already mapped", vaddr); + } + + pTable[vaddr] = system->new_page(); + pTableCache[2].paddr = pTableCache[1].paddr; + pTableCache[2].vaddr = pTableCache[1].vaddr; + pTableCache[1].paddr = pTableCache[0].paddr; + pTableCache[1].vaddr = pTableCache[0].vaddr; + pTableCache[0].paddr = pTable[vaddr]; + pTableCache[0].vaddr = vaddr; + } +} + + + +bool +PageTable::translate(Addr vaddr, Addr &paddr) +{ + Addr page_addr = pageAlign(vaddr); + paddr = 0; + + if (pTableCache[0].vaddr == vaddr) { + paddr = pTableCache[0].paddr; + return true; + } + if (pTableCache[1].vaddr == vaddr) { + paddr = pTableCache[1].paddr; + return true; + } + if (pTableCache[2].vaddr == vaddr) { + paddr = pTableCache[2].paddr; + return true; + } + + m5::hash_map<Addr,Addr>::iterator iter = pTable.find(page_addr); + + if (iter == pTable.end()) { + return false; + } + + paddr = iter->second + pageOffset(vaddr); + return true; +} + + +Fault +PageTable::translate(RequestPtr &req) +{ + Addr paddr; + assert(pageAlign(req->getVaddr() + req->getSize() - 1) + == pageAlign(req->getVaddr())); + if (!translate(req->getVaddr(), paddr)) { + return genPageTableFault(req->getVaddr()); + } + req->setPaddr(paddr); + return page_check(req->getPaddr(), req->getSize()); +} diff --git a/src/mem/page_table.hh b/src/mem/page_table.hh new file mode 100644 index 000000000..fce063280 --- /dev/null +++ b/src/mem/page_table.hh @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2003 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +/** + * @file + * Declaration of a non-full system Page Table. + */ + +#ifndef __PAGE_TABLE__ +#define __PAGE_TABLE__ + +#include <string> + +#include "sim/faults.hh" +#include "arch/isa_traits.hh" +#include "base/hashmap.hh" +#include "base/trace.hh" +#include "mem/request.hh" +#include "mem/packet.hh" +#include "sim/sim_object.hh" + +class System; + +/** + * Page Table Declaration. + */ +class PageTable +{ + protected: + m5::hash_map<Addr,Addr> pTable; + + struct cacheElement { + Addr paddr; + Addr vaddr; + } ; + + struct cacheElement pTableCache[3]; + + const Addr pageSize; + const Addr offsetMask; + + System *system; + + public: + + PageTable(System *_system, Addr _pageSize = TheISA::VMPageSize); + + ~PageTable(); + + Addr pageAlign(Addr a) { return (a & ~offsetMask); } + Addr pageOffset(Addr a) { return (a & offsetMask); } + + Fault page_check(Addr addr, int64_t size) const; + + void allocate(Addr vaddr, int64_t size); + + /** + * Translate function + * @param vaddr The virtual address. + * @return Physical address from translation. + */ + bool translate(Addr vaddr, Addr &paddr); + + /** + * Perform a translation on the memory request, fills in paddr + * field of mem_req. + * @param req The memory request. + */ + Fault translate(RequestPtr &req); + +}; + +#endif diff --git a/src/mem/physical.cc b/src/mem/physical.cc new file mode 100644 index 000000000..8fea733ec --- /dev/null +++ b/src/mem/physical.cc @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + * Ali Saidi + */ + +#include <sys/types.h> +#include <sys/mman.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <zlib.h> + +#include <iostream> +#include <string> + + +#include "base/misc.hh" +#include "config/full_system.hh" +#include "mem/packet_impl.hh" +#include "mem/physical.hh" +#include "sim/host.hh" +#include "sim/builder.hh" +#include "sim/eventq.hh" +#include "arch/isa_traits.hh" + + +using namespace std; +using namespace TheISA; + + +PhysicalMemory::PhysicalMemory(Params *p) + : MemObject(p->name), pmemAddr(NULL), port(NULL), lat(p->latency), _params(p) +{ + if (params()->addrRange.size() % TheISA::PageBytes != 0) + panic("Memory Size not divisible by page size\n"); + + int map_flags = MAP_ANON | MAP_PRIVATE; + pmemAddr = (uint8_t *)mmap(NULL, params()->addrRange.size(), PROT_READ | PROT_WRITE, + map_flags, -1, 0); + + if (pmemAddr == (void *)MAP_FAILED) { + perror("mmap"); + fatal("Could not mmap!\n"); + } + + pagePtr = 0; +} + +void +PhysicalMemory::init() +{ + if (!port) + panic("PhysicalMemory not connected to anything!"); + port->sendStatusChange(Port::RangeChange); +} + +PhysicalMemory::~PhysicalMemory() +{ + if (pmemAddr) + munmap(pmemAddr, params()->addrRange.size()); + //Remove memPorts? +} + +Addr +PhysicalMemory::new_page() +{ + Addr return_addr = pagePtr << LogVMPageSize; + return_addr += params()->addrRange.start; + + ++pagePtr; + return return_addr; +} + +int +PhysicalMemory::deviceBlockSize() +{ + //Can accept anysize request + return 0; +} + +Tick +PhysicalMemory::calculateLatency(Packet *pkt) +{ + return lat; +} + +void +PhysicalMemory::doFunctionalAccess(Packet *pkt) +{ + assert(pkt->getAddr() + pkt->getSize() < params()->addrRange.size()); + + switch (pkt->cmd) { + case Packet::ReadReq: + memcpy(pkt->getPtr<uint8_t>(), + pmemAddr + pkt->getAddr() - params()->addrRange.start, + pkt->getSize()); + break; + case Packet::WriteReq: + memcpy(pmemAddr + pkt->getAddr() - params()->addrRange.start, + pkt->getPtr<uint8_t>(), + pkt->getSize()); + // temporary hack: will need to add real LL/SC implementation + // for cacheless systems later. + if (pkt->req->getFlags() & LOCKED) { + pkt->req->setScResult(1); + } + break; + default: + panic("unimplemented"); + } + + pkt->result = Packet::Success; +} + +Port * +PhysicalMemory::getPort(const std::string &if_name, int idx) +{ + if (if_name == "port" && idx == -1) { + if (port != NULL) + panic("PhysicalMemory::getPort: additional port requested to memory!"); + port = new MemoryPort(name() + "-port", this); + return port; + } else if (if_name == "functional") { + /* special port for functional writes at startup. */ + return new MemoryPort(name() + "-funcport", this); + } else { + panic("PhysicalMemory::getPort: unknown port %s requested", if_name); + } +} + +void +PhysicalMemory::recvStatusChange(Port::Status status) +{ +} + +PhysicalMemory::MemoryPort::MemoryPort(const std::string &_name, + PhysicalMemory *_memory) + : SimpleTimingPort(_name), memory(_memory) +{ } + +void +PhysicalMemory::MemoryPort::recvStatusChange(Port::Status status) +{ + memory->recvStatusChange(status); +} + +void +PhysicalMemory::MemoryPort::getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) +{ + memory->getAddressRanges(resp, snoop); +} + +void +PhysicalMemory::getAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) +{ + snoop.clear(); + resp.clear(); + resp.push_back(RangeSize(params()->addrRange.start, + params()->addrRange.size())); +} + +int +PhysicalMemory::MemoryPort::deviceBlockSize() +{ + return memory->deviceBlockSize(); +} + +Tick +PhysicalMemory::MemoryPort::recvAtomic(Packet *pkt) +{ + memory->doFunctionalAccess(pkt); + return memory->calculateLatency(pkt); +} + +void +PhysicalMemory::MemoryPort::recvFunctional(Packet *pkt) +{ + // Default implementation of SimpleTimingPort::recvFunctional() + // calls recvAtomic() and throws away the latency; we can save a + // little here by just not calculating the latency. + memory->doFunctionalAccess(pkt); +} + +unsigned int +PhysicalMemory::drain(Event *de) +{ + int count = port->drain(de); + if (count) + changeState(Draining); + else + changeState(Drained); + return count; +} + +void +PhysicalMemory::serialize(ostream &os) +{ + gzFile compressedMem; + string filename = name() + ".physmem"; + + SERIALIZE_SCALAR(filename); + + // write memory file + string thefile = Checkpoint::dir() + "/" + filename.c_str(); + int fd = creat(thefile.c_str(), 0664); + if (fd < 0) { + perror("creat"); + fatal("Can't open physical memory checkpoint file '%s'\n", filename); + } + + compressedMem = gzdopen(fd, "wb"); + if (compressedMem == NULL) + fatal("Insufficient memory to allocate compression state for %s\n", + filename); + + if (gzwrite(compressedMem, pmemAddr, params()->addrRange.size()) != params()->addrRange.size()) { + fatal("Write failed on physical memory checkpoint file '%s'\n", + filename); + } + + if (gzclose(compressedMem)) + fatal("Close failed on physical memory checkpoint file '%s'\n", + filename); +} + +void +PhysicalMemory::unserialize(Checkpoint *cp, const string §ion) +{ + gzFile compressedMem; + long *tempPage; + long *pmem_current; + uint64_t curSize; + uint32_t bytesRead; + const int chunkSize = 16384; + + + string filename; + + UNSERIALIZE_SCALAR(filename); + + filename = cp->cptDir + "/" + filename; + + // mmap memoryfile + int fd = open(filename.c_str(), O_RDONLY); + if (fd < 0) { + perror("open"); + fatal("Can't open physical memory checkpoint file '%s'", filename); + } + + compressedMem = gzdopen(fd, "rb"); + if (compressedMem == NULL) + fatal("Insufficient memory to allocate compression state for %s\n", + filename); + + // unmap file that was mmaped in the constructor + // This is done here to make sure that gzip and open don't muck with our + // nice large space of memory before we reallocate it + munmap(pmemAddr, params()->addrRange.size()); + + pmemAddr = (uint8_t *)mmap(NULL, params()->addrRange.size(), PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + + if (pmemAddr == (void *)MAP_FAILED) { + perror("mmap"); + fatal("Could not mmap physical memory!\n"); + } + + curSize = 0; + tempPage = (long*)malloc(chunkSize); + if (tempPage == NULL) + fatal("Unable to malloc memory to read file %s\n", filename); + + /* Only copy bytes that are non-zero, so we don't give the VM system hell */ + while (curSize < params()->addrRange.size()) { + bytesRead = gzread(compressedMem, tempPage, chunkSize); + if (bytesRead != chunkSize && bytesRead != params()->addrRange.size() - curSize) + fatal("Read failed on physical memory checkpoint file '%s'" + " got %d bytes, expected %d or %d bytes\n", + filename, bytesRead, chunkSize, params()->addrRange.size()-curSize); + + assert(bytesRead % sizeof(long) == 0); + + for (int x = 0; x < bytesRead/sizeof(long); x++) + { + if (*(tempPage+x) != 0) { + pmem_current = (long*)(pmemAddr + curSize + x * sizeof(long)); + *pmem_current = *(tempPage+x); + } + } + curSize += bytesRead; + } + + free(tempPage); + + if (gzclose(compressedMem)) + fatal("Close failed on physical memory checkpoint file '%s'\n", + filename); + +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(PhysicalMemory) + + Param<string> file; + Param<Range<Addr> > range; + Param<Tick> latency; + +END_DECLARE_SIM_OBJECT_PARAMS(PhysicalMemory) + +BEGIN_INIT_SIM_OBJECT_PARAMS(PhysicalMemory) + + INIT_PARAM_DFLT(file, "memory mapped file", ""), + INIT_PARAM(range, "Device Address Range"), + INIT_PARAM(latency, "Memory access latency") + +END_INIT_SIM_OBJECT_PARAMS(PhysicalMemory) + +CREATE_SIM_OBJECT(PhysicalMemory) +{ + PhysicalMemory::Params *p = new PhysicalMemory::Params; + p->name = getInstanceName(); + p->addrRange = range; + p->latency = latency; + return new PhysicalMemory(p); +} + +REGISTER_SIM_OBJECT("PhysicalMemory", PhysicalMemory) diff --git a/src/mem/physical.hh b/src/mem/physical.hh new file mode 100644 index 000000000..02308b2ef --- /dev/null +++ b/src/mem/physical.hh @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + */ + +/* @file + */ + +#ifndef __PHYSICAL_MEMORY_HH__ +#define __PHYSICAL_MEMORY_HH__ + +#include "base/range.hh" +#include "mem/mem_object.hh" +#include "mem/packet.hh" +#include "mem/tport.hh" +#include "sim/eventq.hh" +#include <map> +#include <string> + +// +// Functional model for a contiguous block of physical memory. (i.e. RAM) +// +class PhysicalMemory : public MemObject +{ + class MemoryPort : public SimpleTimingPort + { + PhysicalMemory *memory; + + public: + + MemoryPort(const std::string &_name, PhysicalMemory *_memory); + + protected: + + virtual Tick recvAtomic(Packet *pkt); + + virtual void recvFunctional(Packet *pkt); + + virtual void recvStatusChange(Status status); + + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop); + + virtual int deviceBlockSize(); + }; + + int numPorts; + + + private: + // prevent copying of a MainMemory object + PhysicalMemory(const PhysicalMemory &specmem); + const PhysicalMemory &operator=(const PhysicalMemory &specmem); + + protected: + uint8_t *pmemAddr; + MemoryPort *port; + int pagePtr; + Tick lat; + + public: + Addr new_page(); + uint64_t size() { return params()->addrRange.size(); } + + struct Params + { + std::string name; + Range<Addr> addrRange; + Tick latency; + }; + + protected: + Params *_params; + + public: + const Params *params() const { return _params; } + PhysicalMemory(Params *p); + virtual ~PhysicalMemory(); + + public: + int deviceBlockSize(); + void getAddressRanges(AddrRangeList &resp, AddrRangeList &snoop); + virtual Port *getPort(const std::string &if_name, int idx = -1); + void virtual init(); + unsigned int drain(Event *de); + + protected: + void doFunctionalAccess(Packet *pkt); + virtual Tick calculateLatency(Packet *pkt); + void recvStatusChange(Port::Status status); + + public: + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); + +}; + +#endif //__PHYSICAL_MEMORY_HH__ diff --git a/src/mem/port.cc b/src/mem/port.cc new file mode 100644 index 000000000..17924b759 --- /dev/null +++ b/src/mem/port.cc @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +/** + * @file + * Port object definitions. + */ + +#include "base/chunk_generator.hh" +#include "base/trace.hh" +#include "mem/packet_impl.hh" +#include "mem/port.hh" + +void +Port::setPeer(Port *port) +{ + DPRINTF(Config, "setting peer to %s\n", port->name()); + peer = port; +} + +void +Port::blobHelper(Addr addr, uint8_t *p, int size, Packet::Command cmd) +{ + Request req; + Packet pkt(&req, cmd, Packet::Broadcast); + + for (ChunkGenerator gen(addr, size, peerBlockSize()); + !gen.done(); gen.next()) { + req.setPhys(gen.addr(), gen.size(), 0); + pkt.reinitFromRequest(); + pkt.dataStatic(p); + sendFunctional(&pkt); + p += gen.size(); + } +} + +void +Port::writeBlob(Addr addr, uint8_t *p, int size) +{ + blobHelper(addr, p, size, Packet::WriteReq); +} + +void +Port::readBlob(Addr addr, uint8_t *p, int size) +{ + blobHelper(addr, p, size, Packet::ReadReq); +} + +void +Port::memsetBlob(Addr addr, uint8_t val, int size) +{ + // quick and dirty... + uint8_t *buf = new uint8_t[size]; + + memset(buf, val, size); + blobHelper(addr, buf, size, Packet::WriteReq); + + delete [] buf; +} diff --git a/src/mem/port.hh b/src/mem/port.hh new file mode 100644 index 000000000..6b4184043 --- /dev/null +++ b/src/mem/port.hh @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + */ + +/** + * @file + * Port Object Declaration. Ports are used to interface memory objects to + * each other. They will always come in pairs, and we refer to the other + * port object as the peer. These are used to make the design more + * modular so that a specific interface between every type of objcet doesn't + * have to be created. + */ + +#ifndef __MEM_PORT_HH__ +#define __MEM_PORT_HH__ + +#include <list> +#include <inttypes.h> + +#include "base/misc.hh" +#include "base/range.hh" +#include "mem/packet.hh" +#include "mem/request.hh" + +/** This typedef is used to clean up the parameter list of + * getDeviceAddressRanges() and getPeerAddressRanges(). It's declared + * outside the Port object since it's also used by some mem objects. + * Eventually we should move this typedef to wherever Addr is + * defined. + */ + +typedef std::list<Range<Addr> > AddrRangeList; +typedef std::list<Range<Addr> >::iterator AddrRangeIter; + +/** + * Ports are used to interface memory objects to + * each other. They will always come in pairs, and we refer to the other + * port object as the peer. These are used to make the design more + * modular so that a specific interface between every type of objcet doesn't + * have to be created. + * + * Recv accesor functions are being called from the peer interface. + * Send accessor functions are being called from the device the port is + * associated with, and it will call the peer recv. accessor function. + */ +class Port +{ + private: + + /** Descriptive name (for DPRINTF output) */ + mutable std::string portName; + + /** A pointer to the peer port. Ports always come in pairs, that way they + can use a standardized interface to communicate between different + memory objects. */ + Port *peer; + + public: + + Port() + : peer(NULL) + { } + + /** + * Constructor. + * + * @param _name Port name for DPRINTF output. Should include name + * of memory system object to which the port belongs. + */ + Port(const std::string &_name) + : portName(_name), peer(NULL) + { } + + /** Return port name (for DPRINTF). */ + const std::string &name() const { return portName; } + + virtual ~Port() {}; + + // mey be better to use subclasses & RTTI? + /** Holds the ports status. Currently just that a range recomputation needs + * to be done. */ + enum Status { + RangeChange, + SnoopSquash + }; + + void setName(const std::string &name) + { portName = name; } + + /** Function to set the pointer for the peer port. + @todo should be called by the configuration stuff (python). + */ + void setPeer(Port *port); + + /** Function to set the pointer for the peer port. + @todo should be called by the configuration stuff (python). + */ + Port *getPeer() { return peer; } + + protected: + + /** These functions are protected because they should only be + * called by a peer port, never directly by any outside object. */ + + /** Called to recive a timing call from the peer port. */ + virtual bool recvTiming(Packet *pkt) = 0; + + /** Called to recive a atomic call from the peer port. */ + virtual Tick recvAtomic(Packet *pkt) = 0; + + /** Called to recive a functional call from the peer port. */ + virtual void recvFunctional(Packet *pkt) = 0; + + /** Called to recieve a status change from the peer port. */ + virtual void recvStatusChange(Status status) = 0; + + /** Called by a peer port if the send was unsuccesful, and had to + wait. This shouldn't be valid for response paths (IO Devices). + so it is set to panic if it isn't already defined. + */ + virtual void recvRetry() { panic("??"); } + + /** Called by a peer port in order to determine the block size of the + device connected to this port. It sometimes doesn't make sense for + this function to be called, a DMA interface doesn't really have a + block size, so it is defaulted to a panic. + */ + virtual int deviceBlockSize() { panic("??"); } + + /** The peer port is requesting us to reply with a list of the ranges we + are responsible for. + @param resp is a list of ranges responded to + @param snoop is a list of ranges snooped + */ + virtual void getDeviceAddressRanges(AddrRangeList &resp, + AddrRangeList &snoop) + { panic("??"); } + + public: + + /** Function called by associated memory device (cache, memory, iodevice) + in order to send a timing request to the port. Simply calls the peer + port receive function. + @return This function returns if the send was succesful in it's + recieve. If it was a failure, then the port will wait for a recvRetry + at which point it can possibly issue a successful sendTiming. This is used in + case a cache has a higher priority request come in while waiting for + the bus to arbitrate. + */ + bool sendTiming(Packet *pkt) { return peer->recvTiming(pkt); } + + /** Function called by the associated device to send an atomic + * access, an access in which the data is moved and the state is + * updated in one cycle, without interleaving with other memory + * accesses. Returns estimated latency of access. + */ + Tick sendAtomic(Packet *pkt) + { return peer->recvAtomic(pkt); } + + /** Function called by the associated device to send a functional access, + an access in which the data is instantly updated everywhere in the + memory system, without affecting the current state of any block or + moving the block. + */ + void sendFunctional(Packet *pkt) + { return peer->recvFunctional(pkt); } + + /** Called by the associated device to send a status change to the device + connected to the peer interface. + */ + void sendStatusChange(Status status) {peer->recvStatusChange(status); } + + /** When a timing access doesn't return a success, some time later the + Retry will be sent. + */ + void sendRetry() { return peer->recvRetry(); } + + /** Called by the associated device if it wishes to find out the blocksize + of the device on attached to the peer port. + */ + int peerBlockSize() { return peer->deviceBlockSize(); } + + /** Called by the associated device if it wishes to find out the address + ranges connected to the peer ports devices. + */ + void getPeerAddressRanges(AddrRangeList &resp, AddrRangeList &snoop) + { peer->getDeviceAddressRanges(resp, snoop); } + + /** This function is a wrapper around sendFunctional() + that breaks a larger, arbitrarily aligned access into + appropriate chunks. The default implementation can use + getBlockSize() to determine the block size and go from there. + */ + virtual void readBlob(Addr addr, uint8_t *p, int size); + + /** This function is a wrapper around sendFunctional() + that breaks a larger, arbitrarily aligned access into + appropriate chunks. The default implementation can use + getBlockSize() to determine the block size and go from there. + */ + virtual void writeBlob(Addr addr, uint8_t *p, int size); + + /** Fill size bytes starting at addr with byte value val. This + should not need to be virtual, since it can be implemented in + terms of writeBlob(). However, it shouldn't be + performance-critical either, so it could be if we wanted to. + */ + virtual void memsetBlob(Addr addr, uint8_t val, int size); + + private: + + /** Internal helper function for read/writeBlob(). + */ + void blobHelper(Addr addr, uint8_t *p, int size, Packet::Command cmd); +}; + +/** A simple functional port that is only meant for one way communication to + * physical memory. It is only meant to be used to load data into memory before + * the simulation begins. + */ + +class FunctionalPort : public Port +{ + public: + FunctionalPort(const std::string &_name) + : Port(_name) + {} + + protected: + virtual bool recvTiming(Packet *pkt) { panic("FuncPort is UniDir"); } + virtual Tick recvAtomic(Packet *pkt) { panic("FuncPort is UniDir"); } + virtual void recvFunctional(Packet *pkt) { panic("FuncPort is UniDir"); } + virtual void recvStatusChange(Status status) {} + + public: + /** a write function that also does an endian conversion. */ + template <typename T> + inline void writeHtoG(Addr addr, T d); + + /** a read function that also does an endian conversion. */ + template <typename T> + inline T readGtoH(Addr addr); + + template <typename T> + inline void write(Addr addr, T d) + { + writeBlob(addr, (uint8_t*)&d, sizeof(T)); + } + + template <typename T> + inline T read(Addr addr) + { + T d; + readBlob(addr, (uint8_t*)&d, sizeof(T)); + return d; + } +}; + +#endif //__MEM_PORT_HH__ diff --git a/src/mem/port_impl.hh b/src/mem/port_impl.hh new file mode 100644 index 000000000..b7980bdd2 --- /dev/null +++ b/src/mem/port_impl.hh @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#include "mem/port.hh" +#include "sim/byteswap.hh" + +template <typename T> +void +FunctionalPort::writeHtoG(Addr addr, T d) +{ + d = htog(d); + writeBlob(addr, (uint8_t*)&d, sizeof(T)); +} + + +template <typename T> +T +FunctionalPort::readGtoH(Addr addr) +{ + T d; + readBlob(addr, (uint8_t*)&d, sizeof(T)); + return gtoh(d); +} + diff --git a/src/mem/request.hh b/src/mem/request.hh new file mode 100644 index 000000000..6acd7526c --- /dev/null +++ b/src/mem/request.hh @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + * Steve Reinhardt + * Ali Saidi + */ + +/** + * @file + * Declaration of a request, the overall memory request consisting of + the parts of the request that are persistent throughout the transaction. + */ + +#ifndef __MEM_REQUEST_HH__ +#define __MEM_REQUEST_HH__ + +#include "sim/host.hh" +#include "sim/root.hh" + +#include <cassert> + +class Request; + +typedef Request* RequestPtr; + + +/** The request is a Load locked/store conditional. */ +const unsigned LOCKED = 0x001; +/** The virtual address is also the physical address. */ +const unsigned PHYSICAL = 0x002; +/** The request is an ALPHA VPTE pal access (hw_ld). */ +const unsigned VPTE = 0x004; +/** Use the alternate mode bits in ALPHA. */ +const unsigned ALTMODE = 0x008; +/** The request is to an uncacheable address. */ +const unsigned UNCACHEABLE = 0x010; +/** The request should not cause a page fault. */ +const unsigned NO_FAULT = 0x020; +/** The request should be prefetched into the exclusive state. */ +const unsigned PF_EXCLUSIVE = 0x100; +/** The request should be marked as LRU. */ +const unsigned EVICT_NEXT = 0x200; +/** The request should ignore unaligned access faults */ +const unsigned NO_ALIGN_FAULT = 0x400; +/** The request was an instruction read. */ +const unsigned INST_READ = 0x800; + +class Request +{ + private: + /** + * The physical address of the request. Valid only if validPaddr + * is set. */ + Addr paddr; + + /** + * The size of the request. This field must be set when vaddr or + * paddr is written via setVirt() or setPhys(), so it is always + * valid as long as one of the address fields is valid. */ + int size; + + /** Flag structure for the request. */ + uint32_t flags; + + /** + * The time this request was started. Used to calculate + * latencies. This field is set to curTick any time paddr or vaddr + * is written. */ + Tick time; + + /** The address space ID. */ + int asid; + /** The virtual address of the request. */ + Addr vaddr; + + /** The return value of store conditional. */ + uint64_t scResult; + + /** The cpu number (for statistics, typically). */ + int cpuNum; + /** The requesting thread id (for statistics, typically). */ + int threadNum; + + /** program counter of initiating access; for tracing/debugging */ + Addr pc; + + /** Whether or not paddr is valid (has been written yet). */ + bool validPaddr; + /** Whether or not the asid & vaddr are valid. */ + bool validAsidVaddr; + /** Whether or not the sc result is valid. */ + bool validScResult; + /** Whether or not the cpu number & thread ID are valid. */ + bool validCpuAndThreadNums; + /** Whether or not the pc is valid. */ + bool validPC; + + public: + /** Minimal constructor. No fields are initialized. */ + Request() + : validPaddr(false), validAsidVaddr(false), + validScResult(false), validCpuAndThreadNums(false), validPC(false) + {} + + /** + * Constructor for physical (e.g. device) requests. Initializes + * just physical address, size, flags, and timestamp (to curTick). + * These fields are adequate to perform a request. */ + Request(Addr _paddr, int _size, int _flags) + : validCpuAndThreadNums(false) + { setPhys(_paddr, _size, _flags); } + + Request(int _asid, Addr _vaddr, int _size, int _flags, Addr _pc, + int _cpuNum, int _threadNum) + { + setThreadContext(_cpuNum, _threadNum); + setVirt(_asid, _vaddr, _size, _flags, _pc); + } + + /** + * Set up CPU and thread numbers. */ + void setThreadContext(int _cpuNum, int _threadNum) + { + cpuNum = _cpuNum; + threadNum = _threadNum; + validCpuAndThreadNums = true; + } + + /** + * Set up a physical (e.g. device) request in a previously + * allocated Request object. */ + void setPhys(Addr _paddr, int _size, int _flags) + { + paddr = _paddr; + size = _size; + flags = _flags; + time = curTick; + validPaddr = true; + validAsidVaddr = false; + validPC = false; + validScResult = false; + } + + /** + * Set up a virtual (e.g., CPU) request in a previously + * allocated Request object. */ + void setVirt(int _asid, Addr _vaddr, int _size, int _flags, Addr _pc) + { + asid = _asid; + vaddr = _vaddr; + size = _size; + flags = _flags; + pc = _pc; + time = curTick; + validPaddr = false; + validAsidVaddr = true; + validPC = true; + validScResult = false; + } + + /** Set just the physical address. This should only be used to + * record the result of a translation, and thus the vaddr must be + * valid before this method is called. Otherwise, use setPhys() + * to guarantee that the size and flags are also set. + */ + void setPaddr(Addr _paddr) + { + assert(validAsidVaddr); + paddr = _paddr; + validPaddr = true; + } + + /** Accessor for paddr. */ + Addr getPaddr() { assert(validPaddr); return paddr; } + + /** Accessor for size. */ + int getSize() { assert(validPaddr || validAsidVaddr); return size; } + /** Accessor for time. */ + Tick getTime() { assert(validPaddr || validAsidVaddr); return time; } + + /** Accessor for flags. */ + uint32_t getFlags() { assert(validPaddr || validAsidVaddr); return flags; } + /** Accessor for paddr. */ + void setFlags(uint32_t _flags) + { assert(validPaddr || validAsidVaddr); flags = _flags; } + + /** Accessor function for vaddr.*/ + Addr getVaddr() { assert(validAsidVaddr); return vaddr; } + + /** Accessor function for asid.*/ + int getAsid() { assert(validAsidVaddr); return asid; } + + /** Accessor function to check if sc result is valid. */ + bool scResultValid() { return validScResult; } + /** Accessor function for store conditional return value.*/ + uint64_t getScResult() { assert(validScResult); return scResult; } + /** Accessor function for store conditional return value.*/ + void setScResult(uint64_t _scResult) + { scResult = _scResult; validScResult = true; } + + /** Accessor function for cpu number.*/ + int getCpuNum() { assert(validCpuAndThreadNums); return cpuNum; } + /** Accessor function for thread number.*/ + int getThreadNum() { assert(validCpuAndThreadNums); return threadNum; } + + /** Accessor function for pc.*/ + Addr getPC() { assert(validPC); return pc; } + + /** Accessor Function to Check Cacheability. */ + bool isUncacheable() { return getFlags() & UNCACHEABLE; } + + bool isInstRead() { return getFlags() & INST_READ; } + + friend class Packet; +}; + +#endif // __MEM_REQUEST_HH__ diff --git a/src/mem/tport.cc b/src/mem/tport.cc new file mode 100644 index 000000000..55c301c87 --- /dev/null +++ b/src/mem/tport.cc @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#include "mem/tport.hh" + +void +SimpleTimingPort::recvFunctional(Packet *pkt) +{ + // just do an atomic access and throw away the returned latency + recvAtomic(pkt); +} + +bool +SimpleTimingPort::recvTiming(Packet *pkt) +{ + // If the device is only a slave, it should only be sending + // responses, which should never get nacked. There used to be + // code to hanldle nacks here, but I'm pretty sure it didn't work + // correctly with the drain code, so that would need to be fixed + // if we ever added it back. + assert(pkt->result != Packet::Nacked); + Tick latency = recvAtomic(pkt); + // turn packet around to go back to requester + pkt->makeTimingResponse(); + sendTimingLater(pkt, latency); + return true; +} + +void +SimpleTimingPort::recvRetry() +{ + bool result = true; + while (result && transmitList.size()) { + result = sendTiming(transmitList.front()); + if (result) + transmitList.pop_front(); + } + if (transmitList.size() == 0 && drainEvent) { + drainEvent->process(); + drainEvent = NULL; + } +} + +void +SimpleTimingPort::SendEvent::process() +{ + port->outTiming--; + assert(port->outTiming >= 0); + if (port->sendTiming(packet)) { + // send successfule + if (port->transmitList.size() == 0 && port->drainEvent) { + port->drainEvent->process(); + port->drainEvent = NULL; + } + } else { + // send unsuccessful (due to flow control). Will get retry + // callback later; save for then. + port->transmitList.push_back(packet); + } +} + + +unsigned int +SimpleTimingPort::drain(Event *de) +{ + if (outTiming == 0 && transmitList.size() == 0) + return 0; + drainEvent = de; + return 1; +} diff --git a/src/mem/tport.hh b/src/mem/tport.hh new file mode 100644 index 000000000..df6d48196 --- /dev/null +++ b/src/mem/tport.hh @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +#ifndef __MEM_TPORT_HH__ +#define __MEM_TPORT_HH__ + +/** + * @file + * + * Declaration of SimpleTimingPort. + */ + +#include "mem/port.hh" +#include "sim/eventq.hh" +#include <list> +#include <string> + +/** + * A simple port for interfacing objects that basically have only + * functional memory behavior (e.g. I/O devices) to the memory system. + * Both timing and functional accesses are implemented in terms of + * atomic accesses. A derived port class thus only needs to provide + * recvAtomic() to support all memory access modes. + * + * The tricky part is handling recvTiming(), where the response must + * be scheduled separately via a later call to sendTiming(). This + * feature is handled by scheduling an internal event that calls + * sendTiming() after a delay, and optionally rescheduling the + * response if it is nacked. + */ +class SimpleTimingPort : public Port +{ + protected: + /** A list of outgoing timing response packets that haven't been + * serviced yet. */ + std::list<Packet*> transmitList; + + /** + * This class is used to implemented sendTiming() with a delay. When + * a delay is requested a new event is created. When the event time + * expires it attempts to send the packet. If it cannot, the packet + * is pushed onto the transmit list to be sent when recvRetry() is + * called. */ + class SendEvent : public Event + { + SimpleTimingPort *port; + Packet *packet; + + public: + SendEvent(SimpleTimingPort *p, Packet *pkt, Tick t) + : Event(&mainEventQueue), port(p), packet(pkt) + { setFlags(AutoDelete); schedule(curTick + t); } + + virtual void process(); + + virtual const char *description() + { return "Future scheduled sendTiming event"; } + }; + + + /** Number of timing requests that are emulating the device timing before + * attempting to end up on the bus. + */ + int outTiming; + + /** If we need to drain, keep the drain event around until we're done + * here.*/ + Event *drainEvent; + + /** Schedule a sendTiming() event to be called in the future. */ + void sendTimingLater(Packet *pkt, Tick time) + { outTiming++; new SendEvent(this, pkt, time); } + + /** This function is notification that the device should attempt to send a + * packet again. */ + virtual void recvRetry(); + + /** Implemented using recvAtomic(). */ + void recvFunctional(Packet *pkt); + + /** Implemented using recvAtomic(). */ + bool recvTiming(Packet *pkt); + + /** + * Simple ports generally don't care about any status + * changes... can always override this in cases where that's not + * true. */ + virtual void recvStatusChange(Status status) { } + + + public: + + SimpleTimingPort(std::string pname) + : Port(pname), outTiming(0), drainEvent(NULL) + {} + + /** Hook for draining timing accesses from the system. The + * associated SimObject's drain() functions should be implemented + * something like this when this class is used: + \code + PioDevice::drain(Event *de) + { + unsigned int count; + count = SimpleTimingPort->drain(de); + if (count) + changeState(Draining); + else + changeState(Drained); + return count; + } + \endcode + */ + unsigned int drain(Event *de); +}; + +#endif // __MEM_TPORT_HH__ diff --git a/src/mem/translating_port.cc b/src/mem/translating_port.cc new file mode 100644 index 000000000..d2c854086 --- /dev/null +++ b/src/mem/translating_port.cc @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + * Steve Reinhardt + */ + +#include <string> +#include "base/chunk_generator.hh" +#include "mem/port.hh" +#include "mem/translating_port.hh" +#include "mem/page_table.hh" + +using namespace TheISA; + +TranslatingPort::TranslatingPort(const std::string &_name, + PageTable *p_table, bool alloc) + : FunctionalPort(_name), pTable(p_table), allocating(alloc) +{ } + +TranslatingPort::~TranslatingPort() +{ } + +bool +TranslatingPort::tryReadBlob(Addr addr, uint8_t *p, int size) +{ + Addr paddr; + int prevSize = 0; + + for (ChunkGenerator gen(addr, size, VMPageSize); !gen.done(); gen.next()) { + + if (!pTable->translate(gen.addr(),paddr)) + return false; + + Port::readBlob(paddr, p + prevSize, gen.size()); + prevSize += gen.size(); + } + + return true; +} + +void +TranslatingPort::readBlob(Addr addr, uint8_t *p, int size) +{ + if (!tryReadBlob(addr, p, size)) + fatal("readBlob(0x%x, ...) failed", addr); +} + + +bool +TranslatingPort::tryWriteBlob(Addr addr, uint8_t *p, int size) +{ + + Addr paddr; + int prevSize = 0; + + for (ChunkGenerator gen(addr, size, VMPageSize); !gen.done(); gen.next()) { + + if (!pTable->translate(gen.addr(), paddr)) { + if (allocating) { + pTable->allocate(roundDown(gen.addr(), VMPageSize), + VMPageSize); + pTable->translate(gen.addr(), paddr); + } else { + return false; + } + } + + Port::writeBlob(paddr, p + prevSize, gen.size()); + prevSize += gen.size(); + } + + return true; +} + + +void +TranslatingPort::writeBlob(Addr addr, uint8_t *p, int size) +{ + if (!tryWriteBlob(addr, p, size)) + fatal("writeBlob(0x%x, ...) failed", addr); +} + +bool +TranslatingPort::tryMemsetBlob(Addr addr, uint8_t val, int size) +{ + Addr paddr; + + for (ChunkGenerator gen(addr, size, VMPageSize); !gen.done(); gen.next()) { + + if (!pTable->translate(gen.addr(), paddr)) { + if (allocating) { + pTable->allocate(roundDown(gen.addr(), VMPageSize), + VMPageSize); + pTable->translate(gen.addr(), paddr); + } else { + return false; + } + } + + Port::memsetBlob(paddr, val, gen.size()); + } + + return true; +} + +void +TranslatingPort::memsetBlob(Addr addr, uint8_t val, int size) +{ + if (!tryMemsetBlob(addr, val, size)) + fatal("memsetBlob(0x%x, ...) failed", addr); +} + + +bool +TranslatingPort::tryWriteString(Addr addr, const char *str) +{ + Addr paddr,vaddr; + uint8_t c; + + vaddr = addr; + + do { + c = *str++; + if (!pTable->translate(vaddr++,paddr)) + return false; + + Port::writeBlob(paddr, &c, 1); + } while (c); + + return true; +} + +void +TranslatingPort::writeString(Addr addr, const char *str) +{ + if (!tryWriteString(addr, str)) + fatal("writeString(0x%x, ...) failed", addr); +} + +bool +TranslatingPort::tryReadString(std::string &str, Addr addr) +{ + Addr paddr,vaddr; + uint8_t c; + + vaddr = addr; + + do { + if (!pTable->translate(vaddr++,paddr)) + return false; + + Port::readBlob(paddr, &c, 1); + str += c; + } while (c); + + return true; +} + +void +TranslatingPort::readString(std::string &str, Addr addr) +{ + if (!tryReadString(str, addr)) + fatal("readString(0x%x, ...) failed", addr); +} + diff --git a/src/mem/translating_port.hh b/src/mem/translating_port.hh new file mode 100644 index 000000000..7354278ba --- /dev/null +++ b/src/mem/translating_port.hh @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Ron Dreslinski + * Ali Saidi + */ + +#ifndef __MEM_TRANSLATING_PROT_HH__ +#define __MEM_TRANSLATING_PROT_HH__ + +#include "mem/port.hh" + +class PageTable; + +class TranslatingPort : public FunctionalPort +{ + private: + PageTable *pTable; + bool allocating; + + public: + TranslatingPort(const std::string &_name, + PageTable *p_table, bool alloc = false); + virtual ~TranslatingPort(); + + bool tryReadBlob(Addr addr, uint8_t *p, int size); + bool tryWriteBlob(Addr addr, uint8_t *p, int size); + bool tryMemsetBlob(Addr addr, uint8_t val, int size); + bool tryWriteString(Addr addr, const char *str); + bool tryReadString(std::string &str, Addr addr); + + virtual void readBlob(Addr addr, uint8_t *p, int size); + virtual void writeBlob(Addr addr, uint8_t *p, int size); + virtual void memsetBlob(Addr addr, uint8_t val, int size); + + void writeString(Addr addr, const char *str); + void readString(std::string &str, Addr addr); +}; + +#endif diff --git a/src/mem/vport.cc b/src/mem/vport.cc new file mode 100644 index 000000000..8030c5a15 --- /dev/null +++ b/src/mem/vport.cc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** + * @file + * Port object definitions. + */ + +#include "base/chunk_generator.hh" +#include "mem/vport.hh" + +void +VirtualPort::readBlob(Addr addr, uint8_t *p, int size) +{ + Addr paddr; + for (ChunkGenerator gen(addr, size, TheISA::PageBytes); !gen.done(); + gen.next()) + { + if (tc) + paddr = TheISA::vtophys(tc,gen.addr()); + else + paddr = TheISA::vtophys(gen.addr()); + + FunctionalPort::readBlob(paddr, p, gen.size()); + p += gen.size(); + } +} + +void +VirtualPort::writeBlob(Addr addr, uint8_t *p, int size) +{ + Addr paddr; + for (ChunkGenerator gen(addr, size, TheISA::PageBytes); !gen.done(); + gen.next()) + { + if (tc) + paddr = TheISA::vtophys(tc,gen.addr()); + else + paddr = TheISA::vtophys(gen.addr()); + + FunctionalPort::writeBlob(paddr, p, gen.size()); + p += gen.size(); + } +} + diff --git a/src/mem/vport.hh b/src/mem/vport.hh new file mode 100644 index 000000000..c83836258 --- /dev/null +++ b/src/mem/vport.hh @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2006 The Regents of The University of Michigan + * 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. + * + * Authors: Ali Saidi + */ + +/** + * @file + * Virtual Port Object Declaration. These ports incorporate some translation + * into their access methods. Thus you can use one to read and write data + * to/from virtual addresses. + */ + +#ifndef __MEM_VPORT_HH__ +#define __MEM_VPORT_HH__ + +#include "mem/port_impl.hh" +#include "config/full_system.hh" +#include "arch/vtophys.hh" + + +/** A class that translates a virtual address to a physical address and then + * calls the above read/write functions. If a thread context is provided the + * address can alway be translated, If not it can only be translated if it is a + * simple address masking operation (such as alpha super page accesses). + */ + +class VirtualPort : public FunctionalPort +{ + private: + ThreadContext *tc; + + public: + VirtualPort(const std::string &_name, ThreadContext *_tc = NULL) + : FunctionalPort(_name), tc(_tc) + {} + + /** Return true if we have an thread context. This is used to + * prevent someone from accidently deleting the cpus statically + * allocated vport. + * @return true if a thread context isn't valid + */ + bool nullThreadContext() { return tc != NULL; } + + /** Version of readblob that translates virt->phys and deals + * with page boundries. */ + virtual void readBlob(Addr addr, uint8_t *p, int size); + + /** Version of writeBlob that translates virt->phys and deals + * with page boundries. */ + virtual void writeBlob(Addr addr, uint8_t *p, int size); +}; + +#endif //__MEM_VPORT_HH__ + diff --git a/src/python/SConscript b/src/python/SConscript new file mode 100644 index 000000000..c9e713199 --- /dev/null +++ b/src/python/SConscript @@ -0,0 +1,117 @@ +# -*- mode:python -*- + +# Copyright (c) 2004-2005 The Regents of The University of Michigan +# 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. +# +# Authors: Steve Reinhardt +# Nathan Binkert + +import os, os.path, re, sys +from zipfile import PyZipFile + +# handy function for path joins +def join(*args): + return os.path.normpath(os.path.join(*args)) + +Import('env') + +# This SConscript is in charge of collecting .py files and generating +# a zip archive that is appended to the m5 binary. + +# List of files & directories to include in the zip file. To include +# a package, list only the root directory of the package, not any +# internal .py files (else they will get the path stripped off when +# they are imported into the zip file). +pyzip_files = [] + +# List of additional files on which the zip archive depends, but which +# are not included in pyzip_files... i.e. individual .py files within +# a package. +pyzip_dep_files = [] + +# Add the specified package to the zip archive. Adds the directory to +# pyzip_files and all included .py files to pyzip_dep_files. +def addPkg(pkgdir): + pyzip_files.append(pkgdir) + origdir = os.getcwd() + srcdir = join(Dir('.').srcnode().abspath, pkgdir) + os.chdir(srcdir) + for path, dirs, files in os.walk('.'): + for i,dir in enumerate(dirs): + if dir == 'SCCS': + del dirs[i] + break + + for f in files: + if f.endswith('.py'): + pyzip_dep_files.append(join(pkgdir, path, f)) + + os.chdir(origdir) + +# Generate Python file that contains a dict specifying the current +# build_env flags. +def MakeDefinesPyFile(target, source, env): + f = file(str(target[0]), 'w') + print >>f, "m5_build_env = ", source[0] + f.close() + +optionDict = dict([(opt, env[opt]) for opt in env.ExportOptions]) +env.Command('m5/defines.py', Value(optionDict), MakeDefinesPyFile) + +def MakeInfoPyFile(target, source, env): + f = file(str(target[0]), 'w') + for src in source: + data = ''.join(file(src.srcnode().abspath, 'r').xreadlines()) + print >>f, "%s = %s" % (src, repr(data)) + f.close() + +env.Command('m5/info.py', + [ '#/AUTHORS', '#/LICENSE', '#/README', '#/RELEASE_NOTES' ], + MakeInfoPyFile) + +# Now specify the packages & files for the zip archive. +addPkg('m5') +pyzip_files.append('m5/defines.py') +pyzip_files.append('m5/info.py') +pyzip_files.append(join(env['ROOT'], 'util/pbs/jobfile.py')) + +env.Command(['swig/cc_main_wrap.cc', 'm5/cc_main.py'], + 'swig/cc_main.i', + '$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} ' + '-o ${TARGETS[0]} $SOURCES') + +pyzip_dep_files.append('m5/cc_main.py') + +# Action function to build the zip archive. Uses the PyZipFile module +# included in the standard Python library. +def buildPyZip(target, source, env): + pzf = PyZipFile(str(target[0]), 'w') + for s in source: + pzf.writepy(str(s)) + +# Add the zip file target to the environment. +env.Command('m5py.zip', pyzip_files, buildPyZip) +env.Depends('m5py.zip', pyzip_dep_files) diff --git a/src/python/m5/SimObject.py b/src/python/m5/SimObject.py new file mode 100644 index 000000000..a0d66e643 --- /dev/null +++ b/src/python/m5/SimObject.py @@ -0,0 +1,798 @@ +# Copyright (c) 2004-2006 The Regents of The University of Michigan +# 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. +# +# Authors: Steve Reinhardt +# Nathan Binkert + +import sys, types + +from util import * +from multidict import multidict + +# These utility functions have to come first because they're +# referenced in params.py... otherwise they won't be defined when we +# import params below, and the recursive import of this file from +# params.py will not find these names. +def isSimObject(value): + return isinstance(value, SimObject) + +def isSimObjectClass(value): + return issubclass(value, SimObject) + +def isSimObjectSequence(value): + if not isinstance(value, (list, tuple)) or len(value) == 0: + return False + + for val in value: + if not isNullPointer(val) and not isSimObject(val): + return False + + return True + +def isSimObjectOrSequence(value): + return isSimObject(value) or isSimObjectSequence(value) + +# Have to import params up top since Param is referenced on initial +# load (when SimObject class references Param to create a class +# variable, the 'name' param)... +from params import * +# There are a few things we need that aren't in params.__all__ since +# normal users don't need them +from params import ParamDesc, isNullPointer, SimObjVector + +noDot = False +try: + import pydot +except: + noDot = True + +##################################################################### +# +# M5 Python Configuration Utility +# +# The basic idea is to write simple Python programs that build Python +# objects corresponding to M5 SimObjects for the desired simulation +# configuration. For now, the Python emits a .ini file that can be +# parsed by M5. In the future, some tighter integration between M5 +# and the Python interpreter may allow bypassing the .ini file. +# +# Each SimObject class in M5 is represented by a Python class with the +# same name. The Python inheritance tree mirrors the M5 C++ tree +# (e.g., SimpleCPU derives from BaseCPU in both cases, and all +# SimObjects inherit from a single SimObject base class). To specify +# an instance of an M5 SimObject in a configuration, the user simply +# instantiates the corresponding Python object. The parameters for +# that SimObject are given by assigning to attributes of the Python +# object, either using keyword assignment in the constructor or in +# separate assignment statements. For example: +# +# cache = BaseCache(size='64KB') +# cache.hit_latency = 3 +# cache.assoc = 8 +# +# The magic lies in the mapping of the Python attributes for SimObject +# classes to the actual SimObject parameter specifications. This +# allows parameter validity checking in the Python code. Continuing +# the example above, the statements "cache.blurfl=3" or +# "cache.assoc='hello'" would both result in runtime errors in Python, +# since the BaseCache object has no 'blurfl' parameter and the 'assoc' +# parameter requires an integer, respectively. This magic is done +# primarily by overriding the special __setattr__ method that controls +# assignment to object attributes. +# +# Once a set of Python objects have been instantiated in a hierarchy, +# calling 'instantiate(obj)' (where obj is the root of the hierarchy) +# will generate a .ini file. +# +##################################################################### + +# dict to look up SimObjects based on path +instanceDict = {} + +# The metaclass for SimObject. This class controls how new classes +# that derive from SimObject are instantiated, and provides inherited +# class behavior (just like a class controls how instances of that +# class are instantiated, and provides inherited instance behavior). +class MetaSimObject(type): + # Attributes that can be set only at initialization time + init_keywords = { 'abstract' : types.BooleanType, + 'type' : types.StringType } + # Attributes that can be set any time + keywords = { 'check' : types.FunctionType, + 'cxx_type' : types.StringType, + 'cxx_predecls' : types.ListType, + 'swig_predecls' : types.ListType } + + # __new__ is called before __init__, and is where the statements + # in the body of the class definition get loaded into the class's + # __dict__. We intercept this to filter out parameter & port assignments + # and only allow "private" attributes to be passed to the base + # __new__ (starting with underscore). + def __new__(mcls, name, bases, dict): + # Copy "private" attributes, functions, and classes to the + # official dict. Everything else goes in _init_dict to be + # filtered in __init__. + cls_dict = {} + value_dict = {} + for key,val in dict.items(): + if key.startswith('_') or isinstance(val, (types.FunctionType, + types.TypeType)): + cls_dict[key] = val + else: + # must be a param/port setting + value_dict[key] = val + cls_dict['_value_dict'] = value_dict + return super(MetaSimObject, mcls).__new__(mcls, name, bases, cls_dict) + + # subclass initialization + def __init__(cls, name, bases, dict): + # calls type.__init__()... I think that's a no-op, but leave + # it here just in case it's not. + super(MetaSimObject, cls).__init__(name, bases, dict) + + # initialize required attributes + + # class-only attributes + cls._params = multidict() # param descriptions + cls._ports = multidict() # port descriptions + + # class or instance attributes + cls._values = multidict() # param values + cls._port_refs = multidict() # port ref objects + cls._instantiated = False # really instantiated, cloned, or subclassed + + # We don't support multiple inheritance. If you want to, you + # must fix multidict to deal with it properly. + if len(bases) > 1: + raise TypeError, "SimObjects do not support multiple inheritance" + + base = bases[0] + + # Set up general inheritance via multidicts. A subclass will + # inherit all its settings from the base class. The only time + # the following is not true is when we define the SimObject + # class itself (in which case the multidicts have no parent). + if isinstance(base, MetaSimObject): + cls._params.parent = base._params + cls._ports.parent = base._ports + cls._values.parent = base._values + cls._port_refs.parent = base._port_refs + # mark base as having been subclassed + base._instantiated = True + + # Now process the _value_dict items. They could be defining + # new (or overriding existing) parameters or ports, setting + # class keywords (e.g., 'abstract'), or setting parameter + # values or port bindings. The first 3 can only be set when + # the class is defined, so we handle them here. The others + # can be set later too, so just emulate that by calling + # setattr(). + for key,val in cls._value_dict.items(): + # param descriptions + if isinstance(val, ParamDesc): + cls._new_param(key, val) + + # port objects + elif isinstance(val, Port): + cls._new_port(key, val) + + # init-time-only keywords + elif cls.init_keywords.has_key(key): + cls._set_keyword(key, val, cls.init_keywords[key]) + + # default: use normal path (ends up in __setattr__) + else: + setattr(cls, key, val) + + cls.cxx_type = cls.type + '*' + # A forward class declaration is sufficient since we are just + # declaring a pointer. + cls.cxx_predecls = ['class %s;' % cls.type] + cls.swig_predecls = cls.cxx_predecls + + def _set_keyword(cls, keyword, val, kwtype): + if not isinstance(val, kwtype): + raise TypeError, 'keyword %s has bad type %s (expecting %s)' % \ + (keyword, type(val), kwtype) + if isinstance(val, types.FunctionType): + val = classmethod(val) + type.__setattr__(cls, keyword, val) + + def _new_param(cls, name, pdesc): + # each param desc should be uniquely assigned to one variable + assert(not hasattr(pdesc, 'name')) + pdesc.name = name + cls._params[name] = pdesc + if hasattr(pdesc, 'default'): + cls._set_param(name, pdesc.default, pdesc) + + def _set_param(cls, name, value, param): + assert(param.name == name) + try: + cls._values[name] = param.convert(value) + except Exception, e: + msg = "%s\nError setting param %s.%s to %s\n" % \ + (e, cls.__name__, name, value) + e.args = (msg, ) + raise + + def _new_port(cls, name, port): + # each port should be uniquely assigned to one variable + assert(not hasattr(port, 'name')) + port.name = name + cls._ports[name] = port + if hasattr(port, 'default'): + cls._cls_get_port_ref(name).connect(port.default) + + # same as _get_port_ref, effectively, but for classes + def _cls_get_port_ref(cls, attr): + # Return reference that can be assigned to another port + # via __setattr__. There is only ever one reference + # object per port, but we create them lazily here. + ref = cls._port_refs.get(attr) + if not ref: + ref = cls._ports[attr].makeRef(cls) + cls._port_refs[attr] = ref + return ref + + # Set attribute (called on foo.attr = value when foo is an + # instance of class cls). + def __setattr__(cls, attr, value): + # normal processing for private attributes + if attr.startswith('_'): + type.__setattr__(cls, attr, value) + return + + if cls.keywords.has_key(attr): + cls._set_keyword(attr, value, cls.keywords[attr]) + return + + if cls._ports.has_key(attr): + cls._cls_get_port_ref(attr).connect(value) + return + + if isSimObjectOrSequence(value) and cls._instantiated: + raise RuntimeError, \ + "cannot set SimObject parameter '%s' after\n" \ + " class %s has been instantiated or subclassed" \ + % (attr, cls.__name__) + + # check for param + param = cls._params.get(attr) + if param: + cls._set_param(attr, value, param) + return + + if isSimObjectOrSequence(value): + # If RHS is a SimObject, it's an implicit child assignment. + # Classes don't have children, so we just put this object + # in _values; later, each instance will do a 'setattr(self, + # attr, _values[attr])' in SimObject.__init__ which will + # add this object as a child. + cls._values[attr] = value + return + + # no valid assignment... raise exception + raise AttributeError, \ + "Class %s has no parameter \'%s\'" % (cls.__name__, attr) + + def __getattr__(cls, attr): + if cls._values.has_key(attr): + return cls._values[attr] + + raise AttributeError, \ + "object '%s' has no attribute '%s'" % (cls.__name__, attr) + + def __str__(cls): + return cls.__name__ + + def cxx_decl(cls): + code = "#ifndef __PARAMS__%s\n#define __PARAMS__%s\n\n" % (cls, cls) + + if str(cls) != 'SimObject': + base = cls.__bases__[0].type + else: + base = None + + # The 'dict' attribute restricts us to the params declared in + # the object itself, not including inherited params (which + # will also be inherited from the base class's param struct + # here). + params = cls._params.dict.values() + try: + ptypes = [p.ptype for p in params] + except: + print cls, p, p.ptype_str + print params + raise + + # get a list of lists of predeclaration lines + predecls = [p.cxx_predecls() for p in params] + # flatten + predecls = reduce(lambda x,y:x+y, predecls, []) + # remove redundant lines + predecls2 = [] + for pd in predecls: + if pd not in predecls2: + predecls2.append(pd) + predecls2.sort() + code += "\n".join(predecls2) + code += "\n\n"; + + if base: + code += '#include "params/%s.hh"\n\n' % base + + # Generate declarations for locally defined enumerations. + enum_ptypes = [t for t in ptypes if issubclass(t, Enum)] + if enum_ptypes: + code += "\n".join([t.cxx_decl() for t in enum_ptypes]) + code += "\n\n" + + # now generate the actual param struct + code += "struct %sParams" % cls + if base: + code += " : public %sParams" % base + code += " {\n" + decls = [p.cxx_decl() for p in params] + decls.sort() + code += "".join([" %s\n" % d for d in decls]) + code += "};\n" + + # close #ifndef __PARAMS__* guard + code += "\n#endif\n" + return code + + def swig_decl(cls): + + code = '%%module %sParams\n' % cls + + if str(cls) != 'SimObject': + base = cls.__bases__[0].type + else: + base = None + + # The 'dict' attribute restricts us to the params declared in + # the object itself, not including inherited params (which + # will also be inherited from the base class's param struct + # here). + params = cls._params.dict.values() + ptypes = [p.ptype for p in params] + + # get a list of lists of predeclaration lines + predecls = [p.swig_predecls() for p in params] + # flatten + predecls = reduce(lambda x,y:x+y, predecls, []) + # remove redundant lines + predecls2 = [] + for pd in predecls: + if pd not in predecls2: + predecls2.append(pd) + predecls2.sort() + code += "\n".join(predecls2) + code += "\n\n"; + + if base: + code += '%%import "python/m5/swig/%sParams.i"\n\n' % base + + code += '%{\n' + code += '#include "params/%s.hh"\n' % cls + code += '%}\n\n' + code += '%%include "params/%s.hh"\n\n' % cls + + return code + +# The SimObject class is the root of the special hierarchy. Most of +# the code in this class deals with the configuration hierarchy itself +# (parent/child node relationships). +class SimObject(object): + # Specify metaclass. Any class inheriting from SimObject will + # get this metaclass. + __metaclass__ = MetaSimObject + type = 'SimObject' + + name = Param.String("Object name") + + # Initialize new instance. For objects with SimObject-valued + # children, we need to recursively clone the classes represented + # by those param values as well in a consistent "deep copy"-style + # fashion. That is, we want to make sure that each instance is + # cloned only once, and that if there are multiple references to + # the same original object, we end up with the corresponding + # cloned references all pointing to the same cloned instance. + def __init__(self, **kwargs): + ancestor = kwargs.get('_ancestor') + memo_dict = kwargs.get('_memo') + if memo_dict is None: + # prepare to memoize any recursively instantiated objects + memo_dict = {} + elif ancestor: + # memoize me now to avoid problems with recursive calls + memo_dict[ancestor] = self + + if not ancestor: + ancestor = self.__class__ + ancestor._instantiated = True + + # initialize required attributes + self._parent = None + self._children = {} + self._ccObject = None # pointer to C++ object + self._instantiated = False # really "cloned" + + # Inherit parameter values from class using multidict so + # individual value settings can be overridden. + self._values = multidict(ancestor._values) + # clone SimObject-valued parameters + for key,val in ancestor._values.iteritems(): + if isSimObject(val): + setattr(self, key, val(_memo=memo_dict)) + elif isSimObjectSequence(val) and len(val): + setattr(self, key, [ v(_memo=memo_dict) for v in val ]) + # clone port references. no need to use a multidict here + # since we will be creating new references for all ports. + self._port_refs = {} + for key,val in ancestor._port_refs.iteritems(): + self._port_refs[key] = val.clone(self, memo_dict) + # apply attribute assignments from keyword args, if any + for key,val in kwargs.iteritems(): + setattr(self, key, val) + + # "Clone" the current instance by creating another instance of + # this instance's class, but that inherits its parameter values + # and port mappings from the current instance. If we're in a + # "deep copy" recursive clone, check the _memo dict to see if + # we've already cloned this instance. + def __call__(self, **kwargs): + memo_dict = kwargs.get('_memo') + if memo_dict is None: + # no memo_dict: must be top-level clone operation. + # this is only allowed at the root of a hierarchy + if self._parent: + raise RuntimeError, "attempt to clone object %s " \ + "not at the root of a tree (parent = %s)" \ + % (self, self._parent) + # create a new dict and use that. + memo_dict = {} + kwargs['_memo'] = memo_dict + elif memo_dict.has_key(self): + # clone already done & memoized + return memo_dict[self] + return self.__class__(_ancestor = self, **kwargs) + + def _get_port_ref(self, attr): + # Return reference that can be assigned to another port + # via __setattr__. There is only ever one reference + # object per port, but we create them lazily here. + ref = self._port_refs.get(attr) + if not ref: + ref = self._ports[attr].makeRef(self) + self._port_refs[attr] = ref + return ref + + def __getattr__(self, attr): + if self._ports.has_key(attr): + return self._get_port_ref(attr) + + if self._values.has_key(attr): + return self._values[attr] + + raise AttributeError, "object '%s' has no attribute '%s'" \ + % (self.__class__.__name__, attr) + + # Set attribute (called on foo.attr = value when foo is an + # instance of class cls). + def __setattr__(self, attr, value): + # normal processing for private attributes + if attr.startswith('_'): + object.__setattr__(self, attr, value) + return + + if self._ports.has_key(attr): + # set up port connection + self._get_port_ref(attr).connect(value) + return + + if isSimObjectOrSequence(value) and self._instantiated: + raise RuntimeError, \ + "cannot set SimObject parameter '%s' after\n" \ + " instance been cloned %s" % (attr, `self`) + + # must be SimObject param + param = self._params.get(attr) + if param: + try: + value = param.convert(value) + except Exception, e: + msg = "%s\nError setting param %s.%s to %s\n" % \ + (e, self.__class__.__name__, attr, value) + e.args = (msg, ) + raise + self._set_child(attr, value) + return + + if isSimObjectOrSequence(value): + self._set_child(attr, value) + return + + # no valid assignment... raise exception + raise AttributeError, "Class %s has no parameter %s" \ + % (self.__class__.__name__, attr) + + + # this hack allows tacking a '[0]' onto parameters that may or may + # not be vectors, and always getting the first element (e.g. cpus) + def __getitem__(self, key): + if key == 0: + return self + raise TypeError, "Non-zero index '%s' to SimObject" % key + + # clear out children with given name, even if it's a vector + def clear_child(self, name): + if not self._children.has_key(name): + return + child = self._children[name] + if isinstance(child, SimObjVector): + for i in xrange(len(child)): + del self._children["s%d" % (name, i)] + del self._children[name] + + def add_child(self, name, value): + self._children[name] = value + + def _maybe_set_parent(self, parent, name): + if not self._parent: + self._parent = parent + self._name = name + parent.add_child(name, self) + + def _set_child(self, attr, value): + # if RHS is a SimObject, it's an implicit child assignment + # clear out old child with this name, if any + self.clear_child(attr) + + if isSimObject(value): + value._maybe_set_parent(self, attr) + elif isSimObjectSequence(value): + value = SimObjVector(value) + [v._maybe_set_parent(self, "%s%d" % (attr, i)) + for i,v in enumerate(value)] + + self._values[attr] = value + + def path(self): + if not self._parent: + return 'root' + ppath = self._parent.path() + if ppath == 'root': + return self._name + return ppath + "." + self._name + + def __str__(self): + return self.path() + + def ini_str(self): + return self.path() + + def find_any(self, ptype): + if isinstance(self, ptype): + return self, True + + found_obj = None + for child in self._children.itervalues(): + if isinstance(child, ptype): + if found_obj != None and child != found_obj: + raise AttributeError, \ + 'parent.any matched more than one: %s %s' % \ + (found_obj.path, child.path) + found_obj = child + # search param space + for pname,pdesc in self._params.iteritems(): + if issubclass(pdesc.ptype, ptype): + match_obj = self._values[pname] + if found_obj != None and found_obj != match_obj: + raise AttributeError, \ + 'parent.any matched more than one: %s' % obj.path + found_obj = match_obj + return found_obj, found_obj != None + + def unproxy(self, base): + return self + + def unproxy_all(self): + for param in self._params.iterkeys(): + value = self._values.get(param) + if value != None and proxy.isproxy(value): + try: + value = value.unproxy(self) + except: + print "Error in unproxying param '%s' of %s" % \ + (param, self.path()) + raise + setattr(self, param, value) + + # Unproxy ports in sorted order so that 'append' operations on + # vector ports are done in a deterministic fashion. + port_names = self._ports.keys() + port_names.sort() + for port_name in port_names: + port = self._port_refs.get(port_name) + if port != None: + port.unproxy(self) + + # Unproxy children in sorted order for determinism also. + child_names = self._children.keys() + child_names.sort() + for child in child_names: + self._children[child].unproxy_all() + + def print_ini(self): + print '[' + self.path() + ']' # .ini section header + + instanceDict[self.path()] = self + + if hasattr(self, 'type') and not isinstance(self, ParamContext): + print 'type=%s' % self.type + + child_names = self._children.keys() + child_names.sort() + np_child_names = [c for c in child_names \ + if not isinstance(self._children[c], ParamContext)] + if len(np_child_names): + print 'children=%s' % ' '.join(np_child_names) + + param_names = self._params.keys() + param_names.sort() + for param in param_names: + value = self._values.get(param) + if value != None: + print '%s=%s' % (param, self._values[param].ini_str()) + + port_names = self._ports.keys() + port_names.sort() + for port_name in port_names: + port = self._port_refs.get(port_name, None) + if port != None: + print '%s=%s' % (port_name, port.ini_str()) + + print # blank line between objects + + for child in child_names: + self._children[child].print_ini() + + # Call C++ to create C++ object corresponding to this object and + # (recursively) all its children + def createCCObject(self): + self.getCCObject() # force creation + for child in self._children.itervalues(): + child.createCCObject() + + # Get C++ object corresponding to this object, calling C++ if + # necessary to construct it. Does *not* recursively create + # children. + def getCCObject(self): + if not self._ccObject: + self._ccObject = -1 # flag to catch cycles in recursion + self._ccObject = cc_main.createSimObject(self.path()) + elif self._ccObject == -1: + raise RuntimeError, "%s: recursive call to getCCObject()" \ + % self.path() + return self._ccObject + + # Create C++ port connections corresponding to the connections in + # _port_refs (& recursively for all children) + def connectPorts(self): + for portRef in self._port_refs.itervalues(): + portRef.ccConnect() + for child in self._children.itervalues(): + child.connectPorts() + + def startDrain(self, drain_event, recursive): + count = 0 + # ParamContexts don't serialize + if isinstance(self, SimObject) and not isinstance(self, ParamContext): + count += self._ccObject.drain(drain_event) + if recursive: + for child in self._children.itervalues(): + count += child.startDrain(drain_event, True) + return count + + def resume(self): + if isinstance(self, SimObject) and not isinstance(self, ParamContext): + self._ccObject.resume() + for child in self._children.itervalues(): + child.resume() + + def changeTiming(self, mode): + if isinstance(self, System): + self._ccObject.setMemoryMode(mode) + for child in self._children.itervalues(): + child.changeTiming(mode) + + def takeOverFrom(self, old_cpu): + cpu_ptr = cc_main.convertToBaseCPUPtr(old_cpu._ccObject) + self._ccObject.takeOverFrom(cpu_ptr) + + # generate output file for 'dot' to display as a pretty graph. + # this code is currently broken. + def outputDot(self, dot): + label = "{%s|" % self.path + if isSimObject(self.realtype): + label += '%s|' % self.type + + if self.children: + # instantiate children in same order they were added for + # backward compatibility (else we can end up with cpu1 + # before cpu0). + for c in self.children: + dot.add_edge(pydot.Edge(self.path,c.path, style="bold")) + + simobjs = [] + for param in self.params: + try: + if param.value is None: + raise AttributeError, 'Parameter with no value' + + value = param.value + string = param.string(value) + except Exception, e: + msg = 'exception in %s:%s\n%s' % (self.name, param.name, e) + e.args = (msg, ) + raise + + if isSimObject(param.ptype) and string != "Null": + simobjs.append(string) + else: + label += '%s = %s\\n' % (param.name, string) + + for so in simobjs: + label += "|<%s> %s" % (so, so) + dot.add_edge(pydot.Edge("%s:%s" % (self.path, so), so, + tailport="w")) + label += '}' + dot.add_node(pydot.Node(self.path,shape="Mrecord",label=label)) + + # recursively dump out children + for c in self.children: + c.outputDot(dot) + +class ParamContext(SimObject): + pass + +# Function to provide to C++ so it can look up instances based on paths +def resolveSimObject(name): + obj = instanceDict[name] + return obj.getCCObject() + +# __all__ defines the list of symbols that get exported when +# 'from config import *' is invoked. Try to keep this reasonably +# short to avoid polluting other namespaces. +__all__ = ['SimObject', 'ParamContext'] + + +# see comment on imports at end of __init__.py. +import proxy +import cc_main +import m5 diff --git a/src/python/m5/__init__.py b/src/python/m5/__init__.py new file mode 100644 index 000000000..5717b49b6 --- /dev/null +++ b/src/python/m5/__init__.py @@ -0,0 +1,212 @@ +# Copyright (c) 2005 The Regents of The University of Michigan +# 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. +# +# Authors: Nathan Binkert +# Steve Reinhardt + +import atexit, os, sys + +# import the SWIG-wrapped main C++ functions +import cc_main +# import a few SWIG-wrapped items (those that are likely to be used +# directly by user scripts) completely into this module for +# convenience +from cc_main import simulate, SimLoopExitEvent + +# import the m5 compile options +import defines + +# define this here so we can use it right away if necessary +def panic(string): + print >>sys.stderr, 'panic:', string + sys.exit(1) + +# force scalars to one-element lists for uniformity +def makeList(objOrList): + if isinstance(objOrList, list): + return objOrList + return [objOrList] + +# Prepend given directory to system module search path. We may not +# need this anymore if we can structure our config library more like a +# Python package. +def AddToPath(path): + # if it's a relative path and we know what directory the current + # python script is in, make the path relative to that directory. + if not os.path.isabs(path) and sys.path[0]: + path = os.path.join(sys.path[0], path) + path = os.path.realpath(path) + # sys.path[0] should always refer to the current script's directory, + # so place the new dir right after that. + sys.path.insert(1, path) + +# make a SmartDict out of the build options for our local use +import smartdict +build_env = smartdict.SmartDict() +build_env.update(defines.m5_build_env) + +# make a SmartDict out of the OS environment too +env = smartdict.SmartDict() +env.update(os.environ) + +# The final hook to generate .ini files. Called from the user script +# once the config is built. +def instantiate(root): + params.ticks_per_sec = float(root.clock.frequency) + root.unproxy_all() + # ugly temporary hack to get output to config.ini + sys.stdout = file(os.path.join(options.outdir, 'config.ini'), 'w') + root.print_ini() + sys.stdout.close() # close config.ini + sys.stdout = sys.__stdout__ # restore to original + cc_main.loadIniFile(resolveSimObject) # load config.ini into C++ + root.createCCObject() + root.connectPorts() + cc_main.finalInit() + noDot = True # temporary until we fix dot + if not noDot: + dot = pydot.Dot() + instance.outputDot(dot) + dot.orientation = "portrait" + dot.size = "8.5,11" + dot.ranksep="equally" + dot.rank="samerank" + dot.write("config.dot") + dot.write_ps("config.ps") + +# Export curTick to user script. +def curTick(): + return cc_main.cvar.curTick + +# register our C++ exit callback function with Python +atexit.register(cc_main.doExitCleanup) + +# This loops until all objects have been fully drained. +def doDrain(root): + all_drained = drain(root) + while (not all_drained): + all_drained = drain(root) + +# Tries to drain all objects. Draining might not be completed unless +# all objects return that they are drained on the first call. This is +# because as objects drain they may cause other objects to no longer +# be drained. +def drain(root): + all_drained = False + drain_event = cc_main.createCountedDrain() + unready_objects = root.startDrain(drain_event, True) + # If we've got some objects that can't drain immediately, then simulate + if unready_objects > 0: + drain_event.setCount(unready_objects) + simulate() + else: + all_drained = True + cc_main.cleanupCountedDrain(drain_event) + return all_drained + +def resume(root): + root.resume() + +def checkpoint(root, dir): + if not isinstance(root, objects.Root): + raise TypeError, "Object is not a root object. Checkpoint must be called on a root object." + doDrain(root) + print "Writing checkpoint" + cc_main.serializeAll(dir) + resume(root) + +def restoreCheckpoint(root, dir): + print "Restoring from checkpoint" + cc_main.unserializeAll(dir) + resume(root) + +def changeToAtomic(system): + if not isinstance(system, objects.Root) and not isinstance(system, System): + raise TypeError, "Object is not a root or system object. Checkpoint must be " + "called on a root object." + doDrain(system) + print "Changing memory mode to atomic" + system.changeTiming(cc_main.SimObject.Atomic) + resume(system) + +def changeToTiming(system): + if not isinstance(system, objects.Root) and not isinstance(system, System): + raise TypeError, "Object is not a root or system object. Checkpoint must be " + "called on a root object." + doDrain(system) + print "Changing memory mode to timing" + system.changeTiming(cc_main.SimObject.Timing) + resume(system) + +def switchCpus(cpuList): + if not isinstance(cpuList, list): + raise RuntimeError, "Must pass a list to this function" + for i in cpuList: + if not isinstance(i, tuple): + raise RuntimeError, "List must have tuples of (oldCPU,newCPU)" + + [old_cpus, new_cpus] = zip(*cpuList) + + for cpu in old_cpus: + if not isinstance(cpu, objects.BaseCPU): + raise TypeError, "%s is not of type BaseCPU", cpu + for cpu in new_cpus: + if not isinstance(cpu, objects.BaseCPU): + raise TypeError, "%s is not of type BaseCPU", cpu + + # Drain all of the individual CPUs + drain_event = cc_main.createCountedDrain() + unready_cpus = 0 + for old_cpu in old_cpus: + unready_cpus += old_cpu.startDrain(drain_event, False) + # If we've got some objects that can't drain immediately, then simulate + if unready_cpus > 0: + drain_event.setCount(unready_cpus) + simulate() + cc_main.cleanupCountedDrain(drain_event) + # Now all of the CPUs are ready to be switched out + for old_cpu in old_cpus: + old_cpu._ccObject.switchOut() + index = 0 + print "Switching CPUs" + for new_cpu in new_cpus: + new_cpu.takeOverFrom(old_cpus[index]) + new_cpu._ccObject.resume() + index += 1 + +# Since we have so many mutual imports in this package, we should: +# 1. Put all intra-package imports at the *bottom* of the file, unless +# they're absolutely needed before that (for top-level statements +# or class attributes). Imports of "trivial" packages that don't +# import other packages (e.g., 'smartdict') can be at the top. +# 2. Never use 'from foo import *' on an intra-package import since +# you can get the wrong result if foo is only partially imported +# at the point you do that (i.e., because foo is in the middle of +# importing *you*). +from main import options +import objects +import params +from SimObject import resolveSimObject diff --git a/src/python/m5/attrdict.py b/src/python/m5/attrdict.py new file mode 100644 index 000000000..4ee7f1b8c --- /dev/null +++ b/src/python/m5/attrdict.py @@ -0,0 +1,61 @@ +# Copyright (c) 2006 The Regents of The University of Michigan +# 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. +# +# Authors: Nathan Binkert + +__all__ = [ 'attrdict' ] + +class attrdict(dict): + def __getattr__(self, attr): + if attr in self: + return self.__getitem__(attr) + return super(attrdict, self).__getattribute__(attr) + + def __setattr__(self, attr, value): + if attr in dir(self): + return super(attrdict, self).__setattr__(attr, value) + return self.__setitem__(attr, value) + + def __delattr__(self, attr): + if attr in self: + return self.__delitem__(attr) + return super(attrdict, self).__delattr__(attr, value) + +if __name__ == '__main__': + x = attrdict() + x.y = 1 + x['z'] = 2 + print x['y'], x.y + print x['z'], x.z + print dir(x) + print x + + print + + del x['y'] + del x.z + print dir(x) + print(x) diff --git a/src/python/m5/convert.py b/src/python/m5/convert.py new file mode 100644 index 000000000..580a579bc --- /dev/null +++ b/src/python/m5/convert.py @@ -0,0 +1,229 @@ +# Copyright (c) 2005 The Regents of The University of Michigan +# 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. +# +# Authors: Nathan Binkert + +# metric prefixes +exa = 1.0e18 +peta = 1.0e15 +tera = 1.0e12 +giga = 1.0e9 +mega = 1.0e6 +kilo = 1.0e3 + +milli = 1.0e-3 +micro = 1.0e-6 +nano = 1.0e-9 +pico = 1.0e-12 +femto = 1.0e-15 +atto = 1.0e-18 + +# power of 2 prefixes +kibi = 1024 +mebi = kibi * 1024 +gibi = mebi * 1024 +tebi = gibi * 1024 +pebi = tebi * 1024 +exbi = pebi * 1024 + +# memory size configuration stuff +def toFloat(value): + if not isinstance(value, str): + raise TypeError, "wrong type '%s' should be str" % type(value) + + if value.endswith('Ei'): + return float(value[:-2]) * exbi + elif value.endswith('Pi'): + return float(value[:-2]) * pebi + elif value.endswith('Ti'): + return float(value[:-2]) * tebi + elif value.endswith('Gi'): + return float(value[:-2]) * gibi + elif value.endswith('Mi'): + return float(value[:-2]) * mebi + elif value.endswith('ki'): + return float(value[:-2]) * kibi + elif value.endswith('E'): + return float(value[:-1]) * exa + elif value.endswith('P'): + return float(value[:-1]) * peta + elif value.endswith('T'): + return float(value[:-1]) * tera + elif value.endswith('G'): + return float(value[:-1]) * giga + elif value.endswith('M'): + return float(value[:-1]) * mega + elif value.endswith('k'): + return float(value[:-1]) * kilo + elif value.endswith('m'): + return float(value[:-1]) * milli + elif value.endswith('u'): + return float(value[:-1]) * micro + elif value.endswith('n'): + return float(value[:-1]) * nano + elif value.endswith('p'): + return float(value[:-1]) * pico + elif value.endswith('f'): + return float(value[:-1]) * femto + else: + return float(value) + +def toInteger(value): + value = toFloat(value) + result = long(value) + if value != result: + raise ValueError, "cannot convert '%s' to integer" % value + + return result + +_bool_dict = { + 'true' : True, 't' : True, 'yes' : True, 'y' : True, '1' : True, + 'false' : False, 'f' : False, 'no' : False, 'n' : False, '0' : False + } + +def toBool(value): + if not isinstance(value, str): + raise TypeError, "wrong type '%s' should be str" % type(value) + + value = value.lower() + result = _bool_dict.get(value, None) + if result == None: + raise ValueError, "cannot convert '%s' to bool" % value + return result + +def toFrequency(value): + if not isinstance(value, str): + raise TypeError, "wrong type '%s' should be str" % type(value) + + if value.endswith('THz'): + return float(value[:-3]) * tera + elif value.endswith('GHz'): + return float(value[:-3]) * giga + elif value.endswith('MHz'): + return float(value[:-3]) * mega + elif value.endswith('kHz'): + return float(value[:-3]) * kilo + elif value.endswith('Hz'): + return float(value[:-2]) + + raise ValueError, "cannot convert '%s' to frequency" % value + +def toLatency(value): + if not isinstance(value, str): + raise TypeError, "wrong type '%s' should be str" % type(value) + + if value.endswith('ps'): + return float(value[:-2]) * pico + elif value.endswith('ns'): + return float(value[:-2]) * nano + elif value.endswith('us'): + return float(value[:-2]) * micro + elif value.endswith('ms'): + return float(value[:-2]) * milli + elif value.endswith('s'): + return float(value[:-1]) + + raise ValueError, "cannot convert '%s' to latency" % value + +def toClockPeriod(value): + """result is a clock period""" + + if not isinstance(value, str): + raise TypeError, "wrong type '%s' should be str" % type(value) + + try: + val = toFrequency(value) + if val != 0: + val = 1 / val + return val + except ValueError: + pass + + try: + val = toLatency(value) + return val + except ValueError: + pass + + raise ValueError, "cannot convert '%s' to clock period" % value + + +def toNetworkBandwidth(value): + if not isinstance(value, str): + raise TypeError, "wrong type '%s' should be str" % type(value) + + if value.endswith('Tbps'): + return float(value[:-4]) * tera + elif value.endswith('Gbps'): + return float(value[:-4]) * giga + elif value.endswith('Mbps'): + return float(value[:-4]) * mega + elif value.endswith('kbps'): + return float(value[:-4]) * kilo + elif value.endswith('bps'): + return float(value[:-3]) + else: + return float(value) + + raise ValueError, "cannot convert '%s' to network bandwidth" % value + +def toMemoryBandwidth(value): + if not isinstance(value, str): + raise TypeError, "wrong type '%s' should be str" % type(value) + + if value.endswith('PB/s'): + return float(value[:-4]) * pebi + elif value.endswith('TB/s'): + return float(value[:-4]) * tebi + elif value.endswith('GB/s'): + return float(value[:-4]) * gibi + elif value.endswith('MB/s'): + return float(value[:-4]) * mebi + elif value.endswith('kB/s'): + return float(value[:-4]) * kibi + elif value.endswith('B/s'): + return float(value[:-3]) + + raise ValueError, "cannot convert '%s' to memory bandwidth" % value + +def toMemorySize(value): + if not isinstance(value, str): + raise TypeError, "wrong type '%s' should be str" % type(value) + + if value.endswith('PB'): + return long(value[:-2]) * pebi + elif value.endswith('TB'): + return long(value[:-2]) * tebi + elif value.endswith('GB'): + return long(value[:-2]) * gibi + elif value.endswith('MB'): + return long(value[:-2]) * mebi + elif value.endswith('kB'): + return long(value[:-2]) * kibi + elif value.endswith('B'): + return long(value[:-1]) + + raise ValueError, "cannot convert '%s' to memory size" % value diff --git a/src/python/m5/main.py b/src/python/m5/main.py new file mode 100644 index 000000000..e296453db --- /dev/null +++ b/src/python/m5/main.py @@ -0,0 +1,330 @@ +# Copyright (c) 2005 The Regents of The University of Michigan +# 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. +# +# Authors: Nathan Binkert + +import code, optparse, os, socket, sys +from datetime import datetime +from attrdict import attrdict + +try: + import info +except ImportError: + info = None + +__all__ = [ 'options', 'arguments', 'main' ] + +usage="%prog [m5 options] script.py [script options]" +version="%prog 2.0" +brief_copyright=''' +Copyright (c) 2001-2006 +The Regents of The University of Michigan +All Rights Reserved +''' + +# there's only one option parsing done, so make it global and add some +# helper functions to make it work well. +parser = optparse.OptionParser(usage=usage, version=version, + description=brief_copyright, + formatter=optparse.TitledHelpFormatter()) +parser.disable_interspersed_args() + +# current option group +group = None + +def set_group(*args, **kwargs): + '''set the current option group''' + global group + if not args and not kwargs: + group = None + else: + group = parser.add_option_group(*args, **kwargs) + +class splitter(object): + def __init__(self, split): + self.split = split + def __call__(self, option, opt_str, value, parser): + getattr(parser.values, option.dest).extend(value.split(self.split)) + +def add_option(*args, **kwargs): + '''add an option to the current option group, or global none set''' + + # if action=split, but allows the option arguments + # themselves to be lists separated by the split variable''' + + if kwargs.get('action', None) == 'append' and 'split' in kwargs: + split = kwargs.pop('split') + kwargs['default'] = [] + kwargs['type'] = 'string' + kwargs['action'] = 'callback' + kwargs['callback'] = splitter(split) + + if group: + return group.add_option(*args, **kwargs) + + return parser.add_option(*args, **kwargs) + +def bool_option(name, default, help): + '''add a boolean option called --name and --no-name. + Display help depending on which is the default''' + + tname = '--%s' % name + fname = '--no-%s' % name + dest = name.replace('-', '_') + if default: + thelp = optparse.SUPPRESS_HELP + fhelp = help + else: + thelp = help + fhelp = optparse.SUPPRESS_HELP + + add_option(tname, action="store_true", default=default, help=thelp) + add_option(fname, action="store_false", dest=dest, help=fhelp) + +# Help options +add_option('-A', "--authors", action="store_true", default=False, + help="Show author information") +add_option('-C', "--copyright", action="store_true", default=False, + help="Show full copyright information") +add_option('-R', "--readme", action="store_true", default=False, + help="Show the readme") +add_option('-N', "--release-notes", action="store_true", default=False, + help="Show the release notes") + +# Options for configuring the base simulator +add_option('-d', "--outdir", metavar="DIR", default=".", + help="Set the output directory to DIR [Default: %default]") +add_option('-i', "--interactive", action="store_true", default=False, + help="Invoke the interactive interpreter after running the script") +add_option("--pdb", action="store_true", default=False, + help="Invoke the python debugger before running the script") +add_option('-p', "--path", metavar="PATH[:PATH]", action='append', split=':', + help="Prepend PATH to the system path when invoking the script") +add_option('-q', "--quiet", action="count", default=0, + help="Reduce verbosity") +add_option('-v', "--verbose", action="count", default=0, + help="Increase verbosity") + +# Statistics options +set_group("Statistics Options") +add_option("--stats-file", metavar="FILE", default="m5stats.txt", + help="Sets the output file for statistics [Default: %default]") + +# Debugging options +set_group("Debugging Options") +add_option("--debug-break", metavar="TIME[,TIME]", action='append', split=',', + help="Cycle to create a breakpoint") + +# Tracing options +set_group("Trace Options") +add_option("--trace-flags", metavar="FLAG[,FLAG]", action='append', split=',', + help="Sets the flags for tracing") +add_option("--trace-start", metavar="TIME", default='0s', + help="Start tracing at TIME (must have units)") +add_option("--trace-file", metavar="FILE", default="cout", + help="Sets the output file for tracing [Default: %default]") +add_option("--trace-circlebuf", metavar="SIZE", type="int", default=0, + help="If SIZE is non-zero, turn on the circular buffer with SIZE lines") +add_option("--no-trace-circlebuf", action="store_const", const=0, + dest='trace_circlebuf', help=optparse.SUPPRESS_HELP) +bool_option("trace-dumponexit", default=False, + help="Dump trace buffer on exit") +add_option("--trace-ignore", metavar="EXPR", action='append', split=':', + help="Ignore EXPR sim objects") + +# Execution Trace options +set_group("Execution Trace Options") +bool_option("speculative", default=True, + help="Don't capture speculative instructions") +bool_option("print-cycle", default=True, + help="Don't print cycle numbers in trace output") +bool_option("print-symbol", default=True, + help="Disable PC symbols in trace output") +bool_option("print-opclass", default=True, + help="Don't print op class type in trace output") +bool_option("print-thread", default=True, + help="Don't print thread number in trace output") +bool_option("print-effaddr", default=True, + help="Don't print effective address in trace output") +bool_option("print-data", default=True, + help="Don't print result data in trace output") +bool_option("print-iregs", default=False, + help="Print fetch sequence numbers in trace output") +bool_option("print-fetch-seq", default=False, + help="Print fetch sequence numbers in trace output") +bool_option("print-cpseq", default=False, + help="Print correct path sequence numbers in trace output") +#bool_option("print-reg-delta", default=False, +# help="Print which registers changed to what in trace output") + +options = attrdict() +arguments = [] + +def usage(exitcode=None): + parser.print_help() + if exitcode is not None: + sys.exit(exitcode) + +def parse_args(): + _opts,args = parser.parse_args() + opts = attrdict(_opts.__dict__) + + # setting verbose and quiet at the same time doesn't make sense + if opts.verbose > 0 and opts.quiet > 0: + usage(2) + + # store the verbosity in a single variable. 0 is default, + # negative numbers represent quiet and positive values indicate verbose + opts.verbose -= opts.quiet + + del opts.quiet + + options.update(opts) + arguments.extend(args) + return opts,args + +def main(): + import cc_main + + parse_args() + + done = False + if options.copyright: + done = True + print info.LICENSE + print + + if options.authors: + done = True + print 'Author information:' + print + print info.AUTHORS + print + + if options.readme: + done = True + print 'Readme:' + print + print info.README + print + + if options.release_notes: + done = True + print 'Release Notes:' + print + print info.RELEASE_NOTES + print + + if done: + sys.exit(0) + + if options.verbose >= 0: + print "M5 Simulator System" + print brief_copyright + print + print "M5 compiled %s" % cc_main.cvar.compileDate; + print "M5 started %s" % datetime.now().ctime() + print "M5 executing on %s" % socket.gethostname() + print "command line:", + for argv in sys.argv: + print argv, + print + + # check to make sure we can find the listed script + if not arguments or not os.path.isfile(arguments[0]): + if arguments and not os.path.isfile(arguments[0]): + print "Script %s not found" % arguments[0] + usage(2) + + # tell C++ about output directory + cc_main.setOutputDir(options.outdir) + + # update the system path with elements from the -p option + sys.path[0:0] = options.path + + import objects + + # set stats options + objects.Statistics.text_file = options.stats_file + + # set debugging options + objects.Debug.break_cycles = options.debug_break + + # set tracing options + objects.Trace.flags = options.trace_flags + objects.Trace.start = options.trace_start + objects.Trace.file = options.trace_file + objects.Trace.bufsize = options.trace_circlebuf + objects.Trace.dump_on_exit = options.trace_dumponexit + objects.Trace.ignore = options.trace_ignore + + # set execution trace options + objects.ExecutionTrace.speculative = options.speculative + objects.ExecutionTrace.print_cycle = options.print_cycle + objects.ExecutionTrace.pc_symbol = options.print_symbol + objects.ExecutionTrace.print_opclass = options.print_opclass + objects.ExecutionTrace.print_thread = options.print_thread + objects.ExecutionTrace.print_effaddr = options.print_effaddr + objects.ExecutionTrace.print_data = options.print_data + objects.ExecutionTrace.print_iregs = options.print_iregs + objects.ExecutionTrace.print_fetchseq = options.print_fetch_seq + objects.ExecutionTrace.print_cpseq = options.print_cpseq + #objects.ExecutionTrace.print_reg_delta = options.print_reg_delta + + sys.argv = arguments + sys.path = [ os.path.dirname(sys.argv[0]) ] + sys.path + + scope = { '__file__' : sys.argv[0] } + + # we want readline if we're doing anything interactive + if options.interactive or options.pdb: + exec("import readline", scope) + + # if pdb was requested, execfile the thing under pdb, otherwise, + # just do the execfile normally + if options.pdb: + from pdb import Pdb + debugger = Pdb() + debugger.run('execfile("%s")' % sys.argv[0], scope) + else: + execfile(sys.argv[0], scope) + + # once the script is done + if options.interactive: + interact = code.InteractiveConsole(scope) + interact.interact("M5 Interactive Console") + +if __name__ == '__main__': + from pprint import pprint + + parse_args() + + print 'opts:' + pprint(options, indent=4) + print + + print 'args:' + pprint(arguments, indent=4) diff --git a/src/python/m5/multidict.py b/src/python/m5/multidict.py new file mode 100644 index 000000000..b5cd700ef --- /dev/null +++ b/src/python/m5/multidict.py @@ -0,0 +1,182 @@ +# Copyright (c) 2005 The Regents of The University of Michigan +# 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. +# +# Authors: Nathan Binkert + +__all__ = [ 'multidict' ] + +class multidict(object): + def __init__(self, parent = {}, **kwargs): + self.local = dict(**kwargs) + self.parent = parent + self.deleted = {} + + def __str__(self): + return str(dict(self.items())) + + def __repr__(self): + return `dict(self.items())` + + def __contains__(self, key): + return self.local.has_key(key) or self.parent.has_key(key) + + def __delitem__(self, key): + try: + del self.local[key] + except KeyError, e: + if key in self.parent: + self.deleted[key] = True + else: + raise KeyError, e + + def __setitem__(self, key, value): + self.deleted.pop(key, False) + self.local[key] = value + + def __getitem__(self, key): + try: + return self.local[key] + except KeyError, e: + if not self.deleted.get(key, False) and key in self.parent: + return self.parent[key] + else: + raise KeyError, e + + def __len__(self): + return len(self.local) + len(self.parent) + + def next(self): + for key,value in self.local.items(): + yield key,value + + if self.parent: + for key,value in self.parent.next(): + if key not in self.local and key not in self.deleted: + yield key,value + + def has_key(self, key): + return key in self + + def iteritems(self): + for item in self.next(): + yield item + + def items(self): + return [ item for item in self.next() ] + + def iterkeys(self): + for key,value in self.next(): + yield key + + def keys(self): + return [ key for key,value in self.next() ] + + def itervalues(self): + for key,value in self.next(): + yield value + + def values(self): + return [ value for key,value in self.next() ] + + def get(self, key, default=None): + try: + return self[key] + except KeyError, e: + return default + + def setdefault(self, key, default): + try: + return self[key] + except KeyError: + self.deleted.pop(key, False) + self.local[key] = default + return default + + def _dump(self): + print 'multidict dump' + node = self + while isinstance(node, multidict): + print ' ', node.local + node = node.parent + + def _dumpkey(self, key): + values = [] + node = self + while isinstance(node, multidict): + if key in node.local: + values.append(node.local[key]) + node = node.parent + print key, values + +if __name__ == '__main__': + test1 = multidict() + test2 = multidict(test1) + test3 = multidict(test2) + test4 = multidict(test3) + + test1['a'] = 'test1_a' + test1['b'] = 'test1_b' + test1['c'] = 'test1_c' + test1['d'] = 'test1_d' + test1['e'] = 'test1_e' + + test2['a'] = 'test2_a' + del test2['b'] + test2['c'] = 'test2_c' + del test1['a'] + + test2.setdefault('f', multidict) + + print 'test1>', test1.items() + print 'test2>', test2.items() + #print test1['a'] + print test1['b'] + print test1['c'] + print test1['d'] + print test1['e'] + + print test2['a'] + #print test2['b'] + print test2['c'] + print test2['d'] + print test2['e'] + + for key in test2.iterkeys(): + print key + + test2.get('g', 'foo') + #test2.get('b') + test2.get('b', 'bar') + test2.setdefault('b', 'blah') + print test1 + print test2 + print `test2` + + print len(test2) + + test3['a'] = [ 0, 1, 2, 3 ] + + print test4 diff --git a/src/python/m5/objects/AlphaConsole.py b/src/python/m5/objects/AlphaConsole.py new file mode 100644 index 000000000..1c71493b1 --- /dev/null +++ b/src/python/m5/objects/AlphaConsole.py @@ -0,0 +1,10 @@ +from m5.params import * +from m5.proxy import * +from Device import BasicPioDevice + +class AlphaConsole(BasicPioDevice): + type = 'AlphaConsole' + cpu = Param.BaseCPU(Parent.any, "Processor") + disk = Param.SimpleDisk("Simple Disk") + sim_console = Param.SimConsole(Parent.any, "The Simulator Console") + system = Param.AlphaSystem(Parent.any, "system object") diff --git a/src/python/m5/objects/AlphaTLB.py b/src/python/m5/objects/AlphaTLB.py new file mode 100644 index 000000000..af7c04a84 --- /dev/null +++ b/src/python/m5/objects/AlphaTLB.py @@ -0,0 +1,14 @@ +from m5.SimObject import SimObject +from m5.params import * +class AlphaTLB(SimObject): + type = 'AlphaTLB' + abstract = True + size = Param.Int("TLB size") + +class AlphaDTB(AlphaTLB): + type = 'AlphaDTB' + size = 64 + +class AlphaITB(AlphaTLB): + type = 'AlphaITB' + size = 48 diff --git a/src/python/m5/objects/BadDevice.py b/src/python/m5/objects/BadDevice.py new file mode 100644 index 000000000..919623887 --- /dev/null +++ b/src/python/m5/objects/BadDevice.py @@ -0,0 +1,6 @@ +from m5.params import * +from Device import BasicPioDevice + +class BadDevice(BasicPioDevice): + type = 'BadDevice' + devicename = Param.String("Name of device to error on") diff --git a/src/python/m5/objects/BaseCPU.py b/src/python/m5/objects/BaseCPU.py new file mode 100644 index 000000000..05ccbca6a --- /dev/null +++ b/src/python/m5/objects/BaseCPU.py @@ -0,0 +1,59 @@ +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * +from m5 import build_env +from AlphaTLB import AlphaDTB, AlphaITB +from Bus import Bus + +class BaseCPU(SimObject): + type = 'BaseCPU' + abstract = True + mem = Param.MemObject("memory") + + system = Param.System(Parent.any, "system object") + if build_env['FULL_SYSTEM']: + dtb = Param.AlphaDTB(AlphaDTB(), "Data TLB") + itb = Param.AlphaITB(AlphaITB(), "Instruction TLB") + cpu_id = Param.Int(-1, "CPU identifier") + else: + workload = VectorParam.Process("processes to run") + + max_insts_all_threads = Param.Counter(0, + "terminate when all threads have reached this inst count") + max_insts_any_thread = Param.Counter(0, + "terminate when any thread reaches this inst count") + max_loads_all_threads = Param.Counter(0, + "terminate when all threads have reached this load count") + max_loads_any_thread = Param.Counter(0, + "terminate when any thread reaches this load count") + stats_reset_inst = Param.Counter(0, + "reset stats once this many instructions are committed") + progress_interval = Param.Tick(0, "interval to print out the progress message") + + defer_registration = Param.Bool(False, + "defer registration with system (for sampling)") + + clock = Param.Clock(Parent.clock, "clock speed") + + _mem_ports = [] + + def connectMemPorts(self, bus): + for p in self._mem_ports: + exec('self.%s = bus.port' % p) + + def addPrivateSplitL1Caches(self, ic, dc): + assert(len(self._mem_ports) == 2) + self.icache = ic + self.dcache = dc + self.icache_port = ic.cpu_side + self.dcache_port = dc.cpu_side + self._mem_ports = ['icache.mem_side', 'dcache.mem_side'] +# self.mem = dc + + def addTwoLevelCacheHierarchy(self, ic, dc, l2c): + self.addPrivateSplitL1Caches(ic, dc) + self.toL2Bus = Bus() + self.connectMemPorts(self.toL2Bus) + self.l2cache = l2c + self.l2cache.cpu_side = self.toL2Bus.port + self._mem_ports = ['l2cache.mem_side'] diff --git a/src/python/m5/objects/BaseCache.py b/src/python/m5/objects/BaseCache.py new file mode 100644 index 000000000..db58a177f --- /dev/null +++ b/src/python/m5/objects/BaseCache.py @@ -0,0 +1,65 @@ +from m5.params import * +from MemObject import MemObject + +class Prefetch(Enum): vals = ['none', 'tagged', 'stride', 'ghb'] + +class BaseCache(MemObject): + type = 'BaseCache' + adaptive_compression = Param.Bool(False, + "Use an adaptive compression scheme") + assoc = Param.Int("associativity") + block_size = Param.Int("block size in bytes") + latency = Param.Int("Latency") + compressed_bus = Param.Bool(False, + "This cache connects to a compressed memory") + compression_latency = Param.Latency('0ns', + "Latency in cycles of compression algorithm") + do_copy = Param.Bool(False, "perform fast copies in the cache") + hash_delay = Param.Int(1, "time in cycles of hash access") + lifo = Param.Bool(False, + "whether this NIC partition should use LIFO repl. policy") + max_miss_count = Param.Counter(0, + "number of misses to handle before calling exit") + mshrs = Param.Int("number of MSHRs (max outstanding requests)") + prioritizeRequests = Param.Bool(False, + "always service demand misses first") + protocol = Param.CoherenceProtocol(NULL, "coherence protocol to use") + repl = Param.Repl(NULL, "replacement policy") + size = Param.MemorySize("capacity in bytes") + split = Param.Bool(False, "whether or not this cache is split") + split_size = Param.Int(0, + "How many ways of the cache belong to CPU/LRU partition") + store_compressed = Param.Bool(False, + "Store compressed data in the cache") + subblock_size = Param.Int(0, + "Size of subblock in IIC used for compression") + tgts_per_mshr = Param.Int("max number of accesses per MSHR") + trace_addr = Param.Addr(0, "address to trace") + two_queue = Param.Bool(False, + "whether the lifo should have two queue replacement") + write_buffers = Param.Int(8, "number of write buffers") + prefetch_miss = Param.Bool(False, + "wheter you are using the hardware prefetcher from Miss stream") + prefetch_access = Param.Bool(False, + "wheter you are using the hardware prefetcher from Access stream") + prefetcher_size = Param.Int(100, + "Number of entries in the harware prefetch queue") + prefetch_past_page = Param.Bool(False, + "Allow prefetches to cross virtual page boundaries") + prefetch_serial_squash = Param.Bool(False, + "Squash prefetches with a later time on a subsequent miss") + prefetch_degree = Param.Int(1, + "Degree of the prefetch depth") + prefetch_latency = Param.Tick(10, + "Latency of the prefetcher") + prefetch_policy = Param.Prefetch('none', + "Type of prefetcher to use") + prefetch_cache_check_push = Param.Bool(True, + "Check if in cash on push or pop of prefetch queue") + prefetch_use_cpu_id = Param.Bool(True, + "Use the CPU ID to seperate calculations of prefetches") + prefetch_data_accesses_only = Param.Bool(False, + "Only prefetch on data not on instruction accesses") + hit_latency = Param.Int(1,"Hit Latency of the cache") + cpu_side = Port("Port on side closer to CPU") + mem_side = Port("Port on side closer to MEM") diff --git a/src/python/m5/objects/Bridge.py b/src/python/m5/objects/Bridge.py new file mode 100644 index 000000000..ee8e76bff --- /dev/null +++ b/src/python/m5/objects/Bridge.py @@ -0,0 +1,11 @@ +from m5.params import * +from MemObject import MemObject + +class Bridge(MemObject): + type = 'Bridge' + side_a = Port('Side A port') + side_b = Port('Side B port') + queue_size_a = Param.Int(16, "The number of requests to buffer") + queue_size_b = Param.Int(16, "The number of requests to buffer") + delay = Param.Latency('0ns', "The latency of this bridge") + write_ack = Param.Bool(False, "Should this bridge ack writes") diff --git a/src/python/m5/objects/Bus.py b/src/python/m5/objects/Bus.py new file mode 100644 index 000000000..f6828a0d5 --- /dev/null +++ b/src/python/m5/objects/Bus.py @@ -0,0 +1,8 @@ +from m5.params import * +from MemObject import MemObject + +class Bus(MemObject): + type = 'Bus' + port = VectorPort("vector port for connecting devices") + default = Port("Default port for requests that aren't handeled by a device.") + bus_id = Param.Int(0, "blah") diff --git a/src/python/m5/objects/CoherenceProtocol.py b/src/python/m5/objects/CoherenceProtocol.py new file mode 100644 index 000000000..82adb6862 --- /dev/null +++ b/src/python/m5/objects/CoherenceProtocol.py @@ -0,0 +1,8 @@ +from m5.SimObject import SimObject +from m5.params import * +class Coherence(Enum): vals = ['uni', 'msi', 'mesi', 'mosi', 'moesi'] + +class CoherenceProtocol(SimObject): + type = 'CoherenceProtocol' + do_upgrades = Param.Bool(True, "use upgrade transactions?") + protocol = Param.Coherence("name of coherence protocol") diff --git a/src/python/m5/objects/Device.py b/src/python/m5/objects/Device.py new file mode 100644 index 000000000..4672d1065 --- /dev/null +++ b/src/python/m5/objects/Device.py @@ -0,0 +1,21 @@ +from m5.params import * +from m5.proxy import * +from MemObject import MemObject + +class PioDevice(MemObject): + type = 'PioDevice' + abstract = True + pio = Port("Programmed I/O port") + platform = Param.Platform(Parent.any, "Platform this device is part of") + system = Param.System(Parent.any, "System this device is part of") + +class BasicPioDevice(PioDevice): + type = 'BasicPioDevice' + abstract = True + pio_addr = Param.Addr("Device Address") + pio_latency = Param.Latency('1ns', "Programmed IO latency in simticks") + +class DmaDevice(PioDevice): + type = 'DmaDevice' + abstract = True + dma = Port(Self.pio.peerObj.port, "DMA port") diff --git a/src/python/m5/objects/DiskImage.py b/src/python/m5/objects/DiskImage.py new file mode 100644 index 000000000..d0ada7ee1 --- /dev/null +++ b/src/python/m5/objects/DiskImage.py @@ -0,0 +1,16 @@ +from m5.SimObject import SimObject +from m5.params import * +class DiskImage(SimObject): + type = 'DiskImage' + abstract = True + image_file = Param.String("disk image file") + read_only = Param.Bool(False, "read only image") + +class RawDiskImage(DiskImage): + type = 'RawDiskImage' + +class CowDiskImage(DiskImage): + type = 'CowDiskImage' + child = Param.DiskImage(RawDiskImage(read_only=True), + "child image") + table_size = Param.Int(65536, "initial table size") diff --git a/src/python/m5/objects/Ethernet.py b/src/python/m5/objects/Ethernet.py new file mode 100644 index 000000000..f17a6c888 --- /dev/null +++ b/src/python/m5/objects/Ethernet.py @@ -0,0 +1,193 @@ +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * +from m5 import build_env +from Device import DmaDevice +from Pci import PciDevice, PciConfigData + +class EtherInt(SimObject): + type = 'EtherInt' + abstract = True + peer = Param.EtherInt(NULL, "peer interface") + +class EtherLink(SimObject): + type = 'EtherLink' + int1 = Param.EtherInt("interface 1") + int2 = Param.EtherInt("interface 2") + delay = Param.Latency('0us', "packet transmit delay") + delay_var = Param.Latency('0ns', "packet transmit delay variability") + speed = Param.NetworkBandwidth('1Gbps', "link speed") + dump = Param.EtherDump(NULL, "dump object") + +class EtherBus(SimObject): + type = 'EtherBus' + loopback = Param.Bool(True, "send packet back to the sending interface") + dump = Param.EtherDump(NULL, "dump object") + speed = Param.NetworkBandwidth('100Mbps', "bus speed in bits per second") + +class EtherTap(EtherInt): + type = 'EtherTap' + bufsz = Param.Int(10000, "tap buffer size") + dump = Param.EtherDump(NULL, "dump object") + port = Param.UInt16(3500, "tap port") + +class EtherDump(SimObject): + type = 'EtherDump' + file = Param.String("dump file") + maxlen = Param.Int(96, "max portion of packet data to dump") + +if build_env['ALPHA_TLASER']: + + class EtherDev(DmaDevice): + type = 'EtherDev' + hardware_address = Param.EthernetAddr(NextEthernetAddr, + "Ethernet Hardware Address") + + dma_data_free = Param.Bool(False, "DMA of Data is free") + dma_desc_free = Param.Bool(False, "DMA of Descriptors is free") + dma_read_delay = Param.Latency('0us', "fixed delay for dma reads") + dma_read_factor = Param.Latency('0us', "multiplier for dma reads") + dma_write_delay = Param.Latency('0us', "fixed delay for dma writes") + dma_write_factor = Param.Latency('0us', "multiplier for dma writes") + dma_no_allocate = Param.Bool(True, "Should we allocate cache on read") + + rx_filter = Param.Bool(True, "Enable Receive Filter") + rx_delay = Param.Latency('1us', "Receive Delay") + tx_delay = Param.Latency('1us', "Transmit Delay") + + intr_delay = Param.Latency('0us', "Interrupt Delay") + payload_bus = Param.Bus(NULL, "The IO Bus to attach to for payload") + physmem = Param.PhysicalMemory(Parent.any, "Physical Memory") + tlaser = Param.Turbolaser(Parent.any, "Turbolaser") + + class EtherDevInt(EtherInt): + type = 'EtherDevInt' + device = Param.EtherDev("Ethernet device of this interface") + + +class IGbE(PciDevice): + type = 'IGbE' + hardware_address = Param.EthernetAddr(NextEthernetAddr, "Ethernet Hardware Address") + +class IGbEPciData(PciConfigData): + VendorID = 0x8086 + DeviceID = 0x1026 + SubsystemID = 0x1008 + SubsystemVendorID = 0x8086 + Status = 0x0000 + SubClassCode = 0x00 + ClassCode = 0x02 + ProgIF = 0x00 + BAR0 = 0x00000000 + BAR1 = 0x00000000 + BAR2 = 0x00000000 + BAR3 = 0x00000000 + BAR4 = 0x00000000 + BAR5 = 0x00000000 + MaximumLatency = 0x00 + MinimumGrant = 0xff + InterruptLine = 0x1e + InterruptPin = 0x01 + BAR0Size = '128kB' + +class IGbEInt(EtherInt): + type = 'IGbEInt' + device = Param.IGbE("Ethernet device of this interface") + + + +class EtherDevBase(PciDevice): + hardware_address = Param.EthernetAddr(NextEthernetAddr, + "Ethernet Hardware Address") + + clock = Param.Clock('0ns', "State machine processor frequency") + + dma_read_delay = Param.Latency('0us', "fixed delay for dma reads") + dma_read_factor = Param.Latency('0us', "multiplier for dma reads") + dma_write_delay = Param.Latency('0us', "fixed delay for dma writes") + dma_write_factor = Param.Latency('0us', "multiplier for dma writes") + + rx_delay = Param.Latency('1us', "Receive Delay") + tx_delay = Param.Latency('1us', "Transmit Delay") + rx_fifo_size = Param.MemorySize('512kB', "max size of rx fifo") + tx_fifo_size = Param.MemorySize('512kB', "max size of tx fifo") + + rx_filter = Param.Bool(True, "Enable Receive Filter") + intr_delay = Param.Latency('10us', "Interrupt propagation delay") + rx_thread = Param.Bool(False, "dedicated kernel thread for transmit") + tx_thread = Param.Bool(False, "dedicated kernel threads for receive") + rss = Param.Bool(False, "Receive Side Scaling") + +class NSGigEPciData(PciConfigData): + VendorID = 0x100B + DeviceID = 0x0022 + Status = 0x0290 + SubClassCode = 0x00 + ClassCode = 0x02 + ProgIF = 0x00 + BAR0 = 0x00000001 + BAR1 = 0x00000000 + BAR2 = 0x00000000 + BAR3 = 0x00000000 + BAR4 = 0x00000000 + BAR5 = 0x00000000 + MaximumLatency = 0x34 + MinimumGrant = 0xb0 + InterruptLine = 0x1e + InterruptPin = 0x01 + BAR0Size = '256B' + BAR1Size = '4kB' + +class NSGigE(EtherDevBase): + type = 'NSGigE' + + dma_data_free = Param.Bool(False, "DMA of Data is free") + dma_desc_free = Param.Bool(False, "DMA of Descriptors is free") + dma_no_allocate = Param.Bool(True, "Should we allocate cache on read") + + configdata = NSGigEPciData() + + +class NSGigEInt(EtherInt): + type = 'NSGigEInt' + device = Param.NSGigE("Ethernet device of this interface") + +class SinicPciData(PciConfigData): + VendorID = 0x1291 + DeviceID = 0x1293 + Status = 0x0290 + SubClassCode = 0x00 + ClassCode = 0x02 + ProgIF = 0x00 + BAR0 = 0x00000000 + BAR1 = 0x00000000 + BAR2 = 0x00000000 + BAR3 = 0x00000000 + BAR4 = 0x00000000 + BAR5 = 0x00000000 + MaximumLatency = 0x34 + MinimumGrant = 0xb0 + InterruptLine = 0x1e + InterruptPin = 0x01 + BAR0Size = '64kB' + +class Sinic(EtherDevBase): + type = 'Sinic' + + rx_max_copy = Param.MemorySize('1514B', "rx max copy") + tx_max_copy = Param.MemorySize('16kB', "tx max copy") + rx_max_intr = Param.UInt32(10, "max rx packets per interrupt") + rx_fifo_threshold = Param.MemorySize('384kB', "rx fifo high threshold") + rx_fifo_low_mark = Param.MemorySize('128kB', "rx fifo low threshold") + tx_fifo_high_mark = Param.MemorySize('384kB', "tx fifo high threshold") + tx_fifo_threshold = Param.MemorySize('128kB', "tx fifo low threshold") + virtual_count = Param.UInt32(1, "Virtualized SINIC") + zero_copy = Param.Bool(False, "Zero copy receive") + delay_copy = Param.Bool(False, "Delayed copy transmit") + virtual_addr = Param.Bool(False, "Virtual addressing") + + configdata = SinicPciData() + +class SinicInt(EtherInt): + type = 'SinicInt' + device = Param.Sinic("Ethernet device of this interface") diff --git a/src/python/m5/objects/FUPool.py b/src/python/m5/objects/FUPool.py new file mode 100644 index 000000000..4b4be79a6 --- /dev/null +++ b/src/python/m5/objects/FUPool.py @@ -0,0 +1,6 @@ +from m5.SimObject import SimObject +from m5.params import * + +class FUPool(SimObject): + type = 'FUPool' + FUList = VectorParam.FUDesc("list of FU's for this pool") diff --git a/src/python/m5/objects/FuncUnit.py b/src/python/m5/objects/FuncUnit.py new file mode 100644 index 000000000..f0ad55f7a --- /dev/null +++ b/src/python/m5/objects/FuncUnit.py @@ -0,0 +1,18 @@ +from m5.SimObject import SimObject +from m5.params import * + +class OpType(Enum): + vals = ['(null)', 'IntAlu', 'IntMult', 'IntDiv', 'FloatAdd', + 'FloatCmp', 'FloatCvt', 'FloatMult', 'FloatDiv', 'FloatSqrt', + 'MemRead', 'MemWrite', 'IprAccess', 'InstPrefetch'] + +class OpDesc(SimObject): + type = 'OpDesc' + issueLat = Param.Int(1, "cycles until another can be issued") + opClass = Param.OpType("type of operation") + opLat = Param.Int(1, "cycles until result is available") + +class FUDesc(SimObject): + type = 'FUDesc' + count = Param.Int("number of these FU's available") + opList = VectorParam.OpDesc("operation classes for this FU type") diff --git a/src/python/m5/objects/Ide.py b/src/python/m5/objects/Ide.py new file mode 100644 index 000000000..ef7e28785 --- /dev/null +++ b/src/python/m5/objects/Ide.py @@ -0,0 +1,40 @@ +from m5.SimObject import SimObject +from m5.params import * +from Pci import PciDevice, PciConfigData + +class IdeID(Enum): vals = ['master', 'slave'] + +class IdeControllerPciData(PciConfigData): + VendorID = 0x8086 + DeviceID = 0x7111 + Command = 0x0 + Status = 0x280 + Revision = 0x0 + ClassCode = 0x01 + SubClassCode = 0x01 + ProgIF = 0x85 + BAR0 = 0x00000001 + BAR1 = 0x00000001 + BAR2 = 0x00000001 + BAR3 = 0x00000001 + BAR4 = 0x00000001 + BAR5 = 0x00000001 + InterruptLine = 0x1f + InterruptPin = 0x01 + BAR0Size = '8B' + BAR1Size = '4B' + BAR2Size = '8B' + BAR3Size = '4B' + BAR4Size = '16B' + +class IdeDisk(SimObject): + type = 'IdeDisk' + delay = Param.Latency('1us', "Fixed disk delay in microseconds") + driveID = Param.IdeID('master', "Drive ID") + image = Param.DiskImage("Disk image") + +class IdeController(PciDevice): + type = 'IdeController' + disks = VectorParam.IdeDisk("IDE disks attached to this controller") + + configdata =IdeControllerPciData() diff --git a/src/python/m5/objects/IntrControl.py b/src/python/m5/objects/IntrControl.py new file mode 100644 index 000000000..95be0f4df --- /dev/null +++ b/src/python/m5/objects/IntrControl.py @@ -0,0 +1,6 @@ +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * +class IntrControl(SimObject): + type = 'IntrControl' + cpu = Param.BaseCPU(Parent.any, "the cpu") diff --git a/src/python/m5/objects/MemObject.py b/src/python/m5/objects/MemObject.py new file mode 100644 index 000000000..8982d553d --- /dev/null +++ b/src/python/m5/objects/MemObject.py @@ -0,0 +1,6 @@ +from m5.SimObject import SimObject +from m5.SimObject import SimObject + +class MemObject(SimObject): + type = 'MemObject' + abstract = True diff --git a/src/python/m5/objects/MemTest.py b/src/python/m5/objects/MemTest.py new file mode 100644 index 000000000..97600768f --- /dev/null +++ b/src/python/m5/objects/MemTest.py @@ -0,0 +1,20 @@ +from m5.SimObject import SimObject +from m5.params import * +class MemTest(SimObject): + type = 'MemTest' + cache = Param.BaseCache("L1 cache") + check_mem = Param.FunctionalMemory("check memory") + main_mem = Param.FunctionalMemory("hierarchical memory") + max_loads = Param.Counter("number of loads to execute") + memory_size = Param.Int(65536, "memory size") + percent_copies = Param.Percent(0, "target copy percentage") + percent_dest_unaligned = Param.Percent(50, + "percent of copy dest address that are unaligned") + percent_reads = Param.Percent(65, "target read percentage") + percent_source_unaligned = Param.Percent(50, + "percent of copy source address that are unaligned") + percent_uncacheable = Param.Percent(10, + "target uncacheable percentage") + progress_interval = Param.Counter(1000000, + "progress report interval (in accesses)") + trace_addr = Param.Addr(0, "address to trace") diff --git a/src/python/m5/objects/O3CPU.py b/src/python/m5/objects/O3CPU.py new file mode 100644 index 000000000..59b40c6e8 --- /dev/null +++ b/src/python/m5/objects/O3CPU.py @@ -0,0 +1,115 @@ +from m5.params import * +from m5.proxy import * +from m5 import build_env +from BaseCPU import BaseCPU +from Checker import O3Checker + +class DerivO3CPU(BaseCPU): + type = 'DerivO3CPU' + activity = Param.Unsigned(0, "Initial count") + numThreads = Param.Unsigned(1, "number of HW thread contexts") + + if build_env['FULL_SYSTEM']: + profile = Param.Latency('0ns', "trace the kernel stack") + if build_env['USE_CHECKER']: + if not build_env['FULL_SYSTEM']: + checker = Param.BaseCPU(O3Checker(workload=Parent.workload, + exitOnError=True, + warnOnlyOnLoadError=False), + "checker") + else: + checker = Param.BaseCPU(O3Checker(exitOnError=True, warnOnlyOnLoadError=False), "checker") + checker.itb = Parent.itb + checker.dtb = Parent.dtb + + cachePorts = Param.Unsigned("Cache Ports") + icache_port = Port("Instruction Port") + dcache_port = Port("Data Port") + _mem_ports = ['icache_port', 'dcache_port'] + + decodeToFetchDelay = Param.Unsigned(1, "Decode to fetch delay") + renameToFetchDelay = Param.Unsigned(1 ,"Rename to fetch delay") + iewToFetchDelay = Param.Unsigned(1, "Issue/Execute/Writeback to fetch " + "delay") + commitToFetchDelay = Param.Unsigned(1, "Commit to fetch delay") + fetchWidth = Param.Unsigned(8, "Fetch width") + + renameToDecodeDelay = Param.Unsigned(1, "Rename to decode delay") + iewToDecodeDelay = Param.Unsigned(1, "Issue/Execute/Writeback to decode " + "delay") + commitToDecodeDelay = Param.Unsigned(1, "Commit to decode delay") + fetchToDecodeDelay = Param.Unsigned(1, "Fetch to decode delay") + decodeWidth = Param.Unsigned(8, "Decode width") + + iewToRenameDelay = Param.Unsigned(1, "Issue/Execute/Writeback to rename " + "delay") + commitToRenameDelay = Param.Unsigned(1, "Commit to rename delay") + decodeToRenameDelay = Param.Unsigned(1, "Decode to rename delay") + renameWidth = Param.Unsigned(8, "Rename width") + + commitToIEWDelay = Param.Unsigned(1, "Commit to " + "Issue/Execute/Writeback delay") + renameToIEWDelay = Param.Unsigned(2, "Rename to " + "Issue/Execute/Writeback delay") + issueToExecuteDelay = Param.Unsigned(1, "Issue to execute delay (internal " + "to the IEW stage)") + dispatchWidth = Param.Unsigned(8, "Dispatch width") + issueWidth = Param.Unsigned(8, "Issue width") + wbWidth = Param.Unsigned(8, "Writeback width") + wbDepth = Param.Unsigned(1, "Writeback depth") + fuPool = Param.FUPool("Functional Unit pool") + + iewToCommitDelay = Param.Unsigned(1, "Issue/Execute/Writeback to commit " + "delay") + renameToROBDelay = Param.Unsigned(1, "Rename to reorder buffer delay") + commitWidth = Param.Unsigned(8, "Commit width") + squashWidth = Param.Unsigned(8, "Squash width") + trapLatency = Param.Tick(13, "Trap latency") + fetchTrapLatency = Param.Tick(1, "Fetch trap latency") + + backComSize = Param.Unsigned(5, "Time buffer size for backwards communication") + forwardComSize = Param.Unsigned(5, "Time buffer size for forward communication") + + predType = Param.String("tournament", "Branch predictor type ('local', 'tournament')") + localPredictorSize = Param.Unsigned(2048, "Size of local predictor") + localCtrBits = Param.Unsigned(2, "Bits per counter") + localHistoryTableSize = Param.Unsigned(2048, "Size of local history table") + localHistoryBits = Param.Unsigned(11, "Bits for the local history") + globalPredictorSize = Param.Unsigned(8192, "Size of global predictor") + globalCtrBits = Param.Unsigned(2, "Bits per counter") + globalHistoryBits = Param.Unsigned(4096, "Bits of history") + choicePredictorSize = Param.Unsigned(8192, "Size of choice predictor") + choiceCtrBits = Param.Unsigned(2, "Bits of choice counters") + + BTBEntries = Param.Unsigned(4096, "Number of BTB entries") + BTBTagSize = Param.Unsigned(16, "Size of the BTB tags, in bits") + + RASSize = Param.Unsigned(16, "RAS size") + + LQEntries = Param.Unsigned(32, "Number of load queue entries") + SQEntries = Param.Unsigned(32, "Number of store queue entries") + LFSTSize = Param.Unsigned(1024, "Last fetched store table size") + SSITSize = Param.Unsigned(1024, "Store set ID table size") + + numRobs = Param.Unsigned(1, "Number of Reorder Buffers"); + + numPhysIntRegs = Param.Unsigned(256, "Number of physical integer registers") + numPhysFloatRegs = Param.Unsigned(256, "Number of physical floating point " + "registers") + numIQEntries = Param.Unsigned(64, "Number of instruction queue entries") + numROBEntries = Param.Unsigned(192, "Number of reorder buffer entries") + + instShiftAmt = Param.Unsigned(2, "Number of bits to shift instructions by") + + function_trace = Param.Bool(False, "Enable function trace") + function_trace_start = Param.Tick(0, "Cycle to start function trace") + + smtNumFetchingThreads = Param.Unsigned("SMT Number of Fetching Threads") + smtFetchPolicy = Param.String("SMT Fetch policy") + smtLSQPolicy = Param.String("SMT LSQ Sharing Policy") + smtLSQThreshold = Param.String("SMT LSQ Threshold Sharing Parameter") + smtIQPolicy = Param.String("SMT IQ Sharing Policy") + smtIQThreshold = Param.String("SMT IQ Threshold Sharing Parameter") + smtROBPolicy = Param.String("SMT ROB Sharing Policy") + smtROBThreshold = Param.String("SMT ROB Threshold Sharing Parameter") + smtCommitPolicy = Param.String("SMT Commit Policy") diff --git a/src/python/m5/objects/OzoneCPU.py b/src/python/m5/objects/OzoneCPU.py new file mode 100644 index 000000000..0913e044c --- /dev/null +++ b/src/python/m5/objects/OzoneCPU.py @@ -0,0 +1,95 @@ +from m5.params import * +from m5 import build_env +from BaseCPU import BaseCPU + +class DerivOzoneCPU(BaseCPU): + type = 'DerivOzoneCPU' + + numThreads = Param.Unsigned("number of HW thread contexts") + + checker = Param.BaseCPU("Checker CPU") + if build_env['FULL_SYSTEM']: + profile = Param.Latency('0ns', "trace the kernel stack") + + icache_port = Port("Instruction Port") + dcache_port = Port("Data Port") + + width = Param.Unsigned("Width") + frontEndWidth = Param.Unsigned("Front end width") + frontEndLatency = Param.Unsigned("Front end latency") + backEndWidth = Param.Unsigned("Back end width") + backEndSquashLatency = Param.Unsigned("Back end squash latency") + backEndLatency = Param.Unsigned("Back end latency") + maxInstBufferSize = Param.Unsigned("Maximum instruction buffer size") + maxOutstandingMemOps = Param.Unsigned("Maximum number of outstanding memory operations") + decodeToFetchDelay = Param.Unsigned("Decode to fetch delay") + renameToFetchDelay = Param.Unsigned("Rename to fetch delay") + iewToFetchDelay = Param.Unsigned("Issue/Execute/Writeback to fetch " + "delay") + commitToFetchDelay = Param.Unsigned("Commit to fetch delay") + fetchWidth = Param.Unsigned("Fetch width") + + renameToDecodeDelay = Param.Unsigned("Rename to decode delay") + iewToDecodeDelay = Param.Unsigned("Issue/Execute/Writeback to decode " + "delay") + commitToDecodeDelay = Param.Unsigned("Commit to decode delay") + fetchToDecodeDelay = Param.Unsigned("Fetch to decode delay") + decodeWidth = Param.Unsigned("Decode width") + + iewToRenameDelay = Param.Unsigned("Issue/Execute/Writeback to rename " + "delay") + commitToRenameDelay = Param.Unsigned("Commit to rename delay") + decodeToRenameDelay = Param.Unsigned("Decode to rename delay") + renameWidth = Param.Unsigned("Rename width") + + commitToIEWDelay = Param.Unsigned("Commit to " + "Issue/Execute/Writeback delay") + renameToIEWDelay = Param.Unsigned("Rename to " + "Issue/Execute/Writeback delay") + issueToExecuteDelay = Param.Unsigned("Issue to execute delay (internal " + "to the IEW stage)") + issueWidth = Param.Unsigned("Issue width") + executeWidth = Param.Unsigned("Execute width") + executeIntWidth = Param.Unsigned("Integer execute width") + executeFloatWidth = Param.Unsigned("Floating point execute width") + executeBranchWidth = Param.Unsigned("Branch execute width") + executeMemoryWidth = Param.Unsigned("Memory execute width") + + iewToCommitDelay = Param.Unsigned("Issue/Execute/Writeback to commit " + "delay") + renameToROBDelay = Param.Unsigned("Rename to reorder buffer delay") + commitWidth = Param.Unsigned("Commit width") + squashWidth = Param.Unsigned("Squash width") + + predType = Param.String("Type of branch predictor ('local', 'tournament')") + localPredictorSize = Param.Unsigned("Size of local predictor") + localCtrBits = Param.Unsigned("Bits per counter") + localHistoryTableSize = Param.Unsigned("Size of local history table") + localHistoryBits = Param.Unsigned("Bits for the local history") + globalPredictorSize = Param.Unsigned("Size of global predictor") + globalCtrBits = Param.Unsigned("Bits per counter") + globalHistoryBits = Param.Unsigned("Bits of history") + choicePredictorSize = Param.Unsigned("Size of choice predictor") + choiceCtrBits = Param.Unsigned("Bits of choice counters") + + BTBEntries = Param.Unsigned("Number of BTB entries") + BTBTagSize = Param.Unsigned("Size of the BTB tags, in bits") + + RASSize = Param.Unsigned("RAS size") + + LQEntries = Param.Unsigned("Number of load queue entries") + SQEntries = Param.Unsigned("Number of store queue entries") + lsqLimits = Param.Bool(True, "LSQ size limits dispatch") + LFSTSize = Param.Unsigned("Last fetched store table size") + SSITSize = Param.Unsigned("Store set ID table size") + + numPhysIntRegs = Param.Unsigned("Number of physical integer registers") + numPhysFloatRegs = Param.Unsigned("Number of physical floating point " + "registers") + numIQEntries = Param.Unsigned("Number of instruction queue entries") + numROBEntries = Param.Unsigned("Number of reorder buffer entries") + + instShiftAmt = Param.Unsigned("Number of bits to shift instructions by") + + function_trace = Param.Bool(False, "Enable function trace") + function_trace_start = Param.Tick(0, "Cycle to start function trace") diff --git a/src/python/m5/objects/Pci.py b/src/python/m5/objects/Pci.py new file mode 100644 index 000000000..55bf23534 --- /dev/null +++ b/src/python/m5/objects/Pci.py @@ -0,0 +1,62 @@ +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * +from Device import BasicPioDevice, DmaDevice, PioDevice + +class PciConfigData(SimObject): + type = 'PciConfigData' + VendorID = Param.UInt16("Vendor ID") + DeviceID = Param.UInt16("Device ID") + Command = Param.UInt16(0, "Command") + Status = Param.UInt16(0, "Status") + Revision = Param.UInt8(0, "Device") + ProgIF = Param.UInt8(0, "Programming Interface") + SubClassCode = Param.UInt8(0, "Sub-Class Code") + ClassCode = Param.UInt8(0, "Class Code") + CacheLineSize = Param.UInt8(0, "System Cacheline Size") + LatencyTimer = Param.UInt8(0, "PCI Latency Timer") + HeaderType = Param.UInt8(0, "PCI Header Type") + BIST = Param.UInt8(0, "Built In Self Test") + + BAR0 = Param.UInt32(0x00, "Base Address Register 0") + BAR1 = Param.UInt32(0x00, "Base Address Register 1") + BAR2 = Param.UInt32(0x00, "Base Address Register 2") + BAR3 = Param.UInt32(0x00, "Base Address Register 3") + BAR4 = Param.UInt32(0x00, "Base Address Register 4") + BAR5 = Param.UInt32(0x00, "Base Address Register 5") + BAR0Size = Param.MemorySize32('0B', "Base Address Register 0 Size") + BAR1Size = Param.MemorySize32('0B', "Base Address Register 1 Size") + BAR2Size = Param.MemorySize32('0B', "Base Address Register 2 Size") + BAR3Size = Param.MemorySize32('0B', "Base Address Register 3 Size") + BAR4Size = Param.MemorySize32('0B', "Base Address Register 4 Size") + BAR5Size = Param.MemorySize32('0B', "Base Address Register 5 Size") + + CardbusCIS = Param.UInt32(0x00, "Cardbus Card Information Structure") + SubsystemID = Param.UInt16(0x00, "Subsystem ID") + SubsystemVendorID = Param.UInt16(0x00, "Subsystem Vendor ID") + ExpansionROM = Param.UInt32(0x00, "Expansion ROM Base Address") + InterruptLine = Param.UInt8(0x00, "Interrupt Line") + InterruptPin = Param.UInt8(0x00, "Interrupt Pin") + MaximumLatency = Param.UInt8(0x00, "Maximum Latency") + MinimumGrant = Param.UInt8(0x00, "Minimum Grant") + +class PciConfigAll(PioDevice): + type = 'PciConfigAll' + pio_latency = Param.Tick(1, "Programmed IO latency in simticks") + bus = Param.UInt8(0x00, "PCI bus to act as config space for") + size = Param.MemorySize32('16MB', "Size of config space") + + +class PciDevice(DmaDevice): + type = 'PciDevice' + abstract = True + config = Port(Self.pio.peerObj.port, "PCI configuration space port") + pci_bus = Param.Int("PCI bus") + pci_dev = Param.Int("PCI device number") + pci_func = Param.Int("PCI function code") + pio_latency = Param.Latency('1ns', "Programmed IO latency in simticks") + configdata = Param.PciConfigData(Parent.any, "PCI Config data") + config_latency = Param.Latency('20ns', "Config read or write latency") + +class PciFake(PciDevice): + type = 'PciFake' diff --git a/src/python/m5/objects/PhysicalMemory.py b/src/python/m5/objects/PhysicalMemory.py new file mode 100644 index 000000000..dd3ffd651 --- /dev/null +++ b/src/python/m5/objects/PhysicalMemory.py @@ -0,0 +1,28 @@ +from m5.params import * +from m5.proxy import * +from MemObject import * + +class PhysicalMemory(MemObject): + type = 'PhysicalMemory' + port = Port("the access port") + range = Param.AddrRange(AddrRange('128MB'), "Device Address") + file = Param.String('', "memory mapped file") + latency = Param.Latency(Parent.clock, "latency of an access") + +class DRAMMemory(PhysicalMemory): + type = 'DRAMMemory' + # Many of these should be observed from the configuration + cpu_ratio = Param.Int(5,"ratio between CPU speed and memory bus speed") + mem_type = Param.String("SDRAM", "Type of DRAM (DRDRAM, SDRAM)") + mem_actpolicy = Param.String("open", "Open/Close policy") + memctrladdr_type = Param.String("interleaved", "Mapping interleaved or direct") + bus_width = Param.Int(16, "") + act_lat = Param.Int(2, "RAS to CAS delay") + cas_lat = Param.Int(1, "CAS delay") + war_lat = Param.Int(2, "write after read delay") + pre_lat = Param.Int(2, "precharge delay") + dpl_lat = Param.Int(2, "data in to precharge delay") + trc_lat = Param.Int(6, "row cycle delay") + num_banks = Param.Int(4, "Number of Banks") + num_cpus = Param.Int(4, "Number of CPUs connected to DRAM") + diff --git a/src/python/m5/objects/Platform.py b/src/python/m5/objects/Platform.py new file mode 100644 index 000000000..ab2083eea --- /dev/null +++ b/src/python/m5/objects/Platform.py @@ -0,0 +1,7 @@ +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * +class Platform(SimObject): + type = 'Platform' + abstract = True + intrctrl = Param.IntrControl(Parent.any, "interrupt controller") diff --git a/src/python/m5/objects/Process.py b/src/python/m5/objects/Process.py new file mode 100644 index 000000000..771ad4101 --- /dev/null +++ b/src/python/m5/objects/Process.py @@ -0,0 +1,35 @@ +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * +class Process(SimObject): + type = 'Process' + abstract = True + output = Param.String('cout', 'filename for stdout/stderr') + system = Param.System(Parent.any, "system process will run on") + +class LiveProcess(Process): + type = 'LiveProcess' + executable = Param.String('', "executable (overrides cmd[0] if set)") + cmd = VectorParam.String("command line (executable plus arguments)") + env = VectorParam.String('', "environment settings") + input = Param.String('cin', "filename for stdin") + uid = Param.Int(100, 'user id') + euid = Param.Int(100, 'effective user id') + gid = Param.Int(100, 'group id') + egid = Param.Int(100, 'effective group id') + pid = Param.Int(100, 'process id') + ppid = Param.Int(99, 'parent process id') + +class AlphaLiveProcess(LiveProcess): + type = 'AlphaLiveProcess' + +class SparcLiveProcess(LiveProcess): + type = 'SparcLiveProcess' + +class MipsLiveProcess(LiveProcess): + type = 'MipsLiveProcess' + +class EioProcess(Process): + type = 'EioProcess' + chkpt = Param.String('', "EIO checkpoint file name (optional)") + file = Param.String("EIO trace file name") diff --git a/src/python/m5/objects/Repl.py b/src/python/m5/objects/Repl.py new file mode 100644 index 000000000..10892cf6f --- /dev/null +++ b/src/python/m5/objects/Repl.py @@ -0,0 +1,11 @@ +from m5.SimObject import SimObject +from m5.params import * +class Repl(SimObject): + type = 'Repl' + abstract = True + +class GenRepl(Repl): + type = 'GenRepl' + fresh_res = Param.Int("associativity") + num_pools = Param.Int("capacity in bytes") + pool_res = Param.Int("block size in bytes") diff --git a/src/python/m5/objects/Root.py b/src/python/m5/objects/Root.py new file mode 100644 index 000000000..8e8d87f6d --- /dev/null +++ b/src/python/m5/objects/Root.py @@ -0,0 +1,25 @@ +from m5.SimObject import SimObject +from m5.params import * +from Serialize import Serialize +from Serialize import Statreset +from Statistics import Statistics +from Trace import Trace +from ExeTrace import ExecutionTrace +from Debug import Debug + +class Root(SimObject): + type = 'Root' + clock = Param.RootClock('1THz', "tick frequency") + max_tick = Param.Tick('0', "maximum simulation ticks (0 = infinite)") + progress_interval = Param.Tick('0', + "print a progress message every n ticks (0 = never)") + output_file = Param.String('cout', "file to dump simulator output to") + checkpoint = Param.String('', "checkpoint file to load") +# stats = Param.Statistics(Statistics(), "statistics object") +# trace = Param.Trace(Trace(), "trace object") +# serialize = Param.Serialize(Serialize(), "checkpoint generation options") + stats = Statistics() + trace = Trace() + exetrace = ExecutionTrace() + serialize = Serialize() + debug = Debug() diff --git a/src/python/m5/objects/SimConsole.py b/src/python/m5/objects/SimConsole.py new file mode 100644 index 000000000..bdd7f246d --- /dev/null +++ b/src/python/m5/objects/SimConsole.py @@ -0,0 +1,14 @@ +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * +class ConsoleListener(SimObject): + type = 'ConsoleListener' + port = Param.TcpPort(3456, "listen port") + +class SimConsole(SimObject): + type = 'SimConsole' + append_name = Param.Bool(True, "append name() to filename") + intr_control = Param.IntrControl(Parent.any, "interrupt controller") + listener = Param.ConsoleListener("console listener") + number = Param.Int(0, "console number") + output = Param.String('console', "file to dump output to") diff --git a/src/python/m5/objects/SimpleDisk.py b/src/python/m5/objects/SimpleDisk.py new file mode 100644 index 000000000..099a77dbb --- /dev/null +++ b/src/python/m5/objects/SimpleDisk.py @@ -0,0 +1,7 @@ +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * +class SimpleDisk(SimObject): + type = 'SimpleDisk' + disk = Param.DiskImage("Disk Image") + system = Param.System(Parent.any, "Sysetm Pointer") diff --git a/src/python/m5/objects/SimpleOzoneCPU.py b/src/python/m5/objects/SimpleOzoneCPU.py new file mode 100644 index 000000000..193f31b0f --- /dev/null +++ b/src/python/m5/objects/SimpleOzoneCPU.py @@ -0,0 +1,87 @@ +from m5.params import * +from m5 import build_env +from BaseCPU import BaseCPU + +class SimpleOzoneCPU(BaseCPU): + type = 'SimpleOzoneCPU' + + numThreads = Param.Unsigned("number of HW thread contexts") + + if not build_env['FULL_SYSTEM']: + mem = Param.FunctionalMemory(NULL, "memory") + + width = Param.Unsigned("Width") + frontEndWidth = Param.Unsigned("Front end width") + backEndWidth = Param.Unsigned("Back end width") + backEndSquashLatency = Param.Unsigned("Back end squash latency") + backEndLatency = Param.Unsigned("Back end latency") + maxInstBufferSize = Param.Unsigned("Maximum instruction buffer size") + decodeToFetchDelay = Param.Unsigned("Decode to fetch delay") + renameToFetchDelay = Param.Unsigned("Rename to fetch delay") + iewToFetchDelay = Param.Unsigned("Issue/Execute/Writeback to fetch " + "delay") + commitToFetchDelay = Param.Unsigned("Commit to fetch delay") + fetchWidth = Param.Unsigned("Fetch width") + + renameToDecodeDelay = Param.Unsigned("Rename to decode delay") + iewToDecodeDelay = Param.Unsigned("Issue/Execute/Writeback to decode " + "delay") + commitToDecodeDelay = Param.Unsigned("Commit to decode delay") + fetchToDecodeDelay = Param.Unsigned("Fetch to decode delay") + decodeWidth = Param.Unsigned("Decode width") + + iewToRenameDelay = Param.Unsigned("Issue/Execute/Writeback to rename " + "delay") + commitToRenameDelay = Param.Unsigned("Commit to rename delay") + decodeToRenameDelay = Param.Unsigned("Decode to rename delay") + renameWidth = Param.Unsigned("Rename width") + + commitToIEWDelay = Param.Unsigned("Commit to " + "Issue/Execute/Writeback delay") + renameToIEWDelay = Param.Unsigned("Rename to " + "Issue/Execute/Writeback delay") + issueToExecuteDelay = Param.Unsigned("Issue to execute delay (internal " + "to the IEW stage)") + issueWidth = Param.Unsigned("Issue width") + executeWidth = Param.Unsigned("Execute width") + executeIntWidth = Param.Unsigned("Integer execute width") + executeFloatWidth = Param.Unsigned("Floating point execute width") + executeBranchWidth = Param.Unsigned("Branch execute width") + executeMemoryWidth = Param.Unsigned("Memory execute width") + + iewToCommitDelay = Param.Unsigned("Issue/Execute/Writeback to commit " + "delay") + renameToROBDelay = Param.Unsigned("Rename to reorder buffer delay") + commitWidth = Param.Unsigned("Commit width") + squashWidth = Param.Unsigned("Squash width") + + localPredictorSize = Param.Unsigned("Size of local predictor") + localCtrBits = Param.Unsigned("Bits per counter") + localHistoryTableSize = Param.Unsigned("Size of local history table") + localHistoryBits = Param.Unsigned("Bits for the local history") + globalPredictorSize = Param.Unsigned("Size of global predictor") + globalCtrBits = Param.Unsigned("Bits per counter") + globalHistoryBits = Param.Unsigned("Bits of history") + choicePredictorSize = Param.Unsigned("Size of choice predictor") + choiceCtrBits = Param.Unsigned("Bits of choice counters") + + BTBEntries = Param.Unsigned("Number of BTB entries") + BTBTagSize = Param.Unsigned("Size of the BTB tags, in bits") + + RASSize = Param.Unsigned("RAS size") + + LQEntries = Param.Unsigned("Number of load queue entries") + SQEntries = Param.Unsigned("Number of store queue entries") + LFSTSize = Param.Unsigned("Last fetched store table size") + SSITSize = Param.Unsigned("Store set ID table size") + + numPhysIntRegs = Param.Unsigned("Number of physical integer registers") + numPhysFloatRegs = Param.Unsigned("Number of physical floating point " + "registers") + numIQEntries = Param.Unsigned("Number of instruction queue entries") + numROBEntries = Param.Unsigned("Number of reorder buffer entries") + + instShiftAmt = Param.Unsigned("Number of bits to shift instructions by") + + function_trace = Param.Bool(False, "Enable function trace") + function_trace_start = Param.Tick(0, "Cycle to start function trace") diff --git a/src/python/m5/objects/System.py b/src/python/m5/objects/System.py new file mode 100644 index 000000000..e7dd1bc60 --- /dev/null +++ b/src/python/m5/objects/System.py @@ -0,0 +1,26 @@ +from m5.SimObject import SimObject +from m5.params import * +from m5.proxy import * +from m5 import build_env + +class MemoryMode(Enum): vals = ['invalid', 'atomic', 'timing'] + +class System(SimObject): + type = 'System' + physmem = Param.PhysicalMemory(Parent.any, "phsyical memory") + mem_mode = Param.MemoryMode('atomic', "The mode the memory system is in") + if build_env['FULL_SYSTEM']: + boot_cpu_frequency = Param.Frequency(Self.cpu[0].clock.frequency, + "boot processor frequency") + init_param = Param.UInt64(0, "numerical value to pass into simulator") + boot_osflags = Param.String("a", "boot flags to pass to the kernel") + kernel = Param.String("file that contains the kernel code") + readfile = Param.String("", "file to read startup script from") + symbolfile = Param.String("", "file to get the symbols from") + +class AlphaSystem(System): + type = 'AlphaSystem' + console = Param.String("file that contains the console code") + pal = Param.String("file that contains palcode") + system_type = Param.UInt64("Type of system we are emulating") + system_rev = Param.UInt64("Revision of system we are emulating") diff --git a/src/python/m5/objects/Tsunami.py b/src/python/m5/objects/Tsunami.py new file mode 100644 index 000000000..0b53153a0 --- /dev/null +++ b/src/python/m5/objects/Tsunami.py @@ -0,0 +1,95 @@ +from m5.params import * +from m5.proxy import * +from Device import BasicPioDevice +from Platform import Platform +from AlphaConsole import AlphaConsole +from Uart import Uart8250 +from Pci import PciConfigAll +from BadDevice import BadDevice + +class TsunamiCChip(BasicPioDevice): + type = 'TsunamiCChip' + tsunami = Param.Tsunami(Parent.any, "Tsunami") + +class IsaFake(BasicPioDevice): + type = 'IsaFake' + pio_size = Param.Addr(0x8, "Size of address range") + +class TsunamiIO(BasicPioDevice): + type = 'TsunamiIO' + time = Param.UInt64(1136073600, + "System time to use (0 for actual time, default is 1/1/06)") + tsunami = Param.Tsunami(Parent.any, "Tsunami") + frequency = Param.Frequency('1024Hz', "frequency of interrupts") + +class TsunamiPChip(BasicPioDevice): + type = 'TsunamiPChip' + tsunami = Param.Tsunami(Parent.any, "Tsunami") + +class Tsunami(Platform): + type = 'Tsunami' + system = Param.System(Parent.any, "system") + + cchip = TsunamiCChip(pio_addr=0x801a0000000) + pchip = TsunamiPChip(pio_addr=0x80180000000) + pciconfig = PciConfigAll() + fake_sm_chip = IsaFake(pio_addr=0x801fc000370) + + fake_uart1 = IsaFake(pio_addr=0x801fc0002f8) + fake_uart2 = IsaFake(pio_addr=0x801fc0003e8) + fake_uart3 = IsaFake(pio_addr=0x801fc0002e8) + fake_uart4 = IsaFake(pio_addr=0x801fc0003f0) + + fake_ppc = IsaFake(pio_addr=0x801fc0003bc) + + fake_OROM = IsaFake(pio_addr=0x800000a0000, pio_size=0x60000) + + fake_pnp_addr = IsaFake(pio_addr=0x801fc000279) + fake_pnp_write = IsaFake(pio_addr=0x801fc000a79) + fake_pnp_read0 = IsaFake(pio_addr=0x801fc000203) + fake_pnp_read1 = IsaFake(pio_addr=0x801fc000243) + fake_pnp_read2 = IsaFake(pio_addr=0x801fc000283) + fake_pnp_read3 = IsaFake(pio_addr=0x801fc0002c3) + fake_pnp_read4 = IsaFake(pio_addr=0x801fc000303) + fake_pnp_read5 = IsaFake(pio_addr=0x801fc000343) + fake_pnp_read6 = IsaFake(pio_addr=0x801fc000383) + fake_pnp_read7 = IsaFake(pio_addr=0x801fc0003c3) + + fake_ata0 = IsaFake(pio_addr=0x801fc0001f0) + fake_ata1 = IsaFake(pio_addr=0x801fc000170) + + fb = BadDevice(pio_addr=0x801fc0003d0, devicename='FrameBuffer') + io = TsunamiIO(pio_addr=0x801fc000000) + uart = Uart8250(pio_addr=0x801fc0003f8) + console = AlphaConsole(pio_addr=0x80200000000, disk=Parent.simple_disk) + + # Attach I/O devices to specified bus object. Can't do this + # earlier, since the bus object itself is typically defined at the + # System level. + def attachIO(self, bus): + self.cchip.pio = bus.port + self.pchip.pio = bus.port + self.pciconfig.pio = bus.default + self.fake_sm_chip.pio = bus.port + self.fake_uart1.pio = bus.port + self.fake_uart2.pio = bus.port + self.fake_uart3.pio = bus.port + self.fake_uart4.pio = bus.port + self.fake_ppc.pio = bus.port + self.fake_OROM.pio = bus.port + self.fake_pnp_addr.pio = bus.port + self.fake_pnp_write.pio = bus.port + self.fake_pnp_read0.pio = bus.port + self.fake_pnp_read1.pio = bus.port + self.fake_pnp_read2.pio = bus.port + self.fake_pnp_read3.pio = bus.port + self.fake_pnp_read4.pio = bus.port + self.fake_pnp_read5.pio = bus.port + self.fake_pnp_read6.pio = bus.port + self.fake_pnp_read7.pio = bus.port + self.fake_ata0.pio = bus.port + self.fake_ata1.pio = bus.port + self.fb.pio = bus.port + self.io.pio = bus.port + self.uart.pio = bus.port + self.console.pio = bus.port diff --git a/src/python/m5/objects/Uart.py b/src/python/m5/objects/Uart.py new file mode 100644 index 000000000..62062c6b1 --- /dev/null +++ b/src/python/m5/objects/Uart.py @@ -0,0 +1,17 @@ +from m5.params import * +from m5.proxy import * +from m5 import build_env +from Device import BasicPioDevice + +class Uart(BasicPioDevice): + type = 'Uart' + abstract = True + sim_console = Param.SimConsole(Parent.any, "The console") + +class Uart8250(Uart): + type = 'Uart8250' + +if build_env['ALPHA_TLASER']: + class Uart8530(Uart): + type = 'Uart8530' + diff --git a/src/python/m5/params.py b/src/python/m5/params.py new file mode 100644 index 000000000..cbbd23004 --- /dev/null +++ b/src/python/m5/params.py @@ -0,0 +1,968 @@ +# Copyright (c) 2004-2006 The Regents of The University of Michigan +# 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. +# +# Authors: Steve Reinhardt +# Nathan Binkert + +##################################################################### +# +# Parameter description classes +# +# The _params dictionary in each class maps parameter names to either +# a Param or a VectorParam object. These objects contain the +# parameter description string, the parameter type, and the default +# value (if any). The convert() method on these objects is used to +# force whatever value is assigned to the parameter to the appropriate +# type. +# +# Note that the default values are loaded into the class's attribute +# space when the parameter dictionary is initialized (in +# MetaSimObject._new_param()); after that point they aren't used. +# +##################################################################### + +import sys, inspect, copy +import convert +from util import * + +# Dummy base class to identify types that are legitimate for SimObject +# parameters. +class ParamValue(object): + + cxx_predecls = [] + swig_predecls = [] + + # default for printing to .ini file is regular string conversion. + # will be overridden in some cases + def ini_str(self): + return str(self) + + # allows us to blithely call unproxy() on things without checking + # if they're really proxies or not + def unproxy(self, base): + return self + +# Regular parameter description. +class ParamDesc(object): + def __init__(self, ptype_str, ptype, *args, **kwargs): + self.ptype_str = ptype_str + # remember ptype only if it is provided + if ptype != None: + self.ptype = ptype + + if args: + if len(args) == 1: + self.desc = args[0] + elif len(args) == 2: + self.default = args[0] + self.desc = args[1] + else: + raise TypeError, 'too many arguments' + + if kwargs.has_key('desc'): + assert(not hasattr(self, 'desc')) + self.desc = kwargs['desc'] + del kwargs['desc'] + + if kwargs.has_key('default'): + assert(not hasattr(self, 'default')) + self.default = kwargs['default'] + del kwargs['default'] + + if kwargs: + raise TypeError, 'extra unknown kwargs %s' % kwargs + + if not hasattr(self, 'desc'): + raise TypeError, 'desc attribute missing' + + def __getattr__(self, attr): + if attr == 'ptype': + try: + ptype = eval(self.ptype_str, objects.__dict__) + if not isinstance(ptype, type): + raise NameError + self.ptype = ptype + return ptype + except NameError: + raise TypeError, \ + "Param qualifier '%s' is not a type" % self.ptype_str + raise AttributeError, "'%s' object has no attribute '%s'" % \ + (type(self).__name__, attr) + + def convert(self, value): + if isinstance(value, proxy.BaseProxy): + value.set_param_desc(self) + return value + if not hasattr(self, 'ptype') and isNullPointer(value): + # deferred evaluation of SimObject; continue to defer if + # we're just assigning a null pointer + return value + if isinstance(value, self.ptype): + return value + if isNullPointer(value) and isSimObjectClass(self.ptype): + return value + return self.ptype(value) + + def cxx_predecls(self): + return self.ptype.cxx_predecls + + def swig_predecls(self): + return self.ptype.swig_predecls + + def cxx_decl(self): + return '%s %s;' % (self.ptype.cxx_type, self.name) + +# Vector-valued parameter description. Just like ParamDesc, except +# that the value is a vector (list) of the specified type instead of a +# single value. + +class VectorParamValue(list): + def ini_str(self): + return ' '.join([v.ini_str() for v in self]) + + def unproxy(self, base): + return [v.unproxy(base) for v in self] + +class SimObjVector(VectorParamValue): + def print_ini(self): + for v in self: + v.print_ini() + +class VectorParamDesc(ParamDesc): + # Convert assigned value to appropriate type. If the RHS is not a + # list or tuple, it generates a single-element list. + def convert(self, value): + if isinstance(value, (list, tuple)): + # list: coerce each element into new list + tmp_list = [ ParamDesc.convert(self, v) for v in value ] + if isSimObjectSequence(tmp_list): + return SimObjVector(tmp_list) + else: + return VectorParamValue(tmp_list) + else: + # singleton: leave it be (could coerce to a single-element + # list here, but for some historical reason we don't... + return ParamDesc.convert(self, value) + + def cxx_predecls(self): + return ['#include <vector>'] + self.ptype.cxx_predecls + + def swig_predecls(self): + return ['%include "std_vector.i"'] + self.ptype.swig_predecls + + def cxx_decl(self): + return 'std::vector< %s > %s;' % (self.ptype.cxx_type, self.name) + +class ParamFactory(object): + def __init__(self, param_desc_class, ptype_str = None): + self.param_desc_class = param_desc_class + self.ptype_str = ptype_str + + def __getattr__(self, attr): + if self.ptype_str: + attr = self.ptype_str + '.' + attr + return ParamFactory(self.param_desc_class, attr) + + # E.g., Param.Int(5, "number of widgets") + def __call__(self, *args, **kwargs): + caller_frame = inspect.currentframe().f_back + ptype = None + try: + ptype = eval(self.ptype_str, + caller_frame.f_globals, caller_frame.f_locals) + if not isinstance(ptype, type): + raise TypeError, \ + "Param qualifier is not a type: %s" % ptype + except NameError: + # if name isn't defined yet, assume it's a SimObject, and + # try to resolve it later + pass + return self.param_desc_class(self.ptype_str, ptype, *args, **kwargs) + +Param = ParamFactory(ParamDesc) +VectorParam = ParamFactory(VectorParamDesc) + +##################################################################### +# +# Parameter Types +# +# Though native Python types could be used to specify parameter types +# (the 'ptype' field of the Param and VectorParam classes), it's more +# flexible to define our own set of types. This gives us more control +# over how Python expressions are converted to values (via the +# __init__() constructor) and how these values are printed out (via +# the __str__() conversion method). +# +##################################################################### + +# String-valued parameter. Just mixin the ParamValue class with the +# built-in str class. +class String(ParamValue,str): + cxx_type = 'std::string' + cxx_predecls = ['#include <string>'] + swig_predecls = ['%include "std_string.i"\n' + + '%apply const std::string& {std::string *};'] + pass + +# superclass for "numeric" parameter values, to emulate math +# operations in a type-safe way. e.g., a Latency times an int returns +# a new Latency object. +class NumericParamValue(ParamValue): + def __str__(self): + return str(self.value) + + def __float__(self): + return float(self.value) + + # hook for bounds checking + def _check(self): + return + + def __mul__(self, other): + newobj = self.__class__(self) + newobj.value *= other + newobj._check() + return newobj + + __rmul__ = __mul__ + + def __div__(self, other): + newobj = self.__class__(self) + newobj.value /= other + newobj._check() + return newobj + + def __sub__(self, other): + newobj = self.__class__(self) + newobj.value -= other + newobj._check() + return newobj + +# Metaclass for bounds-checked integer parameters. See CheckedInt. +class CheckedIntType(type): + def __init__(cls, name, bases, dict): + super(CheckedIntType, cls).__init__(name, bases, dict) + + # CheckedInt is an abstract base class, so we actually don't + # want to do any processing on it... the rest of this code is + # just for classes that derive from CheckedInt. + if name == 'CheckedInt': + return + + if not cls.cxx_predecls: + # most derived types require this, so we just do it here once + cls.cxx_predecls = ['#include "sim/host.hh"'] + + if not cls.swig_predecls: + # most derived types require this, so we just do it here once + cls.swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + '%import "sim/host.hh"'] + + if not (hasattr(cls, 'min') and hasattr(cls, 'max')): + if not (hasattr(cls, 'size') and hasattr(cls, 'unsigned')): + panic("CheckedInt subclass %s must define either\n" \ + " 'min' and 'max' or 'size' and 'unsigned'\n" \ + % name); + if cls.unsigned: + cls.min = 0 + cls.max = 2 ** cls.size - 1 + else: + cls.min = -(2 ** (cls.size - 1)) + cls.max = (2 ** (cls.size - 1)) - 1 + +# Abstract superclass for bounds-checked integer parameters. This +# class is subclassed to generate parameter classes with specific +# bounds. Initialization of the min and max bounds is done in the +# metaclass CheckedIntType.__init__. +class CheckedInt(NumericParamValue): + __metaclass__ = CheckedIntType + + def _check(self): + if not self.min <= self.value <= self.max: + raise TypeError, 'Integer param out of bounds %d < %d < %d' % \ + (self.min, self.value, self.max) + + def __init__(self, value): + if isinstance(value, str): + self.value = convert.toInteger(value) + elif isinstance(value, (int, long, float)): + self.value = long(value) + self._check() + +class Int(CheckedInt): cxx_type = 'int'; size = 32; unsigned = False +class Unsigned(CheckedInt): cxx_type = 'unsigned'; size = 32; unsigned = True + +class Int8(CheckedInt): cxx_type = 'int8_t'; size = 8; unsigned = False +class UInt8(CheckedInt): cxx_type = 'uint8_t'; size = 8; unsigned = True +class Int16(CheckedInt): cxx_type = 'int16_t'; size = 16; unsigned = False +class UInt16(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True +class Int32(CheckedInt): cxx_type = 'int32_t'; size = 32; unsigned = False +class UInt32(CheckedInt): cxx_type = 'uint32_t'; size = 32; unsigned = True +class Int64(CheckedInt): cxx_type = 'int64_t'; size = 64; unsigned = False +class UInt64(CheckedInt): cxx_type = 'uint64_t'; size = 64; unsigned = True + +class Counter(CheckedInt): cxx_type = 'Counter'; size = 64; unsigned = True +class Tick(CheckedInt): cxx_type = 'Tick'; size = 64; unsigned = True +class TcpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True +class UdpPort(CheckedInt): cxx_type = 'uint16_t'; size = 16; unsigned = True + +class Percent(CheckedInt): cxx_type = 'int'; min = 0; max = 100 + +class Float(ParamValue, float): + pass + +class MemorySize(CheckedInt): + cxx_type = 'uint64_t' + size = 64 + unsigned = True + def __init__(self, value): + if isinstance(value, MemorySize): + self.value = value.value + else: + self.value = convert.toMemorySize(value) + self._check() + +class MemorySize32(CheckedInt): + size = 32 + unsigned = True + def __init__(self, value): + if isinstance(value, MemorySize): + self.value = value.value + else: + self.value = convert.toMemorySize(value) + self._check() + +class Addr(CheckedInt): + cxx_type = 'Addr' + cxx_predecls = ['#include "targetarch/isa_traits.hh"'] + size = 64 + unsigned = True + def __init__(self, value): + if isinstance(value, Addr): + self.value = value.value + else: + try: + self.value = convert.toMemorySize(value) + except TypeError: + self.value = long(value) + self._check() + + +class MetaRange(type): + def __init__(cls, name, bases, dict): + super(MetaRange, cls).__init__(name, bases, dict) + if name == 'Range': + return + cls.cxx_type = 'Range< %s >' % cls.type.cxx_type + cls.cxx_predecls = \ + ['#include "base/range.hh"'] + cls.type.cxx_predecls + +class Range(ParamValue): + __metaclass__ = MetaRange + type = Int # default; can be overridden in subclasses + def __init__(self, *args, **kwargs): + def handle_kwargs(self, kwargs): + if 'end' in kwargs: + self.second = self.type(kwargs.pop('end')) + elif 'size' in kwargs: + self.second = self.first + self.type(kwargs.pop('size')) - 1 + else: + raise TypeError, "Either end or size must be specified" + + if len(args) == 0: + self.first = self.type(kwargs.pop('start')) + handle_kwargs(self, kwargs) + + elif len(args) == 1: + if kwargs: + self.first = self.type(args[0]) + handle_kwargs(self, kwargs) + elif isinstance(args[0], Range): + self.first = self.type(args[0].first) + self.second = self.type(args[0].second) + else: + self.first = self.type(0) + self.second = self.type(args[0]) - 1 + + elif len(args) == 2: + self.first = self.type(args[0]) + self.second = self.type(args[1]) + else: + raise TypeError, "Too many arguments specified" + + if kwargs: + raise TypeError, "too many keywords: %s" % kwargs.keys() + + def __str__(self): + return '%s:%s' % (self.first, self.second) + +class AddrRange(Range): + type = Addr + +class TickRange(Range): + type = Tick + +# Boolean parameter type. Python doesn't let you subclass bool, since +# it doesn't want to let you create multiple instances of True and +# False. Thus this is a little more complicated than String. +class Bool(ParamValue): + cxx_type = 'bool' + def __init__(self, value): + try: + self.value = convert.toBool(value) + except TypeError: + self.value = bool(value) + + def __str__(self): + return str(self.value) + + def ini_str(self): + if self.value: + return 'true' + return 'false' + +def IncEthernetAddr(addr, val = 1): + bytes = map(lambda x: int(x, 16), addr.split(':')) + bytes[5] += val + for i in (5, 4, 3, 2, 1): + val,rem = divmod(bytes[i], 256) + bytes[i] = rem + if val == 0: + break + bytes[i - 1] += val + assert(bytes[0] <= 255) + return ':'.join(map(lambda x: '%02x' % x, bytes)) + +class NextEthernetAddr(object): + addr = "00:90:00:00:00:01" + + def __init__(self, inc = 1): + self.value = NextEthernetAddr.addr + NextEthernetAddr.addr = IncEthernetAddr(NextEthernetAddr.addr, inc) + +class EthernetAddr(ParamValue): + cxx_type = 'Net::EthAddr' + cxx_predecls = ['#include "base/inet.hh"'] + swig_predecls = ['class Net::EthAddr;'] + def __init__(self, value): + if value == NextEthernetAddr: + self.value = value + return + + if not isinstance(value, str): + raise TypeError, "expected an ethernet address and didn't get one" + + bytes = value.split(':') + if len(bytes) != 6: + raise TypeError, 'invalid ethernet address %s' % value + + for byte in bytes: + if not 0 <= int(byte) <= 256: + raise TypeError, 'invalid ethernet address %s' % value + + self.value = value + + def unproxy(self, base): + if self.value == NextEthernetAddr: + self.addr = self.value().value + return self + + def __str__(self): + if self.value == NextEthernetAddr: + if hasattr(self, 'addr'): + return self.addr + else: + return "NextEthernetAddr (unresolved)" + else: + return self.value + +# Enumerated types are a little more complex. The user specifies the +# type as Enum(foo) where foo is either a list or dictionary of +# alternatives (typically strings, but not necessarily so). (In the +# long run, the integer value of the parameter will be the list index +# or the corresponding dictionary value. For now, since we only check +# that the alternative is valid and then spit it into a .ini file, +# there's not much point in using the dictionary.) + +# What Enum() must do is generate a new type encapsulating the +# provided list/dictionary so that specific values of the parameter +# can be instances of that type. We define two hidden internal +# classes (_ListEnum and _DictEnum) to serve as base classes, then +# derive the new type from the appropriate base class on the fly. + + +# Metaclass for Enum types +class MetaEnum(type): + def __init__(cls, name, bases, init_dict): + if init_dict.has_key('map'): + if not isinstance(cls.map, dict): + raise TypeError, "Enum-derived class attribute 'map' " \ + "must be of type dict" + # build list of value strings from map + cls.vals = cls.map.keys() + cls.vals.sort() + elif init_dict.has_key('vals'): + if not isinstance(cls.vals, list): + raise TypeError, "Enum-derived class attribute 'vals' " \ + "must be of type list" + # build string->value map from vals sequence + cls.map = {} + for idx,val in enumerate(cls.vals): + cls.map[val] = idx + else: + raise TypeError, "Enum-derived class must define "\ + "attribute 'map' or 'vals'" + + cls.cxx_type = name + '::Enum' + + super(MetaEnum, cls).__init__(name, bases, init_dict) + + # Generate C++ class declaration for this enum type. + # Note that we wrap the enum in a class/struct to act as a namespace, + # so that the enum strings can be brief w/o worrying about collisions. + def cxx_decl(cls): + s = 'struct %s {\n enum Enum {\n ' % cls.__name__ + s += ',\n '.join(['%s = %d' % (v,cls.map[v]) for v in cls.vals]) + s += '\n };\n};\n' + return s + +# Base class for enum types. +class Enum(ParamValue): + __metaclass__ = MetaEnum + vals = [] + + def __init__(self, value): + if value not in self.map: + raise TypeError, "Enum param got bad value '%s' (not in %s)" \ + % (value, self.vals) + self.value = value + + def __str__(self): + return self.value + +ticks_per_sec = None + +# how big does a rounding error need to be before we warn about it? +frequency_tolerance = 0.001 # 0.1% + +# convert a floting-point # of ticks to integer, and warn if rounding +# discards too much precision +def tick_check(float_ticks): + if float_ticks == 0: + return 0 + int_ticks = int(round(float_ticks)) + err = (float_ticks - int_ticks) / float_ticks + if err > frequency_tolerance: + print >> sys.stderr, "Warning: rounding error > tolerance" + print >> sys.stderr, " %f rounded to %d" % (float_ticks, int_ticks) + #raise ValueError + return int_ticks + +def getLatency(value): + if isinstance(value, Latency) or isinstance(value, Clock): + return value.value + elif isinstance(value, Frequency) or isinstance(value, RootClock): + return 1 / value.value + elif isinstance(value, str): + try: + return convert.toLatency(value) + except ValueError: + try: + return 1 / convert.toFrequency(value) + except ValueError: + pass # fall through + raise ValueError, "Invalid Frequency/Latency value '%s'" % value + + +class Latency(NumericParamValue): + cxx_type = 'Tick' + cxx_predecls = ['#include "sim/host.hh"'] + swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + '%import "sim/host.hh"'] + def __init__(self, value): + self.value = getLatency(value) + + def __getattr__(self, attr): + if attr in ('latency', 'period'): + return self + if attr == 'frequency': + return Frequency(self) + raise AttributeError, "Latency object has no attribute '%s'" % attr + + # convert latency to ticks + def ini_str(self): + return str(tick_check(self.value * ticks_per_sec)) + +class Frequency(NumericParamValue): + cxx_type = 'Tick' + cxx_predecls = ['#include "sim/host.hh"'] + swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + '%import "sim/host.hh"'] + def __init__(self, value): + self.value = 1 / getLatency(value) + + def __getattr__(self, attr): + if attr == 'frequency': + return self + if attr in ('latency', 'period'): + return Latency(self) + raise AttributeError, "Frequency object has no attribute '%s'" % attr + + # convert frequency to ticks per period + def ini_str(self): + return self.period.ini_str() + +# Just like Frequency, except ini_str() is absolute # of ticks per sec (Hz). +# We can't inherit from Frequency because we don't want it to be directly +# assignable to a regular Frequency parameter. +class RootClock(ParamValue): + cxx_type = 'Tick' + cxx_predecls = ['#include "sim/host.hh"'] + swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + '%import "sim/host.hh"'] + def __init__(self, value): + self.value = 1 / getLatency(value) + + def __getattr__(self, attr): + if attr == 'frequency': + return Frequency(self) + if attr in ('latency', 'period'): + return Latency(self) + raise AttributeError, "Frequency object has no attribute '%s'" % attr + + def ini_str(self): + return str(tick_check(self.value)) + +# A generic frequency and/or Latency value. Value is stored as a latency, +# but to avoid ambiguity this object does not support numeric ops (* or /). +# An explicit conversion to a Latency or Frequency must be made first. +class Clock(ParamValue): + cxx_type = 'Tick' + cxx_predecls = ['#include "sim/host.hh"'] + swig_predecls = ['%import "python/m5/swig/stdint.i"\n' + + '%import "sim/host.hh"'] + def __init__(self, value): + self.value = getLatency(value) + + def __getattr__(self, attr): + if attr == 'frequency': + return Frequency(self) + if attr in ('latency', 'period'): + return Latency(self) + raise AttributeError, "Frequency object has no attribute '%s'" % attr + + def ini_str(self): + return self.period.ini_str() + +class NetworkBandwidth(float,ParamValue): + cxx_type = 'float' + def __new__(cls, value): + val = convert.toNetworkBandwidth(value) / 8.0 + return super(cls, NetworkBandwidth).__new__(cls, val) + + def __str__(self): + return str(self.val) + + def ini_str(self): + return '%f' % (ticks_per_sec / float(self)) + +class MemoryBandwidth(float,ParamValue): + cxx_type = 'float' + def __new__(self, value): + val = convert.toMemoryBandwidth(value) + return super(cls, MemoryBandwidth).__new__(cls, val) + + def __str__(self): + return str(self.val) + + def ini_str(self): + return '%f' % (ticks_per_sec / float(self)) + +# +# "Constants"... handy aliases for various values. +# + +# Special class for NULL pointers. Note the special check in +# make_param_value() above that lets these be assigned where a +# SimObject is required. +# only one copy of a particular node +class NullSimObject(object): + __metaclass__ = Singleton + + def __call__(cls): + return cls + + def _instantiate(self, parent = None, path = ''): + pass + + def ini_str(self): + return 'Null' + + def unproxy(self, base): + return self + + def set_path(self, parent, name): + pass + def __str__(self): + return 'Null' + +# The only instance you'll ever need... +NULL = NullSimObject() + +def isNullPointer(value): + return isinstance(value, NullSimObject) + +# Some memory range specifications use this as a default upper bound. +MaxAddr = Addr.max +MaxTick = Tick.max +AllMemory = AddrRange(0, MaxAddr) + + +##################################################################### +# +# Port objects +# +# Ports are used to interconnect objects in the memory system. +# +##################################################################### + +# Port reference: encapsulates a reference to a particular port on a +# particular SimObject. +class PortRef(object): + def __init__(self, simobj, name): + assert(isSimObject(simobj) or isSimObjectClass(simobj)) + self.simobj = simobj + self.name = name + self.peer = None # not associated with another port yet + self.ccConnected = False # C++ port connection done? + self.index = -1 # always -1 for non-vector ports + + def __str__(self): + return '%s.%s' % (self.simobj, self.name) + + # for config.ini, print peer's name (not ours) + def ini_str(self): + return str(self.peer) + + def __getattr__(self, attr): + if attr == 'peerObj': + # shorthand for proxies + return self.peer.simobj + raise AttributeError, "'%s' object has no attribute '%s'" % \ + (self.__class__.__name__, attr) + + # Full connection is symmetric (both ways). Called via + # SimObject.__setattr__ as a result of a port assignment, e.g., + # "obj1.portA = obj2.portB", or via VectorPortElementRef.__setitem__, + # e.g., "obj1.portA[3] = obj2.portB". + def connect(self, other): + if isinstance(other, VectorPortRef): + # reference to plain VectorPort is implicit append + other = other._get_next() + if self.peer and not proxy.isproxy(self.peer): + print "warning: overwriting port", self, \ + "value", self.peer, "with", other + self.peer = other + if proxy.isproxy(other): + other.set_param_desc(PortParamDesc()) + elif isinstance(other, PortRef): + if other.peer is not self: + other.connect(self) + else: + raise TypeError, \ + "assigning non-port reference '%s' to port '%s'" \ + % (other, self) + + def clone(self, simobj, memo): + if memo.has_key(self): + return memo[self] + newRef = copy.copy(self) + memo[self] = newRef + newRef.simobj = simobj + assert(isSimObject(newRef.simobj)) + if self.peer and not proxy.isproxy(self.peer): + peerObj = memo[self.peer.simobj] + newRef.peer = self.peer.clone(peerObj, memo) + assert(not isinstance(newRef.peer, VectorPortRef)) + return newRef + + def unproxy(self, simobj): + assert(simobj is self.simobj) + if proxy.isproxy(self.peer): + try: + realPeer = self.peer.unproxy(self.simobj) + except: + print "Error in unproxying port '%s' of %s" % \ + (self.name, self.simobj.path()) + raise + self.connect(realPeer) + + # Call C++ to create corresponding port connection between C++ objects + def ccConnect(self): + if self.ccConnected: # already done this + return + peer = self.peer + cc_main.connectPorts(self.simobj.getCCObject(), self.name, self.index, + peer.simobj.getCCObject(), peer.name, peer.index) + self.ccConnected = True + peer.ccConnected = True + +# A reference to an individual element of a VectorPort... much like a +# PortRef, but has an index. +class VectorPortElementRef(PortRef): + def __init__(self, simobj, name, index): + PortRef.__init__(self, simobj, name) + self.index = index + + def __str__(self): + return '%s.%s[%d]' % (self.simobj, self.name, self.index) + +# A reference to a complete vector-valued port (not just a single element). +# Can be indexed to retrieve individual VectorPortElementRef instances. +class VectorPortRef(object): + def __init__(self, simobj, name): + assert(isSimObject(simobj) or isSimObjectClass(simobj)) + self.simobj = simobj + self.name = name + self.elements = [] + + def __str__(self): + return '%s.%s[:]' % (self.simobj, self.name) + + # for config.ini, print peer's name (not ours) + def ini_str(self): + return ' '.join([el.ini_str() for el in self.elements]) + + def __getitem__(self, key): + if not isinstance(key, int): + raise TypeError, "VectorPort index must be integer" + if key >= len(self.elements): + # need to extend list + ext = [VectorPortElementRef(self.simobj, self.name, i) + for i in range(len(self.elements), key+1)] + self.elements.extend(ext) + return self.elements[key] + + def _get_next(self): + return self[len(self.elements)] + + def __setitem__(self, key, value): + if not isinstance(key, int): + raise TypeError, "VectorPort index must be integer" + self[key].connect(value) + + def connect(self, other): + if isinstance(other, (list, tuple)): + # Assign list of port refs to vector port. + # For now, append them... not sure if that's the right semantics + # or if it should replace the current vector. + for ref in other: + self._get_next().connect(ref) + else: + # scalar assignment to plain VectorPort is implicit append + self._get_next().connect(other) + + def clone(self, simobj, memo): + if memo.has_key(self): + return memo[self] + newRef = copy.copy(self) + memo[self] = newRef + newRef.simobj = simobj + assert(isSimObject(newRef.simobj)) + newRef.elements = [el.clone(simobj, memo) for el in self.elements] + return newRef + + def unproxy(self, simobj): + [el.unproxy(simobj) for el in self.elements] + + def ccConnect(self): + [el.ccConnect() for el in self.elements] + +# Port description object. Like a ParamDesc object, this represents a +# logical port in the SimObject class, not a particular port on a +# SimObject instance. The latter are represented by PortRef objects. +class Port(object): + # Port("description") or Port(default, "description") + def __init__(self, *args): + if len(args) == 1: + self.desc = args[0] + elif len(args) == 2: + self.default = args[0] + self.desc = args[1] + else: + raise TypeError, 'wrong number of arguments' + # self.name is set by SimObject class on assignment + # e.g., pio_port = Port("blah") sets self.name to 'pio_port' + + # Generate a PortRef for this port on the given SimObject with the + # given name + def makeRef(self, simobj): + return PortRef(simobj, self.name) + + # Connect an instance of this port (on the given SimObject with + # the given name) with the port described by the supplied PortRef + def connect(self, simobj, ref): + self.makeRef(simobj).connect(ref) + +# VectorPort description object. Like Port, but represents a vector +# of connections (e.g., as on a Bus). +class VectorPort(Port): + def __init__(self, *args): + Port.__init__(self, *args) + self.isVec = True + + def makeRef(self, simobj): + return VectorPortRef(simobj, self.name) + +# 'Fake' ParamDesc for Port references to assign to the _pdesc slot of +# proxy objects (via set_param_desc()) so that proxy error messages +# make sense. +class PortParamDesc(object): + __metaclass__ = Singleton + + ptype_str = 'Port' + ptype = Port + + +__all__ = ['Param', 'VectorParam', + 'Enum', 'Bool', 'String', 'Float', + 'Int', 'Unsigned', 'Int8', 'UInt8', 'Int16', 'UInt16', + 'Int32', 'UInt32', 'Int64', 'UInt64', + 'Counter', 'Addr', 'Tick', 'Percent', + 'TcpPort', 'UdpPort', 'EthernetAddr', + 'MemorySize', 'MemorySize32', + 'Latency', 'Frequency', 'RootClock', 'Clock', + 'NetworkBandwidth', 'MemoryBandwidth', + 'Range', 'AddrRange', 'TickRange', + 'MaxAddr', 'MaxTick', 'AllMemory', + 'NextEthernetAddr', 'NULL', + 'Port', 'VectorPort'] + +# see comment on imports at end of __init__.py. +from SimObject import isSimObject, isSimObjectSequence, isSimObjectClass +import proxy +import objects +import cc_main diff --git a/src/python/m5/proxy.py b/src/python/m5/proxy.py new file mode 100644 index 000000000..7ebc0ae19 --- /dev/null +++ b/src/python/m5/proxy.py @@ -0,0 +1,206 @@ +# Copyright (c) 2004-2006 The Regents of The University of Michigan +# 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. +# +# Authors: Steve Reinhardt +# Nathan Binkert + +##################################################################### +# +# Proxy object support. +# +##################################################################### + +class BaseProxy(object): + def __init__(self, search_self, search_up): + self._search_self = search_self + self._search_up = search_up + self._multiplier = None + + def __str__(self): + if self._search_self and not self._search_up: + s = 'Self' + elif not self._search_self and self._search_up: + s = 'Parent' + else: + s = 'ConfusedProxy' + return s + '.' + self.path() + + def __setattr__(self, attr, value): + if not attr.startswith('_'): + raise AttributeError, \ + "cannot set attribute '%s' on proxy object" % attr + super(BaseProxy, self).__setattr__(attr, value) + + # support multiplying proxies by constants + def __mul__(self, other): + if not isinstance(other, (int, long, float)): + raise TypeError, "Proxy multiplier must be integer" + if self._multiplier == None: + self._multiplier = other + else: + # support chained multipliers + self._multiplier *= other + return self + + __rmul__ = __mul__ + + def _mulcheck(self, result): + if self._multiplier == None: + return result + return result * self._multiplier + + def unproxy(self, base): + obj = base + done = False + + if self._search_self: + result, done = self.find(obj) + + if self._search_up: + while not done: + obj = obj._parent + if not obj: + break + result, done = self.find(obj) + + if not done: + raise AttributeError, \ + "Can't resolve proxy '%s' of type '%s' from '%s'" % \ + (self.path(), self._pdesc.ptype_str, base.path()) + + if isinstance(result, BaseProxy): + if result == self: + raise RuntimeError, "Cycle in unproxy" + result = result.unproxy(obj) + + return self._mulcheck(result) + + def getindex(obj, index): + if index == None: + return obj + try: + obj = obj[index] + except TypeError: + if index != 0: + raise + # if index is 0 and item is not subscriptable, just + # use item itself (so cpu[0] works on uniprocessors) + return obj + getindex = staticmethod(getindex) + + # This method should be called once the proxy is assigned to a + # particular parameter or port to set the expected type of the + # resolved proxy + def set_param_desc(self, pdesc): + self._pdesc = pdesc + +class AttrProxy(BaseProxy): + def __init__(self, search_self, search_up, attr): + super(AttrProxy, self).__init__(search_self, search_up) + self._attr = attr + self._modifiers = [] + + def __getattr__(self, attr): + # python uses __bases__ internally for inheritance + if attr.startswith('_'): + return super(AttrProxy, self).__getattr__(self, attr) + if hasattr(self, '_pdesc'): + raise AttributeError, "Attribute reference on bound proxy" + self._modifiers.append(attr) + return self + + # support indexing on proxies (e.g., Self.cpu[0]) + def __getitem__(self, key): + if not isinstance(key, int): + raise TypeError, "Proxy object requires integer index" + self._modifiers.append(key) + return self + + def find(self, obj): + try: + val = getattr(obj, self._attr) + except: + return None, False + while isproxy(val): + val = val.unproxy(obj) + for m in self._modifiers: + if isinstance(m, str): + val = getattr(val, m) + elif isinstance(m, int): + val = val[m] + else: + assert("Item must be string or integer") + while isproxy(val): + val = val.unproxy(obj) + return val, True + + def path(self): + p = self._attr + for m in self._modifiers: + if isinstance(m, str): + p += '.%s' % m + elif isinstance(m, int): + p += '[%d]' % m + else: + assert("Item must be string or integer") + return p + +class AnyProxy(BaseProxy): + def find(self, obj): + return obj.find_any(self._pdesc.ptype) + + def path(self): + return 'any' + +def isproxy(obj): + if isinstance(obj, (BaseProxy, params.EthernetAddr)): + return True + elif isinstance(obj, (list, tuple)): + for v in obj: + if isproxy(v): + return True + return False + +class ProxyFactory(object): + def __init__(self, search_self, search_up): + self.search_self = search_self + self.search_up = search_up + + def __getattr__(self, attr): + if attr == 'any': + return AnyProxy(self.search_self, self.search_up) + else: + return AttrProxy(self.search_self, self.search_up, attr) + +# global objects for handling proxies +Parent = ProxyFactory(search_self = False, search_up = True) +Self = ProxyFactory(search_self = True, search_up = False) + +# limit exports on 'from proxy import *' +__all__ = ['Parent', 'Self'] + +# see comment on imports at end of __init__.py. +import params # for EthernetAddr diff --git a/src/python/m5/smartdict.py b/src/python/m5/smartdict.py new file mode 100644 index 000000000..d85dbd517 --- /dev/null +++ b/src/python/m5/smartdict.py @@ -0,0 +1,154 @@ +# Copyright (c) 2005 The Regents of The University of Michigan +# 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. +# +# Authors: Nathan Binkert + +# The SmartDict class fixes a couple of issues with using the content +# of os.environ or similar dicts of strings as Python variables: +# +# 1) Undefined variables should return False rather than raising KeyError. +# +# 2) String values of 'False', '0', etc., should evaluate to False +# (not just the empty string). +# +# #1 is solved by overriding __getitem__, and #2 is solved by using a +# proxy class for values and overriding __nonzero__ on the proxy. +# Everything else is just to (a) make proxies behave like normal +# values otherwise, (b) make sure any dict operation returns a proxy +# rather than a normal value, and (c) coerce values written to the +# dict to be strings. + + +from convert import * + +class Variable(str): + """Intelligent proxy class for SmartDict. Variable will use the + various convert functions to attempt to convert values to useable + types""" + def __int__(self): + return toInteger(str(self)) + def __long__(self): + return toLong(str(self)) + def __float__(self): + return toFloat(str(self)) + def __nonzero__(self): + return toBool(str(self)) + def convert(self, other): + t = type(other) + if t == bool: + return bool(self) + if t == int: + return int(self) + if t == long: + return long(self) + if t == float: + return float(self) + return str(self) + def __lt__(self, other): + return self.convert(other) < other + def __le__(self, other): + return self.convert(other) <= other + def __eq__(self, other): + return self.convert(other) == other + def __ne__(self, other): + return self.convert(other) != other + def __gt__(self, other): + return self.convert(other) > other + def __ge__(self, other): + return self.convert(other) >= other + + def __add__(self, other): + return self.convert(other) + other + def __sub__(self, other): + return self.convert(other) - other + def __mul__(self, other): + return self.convert(other) * other + def __div__(self, other): + return self.convert(other) / other + def __truediv__(self, other): + return self.convert(other) / other + + def __radd__(self, other): + return other + self.convert(other) + def __rsub__(self, other): + return other - self.convert(other) + def __rmul__(self, other): + return other * self.convert(other) + def __rdiv__(self, other): + return other / self.convert(other) + def __rtruediv__(self, other): + return other / self.convert(other) + +class UndefinedVariable(object): + """Placeholder class to represent undefined variables. Will + generally cause an exception whenever it is used, but evaluates to + zero for boolean truth testing such as in an if statement""" + def __nonzero__(self): + return False + +class SmartDict(dict): + """Dictionary class that holds strings, but intelligently converts + those strings to other types depending on their usage""" + + def __getitem__(self, key): + """returns a Variable proxy if the values exists in the database and + returns an UndefinedVariable otherwise""" + + if key in self: + return Variable(dict.get(self, key)) + else: + # Note that this does *not* change the contents of the dict, + # so that even after we call env['foo'] we still get a + # meaningful answer from "'foo' in env" (which + # calls dict.__contains__, which we do not override). + return UndefinedVariable() + + def __setitem__(self, key, item): + """intercept the setting of any variable so that we always + store strings in the dict""" + dict.__setitem__(self, key, str(item)) + + def values(self): + return [ Variable(v) for v in dict.values(self) ] + + def itervalues(self): + for value in dict.itervalues(self): + yield Variable(value) + + def items(self): + return [ (k, Variable(v)) for k,v in dict.items(self) ] + + def iteritems(self): + for key,value in dict.iteritems(self): + yield key, Variable(value) + + def get(self, key, default='False'): + return Variable(dict.get(self, key, str(default))) + + def setdefault(self, key, default='False'): + return Variable(dict.setdefault(self, key, str(default))) + +__all__ = [ 'SmartDict' ] diff --git a/src/python/m5/util.py b/src/python/m5/util.py new file mode 100644 index 000000000..28b8b1b94 --- /dev/null +++ b/src/python/m5/util.py @@ -0,0 +1,59 @@ +# Copyright (c) 2004-2006 The Regents of The University of Michigan +# 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. +# +# Authors: Steve Reinhardt +# Nathan Binkert + +############################# +# +# Utility classes & methods +# +############################# + +class Singleton(type): + def __call__(cls, *args, **kwargs): + if hasattr(cls, '_instance'): + return cls._instance + + cls._instance = super(Singleton, cls).__call__(*args, **kwargs) + return cls._instance + +# Apply method to object. +# applyMethod(obj, 'meth', <args>) is equivalent to obj.meth(<args>) +def applyMethod(obj, meth, *args, **kwargs): + return getattr(obj, meth)(*args, **kwargs) + +# If the first argument is an (non-sequence) object, apply the named +# method with the given arguments. If the first argument is a +# sequence, apply the method to each element of the sequence (a la +# 'map'). +def applyOrMap(objOrSeq, meth, *args, **kwargs): + if not isinstance(objOrSeq, (list, tuple)): + return applyMethod(objOrSeq, meth, *args, **kwargs) + else: + return [applyMethod(o, meth, *args, **kwargs) for o in objOrSeq] + + diff --git a/src/sim/async.hh b/src/sim/async.hh new file mode 100644 index 000000000..50ae73040 --- /dev/null +++ b/src/sim/async.hh @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __ASYNC_HH__ +#define __ASYNC_HH__ + +/// +/// @file sim/async.hh +/// This file defines flags used to handle asynchronous simulator events. +/// + +/// @name Asynchronous event flags. +/// To avoid races, signal handlers simply set these flags, which are +/// then checked in the main event loop. Defined in main.cc. +/// @note See the PollQueue object (in pollevent.hh) for the use of async_io and async_alarm. +//@{ +extern volatile bool async_event; ///< Some asynchronous event has happened. +extern volatile bool async_dump; ///< Async request to dump stats. +extern volatile bool async_exit; ///< Async request to exit simulator. +extern volatile bool async_io; ///< Async I/O request (SIGIO). +extern volatile bool async_alarm; ///< Async alarm event (SIGALRM). +//@} + +#endif // __ASYNC_HH__ diff --git a/src/sim/builder.cc b/src/sim/builder.cc new file mode 100644 index 000000000..9074cc899 --- /dev/null +++ b/src/sim/builder.cc @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <assert.h> + +#include "base/inifile.hh" +#include "base/misc.hh" +#include "sim/builder.hh" +#include "sim/host.hh" +#include "sim/sim_object.hh" +#include "sim/root.hh" + +using namespace std; + +SimObjectBuilder::SimObjectBuilder(const std::string &_iniSection) + : ParamContext(_iniSection, NoAutoInit) +{ +} + +SimObjectBuilder::~SimObjectBuilder() +{ +} + +/////////////////////////////////////////// +// +// SimObjectBuilder member definitions +// +/////////////////////////////////////////// + +// override ParamContext::parseParams() to check params based on +// instance name first. If not found, then check based on iniSection +// (as in default ParamContext implementation). +void +SimObjectBuilder::parseParams(IniFile &iniFile) +{ + iniFilePtr = &iniFile; // set object member + + ParamList::iterator i; + + for (i = paramList->begin(); i != paramList->end(); ++i) { + string string_value; + if (iniFile.find(iniSection, (*i)->name, string_value)) + (*i)->parse(string_value); + } +} + + +void +SimObjectBuilder::printErrorProlog(ostream &os) +{ + ccprintf(os, "Error creating object '%s':\n", iniSection); +} + + +//////////////////////////////////////////////////////////////////////// +// +// SimObjectClass member definitions +// +//////////////////////////////////////////////////////////////////////// + +// Map of class names to SimObjectBuilder creation functions. Need to +// make this a pointer so we can force initialization on the first +// reference; otherwise, some SimObjectClass constructors may be invoked +// before the classMap constructor. +map<string,SimObjectClass::CreateFunc> *SimObjectClass::classMap = NULL; + +// SimObjectClass constructor: add mapping to classMap +SimObjectClass::SimObjectClass(const string &className, CreateFunc createFunc) +{ + if (classMap == NULL) + classMap = new map<string,SimObjectClass::CreateFunc>(); + + if ((*classMap)[className]) + panic("Error: simulation object class '%s' redefined\n", className); + + // add className --> createFunc to class map + (*classMap)[className] = createFunc; +} + + +// +// +SimObject * +SimObjectClass::createObject(IniFile &configDB, const std::string &iniSection) +{ + string type; + if (!configDB.find(iniSection, "type", type)) { + // no C++ type associated with this object + return NULL; + } + + // look up className to get appropriate createFunc + if (classMap->find(type) == classMap->end()) + panic("Simulator object type '%s' not found.\n", type); + + + CreateFunc createFunc = (*classMap)[type]; + + // call createFunc with config hierarchy node to get object + // builder instance (context with parameters for object creation) + SimObjectBuilder *objectBuilder = (*createFunc)(iniSection); + + assert(objectBuilder != NULL); + + // parse all parameters in context to generate parameter values + objectBuilder->parseParams(configDB); + + // now create the actual simulation object + SimObject *object = objectBuilder->create(); + + assert(object != NULL); + + // echo object parameters to stats file (for documenting the + // config used to generate the associated stats) + ccprintf(*configStream, "[%s]\n", object->name()); + ccprintf(*configStream, "type=%s\n", type); + objectBuilder->showParams(*configStream); + ccprintf(*configStream, "\n"); + + // done with the SimObjectBuilder now + delete objectBuilder; + + return object; +} + + +// +// static method: +// +void +SimObjectClass::describeAllClasses(ostream &os) +{ + map<string,CreateFunc>::iterator iter; + + for (iter = classMap->begin(); iter != classMap->end(); ++iter) { + const string &className = iter->first; + CreateFunc createFunc = iter->second; + + os << "[" << className << "]\n"; + + // create dummy object builder just to instantiate parameters + SimObjectBuilder *objectBuilder = (*createFunc)(""); + + // now get the object builder to describe ite params + objectBuilder->describeParams(os); + + os << endl; + + // done with the object builder now + delete objectBuilder; + } +} diff --git a/src/sim/builder.hh b/src/sim/builder.hh new file mode 100644 index 000000000..aa0e3d17c --- /dev/null +++ b/src/sim/builder.hh @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __BUILDER_HH__ +#define __BUILDER_HH__ + +#include <iosfwd> +#include <list> +#include <map> +#include <vector> + +#include "sim/param.hh" + +class SimObject; + +// +// A SimObjectBuilder serves as an evaluation context for a set of +// parameters that describe a specific instance of a SimObject. This +// evaluation context corresponds to a section in the .ini file (as +// with the base ParamContext) plus an optional node in the +// configuration hierarchy (the configNode member) for resolving +// SimObject references. SimObjectBuilder is an abstract superclass; +// derived classes specialize the class for particular subclasses of +// SimObject (e.g., BaseCache). +// +// For typical usage, see the definition of +// SimObjectClass::createObject(). +// +class SimObjectBuilder : public ParamContext +{ + public: + SimObjectBuilder(const std::string &_iniSection); + + virtual ~SimObjectBuilder(); + + // call parse() on all params in this context to convert string + // representations to parameter values + virtual void parseParams(IniFile &iniFile); + + // parameter error prolog (override of ParamContext) + virtual void printErrorProlog(std::ostream &); + + // generate the name for this SimObject instance (derived from the + // configuration hierarchy node label and position) + virtual const std::string &getInstanceName() { return iniSection; } + + // Create the actual SimObject corresponding to the parameter + // values in this context. This function is overridden in derived + // classes to call a specific constructor for a particular + // subclass of SimObject. + virtual SimObject *create() = 0; +}; + + +// +// Handy macros for initializing parameter members of classes derived +// from SimObjectBuilder. Assumes that the name of the parameter +// member object is the same as the textual parameter name seen by the +// user. (Note that '#p' is expanded by the preprocessor to '"p"'.) +// +#define INIT_PARAM(p, desc) p(this, #p, desc) +#define INIT_PARAM_DFLT(p, desc, dflt) p(this, #p, desc, dflt) + +// +// Initialize an enumeration variable... assumes that 'map' is the +// name of an array of mappings (char * for SimpleEnumParam, or +// EnumParamMap for MappedEnumParam). +// +#define INIT_ENUM_PARAM(p, desc, map) \ + p(this, #p, desc, map, sizeof(map)/sizeof(map[0])) +#define INIT_ENUM_PARAM_DFLT(p, desc, map, dflt) \ + p(this, #p, desc, map, sizeof(map)/sizeof(map[0]), dflt) + +// +// An instance of SimObjectClass corresponds to a class derived from +// SimObject. The SimObjectClass instance serves to bind the string +// name (found in the config file) to a function that creates an +// instance of the appropriate derived class. +// +// This would be much cleaner in Smalltalk or Objective-C, where types +// are first-class objects themselves. +// +class SimObjectClass +{ + public: + // Type CreateFunc is a pointer to a function that creates a new + // simulation object builder based on a .ini-file parameter + // section (specified by the first string argument), a unique name + // for the object (specified by the second string argument), and + // an optional config hierarchy node (specified by the third + // argument). A pointer to the new SimObjectBuilder is returned. + typedef SimObjectBuilder *(*CreateFunc)(const std::string &iniSection); + + static std::map<std::string,CreateFunc> *classMap; + + // Constructor. For example: + // + // SimObjectClass baseCacheClass("BaseCache", newBaseCacheBuilder); + // + SimObjectClass(const std::string &className, CreateFunc createFunc); + + // create SimObject given name of class and pointer to + // configuration hierarchy node + static SimObject *createObject(IniFile &configDB, + const std::string &iniSection); + + // print descriptions of all parameters registered with all + // SimObject classes + static void describeAllClasses(std::ostream &os); +}; + +// +// Macros to encapsulate the magic of declaring & defining +// SimObjectBuilder and SimObjectClass objects +// + +#define BEGIN_DECLARE_SIM_OBJECT_PARAMS(OBJ_CLASS) \ +class OBJ_CLASS##Builder : public SimObjectBuilder \ +{ \ + public: + +#define END_DECLARE_SIM_OBJECT_PARAMS(OBJ_CLASS) \ + \ + OBJ_CLASS##Builder(const std::string &iniSection); \ + virtual ~OBJ_CLASS##Builder() {} \ + \ + OBJ_CLASS *create(); \ +}; + +#define BEGIN_INIT_SIM_OBJECT_PARAMS(OBJ_CLASS) \ + OBJ_CLASS##Builder::OBJ_CLASS##Builder(const std::string &iSec) \ + : SimObjectBuilder(iSec), + + +#define END_INIT_SIM_OBJECT_PARAMS(OBJ_CLASS) \ +{ \ +} + +#define CREATE_SIM_OBJECT(OBJ_CLASS) \ +OBJ_CLASS *OBJ_CLASS##Builder::create() + +#define REGISTER_SIM_OBJECT(CLASS_NAME, OBJ_CLASS) \ +SimObjectBuilder * \ +new##OBJ_CLASS##Builder(const std::string &iniSection) \ +{ \ + return new OBJ_CLASS##Builder(iniSection); \ +} \ + \ +SimObjectClass the##OBJ_CLASS##Class(CLASS_NAME, \ + new##OBJ_CLASS##Builder); \ + \ +/* see param.hh */ \ +DEFINE_SIM_OBJECT_CLASS_NAME(CLASS_NAME, OBJ_CLASS) + +/* Macros that use the namespace for sinic... yuk. */ +#define BEGIN_DECLARE_SIM_OBJECT_PARAMS_WNS(NAME_SPACE, OBJ_CLASS) \ +class NAME_SPACE##OBJ_CLASS##Builder : public SimObjectBuilder \ +{ \ + public: + +#define END_DECLARE_SIM_OBJECT_PARAMS_WNS(NAME_SPACE, OBJ_CLASS) \ + \ + NAME_SPACE##OBJ_CLASS##Builder(const std::string &iniSection); \ + virtual ~NAME_SPACE##OBJ_CLASS##Builder() {} \ + \ + NAME_SPACE::OBJ_CLASS *create(); \ +}; + +#define BEGIN_INIT_SIM_OBJECT_PARAMS_WNS(NAME_SPACE, OBJ_CLASS) \ + NAME_SPACE::OBJ_CLASS##Builder::OBJ_CLASS##Builder(const std::string &iSec) \ + : SimObjectBuilder(iSec), + + +#define END_INIT_SIM_OBJECT_PARAMS_WNS(NAME_SPACE, OBJ_CLASS) \ +{ \ +} + +#define CREATE_SIM_OBJECT_WNS(NAME_SPACE, OBJ_CLASS) \ +NAME_SPACE::OBJ_CLASS *NAME_SPACE##OBJ_CLASS##Builder::create() + +#define REGISTER_SIM_OBJECT_WNS(NAME_SPACE, CLASS_NAME, OBJ_CLASS) \ +SimObjectBuilder * \ +new##NAME_SPACEi##OBJ_CLASS##Builder(const std::string &iniSection) \ +{ \ + return new NAME_SPACE##OBJ_CLASS##Builder(iniSection); \ +} \ + \ +SimObjectClass the##NAME_SPACE##OBJ_CLASS##Class(CLASS_NAME, \ + new##NAME_SPACE##OBJ_CLASS##Builder); \ + \ +/* see param.hh */ \ +DEFINE_SIM_OBJECT_CLASS_NAME(CLASS_NAME, NAME_SPACE##OBJ_CLASS) + + + +#endif // __BUILDER_HH__ diff --git a/src/sim/byteswap.hh b/src/sim/byteswap.hh new file mode 100644 index 000000000..7648b8fcd --- /dev/null +++ b/src/sim/byteswap.hh @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2004 The Regents of The University of Michigan + * 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. + * + * Authors: Gabe Black + * Ali Saidi + * Nathan Binkert + */ + +//The purpose of this file is to provide endainness conversion utility +//functions. Depending on the endianness of the guest system, either +//the LittleEndianGuest or BigEndianGuest namespace is used. + +#ifndef __SIM_BYTE_SWAP_HH__ +#define __SIM_BYTE_SWAP_HH__ + +#include "base/misc.hh" +#include "sim/host.hh" + +// This lets us figure out what the byte order of the host system is +#if defined(linux) +#include <endian.h> +// If this is a linux system, lets used the optimized definitions if they exist. +// If one doesn't exist, we pretty much get what is listed below, so it all +// works out +#include <byteswap.h> +#else +#include <machine/endian.h> +#endif + +#if defined(__APPLE__) +#include <libkern/OSByteOrder.h> +#endif + +//These functions actually perform the swapping for parameters +//of various bit lengths +static inline uint64_t +swap_byte64(uint64_t x) +{ +#if defined(linux) + return bswap_64(x); +#elif defined(__APPLE__) + return OSSwapInt64(x); +#else + return (uint64_t)((((uint64_t)(x) & 0xff) << 56) | + ((uint64_t)(x) & 0xff00ULL) << 40 | + ((uint64_t)(x) & 0xff0000ULL) << 24 | + ((uint64_t)(x) & 0xff000000ULL) << 8 | + ((uint64_t)(x) & 0xff00000000ULL) >> 8 | + ((uint64_t)(x) & 0xff0000000000ULL) >> 24 | + ((uint64_t)(x) & 0xff000000000000ULL) >> 40 | + ((uint64_t)(x) & 0xff00000000000000ULL) >> 56) ; +#endif +} + +static inline uint32_t +swap_byte32(uint32_t x) +{ +#if defined(linux) + return bswap_32(x); +#elif defined(__APPLE__) + return OSSwapInt32(x); +#else + return (uint32_t)(((uint32_t)(x) & 0xff) << 24 | + ((uint32_t)(x) & 0xff00) << 8 | ((uint32_t)(x) & 0xff0000) >> 8 | + ((uint32_t)(x) & 0xff000000) >> 24); +#endif +} + +static inline uint16_t +swap_byte16(uint16_t x) +{ +#if defined(linux) + return bswap_16(x); +#elif defined(__APPLE__) + return OSSwapInt16(x); +#else + return (uint16_t)(((uint16_t)(x) & 0xff) << 8 | + ((uint16_t)(x) & 0xff00) >> 8); +#endif +} + +// This function lets the compiler figure out how to call the +// swap_byte functions above for different data types. Since the +// sizeof() values are known at compiel time, it should inline to a +// direct call to the right swap_byteNN() function. +template <typename T> +static inline T swap_byte(T x) { + if (sizeof(T) == 8) + return swap_byte64((uint64_t)x); + else if (sizeof(T) == 4) + return swap_byte32((uint32_t)x); + else if (sizeof(T) == 2) + return swap_byte16((uint16_t)x); + else if (sizeof(T) == 1) + return x; + else + panic("Can't byte-swap values larger than 64 bits"); +} + +//The conversion functions with fixed endianness on both ends don't need to +//be in a namespace +template <typename T> static inline T betole(T value) {return swap_byte(value);} +template <typename T> static inline T letobe(T value) {return swap_byte(value);} + +//For conversions not involving the guest system, we can define the functions +//conditionally based on the BYTE_ORDER macro and outside of the namespaces +#if BYTE_ORDER == BIG_ENDIAN +template <typename T> static inline T htole(T value) {return swap_byte(value);} +template <typename T> static inline T letoh(T value) {return swap_byte(value);} +template <typename T> static inline T htobe(T value) {return value;} +template <typename T> static inline T betoh(T value) {return value;} +#elif BYTE_ORDER == LITTLE_ENDIAN +template <typename T> static inline T htole(T value) {return value;} +template <typename T> static inline T letoh(T value) {return value;} +template <typename T> static inline T htobe(T value) {return swap_byte(value);} +template <typename T> static inline T betoh(T value) {return swap_byte(value);} +#else + #error Invalid Endianess +#endif + +namespace BigEndianGuest +{ + template <typename T> + static inline T gtole(T value) {return betole(value);} + template <typename T> + static inline T letog(T value) {return letobe(value);} + template <typename T> + static inline T gtobe(T value) {return value;} + template <typename T> + static inline T betog(T value) {return value;} + template <typename T> + static inline T htog(T value) {return htobe(value);} + template <typename T> + static inline T gtoh(T value) {return betoh(value);} +} + +namespace LittleEndianGuest +{ + template <typename T> + static inline T gtole(T value) {return value;} + template <typename T> + static inline T letog(T value) {return value;} + template <typename T> + static inline T gtobe(T value) {return letobe(value);} + template <typename T> + static inline T betog(T value) {return betole(value);} + template <typename T> + static inline T htog(T value) {return htole(value);} + template <typename T> + static inline T gtoh(T value) {return letoh(value);} +} +#endif // __SIM_BYTE_SWAP_HH__ diff --git a/src/sim/debug.cc b/src/sim/debug.cc new file mode 100644 index 000000000..be9566836 --- /dev/null +++ b/src/sim/debug.cc @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#include <sys/types.h> +#include <signal.h> +#include <unistd.h> + +#include <string> +#include <vector> + +#include "sim/debug.hh" +#include "sim/eventq.hh" +#include "sim/param.hh" +#include "sim/sim_events.hh" + +using namespace std; + +void +debug_break() +{ +#ifndef NDEBUG + kill(getpid(), SIGTRAP); +#else + cprintf("debug_break suppressed, compiled with NDEBUG\n"); +#endif +} + +// +// Debug event: place a breakpoint on the process function and +// schedule the event to break at a particular cycle +// +class DebugBreakEvent : public Event +{ + public: + + DebugBreakEvent(EventQueue *q, Tick _when); + + void process(); // process event + virtual const char *description(); +}; + +// +// constructor: schedule at specified time +// +DebugBreakEvent::DebugBreakEvent(EventQueue *q, Tick _when) + : Event(q, Debug_Break_Pri) +{ + setFlags(AutoDelete); + schedule(_when); +} + +// +// handle debug event: set debugger breakpoint on this function +// +void +DebugBreakEvent::process() +{ + debug_break(); +} + + +const char * +DebugBreakEvent::description() +{ + return "debug break"; +} + +// +// Parameter context for global debug options +// +class DebugContext : public ParamContext +{ + public: + DebugContext(const string &_iniSection) + : ParamContext(_iniSection) {} + void checkParams(); +}; + +DebugContext debugParams("debug"); + +VectorParam<Tick> break_cycles(&debugParams, "break_cycles", + "cycle(s) to create breakpoint events"); + +void +DebugContext::checkParams() +{ + if (break_cycles.isValid()) { + vector<Tick> &cycles = break_cycles; + + vector<Tick>::iterator i = cycles.begin(); + vector<Tick>::iterator end = cycles.end(); + + for (; i < end; ++i) + new DebugBreakEvent(&mainEventQueue, *i); + } +} + +// +// handy function to schedule DebugBreakEvent on main event queue +// (callable from debugger) +// +void sched_break_cycle(Tick when) +{ + new DebugBreakEvent(&mainEventQueue, when); +} + +void eventq_dump() +{ + mainEventQueue.dump(); +} + diff --git a/src/sim/debug.hh b/src/sim/debug.hh new file mode 100644 index 000000000..79792234b --- /dev/null +++ b/src/sim/debug.hh @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __DEBUG_HH__ +#define __DEBUG_HH__ + +void debug_break(); + +#endif // __DEBUG_HH__ diff --git a/src/sim/eventq.cc b/src/sim/eventq.cc new file mode 100644 index 000000000..6ae838897 --- /dev/null +++ b/src/sim/eventq.cc @@ -0,0 +1,269 @@ +/* + * Copyright (c) 2000-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + * Steve Raasch + */ + +#include <assert.h> + +#include <iostream> +#include <string> +#include <vector> + +#include "cpu/smt.hh" +#include "base/misc.hh" + +#include "sim/eventq.hh" +#include "base/trace.hh" +#include "sim/root.hh" + +using namespace std; + +// +// Main Event Queue +// +// Events on this queue are processed at the *beginning* of each +// cycle, before the pipeline simulation is performed. +// +EventQueue mainEventQueue("MainEventQueue"); + +void +EventQueue::insert(Event *event) +{ + if (head == NULL || event->when() < head->when() || + (event->when() == head->when() && + event->priority() <= head->priority())) { + event->next = head; + head = event; + } else { + Event *prev = head; + Event *curr = head->next; + + while (curr) { + if (event->when() <= curr->when() && + (event->when() < curr->when() || + event->priority() <= curr->priority())) + break; + + prev = curr; + curr = curr->next; + } + + event->next = curr; + prev->next = event; + } +} + +void +EventQueue::remove(Event *event) +{ + if (head == NULL) + return; + + if (head == event){ + head = event->next; + return; + } + + Event *prev = head; + Event *curr = head->next; + while (curr && curr != event) { + prev = curr; + curr = curr->next; + } + + if (curr == event) + prev->next = curr->next; +} + +Event * +EventQueue::serviceOne() +{ + Event *event = head; + event->clearFlags(Event::Scheduled); + head = event->next; + + // handle action + if (!event->squashed()) { + event->process(); + if (event->isExitEvent()) { + assert(!event->getFlags(Event::AutoDelete)); // would be silly + return event; + } + } else { + event->clearFlags(Event::Squashed); + } + + if (event->getFlags(Event::AutoDelete) && !event->scheduled()) + delete event; + + return NULL; +} + + +void +Event::serialize(std::ostream &os) +{ + SERIALIZE_SCALAR(_when); + SERIALIZE_SCALAR(_priority); + SERIALIZE_ENUM(_flags); +} + + +void +Event::unserialize(Checkpoint *cp, const string §ion) +{ + if (scheduled()) + deschedule(); + + UNSERIALIZE_SCALAR(_when); + UNSERIALIZE_SCALAR(_priority); + + // need to see if original event was in a scheduled, unsquashed + // state, but don't want to restore those flags in the current + // object itself (since they aren't immediately true) + UNSERIALIZE_ENUM(_flags); + bool wasScheduled = (_flags & Scheduled) && !(_flags & Squashed); + _flags &= ~(Squashed | Scheduled); + + if (wasScheduled) { + DPRINTF(Config, "rescheduling at %d\n", _when); + schedule(_when); + } +} + +void +EventQueue::serialize(ostream &os) +{ + std::list<Event *> eventPtrs; + + int numEvents = 0; + Event *event = head; + while (event) { + if (event->getFlags(Event::AutoSerialize)) { + eventPtrs.push_back(event); + paramOut(os, csprintf("event%d", numEvents++), event->name()); + } + event = event->next; + } + + SERIALIZE_SCALAR(numEvents); + + for (std::list<Event *>::iterator it=eventPtrs.begin(); + it != eventPtrs.end(); ++it) { + (*it)->nameOut(os); + (*it)->serialize(os); + } +} + +void +EventQueue::unserialize(Checkpoint *cp, const std::string §ion) +{ + int numEvents; + UNSERIALIZE_SCALAR(numEvents); + + std::string eventName; + for (int i = 0; i < numEvents; i++) { + // get the pointer value associated with the event + paramIn(cp, section, csprintf("event%d", i), eventName); + + // create the event based on its pointer value + Serializable::create(cp, eventName); + } +} + +void +EventQueue::dump() +{ + cprintf("============================================================\n"); + cprintf("EventQueue Dump (cycle %d)\n", curTick); + cprintf("------------------------------------------------------------\n"); + + if (empty()) + cprintf("<No Events>\n"); + else { + Event *event = head; + while (event) { + event->dump(); + event = event->next; + } + } + + cprintf("============================================================\n"); +} + +extern "C" +void +dumpMainQueue() +{ + mainEventQueue.dump(); +} + + +const char * +Event::description() +{ + return "generic"; +} + +#if TRACING_ON +void +Event::trace(const char *action) +{ + // This DPRINTF is unconditional because calls to this function + // are protected by an 'if (DTRACE(Event))' in the inlined Event + // methods. + // + // This is just a default implementation for derived classes where + // it's not worth doing anything special. If you want to put a + // more informative message in the trace, override this method on + // the particular subclass where you have the information that + // needs to be printed. + DPRINTFN("%s event %s @ %d\n", description(), action, when()); +} +#endif + +void +Event::dump() +{ + cprintf("Event (%s)\n", description()); + cprintf("Flags: %#x\n", _flags); +#if TRACING_ON + cprintf("Created: %d\n", when_created); +#endif + if (scheduled()) { +#if TRACING_ON + cprintf("Scheduled at %d\n", when_scheduled); +#endif + cprintf("Scheduled for %d, priority %d\n", when(), _priority); + } + else { + cprintf("Not Scheduled\n"); + } +} diff --git a/src/sim/eventq.hh b/src/sim/eventq.hh new file mode 100644 index 000000000..537bfb918 --- /dev/null +++ b/src/sim/eventq.hh @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2000-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +/* @file + * EventQueue interfaces + */ + +#ifndef __SIM_EVENTQ_HH__ +#define __SIM_EVENTQ_HH__ + +#include <assert.h> + +#include <algorithm> +#include <map> +#include <string> +#include <vector> + +#include "sim/host.hh" // for Tick + +#include "base/fast_alloc.hh" +#include "base/misc.hh" +#include "base/trace.hh" +#include "sim/serialize.hh" + +class EventQueue; // forward declaration + +////////////////////// +// +// Main Event Queue +// +// Events on this queue are processed at the *beginning* of each +// cycle, before the pipeline simulation is performed. +// +// defined in eventq.cc +// +////////////////////// +extern EventQueue mainEventQueue; + + +/* + * An item on an event queue. The action caused by a given + * event is specified by deriving a subclass and overriding the + * process() member function. + */ +class Event : public Serializable, public FastAlloc +{ + friend class EventQueue; + + private: + /// queue to which this event belongs (though it may or may not be + /// scheduled on this queue yet) + EventQueue *queue; + + Event *next; + + Tick _when; //!< timestamp when event should be processed + int _priority; //!< event priority + char _flags; + + protected: + enum Flags { + None = 0x0, + Squashed = 0x1, + Scheduled = 0x2, + AutoDelete = 0x4, + AutoSerialize = 0x8, + IsExitEvent = 0x10 + }; + + bool getFlags(Flags f) const { return (_flags & f) == f; } + void setFlags(Flags f) { _flags |= f; } + void clearFlags(Flags f) { _flags &= ~f; } + + protected: + EventQueue *theQueue() const { return queue; } + +#if TRACING_ON + Tick when_created; //!< Keep track of creation time For debugging + Tick when_scheduled; //!< Keep track of creation time For debugging + + virtual void trace(const char *action); //!< trace event activity +#else + void trace(const char *) {} +#endif + + unsigned annotated_value; + + public: + + /// Event priorities, to provide tie-breakers for events scheduled + /// at the same cycle. Most events are scheduled at the default + /// priority; these values are used to control events that need to + /// be ordered within a cycle. + enum Priority { + /// Breakpoints should happen before anything else, so we + /// don't miss any action when debugging. + Debug_Break_Pri = -100, + + /// For some reason "delayed" inter-cluster writebacks are + /// scheduled before regular writebacks (which have default + /// priority). Steve? + Delayed_Writeback_Pri = -1, + + /// Default is zero for historical reasons. + Default_Pri = 0, + + /// CPU switches schedule the new CPU's tick event for the + /// same cycle (after unscheduling the old CPU's tick event). + /// The switch needs to come before any tick events to make + /// sure we don't tick both CPUs in the same cycle. + CPU_Switch_Pri = -31, + + /// Serailization needs to occur before tick events also, so + /// that a serialize/unserialize is identical to an on-line + /// CPU switch. + Serialize_Pri = 32, + + /// CPU ticks must come after other associated CPU events + /// (such as writebacks). + CPU_Tick_Pri = 50, + + /// Statistics events (dump, reset, etc.) come after + /// everything else, but before exit. + Stat_Event_Pri = 90, + + /// If we want to exit on this cycle, it's the very last thing + /// we do. + Sim_Exit_Pri = 100 + }; + + /* + * Event constructor + * @param queue that the event gets scheduled on + */ + Event(EventQueue *q, Priority p = Default_Pri) + : queue(q), next(NULL), _priority(p), _flags(None), +#if TRACING_ON + when_created(curTick), when_scheduled(0), +#endif + annotated_value(0) + { + } + + ~Event() {} + + virtual const std::string name() const { + return csprintf("Event_%x", (uintptr_t)this); + } + + /// Determine if the current event is scheduled + bool scheduled() const { return getFlags(Scheduled); } + + /// Schedule the event with the current priority or default priority + void schedule(Tick t); + + /// Reschedule the event with the current priority + void reschedule(Tick t); + + /// Remove the event from the current schedule + void deschedule(); + + /// Return a C string describing the event. This string should + /// *not* be dynamically allocated; just a const char array + /// describing the event class. + virtual const char *description(); + + /// Dump the current event data + void dump(); + + /* + * This member function is invoked when the event is processed + * (occurs). There is no default implementation; each subclass + * must provide its own implementation. The event is not + * automatically deleted after it is processed (to allow for + * statically allocated event objects). + * + * If the AutoDestroy flag is set, the object is deleted once it + * is processed. + */ + virtual void process() = 0; + + void annotate(unsigned value) { annotated_value = value; }; + unsigned annotation() { return annotated_value; } + + /// Squash the current event + void squash() { setFlags(Squashed); } + + /// Check whether the event is squashed + bool squashed() { return getFlags(Squashed); } + + /// See if this is a SimExitEvent (without resorting to RTTI) + bool isExitEvent() { return getFlags(IsExitEvent); } + + /// Get the time that the event is scheduled + Tick when() const { return _when; } + + /// Get the event priority + int priority() const { return _priority; } + + struct priority_compare : + public std::binary_function<Event *, Event *, bool> + { + bool operator()(const Event *l, const Event *r) const { + return l->when() >= r->when() || l->priority() >= r->priority(); + } + }; + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + +template <class T, void (T::* F)()> +void +DelayFunction(Tick when, T *object) +{ + class DelayEvent : public Event + { + private: + T *object; + + public: + DelayEvent(Tick when, T *o) + : Event(&mainEventQueue), object(o) + { setFlags(this->AutoDestroy); schedule(when); } + void process() { (object->*F)(); } + const char *description() { return "delay"; } + }; + + new DelayEvent(when, object); +} + +template <class T, void (T::* F)()> +class EventWrapper : public Event +{ + private: + T *object; + + public: + EventWrapper(T *obj, bool del = false, EventQueue *q = &mainEventQueue, + Priority p = Default_Pri) + : Event(q, p), object(obj) + { + if (del) + setFlags(AutoDelete); + } + void process() { (object->*F)(); } +}; + +/* + * Queue of events sorted in time order + */ +class EventQueue : public Serializable +{ + protected: + std::string objName; + + private: + Event *head; + + void insert(Event *event); + void remove(Event *event); + + public: + + // constructor + EventQueue(const std::string &n) + : objName(n), head(NULL) + {} + + virtual const std::string name() const { return objName; } + + // schedule the given event on this queue + void schedule(Event *ev); + void deschedule(Event *ev); + void reschedule(Event *ev); + + Tick nextTick() { return head->when(); } + Event *serviceOne(); + + // process all events up to the given timestamp. we inline a + // quick test to see if there are any events to process; if so, + // call the internal out-of-line version to process them all. + void serviceEvents(Tick when) { + while (!empty()) { + if (nextTick() > when) + break; + + /** + * @todo this assert is a good bug catcher. I need to + * make it true again. + */ + //assert(head->when() >= when && "event scheduled in the past"); + serviceOne(); + } + } + + // default: process all events up to 'now' (curTick) + void serviceEvents() { serviceEvents(curTick); } + + // return true if no events are queued + bool empty() { return head == NULL; } + + void dump(); + + Tick nextEventTime() { return empty() ? curTick : head->when(); } + + virtual void serialize(std::ostream &os); + virtual void unserialize(Checkpoint *cp, const std::string §ion); +}; + + +////////////////////// +// +// inline functions +// +// can't put these inside declaration due to circular dependence +// between Event and EventQueue classes. +// +////////////////////// + +// schedule at specified time (place on event queue specified via +// constructor) +inline void +Event::schedule(Tick t) +{ + assert(!scheduled()); +// if (t < curTick) +// warn("t is less than curTick, ensure you don't want cycles"); + + setFlags(Scheduled); +#if TRACING_ON + when_scheduled = curTick; +#endif + _when = t; + queue->schedule(this); +} + +inline void +Event::deschedule() +{ + assert(scheduled()); + + clearFlags(Squashed); + clearFlags(Scheduled); + queue->deschedule(this); +} + +inline void +Event::reschedule(Tick t) +{ + assert(scheduled()); + clearFlags(Squashed); + +#if TRACING_ON + when_scheduled = curTick; +#endif + _when = t; + queue->reschedule(this); +} + +inline void +EventQueue::schedule(Event *event) +{ + insert(event); + if (DTRACE(Event)) + event->trace("scheduled"); +} + +inline void +EventQueue::deschedule(Event *event) +{ + remove(event); + if (DTRACE(Event)) + event->trace("descheduled"); +} + +inline void +EventQueue::reschedule(Event *event) +{ + remove(event); + insert(event); + if (DTRACE(Event)) + event->trace("rescheduled"); +} + + + +#endif // __SIM_EVENTQ_HH__ diff --git a/src/sim/faults.cc b/src/sim/faults.cc new file mode 100644 index 000000000..650b728f7 --- /dev/null +++ b/src/sim/faults.cc @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Gabe Black + */ + +#include "base/misc.hh" +#include "sim/faults.hh" +#include "cpu/thread_context.hh" +#include "cpu/base.hh" + +#if !FULL_SYSTEM +void FaultBase::invoke(ThreadContext * tc) +{ + fatal("fault (%s) detected @ PC 0x%08p", name(), tc->readPC()); +} +#else +void FaultBase::invoke(ThreadContext * tc) +{ + DPRINTF(Fault, "Fault %s at PC: %#x\n", name(), tc->readPC()); + tc->getCpuPtr()->recordEvent(csprintf("Fault %s", name())); + + assert(!tc->misspeculating()); +} +#endif + +void UnimpFault::invoke(ThreadContext * tc) +{ + panic("Unimpfault: %s\n", panicStr.c_str()); +} diff --git a/src/sim/faults.hh b/src/sim/faults.hh new file mode 100644 index 000000000..00264d8fc --- /dev/null +++ b/src/sim/faults.hh @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Gabe Black + */ + +#ifndef __FAULTS_HH__ +#define __FAULTS_HH__ + +#include "base/refcnt.hh" +#include "sim/stats.hh" +#include "config/full_system.hh" + +class ThreadContext; +class FaultBase; +typedef RefCountingPtr<FaultBase> Fault; + +typedef const char * FaultName; +typedef Stats::Scalar<> FaultStat; + +// Each class has it's name statically define in _name, +// and has a virtual function to access it's name. +// The function is necessary because otherwise, all objects +// which are being accessed cast as a FaultBase * (namely +// all faults returned using the Fault type) will use the +// generic FaultBase name. + +class FaultBase : public RefCounted +{ + public: + virtual FaultName name() = 0; + virtual void invoke(ThreadContext * tc); +// template<typename T> +// bool isA() {return dynamic_cast<T *>(this);} + virtual bool isMachineCheckFault() {return false;} + virtual bool isAlignmentFault() {return false;} +}; + +FaultBase * const NoFault = 0; + +class UnimpFault : public FaultBase +{ + private: + std::string panicStr; + public: + UnimpFault(std::string _str) + : panicStr(_str) + { } + + FaultName name() {return "Unimplemented simulator feature";} + void invoke(ThreadContext * tc); +}; + +#endif // __FAULTS_HH__ diff --git a/src/sim/host.hh b/src/sim/host.hh new file mode 100644 index 000000000..9c79580b1 --- /dev/null +++ b/src/sim/host.hh @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +/** + * @file + * Defines host-dependent types: + * Counter, Tick, and (indirectly) {int,uint}{8,16,32,64}_t. + */ + +#ifndef __HOST_HH__ +#define __HOST_HH__ + +#include <inttypes.h> + +/** uint64_t constant */ +#define ULL(N) ((uint64_t)N##ULL) +/** int64_t constant */ +#define LL(N) (((int64_t)N##LL) + +/** Statistics counter type. Not much excuse for not using a 64-bit + * integer here, but if you're desperate and only run short + * simulations you could make this 32 bits. + */ +typedef int64_t Counter; + +/** + * Clock cycle count type. + * @note using an unsigned breaks the cache. + */ +typedef int64_t Tick; + +const Tick MaxTick = (1LL << 62); + +/** + * Address type + * This will probably be moved somewhere else in the near future. + * This should be at least as big as the biggest address width in use + * in the system, which will probably be 64 bits. + */ +typedef uint64_t Addr; + +const Addr MaxAddr = (Addr)-1; + +#endif // __HOST_H__ diff --git a/src/sim/main.cc b/src/sim/main.cc new file mode 100644 index 000000000..5725897f8 --- /dev/null +++ b/src/sim/main.cc @@ -0,0 +1,454 @@ +/* + * Copyright (c) 2000-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Raasch + * Nathan Binkert + * Steve Reinhardt + */ + +/// +/// @file sim/main.cc +/// +#include <Python.h> // must be before system headers... see Python docs + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <libgen.h> +#include <stdlib.h> +#include <signal.h> +#include <getopt.h> + +#include <list> +#include <string> +#include <vector> + +#include "base/callback.hh" +#include "base/inifile.hh" +#include "base/misc.hh" +#include "base/output.hh" +#include "base/pollevent.hh" +#include "base/statistics.hh" +#include "base/str.hh" +#include "base/time.hh" +#include "cpu/base.hh" +#include "cpu/smt.hh" +#include "mem/mem_object.hh" +#include "mem/port.hh" +#include "sim/async.hh" +#include "sim/builder.hh" +#include "sim/host.hh" +#include "sim/serialize.hh" +#include "sim/sim_events.hh" +#include "sim/sim_exit.hh" +#include "sim/sim_object.hh" +#include "sim/stat_control.hh" +#include "sim/stats.hh" +#include "sim/root.hh" + +using namespace std; + +// See async.h. +volatile bool async_event = false; +volatile bool async_dump = false; +volatile bool async_dumpreset = false; +volatile bool async_exit = false; +volatile bool async_io = false; +volatile bool async_alarm = false; + +/// Stats signal handler. +void +dumpStatsHandler(int sigtype) +{ + async_event = true; + async_dump = true; +} + +void +dumprstStatsHandler(int sigtype) +{ + async_event = true; + async_dumpreset = true; +} + +/// Exit signal handler. +void +exitNowHandler(int sigtype) +{ + async_event = true; + async_exit = true; +} + +/// Abort signal handler. +void +abortHandler(int sigtype) +{ + cerr << "Program aborted at cycle " << curTick << endl; + +#if TRACING_ON + // dump trace buffer, if there is one + Trace::theLog.dump(cerr); +#endif +} + +extern "C" { void init_cc_main(); } + +int +main(int argc, char **argv) +{ + signal(SIGFPE, SIG_IGN); // may occur on misspeculated paths + signal(SIGTRAP, SIG_IGN); + signal(SIGUSR1, dumpStatsHandler); // dump intermediate stats + signal(SIGUSR2, dumprstStatsHandler); // dump and reset stats + signal(SIGINT, exitNowHandler); // dump final stats and exit + signal(SIGABRT, abortHandler); + + Py_SetProgramName(argv[0]); + + // default path to m5 python code is the currently executing + // file... Python ZipImporter will find embedded zip archive. + // The M5_ARCHIVE environment variable can be used to override this. + char *m5_archive = getenv("M5_ARCHIVE"); + string pythonpath = m5_archive ? m5_archive : argv[0]; + + char *oldpath = getenv("PYTHONPATH"); + if (oldpath != NULL) { + pythonpath += ":"; + pythonpath += oldpath; + } + + if (setenv("PYTHONPATH", pythonpath.c_str(), true) == -1) + fatal("setenv: %s\n", strerror(errno)); + + // initialize embedded Python interpreter + Py_Initialize(); + PySys_SetArgv(argc, argv); + + // initialize SWIG 'cc_main' module + init_cc_main(); + + PyRun_SimpleString("import m5.main"); + PyRun_SimpleString("m5.main.main()"); + + // clean up Python intepreter. + Py_Finalize(); +} + + +void +setOutputDir(const string &dir) +{ + simout.setDirectory(dir); +} + + +IniFile inifile; + +SimObject * +createSimObject(const string &name) +{ + return SimObjectClass::createObject(inifile, name); +} + + +/** + * Pointer to the Python function that maps names to SimObjects. + */ +PyObject *resolveFunc = NULL; + +/** + * Convert a pointer to the Python object that SWIG wraps around a C++ + * SimObject pointer back to the actual C++ pointer. See main.i. + */ +extern "C" SimObject *convertSwigSimObjectPtr(PyObject *); + + +SimObject * +resolveSimObject(const string &name) +{ + PyObject *pyPtr = PyEval_CallFunction(resolveFunc, "(s)", name.c_str()); + if (pyPtr == NULL) { + PyErr_Print(); + panic("resolveSimObject: failure on call to Python for %s", name); + } + + SimObject *simObj = convertSwigSimObjectPtr(pyPtr); + if (simObj == NULL) + panic("resolveSimObject: failure on pointer conversion for %s", name); + + return simObj; +} + + +/** + * Load config.ini into C++ database. Exported to Python via SWIG; + * invoked from m5.instantiate(). + */ +void +loadIniFile(PyObject *_resolveFunc) +{ + resolveFunc = _resolveFunc; + configStream = simout.find("config.out"); + + // The configuration database is now complete; start processing it. + inifile.load(simout.resolve("config.ini")); + + // Initialize statistics database + Stats::InitSimStats(); +} + + +/** + * Look up a MemObject port. Helper function for connectPorts(). + */ +Port * +lookupPort(SimObject *so, const std::string &name, int i) +{ + MemObject *mo = dynamic_cast<MemObject *>(so); + if (mo == NULL) { + warn("error casting SimObject %s to MemObject", so->name()); + return NULL; + } + + Port *p = mo->getPort(name, i); + if (p == NULL) + warn("error looking up port %s on object %s", name, so->name()); + return p; +} + + +/** + * Connect the described MemObject ports. Called from Python via SWIG. + */ +int +connectPorts(SimObject *o1, const std::string &name1, int i1, + SimObject *o2, const std::string &name2, int i2) +{ + Port *p1 = lookupPort(o1, name1, i1); + Port *p2 = lookupPort(o2, name2, i2); + + if (p1 == NULL || p2 == NULL) { + warn("connectPorts: port lookup error"); + return 0; + } + + p1->setPeer(p2); + p2->setPeer(p1); + + return 1; +} + +/** + * Do final initialization steps after object construction but before + * start of simulation. + */ +void +finalInit() +{ + // Parse and check all non-config-hierarchy parameters. + ParamContext::parseAllContexts(inifile); + ParamContext::checkAllContexts(); + + // Echo all parameter settings to stats file as well. + ParamContext::showAllContexts(*configStream); + + // Do a second pass to finish initializing the sim objects + SimObject::initAll(); + + // Restore checkpointed state, if any. +#if 0 + configHierarchy.unserializeSimObjects(); +#endif + + SimObject::regAllStats(); + + // Check to make sure that the stats package is properly initialized + Stats::check(); + + // Reset to put the stats in a consistent state. + Stats::reset(); + + SimStartup(); +} + + +/** Simulate for num_cycles additional cycles. If num_cycles is -1 + * (the default), do not limit simulation; some other event must + * terminate the loop. Exported to Python via SWIG. + * @return The SimLoopExitEvent that caused the loop to exit. + */ +SimLoopExitEvent * +simulate(Tick num_cycles = -1) +{ + warn("Entering event queue @ %d. Starting simulation...\n", curTick); + + // Fix up num_cycles. Special default value -1 means simulate + // "forever"... schedule event at MaxTick just to be safe. + // Otherwise it's a delta for additional cycles to simulate past + // curTick, and thus must be non-negative. + if (num_cycles == -1) + num_cycles = MaxTick; + else if (num_cycles < 0) + fatal("simulate: num_cycles must be >= 0 (was %d)\n", num_cycles); + else + num_cycles = curTick + num_cycles; + + Event *limit_event = new SimLoopExitEvent(num_cycles, + "simulate() limit reached"); + + while (1) { + // there should always be at least one event (the SimLoopExitEvent + // we just scheduled) in the queue + assert(!mainEventQueue.empty()); + assert(curTick <= mainEventQueue.nextTick() && + "event scheduled in the past"); + + // forward current cycle to the time of the first event on the + // queue + curTick = mainEventQueue.nextTick(); + Event *exit_event = mainEventQueue.serviceOne(); + if (exit_event != NULL) { + // hit some kind of exit event; return to Python + // event must be subclass of SimLoopExitEvent... + SimLoopExitEvent *se_event = dynamic_cast<SimLoopExitEvent *>(exit_event); + if (se_event == NULL) + panic("Bogus exit event class!"); + + // if we didn't hit limit_event, delete it + if (se_event != limit_event) { + assert(limit_event->scheduled()); + limit_event->deschedule(); + delete limit_event; + } + + return se_event; + } + + if (async_event) { + async_event = false; + if (async_dump) { + async_dump = false; + + using namespace Stats; + SetupEvent(Dump, curTick); + } + + if (async_dumpreset) { + async_dumpreset = false; + + using namespace Stats; + SetupEvent(Dump | Reset, curTick); + } + + if (async_exit) { + async_exit = false; + exitSimLoop("user interrupt received"); + } + + if (async_io || async_alarm) { + async_io = false; + async_alarm = false; + pollQueue.service(); + } + } + } + + // not reached... only exit is return on SimLoopExitEvent +} + +Event * +createCountedDrain() +{ + return new CountedDrainEvent(); +} + +void +cleanupCountedDrain(Event *counted_drain) +{ + CountedDrainEvent *event = + dynamic_cast<CountedDrainEvent *>(counted_drain); + if (event == NULL) { + fatal("Called cleanupCountedDrain() on an event that was not " + "a CountedDrainEvent."); + } + assert(event->getCount() == 0); + delete event; +} + +void +serializeAll(const std::string &cpt_dir) +{ + Serializable::serializeAll(cpt_dir); +} + +void +unserializeAll(const std::string &cpt_dir) +{ + Serializable::unserializeAll(cpt_dir); +} + +/** + * Queue of C++ callbacks to invoke on simulator exit. + */ +CallbackQueue exitCallbacks; + +/** + * Register an exit callback. + */ +void +registerExitCallback(Callback *callback) +{ + exitCallbacks.add(callback); +} + +BaseCPU * +convertToBaseCPUPtr(SimObject *obj) +{ + BaseCPU *ptr = dynamic_cast<BaseCPU *>(obj); + + if (ptr == NULL) + warn("Casting to BaseCPU pointer failed"); + return ptr; +} + +/** + * Do C++ simulator exit processing. Exported to SWIG to be invoked + * when simulator terminates via Python's atexit mechanism. + */ +void +doExitCleanup() +{ + exitCallbacks.process(); + exitCallbacks.clear(); + + cout.flush(); + + ParamContext::cleanupAllContexts(); + + // print simulation stats + Stats::DumpNow(); +} diff --git a/src/sim/param.cc b/src/sim/param.cc new file mode 100644 index 000000000..b1c50946b --- /dev/null +++ b/src/sim/param.cc @@ -0,0 +1,779 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#include <algorithm> +#include <cassert> +#include <list> +#include <string> +#include <vector> + +#include "base/inifile.hh" +#include "base/misc.hh" +#include "base/range.hh" +#include "base/str.hh" +#include "base/trace.hh" +#include "sim/param.hh" +#include "sim/sim_object.hh" + +using namespace std; + + +//////////////////////////////////////////////////////////////////////// +// +// BaseParam member definitions +// +//////////////////////////////////////////////////////////////////////// + +void +BaseParam::die(const string &err) const +{ + context->printErrorProlog(cerr); + cerr << " parameter '" << name << "': " + << err << endl; + abort(); +} + + +//////////////////////////////////////////////////////////////////////// +// +// Param<T> and VectorParam<T> member definitions +// +// We implement parsing & displaying values for various parameter +// types T using a set of overloaded functions: +// +// - parseParam(string s, T &value) parses s into value +// - showParam(ostream &os, T &value) displays value on os +// +// By making these independent functions, we can reuse the same code +// for type T in both Param<T> and VectorParam<T>. +// +// For enum types, the parseParam function requires additional +// arguments, in which case we must specialize the Param<T>::parse and +// VectorParam<T>::parse calls as well. +// +// Type-specific instances come first, followed by more generic +// templated versions and their instantiations. +// +//////////////////////////////////////////////////////////////////////// + +// +// The base implementations use to_number for parsing and '<<' for +// displaying, suitable for integer types. +// +template <class T> +bool +parseParam(const string &s, T &value) +{ + return to_number(s, value); +} + +template <class T> +void +showParam(ostream &os, T const &value) +{ + os << value; +} + +// +// Template specializations: +// - char (8-bit integer) +// - floating-point types +// - bool +// - string +// + +// Treat 8-bit ints (chars) as ints on output, not as chars +template <> +void +showParam(ostream &os, const char &value) +{ + os << (int)value; +} + + +template <> +void +showParam(ostream &os, const unsigned char &value) +{ + os << (unsigned int)value; +} + + +// Use sscanf() for FP types as to_number() only handles integers +template <> +bool +parseParam(const string &s, float &value) +{ + return (sscanf(s.c_str(), "%f", &value) == 1); +} + +template <> +bool +parseParam(const string &s, double &value) +{ + return (sscanf(s.c_str(), "%lf", &value) == 1); +} + +// Be flexible about what we take for bool +template <> +bool +parseParam(const string &s, bool &value) +{ + const string &ls = to_lower(s); + + if (ls == "true" || ls == "t" || ls == "yes" || ls == "y" || ls == "1") { + value = true; + return true; + } + + if (ls == "false" || ls == "f" || ls == "no" || ls == "n" || ls == "0") { + value = false; + return true; + } + + return false; +} + +// Display bools as strings +template <> +void +showParam(ostream &os, const bool &value) +{ + os << (value ? "true" : "false"); +} + + +// String requires no processing to speak of +template <> +bool +parseParam(const string &s, string &value) +{ + value = s; + return true; +} + +template <> +bool +parseParam(const string &s, Range<uint32_t> &value) +{ + value = s; + return value.valid(); +} + +template <> +bool +parseParam(const string &s, Range<uint64_t> &value) +{ + value = s; + return value.valid(); +} + +// +// End of parseParam/showParam definitions. Now we move on to +// incorporate them into the Param/VectorParam parse() and showValue() +// methods. +// + +// These definitions for Param<T>::parse and VectorParam<T>::parse +// work for any type for which parseParam() takes only two arguments +// (i.e., all the fundamental types like int, bool, etc.), thanks to +// overloading. +template <class T> +void +Param<T>::parse(const string &s) +{ + if (parseParam(s, value)) { + wasSet = true; + } + else { + string err("could not parse \""); + + err += s; + err += "\""; + + die(err); + } +} + +template <class T> +void +VectorParam<T>::parse(const string &s) +{ + if (s.empty()) { + wasSet = true; + return; + } + + vector<string> tokens; + + tokenize(tokens, s, ' '); + + value.resize(tokens.size()); + + for (int i = 0; i < tokens.size(); i++) { + // need to parse into local variable to handle vector<bool>, + // for which operator[] returns a special reference class + // that's not the same as 'bool&', (since it's a packed + // vector) + T scalar_value; + if (!parseParam(tokens[i], scalar_value)) { + string err("could not parse \""); + + err += s; + err += "\""; + + die(err); + } + + // assign parsed value to vector + value[i] = scalar_value; + } + + wasSet = true; +} + +// These definitions for Param<T>::showValue() and +// VectorParam<T>::showValue() work for any type where showParam() +// takes only two arguments (i.e., everything but the SimpleEnum and +// MappedEnum classes). +template <class T> +void +Param<T>::showValue(ostream &os) const +{ + showParam(os, value); +} + +template <class T> +void +VectorParam<T>::showValue(ostream &os) const +{ + for (int i = 0; i < value.size(); i++) { + if (i != 0) { + os << " "; + } + showParam(os, value[i]); + } +} + + +#ifdef INSURE_BUILD +#define INSTANTIATE_PARAM_TEMPLATES(type, typestr) \ +void Param<type>::showType(ostream &os) const { os << typestr; } \ +void VectorParam<type>::showType(ostream &os) const { \ + os << "vector of " << typestr; \ +} \ +template Param<type>; \ +template VectorParam<type>; + +#else +// instantiate all four methods (parse/show, scalar/vector) for basic +// types that can use the above templates +#define INSTANTIATE_PARAM_TEMPLATES(type, typestr) \ +template bool parseParam<type>(const string &s, type &value); \ +template void showParam<type>(ostream &os, type const &value); \ +template void Param<type>::parse(const string &); \ +template void VectorParam<type>::parse(const string &); \ +template void Param<type>::showValue(ostream &) const; \ +template void VectorParam<type>::showValue(ostream &) const; \ +template <> void Param<type>::showType(ostream &os) const { os << typestr; } \ +template <> void VectorParam<type>::showType(ostream &os) const { \ + os << "vector of " << typestr; \ +} +#endif + +INSTANTIATE_PARAM_TEMPLATES(unsigned long long, "ull") +INSTANTIATE_PARAM_TEMPLATES(signed long long, "sll") +INSTANTIATE_PARAM_TEMPLATES(unsigned long, "uns long") +INSTANTIATE_PARAM_TEMPLATES(signed long, "long") +INSTANTIATE_PARAM_TEMPLATES(unsigned int, "uns") +INSTANTIATE_PARAM_TEMPLATES(signed int, "int") +INSTANTIATE_PARAM_TEMPLATES(unsigned short, "uns short") +INSTANTIATE_PARAM_TEMPLATES(signed short, "short") +INSTANTIATE_PARAM_TEMPLATES(unsigned char, "uns char") +INSTANTIATE_PARAM_TEMPLATES(signed char, "char") + +INSTANTIATE_PARAM_TEMPLATES(float, "float") +INSTANTIATE_PARAM_TEMPLATES(double, "double") + +INSTANTIATE_PARAM_TEMPLATES(bool, "bool") +INSTANTIATE_PARAM_TEMPLATES(string, "string") + +INSTANTIATE_PARAM_TEMPLATES(Range<uint64_t>, "uint64 range") +INSTANTIATE_PARAM_TEMPLATES(Range<uint32_t>, "uint32 range") + +#undef INSTANTIATE_PARAM_TEMPLATES + +// +// SimpleEnumParam & MappedEnumParam must specialize their parse(), +// showValue(), and showType() methods. +// + +// +// SimpleEnumParam & SimpleEnumVectorParam +// +bool +parseEnumParam(const char *const *map, const int num_values, + const string &s, int &value) +{ + for (int i = 0; i < num_values; ++i) { + if (s == map[i]) { + value = i; + return true; + } + } + + return false; +} + +void +showEnumParam(ostream &os, + const char *const *map, const int num_values, + int value) +{ + assert(0 <= value && value < num_values); + os << map[value]; +} + +void +showEnumType(ostream &os, + const char *const *map, const int num_values) +{ + os << "{" << map[0]; + for (int i = 1; i < num_values; ++i) + os << "," << map[i]; + + os << "}"; +} + + +// +// MappedEnumParam & MappedEnumVectorParam +// +bool +parseEnumParam(const EnumParamMap *map, const int num_values, + const string &s, int &value) +{ + for (int i = 0; i < num_values; ++i) { + if (s == map[i].name) { + value = map[i].value; + return true; + } + } + + return false; +} + +void +showEnumParam(ostream &os, + const EnumParamMap *map, const int num_values, + int value) +{ + for (int i = 0; i < num_values; ++i) { + if (value == map[i].value) { + os << map[i].name; + return; + } + } + + // if we can't find a reverse mapping just print the int value + os << value; +} + +void +showEnumType(ostream &os, + const EnumParamMap *map, const int num_values) +{ + os << "{" << map[0].name; + for (int i = 1; i < num_values; ++i) + os << "," << map[i].name; + + os << "}"; +} + + +template <class Map> +void +EnumParam<Map>::parse(const string &s) +{ + if (parseEnumParam(map, num_values, s, value)) { + wasSet = true; + } else { + string err("no match for enum string \""); + + err += s; + err += "\""; + + die(err); + } +} + +template <class Map> +void +EnumVectorParam<Map>::parse(const string &s) +{ + vector<string> tokens; + + if (s.empty()) { + wasSet = true; + return; + } + + tokenize(tokens, s, ' '); + + value.resize(tokens.size()); + + for (int i = 0; i < tokens.size(); i++) { + if (!parseEnumParam(map, num_values, tokens[i], value[i])) { + string err("no match for enum string \""); + + err += s; + err += "\""; + + die(err); + } + } + + wasSet = true; +} + +template <class Map> +void +EnumParam<Map>::showValue(ostream &os) const +{ + showEnumParam(os, map, num_values, value); +} + +template <class Map> +void +EnumVectorParam<Map>::showValue(ostream &os) const +{ + for (int i = 0; i < value.size(); i++) { + if (i != 0) { + os << " "; + } + showEnumParam(os, map, num_values, value[i]); + } +} + +template <class Map> +void +EnumParam<Map>::showType(ostream &os) const +{ + showEnumType(os, map, num_values); +} + +template <class Map> +void +EnumVectorParam<Map>::showType(ostream &os) const +{ + os << "vector of"; + showEnumType(os, map, num_values); +} + +template class EnumParam<const char *>; +template class EnumVectorParam<const char *>; + +template class EnumParam<EnumParamMap>; +template class EnumVectorParam<EnumParamMap>; + +//////////////////////////////////////////////////////////////////////// +// +// SimObjectBaseParam methods +// +//////////////////////////////////////////////////////////////////////// + +bool +parseSimObjectParam(ParamContext *context, const string &s, SimObject *&value) +{ + SimObject *obj; + + if (to_lower(s) == "null") { + // explicitly set to null by user; assume that's OK + obj = NULL; + } + else { + // defined in main.cc + extern SimObject *resolveSimObject(const string &); + obj = resolveSimObject(s); + + if (obj == NULL) + return false; + } + + value = obj; + return true; +} + + +void +SimObjectBaseParam::showValue(ostream &os, SimObject *value) const +{ + os << (value ? value->name() : "null"); +} + +void +SimObjectBaseParam::parse(const string &s, SimObject *&value) +{ + if (parseSimObjectParam(context, s, value)) { + wasSet = true; + } + else { + string err("could not resolve object name \""); + + err += s; + err += "\""; + + die(err); + } +} + +void +SimObjectBaseParam::parse(const string &s, vector<SimObject *>&value) +{ + vector<string> tokens; + + tokenize(tokens, s, ' '); + + value.resize(tokens.size()); + + for (int i = 0; i < tokens.size(); i++) { + if (!parseSimObjectParam(context, tokens[i], value[i])) { + string err("could not resolve object name \""); + + err += s; + err += "\""; + + die(err); + } + } + + wasSet = true; +} + +//////////////////////////////////////////////////////////////////////// +// +// ParamContext member definitions +// +//////////////////////////////////////////////////////////////////////// + +list<ParamContext *> *ParamContext::ctxList = NULL; + +ParamContext::ParamContext(const string &_iniSection, InitPhase _initPhase) + : iniFilePtr(NULL), // initialized on call to parseParams() + iniSection(_iniSection), paramList(NULL), + initPhase(_initPhase) +{ + // Put this context on global list for initialization + if (initPhase != NoAutoInit) { + if (ctxList == NULL) + ctxList = new list<ParamContext *>(); + + // keep list sorted by ascending initPhase values + list<ParamContext *>::iterator i = ctxList->begin(); + list<ParamContext *>::iterator end = ctxList->end(); + for (; i != end; ++i) { + if (initPhase <= (*i)->initPhase) { + // found where we want to insert + break; + } + } + // (fall through case: insert at end) + ctxList->insert(i, this); + } +} + + +void +ParamContext::addParam(BaseParam *param) +{ + getParamList()->push_back(param); +} + + +void +ParamContext::parseParams(IniFile &iniFile) +{ + iniFilePtr = &iniFile; // set object member + + ParamList::iterator i; + + for (i = getParamList()->begin(); i != getParamList()->end(); ++i) { + string string_value; + + if (iniFile.find(iniSection, (*i)->name, string_value)) + (*i)->parse(string_value); + } +} + + +// Check parameter values for validity & consistency. Default +// implementation is no-op; derive subclass & override to add +// actual functionality here. +void +ParamContext::checkParams() +{ + // nada +} + + +// Clean up context-related objects at end of execution. Default +// implementation is no-op; derive subclass & override to add actual +// functionality here. +void +ParamContext::cleanup() +{ + // nada +} + + +void +ParamContext::describeParams(ostream &os) +{ + ParamList::iterator i; + + for (i = getParamList()->begin(); i != getParamList()->end(); ++i) { + BaseParam *p = *i; + + os << p->name << " ("; + p->showType(os); + os << "): " << p->description << "\n"; + } +} + + + +void +ParamContext::showParams(ostream &os) +{ + ParamList::iterator i; + + for (i = getParamList()->begin(); i != getParamList()->end(); ++i) { + BaseParam *p = *i; + + if (p->isValid()) { + os << p->name << "="; + p->showValue(os); + os << endl; + } + else { + os << "// "<< p->name << " not specified" << endl; + } + } +} + + +void +ParamContext::printErrorProlog(ostream &os) +{ + os << "Parameter error in section [" << iniSection << "]: " << endl; +} + +// +// static method: call parseParams() on all registered contexts +// +void +ParamContext::parseAllContexts(IniFile &iniFile) +{ + list<ParamContext *>::iterator iter; + + for (iter = ctxList->begin(); iter != ctxList->end(); ++iter) { + ParamContext *pc = *iter; + + pc->parseParams(iniFile); + } +} + + +// +// static method: call checkParams() on all registered contexts +// +void +ParamContext::checkAllContexts() +{ + list<ParamContext *>::iterator iter; + + for (iter = ctxList->begin(); iter != ctxList->end(); ++iter) { + ParamContext *pc = *iter; + + pc->checkParams(); + } +} + + +// +// static method: call showParams() on all registered contexts +// +void +ParamContext::showAllContexts(ostream &os) +{ + list<ParamContext *>::iterator iter; + + for (iter = ctxList->begin(); iter != ctxList->end(); ++iter) { + ParamContext *pc = *iter; + + os << "[" << pc->iniSection << "]" << endl; + pc->showParams(os); + os << endl; + } +} + + +// +// static method: call cleanup() on all registered contexts +// +void +ParamContext::cleanupAllContexts() +{ + list<ParamContext *>::iterator iter; + + for (iter = ctxList->begin(); iter != ctxList->end(); ++iter) { + ParamContext *pc = *iter; + + pc->cleanup(); + } +} + + +// +// static method: call describeParams() on all registered contexts +// +void +ParamContext::describeAllContexts(ostream &os) +{ + list<ParamContext *>::iterator iter; + + for (iter = ctxList->begin(); iter != ctxList->end(); ++iter) { + ParamContext *pc = *iter; + + os << "[" << pc->iniSection << "]\n"; + pc->describeParams(os); + os << endl; + } +} diff --git a/src/sim/param.hh b/src/sim/param.hh new file mode 100644 index 000000000..1bc55c125 --- /dev/null +++ b/src/sim/param.hh @@ -0,0 +1,782 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + */ + +#ifndef __SIM_PARAM_HH__ +#define __SIM_PARAM_HH__ + +#include <iostream> +#include <list> +#include <string> +#include <vector> + +#include "sim/startup.hh" + +// forward decls +class IniFile; +class BaseParam; +class SimObject; + +// +// The context of a parameter definition... usually a subclass of +// SimObjectBuilder (which derives from ParamContext), but abstracted +// here to support more global simulator control parameters as well. +// +class ParamContext : protected StartupCallback +{ + private: + + // static list of all ParamContext objects, built as a side effect + // of the ParamContext constructor + static std::list<ParamContext *> *ctxList; + + protected: + + // .ini file (database) for parameter lookup... initialized on call + // to parseParams() + IniFile *iniFilePtr; + + // .ini file section for parameter lookup + const std::string iniSection; + + typedef std::vector<BaseParam *> ParamList; + + // list of parameters defined in this context + ParamList *paramList; + + ParamList *getParamList() { + if (!paramList) + paramList = new ParamList; + return paramList; + } + + public: + + /// Initialization phases for ParamContext objects. + enum InitPhase { + NoAutoInit = -1, ///< Don't initialize at all... params + /// will be parsed later (used by + /// SimObjectBuilder, which parses + /// params in SimObject::create(). + OutputInitPhase = 0, ///< Output stream initialization + TraceInitPhase = 1, ///< Trace context initialization: + /// depends on output streams, but + /// needs to come before others so we + /// can use tracing in other + /// ParamContext init code + StatsInitPhase = 2, ///< Stats output initialization + DefaultInitPhase = 3 ///< Everything else + }; + + /// Records the initialization phase for this ParamContext. + InitPhase initPhase; + + /// Constructor. + /// @param _iniSection Name of .ini section corresponding to this context. + /// @param _initPhase Initialization phase (see InitPhase). + ParamContext(const std::string &_iniSection, + InitPhase _initPhase = DefaultInitPhase); + + virtual ~ParamContext() {} + + // add a parameter to the context... called from the parameter + // object's constructor (see BaseParam::BaseParam()) + void addParam(BaseParam *); + + // call parse() on all params in this context to convert string + // representations to parameter values + virtual void parseParams(IniFile &iniFile); + + // Check parameter values for validity & consistency. Default + // implementation is no-op; derive subclass & override to add + // actual functionality here + virtual void checkParams(); + + // Clean up at end of execution: close file descriptors, etc. + // Default implementation is no-op; derive subclass & override to + // add actual functionality here + virtual void cleanup(); + + // dump parameter descriptions + void describeParams(std::ostream &); + + // Display the parameters & values used + void showParams(std::ostream &); + + // print context information for parameter error + virtual void printErrorProlog(std::ostream &); + + // generate the name for this instance of this context (used as a + // prefix to create unique names in resolveSimObject() + virtual const std::string &getInstanceName() { return iniSection; } + + // Parse all parameters registered with all ParamContext objects. + static void parseAllContexts(IniFile &iniFile); + + // Check all parameters registered with all ParamContext objects. + // (calls checkParams() on each) + static void checkAllContexts(); + + // Print all parameter values on indicated ostream. + static void showAllContexts(std::ostream &os); + + // Clean up all registered ParamContext objects. (calls cleanup() + // on each) + static void cleanupAllContexts(); + + // print descriptions of all parameters registered with all + // ParamContext objects + static void describeAllContexts(std::ostream &os); +}; + + +// +// Base class for all parameter objects +// +class BaseParam +{ + public: + + ParamContext *context; + std::string name; + std::string description; // text description for help message + bool wasSet; // true if parameter was set by user + bool hasDefault; // true if parameter has default value + + BaseParam(ParamContext *_context, const std::string &_name, + const std::string &_description, bool _hasDefault) + : context(_context), name(_name), description(_description), + wasSet(false), hasDefault(_hasDefault) + { + context->addParam(this); + } + + virtual ~BaseParam() {} + + // a parameter is valid only if its value was set by the user or + // it has a default value + bool isValid() const + { + return (wasSet || hasDefault); + } + + // set value by parsing string + virtual void parse(const std::string &s) = 0; + + // display value to stream + virtual void showValue(std::ostream &) const = 0; + + // display type to stream + virtual void showType(std::ostream &) const = 0; + + // signal parse or usage error + virtual void die(const std::string &err) const; +}; + +// +// Template classes to specialize parameters to specific types. +// +// Param<T> is for single-valued (scalar) parameters of type T. +// VectorParam<T> is for multi-valued (vector) parameters of type T. +// These are specified in the .ini file as a space-delimited list of +// arguments. +// +template <class T> +class Param : public BaseParam +{ + protected: + + T value; + + public: + + // Param with default value: set value to default + Param(ParamContext *context, + const std::string &name, const std::string &description, T dfltValue) + : BaseParam(context, name, description, true), + value(dfltValue) + { + } + + // Param with no default value: leave value uninitialized + Param(ParamContext *context, + const std::string &name, const std::string &description) + : BaseParam(context, name, description, false) + { + } + + virtual ~Param() {} + + operator T&() + { + // if we attempt to reference an invalid parameter (i.e., one + // with no default value that was not set by the user), die. + if (!isValid()) + die("not found"); + return value; + } + + // display value to stream + virtual void showValue(std::ostream &os) const; + + // display type to stream + virtual void showType(std::ostream &) const; + + // set value by parsing string + virtual void parse(const std::string &s); +}; + + +// +// Template class for vector-valued parameters (lists) +// +template <class T> +class VectorParam : public BaseParam +{ + protected: + + std::vector<T> value; + + public: + + typedef typename std::vector<T>::size_type size_type; + + // Param with default value: set value to default + VectorParam(ParamContext *context, const std::string &name, + const std::string &description, + const std::vector<T> &dfltValue) + : BaseParam(context, name, description, true), + value(dfltValue) + { + } + + // Param with no default value: leave value uninitialized + VectorParam(ParamContext *context, + const std::string &name, const std::string &description) + : BaseParam(context, name, description, false) + { + } + + virtual ~VectorParam() {} + + // basic vector access methods + size_type size() const + { + if (!isValid()) + die("not found"); + return value.size(); + } + + const T &operator[](size_type n) const + { + if (!isValid()) + die("not found"); + return value[n]; + } + + // return reference to value vector + operator std::vector<T>&() + { + if (!isValid()) + die("not found"); + return value; + } + + // display value to stream + virtual void showValue(std::ostream &os) const; + + // display type to stream + virtual void showType(std::ostream &) const; + + // set value by parsing string + virtual void parse(const std::string &s); +}; + +// +// Specialization of Param<int> and VectorParam<int> to handle +// enumerated types is done in two ways, using SimpleEnumParam and +// MappedEnumParam (and their vector counterparts, +// SimpleEnumVectorParam and MappedEnumVectorParam). SimpleEnumParam +// takes an array of strings and maps them to integers based on array +// index. MappedEnumParam takes an array of string-to-int mappings, +// allowing for mapping strings to non-contiguous integer values, or +// mapping multiple strings to the same integer value. +// +// Both SimpleEnumParam and MappedEnumParam are implemented using a +// single template class, EnumParam<Map>, which takes the type of the map +// as a parameter (const char * or EnumParamMap). Similarly, +// SimpleEnumVectorParam and MappedEnumVectorParam are both +// implemented using EnumVectorParam<Map>. +// +template <class Map> +class EnumParam : public Param<int> +{ + const int num_values; + const Map *map; + + public: + + // Param with default value: set value to default + EnumParam(ParamContext *context, + const std::string &name, const std::string &description, + const Map *_map, int _num_values, + int dfltValue) + : Param<int>(context, name, description, dfltValue), + num_values(_num_values), map(_map) + { + } + + // Param with no default value: leave value uninitialized + EnumParam(ParamContext *context, + const std::string &name, const std::string &description, + const Map *_map, int _num_values) + : Param<int>(context, name, description), + num_values(_num_values), map(_map) + { + } + + virtual ~EnumParam() {} + + // display value to stream + virtual void showValue(std::ostream &os) const; + + // display type to stream + virtual void showType(std::ostream &) const; + + // set value by parsing string + virtual void parse(const std::string &s); +}; + +// +// Vector counterpart to SimpleEnumParam +// +template <class Map> +class EnumVectorParam : public VectorParam<int> +{ + const int num_values; + const Map *map; + + public: + + // Param with default value: set value to default + EnumVectorParam(ParamContext *context, + const std::string &name, const std::string &description, + const Map *_map, int _num_values, + std::vector<int> &dfltValue) + : VectorParam<int>(context, name, description, dfltValue), + num_values(_num_values), map(_map) + { + } + + // Param with no default value: leave value uninitialized + EnumVectorParam(ParamContext *context, + const std::string &name, const std::string &description, + const Map *_map, int _num_values) + : VectorParam<int>(context, name, description), + num_values(_num_values), map(_map) + { + } + + virtual ~EnumVectorParam() {} + + // display value to stream + virtual void showValue(std::ostream &os) const; + + // display type to stream + virtual void showType(std::ostream &) const; + + // set value by parsing string + virtual void parse(const std::string &s); +}; + +// Specialize EnumParam for a particular enumeration type ENUM +// (automates casting to get value of enum type) + +template <class ENUM> +class SimpleEnumParam : public EnumParam<const char *> +{ + public: + + SimpleEnumParam(ParamContext *context, + const std::string &name, const std::string &description, + const char **_map, int _num_values, + ENUM dfltValue) + : EnumParam<const char *>(context, name, description, + _map, _num_values, (int)dfltValue) + { + } + + SimpleEnumParam(ParamContext *context, + const std::string &name, const std::string &description, + const char **_map, int _num_values) + : EnumParam<const char *>(context, name, description, + _map, _num_values) + { + } + + operator ENUM() const + { + if (!isValid()) + die("not found"); + return (ENUM)value; + } +}; + + +// Specialize EnumParam for a particular enumeration type ENUM +// (automates casting to get value of enum type) + +template <class ENUM> +class SimpleEnumVectorParam : public EnumVectorParam<const char *> +{ + public: + + // skip default value constructor: too much pain to convert + // vector<ENUM> initializer to vector<int> + + + SimpleEnumVectorParam(ParamContext *context, + const std::string &name, + const std::string &description, + const char **_map, int _num_values) + : EnumVectorParam<const char *>(context, name, description, + _map, _num_values) + { + } + + ENUM operator[](size_type n) + { + if (!isValid()) + die("not found"); + return (ENUM)value[n]; + } +}; + + +// +// Handle enums via string-to-int map (see comment above). +// + +// An array of string-to-int mappings must be supplied using the +// following type. +typedef struct { + const char *name; + int value; +} EnumParamMap; + +// Specialize EnumParam for a particular enumeration type ENUM +// (automates casting to get value of enum type) + +template <class ENUM> +class MappedEnumParam : public EnumParam<EnumParamMap> +{ + public: + + MappedEnumParam(ParamContext *context, + const std::string &name, const std::string &description, + const EnumParamMap *_map, int _num_values, + ENUM dfltValue) + : EnumParam<EnumParamMap>(context, name, description, + _map, _num_values, (int)dfltValue) + { + } + + MappedEnumParam(ParamContext *context, + const std::string &name, const std::string &description, + const EnumParamMap *_map, int _num_values) + : EnumParam<EnumParamMap>(context, name, description, + _map, _num_values) + { + } + + operator ENUM() + { + if (!isValid()) + die("not found"); + return (ENUM)value[this->n]; + } +}; + + +// Specialize EnumParam for a particular enumeration type ENUM +// (automates casting to get value of enum type) + +template <class ENUM> +class MappedEnumVectorParam : public EnumVectorParam<EnumParamMap> +{ + public: + + // skip default value constructor: too much pain to convert + // vector<ENUM> initializer to vector<int> + + + MappedEnumVectorParam(ParamContext *context, + const std::string &name, + const std::string &description, + const EnumParamMap *_map, int _num_values) + : EnumVectorParam<EnumParamMap>(context, name, description, + _map, _num_values) + { + } + + ENUM operator[](size_type n) + { + if (!isValid()) + die("not found"); + return (ENUM)value[n]; + } +}; + + +// +// Parameters that point to other simulation objects (e.g. caches, +// busses, etc.) are handled by specializing SimObjectBaseParam to the +// specific subtype. The main purpose of SimObjectBaseParam is to +// provide a place to stick several helper functions common to all +// SimObject-derived parameters. +// +class SimObjectBaseParam : public BaseParam +{ + public: + + SimObjectBaseParam(ParamContext *context, const std::string &name, + const std::string &description, bool hasDefault) + : BaseParam(context, name, description, hasDefault) + { + } + + virtual ~SimObjectBaseParam() {} + + // helper function for SimObjectParam<T>::showValue() + void showValue(std::ostream &os, SimObject *obj) const; + + // helper function for SimObjectParam<T>::parse() + void parse(const std::string &s, SimObject *&value); + + // helper function for SimObjectParam<T>::parse() + void parse(const std::string &s, std::vector<SimObject *>&value_vec); +}; + + +// +// Parameter to a specific type of SimObject. Note that T must be a +// pointer to a class derived from SimObject (e.g., <CPU *>). +// + +template <class T> class SimObjectParam; + +template <class T> +class SimObjectParam<T *> : public SimObjectBaseParam +{ + protected: + + T *value; + + public: + + // initialization w/o default + SimObjectParam(ParamContext *context, + const std::string &name, const std::string &description) + : SimObjectBaseParam(context, name, description, false) + { + } + + // initialization wit=h default + SimObjectParam(ParamContext *context, + const std::string &name, const std::string &description, + T *dfltValue) + : SimObjectBaseParam(context, name, description, true), + value(dfltValue) + { + } + + virtual ~SimObjectParam() {} + + // convert to pointer + operator T*() + { + if (!isValid()) + die("not found"); + return value; + } + + T *operator->() const + { + if (!isValid()) + die("not found"); + return value; + } + + // display value to stream + virtual void showValue(std::ostream &os) const + { + SimObjectBaseParam::showValue(os, value); + } + + // display type to stream: see REGISTER_SIM_OBJECT macro in + // sim_object.hh for declaration + virtual void showType(std::ostream &os) const; + + // set value by parsing string + virtual void parse(const std::string &s) + { + SimObject *so_ptr; + // first parse to generic SimObject * + SimObjectBaseParam::parse(s, so_ptr); + // now dynamic_cast to specific derived type + value = dynamic_cast<T *>(so_ptr); + // check for failure of dynamic_cast + if (value == NULL && so_ptr != NULL) + die("not of appropriate type"); + } +}; + + +// +// Vector counterpart to SimObjectParam<T> +// + +template <class T> class SimObjectVectorParam; + +template <class T> +class SimObjectVectorParam<T *> : public SimObjectBaseParam +{ + protected: + + std::vector<T *> value; + + public: + + typedef typename std::vector<T *>::size_type size_type; + + SimObjectVectorParam(ParamContext *context, + const std::string &name, + const std::string &description) + : SimObjectBaseParam(context, name, description, false) + { + } + + SimObjectVectorParam(ParamContext *context, + const std::string &name, + const std::string &description, + std::vector<T *> dfltValue) + : SimObjectBaseParam(context, name, description, true), + value(dfltValue) + { + } + + virtual ~SimObjectVectorParam() {} + + // basic vector access methods + size_type size() const + { + if (!isValid()) + die("not found"); + return value.size(); + } + + T *&operator[](size_type n) + { + if (!isValid()) + die("not found"); + return value[n]; + } + + // return reference to value vector + operator std::vector<T *>&() + { + if (!isValid()) + die("not found"); + return value; + } + + // display value to stream + virtual void showValue(std::ostream &os) const + { + for (int i = 0; i < value.size(); i++) { + if (i != 0) + os << " "; + SimObjectBaseParam::showValue(os, value[i]); + } + } + + // display type to stream: see + virtual void showType(std::ostream &os) const; + + // set value by parsing string + virtual void parse(const std::string &s) + { + std::vector<SimObject *> so_ptr_vec; + // first parse to generic SimObject * vector (from SimObjectBaseParam) + SimObjectBaseParam::parse(s, so_ptr_vec); + + value.resize(so_ptr_vec.size()); + + for (int i = 0; i < so_ptr_vec.size(); ++i) { + // now dynamic_cast to specific derived type + value[i] = dynamic_cast<T *>(so_ptr_vec[i]); + // check for failure of dynamic_cast + if (value[i] == NULL && so_ptr_vec[i] != NULL) + die("not of appropriate type"); + } + } +}; + +// +// Macro to define showType() methods for SimObjectParam & +// SimObjectVectorParam. Can't do this automatically as it requires a +// string name for the type, which you can't get from a template +// argument. For concrete derived SimObject types, this macro is +// automatically invoked by REGISTER_SIM_OBJECT() (see sim_object.hh). +// +#define DEFINE_SIM_OBJECT_CLASS_NAME(CLASS_NAME, OBJ_CLASS) \ +template<> \ +void \ +SimObjectParam<OBJ_CLASS *>::showType(std::ostream &os) const \ +{ \ + os << CLASS_NAME; \ +} \ + \ +template<> \ +void \ +SimObjectVectorParam<OBJ_CLASS *>::showType(std::ostream &os) const \ +{ \ + os << "vector of " << CLASS_NAME; \ +} + + +// +// Declarations for low-level parsing & displaying functions. These +// are used internally, but should not be used directly by clients of +// the parameter mechanism, but are declared here so they can be +// shared with the serialization code (see sim/serialize.cc). +template <class T> bool parseParam(const std::string &str, T &data); +template <class T> void showParam(std::ostream &os, const T &data); + +#endif // _SIM_PARAM_HH_ diff --git a/src/sim/process.cc b/src/sim/process.cc new file mode 100644 index 000000000..46ccd2596 --- /dev/null +++ b/src/sim/process.cc @@ -0,0 +1,539 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + * Ali Saidi + */ + +#include <unistd.h> +#include <fcntl.h> + +#include <string> + +#include "base/intmath.hh" +#include "base/loader/object_file.hh" +#include "base/loader/symtab.hh" +#include "base/statistics.hh" +#include "config/full_system.hh" +#include "cpu/thread_context.hh" +#include "mem/page_table.hh" +#include "mem/physical.hh" +#include "mem/translating_port.hh" +#include "sim/builder.hh" +#include "sim/process.hh" +#include "sim/stats.hh" +#include "sim/syscall_emul.hh" +#include "sim/system.hh" + +#include "arch/isa_specific.hh" +#if THE_ISA == ALPHA_ISA +#include "arch/alpha/linux/process.hh" +#include "arch/alpha/tru64/process.hh" +#elif THE_ISA == SPARC_ISA +#include "arch/sparc/linux/process.hh" +#include "arch/sparc/solaris/process.hh" +#elif THE_ISA == MIPS_ISA +#include "arch/mips/linux/process.hh" +#else +#error "THE_ISA not set" +#endif + + +using namespace std; +using namespace TheISA; + +// +// The purpose of this code is to fake the loader & syscall mechanism +// when there's no OS: thus there's no resone to use it in FULL_SYSTEM +// mode when we do have an OS +// +#if FULL_SYSTEM +#error "process.cc not compatible with FULL_SYSTEM" +#endif + +// current number of allocated processes +int num_processes = 0; + +Process::Process(const string &nm, + System *_system, + int stdin_fd, // initial I/O descriptors + int stdout_fd, + int stderr_fd) + : SimObject(nm), system(_system) +{ + // initialize first 3 fds (stdin, stdout, stderr) + fd_map[STDIN_FILENO] = stdin_fd; + fd_map[STDOUT_FILENO] = stdout_fd; + fd_map[STDERR_FILENO] = stderr_fd; + + // mark remaining fds as free + for (int i = 3; i <= MAX_FD; ++i) { + fd_map[i] = -1; + } + + mmap_start = mmap_end = 0; + nxm_start = nxm_end = 0; + pTable = new PageTable(system); + // other parameters will be initialized when the program is loaded +} + + +void +Process::regStats() +{ + using namespace Stats; + + num_syscalls + .name(name() + ".PROG:num_syscalls") + .desc("Number of system calls") + ; +} + +// +// static helper functions +// +int +Process::openInputFile(const string &filename) +{ + int fd = open(filename.c_str(), O_RDONLY); + + if (fd == -1) { + perror(NULL); + cerr << "unable to open \"" << filename << "\" for reading\n"; + fatal("can't open input file"); + } + + return fd; +} + + +int +Process::openOutputFile(const string &filename) +{ + int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0774); + + if (fd == -1) { + perror(NULL); + cerr << "unable to open \"" << filename << "\" for writing\n"; + fatal("can't open output file"); + } + + return fd; +} + + +int +Process::registerThreadContext(ThreadContext *tc) +{ + // add to list + int myIndex = threadContexts.size(); + threadContexts.push_back(tc); + + // return CPU number to caller + return myIndex; +} + +void +Process::startup() +{ + if (threadContexts.empty()) + fatal("Process %s is not associated with any CPUs!\n", name()); + + // first thread context for this process... initialize & enable + ThreadContext *tc = threadContexts[0]; + + // mark this context as active so it will start ticking. + tc->activate(0); + + Port *mem_port; + mem_port = system->physmem->getPort("functional"); + initVirtMem = new TranslatingPort("process init port", pTable, true); + mem_port->setPeer(initVirtMem); + initVirtMem->setPeer(mem_port); +} + +void +Process::replaceThreadContext(ThreadContext *tc, int tcIndex) +{ + if (tcIndex >= threadContexts.size()) { + panic("replaceThreadContext: bad tcIndex, %d >= %d\n", + tcIndex, threadContexts.size()); + } + + threadContexts[tcIndex] = tc; +} + +// map simulator fd sim_fd to target fd tgt_fd +void +Process::dup_fd(int sim_fd, int tgt_fd) +{ + if (tgt_fd < 0 || tgt_fd > MAX_FD) + panic("Process::dup_fd tried to dup past MAX_FD (%d)", tgt_fd); + + fd_map[tgt_fd] = sim_fd; +} + + +// generate new target fd for sim_fd +int +Process::alloc_fd(int sim_fd) +{ + // in case open() returns an error, don't allocate a new fd + if (sim_fd == -1) + return -1; + + // find first free target fd + for (int free_fd = 0; free_fd < MAX_FD; ++free_fd) { + if (fd_map[free_fd] == -1) { + fd_map[free_fd] = sim_fd; + return free_fd; + } + } + + panic("Process::alloc_fd: out of file descriptors!"); +} + + +// free target fd (e.g., after close) +void +Process::free_fd(int tgt_fd) +{ + if (fd_map[tgt_fd] == -1) + warn("Process::free_fd: request to free unused fd %d", tgt_fd); + + fd_map[tgt_fd] = -1; +} + + +// look up simulator fd for given target fd +int +Process::sim_fd(int tgt_fd) +{ + if (tgt_fd > MAX_FD) + return -1; + + return fd_map[tgt_fd]; +} + + + +// +// need to declare these here since there is no concrete Process type +// that can be constructed (i.e., no REGISTER_SIM_OBJECT() macro call, +// which is where these get declared for concrete types). +// +DEFINE_SIM_OBJECT_CLASS_NAME("Process", Process) + + +//////////////////////////////////////////////////////////////////////// +// +// LiveProcess member definitions +// +//////////////////////////////////////////////////////////////////////// + + +void +copyStringArray(vector<string> &strings, Addr array_ptr, Addr data_ptr, + TranslatingPort* memPort) +{ + Addr data_ptr_swap; + for (int i = 0; i < strings.size(); ++i) { + data_ptr_swap = htog(data_ptr); + memPort->writeBlob(array_ptr, (uint8_t*)&data_ptr_swap, sizeof(Addr)); + memPort->writeString(data_ptr, strings[i].c_str()); + array_ptr += sizeof(Addr); + data_ptr += strings[i].size() + 1; + } + // add NULL terminator + data_ptr = 0; + + memPort->writeBlob(array_ptr, (uint8_t*)&data_ptr, sizeof(Addr)); +} + +LiveProcess::LiveProcess(const string &nm, ObjectFile *_objFile, + System *_system, + int stdin_fd, int stdout_fd, int stderr_fd, + vector<string> &_argv, vector<string> &_envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid) + : Process(nm, _system, stdin_fd, stdout_fd, stderr_fd), + objFile(_objFile), argv(_argv), envp(_envp) +{ + __uid = _uid; + __euid = _euid; + __gid = _gid; + __egid = _egid; + __pid = _pid; + __ppid = _ppid; + + prog_fname = argv[0]; + + // load up symbols, if any... these may be used for debugging or + // profiling. + if (!debugSymbolTable) { + debugSymbolTable = new SymbolTable(); + if (!objFile->loadGlobalSymbols(debugSymbolTable) || + !objFile->loadLocalSymbols(debugSymbolTable)) { + // didn't load any symbols + delete debugSymbolTable; + debugSymbolTable = NULL; + } + } +} + +void +LiveProcess::argsInit(int intSize, int pageSize) +{ + Process::startup(); + + // load object file into target memory + objFile->loadSections(initVirtMem); + + // Calculate how much space we need for arg & env arrays. + int argv_array_size = intSize * (argv.size() + 1); + int envp_array_size = intSize * (envp.size() + 1); + int arg_data_size = 0; + for (int i = 0; i < argv.size(); ++i) { + arg_data_size += argv[i].size() + 1; + } + int env_data_size = 0; + for (int i = 0; i < envp.size(); ++i) { + env_data_size += envp[i].size() + 1; + } + + int space_needed = + argv_array_size + envp_array_size + arg_data_size + env_data_size; + if (space_needed < 32*1024) + space_needed = 32*1024; + + // set bottom of stack + stack_min = stack_base - space_needed; + // align it + stack_min = roundDown(stack_min, pageSize); + stack_size = stack_base - stack_min; + // map memory + pTable->allocate(stack_min, roundUp(stack_size, pageSize)); + + // map out initial stack contents + Addr argv_array_base = stack_min + intSize; // room for argc + Addr envp_array_base = argv_array_base + argv_array_size; + Addr arg_data_base = envp_array_base + envp_array_size; + Addr env_data_base = arg_data_base + arg_data_size; + + // write contents to stack + uint64_t argc = argv.size(); + if (intSize == 8) + argc = htog((uint64_t)argc); + else if (intSize == 4) + argc = htog((uint32_t)argc); + else + panic("Unknown int size"); + + initVirtMem->writeBlob(stack_min, (uint8_t*)&argc, intSize); + + copyStringArray(argv, argv_array_base, arg_data_base, initVirtMem); + copyStringArray(envp, envp_array_base, env_data_base, initVirtMem); + + threadContexts[0]->setIntReg(ArgumentReg0, argc); + threadContexts[0]->setIntReg(ArgumentReg1, argv_array_base); + threadContexts[0]->setIntReg(StackPointerReg, stack_min); + + Addr prog_entry = objFile->entryPoint(); + threadContexts[0]->setPC(prog_entry); + threadContexts[0]->setNextPC(prog_entry + sizeof(MachInst)); + +#if THE_ISA != ALPHA_ISA //e.g. MIPS or Sparc + threadContexts[0]->setNextNPC(prog_entry + (2 * sizeof(MachInst))); +#endif + + num_processes++; +} + +void +LiveProcess::syscall(int64_t callnum, ThreadContext *tc) +{ + num_syscalls++; + + SyscallDesc *desc = getDesc(callnum); + if (desc == NULL) + fatal("Syscall %d out of range", callnum); + + desc->doSyscall(callnum, this, tc); +} + +LiveProcess * +LiveProcess::create(const std::string &nm, System *system, int stdin_fd, + int stdout_fd, int stderr_fd, std::string executable, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid) +{ + LiveProcess *process = NULL; + + ObjectFile *objFile = createObjectFile(executable); + if (objFile == NULL) { + fatal("Can't load object file %s", executable); + } + +#if THE_ISA == ALPHA_ISA + if (objFile->getArch() != ObjectFile::Alpha) + fatal("Object file architecture does not match compiled ISA (Alpha)."); + switch (objFile->getOpSys()) { + case ObjectFile::Tru64: + process = new AlphaTru64Process(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp, + _uid, _euid, _gid, _egid, _pid, _ppid); + break; + + case ObjectFile::Linux: + process = new AlphaLinuxProcess(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp, + _uid, _euid, _gid, _egid, _pid, _ppid); + break; + + default: + fatal("Unknown/unsupported operating system."); + } +#elif THE_ISA == SPARC_ISA + if (objFile->getArch() != ObjectFile::SPARC) + fatal("Object file architecture does not match compiled ISA (SPARC)."); + switch (objFile->getOpSys()) { + case ObjectFile::Linux: + process = new SparcLinuxProcess(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp, + _uid, _euid, _gid, _egid, _pid, _ppid); + break; + + + case ObjectFile::Solaris: + process = new SparcSolarisProcess(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp, + _uid, _euid, _gid, _egid, _pid, _ppid); + break; + default: + fatal("Unknown/unsupported operating system."); + } +#elif THE_ISA == MIPS_ISA + if (objFile->getArch() != ObjectFile::Mips) + fatal("Object file architecture does not match compiled ISA (MIPS)."); + switch (objFile->getOpSys()) { + case ObjectFile::Linux: + process = new MipsLinuxProcess(nm, objFile, system, + stdin_fd, stdout_fd, stderr_fd, + argv, envp, + _uid, _euid, _gid, _egid, _pid, _ppid); + break; + + default: + fatal("Unknown/unsupported operating system."); + } +#else +#error "THE_ISA not set" +#endif + + + if (process == NULL) + fatal("Unknown error creating process object."); + return process; +} + + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(LiveProcess) + + VectorParam<string> cmd; + Param<string> executable; + Param<string> input; + Param<string> output; + VectorParam<string> env; + SimObjectParam<System *> system; + Param<uint64_t> uid; + Param<uint64_t> euid; + Param<uint64_t> gid; + Param<uint64_t> egid; + Param<uint64_t> pid; + Param<uint64_t> ppid; + +END_DECLARE_SIM_OBJECT_PARAMS(LiveProcess) + + +BEGIN_INIT_SIM_OBJECT_PARAMS(LiveProcess) + + INIT_PARAM(cmd, "command line (executable plus arguments)"), + INIT_PARAM(executable, "executable (overrides cmd[0] if set)"), + INIT_PARAM(input, "filename for stdin (dflt: use sim stdin)"), + INIT_PARAM(output, "filename for stdout/stderr (dflt: use sim stdout)"), + INIT_PARAM(env, "environment settings"), + INIT_PARAM(system, "system"), + INIT_PARAM(uid, "user id"), + INIT_PARAM(euid, "effective user id"), + INIT_PARAM(gid, "group id"), + INIT_PARAM(egid, "effective group id"), + INIT_PARAM(pid, "process id"), + INIT_PARAM(ppid, "parent process id") + +END_INIT_SIM_OBJECT_PARAMS(LiveProcess) + + +CREATE_SIM_OBJECT(LiveProcess) +{ + string in = input; + string out = output; + + // initialize file descriptors to default: same as simulator + int stdin_fd, stdout_fd, stderr_fd; + + if (in == "stdin" || in == "cin") + stdin_fd = STDIN_FILENO; + else + stdin_fd = Process::openInputFile(input); + + if (out == "stdout" || out == "cout") + stdout_fd = STDOUT_FILENO; + else if (out == "stderr" || out == "cerr") + stdout_fd = STDERR_FILENO; + else + stdout_fd = Process::openOutputFile(out); + + stderr_fd = (stdout_fd != STDOUT_FILENO) ? stdout_fd : STDERR_FILENO; + + return LiveProcess::create(getInstanceName(), system, + stdin_fd, stdout_fd, stderr_fd, + (string)executable == "" ? cmd[0] : executable, + cmd, env, + uid, euid, gid, egid, pid, ppid); +} + + +REGISTER_SIM_OBJECT("LiveProcess", LiveProcess) diff --git a/src/sim/process.hh b/src/sim/process.hh new file mode 100644 index 000000000..b2777170f --- /dev/null +++ b/src/sim/process.hh @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __PROCESS_HH__ +#define __PROCESS_HH__ + +// +// The purpose of this code is to fake the loader & syscall mechanism +// when there's no OS: thus there's no reason to use it in FULL_SYSTEM +// mode when we do have an OS. +// +#include "config/full_system.hh" + +#if !FULL_SYSTEM + +#include <vector> + +#include "base/statistics.hh" +#include "sim/sim_object.hh" + +class ThreadContext; +class SyscallDesc; +class PageTable; +class TranslatingPort; +class System; + +void +copyStringArray(std::vector<std::string> &strings, Addr array_ptr, + Addr data_ptr, TranslatingPort* memPort); + +class Process : public SimObject +{ + public: + + /// Pointer to object representing the system this process is + /// running on. + System *system; + + // have we initialized a thread context from this process? If + // yes, subsequent contexts are assumed to be for dynamically + // created threads and are not initialized. + bool initialContextLoaded; + + // thread contexts associated with this process + std::vector<ThreadContext *> threadContexts; + + // number of CPUs (esxec contexts, really) assigned to this process. + unsigned int numCpus() { return threadContexts.size(); } + + // record of blocked context + struct WaitRec + { + Addr waitChan; + ThreadContext *waitingContext; + + WaitRec(Addr chan, ThreadContext *ctx) + : waitChan(chan), waitingContext(ctx) + { } + }; + + // list of all blocked contexts + std::list<WaitRec> waitList; + + Addr brk_point; // top of the data segment + + Addr stack_base; // stack segment base (highest address) + unsigned stack_size; // initial stack size + Addr stack_min; // lowest address accessed on the stack + + // addr to use for next stack region (for multithreaded apps) + Addr next_thread_stack_base; + + // Base of region for mmaps (when user doesn't specify an address). + Addr mmap_start; + Addr mmap_end; + + // Base of region for nxm data + Addr nxm_start; + Addr nxm_end; + + std::string prog_fname; // file name + + Stats::Scalar<> num_syscalls; // number of syscalls executed + + + protected: + // constructor + Process(const std::string &nm, + System *_system, + int stdin_fd, // initial I/O descriptors + int stdout_fd, + int stderr_fd); + + // post initialization startup + virtual void startup(); + + protected: + /// Memory object for initialization (image loading) + TranslatingPort *initVirtMem; + + public: + PageTable *pTable; + + private: + // file descriptor remapping support + static const int MAX_FD = 256; // max legal fd value + int fd_map[MAX_FD+1]; + + public: + // static helper functions to generate file descriptors for constructor + static int openInputFile(const std::string &filename); + static int openOutputFile(const std::string &filename); + + // override of virtual SimObject method: register statistics + virtual void regStats(); + + // register a thread context for this process. + // returns tc's cpu number (index into threadContexts[]) + int registerThreadContext(ThreadContext *tc); + + + void replaceThreadContext(ThreadContext *tc, int tcIndex); + + // map simulator fd sim_fd to target fd tgt_fd + void dup_fd(int sim_fd, int tgt_fd); + + // generate new target fd for sim_fd + int alloc_fd(int sim_fd); + + // free target fd (e.g., after close) + void free_fd(int tgt_fd); + + // look up simulator fd for given target fd + int sim_fd(int tgt_fd); + + virtual void syscall(int64_t callnum, ThreadContext *tc) = 0; +}; + +// +// "Live" process with system calls redirected to host system +// +class ObjectFile; +class LiveProcess : public Process +{ + protected: + ObjectFile *objFile; + std::vector<std::string> argv; + std::vector<std::string> envp; + + LiveProcess(const std::string &nm, ObjectFile *objFile, + System *_system, int stdin_fd, int stdout_fd, int stderr_fd, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid); + + virtual void argsInit(int intSize, int pageSize); + + // Id of the owner of the process + uint64_t __uid; + uint64_t __euid; + uint64_t __gid; + uint64_t __egid; + + // pid of the process and it's parent + uint64_t __pid; + uint64_t __ppid; + + public: + + inline uint64_t uid() {return __uid;} + inline uint64_t euid() {return __euid;} + inline uint64_t gid() {return __gid;} + inline uint64_t egid() {return __egid;} + inline uint64_t pid() {return __pid;} + inline uint64_t ppid() {return __ppid;} + + virtual void syscall(int64_t callnum, ThreadContext *tc); + + virtual SyscallDesc* getDesc(int callnum) = 0; + + // this function is used to create the LiveProcess object, since + // we can't tell which subclass of LiveProcess to use until we + // open and look at the object file. + static LiveProcess *create(const std::string &nm, + System *_system, + int stdin_fd, int stdout_fd, int stderr_fd, + std::string executable, + std::vector<std::string> &argv, + std::vector<std::string> &envp, + uint64_t _uid, uint64_t _euid, + uint64_t _gid, uint64_t _egid, + uint64_t _pid, uint64_t _ppid); +}; + + +#endif // !FULL_SYSTEM + +#endif // __PROCESS_HH__ diff --git a/src/sim/pseudo_inst.cc b/src/sim/pseudo_inst.cc new file mode 100644 index 000000000..aae2f6021 --- /dev/null +++ b/src/sim/pseudo_inst.cc @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2003-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <string> + +#include "sim/pseudo_inst.hh" +#include "arch/vtophys.hh" +#include "base/annotate.hh" +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "cpu/quiesce_event.hh" +#include "kern/kernel_stats.hh" +#include "sim/param.hh" +#include "sim/serialize.hh" +#include "sim/sim_exit.hh" +#include "sim/stat_control.hh" +#include "sim/stats.hh" +#include "sim/system.hh" +#include "sim/debug.hh" +#include "sim/vptr.hh" + +using namespace std; + +using namespace Stats; +using namespace TheISA; + +namespace AlphaPseudo +{ + bool doStatisticsInsts; + bool doCheckpointInsts; + bool doQuiesce; + + void + arm(ThreadContext *tc) + { + if (tc->getKernelStats()) + tc->getKernelStats()->arm(); + } + + void + quiesce(ThreadContext *tc) + { + if (!doQuiesce) + return; + + tc->suspend(); + if (tc->getKernelStats()) + tc->getKernelStats()->quiesce(); + } + + void + quiesceNs(ThreadContext *tc, uint64_t ns) + { + if (!doQuiesce || ns == 0) + return; + + EndQuiesceEvent *quiesceEvent = tc->getQuiesceEvent(); + + if (quiesceEvent->scheduled()) + quiesceEvent->reschedule(curTick + Clock::Int::ns * ns); + else + quiesceEvent->schedule(curTick + Clock::Int::ns * ns); + + tc->suspend(); + if (tc->getKernelStats()) + tc->getKernelStats()->quiesce(); + } + + void + quiesceCycles(ThreadContext *tc, uint64_t cycles) + { + if (!doQuiesce || cycles == 0) + return; + + EndQuiesceEvent *quiesceEvent = tc->getQuiesceEvent(); + + if (quiesceEvent->scheduled()) + quiesceEvent->reschedule(curTick + + tc->getCpuPtr()->cycles(cycles)); + else + quiesceEvent->schedule(curTick + + tc->getCpuPtr()->cycles(cycles)); + + tc->suspend(); + if (tc->getKernelStats()) + tc->getKernelStats()->quiesce(); + } + + uint64_t + quiesceTime(ThreadContext *tc) + { + return (tc->readLastActivate() - tc->readLastSuspend()) / Clock::Int::ns; + } + + void + ivlb(ThreadContext *tc) + { + if (tc->getKernelStats()) + tc->getKernelStats()->ivlb(); + } + + void + ivle(ThreadContext *tc) + { + } + + void + m5exit_old(ThreadContext *tc) + { + exitSimLoop(curTick, "m5_exit_old instruction encountered"); + } + + void + m5exit(ThreadContext *tc, Tick delay) + { + Tick when = curTick + delay * Clock::Int::ns; + exitSimLoop(when, "m5_exit instruction encountered"); + } + + void + loadsymbol(ExecContext *xc) + { + const string &filename = xc->getCpuPtr()->system->params()->symbolfile; + if (filename.empty()) { + return; + } + + std::string buffer; + ifstream file(filename.c_str()); + + if (!file) + fatal("file error: Can't open symbol table file %s\n", filename); + + while (!file.eof()) { + getline(file, buffer); + + if (buffer.empty()) + continue; + + int idx = buffer.find(' '); + if (idx == string::npos) + continue; + + string address = "0x" + buffer.substr(0, idx); + eat_white(address); + if (address.empty()) + continue; + + // Skip over letter and space + string symbol = buffer.substr(idx + 3); + eat_white(symbol); + if (symbol.empty()) + continue; + + Addr addr; + if (!to_number(address, addr)) + continue; + + if (!xc->getSystemPtr()->kernelSymtab->insert(addr, symbol)) + continue; + + + DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr); + } + file.close(); + } + + void + resetstats(ThreadContext *tc, Tick delay, Tick period) + { + if (!doStatisticsInsts) + return; + + + Tick when = curTick + delay * Clock::Int::ns; + Tick repeat = period * Clock::Int::ns; + + using namespace Stats; + SetupEvent(Reset, when, repeat); + } + + void + dumpstats(ThreadContext *tc, Tick delay, Tick period) + { + if (!doStatisticsInsts) + return; + + + Tick when = curTick + delay * Clock::Int::ns; + Tick repeat = period * Clock::Int::ns; + + using namespace Stats; + SetupEvent(Dump, when, repeat); + } + + void + addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr) + { + char symb[100]; + CopyStringOut(tc, symb, symbolAddr, 100); + std::string symbol(symb); + + DPRINTF(Loader, "Loaded symbol: %s @ %#llx\n", symbol, addr); + + tc->getSystemPtr()->kernelSymtab->insert(addr,symbol); + } + + void + anBegin(ThreadContext *tc, uint64_t cur) + { + Annotate::annotations.add(tc->getSystemPtr(), 0, cur >> 32, cur & + 0xFFFFFFFF, 0,0); + } + + void + anWait(ThreadContext *tc, uint64_t cur, uint64_t wait) + { + Annotate::annotations.add(tc->getSystemPtr(), 0, cur >> 32, cur & + 0xFFFFFFFF, wait >> 32, wait & 0xFFFFFFFF); + } + + + void + dumpresetstats(ThreadContext *tc, Tick delay, Tick period) + { + if (!doStatisticsInsts) + return; + + + Tick when = curTick + delay * Clock::Int::ns; + Tick repeat = period * Clock::Int::ns; + + using namespace Stats; + SetupEvent(Dump|Reset, when, repeat); + } + + void + m5checkpoint(ThreadContext *tc, Tick delay, Tick period) + { + if (!doCheckpointInsts) + return; + exitSimLoop("checkpoint"); + } + + uint64_t + readfile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset) + { + const string &file = tc->getCpuPtr()->system->params()->readfile; + if (file.empty()) { + return ULL(0); + } + + uint64_t result = 0; + + int fd = ::open(file.c_str(), O_RDONLY, 0); + if (fd < 0) + panic("could not open file %s\n", file); + + if (::lseek(fd, offset, SEEK_SET) < 0) + panic("could not seek: %s", strerror(errno)); + + char *buf = new char[len]; + char *p = buf; + while (len > 0) { + int bytes = ::read(fd, p, len); + if (bytes <= 0) + break; + + p += bytes; + result += bytes; + len -= bytes; + } + + close(fd); + CopyIn(tc, vaddr, buf, result); + delete [] buf; + return result; + } + + class Context : public ParamContext + { + public: + Context(const string §ion) : ParamContext(section) {} + void checkParams(); + }; + + Context context("pseudo_inst"); + + Param<bool> __quiesce(&context, "quiesce", + "enable quiesce instructions", + true); + Param<bool> __statistics(&context, "statistics", + "enable statistics pseudo instructions", + true); + Param<bool> __checkpoint(&context, "checkpoint", + "enable checkpoint pseudo instructions", + true); + + void + Context::checkParams() + { + doQuiesce = __quiesce; + doStatisticsInsts = __statistics; + doCheckpointInsts = __checkpoint; + } + + void debugbreak(ThreadContext *tc) + { + debug_break(); + } + + void switchcpu(ThreadContext *tc) + { + exitSimLoop("switchcpu"); + } +} diff --git a/src/sim/pseudo_inst.hh b/src/sim/pseudo_inst.hh new file mode 100644 index 000000000..d211de44e --- /dev/null +++ b/src/sim/pseudo_inst.hh @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2003-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +class ThreadContext; + +//We need the "Tick" and "Addr" data types from here +#include "sim/host.hh" + +namespace AlphaPseudo +{ + /** + * @todo these externs are only here for a hack in fullCPU::takeOver... + */ + extern bool doStatisticsInsts; + extern bool doCheckpointInsts; + extern bool doQuiesce; + + void arm(ThreadContext *tc); + void quiesce(ThreadContext *tc); + void quiesceNs(ThreadContext *tc, uint64_t ns); + void quiesceCycles(ThreadContext *tc, uint64_t cycles); + uint64_t quiesceTime(ThreadContext *tc); + void ivlb(ThreadContext *tc); + void ivle(ThreadContext *tc); + void m5exit(ThreadContext *tc, Tick delay); + void m5exit_old(ThreadContext *tc); + void loadsymbol(ThreadContext *xc); + void resetstats(ThreadContext *tc, Tick delay, Tick period); + void dumpstats(ThreadContext *tc, Tick delay, Tick period); + void dumpresetstats(ThreadContext *tc, Tick delay, Tick period); + void m5checkpoint(ThreadContext *tc, Tick delay, Tick period); + uint64_t readfile(ThreadContext *tc, Addr vaddr, uint64_t len, uint64_t offset); + void debugbreak(ThreadContext *tc); + void switchcpu(ThreadContext *tc); + void addsymbol(ThreadContext *tc, Addr addr, Addr symbolAddr); + void anBegin(ThreadContext *tc, uint64_t cur); + void anWait(ThreadContext *tc, uint64_t cur, uint64_t wait); +} diff --git a/src/sim/root.cc b/src/sim/root.cc new file mode 100644 index 000000000..ec5e2f7e2 --- /dev/null +++ b/src/sim/root.cc @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#include <cstring> +#include <fstream> +#include <list> +#include <string> +#include <vector> + +#include "base/misc.hh" +#include "base/output.hh" +#include "sim/builder.hh" +#include "sim/host.hh" +#include "sim/sim_events.hh" +#include "sim/sim_exit.hh" +#include "sim/sim_object.hh" +#include "sim/root.hh" + +using namespace std; + +Tick curTick = 0; +ostream *outputStream; +ostream *configStream; + +/// The simulated frequency of curTick. (This is only here for a short time) +Tick ticksPerSecond; + +namespace Clock { +/// The simulated frequency of curTick. (In ticks per second) +Tick Frequency; + +namespace Float { +double s; +double ms; +double us; +double ns; +double ps; + +double Hz; +double kHz; +double MHz; +double GHZ; +/* namespace Float */ } + +namespace Int { +Tick s; +Tick ms; +Tick us; +Tick ns; +Tick ps; +/* namespace Float */ } + +/* namespace Clock */ } + + +// Dummy Object +class Root : public SimObject +{ + private: + Tick max_tick; + Tick progress_interval; + + public: + Root(const std::string &name, Tick maxtick, Tick pi) + : SimObject(name), max_tick(maxtick), progress_interval(pi) + {} + + virtual void startup(); +}; + +void +Root::startup() +{ + if (max_tick != 0) + exitSimLoop(curTick + max_tick, "reached maximum cycle count"); + + if (progress_interval != 0) + new ProgressEvent(&mainEventQueue, progress_interval); +} + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(Root) + + Param<Tick> clock; + Param<Tick> max_tick; + Param<Tick> progress_interval; + Param<string> output_file; + +END_DECLARE_SIM_OBJECT_PARAMS(Root) + +BEGIN_INIT_SIM_OBJECT_PARAMS(Root) + + INIT_PARAM(clock, "tick frequency"), + INIT_PARAM(max_tick, "maximum simulation time"), + INIT_PARAM(progress_interval, "print a progress message"), + INIT_PARAM(output_file, "file to dump simulator output to") + +END_INIT_SIM_OBJECT_PARAMS(Root) + +CREATE_SIM_OBJECT(Root) +{ + static bool created = false; + if (created) + panic("only one root object allowed!"); + + created = true; + + outputStream = simout.find(output_file); + Root *root = new Root(getInstanceName(), max_tick, progress_interval); + + using namespace Clock; + Frequency = clock; + Float::s = static_cast<double>(Frequency); + Float::ms = Float::s / 1.0e3; + Float::us = Float::s / 1.0e6; + Float::ns = Float::s / 1.0e9; + Float::ps = Float::s / 1.0e12; + + Float::Hz = 1.0 / Float::s; + Float::kHz = 1.0 / Float::ms; + Float::MHz = 1.0 / Float::us; + Float::GHZ = 1.0 / Float::ns; + + Int::s = Frequency; + Int::ms = Int::s / 1000; + Int::us = Int::ms / 1000; + Int::ns = Int::us / 1000; + Int::ps = Int::ns / 1000; + + return root; +} + +REGISTER_SIM_OBJECT("Root", Root) diff --git a/src/sim/serialize.cc b/src/sim/serialize.cc new file mode 100644 index 000000000..941f0b1c6 --- /dev/null +++ b/src/sim/serialize.cc @@ -0,0 +1,442 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Erik Hallnor + * Steve Reinhardt + */ + +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> + +#include <fstream> +#include <list> +#include <string> +#include <vector> + +#include "base/inifile.hh" +#include "base/misc.hh" +#include "base/output.hh" +#include "base/str.hh" +#include "base/trace.hh" +#include "sim/eventq.hh" +#include "sim/param.hh" +#include "sim/serialize.hh" +#include "sim/sim_events.hh" +#include "sim/sim_exit.hh" +#include "sim/sim_object.hh" + +// For stat reset hack +#include "sim/stat_control.hh" + +using namespace std; + +int Serializable::ckptMaxCount = 0; +int Serializable::ckptCount = 0; +int Serializable::ckptPrevCount = -1; + +void +Serializable::nameOut(ostream &os) +{ + os << "\n[" << name() << "]\n"; +} + +void +Serializable::nameOut(ostream &os, const string &_name) +{ + os << "\n[" << _name << "]\n"; +} + +template <class T> +void +paramOut(ostream &os, const std::string &name, const T ¶m) +{ + os << name << "="; + showParam(os, param); + os << "\n"; +} + + +template <class T> +void +paramIn(Checkpoint *cp, const std::string §ion, + const std::string &name, T ¶m) +{ + std::string str; + if (!cp->find(section, name, str) || !parseParam(str, param)) { + fatal("Can't unserialize '%s:%s'\n", section, name); + } +} + + +template <class T> +void +arrayParamOut(ostream &os, const std::string &name, + const T *param, int size) +{ + os << name << "="; + if (size > 0) + showParam(os, param[0]); + for (int i = 1; i < size; ++i) { + os << " "; + showParam(os, param[i]); + } + os << "\n"; +} + + +template <class T> +void +arrayParamIn(Checkpoint *cp, const std::string §ion, + const std::string &name, T *param, int size) +{ + std::string str; + if (!cp->find(section, name, str)) { + fatal("Can't unserialize '%s:%s'\n", section, name); + } + + // code below stolen from VectorParam<T>::parse(). + // it would be nice to unify these somehow... + + vector<string> tokens; + + tokenize(tokens, str, ' '); + + // Need this if we were doing a vector + // value.resize(tokens.size()); + + if (tokens.size() != size) { + fatal("Array size mismatch on %s:%s'\n", section, name); + } + + for (int i = 0; i < tokens.size(); i++) { + // need to parse into local variable to handle vector<bool>, + // for which operator[] returns a special reference class + // that's not the same as 'bool&', (since it's a packed + // vector) + T scalar_value; + if (!parseParam(tokens[i], scalar_value)) { + string err("could not parse \""); + + err += str; + err += "\""; + + fatal(err); + } + + // assign parsed value to vector + param[i] = scalar_value; + } +} + + +void +objParamIn(Checkpoint *cp, const std::string §ion, + const std::string &name, Serializable * ¶m) +{ + if (!cp->findObj(section, name, param)) { + fatal("Can't unserialize '%s:%s'\n", section, name); + } +} + + +#define INSTANTIATE_PARAM_TEMPLATES(type) \ +template void \ +paramOut(ostream &os, const std::string &name, type const ¶m); \ +template void \ +paramIn(Checkpoint *cp, const std::string §ion, \ + const std::string &name, type & param); \ +template void \ +arrayParamOut(ostream &os, const std::string &name, \ + type const *param, int size); \ +template void \ +arrayParamIn(Checkpoint *cp, const std::string §ion, \ + const std::string &name, type *param, int size); + +INSTANTIATE_PARAM_TEMPLATES(signed char) +INSTANTIATE_PARAM_TEMPLATES(unsigned char) +INSTANTIATE_PARAM_TEMPLATES(signed short) +INSTANTIATE_PARAM_TEMPLATES(unsigned short) +INSTANTIATE_PARAM_TEMPLATES(signed int) +INSTANTIATE_PARAM_TEMPLATES(unsigned int) +INSTANTIATE_PARAM_TEMPLATES(signed long) +INSTANTIATE_PARAM_TEMPLATES(unsigned long) +INSTANTIATE_PARAM_TEMPLATES(signed long long) +INSTANTIATE_PARAM_TEMPLATES(unsigned long long) +INSTANTIATE_PARAM_TEMPLATES(bool) +INSTANTIATE_PARAM_TEMPLATES(string) + + +///////////////////////////// + +/// Container for serializing global variables (not associated with +/// any serialized object). +class Globals : public Serializable +{ + public: + const string name() const; + void serialize(ostream &os); + void unserialize(Checkpoint *cp); +}; + +/// The one and only instance of the Globals class. +Globals globals; + +const string +Globals::name() const +{ + return "Globals"; +} + +void +Globals::serialize(ostream &os) +{ + nameOut(os); + SERIALIZE_SCALAR(curTick); + + nameOut(os, "MainEventQueue"); + mainEventQueue.serialize(os); +} + +void +Globals::unserialize(Checkpoint *cp) +{ + const string §ion = name(); + UNSERIALIZE_SCALAR(curTick); + + mainEventQueue.unserialize(cp, "MainEventQueue"); +} + +void +Serializable::serializeAll(const std::string &cpt_dir) +{ + setCheckpointDir(cpt_dir); + string dir = Checkpoint::dir(); + if (mkdir(dir.c_str(), 0775) == -1 && errno != EEXIST) + fatal("couldn't mkdir %s\n", dir); + + string cpt_file = dir + Checkpoint::baseFilename; + ofstream outstream(cpt_file.c_str()); + time_t t = time(NULL); + outstream << "// checkpoint generated: " << ctime(&t); + + globals.serialize(outstream); + SimObject::serializeAll(outstream); +} + +void +Serializable::unserializeAll(const std::string &cpt_dir) +{ + setCheckpointDir(cpt_dir); + string dir = Checkpoint::dir(); + string cpt_file = dir + Checkpoint::baseFilename; + string section = ""; + + DPRINTFR(Config, "Loading checkpoint dir '%s'\n", + dir); + Checkpoint *cp = new Checkpoint(dir, section); + unserializeGlobals(cp); + + SimObject::unserializeAll(cp); +} + +void +Serializable::unserializeGlobals(Checkpoint *cp) +{ + globals.unserialize(cp); +} + +const char *Checkpoint::baseFilename = "m5.cpt"; + +static string checkpointDirBase; + +void +setCheckpointDir(const std::string &name) +{ + checkpointDirBase = name; + if (checkpointDirBase[checkpointDirBase.size() - 1] != '/') + checkpointDirBase += "/"; +} + +string +Checkpoint::dir() +{ + // use csprintf to insert curTick into directory name if it + // appears to have a format placeholder in it. + return (checkpointDirBase.find("%") != string::npos) ? + csprintf(checkpointDirBase, curTick) : checkpointDirBase; +} + +void +debug_serialize(const std::string &cpt_dir) +{ + Serializable::serializeAll(cpt_dir); +} + + +//////////////////////////////////////////////////////////////////////// +// +// SerializableClass member definitions +// +//////////////////////////////////////////////////////////////////////// + +// Map of class names to SerializableBuilder creation functions. +// Need to make this a pointer so we can force initialization on the +// first reference; otherwise, some SerializableClass constructors +// may be invoked before the classMap constructor. +map<string,SerializableClass::CreateFunc> *SerializableClass::classMap = 0; + +// SerializableClass constructor: add mapping to classMap +SerializableClass::SerializableClass(const string &className, + CreateFunc createFunc) +{ + if (classMap == NULL) + classMap = new map<string,SerializableClass::CreateFunc>(); + + if ((*classMap)[className]) + { + cerr << "Error: simulation object class " << className << " redefined" + << endl; + fatal(""); + } + + // add className --> createFunc to class map + (*classMap)[className] = createFunc; +} + + +// +// +Serializable * +SerializableClass::createObject(Checkpoint *cp, + const std::string §ion) +{ + string className; + + if (!cp->find(section, "type", className)) { + fatal("Serializable::create: no 'type' entry in section '%s'.\n", + section); + } + + CreateFunc createFunc = (*classMap)[className]; + + if (createFunc == NULL) { + fatal("Serializable::create: no create function for class '%s'.\n", + className); + } + + Serializable *object = createFunc(cp, section); + + assert(object != NULL); + + return object; +} + + +Serializable * +Serializable::create(Checkpoint *cp, const std::string §ion) +{ + Serializable *object = SerializableClass::createObject(cp, section); + object->unserialize(cp, section); + return object; +} + + +Checkpoint::Checkpoint(const std::string &cpt_dir, const std::string &path) + : db(new IniFile), basePath(path), cptDir(cpt_dir) +{ + string filename = cpt_dir + "/" + Checkpoint::baseFilename; + if (!db->load(filename)) { + fatal("Can't load checkpoint file '%s'\n", filename); + } +} + + +bool +Checkpoint::find(const std::string §ion, const std::string &entry, + std::string &value) +{ + return db->find(section, entry, value); +} + + +bool +Checkpoint::findObj(const std::string §ion, const std::string &entry, + Serializable *&value) +{ + string path; + + if (!db->find(section, entry, path)) + return false; + + if ((value = objMap[path]) != NULL) + return true; + + return false; +} + + +bool +Checkpoint::sectionExists(const std::string §ion) +{ + return db->sectionExists(section); +} + +/** Hacked stat reset event */ + +class StatresetParamContext : public ParamContext +{ + public: + StatresetParamContext(const string §ion); + ~StatresetParamContext(); + void startup(); +}; + +StatresetParamContext statParams("statsreset"); + +Param<Tick> reset_cycle(&statParams, "reset_cycle", + "Cycle to reset stats on", 0); + +StatresetParamContext::StatresetParamContext(const string §ion) + : ParamContext(section) +{ } + +StatresetParamContext::~StatresetParamContext() +{ +} + +void +StatresetParamContext::startup() +{ + if (reset_cycle > 0) { + Stats::SetupEvent(Stats::Reset, curTick + reset_cycle, 0); + cprintf("Stats reset event scheduled for %lli\n", + curTick + reset_cycle); + } +} diff --git a/src/sim/serialize.hh b/src/sim/serialize.hh new file mode 100644 index 000000000..880fb0785 --- /dev/null +++ b/src/sim/serialize.hh @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Erik Hallnor + * Steve Reinhardt + */ + +/* @file + * Serialization Interface Declarations + */ + +#ifndef __SERIALIZE_HH__ +#define __SERIALIZE_HH__ + + +#include <list> +#include <iostream> +#include <map> + +#include "sim/host.hh" + +class IniFile; +class Serializable; +class Checkpoint; + +template <class T> +void paramOut(std::ostream &os, const std::string &name, const T ¶m); + +template <class T> +void paramIn(Checkpoint *cp, const std::string §ion, + const std::string &name, T ¶m); + +template <class T> +void arrayParamOut(std::ostream &os, const std::string &name, + const T *param, int size); + +template <class T> +void arrayParamIn(Checkpoint *cp, const std::string §ion, + const std::string &name, T *param, int size); + +void +objParamIn(Checkpoint *cp, const std::string §ion, + const std::string &name, Serializable * ¶m); + + +// +// These macros are streamlined to use in serialize/unserialize +// functions. It's assumed that serialize() has a parameter 'os' for +// the ostream, and unserialize() has parameters 'cp' and 'section'. +#define SERIALIZE_SCALAR(scalar) paramOut(os, #scalar, scalar) + +#define UNSERIALIZE_SCALAR(scalar) paramIn(cp, section, #scalar, scalar) + +// ENUMs are like SCALARs, but we cast them to ints on the way out +#define SERIALIZE_ENUM(scalar) paramOut(os, #scalar, (int)scalar) + +#define UNSERIALIZE_ENUM(scalar) \ + do { \ + int tmp; \ + paramIn(cp, section, #scalar, tmp); \ + scalar = (typeof(scalar))tmp; \ + } while (0) + +#define SERIALIZE_ARRAY(member, size) \ + arrayParamOut(os, #member, member, size) + +#define UNSERIALIZE_ARRAY(member, size) \ + arrayParamIn(cp, section, #member, member, size) + +#define SERIALIZE_OBJPTR(objptr) paramOut(os, #objptr, (objptr)->name()) + +#define UNSERIALIZE_OBJPTR(objptr) \ + do { \ + Serializable *sptr; \ + objParamIn(cp, section, #objptr, sptr); \ + objptr = dynamic_cast<typeof(objptr)>(sptr); \ + } while (0) + +/* + * Basic support for object serialization. + */ +class Serializable +{ + protected: + void nameOut(std::ostream &os); + void nameOut(std::ostream &os, const std::string &_name); + + public: + Serializable() {} + virtual ~Serializable() {} + + // manditory virtual function, so objects must provide names + virtual const std::string name() const = 0; + + virtual void serialize(std::ostream &os) {} + virtual void unserialize(Checkpoint *cp, const std::string §ion) {} + + static Serializable *create(Checkpoint *cp, + const std::string §ion); + + static int ckptCount; + static int ckptMaxCount; + static int ckptPrevCount; + static void serializeAll(const std::string &cpt_dir); + static void unserializeAll(const std::string &cpt_dir); + static void unserializeGlobals(Checkpoint *cp); +}; + +// +// A SerializableBuilder serves as an evaluation context for a set of +// parameters that describe a specific instance of a Serializable. This +// evaluation context corresponds to a section in the .ini file (as +// with the base ParamContext) plus an optional node in the +// configuration hierarchy (the configNode member) for resolving +// Serializable references. SerializableBuilder is an abstract superclass; +// derived classes specialize the class for particular subclasses of +// Serializable (e.g., BaseCache). +// +// For typical usage, see the definition of +// SerializableClass::createObject(). +// +class SerializableBuilder +{ + public: + + SerializableBuilder() {} + + virtual ~SerializableBuilder() {} + + // Create the actual Serializable corresponding to the parameter + // values in this context. This function is overridden in derived + // classes to call a specific constructor for a particular + // subclass of Serializable. + virtual Serializable *create() = 0; +}; + +// +// An instance of SerializableClass corresponds to a class derived from +// Serializable. The SerializableClass instance serves to bind the string +// name (found in the config file) to a function that creates an +// instance of the appropriate derived class. +// +// This would be much cleaner in Smalltalk or Objective-C, where types +// are first-class objects themselves. +// +class SerializableClass +{ + public: + + // Type CreateFunc is a pointer to a function that creates a new + // simulation object builder based on a .ini-file parameter + // section (specified by the first string argument), a unique name + // for the object (specified by the second string argument), and + // an optional config hierarchy node (specified by the third + // argument). A pointer to the new SerializableBuilder is returned. + typedef Serializable *(*CreateFunc)(Checkpoint *cp, + const std::string §ion); + + static std::map<std::string,CreateFunc> *classMap; + + // Constructor. For example: + // + // SerializableClass baseCacheSerializableClass("BaseCacheSerializable", + // newBaseCacheSerializableBuilder); + // + SerializableClass(const std::string &className, CreateFunc createFunc); + + // create Serializable given name of class and pointer to + // configuration hierarchy node + static Serializable *createObject(Checkpoint *cp, + const std::string §ion); +}; + +// +// Macros to encapsulate the magic of declaring & defining +// SerializableBuilder and SerializableClass objects +// + +#define REGISTER_SERIALIZEABLE(CLASS_NAME, OBJ_CLASS) \ +SerializableClass the##OBJ_CLASS##Class(CLASS_NAME, \ + OBJ_CLASS::createForUnserialize); + +void +setCheckpointDir(const std::string &name); + +class Checkpoint +{ + private: + + IniFile *db; + const std::string basePath; + std::map<std::string, Serializable*> objMap; + + public: + Checkpoint(const std::string &cpt_dir, const std::string &path); + + const std::string cptDir; + + bool find(const std::string §ion, const std::string &entry, + std::string &value); + + bool findObj(const std::string §ion, const std::string &entry, + Serializable *&value); + + bool sectionExists(const std::string §ion); + + // The following static functions have to do with checkpoint + // creation rather than restoration. This class makes a handy + // namespace for them though. + + // Export current checkpoint directory name so other objects can + // derive filenames from it (e.g., memory). The return value is + // guaranteed to end in '/' so filenames can be directly appended. + // This function is only valid while a checkpoint is being created. + static std::string dir(); + + // Filename for base checkpoint file within directory. + static const char *baseFilename; +}; + +#endif // __SERIALIZE_HH__ diff --git a/src/sim/sim_events.cc b/src/sim/sim_events.cc new file mode 100644 index 000000000..d9e8bdeaa --- /dev/null +++ b/src/sim/sim_events.cc @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <string> + +#include "base/callback.hh" +#include "base/hostinfo.hh" +#include "sim/eventq.hh" +#include "sim/param.hh" +#include "sim/sim_events.hh" +#include "sim/sim_exit.hh" +#include "sim/startup.hh" +#include "sim/stats.hh" + +using namespace std; + +// +// handle termination event +// +void +SimLoopExitEvent::process() +{ + // if this got scheduled on a different queue (e.g. the committed + // instruction queue) then make a corresponding event on the main + // queue. + if (theQueue() != &mainEventQueue) { + exitSimLoop(cause, code); + delete this; + } + + // otherwise do nothing... the IsExitEvent flag takes care of + // exiting the simulation loop and returning this object to Python +} + + +const char * +SimLoopExitEvent::description() +{ + return "simulation loop exit"; +} + +void +exitSimLoop(Tick when, const std::string &message, int exit_code) +{ + new SimLoopExitEvent(when, message, exit_code); +} + +void +exitSimLoop(const std::string &message, int exit_code) +{ + exitSimLoop(curTick, message, exit_code); +} + +void +CountedDrainEvent::process() +{ + if (--count == 0) { + exitSimLoop("Finished drain"); + } +} + +// +// constructor: automatically schedules at specified time +// +CountedExitEvent::CountedExitEvent(EventQueue *q, const std::string &_cause, + Tick _when, int &_downCounter) + : Event(q, Sim_Exit_Pri), + cause(_cause), + downCounter(_downCounter) +{ + // catch stupid mistakes + assert(downCounter > 0); + + schedule(_when); +} + + +// +// handle termination event +// +void +CountedExitEvent::process() +{ + if (--downCounter == 0) { + exitSimLoop(cause, 0); + } +} + + +const char * +CountedExitEvent::description() +{ + return "counted exit"; +} + +#ifdef CHECK_SWAP_CYCLES +new CheckSwapEvent(&mainEventQueue, CHECK_SWAP_CYCLES); +#endif + +void +CheckSwapEvent::process() +{ + /* Check the amount of free swap space */ + long swap; + + /* returns free swap in KBytes */ + swap = procInfo("/proc/meminfo", "SwapFree:"); + + if (swap < 1000) + ccprintf(cerr, "\a\a\aWarning! Swap space is low (%d)\n", swap); + + if (swap < 100) { + cerr << "\a\aAborting Simulation! Inadequate swap space!\n\n"; + exitSimLoop("Lack of swap space"); + } + + schedule(curTick + interval); +} + +const char * +CheckSwapEvent::description() +{ + return "check swap"; +} + +// +// handle progress event: print message and reschedule +// +void +ProgressEvent::process() +{ + DPRINTFN("ProgressEvent\n"); + // reschedule for next interval + schedule(curTick + interval); +} + + +const char * +ProgressEvent::description() +{ + return "progress message"; +} diff --git a/src/sim/sim_events.hh b/src/sim/sim_events.hh new file mode 100644 index 000000000..3c4a9dd05 --- /dev/null +++ b/src/sim/sim_events.hh @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __SIM_SIM_EVENTS_HH__ +#define __SIM_SIM_EVENTS_HH__ + +#include "sim/eventq.hh" + +// +// Event to terminate simulation at a particular cycle/instruction +// +class SimLoopExitEvent : public Event +{ + private: + // string explaining why we're terminating + std::string cause; + int code; + + public: + // Default constructor. Only really used for derived classes. + SimLoopExitEvent() + : Event(&mainEventQueue, Sim_Exit_Pri) + { } + + SimLoopExitEvent(Tick _when, const std::string &_cause, int c = 0) + : Event(&mainEventQueue, Sim_Exit_Pri), cause(_cause), + code(c) + { setFlags(IsExitEvent); schedule(_when); } + + SimLoopExitEvent(EventQueue *q, + Tick _when, const std::string &_cause, int c = 0) + : Event(q, Sim_Exit_Pri), cause(_cause), code(c) + { setFlags(IsExitEvent); schedule(_when); } + + std::string getCause() { return cause; } + int getCode() { return code; } + + void process(); // process event + + virtual const char *description(); +}; + +class CountedDrainEvent : public SimLoopExitEvent +{ + private: + // Count of how many objects have not yet drained + int count; + public: + CountedDrainEvent() + : count(0) + { } + void process(); + + void setCount(int _count) { count = _count; } + + int getCount() { return count; } +}; + +// +// Event class to terminate simulation after 'n' related events have +// occurred using a shared counter: used to terminate when *all* +// threads have reached a particular instruction count +// +class CountedExitEvent : public Event +{ + private: + std::string cause; // string explaining why we're terminating + int &downCounter; // decrement & terminate if zero + + public: + CountedExitEvent(EventQueue *q, const std::string &_cause, + Tick _when, int &_downCounter); + + void process(); // process event + + virtual const char *description(); +}; + +// +// Event to check swap usage +// +class CheckSwapEvent : public Event +{ + private: + int interval; + + public: + CheckSwapEvent(EventQueue *q, int ival) + : Event(q), interval(ival) + { schedule(curTick + interval); } + + void process(); // process event + + virtual const char *description(); +}; + +// +// Progress event: print out cycle every so often so we know we're +// making forward progress. +// +class ProgressEvent : public Event +{ + protected: + Tick interval; + + public: + ProgressEvent(EventQueue *q, Tick ival) + : Event(q), interval(ival) + { schedule(curTick + interval); } + + void process(); // process event + + virtual const char *description(); +}; + +#endif // __SIM_SIM_EVENTS_HH__ diff --git a/src/sim/sim_exit.hh b/src/sim/sim_exit.hh new file mode 100644 index 000000000..545bf4ae0 --- /dev/null +++ b/src/sim/sim_exit.hh @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#ifndef __SIM_EXIT_HH__ +#define __SIM_EXIT_HH__ + +#include <string> + +#include "sim/host.hh" + +// forward declaration +class Callback; + +/// Register a callback to be called when Python exits. Defined in +/// sim/main.cc. +void registerExitCallback(Callback *); + +/// Schedule an event to exit the simulation loop (returning to +/// Python) at the indicated tick. The message and exit_code +/// parameters are saved in the SimLoopExitEvent to indicate why the +/// exit occurred. +void exitSimLoop(Tick when, const std::string &message, int exit_code = 0); + +/// Schedule an event to exit the simulation loop (returning to +/// Python) at the end of the current cycle (curTick). The message +/// and exit_code parameters are saved in the SimLoopExitEvent to +/// indicate why the exit occurred. +void exitSimLoop(const std::string &cause, int exit_code = 0); + +#endif // __SIM_EXIT_HH__ diff --git a/src/sim/sim_object.cc b/src/sim/sim_object.cc new file mode 100644 index 000000000..d12b06b7a --- /dev/null +++ b/src/sim/sim_object.cc @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +#include <assert.h> + +#include "base/callback.hh" +#include "base/inifile.hh" +#include "base/match.hh" +#include "base/misc.hh" +#include "base/trace.hh" +#include "base/stats/events.hh" +#include "sim/host.hh" +#include "sim/sim_object.hh" +#include "sim/stats.hh" +#include "sim/param.hh" + +using namespace std; + + +//////////////////////////////////////////////////////////////////////// +// +// SimObject member definitions +// +//////////////////////////////////////////////////////////////////////// + +// +// static list of all SimObjects, used for initialization etc. +// +SimObject::SimObjectList SimObject::simObjectList; + +namespace Stats { + extern ObjectMatch event_ignore; +} + +// +// SimObject constructor: used to maintain static simObjectList +// +SimObject::SimObject(Params *p) + : _params(p) +{ +#ifdef DEBUG + doDebugBreak = false; +#endif + + doRecordEvent = !Stats::event_ignore.match(name()); + simObjectList.push_back(this); + state = Running; +} + +// +// SimObject constructor: used to maintain static simObjectList +// +SimObject::SimObject(const string &_name) + : _params(new Params) +{ + _params->name = _name; +#ifdef DEBUG + doDebugBreak = false; +#endif + + doRecordEvent = !Stats::event_ignore.match(name()); + simObjectList.push_back(this); + state = Running; +} + +void +SimObject::connect() +{ +} + +void +SimObject::init() +{ +} + +// +// no default statistics, so nothing to do in base implementation +// +void +SimObject::regStats() +{ +} + +void +SimObject::regFormulas() +{ +} + +void +SimObject::resetStats() +{ +} + +// +// static function: +// call regStats() on all SimObjects and then regFormulas() on all +// SimObjects. +// +struct SimObjectResetCB : public Callback +{ + virtual void process() { SimObject::resetAllStats(); } +}; + +namespace { + static SimObjectResetCB StatResetCB; +} + +void +SimObject::regAllStats() +{ + SimObjectList::iterator i; + SimObjectList::iterator end = simObjectList.end(); + + /** + * @todo change cprintfs to DPRINTFs + */ + for (i = simObjectList.begin(); i != end; ++i) { +#ifdef STAT_DEBUG + cprintf("registering stats for %s\n", (*i)->name()); +#endif + (*i)->regStats(); + } + + for (i = simObjectList.begin(); i != end; ++i) { +#ifdef STAT_DEBUG + cprintf("registering formulas for %s\n", (*i)->name()); +#endif + (*i)->regFormulas(); + } + + Stats::registerResetCallback(&StatResetCB); +} + +// +// static function: call connect() on all SimObjects. +// +void +SimObject::connectAll() +{ + SimObjectList::iterator i = simObjectList.begin(); + SimObjectList::iterator end = simObjectList.end(); + + for (; i != end; ++i) { + SimObject *obj = *i; + obj->connect(); + } +} + +// +// static function: call init() on all SimObjects. +// +void +SimObject::initAll() +{ + SimObjectList::iterator i = simObjectList.begin(); + SimObjectList::iterator end = simObjectList.end(); + + for (; i != end; ++i) { + SimObject *obj = *i; + obj->init(); + } +} + +// +// static function: call resetStats() on all SimObjects. +// +void +SimObject::resetAllStats() +{ + SimObjectList::iterator i = simObjectList.begin(); + SimObjectList::iterator end = simObjectList.end(); + + for (; i != end; ++i) { + SimObject *obj = *i; + obj->resetStats(); + } +} + +// +// static function: serialize all SimObjects. +// +void +SimObject::serializeAll(ostream &os) +{ + SimObjectList::reverse_iterator ri = simObjectList.rbegin(); + SimObjectList::reverse_iterator rend = simObjectList.rend(); + + for (; ri != rend; ++ri) { + SimObject *obj = *ri; + obj->nameOut(os); + obj->serialize(os); + } +} + +void +SimObject::unserializeAll(Checkpoint *cp) +{ + SimObjectList::reverse_iterator ri = simObjectList.rbegin(); + SimObjectList::reverse_iterator rend = simObjectList.rend(); + + for (; ri != rend; ++ri) { + SimObject *obj = *ri; + DPRINTFR(Config, "Unserializing '%s'\n", + obj->name()); + if(cp->sectionExists(obj->name())) + obj->unserialize(cp, obj->name()); + else + warn("Not unserializing '%s': no section found in checkpoint.\n", + obj->name()); + } +} + +#ifdef DEBUG +// +// static function: flag which objects should have the debugger break +// +void +SimObject::debugObjectBreak(const string &objs) +{ + SimObjectList::const_iterator i = simObjectList.begin(); + SimObjectList::const_iterator end = simObjectList.end(); + + ObjectMatch match(objs); + for (; i != end; ++i) { + SimObject *obj = *i; + obj->doDebugBreak = match.match(obj->name()); + } +} + +void +debugObjectBreak(const char *objs) +{ + SimObject::debugObjectBreak(string(objs)); +} +#endif + +void +SimObject::recordEvent(const std::string &stat) +{ + if (doRecordEvent) + Stats::recordEvent(stat); +} + +unsigned int +SimObject::drain(Event *drain_event) +{ + state = Drained; + return 0; +} + +void +SimObject::resume() +{ + state = Running; +} + +void +SimObject::setMemoryMode(State new_mode) +{ + panic("setMemoryMode() should only be called on systems"); +} + +void +SimObject::switchOut() +{ + panic("Unimplemented!"); +} + +void +SimObject::takeOverFrom(BaseCPU *cpu) +{ + panic("Unimplemented!"); +} + +DEFINE_SIM_OBJECT_CLASS_NAME("SimObject", SimObject) diff --git a/src/sim/sim_object.hh b/src/sim/sim_object.hh new file mode 100644 index 000000000..38f2bdd23 --- /dev/null +++ b/src/sim/sim_object.hh @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2001-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Nathan Binkert + */ + +/* @file + * User Console Definitions + */ + +#ifndef __SIM_OBJECT_HH__ +#define __SIM_OBJECT_HH__ + +#include <map> +#include <list> +#include <vector> +#include <iostream> + +#include "sim/serialize.hh" +#include "sim/startup.hh" + +class BaseCPU; +class Event; + +/* + * Abstract superclass for simulation objects. Represents things that + * correspond to physical components and can be specified via the + * config file (CPUs, caches, etc.). + */ +class SimObject : public Serializable, protected StartupCallback +{ + public: + struct Params { + std::string name; + }; + + enum State { + Running, + Draining, + Drained + }; + private: + State state; + + protected: + Params *_params; + + void changeState(State new_state) { state = new_state; } + + public: + const Params *params() const { return _params; } + + State getState() { return state; } + + private: + typedef std::vector<SimObject *> SimObjectList; + + // list of all instantiated simulation objects + static SimObjectList simObjectList; + + public: + SimObject(Params *_params); + SimObject(const std::string &_name); + + virtual ~SimObject() {} + + virtual const std::string name() const { return params()->name; } + + // initialization pass of all objects. + // Gets invoked after construction, before unserialize. + virtual void init(); + virtual void connect(); + static void initAll(); + static void connectAll(); + + // register statistics for this object + virtual void regStats(); + virtual void regFormulas(); + virtual void resetStats(); + + // static: call reg_stats on all SimObjects + static void regAllStats(); + + // static: call resetStats on all SimObjects + static void resetAllStats(); + + // static: call nameOut() & serialize() on all SimObjects + static void serializeAll(std::ostream &); + static void unserializeAll(Checkpoint *cp); + + // Methods to drain objects in order to take checkpoints + // Or switch from timing -> atomic memory model + // Drain returns 0 if the simobject can drain immediately or + // the number of times the drain_event's process function will be called + // before the object will be done draining. Normally this should be 1 + virtual unsigned int drain(Event *drain_event); + virtual void resume(); + virtual void setMemoryMode(State new_mode); + virtual void switchOut(); + virtual void takeOverFrom(BaseCPU *cpu); + +#ifdef DEBUG + public: + bool doDebugBreak; + static void debugObjectBreak(const std::string &objs); +#endif + + public: + bool doRecordEvent; + void recordEvent(const std::string &stat); +}; + +#endif // __SIM_OBJECT_HH__ diff --git a/src/sim/startup.cc b/src/sim/startup.cc new file mode 100644 index 000000000..838352107 --- /dev/null +++ b/src/sim/startup.cc @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Kevin Lim + */ + +#include <list> + +#include "base/misc.hh" +#include "sim/debug.hh" +#include "sim/startup.hh" + +typedef std::list<StartupCallback *> startupq_t; + +startupq_t *startupq = NULL; + +StartupCallback::StartupCallback() +{ + if (startupq == NULL) + startupq = new startupq_t; + startupq->push_back(this); +} + +StartupCallback::~StartupCallback() +{ + startupq->remove(this); +} + +void StartupCallback::startup() { } + +void +SimStartup() +{ + startupq_t::iterator i = startupq->begin(); + startupq_t::iterator end = startupq->end(); + + while (i != end) { + (*i)->startup(); + ++i; + } +} diff --git a/src/sim/startup.hh b/src/sim/startup.hh new file mode 100644 index 000000000..cfc4450f4 --- /dev/null +++ b/src/sim/startup.hh @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __SIM_STARTUP_HH__ +#define __SIM_STARTUP_HH__ + +struct StartupCallback +{ + StartupCallback(); + virtual ~StartupCallback(); + virtual void startup(); +}; + +void SimStartup(); + +#endif // __SIM_STARTUP_HH__ diff --git a/src/sim/stat_control.cc b/src/sim/stat_control.cc new file mode 100644 index 000000000..dfed2a0c8 --- /dev/null +++ b/src/sim/stat_control.cc @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +// This file will contain default statistics for the simulator that +// don't really belong to a specific simulator object + +#include <fstream> +#include <iostream> +#include <list> + +#include "base/callback.hh" +#include "base/hostinfo.hh" +#include "base/statistics.hh" +#include "base/str.hh" +#include "base/time.hh" +#include "base/stats/output.hh" +#include "cpu/base.hh" +#include "sim/eventq.hh" +#include "sim/sim_object.hh" +#include "sim/stat_control.hh" +#include "sim/root.hh" + +using namespace std; + +Stats::Formula hostInstRate; +Stats::Formula hostTickRate; +Stats::Value hostMemory; +Stats::Value hostSeconds; + +Stats::Value simTicks; +Stats::Value simInsts; +Stats::Value simFreq; +Stats::Formula simSeconds; + +namespace Stats { + +Time statTime(true); +Tick startTick; +Tick lastDump(0); + +class SimTicksReset : public Callback +{ + public: + void process() + { + statTime.set(); + startTick = curTick; + } +}; + +double +statElapsedTime() +{ + Time now(true); + Time elapsed = now - statTime; + return elapsed(); +} + +Tick +statElapsedTicks() +{ + return curTick - startTick; +} + +SimTicksReset simTicksReset; + +void +InitSimStats() +{ + simInsts + .functor(BaseCPU::numSimulatedInstructions) + .name("sim_insts") + .desc("Number of instructions simulated") + .precision(0) + .prereq(simInsts) + ; + + simSeconds + .name("sim_seconds") + .desc("Number of seconds simulated") + ; + + simFreq + .scalar(Clock::Frequency) + .name("sim_freq") + .desc("Frequency of simulated ticks") + ; + + simTicks + .functor(statElapsedTicks) + .name("sim_ticks") + .desc("Number of ticks simulated") + ; + + hostInstRate + .name("host_inst_rate") + .desc("Simulator instruction rate (inst/s)") + .precision(0) + .prereq(simInsts) + ; + + hostMemory + .functor(memUsage) + .name("host_mem_usage") + .desc("Number of bytes of host memory used") + .prereq(hostMemory) + ; + + hostSeconds + .functor(statElapsedTime) + .name("host_seconds") + .desc("Real time elapsed on the host") + .precision(2) + ; + + hostTickRate + .name("host_tick_rate") + .desc("Simulator tick rate (ticks/s)") + .precision(0) + ; + + simSeconds = simTicks / simFreq; + hostInstRate = simInsts / hostSeconds; + hostTickRate = simTicks / hostSeconds; + + registerResetCallback(&simTicksReset); +} + +class StatEvent : public Event +{ + protected: + int flags; + Tick repeat; + + public: + StatEvent(EventQueue *queue, int _flags, Tick _when, Tick _repeat); + virtual void process(); + virtual const char *description(); +}; + +StatEvent::StatEvent(EventQueue *queue, int _flags, Tick _when, Tick _repeat) + : Event(queue, Stat_Event_Pri), + flags(_flags), repeat(_repeat) +{ + setFlags(AutoDelete); + schedule(_when); +} + +const char * +StatEvent::description() +{ + return "Statistics dump and/or reset"; +} + +void +StatEvent::process() +{ + if (flags & Stats::Dump) + DumpNow(); + + if (flags & Stats::Reset) { + cprintf("Resetting stats!\n"); + reset(); + } + + if (repeat) + schedule(curTick + repeat); +} + +list<Output *> OutputList; + +void +DumpNow() +{ + assert(lastDump <= curTick); + if (lastDump == curTick) + return; + lastDump = curTick; + + list<Output *>::iterator i = OutputList.begin(); + list<Output *>::iterator end = OutputList.end(); + for (; i != end; ++i) { + Output *output = *i; + if (!output->valid()) + continue; + + output->output(); + } +} + +void +SetupEvent(int flags, Tick when, Tick repeat, EventQueue *queue) +{ + if (queue == NULL) + queue = &mainEventQueue; + + new StatEvent(queue, flags, when, repeat); +} + +/* namespace Stats */ } + +void debugDumpStats() +{ + Stats::DumpNow(); +} + diff --git a/src/sim/stat_control.hh b/src/sim/stat_control.hh new file mode 100644 index 000000000..67f7cc491 --- /dev/null +++ b/src/sim/stat_control.hh @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __SIM_STAT_CONTROL_HH__ +#define __SIM_STAT_CONTROL_HH__ + +#include <fstream> +#include <list> + +class EventQueue; + +namespace Stats { + +enum { + Reset = 0x1, + Dump = 0x2 +}; + +class Output; +extern std::list<Output *> OutputList; + +void DumpNow(); +void SetupEvent(int flags, Tick when, Tick repeat = 0, EventQueue *queue = NULL); + +void InitSimStats(); + +/* namespace Stats */ } + +#endif // __SIM_STAT_CONTROL_HH__ diff --git a/src/sim/stats.hh b/src/sim/stats.hh new file mode 100644 index 000000000..97251283d --- /dev/null +++ b/src/sim/stats.hh @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __SIM_STATS_HH__ +#define __SIM_STATS_HH__ + +#include "base/statistics.hh" + +extern Stats::Formula simSeconds; +extern Stats::Value simTicks; + +#endif // __SIM_SIM_STATS_HH__ diff --git a/src/sim/syscall_emul.cc b/src/sim/syscall_emul.cc new file mode 100644 index 000000000..9028d590b --- /dev/null +++ b/src/sim/syscall_emul.cc @@ -0,0 +1,501 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Ali Saidi + */ + +#include <fcntl.h> +#include <unistd.h> + +#include <string> +#include <iostream> + +#include "sim/syscall_emul.hh" +#include "base/chunk_generator.hh" +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "cpu/base.hh" +#include "mem/page_table.hh" +#include "sim/process.hh" + +#include "sim/sim_exit.hh" + +using namespace std; +using namespace TheISA; + +void +SyscallDesc::doSyscall(int callnum, LiveProcess *process, ThreadContext *tc) +{ + DPRINTFR(SyscallVerbose, "%d: %s: syscall %s called w/arguments %d,%d,%d,%d\n", + curTick,tc->getCpuPtr()->name(), name, + tc->getSyscallArg(0),tc->getSyscallArg(1), + tc->getSyscallArg(2),tc->getSyscallArg(3)); + + SyscallReturn retval = (*funcPtr)(this, callnum, process, tc); + + DPRINTFR(SyscallVerbose, "%d: %s: syscall %s returns %d\n", + curTick,tc->getCpuPtr()->name(), name, retval.value()); + + if (!(flags & SyscallDesc::SuppressReturnValue)) + tc->setSyscallReturn(retval); +} + + +SyscallReturn +unimplementedFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + fatal("syscall %s (#%d) unimplemented.", desc->name, callnum); + + return 1; +} + + +SyscallReturn +ignoreFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + warn("ignoring syscall %s(%d, %d, ...)", desc->name, + tc->getSyscallArg(0), tc->getSyscallArg(1)); + + return 0; +} + + +SyscallReturn +exitFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + if (tc->exit()) { + exitSimLoop("target called exit()", tc->getSyscallArg(0) & 0xff); + } + + return 1; +} + + +SyscallReturn +getpagesizeFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + return (int)VMPageSize; +} + + +SyscallReturn +obreakFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + Addr junk; + + // change brk addr to first arg + Addr new_brk = tc->getSyscallArg(0); + if (new_brk != 0) { + for (ChunkGenerator gen(p->brk_point, new_brk - p->brk_point, + VMPageSize); !gen.done(); gen.next()) { + if (!p->pTable->translate(gen.addr(), junk)) + p->pTable->allocate(roundDown(gen.addr(), VMPageSize), + VMPageSize); + } + p->brk_point = new_brk; + } + DPRINTF(SyscallVerbose, "Break Point changed to: %#X\n", p->brk_point); + return p->brk_point; +} + + +SyscallReturn +closeFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + int target_fd = tc->getSyscallArg(0); + int status = close(p->sim_fd(target_fd)); + if (status >= 0) + p->free_fd(target_fd); + return status; +} + + +SyscallReturn +readFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + int fd = p->sim_fd(tc->getSyscallArg(0)); + int nbytes = tc->getSyscallArg(2); + BufferArg bufArg(tc->getSyscallArg(1), nbytes); + + int bytes_read = read(fd, bufArg.bufferPtr(), nbytes); + + if (bytes_read != -1) + bufArg.copyOut(tc->getMemPort()); + + return bytes_read; +} + +SyscallReturn +writeFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + int fd = p->sim_fd(tc->getSyscallArg(0)); + int nbytes = tc->getSyscallArg(2); + BufferArg bufArg(tc->getSyscallArg(1), nbytes); + + bufArg.copyIn(tc->getMemPort()); + + int bytes_written = write(fd, bufArg.bufferPtr(), nbytes); + + fsync(fd); + + return bytes_written; +} + + +SyscallReturn +lseekFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + int fd = p->sim_fd(tc->getSyscallArg(0)); + uint64_t offs = tc->getSyscallArg(1); + int whence = tc->getSyscallArg(2); + + off_t result = lseek(fd, offs, whence); + + return (result == (off_t)-1) ? -errno : result; +} + + +SyscallReturn +munmapFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + // given that we don't really implement mmap, munmap is really easy + return 0; +} + + +const char *hostname = "m5.eecs.umich.edu"; + +SyscallReturn +gethostnameFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + int name_len = tc->getSyscallArg(1); + BufferArg name(tc->getSyscallArg(0), name_len); + + strncpy((char *)name.bufferPtr(), hostname, name_len); + + name.copyOut(tc->getMemPort()); + + return 0; +} + +SyscallReturn +unlinkFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + string path; + + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) + return (TheISA::IntReg)-EFAULT; + + int result = unlink(path.c_str()); + return (result == -1) ? -errno : result; +} + +SyscallReturn +renameFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + string old_name; + + if (!tc->getMemPort()->tryReadString(old_name, tc->getSyscallArg(0))) + return -EFAULT; + + string new_name; + + if (!tc->getMemPort()->tryReadString(new_name, tc->getSyscallArg(1))) + return -EFAULT; + + int64_t result = rename(old_name.c_str(), new_name.c_str()); + return (result == -1) ? -errno : result; +} + +SyscallReturn +truncateFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + string path; + + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) + return -EFAULT; + + off_t length = tc->getSyscallArg(1); + + int result = truncate(path.c_str(), length); + return (result == -1) ? -errno : result; +} + +SyscallReturn +ftruncateFunc(SyscallDesc *desc, int num, LiveProcess *process, ThreadContext *tc) +{ + int fd = process->sim_fd(tc->getSyscallArg(0)); + + if (fd < 0) + return -EBADF; + + off_t length = tc->getSyscallArg(1); + + int result = ftruncate(fd, length); + return (result == -1) ? -errno : result; +} + +SyscallReturn +chownFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + string path; + + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) + return -EFAULT; + + /* XXX endianess */ + uint32_t owner = tc->getSyscallArg(1); + uid_t hostOwner = owner; + uint32_t group = tc->getSyscallArg(2); + gid_t hostGroup = group; + + int result = chown(path.c_str(), hostOwner, hostGroup); + return (result == -1) ? -errno : result; +} + +SyscallReturn +fchownFunc(SyscallDesc *desc, int num, LiveProcess *process, ThreadContext *tc) +{ + int fd = process->sim_fd(tc->getSyscallArg(0)); + + if (fd < 0) + return -EBADF; + + /* XXX endianess */ + uint32_t owner = tc->getSyscallArg(1); + uid_t hostOwner = owner; + uint32_t group = tc->getSyscallArg(2); + gid_t hostGroup = group; + + int result = fchown(fd, hostOwner, hostGroup); + return (result == -1) ? -errno : result; +} + + +SyscallReturn +dupFunc(SyscallDesc *desc, int num, LiveProcess *process, ThreadContext *tc) +{ + int fd = process->sim_fd(tc->getSyscallArg(0)); + + if (fd < 0) + return -EBADF; + + int result = dup(fd); + return (result == -1) ? -errno : process->alloc_fd(result); +} + + +SyscallReturn +fcntlFunc(SyscallDesc *desc, int num, LiveProcess *process, + ThreadContext *tc) +{ + int fd = tc->getSyscallArg(0); + + if (fd < 0 || process->sim_fd(fd) < 0) + return -EBADF; + + int cmd = tc->getSyscallArg(1); + switch (cmd) { + case 0: // F_DUPFD + // if we really wanted to support this, we'd need to do it + // in the target fd space. + warn("fcntl(%d, F_DUPFD) not supported, error returned\n", fd); + return -EMFILE; + + case 1: // F_GETFD (get close-on-exec flag) + case 2: // F_SETFD (set close-on-exec flag) + return 0; + + case 3: // F_GETFL (get file flags) + case 4: // F_SETFL (set file flags) + // not sure if this is totally valid, but we'll pass it through + // to the underlying OS + warn("fcntl(%d, %d) passed through to host\n", fd, cmd); + return fcntl(process->sim_fd(fd), cmd); + // return 0; + + case 7: // F_GETLK (get lock) + case 8: // F_SETLK (set lock) + case 9: // F_SETLKW (set lock and wait) + // don't mess with file locking... just act like it's OK + warn("File lock call (fcntl(%d, %d)) ignored.\n", fd, cmd); + return 0; + + default: + warn("Unknown fcntl command %d\n", cmd); + return 0; + } +} + +SyscallReturn +fcntl64Func(SyscallDesc *desc, int num, LiveProcess *process, + ThreadContext *tc) +{ + int fd = tc->getSyscallArg(0); + + if (fd < 0 || process->sim_fd(fd) < 0) + return -EBADF; + + int cmd = tc->getSyscallArg(1); + switch (cmd) { + case 33: //F_GETLK64 + warn("fcntl64(%d, F_GETLK64) not supported, error returned\n", fd); + return -EMFILE; + + case 34: // F_SETLK64 + case 35: // F_SETLKW64 + warn("fcntl64(%d, F_SETLK(W)64) not supported, error returned\n", fd); + return -EMFILE; + + default: + // not sure if this is totally valid, but we'll pass it through + // to the underlying OS + warn("fcntl64(%d, %d) passed through to host\n", fd, cmd); + return fcntl(process->sim_fd(fd), cmd); + // return 0; + } +} + +SyscallReturn +pipePseudoFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + int fds[2], sim_fds[2]; + int pipe_retval = pipe(fds); + + if (pipe_retval < 0) { + // error + return pipe_retval; + } + + sim_fds[0] = process->alloc_fd(fds[0]); + sim_fds[1] = process->alloc_fd(fds[1]); + + // Alpha Linux convention for pipe() is that fd[0] is returned as + // the return value of the function, and fd[1] is returned in r20. + tc->setIntReg(SyscallPseudoReturnReg, sim_fds[1]); + return sim_fds[0]; +} + + +SyscallReturn +getpidPseudoFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + // Make up a PID. There's no interprocess communication in + // fake_syscall mode, so there's no way for a process to know it's + // not getting a unique value. + + tc->setIntReg(SyscallPseudoReturnReg, process->ppid()); + return process->pid(); +} + + +SyscallReturn +getuidPseudoFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + // Make up a UID and EUID... it shouldn't matter, and we want the + // simulation to be deterministic. + + // EUID goes in r20. + tc->setIntReg(SyscallPseudoReturnReg, process->euid()); //EUID + return process->uid(); // UID +} + + +SyscallReturn +getgidPseudoFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + // Get current group ID. EGID goes in r20. + tc->setIntReg(SyscallPseudoReturnReg, process->egid()); //EGID + return process->gid(); +} + + +SyscallReturn +setuidFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + // can't fathom why a benchmark would call this. + warn("Ignoring call to setuid(%d)\n", tc->getSyscallArg(0)); + return 0; +} + +SyscallReturn +getpidFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + // Make up a PID. There's no interprocess communication in + // fake_syscall mode, so there's no way for a process to know it's + // not getting a unique value. + + tc->setIntReg(SyscallPseudoReturnReg, process->ppid()); //PID + return process->pid(); +} + +SyscallReturn +getppidFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + return process->ppid(); +} + +SyscallReturn +getuidFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + return process->uid(); // UID +} + +SyscallReturn +geteuidFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + return process->euid(); // UID +} + +SyscallReturn +getgidFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + return process->gid(); +} + +SyscallReturn +getegidFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + return process->egid(); +} + + diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh new file mode 100644 index 000000000..edd4e331d --- /dev/null +++ b/src/sim/syscall_emul.hh @@ -0,0 +1,943 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Kevin Lim + */ + +#ifndef __SIM_SYSCALL_EMUL_HH__ +#define __SIM_SYSCALL_EMUL_HH__ + +#define NO_STAT64 (defined(__APPLE__) || defined(__OpenBSD__) || \ + defined(__FreeBSD__) || defined(__CYGWIN__)) + +/// +/// @file syscall_emul.hh +/// +/// This file defines objects used to emulate syscalls from the target +/// application on the host machine. + +#include <errno.h> +#include <string> +#ifdef __CYGWIN32__ +#include <sys/fcntl.h> // for O_BINARY +#endif +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/uio.h> + +#include "sim/host.hh" // for Addr +#include "base/chunk_generator.hh" +#include "base/intmath.hh" // for RoundUp +#include "base/misc.hh" +#include "base/trace.hh" +#include "cpu/base.hh" +#include "cpu/thread_context.hh" +#include "mem/translating_port.hh" +#include "mem/page_table.hh" +#include "sim/process.hh" + +/// +/// System call descriptor. +/// +class SyscallDesc { + + public: + + /// Typedef for target syscall handler functions. + typedef SyscallReturn (*FuncPtr)(SyscallDesc *, int num, + LiveProcess *, ThreadContext *); + + const char *name; //!< Syscall name (e.g., "open"). + FuncPtr funcPtr; //!< Pointer to emulation function. + int flags; //!< Flags (see Flags enum). + + /// Flag values for controlling syscall behavior. + enum Flags { + /// Don't set return regs according to funcPtr return value. + /// Used for syscalls with non-standard return conventions + /// that explicitly set the ThreadContext regs (e.g., + /// sigreturn). + SuppressReturnValue = 1 + }; + + /// Constructor. + SyscallDesc(const char *_name, FuncPtr _funcPtr, int _flags = 0) + : name(_name), funcPtr(_funcPtr), flags(_flags) + { + } + + /// Emulate the syscall. Public interface for calling through funcPtr. + void doSyscall(int callnum, LiveProcess *proc, ThreadContext *tc); +}; + + +class BaseBufferArg { + + public: + + BaseBufferArg(Addr _addr, int _size) : addr(_addr), size(_size) + { + bufPtr = new uint8_t[size]; + // clear out buffer: in case we only partially populate this, + // and then do a copyOut(), we want to make sure we don't + // introduce any random junk into the simulated address space + memset(bufPtr, 0, size); + } + + virtual ~BaseBufferArg() { delete [] bufPtr; } + + // + // copy data into simulator space (read from target memory) + // + virtual bool copyIn(TranslatingPort *memport) + { + memport->readBlob(addr, bufPtr, size); + return true; // no EFAULT detection for now + } + + // + // copy data out of simulator space (write to target memory) + // + virtual bool copyOut(TranslatingPort *memport) + { + memport->writeBlob(addr, bufPtr, size); + return true; // no EFAULT detection for now + } + + protected: + Addr addr; + int size; + uint8_t *bufPtr; +}; + + +class BufferArg : public BaseBufferArg +{ + public: + BufferArg(Addr _addr, int _size) : BaseBufferArg(_addr, _size) { } + void *bufferPtr() { return bufPtr; } +}; + +template <class T> +class TypedBufferArg : public BaseBufferArg +{ + public: + // user can optionally specify a specific number of bytes to + // allocate to deal with those structs that have variable-size + // arrays at the end + TypedBufferArg(Addr _addr, int _size = sizeof(T)) + : BaseBufferArg(_addr, _size) + { } + + // type case + operator T*() { return (T *)bufPtr; } + + // dereference operators + T &operator*() { return *((T *)bufPtr); } + T* operator->() { return (T *)bufPtr; } + T &operator[](int i) { return ((T *)bufPtr)[i]; } +}; + +////////////////////////////////////////////////////////////////////// +// +// The following emulation functions are generic enough that they +// don't need to be recompiled for different emulated OS's. They are +// defined in sim/syscall_emul.cc. +// +////////////////////////////////////////////////////////////////////// + + +/// Handler for unimplemented syscalls that we haven't thought about. +SyscallReturn unimplementedFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Handler for unimplemented syscalls that we never intend to +/// implement (signal handling, etc.) and should not affect the correct +/// behavior of the program. Print a warning only if the appropriate +/// trace flag is enabled. Return success to the target program. +SyscallReturn ignoreFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target exit() handler: terminate simulation. +SyscallReturn exitFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target getpagesize() handler. +SyscallReturn getpagesizeFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target obreak() handler: set brk address. +SyscallReturn obreakFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target close() handler. +SyscallReturn closeFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target read() handler. +SyscallReturn readFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target write() handler. +SyscallReturn writeFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target lseek() handler. +SyscallReturn lseekFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target munmap() handler. +SyscallReturn munmapFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target gethostname() handler. +SyscallReturn gethostnameFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target unlink() handler. +SyscallReturn unlinkFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target rename() handler. +SyscallReturn renameFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + + +/// Target truncate() handler. +SyscallReturn truncateFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + + +/// Target ftruncate() handler. +SyscallReturn ftruncateFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + + +/// Target chown() handler. +SyscallReturn chownFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + + +/// Target fchown() handler. +SyscallReturn fchownFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target dup() handler. +SyscallReturn dupFunc(SyscallDesc *desc, int num, + LiveProcess *process, ThreadContext *tc); + +/// Target fnctl() handler. +SyscallReturn fcntlFunc(SyscallDesc *desc, int num, + LiveProcess *process, ThreadContext *tc); + +/// Target fcntl64() handler. +SyscallReturn fcntl64Func(SyscallDesc *desc, int num, + LiveProcess *process, ThreadContext *tc); + +/// Target setuid() handler. +SyscallReturn setuidFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target getpid() handler. +SyscallReturn getpidFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target getuid() handler. +SyscallReturn getuidFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target getgid() handler. +SyscallReturn getgidFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target getppid() handler. +SyscallReturn getppidFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target geteuid() handler. +SyscallReturn geteuidFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target getegid() handler. +SyscallReturn getegidFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + + + +/// Pseudo Funcs - These functions use a different return convension, +/// returning a second value in a register other than the normal return register +SyscallReturn pipePseudoFunc(SyscallDesc *desc, int num, + LiveProcess *process, ThreadContext *tc); + +/// Target getpidPseudo() handler. +SyscallReturn getpidPseudoFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target getuidPseudo() handler. +SyscallReturn getuidPseudoFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + +/// Target getgidPseudo() handler. +SyscallReturn getgidPseudoFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); + + +/// A readable name for 1,000,000, for converting microseconds to seconds. +const int one_million = 1000000; + +/// Approximate seconds since the epoch (1/1/1970). About a billion, +/// by my reckoning. We want to keep this a constant (not use the +/// real-world time) to keep simulations repeatable. +const unsigned seconds_since_epoch = 1000000000; + +/// Helper function to convert current elapsed time to seconds and +/// microseconds. +template <class T1, class T2> +void +getElapsedTime(T1 &sec, T2 &usec) +{ + int elapsed_usecs = curTick / Clock::Int::us; + sec = elapsed_usecs / one_million; + usec = elapsed_usecs % one_million; +} + +////////////////////////////////////////////////////////////////////// +// +// The following emulation functions are generic, but need to be +// templated to account for differences in types, constants, etc. +// +////////////////////////////////////////////////////////////////////// + +#if NO_STAT64 + typedef struct stat hst_stat; + typedef struct stat hst_stat64; +#else + typedef struct stat hst_stat; + typedef struct stat64 hst_stat64; +#endif + +//// Helper function to convert a host stat buffer to a target stat +//// buffer. Also copies the target buffer out to the simulated +//// memory space. Used by stat(), fstat(), and lstat(). + +template <typename target_stat, typename host_stat> +static void +convertStatBuf(target_stat &tgt, host_stat *host, bool fakeTTY = false) +{ + if (fakeTTY) + tgt->st_dev = 0xA; + else + tgt->st_dev = host->st_dev; + tgt->st_dev = htog(tgt->st_dev); + tgt->st_ino = host->st_ino; + tgt->st_ino = htog(tgt->st_ino); + if (fakeTTY) + tgt->st_rdev = 0x880d; + else + tgt->st_rdev = host->st_rdev; + tgt->st_rdev = htog(tgt->st_rdev); + tgt->st_size = host->st_size; + tgt->st_size = htog(tgt->st_size); + tgt->st_atimeX = host->st_atime; + tgt->st_atimeX = htog(tgt->st_atimeX); + tgt->st_mtimeX = host->st_mtime; + tgt->st_mtimeX = htog(tgt->st_mtimeX); + tgt->st_ctimeX = host->st_ctime; + tgt->st_ctimeX = htog(tgt->st_ctimeX); + tgt->st_blksize = host->st_blksize; + tgt->st_blksize = htog(tgt->st_blksize); + tgt->st_blocks = host->st_blocks; + tgt->st_blocks = htog(tgt->st_blocks); +} + +// Same for stat64 + +template <typename target_stat, typename host_stat64> +static void +convertStat64Buf(target_stat &tgt, host_stat64 *host, bool fakeTTY = false) +{ + convertStatBuf<target_stat, host_stat64>(tgt, host, fakeTTY); +#if defined(STAT_HAVE_NSEC) + tgt->st_atime_nsec = host->st_atime_nsec; + tgt->st_atime_nsec = htog(tgt->st_atime_nsec); + tgt->st_mtime_nsec = host->st_mtime_nsec; + tgt->st_mtime_nsec = htog(tgt->st_mtime_nsec); + tgt->st_ctime_nsec = host->st_ctime_nsec; + tgt->st_ctime_nsec = htog(tgt->st_ctime_nsec); +#else + tgt->st_atime_nsec = 0; + tgt->st_mtime_nsec = 0; + tgt->st_ctime_nsec = 0; +#endif +} + +//Here are a couple convenience functions +template<class OS> +static void +copyOutStatBuf(TranslatingPort * mem, Addr addr, + hst_stat *host, bool fakeTTY = false) +{ + typedef TypedBufferArg<typename OS::tgt_stat> tgt_stat_buf; + tgt_stat_buf tgt(addr); + convertStatBuf<tgt_stat_buf, hst_stat>(tgt, host, fakeTTY); + tgt.copyOut(mem); +} + +template<class OS> +static void +copyOutStat64Buf(TranslatingPort * mem, Addr addr, + hst_stat64 *host, bool fakeTTY = false) +{ + typedef TypedBufferArg<typename OS::tgt_stat64> tgt_stat_buf; + tgt_stat_buf tgt(addr); + convertStatBuf<tgt_stat_buf, hst_stat64>(tgt, host, fakeTTY); + tgt.copyOut(mem); +} + +/// Target ioctl() handler. For the most part, programs call ioctl() +/// only to find out if their stdout is a tty, to determine whether to +/// do line or block buffering. +template <class OS> +SyscallReturn +ioctlFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + int fd = tc->getSyscallArg(0); + unsigned req = tc->getSyscallArg(1); + + DPRINTF(SyscallVerbose, "ioctl(%d, 0x%x, ...)\n", fd, req); + + if (fd < 0 || process->sim_fd(fd) < 0) { + // doesn't map to any simulator fd: not a valid target fd + return -EBADF; + } + + switch (req) { + case OS::TIOCISATTY: + case OS::TIOCGETP: + case OS::TIOCSETP: + case OS::TIOCSETN: + case OS::TIOCSETC: + case OS::TIOCGETC: + case OS::TIOCGETS: + case OS::TIOCGETA: + return -ENOTTY; + + default: + fatal("Unsupported ioctl call: ioctl(%d, 0x%x, ...) @ 0x%llx\n", + fd, req, tc->readPC()); + } +} + +/// Target open() handler. +template <class OS> +SyscallReturn +openFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + std::string path; + + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) + return -EFAULT; + + if (path == "/dev/sysdev0") { + // This is a memory-mapped high-resolution timer device on Alpha. + // We don't support it, so just punt. + warn("Ignoring open(%s, ...)\n", path); + return -ENOENT; + } + + int tgtFlags = tc->getSyscallArg(1); + int mode = tc->getSyscallArg(2); + int hostFlags = 0; + + // translate open flags + for (int i = 0; i < OS::NUM_OPEN_FLAGS; i++) { + if (tgtFlags & OS::openFlagTable[i].tgtFlag) { + tgtFlags &= ~OS::openFlagTable[i].tgtFlag; + hostFlags |= OS::openFlagTable[i].hostFlag; + } + } + + // any target flags left? + if (tgtFlags != 0) + warn("Syscall: open: cannot decode flags 0x%x", tgtFlags); + +#ifdef __CYGWIN32__ + hostFlags |= O_BINARY; +#endif + + DPRINTF(SyscallVerbose, "opening file %s\n", path.c_str()); + + // open the file + int fd = open(path.c_str(), hostFlags, mode); + + return (fd == -1) ? -errno : process->alloc_fd(fd); +} + + +/// Target chmod() handler. +template <class OS> +SyscallReturn +chmodFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + std::string path; + + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) + return -EFAULT; + + uint32_t mode = tc->getSyscallArg(1); + mode_t hostMode = 0; + + // XXX translate mode flags via OS::something??? + hostMode = mode; + + // do the chmod + int result = chmod(path.c_str(), hostMode); + if (result < 0) + return -errno; + + return 0; +} + + +/// Target fchmod() handler. +template <class OS> +SyscallReturn +fchmodFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + int fd = tc->getSyscallArg(0); + if (fd < 0 || process->sim_fd(fd) < 0) { + // doesn't map to any simulator fd: not a valid target fd + return -EBADF; + } + + uint32_t mode = tc->getSyscallArg(1); + mode_t hostMode = 0; + + // XXX translate mode flags via OS::someting??? + hostMode = mode; + + // do the fchmod + int result = fchmod(process->sim_fd(fd), hostMode); + if (result < 0) + return -errno; + + return 0; +} + + +/// Target stat() handler. +template <class OS> +SyscallReturn +statFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + std::string path; + + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) + return -EFAULT; + + struct stat hostBuf; + int result = stat(path.c_str(), &hostBuf); + + if (result < 0) + return -errno; + + copyOutStatBuf<OS>(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); + + return 0; +} + + +/// Target fstat64() handler. +template <class OS> +SyscallReturn +fstat64Func(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + int fd = tc->getSyscallArg(0); + if (fd < 0 || process->sim_fd(fd) < 0) { + // doesn't map to any simulator fd: not a valid target fd + return -EBADF; + } + +#if NO_STAT64 + struct stat hostBuf; + int result = fstat(process->sim_fd(fd), &hostBuf); +#else + struct stat64 hostBuf; + int result = fstat64(process->sim_fd(fd), &hostBuf); +#endif + + if (result < 0) + return -errno; + + copyOutStat64Buf<OS>(tc->getMemPort(), tc->getSyscallArg(1), + &hostBuf, (fd == 1)); + + return 0; +} + + +/// Target lstat() handler. +template <class OS> +SyscallReturn +lstatFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + std::string path; + + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) + return -EFAULT; + + struct stat hostBuf; + int result = lstat(path.c_str(), &hostBuf); + + if (result < 0) + return -errno; + + copyOutStatBuf<OS>(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); + + return 0; +} + +/// Target lstat64() handler. +template <class OS> +SyscallReturn +lstat64Func(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + std::string path; + + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) + return -EFAULT; + +#if NO_STAT64 + struct stat hostBuf; + int result = lstat(path.c_str(), &hostBuf); +#else + struct stat64 hostBuf; + int result = lstat64(path.c_str(), &hostBuf); +#endif + + if (result < 0) + return -errno; + + copyOutStat64Buf<OS>(tc->getMemPort(), tc->getSyscallArg(1), &hostBuf); + + return 0; +} + +/// Target fstat() handler. +template <class OS> +SyscallReturn +fstatFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + int fd = process->sim_fd(tc->getSyscallArg(0)); + + DPRINTF(SyscallVerbose, "fstat(%d, ...)\n", fd); + + if (fd < 0) + return -EBADF; + + struct stat hostBuf; + int result = fstat(fd, &hostBuf); + + if (result < 0) + return -errno; + + copyOutStatBuf<OS>(tc->getMemPort(), tc->getSyscallArg(1), + &hostBuf, (fd == 1)); + + return 0; +} + + +/// Target statfs() handler. +template <class OS> +SyscallReturn +statfsFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + std::string path; + + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) + return -EFAULT; + + struct statfs hostBuf; + int result = statfs(path.c_str(), &hostBuf); + + if (result < 0) + return -errno; + + OS::copyOutStatfsBuf(tc->getMemPort(), + (Addr)(tc->getSyscallArg(1)), &hostBuf); + + return 0; +} + + +/// Target fstatfs() handler. +template <class OS> +SyscallReturn +fstatfsFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + int fd = process->sim_fd(tc->getSyscallArg(0)); + + if (fd < 0) + return -EBADF; + + struct statfs hostBuf; + int result = fstatfs(fd, &hostBuf); + + if (result < 0) + return -errno; + + OS::copyOutStatfsBuf(tc->getMemPort(), tc->getSyscallArg(1), + &hostBuf); + + return 0; +} + + +/// Target writev() handler. +template <class OS> +SyscallReturn +writevFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + int fd = tc->getSyscallArg(0); + if (fd < 0 || process->sim_fd(fd) < 0) { + // doesn't map to any simulator fd: not a valid target fd + return -EBADF; + } + + TranslatingPort *p = tc->getMemPort(); + uint64_t tiov_base = tc->getSyscallArg(1); + size_t count = tc->getSyscallArg(2); + struct iovec hiov[count]; + for (int i = 0; i < count; ++i) + { + typename OS::tgt_iovec tiov; + + p->readBlob(tiov_base + i*sizeof(typename OS::tgt_iovec), + (uint8_t*)&tiov, sizeof(typename OS::tgt_iovec)); + hiov[i].iov_len = gtoh(tiov.iov_len); + hiov[i].iov_base = new char [hiov[i].iov_len]; + p->readBlob(gtoh(tiov.iov_base), (uint8_t *)hiov[i].iov_base, + hiov[i].iov_len); + } + + int result = writev(process->sim_fd(fd), hiov, count); + + for (int i = 0; i < count; ++i) + { + delete [] (char *)hiov[i].iov_base; + } + + if (result < 0) + return -errno; + + return 0; +} + + +/// Target mmap() handler. +/// +/// We don't really handle mmap(). If the target is mmaping an +/// anonymous region or /dev/zero, we can get away with doing basically +/// nothing (since memory is initialized to zero and the simulator +/// doesn't really check addresses anyway). Always print a warning, +/// since this could be seriously broken if we're not mapping +/// /dev/zero. +// +/// Someday we should explicitly check for /dev/zero in open, flag the +/// file descriptor, and fail (or implement!) a non-anonymous mmap to +/// anything else. +template <class OS> +SyscallReturn +mmapFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) +{ + Addr start = tc->getSyscallArg(0); + uint64_t length = tc->getSyscallArg(1); + // int prot = tc->getSyscallArg(2); + int flags = tc->getSyscallArg(3); + // int fd = p->sim_fd(tc->getSyscallArg(4)); + // int offset = tc->getSyscallArg(5); + + if ((start % TheISA::VMPageSize) != 0 || + (length % TheISA::VMPageSize) != 0) { + warn("mmap failing: arguments not page-aligned: " + "start 0x%x length 0x%x", + start, length); + return -EINVAL; + } + + if (start != 0) { + warn("mmap: ignoring suggested map address 0x%x, using 0x%x", + start, p->mmap_end); + } + + // pick next address from our "mmap region" + start = p->mmap_end; + p->pTable->allocate(start, length); + p->mmap_end += length; + + if (!(flags & OS::TGT_MAP_ANONYMOUS)) { + warn("allowing mmap of file @ fd %d. " + "This will break if not /dev/zero.", tc->getSyscallArg(4)); + } + + return start; +} + +/// Target getrlimit() handler. +template <class OS> +SyscallReturn +getrlimitFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + unsigned resource = tc->getSyscallArg(0); + TypedBufferArg<typename OS::rlimit> rlp(tc->getSyscallArg(1)); + + switch (resource) { + case OS::TGT_RLIMIT_STACK: + // max stack size in bytes: make up a number (2MB for now) + rlp->rlim_cur = rlp->rlim_max = 8 * 1024 * 1024; + rlp->rlim_cur = htog(rlp->rlim_cur); + rlp->rlim_max = htog(rlp->rlim_max); + break; + + default: + std::cerr << "getrlimitFunc: unimplemented resource " << resource + << std::endl; + abort(); + break; + } + + rlp.copyOut(tc->getMemPort()); + return 0; +} + +/// Target gettimeofday() handler. +template <class OS> +SyscallReturn +gettimeofdayFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + TypedBufferArg<typename OS::timeval> tp(tc->getSyscallArg(0)); + + getElapsedTime(tp->tv_sec, tp->tv_usec); + tp->tv_sec += seconds_since_epoch; + tp->tv_sec = htog(tp->tv_sec); + tp->tv_usec = htog(tp->tv_usec); + + tp.copyOut(tc->getMemPort()); + + return 0; +} + + +/// Target utimes() handler. +template <class OS> +SyscallReturn +utimesFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + std::string path; + + if (!tc->getMemPort()->tryReadString(path, tc->getSyscallArg(0))) + return -EFAULT; + + TypedBufferArg<typename OS::timeval [2]> tp(tc->getSyscallArg(1)); + tp.copyIn(tc->getMemPort()); + + struct timeval hostTimeval[2]; + for (int i = 0; i < 2; ++i) + { + hostTimeval[i].tv_sec = gtoh((*tp)[i].tv_sec); + hostTimeval[i].tv_usec = gtoh((*tp)[i].tv_usec); + } + int result = utimes(path.c_str(), hostTimeval); + + if (result < 0) + return -errno; + + return 0; +} +/// Target getrusage() function. +template <class OS> +SyscallReturn +getrusageFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + int who = tc->getSyscallArg(0); // THREAD, SELF, or CHILDREN + TypedBufferArg<typename OS::rusage> rup(tc->getSyscallArg(1)); + + if (who != OS::TGT_RUSAGE_SELF) { + // don't really handle THREAD or CHILDREN, but just warn and + // plow ahead + warn("getrusage() only supports RUSAGE_SELF. Parameter %d ignored.", + who); + } + + getElapsedTime(rup->ru_utime.tv_sec, rup->ru_utime.tv_usec); + rup->ru_utime.tv_sec = htog(rup->ru_utime.tv_sec); + rup->ru_utime.tv_usec = htog(rup->ru_utime.tv_usec); + + rup->ru_stime.tv_sec = 0; + rup->ru_stime.tv_usec = 0; + rup->ru_maxrss = 0; + rup->ru_ixrss = 0; + rup->ru_idrss = 0; + rup->ru_isrss = 0; + rup->ru_minflt = 0; + rup->ru_majflt = 0; + rup->ru_nswap = 0; + rup->ru_inblock = 0; + rup->ru_oublock = 0; + rup->ru_msgsnd = 0; + rup->ru_msgrcv = 0; + rup->ru_nsignals = 0; + rup->ru_nvcsw = 0; + rup->ru_nivcsw = 0; + + rup.copyOut(tc->getMemPort()); + + return 0; +} + + + + +#endif // __SIM_SYSCALL_EMUL_HH__ diff --git a/src/sim/system.cc b/src/sim/system.cc new file mode 100644 index 000000000..ad70b9b03 --- /dev/null +++ b/src/sim/system.cc @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2003-2006 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Lisa Hsu + * Nathan Binkert + * Ali Saidi + */ + +#include "arch/isa_traits.hh" +#include "base/loader/object_file.hh" +#include "base/loader/symtab.hh" +#include "base/trace.hh" +#include "cpu/thread_context.hh" +#include "mem/mem_object.hh" +#include "mem/physical.hh" +#include "sim/builder.hh" +#include "sim/byteswap.hh" +#include "sim/system.hh" +#if FULL_SYSTEM +#include "arch/vtophys.hh" +#include "base/remote_gdb.hh" +#include "kern/kernel_stats.hh" +#endif + +using namespace std; +using namespace TheISA; + +vector<System *> System::systemList; + +int System::numSystemsRunning = 0; + +System::System(Params *p) + : SimObject(p->name), physmem(p->physmem), numcpus(0), +#if FULL_SYSTEM + init_param(p->init_param), + functionalPort(p->name + "-fport"), + virtPort(p->name + "-vport"), +#else + page_ptr(0), +#endif + memoryMode(p->mem_mode), _params(p) +{ + // add self to global system list + systemList.push_back(this); + +#if FULL_SYSTEM + kernelSymtab = new SymbolTable; + debugSymbolTable = new SymbolTable; + + + /** + * Get a functional port to memory + */ + Port *mem_port; + mem_port = physmem->getPort("functional"); + functionalPort.setPeer(mem_port); + mem_port->setPeer(&functionalPort); + + mem_port = physmem->getPort("functional"); + virtPort.setPeer(mem_port); + mem_port->setPeer(&virtPort); + + + /** + * Load the kernel code into memory + */ + // Load kernel code + kernel = createObjectFile(params()->kernel_path); + if (kernel == NULL) + fatal("Could not load kernel file %s", params()->kernel_path); + + // Load program sections into memory + kernel->loadSections(&functionalPort, LoadAddrMask); + + // setup entry points + kernelStart = kernel->textBase(); + kernelEnd = kernel->bssBase() + kernel->bssSize(); + kernelEntry = kernel->entryPoint(); + + // load symbols + if (!kernel->loadGlobalSymbols(kernelSymtab)) + panic("could not load kernel symbols\n"); + + if (!kernel->loadLocalSymbols(kernelSymtab)) + panic("could not load kernel local symbols\n"); + + if (!kernel->loadGlobalSymbols(debugSymbolTable)) + panic("could not load kernel symbols\n"); + + if (!kernel->loadLocalSymbols(debugSymbolTable)) + panic("could not load kernel local symbols\n"); + + DPRINTF(Loader, "Kernel start = %#x\n", kernelStart); + DPRINTF(Loader, "Kernel end = %#x\n", kernelEnd); + DPRINTF(Loader, "Kernel entry = %#x\n", kernelEntry); + DPRINTF(Loader, "Kernel loaded...\n"); +#endif // FULL_SYSTEM + + // increment the number of running systms + numSystemsRunning++; +} + +System::~System() +{ +#if FULL_SYSTEM + delete kernelSymtab; + delete kernel; +#else + panic("System::fixFuncEventAddr needs to be rewritten " + "to work with syscall emulation"); +#endif // FULL_SYSTEM} +} + +#if FULL_SYSTEM + + +int rgdb_wait = -1; + +#endif // FULL_SYSTEM + + +void +System::setMemoryMode(MemoryMode mode) +{ + assert(getState() == Drained); + memoryMode = mode; +} + +int +System::registerThreadContext(ThreadContext *tc, int id) +{ + if (id == -1) { + for (id = 0; id < threadContexts.size(); id++) { + if (!threadContexts[id]) + break; + } + } + + if (threadContexts.size() <= id) + threadContexts.resize(id + 1); + + if (threadContexts[id]) + panic("Cannot have two CPUs with the same id (%d)\n", id); + + threadContexts[id] = tc; + numcpus++; + +#if FULL_SYSTEM + RemoteGDB *rgdb = new RemoteGDB(this, tc); + GDBListener *gdbl = new GDBListener(rgdb, 7000 + id); + gdbl->listen(); + /** + * Uncommenting this line waits for a remote debugger to connect + * to the simulator before continuing. + */ + if (rgdb_wait != -1 && rgdb_wait == id) + gdbl->accept(); + + if (remoteGDB.size() <= id) { + remoteGDB.resize(id + 1); + } + + remoteGDB[id] = rgdb; +#endif // FULL_SYSTEM + + return id; +} + +void +System::startup() +{ + int i; + for (i = 0; i < threadContexts.size(); i++) + threadContexts[i]->activate(0); +} + +void +System::replaceThreadContext(ThreadContext *tc, int id) +{ + if (id >= threadContexts.size()) { + panic("replaceThreadContext: bad id, %d >= %d\n", + id, threadContexts.size()); + } + + threadContexts[id] = tc; +#if FULL_SYSTEM + remoteGDB[id]->replaceThreadContext(tc); +#endif // FULL_SYSTEM +} + +#if !FULL_SYSTEM +Addr +System::new_page() +{ + Addr return_addr = page_ptr << LogVMPageSize; + ++page_ptr; + return return_addr; +} +#endif + +void +System::serialize(ostream &os) +{ +#if FULL_SYSTEM + kernelSymtab->serialize("kernel_symtab", os); +#endif // FULL_SYSTEM +} + + +void +System::unserialize(Checkpoint *cp, const string §ion) +{ +#if FULL_SYSTEM + kernelSymtab->unserialize("kernel_symtab", cp, section); +#endif // FULL_SYSTEM +} + +void +System::printSystems() +{ + vector<System *>::iterator i = systemList.begin(); + vector<System *>::iterator end = systemList.end(); + for (; i != end; ++i) { + System *sys = *i; + cerr << "System " << sys->name() << ": " << hex << sys << endl; + } +} + +void +printSystems() +{ + System::printSystems(); +} + +const char *System::MemoryModeStrings[3] = {"invalid", "atomic", + "timing"}; + +#if FULL_SYSTEM + +// In full system mode, only derived classes (e.g. AlphaLinuxSystem) +// can be created directly. + +DEFINE_SIM_OBJECT_CLASS_NAME("System", System) + +#else + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(System) + + SimObjectParam<PhysicalMemory *> physmem; + SimpleEnumParam<System::MemoryMode> mem_mode; + +END_DECLARE_SIM_OBJECT_PARAMS(System) + +BEGIN_INIT_SIM_OBJECT_PARAMS(System) + + INIT_PARAM(physmem, "physical memory"), + INIT_ENUM_PARAM(mem_mode, "Memory Mode, (1=atomic, 2=timing)", + System::MemoryModeStrings) + +END_INIT_SIM_OBJECT_PARAMS(System) + +CREATE_SIM_OBJECT(System) +{ + System::Params *p = new System::Params; + p->name = getInstanceName(); + p->physmem = physmem; + p->mem_mode = mem_mode; + return new System(p); +} + +REGISTER_SIM_OBJECT("System", System) + +#endif diff --git a/src/sim/system.hh b/src/sim/system.hh new file mode 100644 index 000000000..3ab1d81f2 --- /dev/null +++ b/src/sim/system.hh @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Steve Reinhardt + * Lisa Hsu + * Nathan Binkert + */ + +#ifndef __SYSTEM_HH__ +#define __SYSTEM_HH__ + +#include <string> +#include <vector> + +#include "base/loader/symtab.hh" +#include "base/misc.hh" +#include "base/statistics.hh" +#include "config/full_system.hh" +#include "cpu/pc_event.hh" +#include "mem/port.hh" +#include "sim/sim_object.hh" +#if FULL_SYSTEM +#include "kern/system_events.hh" +#include "mem/vport.hh" +#endif + +class BaseCPU; +class ThreadContext; +class ObjectFile; +class PhysicalMemory; + +#if FULL_SYSTEM +class Platform; +class GDBListener; +class RemoteGDB; +#endif + +class System : public SimObject +{ + public: + enum MemoryMode { + Invalid=0, + Atomic, + Timing + }; + + static const char *MemoryModeStrings[3]; + + + MemoryMode getMemoryMode() { assert(memoryMode); return memoryMode; } + + /** Change the memory mode of the system. This should only be called by the + * python!! + * @param mode Mode to change to (atomic/timing) + */ + void setMemoryMode(MemoryMode mode); + + PhysicalMemory *physmem; + PCEventQueue pcEventQueue; + + std::vector<ThreadContext *> threadContexts; + int numcpus; + + int getNumCPUs() + { + if (numcpus != threadContexts.size()) + panic("cpu array not fully populated!"); + + return numcpus; + } + +#if FULL_SYSTEM + Platform *platform; + uint64_t init_param; + + /** Port to physical memory used for writing object files into ram at + * boot.*/ + FunctionalPort functionalPort; + VirtualPort virtPort; + + /** kernel symbol table */ + SymbolTable *kernelSymtab; + + /** Object pointer for the kernel code */ + ObjectFile *kernel; + + /** Begining of kernel code */ + Addr kernelStart; + + /** End of kernel code */ + Addr kernelEnd; + + /** Entry point in the kernel to start at */ + Addr kernelEntry; + +#else + + int page_ptr; + + +#endif // FULL_SYSTEM + + protected: + + MemoryMode memoryMode; + +#if FULL_SYSTEM + /** + * Fix up an address used to match PCs for hooking simulator + * events on to target function executions. See comment in + * system.cc for details. + */ + virtual Addr fixFuncEventAddr(Addr addr) = 0; + + /** + * Add a function-based event to the given function, to be looked + * up in the specified symbol table. + */ + template <class T> + T *addFuncEvent(SymbolTable *symtab, const char *lbl) + { + Addr addr = 0; // initialize only to avoid compiler warning + + if (symtab->findAddress(lbl, addr)) { + T *ev = new T(&pcEventQueue, lbl, fixFuncEventAddr(addr)); + return ev; + } + + return NULL; + } + + /** Add a function-based event to kernel code. */ + template <class T> + T *addKernelFuncEvent(const char *lbl) + { + return addFuncEvent<T>(kernelSymtab, lbl); + } + +#endif + public: +#if FULL_SYSTEM + std::vector<RemoteGDB *> remoteGDB; + std::vector<GDBListener *> gdbListen; + virtual bool breakpoint() = 0; +#endif // FULL_SYSTEM + + public: + struct Params + { + std::string name; + PhysicalMemory *physmem; + MemoryMode mem_mode; + +#if FULL_SYSTEM + Tick boot_cpu_frequency; + std::string boot_osflags; + uint64_t init_param; + + std::string kernel_path; + std::string readfile; + std::string symbolfile; +#endif + }; + + protected: + Params *_params; + + public: + System(Params *p); + ~System(); + + void startup(); + + const Params *params() const { return (const Params *)_params; } + + public: + +#if FULL_SYSTEM + /** + * Returns the addess the kernel starts at. + * @return address the kernel starts at + */ + Addr getKernelStart() const { return kernelStart; } + + /** + * Returns the addess the kernel ends at. + * @return address the kernel ends at + */ + Addr getKernelEnd() const { return kernelEnd; } + + /** + * Returns the addess the entry point to the kernel code. + * @return entry point of the kernel code + */ + Addr getKernelEntry() const { return kernelEntry; } + +#else + + Addr new_page(); + +#endif // FULL_SYSTEM + + int registerThreadContext(ThreadContext *tc, int tcIndex); + void replaceThreadContext(ThreadContext *tc, int tcIndex); + + void serialize(std::ostream &os); + void unserialize(Checkpoint *cp, const std::string §ion); + + public: + //////////////////////////////////////////// + // + // STATIC GLOBAL SYSTEM LIST + // + //////////////////////////////////////////// + + static std::vector<System *> systemList; + static int numSystemsRunning; + + static void printSystems(); + + +}; + +#endif // __SYSTEM_HH__ diff --git a/src/sim/vptr.hh b/src/sim/vptr.hh new file mode 100644 index 000000000..bcc22f0ca --- /dev/null +++ b/src/sim/vptr.hh @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2004-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#ifndef __ARCH_ALPHA_VPTR_HH__ +#define __ARCH_ALPHA_VPTR_HH__ + +#include "arch/vtophys.hh" +#include "arch/isa_traits.hh" + +class ThreadContext; + +template <class T> +class VPtr +{ + public: + typedef T Type; + + private: + ThreadContext *tc; + Addr ptr; + + public: + ThreadContext *GetTC() const { return tc; } + Addr GetPointer() const { return ptr; } + + public: + explicit VPtr(ThreadContext *_tc, Addr p = 0) : tc(_tc), ptr(p) { } + template <class U> + VPtr(const VPtr<U> &vp) : tc(vp.GetTC()), ptr(vp.GetPointer()) {} + ~VPtr() {} + + bool operator!() const + { + return ptr == 0; + } + + VPtr<T> operator+(int offset) + { + VPtr<T> ptr(*this); + ptr += offset; + + return ptr; + } + + const VPtr<T> &operator+=(int offset) + { + ptr += offset; + assert((ptr & (TheISA::PageBytes - 1)) + sizeof(T) + < TheISA::PageBytes); + + return *this; + } + + const VPtr<T> &operator=(Addr p) + { + assert((p & (TheISA::PageBytes - 1)) + sizeof(T) + < TheISA::PageBytes); + ptr = p; + + return *this; + } + + template <class U> + const VPtr<T> &operator=(const VPtr<U> &vp) + { + tc = vp.GetTC(); + ptr = vp.GetPointer(); + + return *this; + } + + operator T *() + { + panic("Needs to be rewritten\n"); +/* void *addr = vtomem(tc, ptr, sizeof(T)); + return (T *)addr; + */ + } + + T *operator->() + { + panic("Needs to be rewritten\n"); +/* void *addr = vtomem(tc, ptr, sizeof(T)); + return (T *)addr; + */ + } + + T &operator*() + { + panic("Needs to be rewritten\n"); +/* void *addr = vtomem(tc, ptr, sizeof(T)); + return *(T *)addr; + */ + } +}; + +#endif // __ARCH_ALPHA_VPTR_HH__ diff --git a/src/unittest/Makefile b/src/unittest/Makefile new file mode 100644 index 000000000..1f0584066 --- /dev/null +++ b/src/unittest/Makefile @@ -0,0 +1,98 @@ +# Copyright (c) 2006 The Regents of The University of Michigan +# 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. +# +# Authors: Nathan Binkert +# Steve Reinhardt + +CC?= gcc +CXX?= g++ +PYTHON?=/usr/bin/env python + +CURDIR?= $(shell /bin/pwd) +SRCDIR?= $(CURDIR)/.. + +CCFLAGS= -g -O0 -MMD -I. -I$(SRCDIR) -I- -DTRACING_ON=0 +MYSQL= -I/usr/include/mysql -L/usr/lib/mysql -lmysqlclient + +VPATH=$(SRCDIR):$(CURDIR) + +default: + @echo "You must specify a target" + +base/traceflags.cc base/traceflags.hh: $(SRCDIR)/base/traceflags.py + mkdir -p base; \ + cd base; \ + $(PYTHON) $< + +bitvectest: test/bitvectest.cc + $(CXX) $(CCFLAGS) -o $@ $^ + +circletest: test/circletest.cc base/circlebuf.cc + $(CXX) $(CCFLAGS) -o $@ $^ + +cprintftest: test/cprintftest.cc base/cprintf.cc + $(CXX) $(CCFLAGS) -o $@ $^ + +initest: test/initest.cc base/str.cc base/inifile.cc base/cprintf.cc + $(CXX) $(CCFLAGS) -o $@ $^ + +lrutest: test/lru_test.cc + $(CXX) $(CCFLAGS) -o $@ $^ + +nmtest: test/nmtest.cc base/output.cc base/hostinfo.cc base/cprintf.cc base/misc.cc base/loader/object_file.cc base/loader/symtab.cc base/misc.cc base/str.cc base/loader/aout_object.cc base/loader/ecoff_object.cc base/loader/elf_object.cc + $(CXX) $(CCFLAGS) -I/n/ziff/z/binkertn/build/work/ALPHA_FS -lelf -o $@ $^ + +offtest: test/offtest.cc + $(CXX) $(CCFLAGS) -o $@ $^ + +rangetest: test/rangetest.cc base/range.cc base/str.cc + $(CXX) $(CCFLAGS) -o $@ $^ + +STATTEST+= base/cprintf.cc base/hostinfo.cc base/misc.cc base/mysql.cc +STATTEST+= base/python.cc base/str.cc base/time.cc +STATTEST+= base/statistics.cc base/stats/mysql.cc base/stats/python.cc +STATTEST+= base/stats/statdb.cc base/stats/text.cc base/stats/visit.cc +STATTEST+= test/stattest.cc +stattest: $(STATTEST) + $(CXX) $(CCFLAGS) $(MYSQL) -o $@ $^ + +strnumtest: test/strnumtest.cc base/str.cc + $(CXX) $(CCFLAGS) -o $@ $^ + +symtest: test/symtest.cc base/misc.cc base/symtab.cc base/str.cc + $(CXX) $(CCFLAGS) -o $@ $^ + +tokentest: test/tokentest.cc base/str.cc + $(CXX) $(CCFLAGS) -o $@ $^ + +TRACE+=test/tracetest.cc base/trace.cc base/trace_flags.cc base/cprintf.cc +TRACE+=base/str.cc base/misc.cc +tracetest: $(TRACE) + $(CXX) $(CCFLAGS) -o $@ $^ + +clean: + @rm -rf *test *~ .#* *.core core base +.PHONY: clean diff --git a/src/unittest/bitvectest.cc b/src/unittest/bitvectest.cc new file mode 100644 index 000000000..440a150a3 --- /dev/null +++ b/src/unittest/bitvectest.cc @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iostream.h> + +#include <vector> + +int +main() +{ + vector<bool> v1(100); + + v1[0] = true; + v1.resize(500); + v1[100] = true; + v1[499] = true; + v1.resize(10000); + v1[9999] = true; + + cout << "v1.size() = " << v1.size() << "\n"; + for (int i = 0; i < v1.size(); i++) + if (v1[i]) + cout << "v1[" << i << "] = " << v1[i] << "\n"; + + cout << "\n"; + + vector<bool> v2 = v1; + + for (int i = 0; i < v2.size(); i++) + if (v2[i]) + cout << "v2[" << i << "] = " << v2[i] << "\n"; + + cout << "v1 " << ((v1 == v2) ? "==" : "!=") << " v2" << "\n"; + v2[8583] = true; + cout << "v1 " << ((v1 == v2) ? "==" : "!=") << " v2" << "\n"; + v1[8583] = true; + cout << "v1 " << ((v1 == v2) ? "==" : "!=") << " v2" << "\n"; + v1.resize(100000); + cout << "v1 " << ((v1 == v2) ? "==" : "!=") << " v2" << "\n"; + cout << flush; +} diff --git a/src/unittest/circletest.cc b/src/unittest/circletest.cc new file mode 100644 index 000000000..f072cf044 --- /dev/null +++ b/src/unittest/circletest.cc @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <fcntl.h> +#include <iostream.h> +#include <unistd.h> + +#include "base/circlebuf.hh" + +char *strings[] = +{ "This is the first test\n", + "he went with his woman to the store\n", + "the man with the bat hit the woman with the hat\n", + "that that is is that that was\n", + "sue sells sea shells by the sea shore\n", + "go to the store and buy me some milk and bread\n", + "the friendly flight attendants spoke soothingly to the frightened passengers in their native languages\n" +}; + +const int num_strings = sizeof(strings) / sizeof(char *); + +int +main() +{ + CircleBuf buf(1024); + + for (int count = 0; count < 100; count++) + buf.write(strings[count % num_strings]); + buf.read(STDOUT_FILENO); + write(STDOUT_FILENO, "<\n", 2); + + for (int count = 0; count < 100; count++) + buf.write(strings[count % num_strings]); + buf.read(STDOUT_FILENO, 100); + write(STDOUT_FILENO, "<\n", 2); + + buf.flush(); + buf.write("asdfa asdf asd fasdf asdf\n"); + buf.write(""); + buf.write(""); + buf.write(""); + buf.write(""); + buf.write(""); + buf.write(""); + buf.read(STDOUT_FILENO); + write(STDOUT_FILENO, "<\n", 2); +} diff --git a/src/unittest/cprintftest.cc b/src/unittest/cprintftest.cc new file mode 100644 index 000000000..9c3eb0cd6 --- /dev/null +++ b/src/unittest/cprintftest.cc @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iostream> +#include <list> +#include <string> +#include <sstream> + +#include "base/cprintf.hh" + +using namespace std; + +int +main() +{ + char foo[9]; + cprintf("%s\n", foo); + + cprintf("%shits%%s + %smisses%%s\n", "test", "test"); + cprintf("%%s%-10s %c he went home \'\"%d %#o %#x %1.5f %1.2E\n", + "hello", 'A', 1, 0xff, 0xfffffffffffffULL, 3.141592653589, 1.1e10); + + cout << cformat("%s %#x %s\n") << "hello" << 0 << "foo 0\n"; + cerr << cformat("%s %#x\n") << "hello" << 1 << "foo 1\n"; + + cprintf("another test\n"); + + stringstream buffer; + ccprintf(buffer, "%-10s %c he home \'\"%d %#o %#x %1.5f %1.2E\n", + "hello", 'A', 1, 0xff, 0xfffffffffffffULL, 3.14159265, 1.1e10); + + double f = 314159.26535897932384; + + #define ctest(x, y) printf(x, y); cprintf(x, y); cprintf("\n"); + ctest("%1.8f\n", f); + ctest("%2.8f\n", f); + ctest("%3.8f\n", f); + ctest("%4.8f\n", f); + ctest("%5.8f\n", f); + ctest("%6.8f\n", f); + ctest("%12.8f\n", f); + ctest("%1000.8f\n", f); + ctest("%1.0f\n", f); + ctest("%1.1f\n", f); + ctest("%1.2f\n", f); + ctest("%1.3f\n", f); + ctest("%1.4f\n", f); + ctest("%1.5f\n", f); + ctest("%1.6f\n", f); + ctest("%1.7f\n", f); + ctest("%1.8f\n", f); + ctest("%1.9f\n", f); + ctest("%1.10f\n", f); + ctest("%1.11f\n", f); + ctest("%1.12f\n", f); + ctest("%1.13f\n", f); + ctest("%1.14f\n", f); + ctest("%1.15f\n", f); + ctest("%1.16f\n", f); + ctest("%1.17f\n", f); + ctest("%1.18f\n", f); + + cout << "foo\n"; + + f = 0.00000026535897932384; + ctest("%1.8f\n", f); + ctest("%2.8f\n", f); + ctest("%3.8f\n", f); + ctest("%4.8f\n", f); + ctest("%5.8f\n", f); + ctest("%6.8f\n", f); + ctest("%12.8f\n", f); + ctest("%1.0f\n", f); + ctest("%1.1f\n", f); + ctest("%1.2f\n", f); + ctest("%1.3f\n", f); + ctest("%1.4f\n", f); + ctest("%1.5f\n", f); + ctest("%1.6f\n", f); + ctest("%1.7f\n", f); + ctest("%1.8f\n", f); + ctest("%1.9f\n", f); + ctest("%1.10f\n", f); + ctest("%1.11f\n", f); + ctest("%1.12f\n", f); + ctest("%1.13f\n", f); + ctest("%1.14f\n", f); + ctest("%1.15f\n", f); + ctest("%1.16f\n", f); + ctest("%1.17f\n", f); + ctest("%1.18f\n", f); + + f = 0.00000026535897932384; + ctest("%1.8e\n", f); + ctest("%2.8e\n", f); + ctest("%3.8e\n", f); + ctest("%4.8e\n", f); + ctest("%5.8e\n", f); + ctest("%6.8e\n", f); + ctest("%12.8e\n", f); + ctest("%1.0e\n", f); + ctest("%1.1e\n", f); + ctest("%1.2e\n", f); + ctest("%1.3e\n", f); + ctest("%1.4e\n", f); + ctest("%1.5e\n", f); + ctest("%1.6e\n", f); + ctest("%1.7e\n", f); + ctest("%1.8e\n", f); + ctest("%1.9e\n", f); + ctest("%1.10e\n", f); + ctest("%1.11e\n", f); + ctest("%1.12e\n", f); + ctest("%1.13e\n", f); + ctest("%1.14e\n", f); + ctest("%1.15e\n", f); + ctest("%1.16e\n", f); + ctest("%1.17e\n", f); + ctest("%1.18e\n", f); + + cout << buffer.str(); + + cout.width(0); + cout.precision(1); + cout << f << "\n"; + + string foo1 = "string test"; + cprintf("%s\n", foo1); + + stringstream foo2; + foo2 << "stringstream test"; + cprintf("%s\n", foo2); + + cprintf("%c %c\n", 'c', 65); + + cout << '9'; + return 0; +} diff --git a/src/unittest/foo.ini b/src/unittest/foo.ini new file mode 100644 index 000000000..534a4e001 --- /dev/null +++ b/src/unittest/foo.ini @@ -0,0 +1,10 @@ +#define JUNK +[Foo] +Foo1=89 +Foo2=384 + +[General] +Test3=89 + +[Junk] +Test4+=mia diff --git a/src/unittest/genini.py b/src/unittest/genini.py new file mode 100755 index 000000000..a969a6ee4 --- /dev/null +++ b/src/unittest/genini.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# Copyright (c) 2005 The Regents of The University of Michigan +# 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. +# +# Authors: Nathan Binkert + +import getopt, os, os.path, sys +from os.path import join as joinpath, realpath + +mypath = sys.path[0] +sys.path.append(joinpath(mypath, '..')) +sys.path.append(joinpath(mypath, '../python')) +sys.path.append(joinpath(mypath, '../util/pbs')) + +pathlist = [ '.' ] + +m5_build_env = {} + +try: + opts, args = getopt.getopt(sys.argv[1:], '-E:I:') + for opt,arg in opts: + if opt == '-E': + offset = arg.find('=') + if offset == -1: + name = arg + value = 'True' + else: + name = arg[:offset] + value = arg[offset+1:] + os.environ[name] = value + m5_build_env[name] = value + if opt == '-I': + pathlist.append(arg) +except getopt.GetoptError: + sys.exit('Improper Usage') + +import __main__ +__main__.m5_build_env = m5_build_env + +from m5 import * + +for path in pathlist: + AddToPath(path) + +for arg in args: + m5execfile(arg, globals()) + +if globals().has_key('root') and isinstance(root, Root): + instantiate(root) +else: + print "Instantiation skipped: no root object found." diff --git a/src/unittest/initest.cc b/src/unittest/initest.cc new file mode 100644 index 000000000..8f53fce5c --- /dev/null +++ b/src/unittest/initest.cc @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + * Steve Reinhardt + */ + +#include <iostream> +#include <fstream> +#include <string> +#include <vector> + +#include "base/inifile.hh" +#include "base/cprintf.hh" + +using namespace std; + +char *progname; + +void +usage() +{ + cout << "Usage: " << progname << " <ini file>\n"; + exit(1); +} + +#if 0 +char *defines = getenv("CONFIG_DEFINES"); +if (defines) { + char *c = defines; + while ((c = strchr(c, ' ')) != NULL) { + *c++ = '\0'; + count++; + } + count++; +} + +#endif + +int +main(int argc, char *argv[]) +{ + IniFile simConfigDB; + + progname = argv[0]; + + vector<char *> cppArgs; + + vector<char *> cpp_options; + cpp_options.reserve(argc * 2); + + for (int i = 1; i < argc; ++i) { + char *arg_str = argv[i]; + + // if arg starts with '-', parse as option, + // else treat it as a configuration file name and load it + if (arg_str[0] == '-') { + + // switch on second char + switch (arg_str[1]) { + case 'D': + case 'U': + case 'I': + // cpp options: record & pass to cpp. Note that these + // cannot have spaces, i.e., '-Dname=val' is OK, but + // '-D name=val' is not. I don't consider this a + // problem, since even though gnu cpp accepts the + // latter, other cpp implementations do not (Tru64, + // for one). + cppArgs.push_back(arg_str); + break; + + case '-': + // command-line configuration parameter: + // '--<section>:<parameter>=<value>' + + if (!simConfigDB.add(arg_str + 2)) { + // parse error + ccprintf(cerr, + "Could not parse configuration argument '%s'\n" + "Expecting --<section>:<parameter>=<value>\n", + arg_str); + exit(0); + } + break; + + default: + usage(); + } + } + else { + // no '-', treat as config file name + + if (!simConfigDB.loadCPP(arg_str, cppArgs)) { + cprintf("Error processing file %s\n", arg_str); + exit(1); + } + } + } + + string value; + +#define FIND(C, E) \ + if (simConfigDB.find(C, E, value)) \ + cout << ">" << value << "<\n"; \ + else \ + cout << "Not Found!\n" + + FIND("General", "Test2"); + FIND("Junk", "Test3"); + FIND("Junk", "Test4"); + FIND("General", "Test1"); + FIND("Junk2", "test3"); + FIND("General", "Test3"); + + cout << "\n"; + + simConfigDB.dump(); + + return 0; +} diff --git a/src/unittest/initest.ini b/src/unittest/initest.ini new file mode 100644 index 000000000..ebf2719d8 --- /dev/null +++ b/src/unittest/initest.ini @@ -0,0 +1,14 @@ +#define JUNK +// General stuff +#define FOO(X) BAR##X +[General] + Test1=FOO(asdf) + Test2=bar + +#ifdef JUNK +[Junk] // This is the junk +Test3=yo +Test4=mama +#endif + +#include "foo.ini" diff --git a/src/unittest/lru_test.cc b/src/unittest/lru_test.cc new file mode 100644 index 000000000..d10eb1dd0 --- /dev/null +++ b/src/unittest/lru_test.cc @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Dave Greene + * Nathan Binkert + */ + +#include <iostream> +#include "bhgp.hh" + +int main(void) +{ + typedef AssociativeTable<unsigned int, unsigned int> tableType; + tableType table(10, 4); // 40 entry table + + std::cout << "Initial state:" << std::endl; + table.dump(); + + std::cout << "Inserting (2, 1)" << std::endl; + table[2] = 1; + table.dump(); + + std::cout << "Inserting (5, 2)" << std::endl; + table[5] = 2; + table.dump(); + + std::cout << "Inserting (10 + 2, 3)" << std::endl; + table[10 + 2] = 3; + table.dump(); + + tableType::const_iterator i = table.find(2); + assert(i != table.end()); + std::cout << "Accessed 2: " << *i << std::endl; + table.dump(); + + i = table.find(10 + 2); + assert(i != table.end()); + std::cout << "Accessed 10 + 2: " << *i << std::endl; + table.dump(); + + i = table.find(34); + assert(i == table.end()); + + std::cout << "Inserting (2 * 10 + 2, 4)" << std::endl; + table[2 * 10 + 2] = 4; + table.dump(); + + std::cout << "Replacing (10 + 2) with 5" << std::endl; + table[10 + 2] = 5; + table.dump(); + + std::cout << "Inserting (3 * 10 + 2, 6)" << std::endl; + table[3 * 10 + 2] = 6; + table.dump(); + + std::cout << "Inserting (4 * 10 + 2, 7)" << std::endl; + table[4 * 10 + 2] = 7; + table.dump(); + + return(0); +} diff --git a/src/unittest/nmtest.cc b/src/unittest/nmtest.cc new file mode 100644 index 000000000..b6b74e08d --- /dev/null +++ b/src/unittest/nmtest.cc @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iostream> +#include <string> +#include <vector> + +#include "base/loader/object_file.hh" +#include "base/loader/symtab.hh" +#include "base/misc.hh" +#include "base/str.hh" + +using namespace std; +Tick curTick; + +ostream *outputStream = &cout; + +int +main(int argc, char *argv[]) +{ + if (argc != 2 && argc != 3) + panic("usage: %s <filename> <symbol>\n", argv[0]); + + ObjectFile *obj = createObjectFile(argv[1]); + if (!obj) + panic("file not found\n"); + + SymbolTable symtab; + obj->loadGlobalSymbols(&symtab); + obj->loadLocalSymbols(&symtab); + + if (argc == 2) { + SymbolTable::ATable::const_iterator i = symtab.getAddrTable().begin(); + SymbolTable::ATable::const_iterator end = symtab.getAddrTable().end(); + while (i != end) { + cprintf("%#x %s\n", i->first, i->second); + ++i; + } + } else { + string symbol = argv[2]; + Addr address; + + if (symbol[0] == '0' && symbol[1] == 'x') { + if (to_number(symbol, address) && + symtab.findSymbol(address, symbol)) + cprintf("address = %#x, symbol = %s\n", address, symbol); + else + cprintf("address = %#x was not found\n", address); + } else { + if (symtab.findAddress(symbol, address)) + cprintf("symbol = %s address = %#x\n", symbol, address); + else + cprintf("symbol = %s was not found\n", symbol); + } + } + + return 0; +} diff --git a/src/unittest/offtest.cc b/src/unittest/offtest.cc new file mode 100644 index 000000000..ebfb2515c --- /dev/null +++ b/src/unittest/offtest.cc @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <sys/types.h> +#include <stddef.h> +#include <stdio.h> +#include "dev/pcireg.h" + +int +main() +{ +#define POFFSET(x) \ + printf("offsetof(PCIConfig, hdr."#x") = %d\n", \ + offsetof(PCIConfig, hdr.x)) + + POFFSET(vendor); + POFFSET(device); + POFFSET(command); + POFFSET(status); + POFFSET(revision); + POFFSET(progIF); + POFFSET(subClassCode); + POFFSET(classCode); + POFFSET(cacheLineSize); + POFFSET(latencyTimer); + POFFSET(headerType); + POFFSET(bist); + POFFSET(pci0.baseAddr0); + POFFSET(pci0.baseAddr1); + POFFSET(pci0.baseAddr2); + POFFSET(pci0.baseAddr3); + POFFSET(pci0.baseAddr4); + POFFSET(pci0.baseAddr5); + POFFSET(pci0.cardbusCIS); + POFFSET(pci0.subsystemVendorID); + POFFSET(pci0.expansionROM); + POFFSET(pci0.reserved0); + POFFSET(pci0.reserved1); + POFFSET(pci0.interruptLine); + POFFSET(pci0.interruptPin); + POFFSET(pci0.minimumGrant); + POFFSET(pci0.minimumLatency); + + return 0; +} diff --git a/src/unittest/paramtest.cc b/src/unittest/paramtest.cc new file mode 100644 index 000000000..e513ab981 --- /dev/null +++ b/src/unittest/paramtest.cc @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +// +// This file is not part of the regular simulator. It is solely for +// testing the parameter code. Edit the Makefile to add param_test.cc +// to the sources list, then use configs/test.ini as the configuration +// file. +// +#include "sim/sim_object.hh" +#include "mem/cache/cache.hh" + +class ParamTest : public SimObject +{ + public: + ParamTest(string name) + : SimObject(name) + { + } + + virtual ~ParamTest() {} +}; + +enum Enum1Type { Enum0 }; +enum Enum2Type { Enum10 }; + +BEGIN_DECLARE_SIM_OBJECT_PARAMS(ParamTest) + + Param<int> intparam; + VectorParam<int> vecint; + Param<string> stringparam; + VectorParam<string> vecstring; + Param<bool> boolparam; + VectorParam<bool> vecbool; + SimObjectParam<BaseMemory *> memobj; + SimObjectVectorParam<BaseMemory *> vecmemobj; + SimpleEnumParam<Enum1Type> enum1; + MappedEnumParam<Enum2Type> enum2; + SimpleEnumVectorParam<Enum1Type> vecenum1; + MappedEnumVectorParam<Enum2Type> vecenum2; + +END_DECLARE_SIM_OBJECT_PARAMS(ParamTest) + +const char *enum1_strings[] = +{ + "zero", "one", "two", "three" +}; + +const EnumParamMap enum2_map[] = +{ + { "ten", 10 }, + { "twenty", 20 }, + { "thirty", 30 }, + { "forty", 40 } +}; + +BEGIN_INIT_SIM_OBJECT_PARAMS(ParamTest) + + INIT_PARAM(intparam, "intparam"), + INIT_PARAM(vecint, "vecint"), + INIT_PARAM(stringparam, "stringparam"), + INIT_PARAM(vecstring, "vecstring"), + INIT_PARAM(boolparam, "boolparam"), + INIT_PARAM(vecbool, "vecbool"), + INIT_PARAM(memobj, "memobj"), + INIT_PARAM(vecmemobj, "vecmemobj"), + INIT_ENUM_PARAM(enum1, "enum1", enum1_strings), + INIT_ENUM_PARAM(enum2, "enum2", enum2_map), + INIT_ENUM_PARAM(vecenum1, "vecenum1", enum1_strings), + INIT_ENUM_PARAM(vecenum2, "vecenum2", enum2_map) + +END_INIT_SIM_OBJECT_PARAMS(ParamTest) + + +CREATE_SIM_OBJECT(ParamTest) +{ + return new ParamTest(getInstanceName()); +} + +REGISTER_SIM_OBJECT("ParamTest", ParamTest) diff --git a/src/unittest/rangetest.cc b/src/unittest/rangetest.cc new file mode 100644 index 000000000..b7a68ab44 --- /dev/null +++ b/src/unittest/rangetest.cc @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iostream> +#include <string> + +#include "base/range.hh" + +using namespace std; + +int +main() +{ + Range<int> r1(make_pair(9, 28)); + Range<unsigned> r2("0x1000:+0x100"); + + cout << r1 << "\n" + << r2 << "\n"; + +#define RANGETEST(X, C, Y) \ + cout << X << " "#C" " << Y << " => " << ((X C Y) ? "true" : "false") << "\n" + +#define TESTEM(X, Y) do { \ + RANGETEST(X, < , Y); \ + RANGETEST(X, <=, Y); \ + RANGETEST(X, > , Y); \ + RANGETEST(X, >=, Y); \ + RANGETEST(X, ==, Y); \ + RANGETEST(X, !=, Y); \ + RANGETEST(Y, < , X); \ + RANGETEST(Y, <=, X); \ + RANGETEST(Y, > , X); \ + RANGETEST(Y, >=, X); \ + RANGETEST(Y, ==, X); \ + RANGETEST(Y, !=, X); \ +} while (0) + + TESTEM(8, r1); + TESTEM(9, r1); + TESTEM(27, r1); + TESTEM(28, r1); + + TESTEM(0x0fff, r2); + TESTEM(0x1000, r2); + TESTEM(0x10ff, r2); + TESTEM(0x1100, r2); + + return 0; +} diff --git a/src/unittest/sized_test.cc b/src/unittest/sized_test.cc new file mode 100644 index 000000000..f1bf7528f --- /dev/null +++ b/src/unittest/sized_test.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Dave Greene + * Nathan Binkert + */ + +#include <iostream> +#include <algorithm> + +#include "sized.hh" +#include <queue> +#include <typeinfo> + +template<typename C> +void print(C &cont) +{ + std::cout << std::endl; + std::cout << "Printing " << typeid(cont).name() << std::endl; + while (!cont.empty()) { + std::cout << cont.front() << " "; + cont.pop(); + } + std::cout << std::endl; +} + +int main(void) +{ + sized<std::queue<int>, sized_error_policy<std::queue<int> > > + error_queue(10); + sized<std::queue<int>, sized_drop_policy<std::queue<int> > > + drop_queue(5); + + for (int i = 0; i < 10; ++i) { + error_queue.push(i); + } + + for (int i = 0; i < 3; ++i) { + drop_queue.push(i); + } + + print(error_queue); + print(drop_queue); + + return(0); +} diff --git a/src/unittest/stattest.cc b/src/unittest/stattest.cc new file mode 100644 index 000000000..4e504fde9 --- /dev/null +++ b/src/unittest/stattest.cc @@ -0,0 +1,556 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iomanip> +#include <iostream> +#include <fstream> +#include <string> +#include <unistd.h> + +#include "base/cprintf.hh" +#include "base/misc.hh" +#include "base/statistics.hh" +#include "base/stats/text.hh" +#include "base/stats/mysql.hh" +#include "sim/host.hh" + +using namespace std; +using namespace Stats; + +Tick curTick = 0; +Tick ticksPerSecond = ULL(2000000000); + +Scalar<> s1; +Scalar<> s2; +Average<> s3; +Scalar<> s4; +Vector<> s5; +Distribution<> s6; +Vector<> s7; +AverageVector<> s8; +StandardDeviation<> s9; +AverageDeviation<> s10; +Scalar<> s11; +Distribution<> s12; +VectorDistribution<> s13; +VectorStandardDeviation<> s14; +VectorAverageDeviation<> s15; +Vector2d<> s16; + +Formula f1; +Formula f2; +Formula f3; +Value f4; +Value f5; +Formula f6; +Formula f7; + +ostream *outputStream = &cout; + +double +testfunc() +{ + return 9.8; +} + +class TestClass { + public: + double operator()() { return 9.7; } +}; + +char *progname = ""; + +void +usage() +{ + panic("incorrect usage.\n" + "usage:\n" + "\t%s [-t [-c] [-d]]\n", progname); +} + +int +main(int argc, char *argv[]) +{ + bool descriptions = false; + bool compat = false; + bool text = false; + string mysql_name; + string mysql_host; + string mysql_user = "binkertn"; + string mysql_passwd; + + char c; + progname = argv[0]; + while ((c = getopt(argc, argv, "cdh:P:p:s:tu:")) != -1) { + switch (c) { + case 'c': + compat = true; + break; + case 'd': + descriptions = true; + break; + case 'h': + mysql_host = optarg; + break; + case 'P': + mysql_passwd = optarg; + break; + case 's': + mysql_name = optarg; + break; + case 't': + text = true; + break; + case 'u': + mysql_user = optarg; + break; + default: + usage(); + } + } + + if (!text && (compat || descriptions)) + usage(); + + s5.init(5); + s6.init(1, 100, 13); + s7.init(7); + s8.init(10); + s12.init(1, 100, 13); + s13.init(4, 0, 99, 10); + s14.init(9); + s15.init(10); + s16.init(2, 9); + + s1 + .name("Stat01") + .desc("this is statistic 1") + ; + + s2 + .name("Stat02") + .desc("this is statistic 2") + .prereq(s11) + ; + + s3 + .name("Stat03") + .desc("this is statistic 3") + .prereq(f7) + ; + + s4 + .name("Stat04") + .desc("this is statistic 4") + .prereq(s11) + ; + + s5 + .name("Stat05") + .desc("this is statistic 5") + .prereq(s11) + .subname(0, "foo1") + .subname(1, "foo2") + .subname(2, "foo3") + .subname(3, "foo4") + .subname(4, "foo5") + ; + + s6 + .name("Stat06") + .desc("this is statistic 6") + .prereq(s11) + ; + + s7 + .name("Stat07") + .desc("this is statistic 7") + .precision(1) + .flags(pdf | total) + .prereq(s11) + ; + + s8 + .name("Stat08") + .desc("this is statistic 8") + .precision(2) + .prereq(s11) + .subname(4, "blarg") + ; + + s9 + .name("Stat09") + .desc("this is statistic 9") + .precision(4) + .prereq(s11) + ; + + s10 + .name("Stat10") + .desc("this is statistic 10") + .prereq(s11) + ; + + s12 + .name("Stat12") + .desc("this is statistic 12") + ; + + s13 + .name("Stat13") + .desc("this is statistic 13") + ; + + s14 + .name("Stat14") + .desc("this is statistic 14") + ; + + s15 + .name("Stat15") + .desc("this is statistic 15") + ; + + s16 + .name("Stat16") + .desc("this is statistic 16") + .flags(total) + .subname(0, "sub0") + .subname(1, "sub1") + .ysubname(0, "y0") + .ysubname(1, "y1") + ; + + f1 + .name("Formula1") + .desc("this is formula 1") + .prereq(s11) + ; + + f2 + .name("Formula2") + .desc("this is formula 2") + .prereq(s11) + .precision(1) + ; + + f3 + .name("Formula3") + .desc("this is formula 3") + .prereq(s11) + .subname(0, "bar1") + .subname(1, "bar2") + .subname(2, "bar3") + .subname(3, "bar4") + .subname(4, "bar5") + ; + + f4 + .functor(testfunc) + .name("Formula4") + .desc("this is formula 4") + ; + + TestClass testclass; + f5 + .functor(testclass) + .name("Formula5") + .desc("this is formula 5") + ; + + f6 + .name("Formula6") + .desc("this is formula 6") + ; + + f1 = s1 + s2; + f2 = (-s1) / (-s2) * (-s3 + ULL(100) + s4); + f3 = sum(s5) * s7; + f6 += constant(10.0); + f6 += s5[3]; + f7 = constant(1); + + check(); + reset(); + + s16[1][0] = 1; + s16[0][1] = 3; + s16[0][0] = 2; + s16[1][1] = 9; + s16[1][1] += 9; + s16[1][8] += 8; + s16[1][7] += 7; + s16[1][6] += 6; + s16[1][5] += 5; + s16[1][4] += 4; + + s11 = 1; + s3 = 9; + s8[3] = 9; + s15[0].sample(1234); + s15[1].sample(1234); + s15[2].sample(1234); + s15[3].sample(1234); + s15[4].sample(1234); + s15[5].sample(1234); + s15[6].sample(1234); + s15[7].sample(1234); + s15[8].sample(1234); + s15[9].sample(1234); + + s10.sample(1000000000); + curTick += ULL(1000000); + s10.sample(100000); + s10.sample(100000); + s10.sample(100000); + s10.sample(100000); + s10.sample(100000); + s10.sample(100000); + s10.sample(100000); + s10.sample(100000); + s10.sample(100000); + s10.sample(100000); + s10.sample(100000); + s10.sample(100000); + s10.sample(100000); + s13[0].sample(12); + s13[1].sample(29); + s13[2].sample(12); + s13[3].sample(29); + s13[0].sample(42); + s13[1].sample(29); + s13[2].sample(42); + s13[3].sample(32); + s13[0].sample(52); + s13[1].sample(49); + s13[2].sample(42); + s13[3].sample(25); + s13[0].sample(32); + s13[1].sample(49); + s13[2].sample(22); + s13[3].sample(49); + s13[0].sample(62); + s13[1].sample(99); + s13[2].sample(72); + s13[3].sample(23); + s13[0].sample(52); + s13[1].sample(78); + s13[2].sample(69); + s13[3].sample(49); + + s14[0].sample(1234); + s14[1].sample(4134); + s14[4].sample(1213); + s14[3].sample(1124); + s14[2].sample(1243); + s14[7].sample(1244); + s14[4].sample(7234); + s14[2].sample(9234); + s14[3].sample(1764); + s14[7].sample(1564); + s14[3].sample(3234); + s14[1].sample(2234); + s14[5].sample(1234); + s14[2].sample(4334); + s14[2].sample(1234); + s14[4].sample(4334); + s14[6].sample(1234); + s14[8].sample(8734); + s14[1].sample(5234); + s14[3].sample(8234); + s14[7].sample(5234); + s14[4].sample(4434); + s14[3].sample(7234); + s14[2].sample(1934); + s14[1].sample(9234); + s14[5].sample(5634); + s14[3].sample(1264); + s14[7].sample(5223); + s14[0].sample(1234); + s14[0].sample(5434); + s14[3].sample(8634); + s14[1].sample(1234); + + + s15[0].sample(1234); + s15[1].sample(4134); + curTick += ULL(1000000); + s15[4].sample(1213); + curTick += ULL(1000000); + s15[3].sample(1124); + curTick += ULL(1000000); + s15[2].sample(1243); + curTick += ULL(1000000); + s15[7].sample(1244); + curTick += ULL(1000000); + s15[4].sample(7234); + s15[2].sample(9234); + s15[3].sample(1764); + s15[7].sample(1564); + s15[3].sample(3234); + s15[1].sample(2234); + curTick += ULL(1000000); + s15[5].sample(1234); + curTick += ULL(1000000); + s15[9].sample(4334); + curTick += ULL(1000000); + s15[2].sample(1234); + curTick += ULL(1000000); + s15[4].sample(4334); + s15[6].sample(1234); + curTick += ULL(1000000); + s15[8].sample(8734); + curTick += ULL(1000000); + s15[1].sample(5234); + curTick += ULL(1000000); + s15[3].sample(8234); + curTick += ULL(1000000); + s15[7].sample(5234); + s15[4].sample(4434); + s15[3].sample(7234); + s15[2].sample(1934); + s15[1].sample(9234); + curTick += ULL(1000000); + s15[5].sample(5634); + s15[3].sample(1264); + s15[7].sample(5223); + s15[0].sample(1234); + s15[0].sample(5434); + s15[3].sample(8634); + curTick += ULL(1000000); + s15[1].sample(1234); + + s4 = curTick; + + s8[3] = 99999; + + s3 = 12; + s3++; + curTick += 9; + + s1 = 9; + s1 += 9; + s1 -= 11; + s1++; + ++s1; + s1--; + --s1; + + s2 = 9; + + s5[0] += 1; + s5[1] += 2; + s5[2] += 3; + s5[3] += 4; + s5[4] += 5; + + s7[0] = 10; + s7[1] = 20; + s7[2] = 30; + s7[3] = 40; + s7[4] = 50; + s7[5] = 60; + s7[6] = 70; + + s6.sample(0); + s6.sample(1); + s6.sample(2); + s6.sample(3); + s6.sample(4); + s6.sample(5); + s6.sample(6); + s6.sample(7); + s6.sample(8); + s6.sample(9); + + s6.sample(10); + s6.sample(10); + s6.sample(10); + s6.sample(10); + s6.sample(10); + s6.sample(10); + s6.sample(10); + s6.sample(10); + s6.sample(11); + s6.sample(19); + s6.sample(20); + s6.sample(20); + s6.sample(21); + s6.sample(21); + s6.sample(31); + s6.sample(98); + s6.sample(99); + s6.sample(99); + s6.sample(99); + + s7[0] = 700; + s7[1] = 600; + s7[2] = 500; + s7[3] = 400; + s7[4] = 300; + s7[5] = 200; + s7[6] = 100; + + s9.sample(100); + s9.sample(100); + s9.sample(100); + s9.sample(100); + s9.sample(10); + s9.sample(10); + s9.sample(10); + s9.sample(10); + s9.sample(10); + + curTick += 9; + s4 = curTick; + s6.sample(100); + s6.sample(100); + s6.sample(100); + s6.sample(101); + s6.sample(102); + + s12.sample(100); + + if (text) { + Text out(cout); + out.descriptions = descriptions; + out.compat = compat; + out(); + } + + if (!mysql_name.empty()) { + MySql out; + out.connect(mysql_host, mysql_user, mysql_passwd, "m5stats", + mysql_name, "test"); + out(); + } + + return 0; +} diff --git a/src/unittest/strnumtest.cc b/src/unittest/strnumtest.cc new file mode 100644 index 000000000..ea28e35df --- /dev/null +++ b/src/unittest/strnumtest.cc @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iostream.h> + +#include <string> +#include <vector> + +#include "base/str.hh" + +using namespace std; + +int +main(int argc, char *argv[]) +{ + if (argc != 2) { + cout << "Usage: " << argv[0] << " <number>\n"; + exit(1); + } + + string s = argv[1]; + +#define OUTVAL(valtype, type) do { \ + valtype value; \ + cout << "TYPE = " #valtype "\n"; \ + if (to_number(s, value)) { \ + cout << "Number(" << s << ") = " << dec \ + << (unsigned long long)(unsigned type)value << "\n" \ + << "Number(" << s << ") = " << dec \ + << (signed long long)(signed type)value << "\n" \ + << "Number(" << s << ") = 0x" << hex \ + << (unsigned long long)(unsigned type)value << "\n" \ + << "Number(" << s << ") = 0" << oct \ + << (unsigned long long)(unsigned type)value << "\n\n"; \ + } else \ + cout << "Number(" << s << ") is invalid\n\n"; \ + } while (0) + + OUTVAL(signed long long, long long); + OUTVAL(unsigned long long, long long); + OUTVAL(signed long, long); + OUTVAL(unsigned long, long); + OUTVAL(signed int, int); + OUTVAL(unsigned int, int); + OUTVAL(signed short, short); + OUTVAL(unsigned short, short); + OUTVAL(signed char, char); + OUTVAL(unsigned char, char); + + return 0; +} diff --git a/src/unittest/symtest.cc b/src/unittest/symtest.cc new file mode 100644 index 000000000..f0142b923 --- /dev/null +++ b/src/unittest/symtest.cc @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iostream.h> + +#include "base/str.hh" +#include "base/loader/symtab.hh" + +Tick curTick = 0; + +void +usage(const char *progname) +{ + cout << "Usage: " << progname << " <symbol file> <symbol>" << endl; + + exit(1); +} + +int +main(int argc, char *argv[]) +{ + SymbolTable symtab; + + if (argc != 3) + usage(argv[0]); + + if (!symtab.load(argv[1])) { + cout << "could not load symbol file: " << argv[1] << endl; + exit(1); + } + + string symbol = argv[2]; + Addr address; + + if (!to_number(symbol, address)) { + if (!symtab.findAddress(symbol, address)) { + cout << "could not find symbol: " << symbol << endl; + exit(1); + } + + cout << symbol << " -> " << "0x" << hex << address << endl; + } else { + if (!symtab.findSymbol(address, symbol)) { + cout << "could not find address: " << address << endl; + exit(1); + } + + cout << "0x" << hex << address << " -> " << symbol<< endl; + } + + return 0; +} diff --git a/src/unittest/tokentest.cc b/src/unittest/tokentest.cc new file mode 100644 index 000000000..128346b5b --- /dev/null +++ b/src/unittest/tokentest.cc @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2002-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include <iostream> +#include <string> +#include <vector> + +#include "base/str.hh" + +int +main(int argc, char *argv[]) +{ + using namespace std; + + if (argc != 3) { + cout << "Usage: " << argv[0] << " <string> <token>\n"; + exit(1); + } + + int i; + string test = argv[1]; + vector<string> tokens1; + vector<string> tokens2; + char token = argv[2][0]; + + cout << "string = \"" << test << "\", token = \'" << token << "\'\n"; + cout << "testing without ignore\n"; + tokenize(tokens1, test, token, false); + + if (tokens1.size()) { + int size = tokens1.size(); + cout << "size = " << size << "\n"; + for (i = 0; i < size; i++) { + cout << "'" << tokens1[i] << "' (" << tokens1[i].size() + << ")" << ((i == size - 1) ? "\n" : ", "); + } + } else { + cout << "no tokens" << endl; + } + + cout << "testing with ignore\n"; + tokenize(tokens2, test, token, true); + + if (tokens2.size()) { + int size = tokens2.size(); + cout << "size = " << size << "\n"; + for (i = 0; i < size; i++) { + cout << "'" << tokens2[i] << "' (" << tokens2[i].size() + << ")" << ((i == size - 1) ? "\n" : ", "); + } + } else { + cout << "no tokens" << endl; + } + + return 0; +} diff --git a/src/unittest/tracetest.cc b/src/unittest/tracetest.cc new file mode 100644 index 000000000..b1343aac3 --- /dev/null +++ b/src/unittest/tracetest.cc @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2003-2005 The Regents of The University of Michigan + * 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. + * + * Authors: Nathan Binkert + */ + +#include "sim/host.hh" +#include "base/trace.hh" + +using namespace std; + +Tick curTick = 0; + +struct foo +{ + foo() + { + char foo[9] = "testing"; + DPRINTF(Loader, "%s\n", foo); + } +}; + +int +main() +{ + Trace::flags[Trace::Loader] = true; + Trace::dprintf_stream = &cout; + + foo f; + + return 0; +} |