mirror of
https://github.com/stnolting/neorv32.git
synced 2025-04-23 21:57:33 -04:00
Merge branch 'main' into fixed-bl-isr-alignment
This commit is contained in:
commit
96b0ec1250
78 changed files with 1968 additions and 1556 deletions
|
@ -29,6 +29,11 @@ mimpid = 0x01040312 -> Version 01.04.03.12 -> v1.4.3.12
|
|||
|
||||
| Date | Version | Comment | Ticket |
|
||||
|:----:|:-------:|:--------|:------:|
|
||||
| 12.01.2025 | 1.10.9.5 | minor rtl cleanups; :bug: fix minor bug (multiple drivers on ICC nets; introduced in version 1.10.9.2) | [#1151](https://github.com/stnolting/neorv32/pull/1151) |
|
||||
| 11.01.2025 | 1.10.9.4 | :warning: RTE: use a single, global trap handler table that applies to _both_ cores | [#1150](https://github.com/stnolting/neorv32/pull/1150) |
|
||||
| 10.01.2025 | 1.10.9.3 | split functional behavior of `fence` and `fence.i` instructions | [#1149](https://github.com/stnolting/neorv32/pull/1149) |
|
||||
| 10.01.2025 | 1.10.9.2 | clean-up SMP dual-core configuration (HW and SW optimizations) | [#1146](https://github.com/stnolting/neorv32/pull/1146) |
|
||||
| 09.01.2025 | 1.10.9.1 | fix side-effects of CSR read instructions | [#1145](https://github.com/stnolting/neorv32/pull/1145) |
|
||||
| 08.01.2025 | [**:rocket:1.10.9**](https://github.com/stnolting/neorv32/releases/tag/v1.10.9) | **New release** | |
|
||||
| 07.01.2025 | 1.10.8.9 | rtl edits and cleanups; add dedicated "core complex" wrapper (CPU + L1 caches + bus switch) | [#1144](https://github.com/stnolting/neorv32/pull/1144) |
|
||||
| 04.01.2025 | 1.10.8.8 | :sparkles: add inter-core communication (ICC) for the SMP dual-core setup | [#1142](https://github.com/stnolting/neorv32/pull/1142) |
|
||||
|
|
|
@ -149,7 +149,7 @@ for **custom RISC-V instructions**;
|
|||
caches ([iCACHE](https://stnolting.github.io/neorv32/#_processor_internal_instruction_cache_icache) and
|
||||
[dCACHE](https://stnolting.github.io/neorv32/#_processor_internal_data_cache_dcache))
|
||||
* pre-installed bootloader ([BOOTLDROM](https://stnolting.github.io/neorv32/#_bootloader_rom_bootrom)) with serial user interface;
|
||||
allows booting application code via UART or from external SPI flash
|
||||
allows booting application code via UART, TWI or from external SPI flash
|
||||
|
||||
**Timers and Counters**
|
||||
|
||||
|
|
344
docs/Doxyfile
344
docs/Doxyfile
|
@ -1,4 +1,4 @@
|
|||
# Doxyfile 1.9.4
|
||||
# Doxyfile 1.9.8
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
|
@ -19,7 +19,8 @@
|
|||
# configuration file:
|
||||
# doxygen -x [configFile]
|
||||
# Use doxygen to compare the used configuration file with the template
|
||||
# configuration file without replacing the environment variables:
|
||||
# configuration file without replacing the environment variables or CMake type
|
||||
# replacement variables:
|
||||
# doxygen -x_noenv [configFile]
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -85,7 +86,7 @@ CREATE_SUBDIRS = NO
|
|||
# level increment doubles the number of directories, resulting in 4096
|
||||
# directories at level 8 which is the default and also the maximum value. The
|
||||
# sub-directories are organized in 2 levels, the first level always has a fixed
|
||||
# numer of 16 directories.
|
||||
# number of 16 directories.
|
||||
# Minimum value: 0, maximum value: 8, default value: 8.
|
||||
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
|
||||
|
||||
|
@ -352,6 +353,17 @@ MARKDOWN_SUPPORT = YES
|
|||
|
||||
TOC_INCLUDE_HEADINGS = 5
|
||||
|
||||
# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
|
||||
# generate identifiers for the Markdown headings. Note: Every identifier is
|
||||
# unique.
|
||||
# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
|
||||
# sequence number starting at 0 and GITHUB use the lower case version of title
|
||||
# with any whitespace replaced by '-' and punctuation characters removed.
|
||||
# The default value is: DOXYGEN.
|
||||
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
||||
|
||||
MARKDOWN_ID_STYLE = DOXYGEN
|
||||
|
||||
# When enabled doxygen tries to link words that correspond to documented
|
||||
# classes, or namespaces to their corresponding documentation. Such a link can
|
||||
# be prevented in individual cases by putting a % sign in front of the word or
|
||||
|
@ -476,6 +488,14 @@ LOOKUP_CACHE_SIZE = 0
|
|||
|
||||
NUM_PROC_THREADS = 1
|
||||
|
||||
# If the TIMESTAMP tag is set different from NO then each generated page will
|
||||
# contain the date or date and time when the page was generated. Setting this to
|
||||
# NO can help when comparing the output of multiple runs.
|
||||
# Possible values are: YES, NO, DATETIME and DATE.
|
||||
# The default value is: NO.
|
||||
|
||||
TIMESTAMP = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -557,7 +577,8 @@ 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, these classes will be included in the various overviews. This option
|
||||
# has no effect if EXTRACT_ALL is enabled.
|
||||
# will also hide undocumented C++ concepts if enabled. This option has no effect
|
||||
# if EXTRACT_ALL is enabled.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
|
@ -595,7 +616,8 @@ INTERNAL_DOCS = NO
|
|||
# Windows (including Cygwin) and MacOS, users should typically set this option
|
||||
# to NO, whereas on Linux or other Unix flavors it should typically be set to
|
||||
# YES.
|
||||
# The default value is: system dependent.
|
||||
# Possible values are: SYSTEM, NO and YES.
|
||||
# The default value is: SYSTEM.
|
||||
|
||||
CASE_SENSE_NAMES = YES
|
||||
|
||||
|
@ -847,11 +869,26 @@ WARN_IF_INCOMPLETE_DOC = YES
|
|||
|
||||
WARN_NO_PARAMDOC = NO
|
||||
|
||||
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
|
||||
# undocumented enumeration values. If set to NO, doxygen will accept
|
||||
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
|
||||
# will automatically be disabled.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_IF_UNDOC_ENUM_VAL = NO
|
||||
|
||||
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
|
||||
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
||||
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
||||
# at the end of the doxygen process doxygen will return with a non-zero status.
|
||||
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
|
||||
# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
|
||||
# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
|
||||
# write the warning messages in between other messages but write them at the end
|
||||
# of a run, in case a WARN_LOGFILE is defined the warning messages will be
|
||||
# besides being in the defined file also be shown at the end of a run, unless
|
||||
# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
|
||||
# the behavior will remain as with the setting FAIL_ON_WARNINGS.
|
||||
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_AS_ERROR = NO
|
||||
|
@ -908,10 +945,21 @@ INPUT = $(PWD)/../README.md \
|
|||
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
|
||||
# documentation (see:
|
||||
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
|
||||
# See also: INPUT_FILE_ENCODING
|
||||
# The default value is: UTF-8.
|
||||
|
||||
INPUT_ENCODING = UTF-8
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
|
||||
# character encoding on a per file pattern basis. Doxygen will compare the file
|
||||
# name with each pattern and apply the encoding instead of the default
|
||||
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
|
||||
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
|
||||
# "INPUT_ENCODING" for further information on supported encodings.
|
||||
|
||||
INPUT_FILE_ENCODING =
|
||||
|
||||
# If the value of the INPUT tag contains directories, you can use the
|
||||
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
|
||||
# *.h) to filter out the source-files in the directories.
|
||||
|
@ -923,12 +971,12 @@ INPUT_ENCODING = UTF-8
|
|||
# Note the list of default checked file patterns might differ from the list of
|
||||
# default file extension mappings.
|
||||
#
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
|
||||
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
|
||||
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
|
||||
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
|
||||
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
|
||||
# *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
|
||||
# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl,
|
||||
# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php,
|
||||
# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be
|
||||
# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
|
||||
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
|
||||
|
||||
FILE_PATTERNS = *.c \
|
||||
*.h
|
||||
|
@ -970,9 +1018,6 @@ EXCLUDE_PATTERNS = ~* \
|
|||
# output. The symbol name can be a fully qualified name, a word, or if the
|
||||
# wildcard * is used, a substring. Examples: ANamespace, AClass,
|
||||
# ANamespace::AClass, ANamespace::*Test
|
||||
#
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
EXCLUDE_SYMBOLS =
|
||||
|
||||
|
@ -1017,6 +1062,11 @@ IMAGE_PATH =
|
|||
# code is scanned, but not when the output code is generated. If lines are added
|
||||
# or removed, the anchors will not be placed correctly.
|
||||
#
|
||||
# Note that doxygen will use the data processed and written to standard output
|
||||
# for further processing, therefore nothing else, like debug statements or used
|
||||
# commands (so in case of a Windows batch file always use @echo OFF), should be
|
||||
# written to standard output.
|
||||
#
|
||||
# Note that for custom extensions or not directly supported extensions you also
|
||||
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
|
||||
# properly processed by doxygen.
|
||||
|
@ -1058,6 +1108,15 @@ FILTER_SOURCE_PATTERNS =
|
|||
|
||||
USE_MDFILE_AS_MAINPAGE = doxygen_main.md
|
||||
|
||||
# The Fortran standard specifies that for fixed formatted Fortran code all
|
||||
# characters from position 72 are to be considered as comment. A common
|
||||
# extension is to allow longer lines before the automatic comment starts. The
|
||||
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
|
||||
# be processed before the automatic comment starts.
|
||||
# Minimum value: 7, maximum value: 10000, default value: 72.
|
||||
|
||||
FORTRAN_COMMENT_AFTER = 72
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to source browsing
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -1195,10 +1254,11 @@ CLANG_DATABASE_PATH =
|
|||
|
||||
ALPHABETICAL_INDEX = YES
|
||||
|
||||
# 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 a prefix (or a list of prefixes) that should be ignored
|
||||
# while generating the index headers.
|
||||
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
|
||||
# that should be ignored while generating the index headers. The IGNORE_PREFIX
|
||||
# tag works for classes, function and member names. The entity will be placed in
|
||||
# the alphabetical list under the first letter of the entity name that remains
|
||||
# after removing the prefix.
|
||||
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
|
||||
|
||||
IGNORE_PREFIX =
|
||||
|
@ -1277,7 +1337,12 @@ HTML_STYLESHEET =
|
|||
# Doxygen will copy the style sheet files to the output directory.
|
||||
# Note: The order of the extra style sheet files is of importance (e.g. the last
|
||||
# style sheet in the list overrules the setting of the previous ones in the
|
||||
# list). For an example see the documentation.
|
||||
# list).
|
||||
# Note: Since the styling of scrollbars can currently not be overruled in
|
||||
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
|
||||
# one or more extra stylesheets have been specified. So if scrollbar
|
||||
# customization is desired it has to be added explicitly. For an example see the
|
||||
# documentation.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
|
@ -1292,6 +1357,19 @@ HTML_EXTRA_STYLESHEET =
|
|||
|
||||
HTML_EXTRA_FILES =
|
||||
|
||||
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
|
||||
# should be rendered with a dark or light theme.
|
||||
# Possible values are: LIGHT always generate light mode output, DARK always
|
||||
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
|
||||
# the user preference, use light mode if no preference is set (the default),
|
||||
# AUTO_DARK automatically set the mode according to the user preference, use
|
||||
# dark mode if no preference is set and TOGGLE allow to user to switch between
|
||||
# light and dark mode via a button.
|
||||
# The default value is: AUTO_LIGHT.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_COLORSTYLE = AUTO_LIGHT
|
||||
|
||||
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
|
||||
# will adjust the colors in the style sheet and background images according to
|
||||
# this color. Hue is specified as an angle on a color-wheel, see
|
||||
|
@ -1322,15 +1400,6 @@ HTML_COLORSTYLE_SAT = 100
|
|||
|
||||
HTML_COLORSTYLE_GAMMA = 80
|
||||
|
||||
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to YES can help to show when doxygen was last run and thus if the
|
||||
# documentation is up to date.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_TIMESTAMP = NO
|
||||
|
||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||
# documentation will contain a main index with vertical navigation menus that
|
||||
# are dynamically created via JavaScript. If disabled, the navigation index will
|
||||
|
@ -1350,6 +1419,13 @@ HTML_DYNAMIC_MENUS = YES
|
|||
|
||||
HTML_DYNAMIC_SECTIONS = NO
|
||||
|
||||
# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
|
||||
# dynamically folded and expanded in the generated HTML source code.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_CODE_FOLDING = YES
|
||||
|
||||
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
|
||||
# shown in the various tree structured indices initially; the user can expand
|
||||
# and collapse entries dynamically later on. Doxygen will expand the tree to
|
||||
|
@ -1480,6 +1556,16 @@ BINARY_TOC = NO
|
|||
|
||||
TOC_EXPAND = NO
|
||||
|
||||
# The SITEMAP_URL tag is used to specify the full URL of the place where the
|
||||
# generated documentation will be placed on the server by the user during the
|
||||
# deployment of the documentation. The generated sitemap is called sitemap.xml
|
||||
# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
|
||||
# is specified no sitemap is generated. For information about the sitemap
|
||||
# protocol see https://www.sitemaps.org
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
SITEMAP_URL =
|
||||
|
||||
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
|
||||
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
|
||||
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
|
||||
|
@ -1655,17 +1741,6 @@ HTML_FORMULA_FORMAT = png
|
|||
|
||||
FORMULA_FONTSIZE = 10
|
||||
|
||||
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
|
||||
# generated for formulas are transparent PNGs. Transparent PNGs are not
|
||||
# supported properly for IE 6.0, but are supported on all modern browsers.
|
||||
#
|
||||
# Note that when changing this option you need to delete any form_*.png files in
|
||||
# the HTML output directory before the changes have effect.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
FORMULA_TRANSPARENT = YES
|
||||
|
||||
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
|
||||
# to create new LaTeX commands to be used in formulas as building blocks. See
|
||||
# the section "Including formulas" for details.
|
||||
|
@ -1979,9 +2054,16 @@ PDF_HYPERLINKS = YES
|
|||
|
||||
USE_PDFLATEX = YES
|
||||
|
||||
# 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.
|
||||
# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.
|
||||
# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
|
||||
# mode nothing is printed on the terminal, errors are scrolled as if <return> is
|
||||
# hit at every error; missing files that TeX tries to input or request from
|
||||
# keyboard input (\read on a not open input stream) cause the job to abort,
|
||||
# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
|
||||
# but there is no possibility of user interaction just like in batch mode,
|
||||
# SCROLL In scroll mode, TeX will stop only for missing files to input or if
|
||||
# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
|
||||
# each error, asking for user intervention.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
|
@ -2002,14 +2084,6 @@ LATEX_HIDE_INDICES = NO
|
|||
|
||||
LATEX_BIB_STYLE = plain
|
||||
|
||||
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to NO can help when comparing the output of multiple runs.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_TIMESTAMP = NO
|
||||
|
||||
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# path from which the emoji images will be read. If a relative path is entered,
|
||||
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
||||
|
@ -2175,13 +2249,39 @@ DOCBOOK_OUTPUT = docbook
|
|||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
|
||||
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
|
||||
# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
|
||||
# the structure of the code including all documentation. Note that this feature
|
||||
# is still experimental and incomplete at the moment.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to Sqlite3 output
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
|
||||
# database with symbols found by doxygen stored in tables.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_SQLITE3 = NO
|
||||
|
||||
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
|
||||
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
|
||||
# in front of it.
|
||||
# The default directory is: sqlite3.
|
||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||
|
||||
SQLITE3_OUTPUT = sqlite3
|
||||
|
||||
# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db
|
||||
# database file will be recreated with each doxygen run. If set to NO, doxygen
|
||||
# will warn if an a database file is already found and not modify it.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||
|
||||
SQLITE3_RECREATE_DB = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the Perl module output
|
||||
#---------------------------------------------------------------------------
|
||||
|
@ -2324,15 +2424,15 @@ TAGFILES =
|
|||
|
||||
GENERATE_TAGFILE =
|
||||
|
||||
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
|
||||
# the class index. If set to NO, only the inherited external classes will be
|
||||
# listed.
|
||||
# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
|
||||
# will be listed in the class and namespace index. If set to NO, only the
|
||||
# inherited external classes will be listed.
|
||||
# The default value is: NO.
|
||||
|
||||
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
|
||||
# in the topic index. If set to NO, only the current project's groups will be
|
||||
# listed.
|
||||
# The default value is: YES.
|
||||
|
||||
|
@ -2346,16 +2446,9 @@ EXTERNAL_GROUPS = YES
|
|||
EXTERNAL_PAGES = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
# Configuration options related to diagram generator tools
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
|
||||
DIA_PATH =
|
||||
|
||||
# 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.
|
||||
# The default value is: YES.
|
||||
|
@ -2364,7 +2457,7 @@ 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 (see:
|
||||
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# https://www.graphviz.org/), 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 value is: YES.
|
||||
|
@ -2381,37 +2474,51 @@ HAVE_DOT = NO
|
|||
|
||||
DOT_NUM_THREADS = 0
|
||||
|
||||
# When you want a differently looking font in the dot files that doxygen
|
||||
# generates you can specify the font name using DOT_FONTNAME. You need to make
|
||||
# sure dot is able to find the font, which can be done by putting it in a
|
||||
# standard location or by setting the DOTFONTPATH environment variable or by
|
||||
# setting DOT_FONTPATH to the directory containing the font.
|
||||
# The default value is: Helvetica.
|
||||
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
|
||||
# subgraphs. When you want a differently looking font in the dot files that
|
||||
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
|
||||
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
|
||||
# Edge and Graph Attributes specification</a> You need to make sure dot is able
|
||||
# to find the font, which can be done by putting it in a standard location or by
|
||||
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
|
||||
# directory containing the font. Default graphviz fontsize is 14.
|
||||
# The default value is: fontname=Helvetica,fontsize=10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTNAME = Helvetica
|
||||
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
|
||||
|
||||
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
|
||||
# dot graphs.
|
||||
# Minimum value: 4, maximum value: 24, default value: 10.
|
||||
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
|
||||
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
|
||||
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
|
||||
# arrows shapes.</a>
|
||||
# The default value is: labelfontname=Helvetica,labelfontsize=10.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTSIZE = 10
|
||||
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
|
||||
|
||||
# By default doxygen will tell dot to use the default font as specified with
|
||||
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
|
||||
# the path where dot can find it using this tag.
|
||||
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
|
||||
# around nodes set 'shape=plain' or 'shape=plaintext' <a
|
||||
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
|
||||
# The default value is: shape=box,height=0.2,width=0.4.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
|
||||
|
||||
# You can set the path where dot can find font specified with fontname in
|
||||
# DOT_COMMON_ATTR and others dot attributes.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_FONTPATH =
|
||||
|
||||
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
|
||||
# graph for each documented class showing the direct and indirect inheritance
|
||||
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
|
||||
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
|
||||
# to TEXT the direct and indirect inheritance relations will be shown as texts /
|
||||
# links.
|
||||
# Possible values are: NO, YES, TEXT and GRAPH.
|
||||
# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
|
||||
# generate a graph for each documented class showing the direct and indirect
|
||||
# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
|
||||
# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
|
||||
# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
|
||||
# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
|
||||
# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
|
||||
# relations will be shown as texts / links.
|
||||
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
|
||||
# The default value is: YES.
|
||||
|
||||
CLASS_GRAPH = TEXT
|
||||
|
@ -2419,15 +2526,21 @@ CLASS_GRAPH = TEXT
|
|||
# If the COLLABORATION_GRAPH tag is 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.
|
||||
# class with other documented classes. Explicit enabling a collaboration graph,
|
||||
# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
|
||||
# command \collaborationgraph. Disabling a collaboration graph can be
|
||||
# accomplished by means of the command \hidecollaborationgraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
COLLABORATION_GRAPH = YES
|
||||
|
||||
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
|
||||
# groups, showing the direct groups dependencies. See also the chapter Grouping
|
||||
# in the manual.
|
||||
# groups, showing the direct groups dependencies. Explicit enabling a group
|
||||
# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
|
||||
# of the command \groupgraph. Disabling a directory graph can be accomplished by
|
||||
# means of the command \hidegroupgraph. See also the chapter Grouping in the
|
||||
# manual.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
|
@ -2487,7 +2600,9 @@ TEMPLATE_RELATIONS = NO
|
|||
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES 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.
|
||||
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
|
||||
# can be accomplished by means of the command \includegraph. Disabling an
|
||||
# include graph can be accomplished by means of the command \hideincludegraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
|
@ -2496,7 +2611,10 @@ INCLUDE_GRAPH = YES
|
|||
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES 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.
|
||||
# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
|
||||
# to NO, can be accomplished by means of the command \includedbygraph. Disabling
|
||||
# an included by graph can be accomplished by means of the command
|
||||
# \hideincludedbygraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
|
@ -2536,7 +2654,10 @@ GRAPHICAL_HIERARCHY = YES
|
|||
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
|
||||
# dependencies a directory has on other directories in a graphical way. The
|
||||
# dependency relations are determined by the #include relations between the
|
||||
# files in the directories.
|
||||
# files in the directories. Explicit enabling a directory graph, when
|
||||
# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
|
||||
# \directorygraph. Disabling a directory graph can be accomplished by means of
|
||||
# the command \hidedirectorygraph.
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
|
@ -2552,7 +2673,7 @@ DIR_GRAPH_MAX_DEPTH = 1
|
|||
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
|
||||
# generated by dot. For an explanation of the image formats see the section
|
||||
# output formats in the documentation of the dot tool (Graphviz (see:
|
||||
# http://www.graphviz.org/)).
|
||||
# https://www.graphviz.org/)).
|
||||
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
||||
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
||||
# requirement).
|
||||
|
@ -2590,11 +2711,12 @@ DOT_PATH =
|
|||
|
||||
DOTFILE_DIRS =
|
||||
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
|
||||
MSCFILE_DIRS =
|
||||
DIA_PATH =
|
||||
|
||||
# The DIAFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain dia files that are included in the documentation (see the \diafile
|
||||
|
@ -2644,18 +2766,6 @@ DOT_GRAPH_MAX_NODES = 50
|
|||
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
|
||||
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
|
||||
# background. This is disabled by default, because dot on Windows does not seem
|
||||
# to support this out of the box.
|
||||
#
|
||||
# Warning: Depending on the platform used, enabling this option may lead to
|
||||
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
|
||||
# read).
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
DOT_TRANSPARENT = NO
|
||||
|
||||
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
|
||||
# files in one run (i.e. multiple -o and -T options on the command line). This
|
||||
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
|
||||
|
@ -2683,3 +2793,19 @@ GENERATE_LEGEND = YES
|
|||
# The default value is: YES.
|
||||
|
||||
DOT_CLEANUP = YES
|
||||
|
||||
# You can define message sequence charts within doxygen comments using the \msc
|
||||
# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
|
||||
# use a built-in version of mscgen tool to produce the charts. Alternatively,
|
||||
# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
|
||||
# specifying prog as the value, doxygen will call the tool as prog -T
|
||||
# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
|
||||
# output file formats "png", "eps", "svg", and "ismap".
|
||||
|
||||
MSCGEN_TOOL =
|
||||
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
|
||||
MSCFILE_DIRS =
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
:description: A size-optimized, customizable and highly extensible MCU-class 32-bit RISC-V soft-core CPU and microcontroller-like SoC written in platform-independent VHDL.
|
||||
:revnumber: v1.10.9
|
||||
:icons: font
|
||||
:source-highlighter: highlight.js
|
||||
:imagesdir: ../figures
|
||||
:toc: macro
|
||||
:doctype: book
|
||||
|
|
|
@ -33,7 +33,7 @@ test framework is available in a separate repository at GitHub: https://github.c
|
|||
.Unsupported ISA Extensions
|
||||
[TIP]
|
||||
Executing instructions or accessing CSRs from yet unsupported ISA extensions will raise an illegal
|
||||
instruction exception (see section <<_full_virtualization>>).
|
||||
instruction exception (see section <<_full_virtualization>>).
|
||||
|
||||
**Incompatibility Issues and Limitations**
|
||||
|
||||
|
@ -123,6 +123,7 @@ The generic type "suv(x:y)" represents a `std_ulogic_vector(x downto y)`.
|
|||
| `BOOT_ADDR` | suv(31:0) | CPU reset address. See section <<_address_space>>.
|
||||
| `DEBUG_PARK_ADDR` | suv(31:0) | "Park loop" entry address for the <<_on_chip_debugger_ocd>>, has to be 4-byte aligned.
|
||||
| `DEBUG_EXC_ADDR` | suv(31:0) | "Exception" entry address for the <<_on_chip_debugger_ocd>>, has to be 4-byte aligned.
|
||||
| `ICC_EN` | boolean | Implement <<_inter_core_communication_icc>> module. Automatically enabled for the SMP <<_dual_core_configuration>>.
|
||||
| `RISCV_ISA_Sdext` | boolean | Implement RISC-V-compatible "debug" CPU operation mode required for the <<_on_chip_debugger_ocd>>.
|
||||
| `RISCV_ISA_Sdtrig` | boolean | Implement RISC-V-compatible trigger module. See section <<_on_chip_debugger_ocd>>.
|
||||
| `RISCV_ISA_Smpmp` | boolean | Implement RISC-V-compatible physical memory protection (PMP). See section <<_smpmp_isa_extension>>.
|
||||
|
@ -502,7 +503,7 @@ operation:
|
|||
.Cache Coherency
|
||||
[IMPORTANT]
|
||||
Atomic operations **always bypass** the CPU caches using direct/uncached accesses. Care must be taken
|
||||
to maintain data cache coherency (e.g. by using the `fence` instruction).
|
||||
to maintain data <<_cache_coherency>>.
|
||||
|
||||
|
||||
<<<
|
||||
|
@ -640,10 +641,11 @@ The `I` ISA extensions is the base RISC-V integer ISA that is always enabled.
|
|||
|
||||
.`fence` Instruction
|
||||
[NOTE]
|
||||
The `fence` instruction word's _predecessor_ and _successor_ bits (used for memory ordering) are not evaluated
|
||||
by the hardware at all. For the NEORV32 the `fence` instruction behaves exactly like the `fence.i` instruction
|
||||
(see <<_zifencei_isa_extension>>). However, software should still use distinct `fence` and `fence.i` to provide
|
||||
platform-compatibility and to indicate the actual intention of the according fence instruction(s).
|
||||
Analogous to the `fence.i` instruction (<<_zifencei_isa_extension>>) the `fence` instruction triggers
|
||||
a data cache synchronization operation. See section <<_cache_coherency>> for more information.
|
||||
Furthermore, the `fence` instruction word's _predecessor_ and _successor_ bits (used for memory ordering)
|
||||
are not evaluated by the hardware at all.
|
||||
|
||||
|
||||
.`wfi` Instruction
|
||||
[NOTE]
|
||||
|
@ -716,11 +718,8 @@ The instruction word's `aq` and `lr` memory ordering bits are not evaluated by t
|
|||
|
||||
The `Zifencei` CPU extension allows manual synchronization of the instruction stream. This extension is always enabled.
|
||||
|
||||
.NEORV32 Fence Instructions
|
||||
[NOTE]
|
||||
The NEORV32 treats both fence instructions (`fence` = data fence, `fence.i` = instruction fence) in exactly the same way.
|
||||
Both instructions cause a flush of the CPU's instruction prefetch buffer and also send a fence request via the system
|
||||
bus (see <<_bus_interface>>). This system bus fence operation will, for example, clear/flush all downstream caches.
|
||||
Analogous to the `fence` instruction the `fence.i` instruction triggers an instruction cache synchronization operation.
|
||||
See section <<_cache_coherency>> for more information.
|
||||
|
||||
.Instructions and Timing
|
||||
[cols="<2,<4,<3"]
|
||||
|
|
|
@ -77,9 +77,8 @@ to check if the targeted bits can actually be modified.
|
|||
| 0xf14 | <<_mhartid>> | `CSR_MHARTID` | MRO | Machine hardware thread ID
|
||||
| 0xf15 | <<_mconfigptr>> | `CSR_MCONFIGPTR` | MRO | Machine configuration pointer register
|
||||
5+^| **<<_neorv32_specific_csrs>>**
|
||||
| 0xbc0 | <<_mxiccrxd>> | `CSR_MXICCRXD` | MRW | ICC RX data
|
||||
| 0xbc1 | <<_mxicctxd>> | `CSR_MXICCTXD` | MRW | ICC TX data
|
||||
| 0xbc2 .. 0xbc3 | <<_mxiccsr, `mxiccsr0`>> .. <<_mxiccsr, `mxiccsr0`>> | `CSR_MXICCSR0` .. `CSR_MXICCSR3` | MRW | ICC control and status
|
||||
| 0xbc0 | <<_mxiccsreg>> | `CSR_MXICCSREG` | MRW | Inter-core communication status register
|
||||
| 0xbc1 | <<_mxiccdata>> | `CSR_MXICCDATA` | MRW | Inter-core communication data register
|
||||
| 0x800 .. 0x803 | <<_cfureg, `cfureg0`>> .. <<_cfureg, `cfureg3`>> | `CSR_CFUCREG0` .. `CSR_CFUCREG3` | URW | Custom CFU registers 0 to 3
|
||||
| 0xfc0 | <<_mxisa>> | `CSR_MXISA` | MRO | Extended machine CPU ISA and extensions
|
||||
|=======================
|
||||
|
@ -94,7 +93,7 @@ to check if the targeted bits can actually be modified.
|
|||
===== **`fflags`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Floating-point accrued exceptions
|
||||
| Address | `0x001`
|
||||
|
@ -120,7 +119,7 @@ to check if the targeted bits can actually be modified.
|
|||
===== **`frm`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Floating-point dynamic rounding mode
|
||||
| Address | `0x002`
|
||||
|
@ -143,7 +142,7 @@ to check if the targeted bits can actually be modified.
|
|||
===== **`fcsr`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Floating-point control and status register
|
||||
| Address | `0x003`
|
||||
|
@ -171,7 +170,7 @@ to check if the targeted bits can actually be modified.
|
|||
===== **`mstatus`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine status register - low word
|
||||
| Address | `0x300`
|
||||
|
@ -203,7 +202,7 @@ bit for the higher-privilege mode." - RISC-V ISA Spec.
|
|||
===== **`misa`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | ISA and extensions
|
||||
| Address | `0x301`
|
||||
|
@ -242,7 +241,7 @@ Machine-mode software can discover available `Z*` _sub-extensions_ (like `Zicsr`
|
|||
===== **`mie`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine interrupt-enable register
|
||||
| Address | `0x304`
|
||||
|
@ -268,7 +267,7 @@ Machine-mode software can discover available `Z*` _sub-extensions_ (like `Zicsr`
|
|||
===== **`mtvec`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine trap-handler base address
|
||||
| Address | `0x305`
|
||||
|
@ -298,7 +297,7 @@ As software does not need to determine the interrupt cause the reduction in late
|
|||
===== **`mcounteren`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine counter enable
|
||||
| Address | `0x306`
|
||||
|
@ -324,7 +323,7 @@ As software does not need to determine the interrupt cause the reduction in late
|
|||
===== **`mstatush`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine status register - high word
|
||||
| Address | `0x310`
|
||||
|
@ -343,7 +342,7 @@ As software does not need to determine the interrupt cause the reduction in late
|
|||
===== **`mscratch`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Scratch register for machine trap handlers
|
||||
| Address | `0x340`
|
||||
|
@ -358,11 +357,11 @@ As software does not need to determine the interrupt cause the reduction in late
|
|||
===== **`mepc`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine exception program counter
|
||||
| Address | `0x341`
|
||||
| Reset value | `BOOT_ADDR` (CPU boot address, see <<_cpu_top_entity_generics>>)
|
||||
| Reset value | `0x00000000`
|
||||
| ISA | `Zicsr`
|
||||
| Description | The `mepc` CSR provides the instruction address where execution has stopped/failed when
|
||||
an interrupt is triggered / an exception is raised. See section <<_traps_exceptions_and_interrupts>> for a list of all legal values.
|
||||
|
@ -378,7 +377,7 @@ The `mret` instruction will return to the address stored in `mepc` by automatica
|
|||
===== **`mcause`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine trap cause
|
||||
| Address | `0x342`
|
||||
|
@ -403,7 +402,7 @@ The `mret` instruction will return to the address stored in `mepc` by automatica
|
|||
===== **`mtval`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine trap value
|
||||
| Address | `0x343`
|
||||
|
@ -423,7 +422,7 @@ However, any write-access will be ignored and will not cause an exception to mai
|
|||
===== **`mip`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine interrupt pending
|
||||
| Address | `0x344`
|
||||
|
@ -454,7 +453,7 @@ interrupt-triggering processor module.
|
|||
===== **`mtinst`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine trap instruction
|
||||
| Address | `0x34a`
|
||||
|
@ -487,7 +486,7 @@ while all remaining bits represent the pre-decoded 32-bit instruction equivalent
|
|||
===== **`menvcfg`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine environment configuration register - low word
|
||||
| Address | `0x30a`
|
||||
|
@ -502,7 +501,7 @@ while all remaining bits represent the pre-decoded 32-bit instruction equivalent
|
|||
===== **`menvcfgh`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine environment configuration register - high word
|
||||
| Address | `0x31a`
|
||||
|
@ -528,7 +527,7 @@ See section <<_smpmp_isa_extension>> for more information.
|
|||
===== **`pmpcfg`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Physical memory protection region configuration registers
|
||||
| Address | `0x3a0` (`pmpcfg0`)
|
||||
|
@ -563,7 +562,7 @@ implementation of the according modes.
|
|||
===== **`pmpaddr`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Physical memory protection region address registers
|
||||
| Address | `0x3b0` (`pmpaddr1`)
|
||||
|
@ -618,7 +617,7 @@ if this instruction is actually going to retire or if it causes an exception.
|
|||
===== **`cycle[h]`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Cycle counter
|
||||
| Address | `0xc00` (`cycle`)
|
||||
|
@ -635,7 +634,7 @@ counter are read-only. Any write access will raise an illegal instruction except
|
|||
===== **`instret[h]`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Instructions-retired counter
|
||||
| Address | `0xc02` (`instret`)
|
||||
|
@ -652,7 +651,7 @@ counter are read-only. Any write access will raise an illegal instruction except
|
|||
===== **`mcycle[h]`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine cycle counter
|
||||
| Address | `0xb00` (`mcycle`)
|
||||
|
@ -669,7 +668,7 @@ cycle (CPU not in sleep mode). These registers are read/write only for machine-m
|
|||
===== **`minstret[h]`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine instructions-retired counter
|
||||
| Address | `0xb02` (`minstret`)
|
||||
|
@ -710,7 +709,7 @@ If `HPM_NUM_CNTS` is less than 64, all remaining MSB-aligned bits are hardwired
|
|||
===== **`mhpmevent`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine hardware performance monitor event select
|
||||
| Address | `0x233` (`mhpmevent3`)
|
||||
|
@ -767,7 +766,7 @@ cause an interrupt, trigger a privilege mode change or were not meant to retire
|
|||
===== **`mhpmcounter[h]`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine hardware performance monitor (HPM) counter
|
||||
| Address | `0xb03`, `0xb83` (`mhpmcounter3`, `mhpmcounter3h`)
|
||||
|
@ -801,7 +800,7 @@ and are not accessible for lower-privileged software.
|
|||
===== **`mcountinhibit`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine counter-inhibit register
|
||||
| Address | `0x320`
|
||||
|
@ -831,7 +830,7 @@ and are not accessible for lower-privileged software.
|
|||
===== **`mvendorid`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine vendor ID
|
||||
| Address | `0xf11`
|
||||
|
@ -846,7 +845,7 @@ and are not accessible for lower-privileged software.
|
|||
===== **`marchid`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine architecture ID
|
||||
| Address | `0xf12`
|
||||
|
@ -862,7 +861,7 @@ and are not accessible for lower-privileged software.
|
|||
===== **`mimpid`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine implementation ID
|
||||
| Address | `0xf13`
|
||||
|
@ -878,7 +877,7 @@ NEORV32 as BCD-coded number (example: `mimpid = 0x01020312` → 01.02.03.12 →
|
|||
===== **`mhartid`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine hardware thread ID
|
||||
| Address | `0xf14`
|
||||
|
@ -894,7 +893,7 @@ core's hart ID is unique starting at 0 for the first core.
|
|||
===== **`mconfigptr`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine configuration pointer register
|
||||
| Address | `0xf15`
|
||||
|
@ -919,7 +918,7 @@ custom/implementation-specific use (assured by the RISC-V privileged specificati
|
|||
===== **`cfureg`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Custom (user-defined) CFU CSRs
|
||||
| Address | `0x800` (`cfureg0`)
|
||||
|
@ -931,66 +930,49 @@ custom/implementation-specific use (assured by the RISC-V privileged specificati
|
|||
| Description | User-defined CSRs to be used within the <<_custom_functions_unit_cfu>>.
|
||||
|=======================
|
||||
|
||||
|
||||
{empty} +
|
||||
[discrete]
|
||||
===== **`mxiccrxd`**
|
||||
===== **`mxiccsreg`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | <<_inter_core_communication_icc>> RX data
|
||||
| Name | <<_inter_core_communication_icc>> status register
|
||||
| Address | `0xbc0`
|
||||
| Reset value | `0x00000000`
|
||||
| ISA | `Zicsr` & `X`
|
||||
| Description | RX data from selected link. Buffered by a 4-entries-deep and 32-bit wide FIFO.
|
||||
This CSR is hardwired to all-zero if there is just a single CPU core in the system.
|
||||
|=======================
|
||||
|
||||
|
||||
{empty} +
|
||||
[discrete]
|
||||
===== **`mxicctxd`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
|=======================
|
||||
| Name | <<_inter_core_communication_icc>> TX data
|
||||
| Address | `0xbc1`
|
||||
| Reset value | `0x00000000`
|
||||
| ISA | `Zicsr` & `X`
|
||||
| Description | TX data for selected link. Buffered by a 4-entries-deep and 32-bit wide FIFO.
|
||||
This CSR is hardwired to all-zero if there is just a single CPU core in the system.
|
||||
|=======================
|
||||
|
||||
{empty} +
|
||||
[discrete]
|
||||
===== **`mxiccsr`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
|=======================
|
||||
| Name | <<_inter_core_communication_icc>> control and status
|
||||
| Address | `0xbc2` (`mxiccsr0`)
|
||||
| | `0xbc3` (`mxiccsr1`)
|
||||
| Reset value | `0x40000000`
|
||||
| ISA | `Zicsr` & `X`
|
||||
| Description | Link selection and status. Note that `mxiccsr1` is just a mirrored copy of `mxiccsr0`.
|
||||
This CSR is hardwired to all-zero if there is just a single CPU core in the system.
|
||||
| Description | Shows the status of the core's inter-core communication link (message queue / FIFO status flags).
|
||||
The entire CSR is read-only. However, write accesses are ignored.
|
||||
This CSR is hardwired to all-zero if the <<_dual_core_configuration>> is disabled.
|
||||
|=======================
|
||||
|
||||
.`mxiccsr` CSR Bits
|
||||
.`mxiccsreg` CSR Bits
|
||||
[cols="^1,^2,^1,<5"]
|
||||
[options="header",grid="rows"]
|
||||
|=======================
|
||||
| Bit | Name [C] | R/W | Description
|
||||
| 1:0 | `CSR_MXICCSR_LINK_MSB : CSR_MXICCSR_LINK_LSB` | r/w | Link select. The value in this memory corresponds
|
||||
to the ID of the core to which a connection is to be established via a link. The ICC data registers <<_mxiccrxd>>
|
||||
and <<_mxicctxd>> will only access the queue FIFOs of the selected link. Note that only bit 0 is writable. Bit 1
|
||||
is hardwaired to zero.
|
||||
| 29:2 | - | r/- | Reserved; hardwired to zero.
|
||||
| 30 | `CSR_MXICCSR_TX_FREE` | r/- | Set if there is free space for TX data for the selected link.
|
||||
| 31 | `CSR_MXICCSR_RX_AVAIL` | r/- | Set if RX data from the selected link is available.
|
||||
| 0 | `CSR_MXICCSREG_RX_AVAIL` | r/- | Set if RX data from the other core is available.
|
||||
| 1 | `CSR_MXICCSREG_TX_FREE` | r/- | Set if there is free space for TX data for the other core.
|
||||
| 31:2 | - | r/- | Reserved; hardwired to zero.
|
||||
|=======================
|
||||
|
||||
|
||||
{empty} +
|
||||
[discrete]
|
||||
===== **`mxiccdata`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | <<_inter_core_communication_icc>> data register
|
||||
| Address | `0xbc1`
|
||||
| Reset value | `0x00000000`
|
||||
| ISA | `Zicsr` & `X`
|
||||
| Description | This CSR provides access to the inter-core communication message queues that are implemented
|
||||
as simple FIFOs. Writing to this register will put data into the message queue so it can be read by the other
|
||||
core. Reading from this register will return data received from the other core (i.e. this CSR has side effects
|
||||
when reading). A read access will return all-zero of no RX data is available from the other core.
|
||||
This CSR is hardwired to all-zero if the <<_dual_core_configuration>> is disabled.
|
||||
|=======================
|
||||
|
||||
|
||||
|
@ -999,7 +981,7 @@ is hardwaired to zero.
|
|||
===== **`mxisa`**
|
||||
|
||||
[cols="<1,<8"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Name | Machine extended ISA and extensions register
|
||||
| Address | `0xfc0`
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
:sectnums:
|
||||
=== Dual-Core Configuration
|
||||
|
||||
.Dual-Core Example
|
||||
.Dual-Core Example Programs
|
||||
[TIP]
|
||||
A simple dual-core example program can be found in `sw/example/demo_dual_core`.
|
||||
A set of rather simple dual-core example programs can be found in `sw/example/demo_dual_core*`.
|
||||
|
||||
Optionally, the CPU core can be implemented as **symmetric multiprocessing (SMP) dual-core** system.
|
||||
This dual-core configuration is enabled by the `DUAL_CORE_EN` <<_processor_top_entity_generics, top generic>>.
|
||||
|
@ -24,29 +24,49 @@ The following table summarizes the most important aspects when using the dual-co
|
|||
[cols="<2,<10"]
|
||||
[grid="rows"]
|
||||
|=======================
|
||||
| **CPU configuration** | Both cores use the same cache, CPU and ISA configuration provided by the according top generics.
|
||||
| **Debugging** | A special SMP openOCD script (`sw/openocd/openocd_neorv32.dual_core.cfg`) is required to
|
||||
debug both cores at one. SMP-debugging is fully supported by RISC-V gdb port.
|
||||
debug both cores at once. SMP-debugging is fully supported by the RISC-V gdb port.
|
||||
| **Clock and reset** | Both cores use the same global processor clock and reset. If <<_cpu_clock_gating>>
|
||||
is enabled the clock of each core can be individually halted by putting it into <<_sleep_mode>>.
|
||||
| **Address space** | Both cores have access to the same <<_address_space>>.
|
||||
is enabled, the clock of each core can be individually halted by putting the core into <<_sleep_mode>>.
|
||||
| **Address space** | Both cores have full access to the same physical <<_address_space>>.
|
||||
| **Interrupts** | All <<_processor_interrupts>> are routed to both cores. Hence, each core has access to
|
||||
all <<_neorv32_specific_fast_interrupt_requests>> (FIRQs). Additionally, the RISC-V machine-level _external
|
||||
interrupt_ (via the top `mext_irq_i` port) is also send to both cores. In contrast, the RISC-V machine level
|
||||
_software_ and _timer_ interrupts are exclusive for each core (provided by the <<_core_local_interruptor_clint>>).
|
||||
| **RTE** | The <<_neorv32_runtime_environment>> also supports the dual-core configuration. However, it needs
|
||||
to be explicitly initialized on each core individually. The RTE trap handling provides a individual handler
|
||||
tables for each core.
|
||||
_software_ and _timer_ interrupts are core-exclusive (provided by the <<_core_local_interruptor_clint>>).
|
||||
| **RTE** | The <<_neorv32_runtime_environment>> can be used for both cores. However, the RTE needs to be
|
||||
explicitly initialized on each core (executing `neorv32_rte_setup()`). Note that the installed trap handlers
|
||||
apply to both cores. The installed user-defined trap handlers can determine the core's ID to perform
|
||||
core-specific trap handling.
|
||||
| **Memory** | Each core has its own stack. The top of stack of core 0 is defined by the <<_linker_script>>
|
||||
while the top of stack of core 1 has to be explicitly defined by core 0 (see <<_dual_core_boot>>). Both
|
||||
cores share the same heap, `.data` and `.bss` sections.
|
||||
| **Constructors and destructors** | Constructors and destructors are executed on core 0 only.
|
||||
(see )
|
||||
| **Core communication** | See section <<_inter_core_communication_icc>>.
|
||||
cores share the same heap, `.data` and `.bss` sections. Hence, only core 0 setups the `.data` and `.bss`
|
||||
sections at boot-up.
|
||||
| **Constructors and destructors** | Constructors and destructors are executed by core 0 only
|
||||
(see section <<_c_standard_library>>).
|
||||
| **Cache coherency** | Be aware that there is no cache snooping available. If any level-1 cache is enabled
|
||||
(<<_processor_internal_instruction_cache_icache>> and/or <<_processor_internal_data_cache_dcache>>) care
|
||||
must be taken to prevent access to outdated data - either by using cache synchronization (`fence` / `fence.i`
|
||||
instructions) or by using <<_atomic_memory_access>>.
|
||||
| **Inter-core communication** | See section <<_inter_core_communication_icc>>.
|
||||
| **Bootloader** | Only core 0 will boot and execute the bootloader while core 1 is held in standby.
|
||||
| **Booting** | See section <<_dual_core_boot>>.
|
||||
|=======================
|
||||
|
||||
|
||||
==== SMP Software Library
|
||||
|
||||
An SMP library provides basic functions for launching the secondary core and for performing direct
|
||||
core-to-core communication:
|
||||
|
||||
[cols="<1,<8"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| neorv32_smp.c | link:https://stnolting.github.io/neorv32/sw/neorv32__smp_8c.html[Online software reference (Doxygen)]
|
||||
| neorv32_smp.h | link:https://stnolting.github.io/neorv32/sw/neorv32__smp_8h.html[Online software reference (Doxygen)]
|
||||
|=======================
|
||||
|
||||
|
||||
==== Inter-Core Communication (ICC)
|
||||
|
||||
Both cores can communicate with each other via a direct point-to-point connection based on FIFO-like message
|
||||
|
@ -55,32 +75,23 @@ shared-memory communication. Additionally, communication using these links is gu
|
|||
|
||||
The inter-core communication (ICC) module is implemented as dedicated hardware module within each CPU core
|
||||
(VHDL file `rtl/core/neorv32_cpu_icc.vhd`). This module is automatically included if the dual-core option
|
||||
is enabled. Each core provides a 32-bit wide and 4 entries deep FIFO for sending data to the other core.
|
||||
is enabled. Each core provides a **32-bit wide** and **4 entries deep** FIFO for sending data to the other core.
|
||||
Hence, there are two FIFOs: one for sending data from core 0 to core 1 and another one for sending data the
|
||||
opposite way.
|
||||
|
||||
The ICC communication links are accessed via NEORV32-specific CSRs. Hence, those FIFOs are accessible only
|
||||
by the CPU core itself and cannot be accessed by the DMA or any other CPU core. In total, three CSRs are
|
||||
provided to handle communications:
|
||||
The ICC communication links are accessed via two NEORV32-specific CSRs. Hence, those FIFOs are accessible only
|
||||
by the CPU core itself and cannot be accessed by the DMA or any other CPU core.
|
||||
|
||||
The <<_mxiccsr>> is used to select the core with which to communicate. In the dual-core configuration core 1
|
||||
can only select core 0 and vice versa. The core selection in this register allows access to the according
|
||||
message FIFOs via the two other CSRs. Additionally, the CSR provides status flags (TX FIFO data available;
|
||||
RX FIFO free space) related to the selected communication link.
|
||||
|
||||
The <<_mxiccrxd>> and <<_mxicctxd>> CSRs are used for the actual data read and write operations. Writing data
|
||||
to <<<<_mxicctxd>>> will send to the message queue of the core selected by <<_mxiccsr>>. Conversely, reading
|
||||
data from <<_mxiccrxd>> will return data received from the core selected by <<_mxiccsr>>.
|
||||
The <<_mxiccsreg>> provides read-only status information about the core's ICC links: bit 0 becomes set if
|
||||
there is RX data available for _this_ core (send from the the other core). Bit 1 is set as long there is
|
||||
free space in _this_ core's TX data FIFO. The <<_mxiccdata>> CSR is used for actual data send/receive operations.
|
||||
Writing this register will put the according data word into the TX link FIFO of _this_ core. Reading this CSR
|
||||
will return a data word from the RX FIFO of _this_ core.
|
||||
|
||||
The ICC FIFOs do not provide any interrupt capabilities. Software is expected to use the machine-software
|
||||
interrupt of the receiving core (provided by the <<_core_local_interruptor_clint>>) to inform it about
|
||||
available messages.
|
||||
|
||||
.ICC Software API
|
||||
[TIP]
|
||||
The NEORV32 software framework provides API wrappers to abstract inter-core communication:
|
||||
`sw/lib/include/noevr32_smp.h`
|
||||
|
||||
|
||||
==== Dual-Core Boot
|
||||
|
||||
|
@ -94,11 +105,20 @@ To boot-up core 1, the primary core has to use a special library function provid
|
|||
.CPU Core 1 launch function prototype (note that this function can only be executed on core 0)
|
||||
[source,c]
|
||||
----
|
||||
int neorv32_smp_launch(int hart_id, void (*entry_point)(void), uint8_t* stack_memory, size_t stack_size_bytes);
|
||||
int neorv32_smp_launch(int (*entry_point)(void), uint8_t* stack_memory, size_t stack_size_bytes);
|
||||
----
|
||||
|
||||
When executed, core 0 use the <<_inter_core_communication_icc>> to send launch data that includes the entry point
|
||||
When executed, core 0 uses the <<_inter_core_communication_icc>> to send launch data that includes the entry point
|
||||
for core 1 (via `entry_point`) and the actual stack configuration (via `stack_memory` and `stack_size_bytes`).
|
||||
Note that the main function for core 1 has to use a specific type (return `int`, no arguments):
|
||||
|
||||
.CPU Core 1 Main Function
|
||||
[source,c]
|
||||
----
|
||||
int core1_main(void) {
|
||||
return 0; // return to crt0 and go to sleep mode
|
||||
}
|
||||
----
|
||||
|
||||
.Core 1 Stack Memory
|
||||
[NOTE]
|
||||
|
|
|
@ -190,7 +190,6 @@ rtl/core
|
|||
├-neorv32_clint.vhd - Core local interruptor
|
||||
├-neorv32_clockgate.vhd - Generic clock gating switch
|
||||
├-neorv32_cfs.vhd - Custom functions subsystem
|
||||
├-neorv32_core_complex.vhd - NEORV32 CORE COMPLEX TOP ENTITY
|
||||
├-neorv32_cpu.vhd - NEORV32 CPU TOP ENTITY
|
||||
├-neorv32_cpu_alu.vhd - Arithmetic/logic unit
|
||||
├-neorv32_cpu_control.vhd - CPU control, exception system and CSRs
|
||||
|
|
|
@ -575,6 +575,25 @@ Accesses that are delegated to the external bus interface have a different maxim
|
|||
explicit specific processor generic. See section <<_processor_external_bus_interface_xbus>> for more information.
|
||||
|
||||
|
||||
:sectnums:
|
||||
==== IO Switch
|
||||
|
||||
The IO switch further decodes the address when accessing the processor-internal IO/peripheral devices and forwards
|
||||
the access request to the according module. Note that a total address space size of 256 bytes is assigned to each
|
||||
IO module in order to simplify address decoding. The IO-specific address map is also defined in the main VHDL
|
||||
package file (`rtl/core/neorv323_package.vhd`).
|
||||
|
||||
.Exemplary Cut-Out from the IO Address Map
|
||||
[source,vhdl]
|
||||
----
|
||||
-- IO Address Map --
|
||||
constant iodev_size_c : natural := 256; -- size of a single IO device (bytes)
|
||||
constant base_io_cfs_c : std_ulogic_vector(31 downto 0) := x"ffffeb00";
|
||||
constant base_io_slink_c : std_ulogic_vector(31 downto 0) := x"ffffec00";
|
||||
constant base_io_dma_c : std_ulogic_vector(31 downto 0) := x"ffffed00";
|
||||
----
|
||||
|
||||
|
||||
:sectnums:
|
||||
==== Atomic Memory Operations Controller
|
||||
|
||||
|
@ -595,41 +614,62 @@ written to the addressed memory cell. In parallel, the data from the first buffe
|
|||
content of the addresses memory cell) is sent back to the requesting CPU.
|
||||
|=======================
|
||||
|
||||
The controller performs two bus transactions: a read operations and a write operation. Only the acknowledge/error
|
||||
handshake of the last transaction is sent back to the CPU.
|
||||
|
||||
As the AMO controller is the memory-nearest instance (see <<_bus_system>>) the previously described set of operations
|
||||
cannot be interrupted. Hence, they execute in an atomic way.
|
||||
.Direct Access
|
||||
[IMPORTANT]
|
||||
Atomic operations **always bypass** the CPU's <<_processor_internal_data_cache_dcache, data cache>>
|
||||
using direct/uncached accesses. Care must be taken to maintain data <<_cache_coherency>>.
|
||||
|
||||
.Physical Memory Attributes
|
||||
[NOTE]
|
||||
Atomic memory operations can be executed for _any_ address. This also includes
|
||||
cached memory, memory-mapped IO devices and processor-external address spaces.
|
||||
|
||||
.Cache Coherency
|
||||
[IMPORTANT]
|
||||
Atomic operations **always bypass** the CPU's <<_processor_internal_data_cache_dcache, data cache>>
|
||||
using direct/uncached accesses. Care must be taken to maintain data cache coherency when accessing
|
||||
cached memory (e.g. by using the `fence` instruction).
|
||||
The controller performs two bus transactions: a read operations and a write operation. Only the acknowledge/error
|
||||
handshake of the last transaction is sent back to the CPU.
|
||||
|
||||
As the AMO controller is the memory-nearest instance (see <<_bus_system>>) the previously described set of operations
|
||||
cannot be interrupted. Hence, they execute in an atomic way.
|
||||
|
||||
|
||||
:sectnums:
|
||||
==== IO Switch
|
||||
==== Cache Coherency
|
||||
|
||||
The IO switch further decodes the address when accessing the processor-internal IO/peripheral devices and forwards
|
||||
the access request to the according module. Note that a total address space size of 256 bytes is assigned to each
|
||||
IO module in order to simplify address decoding. The IO-specific address map is also defined in the main VHDL
|
||||
package file (`rtl/core/neorv323_package.vhd`).
|
||||
In total the NEORV32 Processor provides up to four optional caches organized in two levels. Level-1
|
||||
caches are closer to the CPU while level-2 caches are closer to main memory (however, this highly depends
|
||||
on the the actual cache configurations).
|
||||
|
||||
.Exemplary Cut-Out from the IO Address Map
|
||||
[source,vhdl]
|
||||
----
|
||||
-- IO Address Map --
|
||||
constant iodev_size_c : natural := 256; -- size of a single IO device (bytes)
|
||||
constant base_io_cfs_c : std_ulogic_vector(31 downto 0) := x"ffffeb00";
|
||||
constant base_io_slink_c : std_ulogic_vector(31 downto 0) := x"ffffec00";
|
||||
constant base_io_dma_c : std_ulogic_vector(31 downto 0) := x"ffffed00";
|
||||
----
|
||||
* The <<_processor_internal_data_cache_dcache>> (level-1)
|
||||
* The <<_processor_internal_instruction_cache_icache>> (level-1)
|
||||
* The cache of the <<_processor_external_bus_interface_xbus>> (level-2)
|
||||
* The cache of the <<_execute_in_place_module_xip>> (level-2)
|
||||
|
||||
As all caches operate transparently for the software, special attention must therefore be paid to coherence.
|
||||
Note that coherence and cache _synchronization_ is **not** performed by the hardware itself (there is no
|
||||
snooping implemented).
|
||||
|
||||
The NEORV32 uses two instructions for manual cache synchronization (both instructions are always available
|
||||
regardless of the actual CPU/ISA configuration):
|
||||
|
||||
* `fence` (<<_i_isa_extension>> / <<_e_isa_extension>>)
|
||||
* `fence.i` (<<_zifencei_isa_extension>>)
|
||||
|
||||
By executing the "data" `fence` instruction the CPU's data cache is synchronized in four steps:
|
||||
|
||||
[start=1]
|
||||
. The CPU data cache is flushed: all local modifications are copied to the next higher memory level;
|
||||
this can be the XBUS cache or main memory.
|
||||
. The CPU data cache is cleared invalidating all local entries.
|
||||
. The synchronization request is sent to the next-higher memory level (for example to the XBUS cache
|
||||
so it can perform the same synchronization steps).
|
||||
. The CPU data cache is reloaded with up-to-date data from the next higher memory level.
|
||||
|
||||
By executing the "instruction" `fence.i` instruction the CPU's instruction cache is synchronized in three steps:
|
||||
|
||||
[start=1]
|
||||
. The synchronization request is sent to the next-higher memory level (for example to the XBUS cache
|
||||
so it can perform the same synchronization steps).
|
||||
. The CPU instruction cache is cleared invalidating all local entries.
|
||||
. The CPU instruction cache is reloaded with up-to-date data from the next higher memory level.
|
||||
|
||||
|
||||
<<<
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
==== Bootloader ROM (BOOTROM)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_boot_rom.vhd | default platform-agnostic bootloader ROM
|
||||
| | neorv32_bootloader_image.vhd | initialization image (a VHDL package)
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Custom Functions Subsystem (CFS)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_cfs.vhd |
|
||||
| Software driver files: | neorv32_cfs.c |
|
||||
| | neorv32_cfs.h |
|
||||
| Software driver files: | neorv32_cfs.c | link:https://stnolting.github.io/neorv32/sw/neorv32__cfs_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_cfs.h | link:https://stnolting.github.io/neorv32/sw/neorv32__cfs_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `cfs_in_i` | custom input conduit
|
||||
| | `cfs_out_o` | custom output conduit
|
||||
| Configuration generics: | `IO_CFS_EN` | implement CFS when `true`
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Core Local Interruptor (CLINT)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_clint.vhd |
|
||||
| Software driver files: | neorv32_clint.c |
|
||||
| | neorv32_clint.h |
|
||||
| Software driver files: | neorv32_clint.c | link:https://stnolting.github.io/neorv32/sw/neorv32__clint_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_clint.h | link:https://stnolting.github.io/neorv32/sw/neorv32__clint_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `mtime_irq_i` | RISC-V machine timer IRQ if CLINT is **not** implemented
|
||||
| | `msw_irq_i` | RISC-V software IRQ if CLINT is **not** implemented
|
||||
| | `mtime_time_o` | Current system time (from CLINT's MTIMER)
|
||||
|
@ -56,6 +56,9 @@ machine software interrupt become available as processor-external signals (`mtim
|
|||
| Address | Name [C] | Bits | R/W | Function
|
||||
.2+<| `0xfff40000` .2+<| `MSWI[0]` ^| 0 ^| r/w <| trigger machine software interrupt for hart 0 when set
|
||||
^| 31:1 ^| r/- <| hardwired to zero
|
||||
.2+<| `0xfff40004` .2+<| `MSWI[1]` ^| 0 ^| r/w <| trigger machine software interrupt for hart 1 when set
|
||||
^| 31:1 ^| r/- <| hardwired to zero
|
||||
| `0xfff44000` | `MTIMECMP[0]` | 63:0 | r/w | 64-bit time compare for hart 0
|
||||
| `0xfff44008` | `MTIMECMP[1]` | 63:0 | r/w | 64-bit time compare for hart 1
|
||||
| `0xfff4bff8` | `MTIME` | 63:0 | r/w | 64-bit global machine timer
|
||||
|=======================
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Cyclic Redundancy Check (CRC)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_crc.vhd |
|
||||
| Software driver files: | neorv32_crc.c |
|
||||
| | neorv32_crc.h |
|
||||
| Software driver files: | neorv32_crc.c | link:https://stnolting.github.io/neorv32/sw/neorv32__crc_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_crc.h | link:https://stnolting.github.io/neorv32/sw/neorv32__crc_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | none |
|
||||
| Configuration generics: | `IO_CRC_EN` | implement CRC module when `true`
|
||||
| CPU interrupts: | none |
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
==== Processor-Internal Data Cache (dCACHE)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_cache.vhd | Generic cache module
|
||||
| Software driver files: | none | _implicitly used_
|
||||
|
@ -37,7 +37,8 @@ The CPU cache(s) should not be implemented when using only processor-internal da
|
|||
|
||||
.Manual Cache Flush/Clear/Reload
|
||||
[NOTE]
|
||||
By executing the `fence(.i)` instruction the cache is flushed, cleared and a reload from main memory is triggered.
|
||||
By executing the `fence` instruction the data cache is flushed, cleared and reloaded.
|
||||
See section <<_cache_coherency>> for more information.
|
||||
|
||||
.Retrieve Cache Configuration from Software
|
||||
[TIP]
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Direct Memory Access Controller (DMA)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_dma.vhd |
|
||||
| Software driver files: | neorv32_dma.c |
|
||||
| | neorv32_dma.h |
|
||||
| Software driver files: | neorv32_dma.c | link:https://stnolting.github.io/neorv32/sw/neorv32__dma_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_dma.h | link:https://stnolting.github.io/neorv32/sw/neorv32__dma_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | none |
|
||||
| Configuration generics: | `IO_DMA_EN` | implement DMA when `true`
|
||||
| CPU interrupts: | fast IRQ channel 10 | DMA transfer done (see <<_processor_interrupts>>)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
==== Data Memory (DMEM)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_dmem.vhd | default platform-agnostic data memory
|
||||
| Software driver files: | none | _implicitly used_
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== General Purpose Input and Output Port (GPIO)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_gpio.vhd |
|
||||
| Software driver files: | neorv32_gpio.c |
|
||||
| | neorv32_gpio.h |
|
||||
| Software driver files: | neorv32_gpio.c | link:https://stnolting.github.io/neorv32/sw/neorv32__gpio_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_gpio.h | link:https://stnolting.github.io/neorv32/sw/neorv32__gpio_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `gpio_o` | 64-bit parallel output port
|
||||
| | `gpio_i` | 64-bit parallel input port
|
||||
| Configuration generics: | `IO_GPIO_NUM` | number of input/output pairs to implement (0..64)
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== General Purpose Timer (GPTMR)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_gptmr.vhd |
|
||||
| Software driver files: | neorv32_gptmr.c |
|
||||
| | neorv32_gptmr.h |
|
||||
| Software driver files: | neorv32_gptmr.c | link:https://stnolting.github.io/neorv32/sw/neorv32__gptmr_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_gptmr.h | link:https://stnolting.github.io/neorv32/sw/neorv32__gptmr_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | none |
|
||||
| Configuration generics: | `IO_GPTMR_EN` | implement general purpose timer when `true`
|
||||
| CPU interrupts: | fast IRQ channel 12 | timer interrupt (see <<_processor_interrupts>>)
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
==== Processor-Internal Instruction Cache (iCACHE)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_cache.vhd | Generic cache module
|
||||
| Software driver files: | none | _implicitly used_
|
||||
|
@ -37,7 +37,8 @@ The CPU cache(s) should not be implemented when using only processor-internal da
|
|||
|
||||
.Manual Cache Clear/Reload
|
||||
[NOTE]
|
||||
By executing the `fence(.i)` instruction the cache is cleared and a reload from main memory is triggered.
|
||||
By executing the `fence.i` instruction the instruction cache is cleared and reloaded.
|
||||
See section <<_cache_coherency>> for more information.
|
||||
|
||||
.Retrieve Cache Configuration from Software
|
||||
[TIP]
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
==== Instruction Memory (IMEM)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_imem.vhd | default platform-agnostic instruction memory (RAM or ROM)
|
||||
| | neorv32_application_image.vhd | initialization image (a VHDL package)
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Smart LED Interface (NEOLED)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_neoled.vhd |
|
||||
| Software driver files: | neorv32_neoled.c |
|
||||
| | neorv32_neoled.h |
|
||||
| Software driver files: | neorv32_neoled.c | link:https://stnolting.github.io/neorv32/sw/neorv32__neoled_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_neoled.h | link:https://stnolting.github.io/neorv32/sw/neorv32__neoled_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `neoled_o` | 1-bit serial data output
|
||||
| Configuration generics: | `IO_NEOLED_EN` | implement NEOLED controller when `true`
|
||||
| | `IO_NEOLED_TX_FIFO` | TX FIFO depth, has to be a power of 2, min 1
|
||||
|
|
|
@ -3,11 +3,12 @@
|
|||
==== One-Wire Serial Interface Controller (ONEWIRE)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_onewire.vhd |
|
||||
| Software driver files: | neorv32_onewire.c |
|
||||
| | neorv32_onewire.h |
|
||||
| Software driver files: | neorv32_onewire.c | link:https://stnolting.github.io/neorv32/sw/neorv32__onewire_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_onewire.h | link:https://stnolting.github.io/neorv32/sw/neorv32__onewire_8h.html[Online software reference (Doxygen)]
|
||||
| Software reference: | link:https://stnolting.github.io/neorv32/sw/neorv32__onewire_8h.html[Online Doxygen] |
|
||||
| Top entity ports: | `onewire_i` | 1-bit 1-wire bus sense input
|
||||
| | `onewire_o` | 1-bit 1-wire bus output (pull low only)
|
||||
| Configuration generics: | `IO_ONEWIRE_EN` | implement ONEWIRE interface controller when `true`
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Pulse-Width Modulation Controller (PWM)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_pwm.vhd |
|
||||
| Software driver files: | neorv32_pwm.c |
|
||||
| | neorv32_pwm.h |
|
||||
| Software driver files: | neorv32_pwm.c | link:https://stnolting.github.io/neorv32/sw/neorv32__pwm_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_pwm.h | link:https://stnolting.github.io/neorv32/sw/neorv32__pwm_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `pwm_o` | PWM output channels (16-bit)
|
||||
| Configuration generics: | `IO_PWM_NUM_CH` | number of PWM channels to implement (0..16)
|
||||
| CPU interrupts: | none |
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Serial Data Interface Controller (SDI)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_sdi.vhd |
|
||||
| Software driver files: | neorv32_sdi.c |
|
||||
| | neorv32_sdi.h |
|
||||
| Software driver files: | neorv32_sdi.c | link:https://stnolting.github.io/neorv32/sw/neorv32__sdi_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_sdi.h | link:https://stnolting.github.io/neorv32/sw/neorv32__sdi_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `sdi_clk_i` | 1-bit serial clock input
|
||||
| | `sdi_dat_o` | 1-bit serial data output
|
||||
| | `sdi_dat_i` | 1-bit serial data input
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Stream Link Interface (SLINK)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_slink.vhd |
|
||||
| Software driver files: | neorv32_slink.c |
|
||||
| | neorv32_slink.h |
|
||||
| Software driver files: | neorv32_slink.c | link:https://stnolting.github.io/neorv32/sw/neorv32__slink_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_slink.h | link:https://stnolting.github.io/neorv32/sw/neorv32__slink_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `slink_rx_dat_i` | RX link data (32-bit)
|
||||
| | `slink_rx_src_i` | RX routing information (4-bit)
|
||||
| | `slink_rx_val_i` | RX link data valid (1-bit)
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Serial Peripheral Interface Controller (SPI)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_spi.vhd |
|
||||
| Software driver files: | neorv32_spi.c |
|
||||
| | neorv32_spi.h |
|
||||
| Software driver files: | neorv32_spi.c | link:https://stnolting.github.io/neorv32/sw/neorv32__spi_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_spi.h | link:https://stnolting.github.io/neorv32/sw/neorv32__spi_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `spi_clk_o` | 1-bit serial clock output
|
||||
| | `spi_dat_o` | 1-bit serial data output
|
||||
| | `spi_dat_i` | 1-bit serial data input
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
==== System Configuration Information Memory (SYSINFO)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_sysinfo.vhd |
|
||||
| Software driver files: | neorv32_sysinfo.h |
|
||||
| Software driver files: | neorv32_sysinfo.h | link:https://stnolting.github.io/neorv32/sw/neorv32__sysinfo_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | none |
|
||||
| Configuration generics: | * | most of the top's configuration generics
|
||||
| CPU interrupts: | none |
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== True Random-Number Generator (TRNG)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_trng.vhd |
|
||||
| Software driver files: | neorv32_trng.c |
|
||||
| | neorv32_trng.h |
|
||||
| Software driver files: | neorv32_trng.c | link:https://stnolting.github.io/neorv32/sw/neorv32__trng_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_trng.h | link:https://stnolting.github.io/neorv32/sw/neorv32__trng_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | none |
|
||||
| Configuration generics: | `IO_TRNG_EN` | implement TRNG when `true`
|
||||
| | `IO_TRNG_FIFO` | data FIFO depth, min 1, has to be a power of two
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Two-Wire Serial Device Controller (TWD)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_twd.vhd |
|
||||
| Software driver files: | neorv32_twd.c |
|
||||
| | neorv32_twd.h |
|
||||
| Software driver files: | neorv32_twd.c | link:https://stnolting.github.io/neorv32/sw/neorv32__twd_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_twd.h | link:https://stnolting.github.io/neorv32/sw/neorv32__twd_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `twd_sda_i` | 1-bit serial data line sense input
|
||||
| | `twd_sda_o` | 1-bit serial data line output (pull low only)
|
||||
| | `twd_scl_i` | 1-bit serial clock line sense input
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Two-Wire Serial Interface Controller (TWI)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_twi.vhd |
|
||||
| Software driver files: | neorv32_twi.c |
|
||||
| | neorv32_twi.h |
|
||||
| Software driver files: | neorv32_twi.c | link:https://stnolting.github.io/neorv32/sw/neorv32__twi_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_twi.h | link:https://stnolting.github.io/neorv32/sw/neorv32__twi_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `twi_sda_i` | 1-bit serial data line sense input
|
||||
| | `twi_sda_o` | 1-bit serial data line output (pull low only)
|
||||
| | `twi_scl_i` | 1-bit serial clock line sense input
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Primary Universal Asynchronous Receiver and Transmitter (UART0)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_uart.vhd |
|
||||
| Software driver files: | neorv32_uart.c |
|
||||
| | neorv32_uart.h |
|
||||
| Software driver files: | neorv32_uart.c | link:https://stnolting.github.io/neorv32/sw/neorv32__uart_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_uart.h | link:https://stnolting.github.io/neorv32/sw/neorv32__uart_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `uart0_txd_o` | serial transmitter output
|
||||
| | `uart0_rxd_i` | serial receiver input
|
||||
| | `uart0_rts_o` | flow control: RX ready to receive, low-active
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== Watchdog Timer (WDT)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_wdt.vhd |
|
||||
| Software driver files: | neorv32_wdt.c |
|
||||
| | neorv32_wdt.h |
|
||||
| Software driver files: | neorv32_wdt.c | link:https://stnolting.github.io/neorv32/sw/neorv32__wdt_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_wdt.h | link:https://stnolting.github.io/neorv32/sw/neorv32__wdt_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | none |
|
||||
| Configuration generics: | `IO_WDT_EN` | implement watchdog when `true`
|
||||
| CPU interrupts: | none |
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
==== Processor-External Bus Interface (XBUS)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_xbus.vhd | External bus gateway
|
||||
| | neorv32_cache.vhd | Generic cache module
|
||||
|
@ -133,6 +133,12 @@ The **write-allocate** strategy will fetch the entire referenced block from main
|
|||
a cache write-miss. The **write-back** strategy will gather all writes locally inside the cache until the according
|
||||
cache block is about to be replaced. In this case, the entire modified cache block is written back to main memory.
|
||||
|
||||
.Manual Cache Flush/Clear/Reload
|
||||
[NOTE]
|
||||
By executing a `fence` **or** `fence.i` instruction the XBUS cache is flushed (local modifications are send back to
|
||||
main memory), cleared (all cache entries are invalidated) and a reloaded (fetching new data from main memory).
|
||||
See section <<_cache_coherency>> for more information.
|
||||
|
||||
.Cached/Uncached Accesses
|
||||
[NOTE]
|
||||
The data cache provides direct accesses (= uncached) to memory in order to access memory-mapped IO.
|
||||
|
|
|
@ -3,12 +3,12 @@
|
|||
==== Execute In Place Module (XIP)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_xip.vhd | XIP module
|
||||
| | neorv32_cache.vhd | Generic cache module
|
||||
| Software driver files: | neorv32_xip.c |
|
||||
| | neorv32_xip.h |
|
||||
| Software driver files: | neorv32_xip.c | link:https://stnolting.github.io/neorv32/sw/neorv32__xip_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_xip.h | link:https://stnolting.github.io/neorv32/sw/neorv32__xip_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `xip_csn_o` | 1-bit chip select, low-active
|
||||
| | `xip_clk_o` | 1-bit serial clock output
|
||||
| | `xip_dat_i` | 1-bit serial data input
|
||||
|
@ -181,7 +181,7 @@ When the cache is implemented, the XIP module operates in **burst mode** utilizi
|
|||
Thus, several bytes (= `XIP_CACHE_BLOCK_SIZE`) are read consecutively from the flash using a single read command.
|
||||
|
||||
The XIP cache is cleared when the XIP module is disabled (`XIP_CTRL_EN = 0`), when XIP mode is disabled
|
||||
(`XIP_CTRL_XIP_EN = 0`) or when the CPU issues a `fence(.i)` instruction.
|
||||
(`XIP_CTRL_XIP_EN = 0`) or when the CPU issues a `fence[.i]` instruction.
|
||||
|
||||
|
||||
**Register Map**
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
==== External Interrupt Controller (XIRQ)
|
||||
|
||||
[cols="<3,<3,<4"]
|
||||
[frame="topbot",grid="none"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| Hardware source files: | neorv32_xirq.vhd |
|
||||
| Software driver files: | neorv32_xirq.c |
|
||||
| | neorv32_xirq.h |
|
||||
| Software driver files: | neorv32_xirq.c | link:https://stnolting.github.io/neorv32/sw/neorv32__xirq_8c.html[Online software reference (Doxygen)]
|
||||
| | neorv32_xirq.h | link:https://stnolting.github.io/neorv32/sw/neorv32__xirq_8h.html[Online software reference (Doxygen)]
|
||||
| Top entity ports: | `xirq_i` | External interrupts input (32-bit)
|
||||
| Configuration generics: | `XIRQ_NUM_CH` | Number of external IRQ channels to implement (0..32)
|
||||
| CPU interrupts: | fast IRQ channel 8 | XIRQ (see <<_processor_interrupts>>)
|
||||
|
|
|
@ -93,7 +93,7 @@ The NEORV32 HAL consists of the following files.
|
|||
| `neorv32_newlib.c` | - | Platform-specific system calls for _newlib_
|
||||
|=======================
|
||||
|
||||
.Core Library Documentation
|
||||
.Core Libraries Documentation
|
||||
[TIP]
|
||||
The Doxygen-based documentation of the software framework including all core libraries is available online at
|
||||
https://stnolting.github.io/neorv32/sw/files.html.
|
||||
|
@ -171,7 +171,7 @@ NEORV32_HOME ?= ../../..
|
|||
include $(NEORV32_HOME)/sw/common/common.mk
|
||||
```
|
||||
|
||||
.New Project
|
||||
.Setup of a New Project
|
||||
[TIP]
|
||||
When creating a new project, copy an existing project folder or at least the makefile to the new project folder.
|
||||
It is recommended to create new projects also in `sw/example` to keep the file dependencies. However, these
|
||||
|
@ -380,7 +380,8 @@ Note that `\n` (newline) is automatically converted to `\r\n` (carriage-return a
|
|||
.Constructors and Destructors
|
||||
[NOTE]
|
||||
Constructors and destructors for plain C code or for C++ applications are supported by the software framework.
|
||||
See `sw/example/hello_cpp` for a minimal example.
|
||||
See `sw/example/hello_cpp` for a minimal example. Note that constructor and destructors are only executed
|
||||
by core 0 (primary core) in the SMP <<_dual_core_configuration>>.
|
||||
|
||||
.Newlib Test/Demo Program
|
||||
[TIP]
|
||||
|
|
|
@ -29,6 +29,11 @@ An "auto boot" feature can optionally fetch this executable right after reset if
|
|||
via UART. This allows to build processor setups with _non-volatile application storage_ while maintaining the option
|
||||
to update the application software at any timer.
|
||||
|
||||
.Software Documentation
|
||||
[TIP]
|
||||
The Doxygen-based documentation of the bootloader's software is available online:
|
||||
https://stnolting.github.io/neorv32/sw/bootloader_8c.html
|
||||
|
||||
|
||||
:sectnums:
|
||||
==== Bootloader SoC/CPU Requirements
|
||||
|
@ -46,6 +51,7 @@ The bootloader requires certain CPU and SoC extensions and modules to be enabled
|
|||
| _RECOMMENDED_ | The machine timer of the <<_core_local_interruptor_clint>> is used to control blinking of the status LED and also to automatically trigger the <<_auto_boot_sequence>>.
|
||||
| OPTIONAL | The SPI controller (<<_serial_peripheral_interface_controller_spi>>) is needed to store/load executable from external flash using the <<_auto_boot_sequence>>.
|
||||
| OPTIONAL | The XIP controller (<<_execute_in_place_module_xip>>) is needed to boot/execute code directly from a pre-programmed SPI flash.
|
||||
| OPTIONAL | The TWI controller (<<_two_wire_serial_interface_controller_twi>>) is needed to boot/execute code directly from pre-programmed TWI memory.
|
||||
|=======================
|
||||
|
||||
|
||||
|
@ -70,6 +76,17 @@ Most properties (like chip select line, flash address width, SPI clock frequency
|
|||
without the need to change the source code. Custom configuration can be made using command line switches (defines) when recompiling
|
||||
the bootloader. See the User Guide https://stnolting.github.io/neorv32/ug/#_customizing_the_internal_bootloader for more information.
|
||||
|
||||
:sectnums:
|
||||
==== Bootloader TWI memory Requirements
|
||||
|
||||
The bootloader can access an TWI-compatible memory via the processor's top entity TWI port. Single- and dual address memory is supported, and reading is done in the following pattern
|
||||
`Device Address + Enabled Read | Memory Address Byte 0 | Memory Address 1 (optional) | Read Byte 0 | Read Byte 1 | Read Byte 2 | Read Byte 3`.
|
||||
The addresses are incremented until the end of the program binary is reached.
|
||||
|
||||
A python upload script for uploading is provided in the `sw/eeprom_upload` folder. Currently only for the https://www.robot-electronics.co.uk/htm/usb_iss_tech.htm[USB-ISS] module.
|
||||
|
||||
|
||||
Clock speed information can be read here: <<_two_wire_serial_interface_controller_twi>>.
|
||||
|
||||
:sectnums:
|
||||
==== Bootloader Console
|
||||
|
@ -154,6 +171,7 @@ Available CMDs:
|
|||
u: Upload
|
||||
s: Store to flash
|
||||
l: Load from flash
|
||||
t: Load from TWI Device
|
||||
x: Boot from flash (XIP)
|
||||
e: Execute
|
||||
CMD:>
|
||||
|
@ -167,6 +185,7 @@ The auto boot countdown is stopped and the bootloader's user console is ready to
|
|||
* `u`: Upload new program executable (`neorv32_exe.bin`) via UART into the instruction memory
|
||||
* `s`: Store executable to SPI flash at `spi_csn_o(0)` (little-endian byte order)
|
||||
* `l`: Load executable from SPI flash at `spi_csn_o(0)` (little-endian byte order)
|
||||
* `t`: Load executable from TWI memory at `0x50` (little-endian byte order) (disabled by default)
|
||||
* `x`: Boot program directly from flash via XIP (requires a pre-programmed image)
|
||||
* `e`: Start the application, which is currently stored in the instruction memory (IMEM)
|
||||
|
||||
|
@ -206,7 +225,8 @@ https://stnolting.github.io/neorv32/ug/#_programming_an_external_spi_flash_via_t
|
|||
|
||||
When you reset the NEORV32 processor, the bootloader waits 8 seconds for a UART console input before it
|
||||
starts the automatic boot sequence. This sequence tries to fetch a valid boot image from the external SPI
|
||||
flash, connected to SPI chip select `spi_csn_o(0)`. If a valid boot image is found that can be successfully
|
||||
flash, connected to SPI chip select `spi_csn_o(0)` or from external TWI memory. If both are enabled, the bootloader
|
||||
will select SPI. If a valid boot image is found that can be successfully
|
||||
transferred into the instruction memory, it is automatically started. If no SPI flash is detected or if there
|
||||
is no valid boot image found, and error code will be shown.
|
||||
|
||||
|
@ -217,8 +237,10 @@ is no valid boot image found, and error code will be shown.
|
|||
If something goes wrong during bootloader operation an error code and a short message is shown. In this case the processor
|
||||
is halted (entering <<_sleep_mode>>), the bootloader status LED is permanently activated and the processor has to be reset manually.
|
||||
|
||||
.Debugging Information
|
||||
[TIP]
|
||||
In many cases the error source is just _temporary_ (like some HF spike during an UART upload). Just try again.
|
||||
If an unexpected exception has been raised, the bootloader prints hexadecimal debug information showing
|
||||
the <<_mcause>>, <<_mepc>> and <<_mtval>> CSR values.
|
||||
|
||||
[cols="<2,<8"]
|
||||
[grid="rows"]
|
||||
|
@ -228,8 +250,5 @@ In many cases the error source is just _temporary_ (like some HF spike during an
|
|||
| **`ERR_CHKS`** | This indicates a checksum error. Something went wrong during the transfer of the program image (upload via UART or loading from the external SPI flash). If the error was caused by a UART upload, just try it again. When the error was generated during a flash access, the stored image might be corrupted.
|
||||
| **`ERR_FLSH`** | This error occurs if the attached SPI flash cannot be accessed. Make sure you have the right type of flash and that it is properly connected to the NEORV32 SPI port using chip select #0.
|
||||
| **`ERR_EXC`** | The bootloader encountered an unexpected exception during operation. This might be caused when it tries to access peripherals that were not implemented during synthesis. Example: executing commands `l` or `s` (SPI flash operations) without the SPI module being implemented.
|
||||
| **`ERR_TWI`** | The TWI received an unexpected NACK while reading the external memory. Are the address and speed settings correct?
|
||||
|=======================
|
||||
|
||||
[TIP]
|
||||
If an unexpected exception has been raised, the bootloader prints hexadecimal debug information showing
|
||||
the <<_mcause>>, <<_mepc>> and <<_mtval>> CSR values.
|
||||
|
|
|
@ -1,71 +1,70 @@
|
|||
:sectnums:
|
||||
=== NEORV32 Runtime Environment
|
||||
|
||||
The NEORV32 software framework provides a minimal **runtime environment** (abbreviated "RTE") that takes care of a stable
|
||||
and _safe_ execution environment by handling _all_ traps (exceptions & interrupts). The RTE simplifies trap handling
|
||||
by wrapping the CPU's privileged architecture (i.e. trap-related CSRs and the actual execution of trap handlers)
|
||||
into a unified software API.
|
||||
The NEORV32 software framework provides a minimal runtime environment ("RTE") that takes care of a stable
|
||||
and _safe_ execution environment by providing a unified interface for handling of _all_ traps (exceptions and
|
||||
interrupts). Once initialized, the RTE provides <<_default_rte_trap_handlers>> that catch all possible traps.
|
||||
These default handlers just output a message via UART when a certain trap has been triggered. The default
|
||||
handlers can be overridden by the application code to install application-specific handler functions for each trap.
|
||||
|
||||
Once initialized, the RTE provides <<_default_rte_trap_handlers>> that catch all possible traps. These
|
||||
default handlers just output a message via UART to inform the user when a certain trap has been triggered. The
|
||||
default handlers can be overridden by the application code to install application-specific handler functions for each trap.
|
||||
|
||||
[IMPORTANT]
|
||||
Using the RTE is **optional but highly recommended**. The RTE provides a simple and comfortable way of delegating
|
||||
traps to application-specific handlers while making sure that all traps (even though they are not explicitly used
|
||||
by the application) are handled correctly. Performance-optimized applications or embedded operating systems may
|
||||
not use the RTE at all in order to increase response time.
|
||||
Using the RTE is **optional but highly recommended** for bare-metal / non-OS applications. The RTE provides a
|
||||
simple and comfortable way of delegating traps to application-specific handlers while making sure that all traps
|
||||
(even though they are not explicitly used by the application) are handled correctly. Performance-optimized
|
||||
applications or embedded operating systems may not use the RTE at all in order to increase response time.
|
||||
|
||||
|
||||
==== RTE Operation
|
||||
|
||||
The RTE manages the trap-related CSRs of the CPU's privileged architecture (<<_machine_trap_handling_csrs>>).
|
||||
It initializes the <<_mtvec>> CSR in DIRECT mode, which then provides the base entry point for _all_ traps. The address
|
||||
stored to this register defines the address of the **first-level trap handler**, which is provided by the
|
||||
NEORV32 RTE. Whenever an exception or interrupt is triggered this first-level trap handler is executed.
|
||||
The RTE manages the trap-related CSRs of the CPU's privileged architecture (see <<_machine_trap_handling_csrs>>).
|
||||
It initializes the <<_mtvec>> CSR in DIRECT mode, which provides the base entry point for _all_ traps. The address
|
||||
stored to this register defines the address of the **first-level trap handler**, which is provided by the NEORV32
|
||||
RTE. Whenever an exception or interrupt is triggered this first-level trap handler is executed.
|
||||
|
||||
The first-level handler performs a complete context save, analyzes the source of the trap and
|
||||
calls the according **second-level trap handler**, which takes care of the actual exception/interrupt
|
||||
handling. The RTE manages a private look-up table to store the addresses of the according second-level trap handlers.
|
||||
The first-level handler performs a complete context save, analyzes the source of the trap and calls the according
|
||||
**second-level trap handler**, which takes care of the actual exception/interrupt handling. The RTE manages an
|
||||
internal look-up table to track the addresses of the according second-level trap handlers.
|
||||
|
||||
After the initial RTE setup, each entry in the RTE's trap handler look-up table is initialized with a
|
||||
<<_default_rte_trap_handlers>>. These default handler do not execute any trap-related operations - they
|
||||
just output a message via the *primary UART (UART0)* to inform the user that a trap has occurred, which is not (yet)
|
||||
handled by the actual application. After sending this message, the RTE tries to continue executing the actual program
|
||||
by resolving the trap cause.
|
||||
<<_default_rte_trap_handlers>>. These default handler do not execute any trap-related operations - they just output
|
||||
a debugging message via the primary UART (UART0) (if enabled) to inform the user that a trap has occurred that is
|
||||
not (yet) handled by a proper application-specific trap handler. After sending this message, the RTE tries to resume
|
||||
normal execution by moving on to the next linear instruction.
|
||||
|
||||
.Dual-Core Configuration
|
||||
[NOTE]
|
||||
The RTE also supports the SMP <<_dual_core_configuration>> as it provides core-individual internal trap management.
|
||||
The RTE's internal trap handler look-up table is used globally for **both** cores. If a core-specific handling is
|
||||
required, the according user-defined trap handler need to retrieve the core's ID from <<_mhartid>> and branch
|
||||
accordingly.
|
||||
|
||||
|
||||
==== Using the RTE
|
||||
|
||||
.Machine-Mode Only
|
||||
[IMPORTANT]
|
||||
All provided RTE functions can be called only from machine-mode code.
|
||||
The NEORV32 runtime environment is part of the default NEORV32 software framework. The links to the according
|
||||
software references are listed below.
|
||||
|
||||
The NEORV32 is part of the default NEORV32 software framework. However, it has to explicitly enabled by calling
|
||||
the RTE's setup function:
|
||||
[cols="<1,<8"]
|
||||
[grid="none"]
|
||||
|=======================
|
||||
| neorv32_rte.c | link:https://stnolting.github.io/neorv32/sw/neorv32__rte_8c.html[Online software reference (Doxygen)]
|
||||
| neorv32_rte.h | link:https://stnolting.github.io/neorv32/sw/neorv32__rte_8h.html[Online software reference (Doxygen)]
|
||||
|=======================
|
||||
|
||||
.RTE Setup (Function Prototype)
|
||||
The RTE has to be explicitly enabled by calling the according setup function. It is recommended to do this right at the
|
||||
beginning of the application's `main` function. For the SMP <<_dual_core_configuration>> the RTE setup functions has to
|
||||
be called on each core that wants to use the RTE.
|
||||
|
||||
.RTE Setup Right at the Beginning of "main"
|
||||
[source,c]
|
||||
----
|
||||
void neorv32_rte_setup(void);
|
||||
int main() {
|
||||
|
||||
neorv32_rte_setup(); // setup NEORV32 runtime environment
|
||||
|
||||
...
|
||||
----
|
||||
|
||||
.RTE Setup
|
||||
[NOTE]
|
||||
The RTE should be enabled right at the beginning of the application's `main` function. For the SMP
|
||||
<<_dual_core_configuration>> the RTE setup functions has to be called on each core individually.
|
||||
|
||||
[IMPORTANT]
|
||||
It is recommended to not use the <<_mscratch>> CSR when using the RTE as this register is used to provide services
|
||||
for <<_application_context_handling>> (i.e. modifying the registers of application code that caused a trap).
|
||||
|
||||
As mentioned above, all traps will just trigger execution of the RTE's <<_default_rte_trap_handlers>> at first.
|
||||
To use application-specific handlers, which actually "handle" a trap, the default handlers can be overridden
|
||||
by installing user-defined ones:
|
||||
After setup, all traps will trigger execution of the RTE's <<_default_rte_trap_handlers>> at first. In order to use
|
||||
application-specific trap handlers the default debug handlers can be overridden by installing user-defined ones:
|
||||
|
||||
.Installing an Application-Specific Trap Handler (Function Prototype)
|
||||
[source,c]
|
||||
|
@ -73,46 +72,11 @@ by installing user-defined ones:
|
|||
int neorv32_rte_handler_install(uint8_t id, void (*handler)(void));
|
||||
----
|
||||
|
||||
The first argument `id` defines the "trap ID" (for example a certain interrupt request) that shall be handled
|
||||
by the user-defined handler. These IDs are defined in `sw/lib/include/neorv32_rte.h`:
|
||||
|
||||
.RTE Trap Identifiers (cut-out)
|
||||
[source,c]
|
||||
----
|
||||
enum NEORV32_RTE_TRAP_enum {
|
||||
RTE_TRAP_I_MISALIGNED = 0, /**< Instruction address misaligned */
|
||||
RTE_TRAP_I_ACCESS = 1, /**< Instruction (bus) access fault */
|
||||
RTE_TRAP_I_ILLEGAL = 2, /**< Illegal instruction */
|
||||
RTE_TRAP_BREAKPOINT = 3, /**< Breakpoint (EBREAK instruction) */
|
||||
RTE_TRAP_L_MISALIGNED = 4, /**< Load address misaligned */
|
||||
RTE_TRAP_L_ACCESS = 5, /**< Load (bus) access fault */
|
||||
RTE_TRAP_S_MISALIGNED = 6, /**< Store address misaligned */
|
||||
RTE_TRAP_S_ACCESS = 7, /**< Store (bus) access fault */
|
||||
RTE_TRAP_UENV_CALL = 8, /**< Environment call from user mode (ECALL instruction) */
|
||||
RTE_TRAP_MENV_CALL = 9, /**< Environment call from machine mode (ECALL instruction) */
|
||||
RTE_TRAP_MSI = 10, /**< Machine software interrupt */
|
||||
RTE_TRAP_MTI = 11, /**< Machine timer interrupt */
|
||||
RTE_TRAP_MEI = 12, /**< Machine external interrupt */
|
||||
RTE_TRAP_FIRQ_0 = 13, /**< Fast interrupt channel 0 */
|
||||
RTE_TRAP_FIRQ_1 = 14, /**< Fast interrupt channel 1 */
|
||||
RTE_TRAP_FIRQ_2 = 15, /**< Fast interrupt channel 2 */
|
||||
RTE_TRAP_FIRQ_3 = 16, /**< Fast interrupt channel 3 */
|
||||
RTE_TRAP_FIRQ_4 = 17, /**< Fast interrupt channel 4 */
|
||||
RTE_TRAP_FIRQ_5 = 18, /**< Fast interrupt channel 5 */
|
||||
RTE_TRAP_FIRQ_6 = 19, /**< Fast interrupt channel 6 */
|
||||
RTE_TRAP_FIRQ_7 = 20, /**< Fast interrupt channel 7 */
|
||||
RTE_TRAP_FIRQ_8 = 21, /**< Fast interrupt channel 8 */
|
||||
RTE_TRAP_FIRQ_9 = 22, /**< Fast interrupt channel 9 */
|
||||
RTE_TRAP_FIRQ_10 = 23, /**< Fast interrupt channel 10 */
|
||||
RTE_TRAP_FIRQ_11 = 24, /**< Fast interrupt channel 11 */
|
||||
RTE_TRAP_FIRQ_12 = 25, /**< Fast interrupt channel 12 */
|
||||
RTE_TRAP_FIRQ_13 = 26, /**< Fast interrupt channel 13 */
|
||||
RTE_TRAP_FIRQ_14 = 27, /**< Fast interrupt channel 14 */
|
||||
RTE_TRAP_FIRQ_15 = 28 /**< Fast interrupt channel 15 */
|
||||
----
|
||||
|
||||
The second argument `*handler` is the actual function that implements the user-defined trap handler.
|
||||
The custom handler functions need to have a specific format without any arguments and with no return value:
|
||||
The first argument `id` defines the "trap ID" (for example a certain interrupt request) that shall be handled by the
|
||||
user-defined handler. These IDs are defined in `sw/lib/include/neorv32_rte.h`. However, more convenient device-specific
|
||||
aliases are also defined in `sw/lib/include/neorv32.h`. The second argument `handler` is the actual function that
|
||||
implements the user-defined trap handler. The custom handler functions must have a specific type without any arguments
|
||||
and with no return value:
|
||||
|
||||
.Custom Trap Handler (Function Prototype)
|
||||
[source,c]
|
||||
|
@ -124,140 +88,81 @@ void custom_trap_handler_xyz(void) {
|
|||
----
|
||||
|
||||
.Custom Trap Handler Attributes
|
||||
[WARNING]
|
||||
Do **NOT** use the `((interrupt))` attribute for the application trap handler functions! This
|
||||
will place a `mret` instruction to the end of it making it impossible to return to the first-level
|
||||
trap handler of the RTE core, which will cause stack corruption.
|
||||
[IMPORTANT]
|
||||
Do **NOT** use the `((interrupt))` attribute for the application trap handler functions! This would place an `mret`
|
||||
instruction at the end of the handler making it impossible to return to the first-level trap handler of the RTE core.
|
||||
|
||||
The following example shows how to install a custom handler (`custom_timer_irq_handler`) for handling
|
||||
the RISC-V CLINT timer interrupt:
|
||||
.`mscratch` CSR
|
||||
[IMPORTANT]
|
||||
The <<_mscratch>> CSR should not be used inside an application trap handler as this register is used by the RTE to
|
||||
provide the base address of the application's stack frame <<_application_context_handling>> (i.e. modifying the
|
||||
registers of application code that caused a trap).
|
||||
|
||||
.Installing a CLINT Timer IRQ Handler
|
||||
The following example shows how to install trap handlers for exemplary traps.
|
||||
|
||||
.Installing Custom Trap Handlers Examples
|
||||
[source,c]
|
||||
----
|
||||
neorv32_rte_handler_install(RTE_TRAP_MTI, custom_timer_irq_handler);
|
||||
neorv32_rte_handler_install(RTE_TRAP_MTI, machine_timer_irq_handler); // handler for machine timer interrupt
|
||||
neorv32_rte_handler_install(RTE_TRAP_MENV_CALL, environment_call_handler); // handler for machine environment call exception
|
||||
neorv32_rte_handler_install(SLINK_RX_RTE_ID, slink_rx_handler); // handler for SLINK receive interrupt
|
||||
----
|
||||
|
||||
User-defined trap handlers can also be un-installed. This will remove the users trap handler from the RTE core
|
||||
and will re-install the <<_default_rte_trap_handlers>> for the specific trap.
|
||||
|
||||
.Function Prototype: Installing an Application-Specific Trap Handler
|
||||
[source,c]
|
||||
----
|
||||
int neorv32_rte_handler_uninstall(uint8_t id);
|
||||
----
|
||||
|
||||
The argument `id` defines the identifier of the according trap that shall be un-installed.
|
||||
The following example shows how to un-install the custom handler `custom_timer_irq_handler` from the
|
||||
RISC-V CLINT timer interrupt:
|
||||
|
||||
.Example: Removing the Custom CLINT Timer IRQ Handler
|
||||
[source,c]
|
||||
----
|
||||
neorv32_rte_handler_uninstall(RTE_TRAP_MTI);
|
||||
----
|
||||
|
||||
.Dual-Core Configuration
|
||||
[NOTE]
|
||||
The RTE handler install/uninstall functions can be called on any core in the SMP <<_dual_core_configuration>>.
|
||||
Internally, the functions will only access the core-specific management entries.
|
||||
|
||||
|
||||
==== Default RTE Trap Handlers
|
||||
|
||||
The default RTE trap handlers are executed when a certain trap is triggered that is not (yet) handled by an
|
||||
application-defined trap handler. The default handler will output a message giving additional debug information
|
||||
via the <<_primary_universal_asynchronous_receiver_and_transmitter_uart0>> to inform the user and it will also
|
||||
try to resume normal program execution. Some exemplary RTE outputs are shown below.
|
||||
try to resume normal program execution (exemplary RTE outputs are shown below). The specific message right at
|
||||
the beginning of the debug trap handler message corresponds to the trap code obtained from the <<_mcause>> CSR
|
||||
(see <<_neorv32_trap_listing>>).
|
||||
|
||||
.Continuing Execution
|
||||
[WARNING]
|
||||
In most cases the RTE can successfully continue operation - for example if it catches an **interrupt** request
|
||||
that is not handled by the actual application program. However, if the RTE catches an un-handled **trap** like
|
||||
a bus access fault exception continuing execution will most likely fail making the CPU crash. Some exceptions
|
||||
cannot be resolved by the default debug trap handlers and will halt the CPU (see example below).
|
||||
a bus access fault exception, continuing execution will most likely fail making the CPU crash.
|
||||
|
||||
.RTE Default Trap Handler Output Examples
|
||||
.RTE Default Trap Handler UART0 Output Examples
|
||||
[source]
|
||||
----
|
||||
<NEORV32-RTE> [M] Illegal instruction @ PC=0x000002d6, MTINST=0x000000FF, MTVAL=0x00000000 </NEORV32-RTE> <1>
|
||||
<NEORV32-RTE> [U] Illegal instruction @ PC=0x00000302, MTINST=0x00000000, MTVAL=0x00000000 </NEORV32-RTE> <2>
|
||||
<NEORV32-RTE> [U] Load address misaligned @ PC=0x00000440, MTINST=0x01052603, MTVAL=0x80000101 </NEORV32-RTE> <3>
|
||||
<NEORV32-RTE> [M] Fast IRQ 0x00000003 @ PC=0x00000820, MTINST=0x00000000, MTVAL=0x00000000 </NEORV32-RTE> <4>
|
||||
<NEORV32-RTE> [M] Instruction access fault @ PC=0x90000000, MTINST=0x42078b63, MTVAL=0x00000000 !!FATAL EXCEPTION!! Halting CPU. </NEORV32-RTE>\n <5>
|
||||
<NEORV32-RTE> [cpu0] [M] Illegal instruction @ PC=0x000002d6, MTINST=0x000000FF, MTVAL=0x00000000 </NEORV32-RTE> <1>
|
||||
<NEORV32-RTE> [cpu0] [U] Illegal instruction @ PC=0x00000302, MTINST=0x00000000, MTVAL=0x00000000 </NEORV32-RTE> <2>
|
||||
<NEORV32-RTE> [cpu0] [U] Load address misaligned @ PC=0x00000440, MTINST=0x01052603, MTVAL=0x80000101 </NEORV32-RTE> <3>
|
||||
<NEORV32-RTE> [cpu1] [M] Fast IRQ 0x3 @ PC=0x00000820, MTINST=0x00000000, MTVAL=0x00000000 </NEORV32-RTE> <4>
|
||||
<NEORV32-RTE> [cpu1] [M] Instruction access fault @ PC=0x90000000, MTINST=0x42078b63, MTVAL=0x00000000 !!FATAL EXCEPTION!! Halting CPU. </NEORV32-RTE>\n <5>
|
||||
----
|
||||
<1> Illegal 32-bit instruction `MTINST=0x000000FF` at address `PC=0x000002d6` while the CPU was in machine-mode (`[M]`).
|
||||
<2> Illegal 16-bit instruction `MTINST=0x00000000` at address `PC=0x00000302` while the CPU was in user-mode (`[U]`).
|
||||
<3> Misaligned load access at address `PC=0x00000440` caused by instruction `MTINST=0x01052603` (trying to load a full 32-bit word from address `MTVAL=0x80000101`) while the CPU was in machine-mode (`[U]`).
|
||||
<4> Fast interrupt request from channel 3 before executing instruction at address `PC=0x00000820` while the CPU was in machine-mode (`[M]`).
|
||||
<5> Instruction bus access fault at address `PC=0x90000000` while executing instruction `MTINST=0x42078b63` - this is fatal for the default debug trap handler while the CPU was in machine-mode (`[M]`).
|
||||
|
||||
The specific message right at the beginning of the debug trap handler message corresponds to the trap code
|
||||
obtained from the <<_mcause>> CSR (see <<_neorv32_trap_listing>>). A full list of all messages and the according
|
||||
`mcause` trap codes is shown below.
|
||||
|
||||
.RTE Default Trap Handler Messages and According `mcause` Values
|
||||
[cols="<5,^5"]
|
||||
[options="header",grid="rows"]
|
||||
|=======================
|
||||
| Trap identifier | According `mcause` CSR value
|
||||
| "Instruction address misaligned" | `0x00000000`
|
||||
| "Instruction access fault" | `0x00000001`
|
||||
| "Illegal instruction" | `0x00000002`
|
||||
| "Breakpoint" | `0x00000003`
|
||||
| "Load address misaligned" | `0x00000004`
|
||||
| "Load access fault" | `0x00000005`
|
||||
| "Store address misaligned" | `0x00000006`
|
||||
| "Store access fault" | `0x00000007`
|
||||
| "Environment call from U-mode" | `0x00000008`
|
||||
| "Environment call from M-mode" | `0x0000000b`
|
||||
| "Machine software IRQ" | `0x80000003`
|
||||
| "Machine timer IRQ" | `0x80000007`
|
||||
| "Machine external IRQ" | `0x8000000b`
|
||||
| "Fast IRQ 0x00000000" | `0x80000010`
|
||||
| "Fast IRQ 0x00000001" | `0x80000011`
|
||||
| "Fast IRQ 0x00000002" | `0x80000012`
|
||||
| "Fast IRQ 0x00000003" | `0x80000013`
|
||||
| "Fast IRQ 0x00000004" | `0x80000014`
|
||||
| "Fast IRQ 0x00000005" | `0x80000015`
|
||||
| "Fast IRQ 0x00000006" | `0x80000016`
|
||||
| "Fast IRQ 0x00000007" | `0x80000017`
|
||||
| "Fast IRQ 0x00000008" | `0x80000018`
|
||||
| "Fast IRQ 0x00000009" | `0x80000019`
|
||||
| "Fast IRQ 0x0000000a" | `0x8000001a`
|
||||
| "Fast IRQ 0x0000000b" | `0x8000001b`
|
||||
| "Fast IRQ 0x0000000c" | `0x8000001c`
|
||||
| "Fast IRQ 0x0000000d" | `0x8000001d`
|
||||
| "Fast IRQ 0x0000000e" | `0x8000001e`
|
||||
| "Fast IRQ 0x0000000f" | `0x8000001f`
|
||||
| "Unknown trap cause" | undefined
|
||||
|=======================
|
||||
<1> Illegal 32-bit instruction `MTINST=0x000000FF` at address `PC=0x000002d6` while the CPU 0 was in machine-mode (`[M]`).
|
||||
<2> Illegal 16-bit instruction `MTINST=0x00000000` at address `PC=0x00000302` while the CPU 0 was in user-mode (`[U]`).
|
||||
<3> Misaligned load access at address `PC=0x00000440` caused by instruction `MTINST=0x01052603` (trying to load a full 32-bit word from address `MTVAL=0x80000101`) while the CPU 0 was in user-mode (`[U]`).
|
||||
<4> Fast interrupt request from channel 3 before executing instruction at address `PC=0x00000820` while the CPU 1 was in machine-mode (`[M]`).
|
||||
<5> Instruction bus access fault at address `PC=0x90000000` while executing instruction `MTINST=0x42078b63` while the CPU 1 was in machine-mode (`[M]`).
|
||||
|
||||
|
||||
==== Application Context Handling
|
||||
|
||||
Upon trap entry the RTE backups the _entire_ application context (i.e. all `x` general purpose registers)
|
||||
to the stack. The context is restored automatically after trap completion. The base address of the according
|
||||
stack frame is copied to the <<_mscratch>> CSR. By having this information available, the RTE provides dedicated
|
||||
functions for accessing and _altering_ the application context:
|
||||
Upon trap entry the RTE backups the entire application context (i.e. all `x` general purpose registers) to the
|
||||
stack. The context is restored automatically after trap completion. The base address of the according stack frame
|
||||
is copied to the <<_mscratch>> CSR. By having this information available, the RTE provides dedicated functions
|
||||
for accessing and altering the application context:
|
||||
|
||||
.Context Access Functions
|
||||
.RTE Context Access Functions
|
||||
[source,c]
|
||||
----
|
||||
// Prototypes
|
||||
uint32_t neorv32_rte_context_get(int x); // read register x
|
||||
void neorv32_rte_context_put(int x, uint32_t data); write data to register x
|
||||
uint32_t neorv32_rte_context_get(int x); // read register
|
||||
void neorv32_rte_context_put(int x, uint32_t data); // write data to register
|
||||
|
||||
// Examples
|
||||
uint32_t tmp = neorv32_rte_context_get(9); // read register 'x9'
|
||||
neorv32_rte_context_put(28, tmp); // write 'tmp' to register 'x28'
|
||||
----
|
||||
|
||||
.RISC-V `E` Extension
|
||||
[NOTE]
|
||||
Registers `x16..x31` are not available if the RISC-V <<_e_isa_extension>> is enabled.
|
||||
The `x` argument is used to specify one of the RISC-V general purpose register `x0` to `x31`. Note that registers
|
||||
`x16` to `x31` are not available if the RISC-V <<_e_isa_extension>> is enabled. For he SMP <<_dual_core_configuration>>
|
||||
the provided context functions will access the stack frame of the interrupted application code that was running
|
||||
on the specific CPU core that caused the trap entry.
|
||||
|
||||
The context access functions can be used by application-specific trap handlers to emulate unsupported
|
||||
The context access functions can be used by application-specific trap handlers to _emulate_ unsupported
|
||||
CPU / SoC features like unimplemented IO modules, unsupported instructions and even unaligned memory accesses.
|
||||
|
||||
.Demo Program: Emulate Unaligned Memory Access
|
||||
|
|
|
@ -39,14 +39,23 @@ minimal base & privileged ISA `rv32e_zicsr_zifencei` only to ensure it can work
|
|||
| `SPI_BOOT_BASE_ADDR` | `0x00400000` | _any_ 32-bit value | Defines the _base_ address of the executable in external flash
|
||||
4+^| XIP configuration
|
||||
| `XIP_EN` | `0` | `0`, `1` | Set `1` to enable the XIP options
|
||||
4+^| TWI configuration
|
||||
| `TWI_EN` | `0` | `0`, `1` | Set `1` to enable the usage of the TWI module (including load executables from TWI device option)
|
||||
| `TWI_CLK_PRSC` | `CLK_PRSC_64` | `CLK_PRSC_2` `CLK_PRSC_4` `CLK_PRSC_8` `CLK_PRSC_64` `CLK_PRSC_128` `CLK_PRSC_1024` `CLK_PRSC_2024` `CLK_PRSC_4096` | TWI clock pre-scaler (dividing main processor clock)
|
||||
| `TWI_CLK_DIV` | `3` | `0` ... `15` | TWI clock divider (dividing twi clock)
|
||||
| `TWI_DEVICE_ID` | `0x50` | `0x00` ... `0x7F` | First TWI device ID to start. Is incremented until the end of the program is reached, when `TWI_ADDR_BYTES` is `1`.
|
||||
| `TWI_ADDR_BYTES` | `1` | `1`, `2` | TWI memory address size in number of bytes. When `TWI_ADDR_BYTES` is `1`, `TWI_DEVICE_ID` the gets incremented as well.
|
||||
|=======================
|
||||
|
||||
[IMPORTANT]
|
||||
Enabling all features while sticking to the minimal RISC-V ISA will result in a too-large binary!
|
||||
|
||||
[NOTE]
|
||||
The XIP options re-use the "SPI configuration" options for configuring the XIP's SPI connection.
|
||||
|
||||
Each configuration parameter is implemented as C-language `define` that can be manually overridden (_redefined_) when
|
||||
invoking the bootloader's makefile. The according parameter and its new value has to be _appended_
|
||||
(using `+=`) to the makefile `USER_FLAGS` variable. Make sure to use the `-D` prefix here.
|
||||
(using `+=`) to the makefile `USER_FLAGS` variable. Make sure to use the `-D` prefix here. The configuration is also listed in the makefile of the bootloader.
|
||||
|
||||
For example, to configure a UART Baud rate of 57600 and redirecting the status LED to GPIO output pin 20
|
||||
use the following command:
|
||||
|
|
|
@ -123,6 +123,9 @@ See section https://stnolting.github.io/neorv32/#_bootloader[Bootloader] of the
|
|||
See section <<_programming_an_external_spi_flash_via_the_bootloader>> to learn how to use an external SPI
|
||||
flash for nonvolatile program storage.
|
||||
|
||||
[TIP]
|
||||
The bootloader also supports booting from external TWI memory. Enable it in the bootloader makefile, but be careful, enabling all features may result in a too-big binary.
|
||||
|
||||
[TIP]
|
||||
Executables can also be uploaded via the **on-chip debugger**.
|
||||
See section <<_debugging_with_gdb>> for more information.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
-- The NEORV32 RISC-V Processor - github.com/stnolting/neorv32
|
||||
-- Auto-generated memory initialization image (for internal IMEM)
|
||||
-- Source: demo_blink_led/build/main.bin
|
||||
-- Built: 07.01.2025 21:36:11
|
||||
-- Built: 10.01.2025 10:25:11
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
|
@ -11,7 +11,7 @@ use neorv32.neorv32_package.all;
|
|||
|
||||
package neorv32_application_image is
|
||||
|
||||
constant application_init_size_c : natural := 1228; -- bytes
|
||||
constant application_init_size_c : natural := 1216; -- bytes
|
||||
constant application_init_image_c : mem32_t := (
|
||||
x"f14020f3",
|
||||
x"80002217",
|
||||
|
@ -23,11 +23,11 @@ x"000022b7",
|
|||
x"80028293",
|
||||
x"30029073",
|
||||
x"00000317",
|
||||
x"19430313",
|
||||
x"18830313",
|
||||
x"30531073",
|
||||
x"30401073",
|
||||
x"00000397",
|
||||
x"49838393",
|
||||
x"48c38393",
|
||||
x"80000417",
|
||||
x"fc440413",
|
||||
x"80000497",
|
||||
|
@ -37,7 +37,7 @@ x"fb450513",
|
|||
x"80000597",
|
||||
x"fac58593",
|
||||
x"00000617",
|
||||
x"19c60613",
|
||||
x"19060613",
|
||||
x"00000693",
|
||||
x"00000713",
|
||||
x"00000793",
|
||||
|
@ -57,26 +57,23 @@ x"00000e13",
|
|||
x"00000e93",
|
||||
x"00000f13",
|
||||
x"00000f93",
|
||||
x"04008a63",
|
||||
x"04008463",
|
||||
x"00000797",
|
||||
x"01878793",
|
||||
x"30579073",
|
||||
x"30446073",
|
||||
x"30046073",
|
||||
x"0e80006f",
|
||||
x"0dc0006f",
|
||||
x"fff40737",
|
||||
x"00209793",
|
||||
x"00f70733",
|
||||
x"00072023",
|
||||
x"bc201073",
|
||||
x"bc0026f3",
|
||||
x"00072223",
|
||||
x"bc1026f3",
|
||||
x"ffab4737",
|
||||
x"32170713",
|
||||
x"00d70463",
|
||||
x"30200073",
|
||||
x"bc102173",
|
||||
x"bc102673",
|
||||
x"bc171073",
|
||||
x"bc002173",
|
||||
x"bc002673",
|
||||
x"0540006f",
|
||||
x"00838e63",
|
||||
x"00945c63",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
-- The NEORV32 RISC-V Processor - github.com/stnolting/neorv32
|
||||
-- Auto-generated memory initialization image (for internal BOOTROM)
|
||||
-- Source: bootloader/build/main.bin
|
||||
-- Built: 07.01.2025 21:35:42
|
||||
-- Built: 10.01.2025 10:24:53
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
|
@ -11,7 +11,7 @@ use neorv32.neorv32_package.all;
|
|||
|
||||
package neorv32_bootloader_image is
|
||||
|
||||
constant bootloader_init_size_c : natural := 4044; -- bytes
|
||||
constant bootloader_init_size_c : natural := 4032; -- bytes
|
||||
constant bootloader_init_image_c : mem32_t := (
|
||||
x"f14020f3",
|
||||
x"80200217",
|
||||
|
@ -23,11 +23,11 @@ x"000022b7",
|
|||
x"80028293",
|
||||
x"30029073",
|
||||
x"00000317",
|
||||
x"10430313",
|
||||
x"0f830313",
|
||||
x"30531073",
|
||||
x"30401073",
|
||||
x"00001397",
|
||||
x"f9838393",
|
||||
x"f8c38393",
|
||||
x"80200417",
|
||||
x"fc440413",
|
||||
x"80200497",
|
||||
|
@ -37,30 +37,27 @@ x"fb450513",
|
|||
x"80200597",
|
||||
x"fb458593",
|
||||
x"00000617",
|
||||
x"10c60613",
|
||||
x"10060613",
|
||||
x"00000693",
|
||||
x"00000713",
|
||||
x"00000793",
|
||||
x"04008a63",
|
||||
x"04008463",
|
||||
x"00000797",
|
||||
x"01878793",
|
||||
x"30579073",
|
||||
x"30446073",
|
||||
x"30046073",
|
||||
x"0980006f",
|
||||
x"08c0006f",
|
||||
x"fff40737",
|
||||
x"00209793",
|
||||
x"00f70733",
|
||||
x"00072023",
|
||||
x"bc201073",
|
||||
x"bc0026f3",
|
||||
x"00072223",
|
||||
x"bc1026f3",
|
||||
x"ffab4737",
|
||||
x"32170713",
|
||||
x"00d70463",
|
||||
x"30200073",
|
||||
x"bc102173",
|
||||
x"bc102673",
|
||||
x"bc171073",
|
||||
x"bc002173",
|
||||
x"bc002673",
|
||||
x"0300006f",
|
||||
x"00838e63",
|
||||
x"00945c63",
|
||||
|
@ -112,7 +109,7 @@ x"ffe017b7",
|
|||
x"00112823",
|
||||
x"00812623",
|
||||
x"00912423",
|
||||
x"a4078793",
|
||||
x"a3478793",
|
||||
x"30579073",
|
||||
x"fffe07b7",
|
||||
x"0087a783",
|
||||
|
@ -196,54 +193,54 @@ x"30479073",
|
|||
x"00800793",
|
||||
x"3007a073",
|
||||
x"ffe01537",
|
||||
x"dbc50513",
|
||||
x"db050513",
|
||||
x"6b4000ef",
|
||||
x"f1302573",
|
||||
x"648000ef",
|
||||
x"ffe01537",
|
||||
x"df450513",
|
||||
x"de850513",
|
||||
x"6a0000ef",
|
||||
x"fffe0437",
|
||||
x"00042503",
|
||||
x"630000ef",
|
||||
x"ffe01537",
|
||||
x"dfc50513",
|
||||
x"df050513",
|
||||
x"688000ef",
|
||||
x"30102573",
|
||||
x"61c000ef",
|
||||
x"ffe01537",
|
||||
x"e0450513",
|
||||
x"df850513",
|
||||
x"674000ef",
|
||||
x"fc002573",
|
||||
x"608000ef",
|
||||
x"ffe01537",
|
||||
x"e0c50513",
|
||||
x"e0050513",
|
||||
x"660000ef",
|
||||
x"00842503",
|
||||
x"00100493",
|
||||
x"5f0000ef",
|
||||
x"ffe01537",
|
||||
x"e1450513",
|
||||
x"e0850513",
|
||||
x"648000ef",
|
||||
x"00444503",
|
||||
x"00a49533",
|
||||
x"ffc57513",
|
||||
x"5d4000ef",
|
||||
x"ffe01537",
|
||||
x"e1c50513",
|
||||
x"e1050513",
|
||||
x"62c000ef",
|
||||
x"00544783",
|
||||
x"00f49533",
|
||||
x"ffc57513",
|
||||
x"5b8000ef",
|
||||
x"ffe014b7",
|
||||
x"db848513",
|
||||
x"dac48513",
|
||||
x"610000ef",
|
||||
x"00842783",
|
||||
x"00f79713",
|
||||
x"06075063",
|
||||
x"ffe01537",
|
||||
x"e2450513",
|
||||
x"e1850513",
|
||||
x"5f8000ef",
|
||||
x"2e0000ef",
|
||||
x"00042703",
|
||||
|
@ -263,13 +260,13 @@ x"00f69613",
|
|||
x"0a065463",
|
||||
x"ffe01537",
|
||||
x"00472783",
|
||||
x"e5050513",
|
||||
x"e4450513",
|
||||
x"5a8000ef",
|
||||
x"ffe017b7",
|
||||
x"e5c78513",
|
||||
x"e5078513",
|
||||
x"59c000ef",
|
||||
x"ffe01537",
|
||||
x"edc50513",
|
||||
x"ed050513",
|
||||
x"590000ef",
|
||||
x"fff507b7",
|
||||
x"0007a703",
|
||||
|
@ -279,14 +276,14 @@ x"0047a403",
|
|||
x"0ff47413",
|
||||
x"00040513",
|
||||
x"4f4000ef",
|
||||
x"db848513",
|
||||
x"dac48513",
|
||||
x"568000ef",
|
||||
x"f9b40413",
|
||||
x"0ff47413",
|
||||
x"01300793",
|
||||
x"2287e863",
|
||||
x"ffe017b7",
|
||||
x"f5878793",
|
||||
x"f4c78793",
|
||||
x"00241413",
|
||||
x"00f40433",
|
||||
x"00042783",
|
||||
|
@ -310,7 +307,7 @@ x"00b41463",
|
|||
x"f2f564e3",
|
||||
x"00100513",
|
||||
x"6f8000ef",
|
||||
x"db848513",
|
||||
x"dac48513",
|
||||
x"4ec000ef",
|
||||
x"00000513",
|
||||
x"031000ef",
|
||||
|
@ -323,20 +320,20 @@ x"800007b7",
|
|||
x"0047a403",
|
||||
x"00041863",
|
||||
x"ffe01537",
|
||||
x"ee450513",
|
||||
x"ed850513",
|
||||
x"f1dff06f",
|
||||
x"ffe01537",
|
||||
x"f0050513",
|
||||
x"ef450513",
|
||||
x"4ac000ef",
|
||||
x"00040513",
|
||||
x"440000ef",
|
||||
x"ffe01537",
|
||||
x"f0850513",
|
||||
x"efc50513",
|
||||
x"498000ef",
|
||||
x"00400537",
|
||||
x"42c000ef",
|
||||
x"ffe01537",
|
||||
x"f2050513",
|
||||
x"f1450513",
|
||||
x"484000ef",
|
||||
x"fff507b7",
|
||||
x"0007a703",
|
||||
|
@ -354,7 +351,7 @@ x"00050663",
|
|||
x"00300513",
|
||||
x"498000ef",
|
||||
x"ffe01537",
|
||||
x"f2c50513",
|
||||
x"f2050513",
|
||||
x"43c000ef",
|
||||
x"01045793",
|
||||
x"00178793",
|
||||
|
@ -392,7 +389,7 @@ x"00850513",
|
|||
x"40e005b3",
|
||||
x"2a8000ef",
|
||||
x"ffe01537",
|
||||
x"da050513",
|
||||
x"d9450513",
|
||||
x"e09ff06f",
|
||||
x"00f12223",
|
||||
x"1ec000ef",
|
||||
|
@ -418,14 +415,14 @@ x"800007b7",
|
|||
x"0047a783",
|
||||
x"e60790e3",
|
||||
x"ffe01537",
|
||||
x"f3c50513",
|
||||
x"f3050513",
|
||||
x"da1ff06f",
|
||||
x"fffe07b7",
|
||||
x"0087a783",
|
||||
x"2007f793",
|
||||
x"00079863",
|
||||
x"ffe01537",
|
||||
x"f4c50513",
|
||||
x"f4050513",
|
||||
x"d85ff06f",
|
||||
x"00100513",
|
||||
x"e35ff06f",
|
||||
|
@ -614,7 +611,7 @@ x"01c00493",
|
|||
x"00945733",
|
||||
x"ffe017b7",
|
||||
x"00f77713",
|
||||
x"fa878793",
|
||||
x"f9c78793",
|
||||
x"00e787b3",
|
||||
x"0007c503",
|
||||
x"ffc48493",
|
||||
|
@ -650,13 +647,13 @@ x"ff810113",
|
|||
x"00812023",
|
||||
x"00050413",
|
||||
x"ffe01537",
|
||||
x"d4850513",
|
||||
x"d3c50513",
|
||||
x"00112223",
|
||||
x"f99ff0ef",
|
||||
x"00241793",
|
||||
x"ffe01537",
|
||||
x"008787b3",
|
||||
x"fb850513",
|
||||
x"fac50513",
|
||||
x"00f50533",
|
||||
x"f81ff0ef",
|
||||
x"00800793",
|
||||
|
@ -737,7 +734,7 @@ x"0087a783",
|
|||
x"00e79713",
|
||||
x"04075263",
|
||||
x"ffe01537",
|
||||
x"d5050513",
|
||||
x"d4450513",
|
||||
x"e41ff0ef",
|
||||
x"00048513",
|
||||
x"dd5ff0ef",
|
||||
|
@ -750,7 +747,7 @@ x"da5ff0ef",
|
|||
x"34302573",
|
||||
x"db5ff0ef",
|
||||
x"ffe01537",
|
||||
x"db850513",
|
||||
x"dac50513",
|
||||
x"e0dff0ef",
|
||||
x"00440413",
|
||||
x"34141073",
|
||||
|
@ -765,7 +762,7 @@ x"00a12023",
|
|||
x"00f4a023",
|
||||
x"02051863",
|
||||
x"ffe01537",
|
||||
x"d5c50513",
|
||||
x"d5050513",
|
||||
x"dd1ff0ef",
|
||||
x"00012503",
|
||||
x"004005b7",
|
||||
|
@ -776,12 +773,12 @@ x"04f50863",
|
|||
x"00000513",
|
||||
x"0380006f",
|
||||
x"ffe01537",
|
||||
x"d7c50513",
|
||||
x"d7050513",
|
||||
x"da5ff0ef",
|
||||
x"00400537",
|
||||
x"d39ff0ef",
|
||||
x"ffe01537",
|
||||
x"d9850513",
|
||||
x"d8c50513",
|
||||
x"d91ff0ef",
|
||||
x"fffe07b7",
|
||||
x"0087a783",
|
||||
|
@ -813,7 +810,7 @@ x"00d787b3",
|
|||
x"00200513",
|
||||
x"fa0792e3",
|
||||
x"ffe01537",
|
||||
x"da050513",
|
||||
x"d9450513",
|
||||
x"d11ff0ef",
|
||||
x"800007b7",
|
||||
x"0087a223",
|
||||
|
@ -847,12 +844,12 @@ x"40a00533",
|
|||
x"e0400437",
|
||||
x"00a47433",
|
||||
x"ffe01537",
|
||||
x"da450513",
|
||||
x"d9850513",
|
||||
x"c89ff0ef",
|
||||
x"00040513",
|
||||
x"c1dff0ef",
|
||||
x"ffe01537",
|
||||
x"db450513",
|
||||
x"da850513",
|
||||
x"c75ff0ef",
|
||||
x"975ff0ef",
|
||||
x"00050863",
|
||||
|
@ -901,8 +898,8 @@ x"72656461",
|
|||
x"0a3e3e20",
|
||||
x"444c420a",
|
||||
x"4a203a56",
|
||||
x"20206e61",
|
||||
x"30322037",
|
||||
x"31206e61",
|
||||
x"30322030",
|
||||
x"480a3532",
|
||||
x"203a5657",
|
||||
x"00000020",
|
||||
|
@ -995,26 +992,26 @@ x"00002e65",
|
|||
x"61766e49",
|
||||
x"2064696c",
|
||||
x"00444d43",
|
||||
x"ffe00644",
|
||||
x"ffe0066c",
|
||||
x"ffe0066c",
|
||||
x"ffe003f0",
|
||||
x"ffe0066c",
|
||||
x"ffe0066c",
|
||||
x"ffe0066c",
|
||||
x"ffe0063c",
|
||||
x"ffe0066c",
|
||||
x"ffe0066c",
|
||||
x"ffe0066c",
|
||||
x"ffe0066c",
|
||||
x"ffe0066c",
|
||||
x"ffe004b4",
|
||||
x"ffe004c8",
|
||||
x"ffe0066c",
|
||||
x"ffe00638",
|
||||
x"ffe00660",
|
||||
x"ffe00660",
|
||||
x"ffe003e4",
|
||||
x"ffe00660",
|
||||
x"ffe00660",
|
||||
x"ffe00660",
|
||||
x"ffe00630",
|
||||
x"ffe00660",
|
||||
x"ffe00660",
|
||||
x"ffe00660",
|
||||
x"ffe00660",
|
||||
x"ffe00660",
|
||||
x"ffe004a8",
|
||||
x"ffe004bc",
|
||||
x"ffe0066c",
|
||||
x"ffe0066c",
|
||||
x"ffe0065c",
|
||||
x"ffe00660",
|
||||
x"ffe004b0",
|
||||
x"ffe00660",
|
||||
x"ffe00660",
|
||||
x"ffe00650",
|
||||
x"33323130",
|
||||
x"37363534",
|
||||
x"62613938",
|
||||
|
|
|
@ -827,7 +827,7 @@ begin
|
|||
arbiter_nxt.state <= S_READ_WAIT;
|
||||
end if;
|
||||
|
||||
when S_READ_WAIT => -- wait for device read-access to complete
|
||||
when S_READ_WAIT => -- wait for read-access to complete
|
||||
-- ------------------------------------------------------------
|
||||
arbiter_nxt.rdata <= sys_rsp_i.data;
|
||||
if (sys_rsp_i.ack = '1') or (sys_rsp_i.err = '1') then
|
||||
|
@ -838,11 +838,11 @@ begin
|
|||
-- ------------------------------------------------------------
|
||||
arbiter_nxt.state <= S_WRITE;
|
||||
|
||||
when S_WRITE => -- wait operation result to device
|
||||
when S_WRITE => -- write operation result
|
||||
-- ------------------------------------------------------------
|
||||
arbiter_nxt.state <= S_WRITE_WAIT;
|
||||
|
||||
when S_WRITE_WAIT => -- wait for device write-access to complete
|
||||
when S_WRITE_WAIT => -- wait for write-access to complete
|
||||
-- ------------------------------------------------------------
|
||||
if (sys_rsp_i.ack = '1') or (sys_rsp_i.err = '1') then
|
||||
arbiter_nxt.state <= S_IDLE;
|
||||
|
@ -883,12 +883,12 @@ begin
|
|||
alu_res <= (others => '0');
|
||||
elsif rising_edge(clk_i) then
|
||||
case arbiter.cmd(2 downto 0) is
|
||||
when "000" => alu_res <= arbiter.wdata; -- AMOSWAP
|
||||
when "001" => alu_res <= std_ulogic_vector(unsigned(arbiter.rdata) + unsigned(arbiter.wdata)); -- AMOADD
|
||||
when "010" => alu_res <= arbiter.rdata xor arbiter.wdata; -- AMOXOR
|
||||
when "011" => alu_res <= arbiter.rdata and arbiter.wdata; -- AMOAND
|
||||
when "100" => alu_res <= arbiter.rdata or arbiter.wdata; -- AMOOR
|
||||
when others => alu_res <= cmp_res; -- AMOMIN[U] / AMOMAX[U]
|
||||
when "000" => alu_res <= arbiter.wdata; -- AMOSWAP.W
|
||||
when "001" => alu_res <= std_ulogic_vector(unsigned(arbiter.rdata) + unsigned(arbiter.wdata)); -- AMOADD.W
|
||||
when "010" => alu_res <= arbiter.rdata xor arbiter.wdata; -- AMOXOR.W
|
||||
when "011" => alu_res <= arbiter.rdata and arbiter.wdata; -- AMOAND.W
|
||||
when "100" => alu_res <= arbiter.rdata or arbiter.wdata; -- AMOOR.W
|
||||
when others => alu_res <= cmp_res; -- AMOMIN[U].W / AMOMAX[U].W
|
||||
end case;
|
||||
end if;
|
||||
end process amo_alu;
|
||||
|
|
|
@ -8,11 +8,6 @@
|
|||
-- the 4 most significant address bits, well as all atomic (reservation set) --
|
||||
-- operations will always **bypass** the cache resulting in "direct accesses". --
|
||||
-- --
|
||||
-- A fence request will first flush the data cache (write back modified blocks to --
|
||||
-- main memory before invalidating all cache blocks to force a re-fetch from main --
|
||||
-- memory. After this, the fence request is forwarded to the downstream memory --
|
||||
-- system. --
|
||||
-- --
|
||||
-- Simplified cache architecture ("-->" = direction of access requests): --
|
||||
-- --
|
||||
-- Direct Access +----------+ --
|
||||
|
@ -851,8 +846,8 @@ begin
|
|||
bus_req_o.ben <= (others => '1'); -- full-word writes only
|
||||
bus_req_o.src <= '0'; -- cache accesses are always data accesses
|
||||
bus_req_o.priv <= '0'; -- cache accesses are always "unprivileged" accesses
|
||||
bus_req_o.amo <= '0'; -- cache accesses can never be an atomic memory operation set operation
|
||||
bus_req_o.amoop <= (others => '0'); -- cache accesses can never be an atomic memory operation set operation
|
||||
bus_req_o.amo <= '0'; -- cache accesses can never be an atomic memory operation
|
||||
bus_req_o.amoop <= (others => '0'); -- cache accesses can never be an atomic memory operation
|
||||
bus_req_o.debug <= host_req_i.debug;
|
||||
if (state = S_IDLE) then
|
||||
bus_req_o.sleep <= host_req_i.sleep;
|
||||
|
@ -946,9 +941,10 @@ begin
|
|||
|
||||
when S_FLUSH_START => -- start checking for dirty blocks
|
||||
-- ------------------------------------------------------------
|
||||
addr_nxt.idx <= (others => '0'); -- start with index 0
|
||||
upret_nxt <= S_FLUSH_CHECK; -- come back to S_FLUSH_CHECK after block upload
|
||||
state_nxt <= S_FLUSH_READ;
|
||||
addr_nxt.idx <= (others => '0'); -- start with index 0
|
||||
bus_req_o.fence <= bool_to_ulogic_f(READ_ONLY); -- forward fence request
|
||||
upret_nxt <= S_FLUSH_CHECK; -- come back to S_FLUSH_CHECK after block upload
|
||||
state_nxt <= S_FLUSH_READ;
|
||||
|
||||
when S_FLUSH_READ => -- cache read access latency cycle
|
||||
-- ------------------------------------------------------------
|
||||
|
@ -963,7 +959,7 @@ begin
|
|||
else -- move on to next block
|
||||
addr_nxt.idx <= std_ulogic_vector(unsigned(addr.idx) + 1);
|
||||
if (and_reduce_f(addr.idx) = '1') then -- all blocks done?
|
||||
bus_req_o.fence <= '1'; -- forward fence request to downstream memories
|
||||
bus_req_o.fence <= not bool_to_ulogic_f(READ_ONLY); -- forward fence request
|
||||
state_nxt <= S_IDLE;
|
||||
else -- go to next block
|
||||
state_nxt <= S_FLUSH_READ;
|
||||
|
|
|
@ -1,257 +0,0 @@
|
|||
-- ================================================================================ --
|
||||
-- NEORV32 SoC - Core Complex Top --
|
||||
-- -------------------------------------------------------------------------------- --
|
||||
-- CPU core + optional L1 I-cache + optional L1 D-cache + bus switch --
|
||||
-- -------------------------------------------------------------------------------- --
|
||||
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
|
||||
-- Copyright (c) NEORV32 contributors. --
|
||||
-- Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. --
|
||||
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
|
||||
-- SPDX-License-Identifier: BSD-3-Clause --
|
||||
-- ================================================================================ --
|
||||
|
||||
library ieee;
|
||||
use ieee.std_logic_1164.all;
|
||||
|
||||
library neorv32;
|
||||
use neorv32.neorv32_package.all;
|
||||
|
||||
entity neorv32_core_complex is
|
||||
generic (
|
||||
-- General --
|
||||
HART_ID : natural range 0 to 3;
|
||||
NUM_HARTS : natural range 1 to 4;
|
||||
VENDOR_ID : std_ulogic_vector(31 downto 0);
|
||||
BOOT_ADDR : std_ulogic_vector(31 downto 0);
|
||||
DEBUG_PARK_ADDR : std_ulogic_vector(31 downto 0);
|
||||
DEBUG_EXC_ADDR : std_ulogic_vector(31 downto 0);
|
||||
-- RISC-V ISA Extensions --
|
||||
RISCV_ISA_C : boolean;
|
||||
RISCV_ISA_E : boolean;
|
||||
RISCV_ISA_M : boolean;
|
||||
RISCV_ISA_U : boolean;
|
||||
RISCV_ISA_Zaamo : boolean;
|
||||
RISCV_ISA_Zba : boolean;
|
||||
RISCV_ISA_Zbb : boolean;
|
||||
RISCV_ISA_Zbkb : boolean;
|
||||
RISCV_ISA_Zbkc : boolean;
|
||||
RISCV_ISA_Zbkx : boolean;
|
||||
RISCV_ISA_Zbs : boolean;
|
||||
RISCV_ISA_Zfinx : boolean;
|
||||
RISCV_ISA_Zicntr : boolean;
|
||||
RISCV_ISA_Zicond : boolean;
|
||||
RISCV_ISA_Zihpm : boolean;
|
||||
RISCV_ISA_Zknd : boolean;
|
||||
RISCV_ISA_Zkne : boolean;
|
||||
RISCV_ISA_Zknh : boolean;
|
||||
RISCV_ISA_Zksed : boolean;
|
||||
RISCV_ISA_Zksh : boolean;
|
||||
RISCV_ISA_Zmmul : boolean;
|
||||
RISCV_ISA_Zxcfu : boolean;
|
||||
RISCV_ISA_Sdext : boolean;
|
||||
RISCV_ISA_Sdtrig : boolean;
|
||||
RISCV_ISA_Smpmp : boolean;
|
||||
-- Tuning Options --
|
||||
CPU_CLOCK_GATING_EN : boolean;
|
||||
CPU_FAST_MUL_EN : boolean;
|
||||
CPU_FAST_SHIFT_EN : boolean;
|
||||
CPU_RF_HW_RST_EN : boolean;
|
||||
-- Physical Memory Protection (PMP) --
|
||||
PMP_NUM_REGIONS : natural range 0 to 16;
|
||||
PMP_MIN_GRANULARITY : natural;
|
||||
PMP_TOR_MODE_EN : boolean;
|
||||
PMP_NAP_MODE_EN : boolean;
|
||||
-- Hardware Performance Monitors (HPM) --
|
||||
HPM_NUM_CNTS : natural range 0 to 13;
|
||||
HPM_CNT_WIDTH : natural range 0 to 64;
|
||||
-- Instruction Cache (iCACHE) --
|
||||
ICACHE_EN : boolean;
|
||||
ICACHE_NUM_BLOCKS : natural range 1 to 256;
|
||||
ICACHE_BLOCK_SIZE : natural range 4 to 2**16;
|
||||
ICACHE_UC_BEGIN : std_ulogic_vector(31 downto 0);
|
||||
-- Data Cache (dCACHE) --
|
||||
DCACHE_EN : boolean;
|
||||
DCACHE_NUM_BLOCKS : natural range 1 to 256;
|
||||
DCACHE_BLOCK_SIZE : natural range 4 to 2**16;
|
||||
DCACHE_UC_BEGIN : std_ulogic_vector(31 downto 0)
|
||||
);
|
||||
port (
|
||||
-- global control --
|
||||
clk_i : in std_ulogic;
|
||||
rstn_i : in std_ulogic;
|
||||
-- interrupts --
|
||||
msi_i : in std_ulogic;
|
||||
mei_i : in std_ulogic;
|
||||
mti_i : in std_ulogic;
|
||||
firq_i : in std_ulogic_vector(15 downto 0);
|
||||
dbi_i : in std_ulogic;
|
||||
-- inter-core communication links --
|
||||
icc_tx_o : out icc_t; -- TX links
|
||||
icc_rx_i : in icc_t; -- RX links
|
||||
-- system bus interface --
|
||||
bus_req_o : out bus_req_t;
|
||||
bus_rsp_i : in bus_rsp_t
|
||||
);
|
||||
end neorv32_core_complex;
|
||||
|
||||
architecture neorv32_core_complex_rtl of neorv32_core_complex is
|
||||
|
||||
-- bus system --
|
||||
signal cpu_i_req, cpu_d_req, icache_req, dcache_req : bus_req_t;
|
||||
signal cpu_i_rsp, cpu_d_rsp, icache_rsp, dcache_rsp : bus_rsp_t;
|
||||
|
||||
begin
|
||||
|
||||
-- CPU Core -------------------------------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
neorv32_cpu_inst: entity neorv32.neorv32_cpu
|
||||
generic map (
|
||||
-- General --
|
||||
HART_ID => HART_ID,
|
||||
NUM_HARTS => NUM_HARTS,
|
||||
VENDOR_ID => VENDOR_ID,
|
||||
BOOT_ADDR => BOOT_ADDR,
|
||||
DEBUG_PARK_ADDR => DEBUG_PARK_ADDR,
|
||||
DEBUG_EXC_ADDR => DEBUG_EXC_ADDR,
|
||||
-- RISC-V ISA Extensions --
|
||||
RISCV_ISA_C => RISCV_ISA_C,
|
||||
RISCV_ISA_E => RISCV_ISA_E,
|
||||
RISCV_ISA_M => RISCV_ISA_M,
|
||||
RISCV_ISA_U => RISCV_ISA_U,
|
||||
RISCV_ISA_Zaamo => RISCV_ISA_Zaamo,
|
||||
RISCV_ISA_Zba => RISCV_ISA_Zba,
|
||||
RISCV_ISA_Zbb => RISCV_ISA_Zbb,
|
||||
RISCV_ISA_Zbkb => RISCV_ISA_Zbkb,
|
||||
RISCV_ISA_Zbkc => RISCV_ISA_Zbkc,
|
||||
RISCV_ISA_Zbkx => RISCV_ISA_Zbkx,
|
||||
RISCV_ISA_Zbs => RISCV_ISA_Zbs,
|
||||
RISCV_ISA_Zfinx => RISCV_ISA_Zfinx,
|
||||
RISCV_ISA_Zicntr => RISCV_ISA_Zicntr,
|
||||
RISCV_ISA_Zicond => RISCV_ISA_Zicond,
|
||||
RISCV_ISA_Zihpm => RISCV_ISA_Zihpm,
|
||||
RISCV_ISA_Zknd => RISCV_ISA_Zknd,
|
||||
RISCV_ISA_Zkne => RISCV_ISA_Zkne,
|
||||
RISCV_ISA_Zknh => RISCV_ISA_Zknh,
|
||||
RISCV_ISA_Zksed => RISCV_ISA_Zksed,
|
||||
RISCV_ISA_Zksh => RISCV_ISA_Zksh,
|
||||
RISCV_ISA_Zmmul => RISCV_ISA_Zmmul,
|
||||
RISCV_ISA_Zxcfu => RISCV_ISA_Zxcfu,
|
||||
RISCV_ISA_Sdext => RISCV_ISA_Sdext,
|
||||
RISCV_ISA_Sdtrig => RISCV_ISA_Sdtrig,
|
||||
RISCV_ISA_Smpmp => RISCV_ISA_Smpmp,
|
||||
-- Tuning Options --
|
||||
CPU_CLOCK_GATING_EN => CPU_CLOCK_GATING_EN,
|
||||
CPU_FAST_MUL_EN => CPU_FAST_MUL_EN,
|
||||
CPU_FAST_SHIFT_EN => CPU_FAST_SHIFT_EN,
|
||||
CPU_RF_HW_RST_EN => CPU_RF_HW_RST_EN,
|
||||
-- Physical Memory Protection (PMP) --
|
||||
PMP_NUM_REGIONS => PMP_NUM_REGIONS,
|
||||
PMP_MIN_GRANULARITY => PMP_MIN_GRANULARITY,
|
||||
PMP_TOR_MODE_EN => PMP_TOR_MODE_EN,
|
||||
PMP_NAP_MODE_EN => PMP_NAP_MODE_EN,
|
||||
-- Hardware Performance Monitors (HPM) --
|
||||
HPM_NUM_CNTS => HPM_NUM_CNTS,
|
||||
HPM_CNT_WIDTH => HPM_CNT_WIDTH
|
||||
)
|
||||
port map (
|
||||
-- global control --
|
||||
clk_i => clk_i,
|
||||
rstn_i => rstn_i,
|
||||
-- interrupts --
|
||||
msi_i => msi_i,
|
||||
mei_i => mei_i,
|
||||
mti_i => mti_i,
|
||||
firq_i => firq_i,
|
||||
dbi_i => dbi_i,
|
||||
-- inter-core communication links --
|
||||
icc_tx_o => icc_tx_o,
|
||||
icc_rx_i => icc_rx_i,
|
||||
-- instruction bus interface --
|
||||
ibus_req_o => cpu_i_req,
|
||||
ibus_rsp_i => cpu_i_rsp,
|
||||
-- data bus interface --
|
||||
dbus_req_o => cpu_d_req,
|
||||
dbus_rsp_i => cpu_d_rsp
|
||||
);
|
||||
|
||||
|
||||
-- CPU L1 Instruction Cache (I-Cache) -----------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
neorv32_icache_enabled:
|
||||
if ICACHE_EN generate
|
||||
neorv32_icache_inst: entity neorv32.neorv32_cache
|
||||
generic map (
|
||||
NUM_BLOCKS => ICACHE_NUM_BLOCKS,
|
||||
BLOCK_SIZE => ICACHE_BLOCK_SIZE,
|
||||
UC_BEGIN => ICACHE_UC_BEGIN(31 downto 28),
|
||||
UC_ENABLE => true,
|
||||
READ_ONLY => true
|
||||
)
|
||||
port map (
|
||||
clk_i => clk_i,
|
||||
rstn_i => rstn_i,
|
||||
host_req_i => cpu_i_req,
|
||||
host_rsp_o => cpu_i_rsp,
|
||||
bus_req_o => icache_req,
|
||||
bus_rsp_i => icache_rsp
|
||||
);
|
||||
end generate;
|
||||
|
||||
neorv32_icache_disabled:
|
||||
if not ICACHE_EN generate
|
||||
icache_req <= cpu_i_req;
|
||||
cpu_i_rsp <= icache_rsp;
|
||||
end generate;
|
||||
|
||||
|
||||
-- CPU L1 Data Cache (D-Cache) ------------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
neorv32_dcache_enabled:
|
||||
if DCACHE_EN generate
|
||||
neorv32_dcache_inst: entity neorv32.neorv32_cache
|
||||
generic map (
|
||||
NUM_BLOCKS => DCACHE_NUM_BLOCKS,
|
||||
BLOCK_SIZE => DCACHE_BLOCK_SIZE,
|
||||
UC_BEGIN => DCACHE_UC_BEGIN(31 downto 28),
|
||||
UC_ENABLE => true,
|
||||
READ_ONLY => false
|
||||
)
|
||||
port map (
|
||||
clk_i => clk_i,
|
||||
rstn_i => rstn_i,
|
||||
host_req_i => cpu_d_req,
|
||||
host_rsp_o => cpu_d_rsp,
|
||||
bus_req_o => dcache_req,
|
||||
bus_rsp_i => dcache_rsp
|
||||
);
|
||||
end generate;
|
||||
|
||||
neorv32_dcache_disabled:
|
||||
if not DCACHE_EN generate
|
||||
dcache_req <= cpu_d_req;
|
||||
cpu_d_rsp <= dcache_rsp;
|
||||
end generate;
|
||||
|
||||
|
||||
-- Core Instruction/Data Bus Switch -------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
neorv32_core_bus_switch_inst: entity neorv32.neorv32_bus_switch
|
||||
generic map (
|
||||
ROUND_ROBIN_EN => false, -- use prioritizing arbitration
|
||||
PORT_A_READ_ONLY => false,
|
||||
PORT_B_READ_ONLY => true -- instruction fetch is read-only
|
||||
)
|
||||
port map (
|
||||
clk_i => clk_i,
|
||||
rstn_i => rstn_i,
|
||||
a_lock_i => '0', -- no exclusive accesses
|
||||
a_req_i => dcache_req, -- data accesses are prioritized
|
||||
a_rsp_o => dcache_rsp,
|
||||
b_req_i => icache_req,
|
||||
b_rsp_o => icache_rsp,
|
||||
x_req_o => bus_req_o,
|
||||
x_rsp_i => bus_rsp_i
|
||||
);
|
||||
|
||||
|
||||
end neorv32_core_complex_rtl;
|
|
@ -22,12 +22,12 @@ use neorv32.neorv32_package.all;
|
|||
entity neorv32_cpu is
|
||||
generic (
|
||||
-- General --
|
||||
HART_ID : natural range 0 to 3; -- hardware thread ID
|
||||
NUM_HARTS : natural range 1 to 4; -- total number of harts in the system, has to be a power of 2
|
||||
HART_ID : natural range 0 to 1023; -- hardware thread ID
|
||||
VENDOR_ID : std_ulogic_vector(31 downto 0); -- vendor's JEDEC ID
|
||||
BOOT_ADDR : std_ulogic_vector(31 downto 0); -- cpu boot address
|
||||
DEBUG_PARK_ADDR : std_ulogic_vector(31 downto 0); -- cpu debug mode parking loop entry address
|
||||
DEBUG_EXC_ADDR : std_ulogic_vector(31 downto 0); -- cpu debug mode exception entry address
|
||||
ICC_EN : boolean; -- implement inter-core communication (ICC) links
|
||||
-- RISC-V ISA Extensions --
|
||||
RISCV_ISA_C : boolean; -- implement compressed extension
|
||||
RISCV_ISA_E : boolean; -- implement embedded RF extension
|
||||
|
@ -136,56 +136,57 @@ begin
|
|||
|
||||
-- Configuration Info and Sanity Checks ---------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
-- CPU ISA configuration (in alphabetical order - not in canonical order!) --
|
||||
assert false report "[NEORV32] CPU ISA: rv32" &
|
||||
cond_sel_string_f(RISCV_ISA_E, "e", "i") &
|
||||
cond_sel_string_f(riscv_b_c, "b", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_C, "c", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_M, "m", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_U, "u", "" ) &
|
||||
cond_sel_string_f(true, "x", "" ) & -- always enabled
|
||||
cond_sel_string_f(RISCV_ISA_Zaamo, "_zaamo", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zba, "_zba", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zbb, "_zbb", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zbkb, "_zbkb", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zbkc, "_zbkc", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zbkx, "_zbkx", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zbs, "_zbs", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zicntr, "_zicntr", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zicond, "_zicond", "" ) &
|
||||
cond_sel_string_f(true, "_zicsr", "" ) & -- always enabled
|
||||
cond_sel_string_f(true, "_zifencei", "" ) & -- always enabled
|
||||
cond_sel_string_f(RISCV_ISA_Zihpm, "_zihpm", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zfinx, "_zfinx", "" ) &
|
||||
cond_sel_string_f(riscv_zkn_c, "_zkn", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zknd, "_zknd", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zkne, "_zkne", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zknh, "_zknh", "" ) &
|
||||
cond_sel_string_f(riscv_zks_c, "_zks", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zksed, "_zksed", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zksh, "_zksh", "" ) &
|
||||
cond_sel_string_f(riscv_zkt_c, "_zkt", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zmmul, "_zmmul", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zxcfu, "_zxcfu", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Sdext, "_sdext", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Sdtrig, "_sdtrig", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Smpmp, "_smpmp", "" )
|
||||
severity note;
|
||||
hello_neorv32:
|
||||
if HART_ID = 0 generate -- print only for core 0
|
||||
|
||||
-- CPU tuning options --
|
||||
assert false report "[NEORV32] CPU tuning options: " &
|
||||
cond_sel_string_f(CPU_CLOCK_GATING_EN, "clock_gating ", "") &
|
||||
cond_sel_string_f(CPU_FAST_MUL_EN, "fast_mul ", "") &
|
||||
cond_sel_string_f(CPU_FAST_SHIFT_EN, "fast_shift ", "") &
|
||||
cond_sel_string_f(CPU_RF_HW_RST_EN, "rf_hw_rst ", "")
|
||||
severity note;
|
||||
-- CPU ISA configuration (in alphabetical order - not in canonical order!) --
|
||||
assert false report "[NEORV32] CPU ISA: rv32" &
|
||||
cond_sel_string_f(RISCV_ISA_E, "e", "i") &
|
||||
cond_sel_string_f(riscv_b_c, "b", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_C, "c", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_M, "m", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_U, "u", "" ) &
|
||||
cond_sel_string_f(true, "x", "" ) & -- always enabled
|
||||
cond_sel_string_f(RISCV_ISA_Zaamo, "_zaamo", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zba, "_zba", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zbb, "_zbb", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zbkb, "_zbkb", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zbkc, "_zbkc", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zbkx, "_zbkx", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zbs, "_zbs", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zicntr, "_zicntr", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zicond, "_zicond", "" ) &
|
||||
cond_sel_string_f(true, "_zicsr", "" ) & -- always enabled
|
||||
cond_sel_string_f(true, "_zifencei", "" ) & -- always enabled
|
||||
cond_sel_string_f(RISCV_ISA_Zihpm, "_zihpm", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zfinx, "_zfinx", "" ) &
|
||||
cond_sel_string_f(riscv_zkn_c, "_zkn", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zknd, "_zknd", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zkne, "_zkne", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zknh, "_zknh", "" ) &
|
||||
cond_sel_string_f(riscv_zks_c, "_zks", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zksed, "_zksed", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zksh, "_zksh", "" ) &
|
||||
cond_sel_string_f(riscv_zkt_c, "_zkt", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zmmul, "_zmmul", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Zxcfu, "_zxcfu", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Sdext, "_sdext", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Sdtrig, "_sdtrig", "" ) &
|
||||
cond_sel_string_f(RISCV_ISA_Smpmp, "_smpmp", "" )
|
||||
severity note;
|
||||
|
||||
-- simulation notifier --
|
||||
assert not is_simulation_c report "[NEORV32] Assuming this is a simulation." severity warning;
|
||||
-- CPU tuning options --
|
||||
assert false report "[NEORV32] CPU tuning options: " &
|
||||
cond_sel_string_f(CPU_CLOCK_GATING_EN, "clock_gating ", "") &
|
||||
cond_sel_string_f(CPU_FAST_MUL_EN, "fast_mul ", "") &
|
||||
cond_sel_string_f(CPU_FAST_SHIFT_EN, "fast_shift ", "") &
|
||||
cond_sel_string_f(CPU_RF_HW_RST_EN, "rf_hw_rst ", "")
|
||||
severity note;
|
||||
|
||||
-- ID checks --
|
||||
assert is_power_of_two_f(NUM_HARTS) report "[NEORV32] NUM_HARTS has to be a power of two." severity error;
|
||||
assert (HART_ID < NUM_HARTS) report "[NEORV32] HART_ID out of range." severity error;
|
||||
-- simulation notifier --
|
||||
assert not is_simulation_c report "[NEORV32] Assuming this is a simulation." severity warning;
|
||||
|
||||
end generate;
|
||||
|
||||
|
||||
-- Clock Gating ---------------------------------------------------------------------------
|
||||
|
@ -438,12 +439,8 @@ begin
|
|||
-- Inter-Core Communication (ICC) ---------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
icc_enabled:
|
||||
if NUM_HARTS > 1 generate
|
||||
if ICC_EN generate
|
||||
neorv32_cpu_icc_inst: entity neorv32.neorv32_cpu_icc
|
||||
generic map (
|
||||
HART_ID => HART_ID, -- ID of this core
|
||||
NUM_HARTS => NUM_HARTS -- number of cores, has to be a power of two
|
||||
)
|
||||
port map (
|
||||
-- global control --
|
||||
clk_i => clk_i, -- global clock, rising edge
|
||||
|
@ -461,7 +458,7 @@ begin
|
|||
end generate;
|
||||
|
||||
icc_disabled:
|
||||
if NUM_HARTS = 1 generate
|
||||
if not ICC_EN generate
|
||||
xcsr_rdata_icc <= (others => '0');
|
||||
icc_tx_o <= icc_terminate_c;
|
||||
end generate;
|
||||
|
|
|
@ -29,7 +29,7 @@ use neorv32.neorv32_package.all;
|
|||
entity neorv32_cpu_control is
|
||||
generic (
|
||||
-- General --
|
||||
HART_ID : natural range 0 to 3; -- hardware thread ID
|
||||
HART_ID : natural range 0 to 1023; -- hardware thread ID
|
||||
VENDOR_ID : std_ulogic_vector(31 downto 0); -- vendor's JEDEC ID
|
||||
BOOT_ADDR : std_ulogic_vector(31 downto 0); -- cpu boot address
|
||||
DEBUG_PARK_ADDR : std_ulogic_vector(31 downto 0); -- cpu debug-mode parking loop entry address, 4-byte aligned
|
||||
|
@ -210,6 +210,7 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
|
|||
type csr_t is record
|
||||
addr : std_ulogic_vector(11 downto 0); -- physical access address
|
||||
we, we_nxt : std_ulogic; -- write enable
|
||||
re, re_nxt : std_ulogic; -- read enable
|
||||
operand : std_ulogic_vector(XLEN-1 downto 0); -- write operand
|
||||
wdata : std_ulogic_vector(XLEN-1 downto 0); -- write data
|
||||
rdata : std_ulogic_vector(XLEN-1 downto 0); -- read data
|
||||
|
@ -225,8 +226,8 @@ architecture neorv32_cpu_control_rtl of neorv32_cpu_control is
|
|||
mie_mti : std_ulogic; -- machine timer interrupt enable
|
||||
mie_firq : std_ulogic_vector(15 downto 0); -- fast interrupt enable
|
||||
--
|
||||
privilege : std_ulogic; -- current privilege mode
|
||||
privilege_eff : std_ulogic; -- current *effective* privilege mode
|
||||
prv_level : std_ulogic; -- current privilege level
|
||||
prv_level_eff : std_ulogic; -- current *effective* privilege level
|
||||
--
|
||||
mepc : std_ulogic_vector(XLEN-1 downto 0); -- machine exception PC
|
||||
mcause : std_ulogic_vector(5 downto 0); -- machine trap cause
|
||||
|
@ -338,7 +339,7 @@ begin
|
|||
-- ------------------------------------------------------------
|
||||
fetch_engine.restart <= '0'; -- restart done
|
||||
fetch_engine.pc <= exe_engine.pc2(XLEN-1 downto 1) & '0'; -- initialize from PC incl. 16-bit-alignment bit
|
||||
fetch_engine.priv <= csr.privilege_eff; -- set new privilege level
|
||||
fetch_engine.priv <= csr.prv_level_eff; -- set new privilege level
|
||||
fetch_engine.state <= IF_REQUEST;
|
||||
|
||||
end case;
|
||||
|
@ -371,7 +372,7 @@ begin
|
|||
ibus_req_o.src <= '1'; -- source = instruction fetch
|
||||
ibus_req_o.amo <= '0'; -- cannot be an atomic memory operation
|
||||
ibus_req_o.amoop <= (others => '0'); -- cannot be an atomic memory operation
|
||||
ibus_req_o.fence <= ctrl.lsu_fence; -- fence operation, valid without STB being set
|
||||
ibus_req_o.fence <= ctrl.if_fence; -- fence operation, valid without STB being set
|
||||
ibus_req_o.sleep <= sleep_mode; -- sleep mode, valid without STB being set
|
||||
ibus_req_o.debug <= debug_ctrl.run; -- debug mode, valid without STB being set
|
||||
|
||||
|
@ -598,6 +599,7 @@ begin
|
|||
trap_ctrl.ebreak <= '0';
|
||||
trap_ctrl.hwtrig <= '0';
|
||||
csr.we_nxt <= '0';
|
||||
csr.re_nxt <= '0';
|
||||
ctrl_nxt <= ctrl_bus_zero_c; -- all zero/off by default (ALU operation = ZERO, ALU.adder_out = ADD)
|
||||
|
||||
-- ALU sign control --
|
||||
|
@ -751,7 +753,8 @@ begin
|
|||
|
||||
-- memory fence operations (execute even if illegal funct3) --
|
||||
when opcode_fence_c =>
|
||||
ctrl_nxt.lsu_fence <= '1'; -- [NOTE] fence == fence.i; ignore all ordering bits
|
||||
ctrl_nxt.if_fence <= exe_engine.ir(instr_funct3_lsb_c); -- fence.i
|
||||
ctrl_nxt.lsu_fence <= not exe_engine.ir(instr_funct3_lsb_c); -- fence
|
||||
exe_engine_nxt.state <= EX_RESTART; -- reset instruction fetch + IPB (actually only required for fence.i)
|
||||
|
||||
-- FPU: floating-point operations --
|
||||
|
@ -766,6 +769,11 @@ begin
|
|||
|
||||
-- environment/CSR operation or ILLEGAL opcode --
|
||||
when others =>
|
||||
if ((funct3_v = funct3_csrrw_c) or (funct3_v = funct3_csrrwi_c)) and (exe_engine.ir(instr_rd_msb_c downto instr_rd_lsb_c) = "00000") then
|
||||
csr.re_nxt <= '0'; -- no read if CSRRW[I] and rd = 0
|
||||
else
|
||||
csr.re_nxt <= '1';
|
||||
end if;
|
||||
exe_engine_nxt.state <= EX_SYSTEM;
|
||||
|
||||
end case; -- /EX_EXECUTE
|
||||
|
@ -808,9 +816,7 @@ begin
|
|||
(trap_ctrl.exc_buf(exc_saccess_c) = '1') or (trap_ctrl.exc_buf(exc_laccess_c) = '1') or -- access exception
|
||||
(trap_ctrl.exc_buf(exc_salign_c) = '1') or (trap_ctrl.exc_buf(exc_lalign_c) = '1') or -- alignment exception
|
||||
(trap_ctrl.exc_buf(exc_illegal_c) = '1') then -- illegal instruction exception
|
||||
if (RISCV_ISA_Zaamo and (opcode(2) = opcode_amo_c(2))) or (opcode(5) = '0') then -- atomic operation / normal load
|
||||
ctrl_nxt.rf_wb_en <= '1'; -- allow write-back to register file (won't happen in case of exception)
|
||||
end if;
|
||||
ctrl_nxt.rf_wb_en <= not ctrl.lsu_rw; -- write-back to register file if read operation (won't happen in case of exception)
|
||||
exe_engine_nxt.state <= EX_DISPATCH;
|
||||
end if;
|
||||
|
||||
|
@ -836,7 +842,7 @@ begin
|
|||
if (funct3_v = funct3_csrrw_c) or (funct3_v = funct3_csrrwi_c) or (exe_engine.ir(instr_rs1_msb_c downto instr_rs1_lsb_c) /= "00000") then
|
||||
csr.we_nxt <= '1'; -- CSRRW[I]: always write CSR; CSRR[S/C][I]: write CSR if rs1/imm5 is NOT zero
|
||||
end if;
|
||||
-- always write to RF; ENVIRONMENT operations have rd = zero so this does not hurt --
|
||||
-- always write to RF (even if csr.re = 0, but then we have rd = 0); ENVIRONMENT operations have rd = zero so this does not hurt --
|
||||
ctrl_nxt.rf_wb_en <= '1'; -- won't happen if exception
|
||||
|
||||
end case;
|
||||
|
@ -846,6 +852,8 @@ begin
|
|||
-- CPU Control Bus Output -----------------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
|
||||
-- instruction fetch --
|
||||
ctrl_o.if_fence <= ctrl.if_fence;
|
||||
-- register file --
|
||||
ctrl_o.rf_wb_en <= ctrl.rf_wb_en and (not trap_ctrl.exc_fire); -- inhibit write-back if exception
|
||||
ctrl_o.rf_rs1 <= exe_engine.ir(instr_rs1_msb_c downto instr_rs1_lsb_c);
|
||||
|
@ -866,13 +874,13 @@ begin
|
|||
ctrl_o.lsu_rw <= ctrl.lsu_rw;
|
||||
ctrl_o.lsu_mo_we <= '1' when (exe_engine.state = EX_MEM_REQ) else '0'; -- write memory output registers (data & address)
|
||||
ctrl_o.lsu_fence <= ctrl.lsu_fence;
|
||||
ctrl_o.lsu_priv <= csr.mstatus_mpp when (csr.mstatus_mprv = '1') else csr.privilege_eff; -- effective privilege level for loads/stores in M-mode
|
||||
ctrl_o.lsu_priv <= csr.mstatus_mpp when (csr.mstatus_mprv = '1') else csr.prv_level_eff; -- effective privilege level for loads/stores in M-mode
|
||||
-- instruction word bit fields --
|
||||
ctrl_o.ir_funct3 <= exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c);
|
||||
ctrl_o.ir_funct12 <= exe_engine.ir(instr_funct12_msb_c downto instr_funct12_lsb_c);
|
||||
ctrl_o.ir_opcode <= opcode;
|
||||
-- cpu status --
|
||||
ctrl_o.cpu_priv <= csr.privilege_eff;
|
||||
ctrl_o.cpu_priv <= csr.prv_level_eff;
|
||||
ctrl_o.cpu_sleep <= sleep_mode;
|
||||
ctrl_o.cpu_trap <= trap_ctrl.env_enter;
|
||||
ctrl_o.cpu_debug <= debug_ctrl.run;
|
||||
|
@ -923,10 +931,10 @@ begin
|
|||
csr_valid(2) <= bool_to_ulogic_f(RISCV_ISA_Zfinx); -- available if FPU implemented
|
||||
|
||||
-- machine trap setup/handling, environment/information registers, etc. --
|
||||
when csr_mstatus_c | csr_mstatush_c | csr_misa_c | csr_mie_c | csr_mtvec_c | csr_mscratch_c |
|
||||
csr_mepc_c | csr_mcause_c | csr_mip_c | csr_mtval_c | csr_mtinst_c | csr_mcountinhibit_c |
|
||||
csr_mvendorid_c | csr_marchid_c | csr_mimpid_c | csr_mhartid_c | csr_mconfigptr_c | csr_mxisa_c |
|
||||
csr_mxiccrxd_c | csr_mxicctxd_c | csr_mxiccsr0_c | csr_mxiccsr1_c =>
|
||||
when csr_mstatus_c | csr_mstatush_c | csr_misa_c | csr_mie_c | csr_mtvec_c |
|
||||
csr_mscratch_c | csr_mepc_c | csr_mcause_c | csr_mip_c | csr_mtval_c |
|
||||
csr_mtinst_c | csr_mcountinhibit_c | csr_mvendorid_c | csr_marchid_c | csr_mimpid_c |
|
||||
csr_mhartid_c | csr_mconfigptr_c | csr_mxisa_c | csr_mxiccsreg_c | csr_mxiccdata_c =>
|
||||
csr_valid(2) <= '1'; -- always implemented
|
||||
|
||||
-- machine-controlled user-mode CSRs --
|
||||
|
@ -989,12 +997,12 @@ begin
|
|||
if (csr_addr_v(11 downto 4) = csr_dcsr_c(11 downto 4)) and -- debug-mode-only CSR?
|
||||
RISCV_ISA_Sdext and (debug_ctrl.run = '0') then -- debug-mode implemented and not running?
|
||||
csr_valid(0) <= '0'; -- invalid access
|
||||
elsif RISCV_ISA_Zicntr and RISCV_ISA_U and (csr.privilege_eff = '0') and -- any user-mode counters available and in user-mode?
|
||||
elsif RISCV_ISA_Zicntr and RISCV_ISA_U and (csr.prv_level_eff = '0') and -- any user-mode counters available and in user-mode?
|
||||
(csr_addr_v(11 downto 8) = csr_cycle_c(11 downto 8)) and -- user-mode counter access
|
||||
(((csr_addr_v(1 downto 0) = csr_cycle_c(1 downto 0)) and (csr.mcounteren_cy = '0')) or -- illegal access to cycle
|
||||
((csr_addr_v(1 downto 0) = csr_instret_c(1 downto 0)) and (csr.mcounteren_ir = '0'))) then -- illegal access to instret
|
||||
csr_valid(0) <= '0'; -- invalid access
|
||||
elsif (csr_addr_v(9 downto 8) /= "00") and (csr.privilege_eff = '0') then -- invalid privilege level
|
||||
elsif (csr_addr_v(9 downto 8) /= "00") and (csr.prv_level_eff = '0') then -- invalid privilege level
|
||||
csr_valid(0) <= '0'; -- invalid access
|
||||
else
|
||||
csr_valid(0) <= '1'; -- access granted
|
||||
|
@ -1037,7 +1045,7 @@ begin
|
|||
end case;
|
||||
|
||||
when opcode_amo_c => -- atomic memory operation
|
||||
if RISCV_ISA_Zaamo and (exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "010") then
|
||||
if RISCV_ISA_Zaamo and (exe_engine.ir(instr_funct3_msb_c downto instr_funct3_lsb_c) = "010") then -- word-quantity only
|
||||
case exe_engine.ir(instr_funct5_msb_c downto instr_funct5_lsb_c) is
|
||||
when "00001" | "00000" | "00100" | "01100" | "01000" | "10000" | "10100" | "11000" | "11100" => illegal_cmd <= '0';
|
||||
when others => illegal_cmd <= '1';
|
||||
|
@ -1058,9 +1066,9 @@ begin
|
|||
case exe_engine.ir(instr_funct12_msb_c downto instr_funct12_lsb_c) is
|
||||
when funct12_ecall_c => illegal_cmd <= '0'; -- ecall is always allowed
|
||||
when funct12_ebreak_c => illegal_cmd <= '0'; -- ebreak is always allowed
|
||||
when funct12_mret_c => illegal_cmd <= (not csr.privilege) or debug_ctrl.run; -- mret allowed in (real/non-debug) M-mode only
|
||||
when funct12_mret_c => illegal_cmd <= (not csr.prv_level) or debug_ctrl.run; -- mret allowed in (real/non-debug) M-mode only
|
||||
when funct12_dret_c => illegal_cmd <= not debug_ctrl.run; -- dret allowed in debug mode only
|
||||
when funct12_wfi_c => illegal_cmd <= (not csr.privilege) and csr.mstatus_tw; -- wfi allowed in M-mode or if TW is zero
|
||||
when funct12_wfi_c => illegal_cmd <= (not csr.prv_level) and csr.mstatus_tw; -- wfi allowed in M-mode or if TW is zero
|
||||
when others => illegal_cmd <= '1'; -- undefined
|
||||
end case;
|
||||
end if;
|
||||
|
@ -1118,8 +1126,8 @@ begin
|
|||
if RISCV_ISA_Sdext then
|
||||
trap_ctrl.exc_buf(exc_ebreak_c) <= (not trap_ctrl.env_enter) and (trap_ctrl.exc_buf(exc_ebreak_c) or
|
||||
(trap_ctrl.hwtrig and (not csr.tdata1_action)) or -- trigger module fires and enter-debug-action is disabled
|
||||
(trap_ctrl.ebreak and ( csr.privilege) and (not csr.dcsr_ebreakm) and (not debug_ctrl.run)) or -- enter M-mode handler on ebreak in M-mode
|
||||
(trap_ctrl.ebreak and (not csr.privilege) and (not csr.dcsr_ebreaku) and (not debug_ctrl.run))); -- enter M-mode handler on ebreak in U-mode
|
||||
(trap_ctrl.ebreak and ( csr.prv_level) and (not csr.dcsr_ebreakm) and (not debug_ctrl.run)) or -- enter M-mode handler on ebreak in M-mode
|
||||
(trap_ctrl.ebreak and (not csr.prv_level) and (not csr.dcsr_ebreaku) and (not debug_ctrl.run))); -- enter M-mode handler on ebreak in U-mode
|
||||
else
|
||||
trap_ctrl.exc_buf(exc_ebreak_c) <= (trap_ctrl.exc_buf(exc_ebreak_c) or trap_ctrl.ebreak or (trap_ctrl.hwtrig and (not csr.tdata1_action))) and (not trap_ctrl.env_enter);
|
||||
end if;
|
||||
|
@ -1192,7 +1200,7 @@ begin
|
|||
if (trap_ctrl.exc_buf(exc_iaccess_c) = '1') then trap_ctrl.cause <= trap_iaf_c; -- instruction access fault
|
||||
elsif (trap_ctrl.exc_buf(exc_illegal_c) = '1') then trap_ctrl.cause <= trap_iil_c; -- illegal instruction
|
||||
elsif (trap_ctrl.exc_buf(exc_ialign_c) = '1') then trap_ctrl.cause <= trap_ima_c; -- instruction address misaligned
|
||||
elsif (trap_ctrl.exc_buf(exc_ecall_c) = '1') then trap_ctrl.cause <= trap_env_c(6 downto 2) & replicate_f(csr.privilege, 2); -- environment call (U/M)
|
||||
elsif (trap_ctrl.exc_buf(exc_ecall_c) = '1') then trap_ctrl.cause <= trap_env_c(6 downto 2) & replicate_f(csr.prv_level, 2); -- environment call (U/M)
|
||||
elsif (trap_ctrl.exc_buf(exc_ebreak_c) = '1') then trap_ctrl.cause <= trap_brk_c; -- environment breakpoint
|
||||
elsif (trap_ctrl.exc_buf(exc_salign_c) = '1') then trap_ctrl.cause <= trap_sma_c; -- store address misaligned
|
||||
elsif (trap_ctrl.exc_buf(exc_lalign_c) = '1') then trap_ctrl.cause <= trap_lma_c; -- load address misaligned
|
||||
|
@ -1266,7 +1274,7 @@ begin
|
|||
trap_ctrl.irq_fire(0) <= '1' when
|
||||
(exe_engine.state = EX_EXECUTE) and -- trigger system IRQ only in EX_EXECUTE state
|
||||
(or_reduce_f(trap_ctrl.irq_buf(irq_firq_15_c downto irq_msi_irq_c)) = '1') and -- pending system IRQ
|
||||
((csr.mstatus_mie = '1') or (csr.privilege = priv_mode_u_c)) and -- IRQ only when in M-mode and MIE=1 OR when in U-mode
|
||||
((csr.mstatus_mie = '1') or (csr.prv_level = priv_mode_u_c)) and -- IRQ only when in M-mode and MIE=1 OR when in U-mode
|
||||
(debug_ctrl.run = '0') and (csr.dcsr_step = '0') else '0'; -- no system IRQs when in debug-mode / during single-stepping
|
||||
|
||||
-- debug-entry halt interrupt? --
|
||||
|
@ -1334,7 +1342,7 @@ begin
|
|||
-- External CSR Interface -----------------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
xcsr_we_o <= csr.we;
|
||||
xcsr_re_o <= '1' when (exe_engine.state = EX_SYSTEM) else '0';
|
||||
xcsr_re_o <= csr.re;
|
||||
xcsr_addr_o <= csr.addr;
|
||||
xcsr_wdata_o <= csr.wdata;
|
||||
|
||||
|
@ -1345,7 +1353,7 @@ begin
|
|||
begin
|
||||
if (rstn_i = '0') then
|
||||
csr.we <= '0';
|
||||
csr.privilege <= priv_mode_m_c;
|
||||
csr.prv_level <= priv_mode_m_c;
|
||||
csr.mstatus_mie <= '0';
|
||||
csr.mstatus_mpie <= '0';
|
||||
csr.mstatus_mpp <= priv_mode_m_c;
|
||||
|
@ -1356,8 +1364,8 @@ begin
|
|||
csr.mie_mti <= '0';
|
||||
csr.mie_firq <= (others => '0');
|
||||
csr.mtvec <= (others => '0');
|
||||
csr.mscratch <= x"19880704";
|
||||
csr.mepc <= BOOT_ADDR(XLEN-1 downto 2) & "00"; -- 32-bit-aligned boot address
|
||||
csr.mscratch <= (others => '0');
|
||||
csr.mepc <= (others => '0');
|
||||
csr.mcause <= (others => '0');
|
||||
csr.mtval <= (others => '0');
|
||||
csr.mtinst <= (others => '0');
|
||||
|
@ -1530,16 +1538,16 @@ begin
|
|||
csr.mtinst <= (others => '0');
|
||||
end if;
|
||||
-- update privilege level and interrupt-enable stack --
|
||||
csr.privilege <= priv_mode_m_c; -- execute trap in machine mode
|
||||
csr.prv_level <= priv_mode_m_c; -- execute trap in machine mode
|
||||
csr.mstatus_mie <= '0'; -- disable interrupts
|
||||
csr.mstatus_mpie <= csr.mstatus_mie; -- backup previous mie state
|
||||
csr.mstatus_mpp <= csr.privilege; -- backup previous privilege mode
|
||||
csr.mstatus_mpp <= csr.prv_level; -- backup previous privilege level
|
||||
end if;
|
||||
|
||||
-- DEBUG trap entry - no CSR update when already in debug-mode! --
|
||||
if RISCV_ISA_Sdext and (trap_ctrl.cause(5) = '1') and (debug_ctrl.run = '0') then
|
||||
csr.dcsr_cause <= trap_ctrl.cause(2 downto 0); -- trap cause
|
||||
csr.dcsr_prv <= csr.privilege; -- current privilege mode when debug mode was entered
|
||||
csr.dcsr_prv <= csr.prv_level; -- current privilege level when debug mode was entered
|
||||
csr.dpc <= trap_ctrl.epc(XLEN-1 downto 1) & '0'; -- trap PC
|
||||
end if;
|
||||
|
||||
|
@ -1551,18 +1559,18 @@ begin
|
|||
-- return from debug mode --
|
||||
if RISCV_ISA_Sdext and (debug_ctrl.run = '1') then
|
||||
if RISCV_ISA_U then
|
||||
csr.privilege <= csr.dcsr_prv;
|
||||
csr.prv_level <= csr.dcsr_prv;
|
||||
if (csr.dcsr_prv /= priv_mode_m_c) then
|
||||
csr.mstatus_mprv <= '0'; -- clear if return to priv. mode less than M
|
||||
csr.mstatus_mprv <= '0'; -- clear if return to priv. level less than M
|
||||
end if;
|
||||
end if;
|
||||
-- return from normal trap --
|
||||
else
|
||||
if RISCV_ISA_U then
|
||||
csr.privilege <= csr.mstatus_mpp; -- restore previous privilege mode
|
||||
csr.mstatus_mpp <= priv_mode_u_c; -- set to least-privileged mode that is supported
|
||||
csr.prv_level <= csr.mstatus_mpp; -- restore previous privilege level
|
||||
csr.mstatus_mpp <= priv_mode_u_c; -- set to least-privileged level that is supported
|
||||
if (csr.mstatus_mpp /= priv_mode_m_c) then
|
||||
csr.mstatus_mprv <= '0'; -- clear if return to priv. mode less than M
|
||||
csr.mstatus_mprv <= '0'; -- clear if return to priv. level less than M
|
||||
end if;
|
||||
end if;
|
||||
csr.mstatus_mie <= csr.mstatus_mpie; -- restore machine-mode IRQ enable flag
|
||||
|
@ -1596,7 +1604,7 @@ begin
|
|||
|
||||
-- no user mode --
|
||||
if not RISCV_ISA_U then
|
||||
csr.privilege <= priv_mode_m_c;
|
||||
csr.prv_level <= priv_mode_m_c;
|
||||
csr.mstatus_mpp <= priv_mode_m_c;
|
||||
csr.mstatus_mprv <= '0';
|
||||
csr.mstatus_tw <= '0';
|
||||
|
@ -1628,8 +1636,8 @@ begin
|
|||
end if;
|
||||
end process csr_write_access;
|
||||
|
||||
-- effective privilege mode is MACHINE when in debug mode --
|
||||
csr.privilege_eff <= priv_mode_m_c when (debug_ctrl.run = '1') else csr.privilege;
|
||||
-- effective privilege level is MACHINE when in debug mode --
|
||||
csr.prv_level_eff <= priv_mode_m_c when (debug_ctrl.run = '1') else csr.prv_level;
|
||||
|
||||
|
||||
-- CSR Read Access ------------------------------------------------------------------------
|
||||
|
@ -1637,10 +1645,12 @@ begin
|
|||
csr_read_access: process(rstn_i, clk_i)
|
||||
begin
|
||||
if (rstn_i = '0') then
|
||||
csr.re <= '0';
|
||||
csr.rdata <= (others => '0');
|
||||
elsif rising_edge(clk_i) then
|
||||
csr.re <= csr.re_nxt and (not trap_ctrl.exc_buf(exc_illegal_c)); -- read if not an illegal instruction
|
||||
csr.rdata <= (others => '0'); -- default; output all-zero if there is no explicit CSR read operation
|
||||
if (exe_engine.state = EX_SYSTEM) then -- always read from CSR file in EX_SYSTEM state
|
||||
if (csr.re = '1') then
|
||||
case csr.addr is -- address is zero if there is no CSR operation
|
||||
|
||||
-- --------------------------------------------------------------------
|
||||
|
@ -1662,7 +1672,7 @@ begin
|
|||
-- --------------------------------------------------------------------
|
||||
-- inter-core communication
|
||||
-- --------------------------------------------------------------------
|
||||
when csr_mxiccrxd_c | csr_mxicctxd_c | csr_mxiccsr0_c | csr_mxiccsr1_c =>
|
||||
when csr_mxiccsreg_c | csr_mxiccdata_c =>
|
||||
csr.rdata <= xcsr_rdata_i; -- implemented externally
|
||||
|
||||
-- --------------------------------------------------------------------
|
||||
|
@ -1678,13 +1688,13 @@ begin
|
|||
-- when csr_mstatush_c => csr.rdata <= (others => '0'); -- machine status register, high word - hardwired to zero
|
||||
|
||||
when csr_misa_c => -- ISA and extensions
|
||||
csr.rdata(1) <= bool_to_ulogic_f(RISCV_ISA_B); -- B CPU extension
|
||||
csr.rdata(2) <= bool_to_ulogic_f(RISCV_ISA_C); -- C CPU extension
|
||||
csr.rdata(4) <= bool_to_ulogic_f(RISCV_ISA_E); -- E CPU extension
|
||||
csr.rdata(8) <= bool_to_ulogic_f(not RISCV_ISA_E); -- I CPU extension (if not E)
|
||||
csr.rdata(12) <= bool_to_ulogic_f(RISCV_ISA_M); -- M CPU extension
|
||||
csr.rdata(20) <= bool_to_ulogic_f(RISCV_ISA_U); -- U CPU extension
|
||||
csr.rdata(23) <= '1'; -- X CPU extension (non-standard / NEORV32-specific)
|
||||
csr.rdata(1) <= bool_to_ulogic_f(RISCV_ISA_B);
|
||||
csr.rdata(2) <= bool_to_ulogic_f(RISCV_ISA_C);
|
||||
csr.rdata(4) <= bool_to_ulogic_f(RISCV_ISA_E);
|
||||
csr.rdata(8) <= bool_to_ulogic_f(not RISCV_ISA_E); -- I = not E
|
||||
csr.rdata(12) <= bool_to_ulogic_f(RISCV_ISA_M);
|
||||
csr.rdata(20) <= bool_to_ulogic_f(RISCV_ISA_U);
|
||||
csr.rdata(23) <= '1'; -- X CPU extension (non-standard / NEORV32-specific)
|
||||
csr.rdata(31 downto 30) <= "01"; -- MXL = 32
|
||||
|
||||
when csr_mie_c => -- machine interrupt-enable register
|
||||
|
@ -1868,7 +1878,6 @@ begin
|
|||
csr.rdata(24) <= bool_to_ulogic_f(RISCV_ISA_Zbs); -- Zbs: single-bit bit-manipulation
|
||||
csr.rdata(25) <= bool_to_ulogic_f(RISCV_ISA_Zaamo); -- Zaamo: atomic memory operations
|
||||
csr.rdata(26) <= '0'; -- reserved
|
||||
csr.rdata(27) <= '0'; -- reserved
|
||||
-- tuning options --
|
||||
csr.rdata(27) <= bool_to_ulogic_f(CPU_CLOCK_GATING_EN); -- enable clock gating when in sleep mode
|
||||
csr.rdata(28) <= bool_to_ulogic_f(CPU_RF_HW_RST_EN); -- full hardware reset of register file
|
||||
|
@ -2066,8 +2075,8 @@ begin
|
|||
-- debug mode entry triggers --
|
||||
debug_ctrl.trig_hw <= trap_ctrl.hwtrig and (not debug_ctrl.run) and csr.tdata1_action and csr.tdata1_dmode; -- enter debug mode by HW trigger module
|
||||
debug_ctrl.trig_break <= trap_ctrl.ebreak and (debug_ctrl.run or -- re-enter debug mode
|
||||
(( csr.privilege) and csr.dcsr_ebreakm) or -- enabled goto-debug-mode in machine mode on "ebreak"
|
||||
((not csr.privilege) and csr.dcsr_ebreaku)); -- enabled goto-debug-mode in user mode on "ebreak"
|
||||
(( csr.prv_level) and csr.dcsr_ebreakm) or -- enabled goto-debug-mode in machine mode on "ebreak"
|
||||
((not csr.prv_level) and csr.dcsr_ebreaku)); -- enabled goto-debug-mode in user mode on "ebreak"
|
||||
debug_ctrl.trig_halt <= irq_dbg_i and (not debug_ctrl.run); -- external halt request (if not halted already)
|
||||
debug_ctrl.trig_step <= csr.dcsr_step and (not debug_ctrl.run); -- single-step mode (trigger when NOT CURRENTLY in debug mode)
|
||||
|
||||
|
@ -2100,7 +2109,7 @@ begin
|
|||
csr.dcsr_rd(4) <= '1'; -- mprven: mstatus.mprv is also evaluated in debug mode
|
||||
csr.dcsr_rd(3) <= '0'; -- nmip: no pending non-maskable interrupt
|
||||
csr.dcsr_rd(2) <= csr.dcsr_step; -- step: single-step mode
|
||||
csr.dcsr_rd(1 downto 0) <= (others => csr.dcsr_prv); -- prv: privilege mode when debug mode was entered
|
||||
csr.dcsr_rd(1 downto 0) <= (others => csr.dcsr_prv); -- prv: privilege level when debug mode was entered
|
||||
|
||||
|
||||
-- ****************************************************************************************************************************
|
||||
|
|
|
@ -16,10 +16,6 @@ library neorv32;
|
|||
use neorv32.neorv32_package.all;
|
||||
|
||||
entity neorv32_cpu_icc is
|
||||
generic (
|
||||
HART_ID : natural range 0 to 3; -- ID of this core
|
||||
NUM_HARTS : natural range 1 to 4 -- number of cores, has to be a power of two
|
||||
);
|
||||
port (
|
||||
-- global control --
|
||||
clk_i : in std_ulogic; -- global clock, rising edge
|
||||
|
@ -38,109 +34,57 @@ end neorv32_cpu_icc;
|
|||
|
||||
architecture neorv32_cpu_icc_rtl of neorv32_cpu_icc is
|
||||
|
||||
-- link select --
|
||||
constant id_width_c : natural := index_size_f(NUM_HARTS);
|
||||
signal link_id : std_ulogic_vector(id_width_c-1 downto 0);
|
||||
|
||||
-- link control --
|
||||
signal link_sel, tx_fifo_we, tx_fifo_free : std_ulogic_vector(NUM_HARTS-1 downto 0);
|
||||
|
||||
-- incoming data as array --
|
||||
type rx_data_t is array (0 to NUM_HARTS-1) of std_ulogic_vector(XLEN-1 downto 0);
|
||||
signal rx_data : rx_data_t;
|
||||
signal tx_fifo_we, tx_fifo_free : std_ulogic;
|
||||
|
||||
begin
|
||||
|
||||
-- CSR Access -----------------------------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
csr_write: process(rstn_i, clk_i)
|
||||
begin
|
||||
if (rstn_i = '0') then
|
||||
link_id <= (others => '0');
|
||||
elsif rising_edge(clk_i) then
|
||||
if (csr_we_i = '1') and (csr_addr_i(11 downto 1) = csr_mxiccsr0_c(11 downto 1)) then
|
||||
link_id <= csr_wdata_i(id_width_c-1 downto 0);
|
||||
end if;
|
||||
end if;
|
||||
end process csr_write;
|
||||
|
||||
csr_read: process(csr_addr_i, link_id, icc_rx_i, tx_fifo_free, rx_data)
|
||||
csr_read: process(csr_addr_i, icc_rx_i, tx_fifo_free)
|
||||
begin
|
||||
csr_rdata_o <= (others => '0'); -- default
|
||||
if (csr_addr_i(11 downto 2) = csr_mxiccrxd_c(11 downto 2)) then -- ICC CSRs base address
|
||||
if (csr_addr_i(1) = '0') then -- data register(s)
|
||||
csr_rdata_o <= rx_data(to_integer(unsigned(link_id)));
|
||||
else -- control and status register(s)
|
||||
csr_rdata_o(XLEN-1) <= icc_rx_i.rdy(to_integer(unsigned(link_id)));
|
||||
csr_rdata_o(XLEN-2) <= tx_fifo_free(to_integer(unsigned(link_id)));
|
||||
csr_rdata_o(id_width_c-1 downto 0) <= link_id;
|
||||
if (csr_addr_i(11 downto 1) = csr_mxiccsreg_c(11 downto 1)) then -- ICC CSR base address
|
||||
if (csr_addr_i(0) = '0') then -- csr_mxiccsreg_c - control and status register
|
||||
csr_rdata_o(0) <= icc_rx_i.rdy;
|
||||
csr_rdata_o(1) <= tx_fifo_free;
|
||||
else -- csr_mxiccdata_c - data register
|
||||
if (icc_rx_i.rdy = '1') then -- "output gate": read zero if no RX data is available
|
||||
csr_rdata_o <= icc_rx_i.dat;
|
||||
end if;
|
||||
end if;
|
||||
end if;
|
||||
end process csr_read;
|
||||
|
||||
-- link read/write --
|
||||
icc_tx_o.ack <= '1' when (csr_re_i = '1') and (csr_addr_i = csr_mxiccdata_c) else '0';
|
||||
tx_fifo_we <= '1' when (csr_we_i = '1') and (csr_addr_i = csr_mxiccdata_c) else '0';
|
||||
|
||||
-- Communication Links --------------------------------------------------------------------
|
||||
|
||||
-- Outgoing/TX Message Queue (FIFO) -------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
link_gen:
|
||||
for i in 0 to NUM_HARTS-1 generate
|
||||
|
||||
-- TX FIFOs for outgoing links --
|
||||
queue_gen:
|
||||
if i /= HART_ID generate
|
||||
queue_inst: entity neorv32.neorv32_fifo
|
||||
generic map (
|
||||
FIFO_DEPTH => 4, -- yes, this is fixed
|
||||
FIFO_WIDTH => XLEN,
|
||||
FIFO_RSYNC => true,
|
||||
FIFO_SAFE => true,
|
||||
FULL_RESET => true
|
||||
)
|
||||
port map (
|
||||
-- control --
|
||||
clk_i => clk_i,
|
||||
rstn_i => rstn_i,
|
||||
clear_i => '0',
|
||||
half_o => open,
|
||||
-- write port --
|
||||
wdata_i => csr_wdata_i,
|
||||
we_i => tx_fifo_we(i),
|
||||
free_o => tx_fifo_free(i),
|
||||
-- read port --
|
||||
re_i => icc_rx_i.ack(i),
|
||||
rdata_o => icc_tx_o.dat(i*XLEN+(XLEN-1) downto i*XLEN),
|
||||
avail_o => icc_tx_o.rdy(i)
|
||||
);
|
||||
end generate;
|
||||
|
||||
-- no FIFO/link for *this* core --
|
||||
queue_terminate:
|
||||
if i = HART_ID generate
|
||||
tx_fifo_free(i) <= '0';
|
||||
icc_tx_o.dat(i*XLEN+(XLEN-1) downto i*XLEN) <= (others => '0');
|
||||
icc_tx_o.rdy(i) <= '0';
|
||||
end generate;
|
||||
|
||||
-- reorganize incoming links as 2d-array --
|
||||
rx_data(i) <= icc_rx_i.dat(i*XLEN+(XLEN-1) downto i*XLEN);
|
||||
|
||||
-- link control --
|
||||
link_sel(i) <= '1' when (unsigned(link_id) = to_unsigned(i, id_width_c)) else '0';
|
||||
icc_tx_o.ack(i) <= '1' when (csr_re_i = '1') and (csr_addr_i = csr_mxiccrxd_c) and (link_sel(i) = '1') else '0';
|
||||
tx_fifo_we(i) <= '1' when (csr_we_i = '1') and (csr_addr_i = csr_mxicctxd_c) and (link_sel(i) = '1') else '0';
|
||||
|
||||
end generate;
|
||||
|
||||
|
||||
-- terminate unused links --
|
||||
link_terminate:
|
||||
if NUM_HARTS < 4 generate
|
||||
link_terminate_gen:
|
||||
for i in NUM_HARTS to 3 generate
|
||||
icc_tx_o.rdy(i) <= '0';
|
||||
icc_tx_o.ack(i) <= '0';
|
||||
icc_tx_o.dat(i*XLEN+(XLEN-1) downto i*XLEN) <= (others => '0');
|
||||
end generate;
|
||||
end generate;
|
||||
tx_queue_inst: entity neorv32.neorv32_fifo
|
||||
generic map (
|
||||
FIFO_DEPTH => 4, -- yes, this is fixed
|
||||
FIFO_WIDTH => XLEN,
|
||||
FIFO_RSYNC => true,
|
||||
FIFO_SAFE => true,
|
||||
FULL_RESET => false -- no need for a full HW reset as we have an "output gate"
|
||||
)
|
||||
port map (
|
||||
-- control --
|
||||
clk_i => clk_i,
|
||||
rstn_i => rstn_i,
|
||||
clear_i => '0',
|
||||
half_o => open,
|
||||
-- write port --
|
||||
wdata_i => csr_wdata_i,
|
||||
we_i => tx_fifo_we,
|
||||
free_o => tx_fifo_free,
|
||||
-- read port --
|
||||
re_i => icc_rx_i.ack,
|
||||
rdata_o => icc_tx_o.dat,
|
||||
avail_o => icc_tx_o.rdy
|
||||
);
|
||||
|
||||
|
||||
end neorv32_cpu_icc_rtl;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
-- -------------------------------------------------------------------------------- --
|
||||
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
|
||||
-- Copyright (c) NEORV32 contributors. --
|
||||
-- Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. --
|
||||
-- Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. --
|
||||
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
|
||||
-- SPDX-License-Identifier: BSD-3-Clause --
|
||||
-- ================================================================================ --
|
||||
|
@ -117,6 +117,7 @@ begin
|
|||
-- atomic memory access operation encoding --
|
||||
amo_encode: process(ctrl_i.ir_funct12)
|
||||
begin
|
||||
amo_cmd <= (others => '0'); -- default
|
||||
if AMO_EN then
|
||||
case ctrl_i.ir_funct12(11 downto 7) is
|
||||
when "00000" => amo_cmd <= "0001"; -- ADD
|
||||
|
@ -129,8 +130,6 @@ begin
|
|||
when "11100" => amo_cmd <= "0111"; -- MAXU
|
||||
when others => amo_cmd <= "0000"; -- SWAP
|
||||
end case;
|
||||
else
|
||||
amo_cmd <= (others => '0');
|
||||
end if;
|
||||
end process;
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ package neorv32_package is
|
|||
|
||||
-- Architecture Constants -----------------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100900"; -- hardware version
|
||||
constant hw_version_c : std_ulogic_vector(31 downto 0) := x"01100905"; -- hardware version
|
||||
constant archid_c : natural := 19; -- official RISC-V architecture ID
|
||||
constant XLEN : natural := 32; -- native data path width
|
||||
|
||||
|
@ -224,19 +224,18 @@ package neorv32_package is
|
|||
err => '0'
|
||||
);
|
||||
|
||||
-- Inter-Core Communication (ICC) Links ---------------------------------------------------
|
||||
-- Inter-Core Communication (ICC) Link ----------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
-- icc link (for up to 4 cores) --
|
||||
type icc_t is record
|
||||
rdy : std_ulogic_vector(4-1 downto 0); -- data available
|
||||
ack : std_ulogic_vector(4-1 downto 0); -- read-enable
|
||||
dat : std_ulogic_vector(4*XLEN-1 downto 0); -- data word
|
||||
rdy : std_ulogic; -- data available
|
||||
ack : std_ulogic; -- read-enable
|
||||
dat : std_ulogic_vector(XLEN-1 downto 0); -- data word
|
||||
end record;
|
||||
|
||||
-- endpoint termination --
|
||||
constant icc_terminate_c : icc_t := (
|
||||
rdy => (others => '0'),
|
||||
ack => (others => '0'),
|
||||
rdy => '0',
|
||||
ack => '0',
|
||||
dat => (others => '0')
|
||||
);
|
||||
|
||||
|
@ -336,11 +335,11 @@ package neorv32_package is
|
|||
|
||||
-- RISC-V Funct12 - SYSTEM ----------------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
constant funct12_ecall_c : std_ulogic_vector(11 downto 0) := x"000"; -- ecall
|
||||
constant funct12_ebreak_c : std_ulogic_vector(11 downto 0) := x"001"; -- ebreak
|
||||
constant funct12_wfi_c : std_ulogic_vector(11 downto 0) := x"105"; -- wfi
|
||||
constant funct12_mret_c : std_ulogic_vector(11 downto 0) := x"302"; -- mret
|
||||
constant funct12_dret_c : std_ulogic_vector(11 downto 0) := x"7b2"; -- dret
|
||||
constant funct12_ecall_c : std_ulogic_vector(11 downto 0) := x"000";
|
||||
constant funct12_ebreak_c : std_ulogic_vector(11 downto 0) := x"001";
|
||||
constant funct12_wfi_c : std_ulogic_vector(11 downto 0) := x"105";
|
||||
constant funct12_mret_c : std_ulogic_vector(11 downto 0) := x"302";
|
||||
constant funct12_dret_c : std_ulogic_vector(11 downto 0) := x"7b2";
|
||||
|
||||
-- RISC-V Floating-Point Stuff ------------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
|
@ -464,7 +463,6 @@ package neorv32_package is
|
|||
constant csr_mhpmcounter13_c : std_ulogic_vector(11 downto 0) := x"b0d";
|
||||
constant csr_mhpmcounter14_c : std_ulogic_vector(11 downto 0) := x"b0e";
|
||||
constant csr_mhpmcounter15_c : std_ulogic_vector(11 downto 0) := x"b0f";
|
||||
--
|
||||
constant csr_mcycleh_c : std_ulogic_vector(11 downto 0) := x"b80";
|
||||
--constant csr_mtimeh_c : std_ulogic_vector(11 downto 0) := x"b81";
|
||||
constant csr_minstreth_c : std_ulogic_vector(11 downto 0) := x"b82";
|
||||
|
@ -482,15 +480,12 @@ package neorv32_package is
|
|||
constant csr_mhpmcounter14h_c : std_ulogic_vector(11 downto 0) := x"b8e";
|
||||
constant csr_mhpmcounter15h_c : std_ulogic_vector(11 downto 0) := x"b8f";
|
||||
-- NEORV32-specific read/write machine registers --
|
||||
constant csr_mxiccrxd_c : std_ulogic_vector(11 downto 0) := x"bc0";
|
||||
constant csr_mxicctxd_c : std_ulogic_vector(11 downto 0) := x"bc1";
|
||||
constant csr_mxiccsr0_c : std_ulogic_vector(11 downto 0) := x"bc2";
|
||||
constant csr_mxiccsr1_c : std_ulogic_vector(11 downto 0) := x"bc3";
|
||||
constant csr_mxiccsreg_c : std_ulogic_vector(11 downto 0) := x"bc0";
|
||||
constant csr_mxiccdata_c : std_ulogic_vector(11 downto 0) := x"bc1";
|
||||
-- user counters/timers --
|
||||
constant csr_cycle_c : std_ulogic_vector(11 downto 0) := x"c00";
|
||||
--constant csr_time_c : std_ulogic_vector(11 downto 0) := x"c01";
|
||||
constant csr_instret_c : std_ulogic_vector(11 downto 0) := x"c02";
|
||||
--
|
||||
constant csr_cycleh_c : std_ulogic_vector(11 downto 0) := x"c80";
|
||||
--constant csr_timeh_c : std_ulogic_vector(11 downto 0) := x"c81";
|
||||
constant csr_instreth_c : std_ulogic_vector(11 downto 0) := x"c82";
|
||||
|
@ -502,6 +497,7 @@ package neorv32_package is
|
|||
constant csr_mconfigptr_c : std_ulogic_vector(11 downto 0) := x"f15";
|
||||
-- NEORV32-specific read-only machine registers --
|
||||
constant csr_mxisa_c : std_ulogic_vector(11 downto 0) := x"fc0";
|
||||
--constant csr_mxisah_c : std_ulogic_vector(11 downto 0) := x"fc1"; -- to be implemented...
|
||||
|
||||
-- **********************************************************************************************************
|
||||
-- CPU Control
|
||||
|
@ -510,6 +506,8 @@ package neorv32_package is
|
|||
-- Main CPU Control Bus -------------------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
type ctrl_bus_t is record
|
||||
-- instruction fetch --
|
||||
if_fence : std_ulogic; -- fence.i operation
|
||||
-- register file --
|
||||
rf_wb_en : std_ulogic; -- write back enable
|
||||
rf_rs1 : std_ulogic_vector(4 downto 0); -- source register 1 address
|
||||
|
@ -529,7 +527,7 @@ package neorv32_package is
|
|||
lsu_req : std_ulogic; -- trigger memory access request
|
||||
lsu_rw : std_ulogic; -- 0: read access, 1: write access
|
||||
lsu_mo_we : std_ulogic; -- memory address and data output register write enable
|
||||
lsu_fence : std_ulogic; -- fence(.i) operation
|
||||
lsu_fence : std_ulogic; -- fence operation
|
||||
lsu_priv : std_ulogic; -- effective privilege mode for load/store
|
||||
-- instruction word --
|
||||
ir_funct3 : std_ulogic_vector(2 downto 0); -- funct3 bit field
|
||||
|
@ -544,6 +542,7 @@ package neorv32_package is
|
|||
|
||||
-- control bus reset initializer --
|
||||
constant ctrl_bus_zero_c : ctrl_bus_t := (
|
||||
if_fence => '0',
|
||||
rf_wb_en => '0',
|
||||
rf_rs1 => (others => '0'),
|
||||
rf_rs2 => (others => '0'),
|
||||
|
|
|
@ -310,15 +310,15 @@ architecture neorv32_top_rtl of neorv32_top is
|
|||
signal dci_ndmrstn : std_ulogic;
|
||||
signal dci_haltreq : std_ulogic_vector(num_cores_c-1 downto 0);
|
||||
|
||||
-- CPU ICC links (up to 4 instances) --
|
||||
type multicore_icc_t is array (0 to 3) of icc_t;
|
||||
signal icc_tx, icc_rx : multicore_icc_t;
|
||||
-- CPU ICC links --
|
||||
type core_complex_icc_t is array (0 to num_cores_c-1) of icc_t;
|
||||
signal icc_tx, icc_rx : core_complex_icc_t;
|
||||
|
||||
-- bus: CPU core complex (up to 4 instances) --
|
||||
type multicore_req_t is array (0 to 3) of bus_req_t;
|
||||
type multicore_rsp_t is array (0 to 3) of bus_rsp_t;
|
||||
signal core_req : multicore_req_t;
|
||||
signal core_rsp : multicore_rsp_t;
|
||||
-- bus: CPU core complex --
|
||||
type core_complex_req_t is array (0 to num_cores_c-1) of bus_req_t;
|
||||
type core_complex_rsp_t is array (0 to num_cores_c-1) of bus_rsp_t;
|
||||
signal cpu_i_req, cpu_d_req, icache_req, dcache_req, core_req : core_complex_req_t;
|
||||
signal cpu_i_rsp, cpu_d_rsp, icache_rsp, dcache_rsp, core_rsp : core_complex_rsp_t;
|
||||
|
||||
-- bus: system bus (including DMA complex) --
|
||||
signal sys1_req, sys2_req, dma_req, sys3_req : bus_req_t;
|
||||
|
@ -494,21 +494,21 @@ begin
|
|||
cpu_firq(14) <= firq(FIRQ_SLINK_RX);
|
||||
cpu_firq(15) <= firq(FIRQ_SLINK_TX);
|
||||
|
||||
-- CPU core(s) + optional L1 caches --
|
||||
-- CPU core(s) + optional L1 caches + bus switch --
|
||||
core_complex_gen:
|
||||
for i in 0 to num_cores_c-1 generate
|
||||
|
||||
-- Core Complex ---------------------------------------------------------------------------
|
||||
-- CPU Core -------------------------------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
neorv32_cpu_neorv32_core_complex: entity neorv32.neorv32_core_complex
|
||||
neorv32_cpu_inst: entity neorv32.neorv32_cpu
|
||||
generic map (
|
||||
-- General --
|
||||
HART_ID => i,
|
||||
NUM_HARTS => num_cores_c,
|
||||
VENDOR_ID => vendorid_c,
|
||||
BOOT_ADDR => cpu_boot_addr_c,
|
||||
DEBUG_PARK_ADDR => dm_park_entry_c,
|
||||
DEBUG_EXC_ADDR => dm_exc_entry_c,
|
||||
ICC_EN => DUAL_CORE_EN,
|
||||
-- RISC-V ISA Extensions --
|
||||
RISCV_ISA_C => RISCV_ISA_C,
|
||||
RISCV_ISA_E => RISCV_ISA_E,
|
||||
|
@ -547,60 +547,118 @@ begin
|
|||
PMP_NAP_MODE_EN => PMP_NAP_MODE_EN,
|
||||
-- Hardware Performance Monitors (HPM) --
|
||||
HPM_NUM_CNTS => HPM_NUM_CNTS,
|
||||
HPM_CNT_WIDTH => HPM_CNT_WIDTH,
|
||||
-- Instruction Cache (iCACHE) --
|
||||
ICACHE_EN => ICACHE_EN,
|
||||
ICACHE_NUM_BLOCKS => ICACHE_NUM_BLOCKS,
|
||||
ICACHE_BLOCK_SIZE => ICACHE_BLOCK_SIZE,
|
||||
ICACHE_UC_BEGIN => mem_uncached_begin_c,
|
||||
-- Data Cache (dCACHE) --
|
||||
DCACHE_EN => DCACHE_EN,
|
||||
DCACHE_NUM_BLOCKS => DCACHE_NUM_BLOCKS,
|
||||
DCACHE_BLOCK_SIZE => DCACHE_BLOCK_SIZE,
|
||||
DCACHE_UC_BEGIN => mem_uncached_begin_c
|
||||
HPM_CNT_WIDTH => HPM_CNT_WIDTH
|
||||
)
|
||||
port map (
|
||||
-- global control --
|
||||
clk_i => clk_i,
|
||||
rstn_i => rstn_sys,
|
||||
clk_i => clk_i,
|
||||
rstn_i => rstn_sys,
|
||||
-- interrupts --
|
||||
msi_i => msw_irq(i),
|
||||
mei_i => mext_irq_i,
|
||||
mti_i => mtime_irq(i),
|
||||
firq_i => cpu_firq,
|
||||
dbi_i => dci_haltreq(i),
|
||||
msi_i => msw_irq(i),
|
||||
mei_i => mext_irq_i,
|
||||
mti_i => mtime_irq(i),
|
||||
firq_i => cpu_firq,
|
||||
dbi_i => dci_haltreq(i),
|
||||
-- inter-core communication links --
|
||||
icc_tx_o => icc_tx(i),
|
||||
icc_rx_i => icc_rx(i),
|
||||
-- system bus interface --
|
||||
bus_req_o => core_req(i),
|
||||
bus_rsp_i => core_rsp(i)
|
||||
icc_tx_o => icc_tx(i),
|
||||
icc_rx_i => icc_rx(i),
|
||||
-- instruction bus interface --
|
||||
ibus_req_o => cpu_i_req(i),
|
||||
ibus_rsp_i => cpu_i_rsp(i),
|
||||
-- data bus interface --
|
||||
dbus_req_o => cpu_d_req(i),
|
||||
dbus_rsp_i => cpu_d_rsp(i)
|
||||
);
|
||||
|
||||
-- inter-core communication (ICC) links --
|
||||
icc_connect: process(icc_tx)
|
||||
begin
|
||||
icc_rx(i) <= icc_terminate_c;
|
||||
for j in 0 to num_cores_c-1 loop -- connect this core with every other core
|
||||
icc_rx(i).rdy(j) <= icc_tx(j).rdy(i);
|
||||
icc_rx(i).ack(j) <= icc_tx(j).ack(i);
|
||||
icc_rx(i).dat(j*32+31 downto j*32) <= icc_tx(j).dat(i*32+31 downto i*32);
|
||||
end loop;
|
||||
end process icc_connect;
|
||||
|
||||
-- CPU L1 Instruction Cache (I-Cache) -----------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
neorv32_icache_enabled:
|
||||
if ICACHE_EN generate
|
||||
neorv32_icache_inst: entity neorv32.neorv32_cache
|
||||
generic map (
|
||||
NUM_BLOCKS => ICACHE_NUM_BLOCKS,
|
||||
BLOCK_SIZE => ICACHE_BLOCK_SIZE,
|
||||
UC_BEGIN => mem_uncached_begin_c(31 downto 28),
|
||||
UC_ENABLE => true,
|
||||
READ_ONLY => true
|
||||
)
|
||||
port map (
|
||||
clk_i => clk_i,
|
||||
rstn_i => rstn_sys,
|
||||
host_req_i => cpu_i_req(i),
|
||||
host_rsp_o => cpu_i_rsp(i),
|
||||
bus_req_o => icache_req(i),
|
||||
bus_rsp_i => icache_rsp(i)
|
||||
);
|
||||
end generate;
|
||||
|
||||
neorv32_icache_disabled:
|
||||
if not ICACHE_EN generate
|
||||
icache_req(i) <= cpu_i_req(i);
|
||||
cpu_i_rsp(i) <= icache_rsp(i);
|
||||
end generate;
|
||||
|
||||
|
||||
-- CPU L1 Data Cache (D-Cache) ------------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
neorv32_dcache_enabled:
|
||||
if DCACHE_EN generate
|
||||
neorv32_dcache_inst: entity neorv32.neorv32_cache
|
||||
generic map (
|
||||
NUM_BLOCKS => DCACHE_NUM_BLOCKS,
|
||||
BLOCK_SIZE => DCACHE_BLOCK_SIZE,
|
||||
UC_BEGIN => mem_uncached_begin_c(31 downto 28),
|
||||
UC_ENABLE => true,
|
||||
READ_ONLY => false
|
||||
)
|
||||
port map (
|
||||
clk_i => clk_i,
|
||||
rstn_i => rstn_sys,
|
||||
host_req_i => cpu_d_req(i),
|
||||
host_rsp_o => cpu_d_rsp(i),
|
||||
bus_req_o => dcache_req(i),
|
||||
bus_rsp_i => dcache_rsp(i)
|
||||
);
|
||||
end generate;
|
||||
|
||||
neorv32_dcache_disabled:
|
||||
if not DCACHE_EN generate
|
||||
dcache_req(i) <= cpu_d_req(i);
|
||||
cpu_d_rsp(i) <= dcache_rsp(i);
|
||||
end generate;
|
||||
|
||||
|
||||
-- Core Instruction/Data Bus Switch -------------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
neorv32_core_bus_switch_inst: entity neorv32.neorv32_bus_switch
|
||||
generic map (
|
||||
ROUND_ROBIN_EN => false, -- use prioritizing arbitration
|
||||
PORT_A_READ_ONLY => false,
|
||||
PORT_B_READ_ONLY => true -- instruction fetch is read-only
|
||||
)
|
||||
port map (
|
||||
clk_i => clk_i,
|
||||
rstn_i => rstn_sys,
|
||||
a_lock_i => '0', -- no exclusive accesses
|
||||
a_req_i => dcache_req(i), -- data accesses are prioritized
|
||||
a_rsp_o => dcache_rsp(i),
|
||||
b_req_i => icache_req(i),
|
||||
b_rsp_o => icache_rsp(i),
|
||||
x_req_o => core_req(i),
|
||||
x_rsp_i => core_rsp(i)
|
||||
);
|
||||
|
||||
end generate; -- /core_complex
|
||||
|
||||
-- terminate unused interfaces --
|
||||
core_complex_terminate:
|
||||
if num_cores_c < 4 generate
|
||||
core_complex_terminate_gen:
|
||||
for i in num_cores_c to 3 generate
|
||||
core_req(i) <= req_terminate_c;
|
||||
core_rsp(i) <= rsp_terminate_c;
|
||||
icc_rx(i) <= icc_terminate_c;
|
||||
icc_tx(i) <= icc_terminate_c;
|
||||
end generate;
|
||||
end generate;
|
||||
|
||||
-- Inter-Core Communication (ICC) Links ---------------------------------------------------
|
||||
-- -------------------------------------------------------------------------------------------
|
||||
icc_connect: process(icc_tx)
|
||||
begin
|
||||
icc_rx(icc_rx'left) <= icc_tx(icc_tx'right);
|
||||
icc_rx(icc_rx'right) <= icc_tx(icc_tx'left);
|
||||
end process icc_connect;
|
||||
|
||||
|
||||
-- Core Complex Bus Arbiter ---------------------------------------------------------------
|
||||
|
@ -617,10 +675,10 @@ begin
|
|||
clk_i => clk_i,
|
||||
rstn_i => rstn_sys,
|
||||
a_lock_i => '0',
|
||||
a_req_i => core_req(0),
|
||||
a_rsp_o => core_rsp(0),
|
||||
b_req_i => core_req(1),
|
||||
b_rsp_o => core_rsp(1),
|
||||
a_req_i => core_req(core_req'left),
|
||||
a_rsp_o => core_rsp(core_rsp'left),
|
||||
b_req_i => core_req(core_req'right),
|
||||
b_rsp_o => core_rsp(core_rsp'right),
|
||||
x_req_o => sys1_req,
|
||||
x_rsp_i => sys1_rsp
|
||||
);
|
||||
|
|
|
@ -19,7 +19,6 @@ NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_cpu_icc.vhd
|
|||
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_cpu.vhd
|
||||
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_bus.vhd
|
||||
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_cache.vhd
|
||||
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_core_complex.vhd
|
||||
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_dma.vhd
|
||||
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_application_image.vhd
|
||||
NEORV32_RTL_PATH_PLACEHOLDER/core/neorv32_imem.vhd
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
@ -58,6 +58,7 @@
|
|||
#endif
|
||||
|
||||
/* -------- Auto-boot configuration -------- */
|
||||
/* Priority SPI > TWI */
|
||||
|
||||
/** Time until the auto-boot sequence starts (in seconds); 0 = disabled */
|
||||
#ifndef AUTO_BOOT_TIMEOUT
|
||||
|
@ -103,6 +104,33 @@
|
|||
#define XIP_EN 1
|
||||
#endif
|
||||
|
||||
/* -------- TWI configuration -------- */
|
||||
|
||||
/** Enable TWI for copying to RAM */
|
||||
#ifndef TWI_EN
|
||||
#define TWI_EN 0
|
||||
#endif
|
||||
|
||||
/** TWI Clock pre-scaler */
|
||||
#ifndef TWI_CLK_PRSC
|
||||
#define TWI_CLK_PRSC CLK_PRSC_64
|
||||
#endif
|
||||
|
||||
/** TWI Clock divider */
|
||||
#ifndef TWI_CLK_DIV
|
||||
#define TWI_CLK_DIV 3
|
||||
#endif
|
||||
|
||||
/** TWI First Device ID */
|
||||
#ifndef TWI_DEVICE_ID
|
||||
#define TWI_DEVICE_ID 0x50
|
||||
#endif
|
||||
|
||||
/** TWI Memory address width (in numbers of bytes; 1 or 2) */
|
||||
#ifndef TWI_ADDR_BYTES
|
||||
#define TWI_ADDR_BYTES 1
|
||||
#endif
|
||||
|
||||
/**@}*/
|
||||
|
||||
|
||||
|
@ -111,7 +139,8 @@
|
|||
**************************************************************************/
|
||||
enum EXE_STREAM_SOURCE_enum {
|
||||
EXE_STREAM_UART = 0, /**< Get executable via UART */
|
||||
EXE_STREAM_FLASH = 1 /**< Get executable via SPI flash */
|
||||
EXE_STREAM_FLASH = 1, /**< Get executable via SPI flash */
|
||||
EXE_STREAM_TWI = 2 /**< Get executable via TWI device */
|
||||
};
|
||||
|
||||
|
||||
|
@ -122,18 +151,20 @@ enum ERROR_CODES_enum {
|
|||
ERROR_SIGNATURE = 0, /**< 0: Wrong signature in executable */
|
||||
ERROR_SIZE = 1, /**< 1: Insufficient instruction memory capacity */
|
||||
ERROR_CHECKSUM = 2, /**< 2: Checksum error in executable */
|
||||
ERROR_FLASH = 3 /**< 3: SPI flash access error */
|
||||
ERROR_FLASH = 3, /**< 3: SPI flash access error */
|
||||
ERROR_TWI = 4 /**< 3: TWI access error (missing ACK) */
|
||||
};
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Error messages
|
||||
**************************************************************************/
|
||||
const char error_message[4][5] = {
|
||||
const char error_message[5][5] = {
|
||||
"EXE",
|
||||
"SIZE",
|
||||
"CHKS",
|
||||
"FLSH"
|
||||
"SPI",
|
||||
"TWI"
|
||||
};
|
||||
|
||||
|
||||
|
@ -237,6 +268,9 @@ void spi_flash_write_disable(void);
|
|||
uint8_t spi_flash_read_status(void);
|
||||
void spi_flash_write_addr(uint32_t addr);
|
||||
|
||||
// TWI driver functions
|
||||
uint32_t twi_read_addr(uint32_t addr);
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Bootloader main.
|
||||
|
@ -279,6 +313,11 @@ int main(void) {
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if (TWI_EN != 0)
|
||||
// setup TWI
|
||||
neorv32_twi_setup(TWI_CLK_PRSC, TWI_CLK_DIV, 0);
|
||||
#endif
|
||||
|
||||
// Configure CLINT timer interrupt
|
||||
if (neorv32_clint_available()) {
|
||||
NEORV32_CLINT->MTIME.uint32[0] = 0;
|
||||
|
@ -314,7 +353,7 @@ int main(void) {
|
|||
// ------------------------------------------------
|
||||
// Auto boot sequence
|
||||
// ------------------------------------------------
|
||||
#if (SPI_EN != 0)
|
||||
#if (SPI_EN != 0 || TWI_EN != 0)
|
||||
#if (AUTO_BOOT_TIMEOUT != 0)
|
||||
if (neorv32_clint_available()) {
|
||||
|
||||
|
@ -331,7 +370,11 @@ int main(void) {
|
|||
}
|
||||
|
||||
if (neorv32_clint_time_get() >= timeout_time) { // timeout? start auto boot sequence
|
||||
get_exe(EXE_STREAM_FLASH); // try booting from flash
|
||||
#if (SPI_EN != 0)
|
||||
get_exe(EXE_STREAM_FLASH); // try booting from flash
|
||||
#elif (TWI_EN != 0)
|
||||
get_exe(EXE_STREAM_TWI); // try booting from twi
|
||||
#endif
|
||||
PRINT_TEXT("\n");
|
||||
start_app(0);
|
||||
while(1);
|
||||
|
@ -377,6 +420,11 @@ int main(void) {
|
|||
else if (c == 'l') { // copy executable from flash
|
||||
get_exe(EXE_STREAM_FLASH);
|
||||
}
|
||||
#endif
|
||||
#if (TWI_EN != 0)
|
||||
else if (c == 't') { // copy executable from TWI
|
||||
get_exe(EXE_STREAM_TWI);
|
||||
}
|
||||
#endif
|
||||
else if (c == 'e') { // start application program from IMEM
|
||||
if (exe_available == 0) { // executable available?
|
||||
|
@ -419,6 +467,9 @@ void print_help(void) {
|
|||
" s: Store to flash\n"
|
||||
" l: Load from flash\n"
|
||||
#endif
|
||||
#if (TWI_EN != 0)
|
||||
" t: Load from TWI Device\n"
|
||||
#endif
|
||||
#if (XIP_EN != 0)
|
||||
" x: Boot from flash (XIP)\n"
|
||||
#endif
|
||||
|
@ -521,25 +572,36 @@ void get_exe(int src) {
|
|||
getting_exe = 1; // to inform trap handler we were trying to get an executable
|
||||
|
||||
// flash image base address
|
||||
uint32_t addr = (uint32_t)SPI_BOOT_BASE_ADDR;
|
||||
uint32_t addr = 0;
|
||||
if (src == EXE_STREAM_FLASH) {
|
||||
addr = (uint32_t)SPI_BOOT_BASE_ADDR;
|
||||
}
|
||||
|
||||
|
||||
// get image from UART?
|
||||
if (src == EXE_STREAM_UART) {
|
||||
PRINT_TEXT("Awaiting neorv32_exe.bin... ");
|
||||
}
|
||||
#if (SPI_EN != 0)
|
||||
else {
|
||||
#if (SPI_EN != 0)
|
||||
else if(src == EXE_STREAM_FLASH) {
|
||||
PRINT_TEXT("Loading from SPI flash @");
|
||||
PRINT_XNUM(addr);
|
||||
PRINT_TEXT("...\n");
|
||||
|
||||
// flash checks
|
||||
if (((NEORV32_SYSINFO->SOC & (1<<SYSINFO_SOC_IO_SPI)) == 0) || // SPI module not implemented?
|
||||
(spi_flash_check() != 0)) { // check if flash ready (or available at all)
|
||||
(spi_flash_check() != 0)) { // check if flash ready (or available at all)
|
||||
system_error(ERROR_FLASH);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#if (TWI_EN)
|
||||
else if(src == EXE_STREAM_TWI) {
|
||||
PRINT_TEXT("Loading from TWI Devices, starting with ");
|
||||
PRINT_XNUM(TWI_DEVICE_ID);
|
||||
PRINT_TEXT("...\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
// check if valid image
|
||||
uint32_t signature = get_exe_word(src, addr + EXE_OFFSET_SIGNATURE);
|
||||
|
@ -647,27 +709,34 @@ void save_exe(void) {
|
|||
* Get word from executable stream
|
||||
*
|
||||
* @param src Source of executable stream data. See #EXE_STREAM_SOURCE_enum.
|
||||
* @param addr Address when accessing SPI flash.
|
||||
* @param addr Address when accessing SPI flash or TWI Device.
|
||||
* @return 32-bit data word from stream.
|
||||
**************************************************************************/
|
||||
uint32_t get_exe_word(int src, uint32_t addr) {
|
||||
#if (TWI_EN != 0)
|
||||
if (src == EXE_STREAM_TWI) {
|
||||
return twi_read_addr(addr);
|
||||
} else {
|
||||
#endif
|
||||
union {
|
||||
uint32_t uint32;
|
||||
uint8_t uint8[sizeof(uint32_t)];
|
||||
} data;
|
||||
|
||||
union {
|
||||
uint32_t uint32;
|
||||
uint8_t uint8[sizeof(uint32_t)];
|
||||
} data;
|
||||
uint32_t i;
|
||||
|
||||
uint32_t i;
|
||||
for (i=0; i<4; i++) {
|
||||
if (src == EXE_STREAM_UART) {
|
||||
data.uint8[i] = (uint8_t)PRINT_GETC();
|
||||
for (i=0; i<4; i++) {
|
||||
if (src == EXE_STREAM_UART) {
|
||||
data.uint8[i] = (uint8_t)PRINT_GETC();
|
||||
}
|
||||
else {
|
||||
data.uint8[i] = spi_flash_read_byte(addr + i); // little-endian byte order
|
||||
}
|
||||
}
|
||||
else {
|
||||
data.uint8[i] = spi_flash_read_byte(addr + i); // little-endian byte order
|
||||
}
|
||||
}
|
||||
|
||||
return data.uint32;
|
||||
#if (TWI_EN != 0)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
@ -952,3 +1021,87 @@ void spi_flash_write_addr(uint32_t addr) {
|
|||
#error "Unsupported SPI_FLASH_ADDR_BYTES configuration!"
|
||||
#endif
|
||||
}
|
||||
|
||||
// ##########################################################################################################
|
||||
// TWI driver functions
|
||||
// ##########################################################################################################
|
||||
uint32_t twi_read_addr(uint32_t addr) {
|
||||
|
||||
#if (TWI_EN != 0)
|
||||
int device_nack = 0;
|
||||
uint8_t transfer;
|
||||
|
||||
union {
|
||||
uint32_t uint32;
|
||||
uint8_t uint8[sizeof(uint32_t)];
|
||||
} data, address;
|
||||
|
||||
address.uint32 = addr;
|
||||
|
||||
#if (TWI_ADDR_BYTES == 1)
|
||||
uint8_t device_id = address.uint8[TWI_ADDR_BYTES] + TWI_DEVICE_ID;
|
||||
#elif (TWI_ADDR_BYTES == 2)
|
||||
uint8_t device_id = TWI_DEVICE_ID;
|
||||
#else
|
||||
#error "Unsupported TWI_ADDR_BYTES configuration!"
|
||||
#endif
|
||||
|
||||
|
||||
/***********************
|
||||
* Set address to read
|
||||
***********************/
|
||||
|
||||
neorv32_twi_generate_start();
|
||||
|
||||
// Send device addr
|
||||
transfer = device_id << 1;
|
||||
device_nack |= neorv32_twi_trans(&transfer, 0);
|
||||
|
||||
// Send read address
|
||||
#if (TWI_ADDR_BYTES == 1)
|
||||
transfer = address.uint8[0];
|
||||
device_nack |= neorv32_twi_trans(&transfer, 0);
|
||||
#elif (TWI_ADDR_BYTES == 2)
|
||||
transfer = address.uint8[1];
|
||||
device_nack |= neorv32_twi_trans(&transfer, 0);
|
||||
transfer = address.uint8[0];
|
||||
device_nack |= neorv32_twi_trans(&transfer, 0);
|
||||
#else
|
||||
#error "Unsupported TWI_ADDR_BYTES configuration!"
|
||||
#endif
|
||||
|
||||
/***********************
|
||||
* Read data
|
||||
***********************/
|
||||
|
||||
neorv32_twi_generate_start();
|
||||
|
||||
// Send device addr with read flag
|
||||
transfer = device_id << 1;
|
||||
transfer |= 0x01;
|
||||
device_nack |= neorv32_twi_trans(&transfer, 0);
|
||||
|
||||
if (device_nack)
|
||||
{
|
||||
system_error(ERROR_TWI);
|
||||
}
|
||||
|
||||
// Read
|
||||
for (uint8_t i = 0; i <= 2; i++)
|
||||
{
|
||||
transfer = 0xFF;
|
||||
neorv32_twi_trans(&transfer, 1); // ACK by master
|
||||
data.uint8[i] = transfer;
|
||||
}
|
||||
// Last read with NACK by master
|
||||
transfer = 0xFF;
|
||||
neorv32_twi_trans(&transfer, 0); // NACK by master
|
||||
data.uint8[3] = transfer;
|
||||
|
||||
neorv32_twi_generate_stop();
|
||||
|
||||
return data.uint32;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -18,6 +18,33 @@ USER_FLAGS += \
|
|||
-DMAKE_BOOTLOADER \
|
||||
-flto
|
||||
|
||||
# Set which bootloader features to compile and associated settings.
|
||||
# Warning: Enabling them all while sticking to the minimal RISC-V ISA will result in a too-large binary!
|
||||
|
||||
#USER_FLAGS += -DUART_EN=1
|
||||
#USER_FLAGS += -DUART_EN=19200
|
||||
#USER_FLAGS += -DUART_HW_HANDSHAKE_EN=0
|
||||
|
||||
#USER_FLAGS += -DSTATUS_LED_EN=1
|
||||
#USER_FLAGS += -DSTATUS_LED_PIN=0
|
||||
|
||||
#USER_FLAGS += -DAUTO_BOOT_TIMEOUT=10
|
||||
|
||||
#USER_FLAGS += -DSPI_EN=0
|
||||
#USER_FLAGS += -DSPI_FLASH_CS=0
|
||||
#USER_FLAGS += -DSPI_FLASH_ADDR_BYTES=3
|
||||
#USER_FLAGS += -DSPI_FLASH_SECTOR_SIZE=65536
|
||||
#USER_FLAGS += -DSPI_FLASH_CLK_PRSC=CLK_PRSC_8
|
||||
#USER_FLAGS += -DSPI_BOOT_BASE_ADDR=0x00400000UL
|
||||
|
||||
#USER_FLAGS += -DXIP_EN=1
|
||||
|
||||
#USER_FLAGS += -DTWI_EN=1
|
||||
#USER_FLAGS += -DTWI_CLK_PRSC=CLK_PRSC_64
|
||||
#USER_FLAGS += -DTWI_CLK_DIV=3
|
||||
#USER_FLAGS += -DTWI_DEVICE_ID=0x50
|
||||
#USER_FLAGS += -DTWI_ADDR_BYTES=1
|
||||
|
||||
# Set path to NEORV32 root directory
|
||||
NEORV32_HOME ?= ../..
|
||||
|
||||
|
|
|
@ -72,41 +72,38 @@ __crt0_entry:
|
|||
|
||||
|
||||
// ************************************************************************************************
|
||||
// SMP multi-core setup - wait for configuration if we are not core 0.
|
||||
// SMP dual-core setup - wait for configuration if we are not core 0.
|
||||
// ************************************************************************************************
|
||||
#ifndef DISABLE_MULTICORE
|
||||
__crt0_multicore_check:
|
||||
beqz x1, __crt0_multicore_primary // proceed with normal boot-up if we are core 0
|
||||
#ifndef DISABLE_DUALCORE
|
||||
__crt0_dualcore_check:
|
||||
beqz x1, __crt0_dualcore_primary // proceed with normal boot-up if we are core 0
|
||||
|
||||
// setup machine software interrupt
|
||||
la x15, __crt0_multicore_wakeup
|
||||
la x15, __crt0_dualcore_wakeup
|
||||
csrw mtvec, x15 // install interrupt handler
|
||||
csrsi mie, 1 << 3 // enable software interrupt source
|
||||
csrsi mstatus, 1 << 3 // enable machine-level interrupts
|
||||
j __crt0_sleep // wait for interrupt in sleep mode
|
||||
|
||||
// machine software interrupt handler
|
||||
__crt0_multicore_wakeup:
|
||||
__crt0_dualcore_wakeup:
|
||||
li x14, 0xfff40000 // CLINT.MSWI base address
|
||||
slli x15, x1, 2 // offset = hart_id * 4
|
||||
add x14, x14, x15
|
||||
sw zero, 0(x14) // CLINT.MSWI[hart_id]
|
||||
sw zero, 4(x14) // clear CLINT.MSWI[1]
|
||||
|
||||
// check launch configuration from core 0
|
||||
csrw 0xbc2, zero // ICC.SR: link select = 0
|
||||
csrr x13, 0xbc0 // ICC.RX: signature
|
||||
csrr x13, 0xbc1 // ICC_DATA: signature
|
||||
li x14, 0xffab4321 // expected signature
|
||||
beq x14, x13, __crt0_multicore_launch
|
||||
beq x14, x13, __crt0_dualcore_launch
|
||||
mret // go back to sleep if incorrect signature
|
||||
|
||||
// get launch configuration from core 0
|
||||
__crt0_multicore_launch:
|
||||
csrw 0xbc1, x14 // ICC.TX: acknowledge start
|
||||
csrr x2, 0xbc0 // ICC.RX: stack top -> sp
|
||||
csrr x12, 0xbc0 // ICC.RX: entry point
|
||||
__crt0_dualcore_launch:
|
||||
csrr x2, 0xbc1 // ICC_DATA: stack top -> sp
|
||||
csrr x12, 0xbc1 // ICC_DATA: entry point
|
||||
csrw 0xbc1, x14 // ICC_DATA: acknowledge start
|
||||
j __crt0_main_entry // start at entry point
|
||||
|
||||
__crt0_multicore_primary:
|
||||
__crt0_dualcore_primary:
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -160,7 +157,7 @@ __crt0_constructors_end:
|
|||
// Setup arguments and call main function.
|
||||
// ************************************************************************************************
|
||||
__crt0_main_entry:
|
||||
fence // reload instruction cache
|
||||
fence // reload data cache
|
||||
fence.i // reload instruction cache
|
||||
|
||||
csrw mstatus, x5 // re-initialize
|
||||
|
|
|
@ -8,29 +8,42 @@
|
|||
|
||||
/**********************************************************************//**
|
||||
* @file demo_dual_core/main.c
|
||||
* @author Stephan Nolting
|
||||
* @brief Simple dual-core SMP demo program.
|
||||
**************************************************************************/
|
||||
#include <neorv32.h>
|
||||
#include "spinlock.h"
|
||||
|
||||
/** User configuration */
|
||||
#define BAUD_RATE 19200 // UART0 Baud rate
|
||||
|
||||
/** Function prototypes */
|
||||
void main_core1(void);
|
||||
void clint_mtime_handler_core0(void); // core0 MTIMER interrupt handler
|
||||
void clint_mtime_handler_core1(void); // core1 MTIMER interrupt handler
|
||||
#define BAUD_RATE 19200
|
||||
|
||||
/** Global variables */
|
||||
volatile uint8_t __attribute__ ((aligned (16))) core1_stack[2048]; // stack memory for core1
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Main function for core 1 (secondary core).
|
||||
*
|
||||
* @return Irrelevant (but can be inspected by the debugger).
|
||||
**************************************************************************/
|
||||
int main_core1(void) {
|
||||
|
||||
// setup NEORV32 runtime-environment (RTE) for _this_ core (core1)
|
||||
neorv32_rte_setup();
|
||||
|
||||
// print message from core 0
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("Hello world! This is core 1 running!\n");
|
||||
spin_unlock();
|
||||
|
||||
return 0; // return to crt0 and halt
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Main function for core 0 (primary core).
|
||||
*
|
||||
* @attention This program requires the dual-core configuration, the CLINT, UART0
|
||||
* and the Zaamo ISA extension.
|
||||
* and the A/Zaamo ISA extension.
|
||||
*
|
||||
* @return Irrelevant (but can be inspected by the debugger).
|
||||
**************************************************************************/
|
||||
|
@ -45,11 +58,11 @@ int main(void) {
|
|||
return -1;
|
||||
}
|
||||
neorv32_uart0_setup(BAUD_RATE, 0);
|
||||
neorv32_uart0_printf("\n<< NEORV32 Dual-Core SMP Demo >>\n\n");
|
||||
neorv32_uart0_printf("\n<< NEORV32 Simple SMP Dual-Core Demo >>\n\n");
|
||||
|
||||
|
||||
// check hardware/software configuration
|
||||
if (NEORV32_SYSINFO->MISC[SYSINFO_MISC_HART] != 2) { // two cores available?
|
||||
if (neorv32_sysinfo_get_numcores() < 2) { // two cores available?
|
||||
neorv32_uart0_printf("[ERROR] dual-core option not enabled!\n");
|
||||
return -1;
|
||||
}
|
||||
|
@ -58,40 +71,29 @@ int main(void) {
|
|||
return -1;
|
||||
}
|
||||
if ((neorv32_cpu_csr_read(CSR_MXISA) & (1<<CSR_MXISA_ZAAMO)) == 0) { // atomic memory operations available?
|
||||
neorv32_uart0_printf("[ERROR] 'Zaamo' ISA extension not available!\n");
|
||||
neorv32_uart0_printf("[ERROR] 'A'/'Zaamo' ISA extension not available!\n");
|
||||
return -1;
|
||||
}
|
||||
#ifndef __riscv_atomic
|
||||
#warning "Application has to be compiled with 'A' ISA extension!"
|
||||
#warning "Application has to be compiled with RISC-V 'A' ISA extension!"
|
||||
neorv32_uart0_printf("[ERROR] Application has to be compiled with 'A' ISA extension!\n");
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
|
||||
// initialize _global_ system timer (CLINT's machine timer)
|
||||
neorv32_clint_time_set(0);
|
||||
|
||||
// setup MTIMER interrupt for this core (core0)
|
||||
// the core-specific installation is handled entirely by the RTE
|
||||
neorv32_clint_mtimecmp_set(0); // initialize core-specific MTIMECMP
|
||||
neorv32_rte_handler_install(RTE_TRAP_MTI, clint_mtime_handler_core0); // install trap handler to RTE
|
||||
neorv32_cpu_csr_set(CSR_MIE, 1 << CSR_MIE_MTIE); // enable MTIMER interrupt source
|
||||
|
||||
|
||||
// Core one is halted in crt0 right after reset and wait for its machine-level software
|
||||
// interrupt before resuming. Before the interrupt is triggered, a launch configuration
|
||||
// for core 1 has to be provided. This launch configuration defines the entry point for
|
||||
// core 1 as well as the stack setup. All this is handle by "neorv32_rte_smp_launch()".
|
||||
// core 1 as well as the stack setup. All this is handle by "neorv32_smp_launch()".
|
||||
|
||||
neorv32_uart0_printf("Launching core1...\n");
|
||||
|
||||
// Launch execution of core 1. Arguments:
|
||||
// 1st: Hart ID of the core that we want to launch.
|
||||
// 2nd: "main_core1" is the entry point for the core and we provide a total of 2kB of stack for it.
|
||||
// 3rd: Pointer to the core's stack memory array.
|
||||
// 4th: Size of the core's stack memory array.
|
||||
// 1st: "main_core1" is the entry point for the core.
|
||||
// 2nd: Pointer to the core's stack memory array.
|
||||
// 3rd: Size of the core's stack memory array.
|
||||
|
||||
int smp_launch_rc = neorv32_smp_launch(1, main_core1, (uint8_t*)core1_stack, sizeof(core1_stack));
|
||||
int smp_launch_rc = neorv32_smp_launch(main_core1, (uint8_t*)core1_stack, sizeof(core1_stack));
|
||||
|
||||
// Here we are using a statically allocated array as stack memory. Alternatively, malloc
|
||||
// could be used (it is recommend to align the stack memory on a 16-byte boundary):
|
||||
|
@ -100,102 +102,17 @@ int main(void) {
|
|||
// check if launching was successful
|
||||
if (smp_launch_rc) {
|
||||
neorv32_uart0_printf("[ERROR] Launching core1 failed (%d)!\n", smp_launch_rc);
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Core1 should be running now.
|
||||
// UART0 is used by both cores so it is a shared resource. We need to ensure exclusive
|
||||
// access. Therefore, we use a simple spinlock (based on atomic load-reservate /
|
||||
// store-conditional primitives).
|
||||
|
||||
// print message from core0
|
||||
// use spinlock to have exclusive access to UART0
|
||||
|
||||
// UART0 is used by both cores so it is a shared resource. We need to ensure exclusive
|
||||
// access by using a simple spinlock (based on atomic memory operations).
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("This is a message from core 0!\n");
|
||||
spin_unlock();
|
||||
|
||||
|
||||
// Test core0 RTE: raise an environment call exception
|
||||
// As the RTE's debug handler is using UART0 we should use the spinlock here, too
|
||||
spin_lock();
|
||||
asm volatile("ecall");
|
||||
spin_unlock();
|
||||
|
||||
|
||||
// enable machine-level interrupts and wait in sleep mode for the MTIMER interrupt
|
||||
neorv32_cpu_csr_set(CSR_MSTATUS, 1 << CSR_MSTATUS_MIE);
|
||||
while (1) {
|
||||
neorv32_cpu_sleep();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Main function for core 1 (secondary core).
|
||||
**************************************************************************/
|
||||
void main_core1(void) {
|
||||
|
||||
// setup NEORV32 runtime-environment (RTE) for _this_ core (core1)
|
||||
neorv32_rte_setup();
|
||||
|
||||
|
||||
// print message from core0
|
||||
// use spinlock to have exclusive access to UART0
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("Hello world! This is core1 running!\n");
|
||||
spin_unlock();
|
||||
|
||||
|
||||
// setup MTIMER interrupt for this core (core1)
|
||||
// the core-specific installation is handled entirely by the RTE
|
||||
neorv32_clint_mtimecmp_set(0); // initialize core-specific MTIMECMP
|
||||
neorv32_rte_handler_install(RTE_TRAP_MTI, clint_mtime_handler_core1); // install trap handler to RTE
|
||||
neorv32_cpu_csr_set(CSR_MIE, 1 << CSR_MIE_MTIE); // enable MTIMER interrupt source
|
||||
|
||||
|
||||
// Test core1 RTE: raise an environment call exception
|
||||
// As the RTE's debug handler is using UART0 we should use the spinlock here, too
|
||||
spin_lock();
|
||||
asm volatile("ecall");
|
||||
spin_unlock();
|
||||
|
||||
|
||||
// enable machine-level interrupts and wait in sleep mode
|
||||
neorv32_cpu_csr_set(CSR_MSTATUS, 1 << CSR_MSTATUS_MIE);
|
||||
while (1) {
|
||||
neorv32_cpu_sleep();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* CLINT machine timer interrupt handler for core0.
|
||||
**************************************************************************/
|
||||
void clint_mtime_handler_core0(void) {
|
||||
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("[core0] Primary core 1-second MTIMER interrupt. SMP is so cool!\n");
|
||||
spin_unlock();
|
||||
|
||||
// program next interrupt time (in 1 second)
|
||||
// this is automatically mapped to core0's MTIMECMP register
|
||||
neorv32_clint_mtimecmp_set(neorv32_clint_time_get() + 1*neorv32_sysinfo_get_clk());
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* CLINT machine timer interrupt handler for core1.
|
||||
**************************************************************************/
|
||||
void clint_mtime_handler_core1(void) {
|
||||
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("[core1] Secondary core 2-seconds MTIMER interrupt. Dual-core rules!\n");
|
||||
spin_unlock();
|
||||
|
||||
// program next interrupt time (in 2 seconds)
|
||||
// this is automatically mapped to core1's MTIMECMP register
|
||||
neorv32_clint_mtimecmp_set(neorv32_clint_time_get() + 2*neorv32_sysinfo_get_clk());
|
||||
return 0; // return to crt0 and halt
|
||||
}
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
/**
|
||||
* @file spinlock.c
|
||||
* @brief Single simple spin-lock based on atomic memory operations.
|
||||
* @brief Single simple spinlock based on atomic memory operations.
|
||||
*/
|
||||
#include <neorv32.h>
|
||||
|
||||
/**********************************************************************//**
|
||||
* Private spinlock locked variable. We can only use a single spinlock
|
||||
* as the processor only features a single reservation set.
|
||||
* Private spinlock locked variable.
|
||||
**************************************************************************/
|
||||
static volatile uint32_t __spin_locked = 0;
|
||||
|
||||
|
@ -14,7 +13,7 @@ static volatile uint32_t __spin_locked = 0;
|
|||
/**********************************************************************//**
|
||||
* Spinlock: set lock.
|
||||
*
|
||||
* @warning This function is blocking until the lock is acquired.
|
||||
* @warning This function is blocking until the lock is acquired and set.
|
||||
**************************************************************************/
|
||||
void spin_lock(void) {
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* @file spinlock.h
|
||||
* @brief Single simple spin-lock based on atomic lr/sc operations.
|
||||
* @brief Single simple spin-lock based on atomic memory operations.
|
||||
*/
|
||||
|
||||
#ifndef spinlock_h
|
||||
|
|
33
sw/example/demo_dual_core_icc/Makefile
Normal file
33
sw/example/demo_dual_core_icc/Makefile
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Application makefile.
|
||||
# Use this makefile to configure all relevant CPU / compiler options.
|
||||
|
||||
# Override the default CPU ISA
|
||||
MARCH = rv32ia_zicsr_zifencei
|
||||
|
||||
# Override the default RISC-V GCC prefix
|
||||
#RISCV_PREFIX ?= riscv-none-elf-
|
||||
|
||||
# Override default optimization goal
|
||||
EFFORT = -Os
|
||||
|
||||
# Add extended debug symbols
|
||||
USER_FLAGS += -ggdb -gdwarf-3
|
||||
|
||||
# Adjust processor IMEM size
|
||||
USER_FLAGS += -Wl,--defsym,__neorv32_rom_size=16k
|
||||
|
||||
# Adjust processor DMEM size
|
||||
USER_FLAGS += -Wl,--defsym,__neorv32_ram_size=8k
|
||||
|
||||
# Adjust maximum heap size
|
||||
#USER_FLAGS += -Wl,--defsym,__neorv32_heap_size=3k
|
||||
|
||||
# Additional sources
|
||||
#APP_SRC += $(wildcard ./*.c)
|
||||
#APP_INC += -I .
|
||||
|
||||
# Set path to NEORV32 root directory
|
||||
NEORV32_HOME ?= ../../..
|
||||
|
||||
# Include the main NEORV32 makefile
|
||||
include $(NEORV32_HOME)/sw/common/common.mk
|
158
sw/example/demo_dual_core_icc/main.c
Normal file
158
sw/example/demo_dual_core_icc/main.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
||||
/**********************************************************************//**
|
||||
* @file demo_dual_core_icc/main.c
|
||||
* @brief Set up the second core to accept, and run, any function pointer
|
||||
* pushed into its ICC FIFO. Push in a few pieces of code and get answers back.
|
||||
* Shamelessly copied from (BSD-3-Clause license):
|
||||
* https://github.com/raspberrypi/pico-examples/tree/master/multicore/multicore_runner
|
||||
**************************************************************************/
|
||||
#include <neorv32.h>
|
||||
|
||||
/** User configuration */
|
||||
#define BAUD_RATE 19200 // UART0 Baud rate
|
||||
#define TEST_NUM 11 // test number
|
||||
|
||||
/** Global variables */
|
||||
volatile uint8_t __attribute__ ((aligned (16))) core1_stack[2048]; // stack memory for core1
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Main function for core 1 (secondary core).
|
||||
*
|
||||
* @return Irrelevant (but can be inspected by the debugger).
|
||||
**************************************************************************/
|
||||
int core1_entry(void ) {
|
||||
|
||||
// setup NEORV32 runtime-environment (RTE) for _this_ core (core1)
|
||||
neorv32_rte_setup();
|
||||
|
||||
// Function pointer is passed via the ICC RX FIFO of _this_ core.
|
||||
// We have one incoming int32_t as a parameter, and will provide an
|
||||
// int32_t return value by simply pushing it back on the FIFO
|
||||
// which also indicates the result is ready.
|
||||
while (1) {
|
||||
int32_t (*func)() = (int32_t(*)())neorv32_smp_icc_pop();
|
||||
int32_t p = neorv32_smp_icc_pop();
|
||||
int32_t result = (*func)(p);
|
||||
neorv32_smp_icc_push(result);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Compute factorial.
|
||||
*
|
||||
* @param[in] n Compute factorial of n.
|
||||
* @return Factorial of n.
|
||||
**************************************************************************/
|
||||
int32_t factorial(int32_t n) {
|
||||
|
||||
int32_t i = 0, f = 1;
|
||||
for (i = 2; i <= n; i++) {
|
||||
f *= i;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Compute n-th Fibonacci number.
|
||||
*
|
||||
* @param[in] n Compute factorial of n.
|
||||
* @return n-th Fibonacci number.
|
||||
**************************************************************************/
|
||||
int32_t fibonacci(int32_t n) {
|
||||
|
||||
if (n == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (n == 1) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t i = 0, n1 = 0, n2 = 1, n3 = 0;
|
||||
for (i = 2; i <= n; i++) {
|
||||
n3 = n1 + n2;
|
||||
n1 = n2;
|
||||
n2 = n3;
|
||||
}
|
||||
return n3;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Set up the second core to accept, and run, any function pointer
|
||||
* pushed into its ICC FIFO. Push in a few pieces of code and get answers back.
|
||||
*
|
||||
* @warning This program requires the dual-core configuration, the CLINT and UART0.
|
||||
*
|
||||
* @note This program was "inspired" by https://github.com/raspberrypi/pico-examples/tree/master.
|
||||
* BSD-3-Clause license.
|
||||
*
|
||||
* @return Irrelevant (but can be inspected by the debugger).
|
||||
**************************************************************************/
|
||||
int main(void) {
|
||||
|
||||
// setup NEORV32 runtime-environment (RTE) for _this_ core (core0)
|
||||
neorv32_rte_setup();
|
||||
|
||||
|
||||
// setup UART0 at default baud rate, no interrupts
|
||||
if (neorv32_uart0_available() == 0) { // UART0 available?
|
||||
return -1;
|
||||
}
|
||||
neorv32_uart0_setup(BAUD_RATE, 0);
|
||||
neorv32_uart0_printf("\n<< NEORV32 SMP Dual-Core Inter-Core Communication Demo >>\n\n");
|
||||
|
||||
|
||||
// check hardware/software configuration
|
||||
if (neorv32_sysinfo_get_numcores() < 2) { // two cores available?
|
||||
neorv32_uart0_printf("[ERROR] dual-core option not enabled!\n");
|
||||
return -1;
|
||||
}
|
||||
if (neorv32_clint_available() == 0) { // CLINT available?
|
||||
neorv32_uart0_printf("[ERROR] CLINT module not available!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Core one is halted in crt0 right after reset and wait for its machine-level software
|
||||
// interrupt before resuming. Before the interrupt is triggered, a launch configuration
|
||||
// for core 1 has to be provided. This launch configuration defines the entry point for
|
||||
// core 1 as well as the stack setup. All this is handle by "neorv32_smp_launch()".
|
||||
|
||||
neorv32_uart0_printf("Launching core 1...\n");
|
||||
int smp_launch_rc = neorv32_smp_launch(core1_entry, (uint8_t*)core1_stack, sizeof(core1_stack));
|
||||
|
||||
// check if launching was successful
|
||||
if (smp_launch_rc) {
|
||||
neorv32_uart0_printf("[ERROR] Launching core1 failed (%d)!\n", smp_launch_rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// This example dispatches arbitrary functions to run on the second core. To do this we
|
||||
// run a dispatcher on the second core that accepts a function pointer and runs it.
|
||||
|
||||
neorv32_smp_icc_push((uintptr_t) &factorial);
|
||||
neorv32_smp_icc_push(TEST_NUM);
|
||||
// We could now do a load of stuff on core 0 and get our result later
|
||||
neorv32_uart0_printf("Factorial(%d) is %d\n", TEST_NUM, neorv32_smp_icc_pop());
|
||||
|
||||
// Now try a different function
|
||||
neorv32_smp_icc_push((uintptr_t) &fibonacci);
|
||||
neorv32_smp_icc_push(TEST_NUM);
|
||||
neorv32_uart0_printf("Fibonacci(%d) is %d\n", TEST_NUM, neorv32_smp_icc_pop());
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
33
sw/example/demo_dual_core_rte/Makefile
Normal file
33
sw/example/demo_dual_core_rte/Makefile
Normal file
|
@ -0,0 +1,33 @@
|
|||
# Application makefile.
|
||||
# Use this makefile to configure all relevant CPU / compiler options.
|
||||
|
||||
# Override the default CPU ISA
|
||||
MARCH = rv32ia_zicsr_zifencei
|
||||
|
||||
# Override the default RISC-V GCC prefix
|
||||
#RISCV_PREFIX ?= riscv-none-elf-
|
||||
|
||||
# Override default optimization goal
|
||||
EFFORT = -Os
|
||||
|
||||
# Add extended debug symbols
|
||||
USER_FLAGS += -ggdb -gdwarf-3
|
||||
|
||||
# Adjust processor IMEM size
|
||||
USER_FLAGS += -Wl,--defsym,__neorv32_rom_size=16k
|
||||
|
||||
# Adjust processor DMEM size
|
||||
USER_FLAGS += -Wl,--defsym,__neorv32_ram_size=8k
|
||||
|
||||
# Adjust maximum heap size
|
||||
#USER_FLAGS += -Wl,--defsym,__neorv32_heap_size=3k
|
||||
|
||||
# Additional sources
|
||||
#APP_SRC += $(wildcard ./*.c)
|
||||
#APP_INC += -I .
|
||||
|
||||
# Set path to NEORV32 root directory
|
||||
NEORV32_HOME ?= ../../..
|
||||
|
||||
# Include the main NEORV32 makefile
|
||||
include $(NEORV32_HOME)/sw/common/common.mk
|
203
sw/example/demo_dual_core_rte/main.c
Normal file
203
sw/example/demo_dual_core_rte/main.c
Normal file
|
@ -0,0 +1,203 @@
|
|||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
||||
/**********************************************************************//**
|
||||
* @file demo_dual_core_rte/main.c
|
||||
* @brief SMP dual-core program to show how to use the RTE on two cores.
|
||||
* This example runs the same code on both cores and triggers the timer
|
||||
* and software interrupts to showcase dual-core trap handling using the
|
||||
* NEORV32 runtime environment (RTE).
|
||||
**************************************************************************/
|
||||
#include <neorv32.h>
|
||||
#include "spinlock.h"
|
||||
|
||||
/** User configuration */
|
||||
#define BAUD_RATE 19200
|
||||
|
||||
/** Global variables */
|
||||
volatile uint8_t __attribute__ ((aligned (16))) core1_stack[2048]; // stack memory for core1
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Machine timer (CLINT) interrupt handler for BOTH cores.
|
||||
**************************************************************************/
|
||||
void trap_handler_mtmi(void) {
|
||||
|
||||
// find out which core is currently executing this
|
||||
uint32_t core_id = neorv32_smp_whoami();
|
||||
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("[core %u] MTIMER interrupt.\n", core_id);
|
||||
spin_unlock();
|
||||
|
||||
// compute next interrupt time
|
||||
uint64_t next_irq_time = neorv32_clint_time_get(); // current system time from CLINT.MTIME
|
||||
if (core_id == 0) {
|
||||
next_irq_time += 1 * neorv32_sysinfo_get_clk(); // 1 second for core 0
|
||||
}
|
||||
else {
|
||||
next_irq_time += 2 * neorv32_sysinfo_get_clk(); // 2 seconds for core 0
|
||||
}
|
||||
|
||||
// this is automatically mapped to the current core's MTIMECMP register
|
||||
neorv32_clint_mtimecmp_set(next_irq_time);
|
||||
|
||||
// trigger software interrupt of the other core
|
||||
if (core_id == 0) {
|
||||
neorv32_clint_msi_set(1); // trigger core 1
|
||||
}
|
||||
else {
|
||||
neorv32_clint_msi_set(0); // trigger core 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Machine software (CLINT) interrupt handler for BOTH cores.
|
||||
**************************************************************************/
|
||||
void trap_handler_mswi(void) {
|
||||
|
||||
// find out which core is currently executing this
|
||||
uint32_t core_id = neorv32_smp_whoami();
|
||||
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("[core %u] Software interrupt.\n", core_id);
|
||||
spin_unlock();
|
||||
|
||||
// clear software interrupt of current core
|
||||
neorv32_clint_msi_clr(core_id);
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Machine environment call trap handler for BOTH cores.
|
||||
**************************************************************************/
|
||||
void trap_handler_ecall(void) {
|
||||
|
||||
// find out which core is currently executing this
|
||||
uint32_t core_id = neorv32_smp_whoami();
|
||||
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("[core %u] Environment call.\n", core_id);
|
||||
spin_unlock();
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* "Application code" executed by BOTH cores.
|
||||
*
|
||||
* @return Irrelevant (but can be inspected by the debugger).
|
||||
**************************************************************************/
|
||||
int app_main(void) {
|
||||
|
||||
// (re-)setup NEORV32 runtime-environment (RTE) for the core that is executing this code
|
||||
neorv32_rte_setup();
|
||||
|
||||
|
||||
// print message; use spinlock to have exclusive access to UART0
|
||||
uint32_t core_id = neorv32_smp_whoami(); // find out which core is currently executing this
|
||||
spin_lock();
|
||||
neorv32_uart0_printf("[core %u] Hello world! This is core %u starting 'app_main()'.\n", core_id, core_id);
|
||||
spin_unlock();
|
||||
|
||||
|
||||
// The NEORV32 Runtime Environment (RTE) provides an internal trap vector table. Each entry
|
||||
// corresponds to a specific trap (exception or interrupt). Application software can install
|
||||
// specific trap handler function to take care of each type of trap.
|
||||
|
||||
// However, there is only a single trap vector table. Hence, both cores will execute the SAME
|
||||
// handler function if they encounter the same trap.
|
||||
|
||||
// setup machine timer interrupt for ALL cores
|
||||
neorv32_clint_mtimecmp_set(0); // initialize core-specific MTIMECMP
|
||||
neorv32_rte_handler_install(RTE_TRAP_MTI, trap_handler_mtmi); // install trap handler
|
||||
neorv32_cpu_csr_set(CSR_MIE, 1 << CSR_MIE_MTIE); // enable interrupt source
|
||||
|
||||
// setup machine software interrupt for ALL cores
|
||||
neorv32_rte_handler_install(RTE_TRAP_MSI, trap_handler_mswi); // install trap handler
|
||||
neorv32_cpu_csr_set(CSR_MIE, 1 << CSR_MIE_MSIE); // enable interrupt source
|
||||
|
||||
// setup machine environment call trap for ALL cores
|
||||
neorv32_rte_handler_install(RTE_TRAP_MENV_CALL, trap_handler_ecall); // install trap handler
|
||||
|
||||
|
||||
// trigger environment call exception (just to test the according handler)
|
||||
asm volatile ("ecall");
|
||||
|
||||
// enable machine-level interrupts and wait in sleep mode
|
||||
neorv32_cpu_csr_set(CSR_MSTATUS, 1 << CSR_MSTATUS_MIE);
|
||||
while (1) {
|
||||
neorv32_cpu_sleep();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Main function for core 0 (primary core).
|
||||
*
|
||||
* @warning This program requires the dual-core configuration, the CLINT, UART0
|
||||
* and the A/Zaamo ISA extension.
|
||||
*
|
||||
* @return Irrelevant (but can be inspected by the debugger).
|
||||
**************************************************************************/
|
||||
int main(void) {
|
||||
|
||||
// setup NEORV32 runtime-environment (RTE) for _this_ core (core 0)
|
||||
// this is not required but keeps us safe
|
||||
neorv32_rte_setup();
|
||||
|
||||
|
||||
// setup UART0 at default baud rate, no interrupts
|
||||
if (neorv32_uart0_available() == 0) { // UART0 available?
|
||||
return -1;
|
||||
}
|
||||
neorv32_uart0_setup(BAUD_RATE, 0);
|
||||
neorv32_uart0_printf("\n<< NEORV32 SMP Dual-Core RTE Demo >>\n\n");
|
||||
|
||||
|
||||
// check hardware/software configuration
|
||||
if (neorv32_sysinfo_get_numcores() < 2) { // two cores available?
|
||||
neorv32_uart0_printf("[ERROR] dual-core option not enabled!\n");
|
||||
return -1;
|
||||
}
|
||||
if (neorv32_clint_available() == 0) { // CLINT available?
|
||||
neorv32_uart0_printf("[ERROR] CLINT module not available!\n");
|
||||
return -1;
|
||||
}
|
||||
if ((neorv32_cpu_csr_read(CSR_MXISA) & (1<<CSR_MXISA_ZAAMO)) == 0) { // atomic memory operations available?
|
||||
neorv32_uart0_printf("[ERROR] 'A'/'Zaamo' ISA extension not available!\n");
|
||||
return -1;
|
||||
}
|
||||
#ifndef __riscv_atomic
|
||||
#warning "Application has to be compiled with RISC-V 'A' ISA extension!"
|
||||
neorv32_uart0_printf("[ERROR] Application has to be compiled with 'A' ISA extension!\n");
|
||||
return -1;
|
||||
#endif
|
||||
|
||||
|
||||
// initialize _global_ system timer (CLINT's machine timer)
|
||||
neorv32_clint_time_set(0);
|
||||
|
||||
|
||||
// start core 1
|
||||
neorv32_uart0_printf("Launching core 1...\n");
|
||||
int smp_launch_rc = neorv32_smp_launch(app_main, (uint8_t*)core1_stack, sizeof(core1_stack));
|
||||
if (smp_launch_rc) { // check if launching was successful
|
||||
neorv32_uart0_printf("[ERROR] Launching core 1 failed (%d)!\n", smp_launch_rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// start the "application code" that is executed by both cores
|
||||
app_main();
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
31
sw/example/demo_dual_core_rte/spinlock.c
Normal file
31
sw/example/demo_dual_core_rte/spinlock.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @file spinlock.c
|
||||
* @brief Single simple spinlock based on atomic memory operations.
|
||||
*/
|
||||
#include <neorv32.h>
|
||||
|
||||
/**********************************************************************//**
|
||||
* Private spinlock locked variable.
|
||||
**************************************************************************/
|
||||
static volatile uint32_t __spin_locked = 0;
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Spinlock: set lock.
|
||||
*
|
||||
* @warning This function is blocking until the lock is acquired and set.
|
||||
**************************************************************************/
|
||||
void spin_lock(void) {
|
||||
|
||||
while(__sync_lock_test_and_set(&__spin_locked, -1)); // -> amoswap.w
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Spinlock: remove lock.
|
||||
**************************************************************************/
|
||||
void spin_unlock(void) {
|
||||
|
||||
//__sync_lock_release(&__spin_locked); // uses fence that is not required here
|
||||
__sync_lock_test_and_set(&__spin_locked, 0); // -> amoswap.w
|
||||
}
|
12
sw/example/demo_dual_core_rte/spinlock.h
Normal file
12
sw/example/demo_dual_core_rte/spinlock.h
Normal file
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* @file spinlock.h
|
||||
* @brief Single simple spin-lock based on atomic memory operations.
|
||||
*/
|
||||
|
||||
#ifndef spinlock_h
|
||||
#define spinlock_h
|
||||
|
||||
void spin_lock(void);
|
||||
void spin_unlock(void);
|
||||
|
||||
#endif // spinlock_h
|
|
@ -68,7 +68,7 @@ void xirq_trap_handler0(void);
|
|||
void xirq_trap_handler1(void);
|
||||
void test_ok(void);
|
||||
void test_fail(void);
|
||||
void core1_main(void);
|
||||
int core1_main(void);
|
||||
|
||||
// MCAUSE value that will be NEVER set by the hardware
|
||||
const uint32_t mcause_never_c = 0x80000000UL; // = reserved
|
||||
|
@ -2154,7 +2154,7 @@ int main() {
|
|||
neorv32_cpu_csr_write(CSR_MIE, 1 << CSR_MIE_MSIE);
|
||||
|
||||
// launch core 1
|
||||
tmp_a = (uint32_t)neorv32_smp_launch(1, core1_main, (uint8_t*)core1_stack, sizeof(core1_stack));
|
||||
tmp_a = (uint32_t)neorv32_smp_launch(core1_main, (uint8_t*)core1_stack, sizeof(core1_stack));
|
||||
|
||||
// wait for software interrupt (issued by core 1) in sleep mode
|
||||
neorv32_cpu_sleep();
|
||||
|
@ -2405,8 +2405,10 @@ void test_fail(void) {
|
|||
/**********************************************************************//**
|
||||
* Test code to be run on second CPU core
|
||||
**************************************************************************/
|
||||
void core1_main(void) {
|
||||
int core1_main(void) {
|
||||
|
||||
// trigger software interrupt of core0
|
||||
neorv32_clint_msi_set(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
/**
|
||||
* @file neorv32_aux.h
|
||||
* @brief General auxiliary functions header file.
|
||||
* @see https://stnolting.github.io/neorv32/sw/files.html
|
||||
*/
|
||||
|
||||
#ifndef neorv32_aux_h
|
||||
|
@ -51,7 +50,7 @@ uint64_t neorv32_aux_hexstr2uint64(char *buffer, unsigned int length);
|
|||
uint32_t neorv32_aux_xorshift32(void);
|
||||
void neorv32_aux_itoa(char *buffer, uint32_t num, uint32_t base);
|
||||
void neorv32_aux_print_hw_config(void);
|
||||
void neorv32_aux_print_hw_version(void);
|
||||
void neorv32_aux_print_hw_version(uint32_t impid);
|
||||
void neorv32_aux_print_about(void);
|
||||
void neorv32_aux_print_logo(void);
|
||||
void neorv32_aux_print_license(void);
|
||||
|
|
|
@ -138,10 +138,8 @@ enum NEORV32_CSR_enum {
|
|||
CSR_MHPMCOUNTER15H = 0xb8f, /**< 0xb8f - mhpmcounter15h: Machine hardware performance monitor 15 counter high word */
|
||||
|
||||
/* inter-core communication */
|
||||
CSR_MXICCRXD = 0xbc0, /**< 0xbc0 - mxiccrxd: Machine ICC link RX data */
|
||||
CSR_MXICCTXD = 0xbc1, /**< 0xbc1 - mxicctxd: Machine ICC link TX data */
|
||||
CSR_MXICCSR0 = 0xbc2, /**< 0xbc1 - mxiccsr0: Machine ICC link status register 0 (#NEORV32_CSR_MXICCSR_enum) */
|
||||
CSR_MXICCSR1 = 0xbc3, /**< 0xbc1 - mxiccsr1: Machine ICC link status register 1 (#NEORV32_CSR_MXICCSR_enum) */
|
||||
CSR_MXICCSREG = 0xbc0, /**< 0xbc0 - mxiccsreg: Machine ICC status register (#)*/
|
||||
CSR_MXICCDATA = 0xbc1, /**< 0xbc1 - mxiccdata: Machine ICC RX/TX data register */
|
||||
|
||||
/* user counters and timers */
|
||||
CSR_CYCLE = 0xc00, /**< 0xc00 - cycle: User cycle counter low word */
|
||||
|
@ -347,14 +345,11 @@ enum NEORV32_CSR_MXISA_enum {
|
|||
|
||||
|
||||
/**********************************************************************//**
|
||||
* CPU mxiccsr CSR (r/w): Inter-core communication control and status (NEORV32-specific)
|
||||
* CPU mxiccsreg CSR (r/w): Inter-core communication status register (NEORV32-specific)
|
||||
**************************************************************************/
|
||||
enum NEORV32_CSR_MXICCSR_enum {
|
||||
CSR_MXICCSR_LINK_LSB = 0, /**< CPU mxiccsr CSR (0): link/hart select LSB (r/w)*/
|
||||
CSR_MXICCSR_LINK_MSB = 1, /**< CPU mxiccsr CSR (1): link/hart select MSB (r/w)*/
|
||||
|
||||
CSR_MXICCSR_TX_FREE = 30, /**< CPU mxiccsr CSR (30): Free space in selected link's TX FIFO (r/-)*/
|
||||
CSR_MXICCSR_RX_AVAIL = 31 /**< CPU mxiccsr CSR (31): Data available in selected link's RX FIFO (r/-)*/
|
||||
enum NEORV32_CSR_MXICCSREG_enum {
|
||||
CSR_MXICCSREG_RX_AVAIL = 0, /**< CPU mxiccsreg CSR (0): Data available in link's RX FIFO (r/-)*/
|
||||
CSR_MXICCSREG_TX_FREE = 1 /**< CPU mxiccsreg CSR (1): Free space in link's TX FIFO (r/-)*/
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* NEORV32 runtime environment trap IDs.
|
||||
**************************************************************************/
|
||||
/**@{*/
|
||||
/**< Trap ID enumeration */
|
||||
enum NEORV32_RTE_TRAP_enum {
|
||||
RTE_TRAP_I_ACCESS = 0, /**< Instruction access fault */
|
||||
RTE_TRAP_I_ILLEGAL = 1, /**< Illegal instruction */
|
||||
|
@ -53,6 +54,7 @@ enum NEORV32_RTE_TRAP_enum {
|
|||
RTE_TRAP_FIRQ_14 = 27, /**< Fast interrupt channel 14 */
|
||||
RTE_TRAP_FIRQ_15 = 28 /**< Fast interrupt channel 15 */
|
||||
};
|
||||
/**< Total number of trap IDs */
|
||||
#define NEORV32_RTE_NUM_TRAPS 29
|
||||
/**@}*/
|
||||
|
||||
|
@ -63,7 +65,6 @@ enum NEORV32_RTE_TRAP_enum {
|
|||
void neorv32_rte_setup(void);
|
||||
void neorv32_rte_core(void);
|
||||
int neorv32_rte_handler_install(int id, void (*handler)(void));
|
||||
int neorv32_rte_handler_uninstall(int id);
|
||||
void neorv32_rte_debug_handler(void);
|
||||
uint32_t neorv32_rte_context_get(int x);
|
||||
void neorv32_rte_context_put(int x, uint32_t data);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
/**
|
||||
* @file neorv32_smp.h
|
||||
* @brief SMP HW driver header file.
|
||||
* @brief Symmetric multiprocessing (SMP) library header file.
|
||||
*/
|
||||
|
||||
#ifndef neorv32_smp_h
|
||||
|
@ -19,7 +19,9 @@
|
|||
* @name Prototypes
|
||||
**************************************************************************/
|
||||
/**@{*/
|
||||
int neorv32_smp_launch(int hart_id, void (*entry_point)(void), uint8_t* stack_memory, size_t stack_size_bytes);
|
||||
int neorv32_smp_launch(int (*entry_point)(void), uint8_t* stack_memory, size_t stack_size_bytes);
|
||||
void neorv32_smp_icc_push(uint32_t data);
|
||||
uint32_t neorv32_smp_icc_pop(void);
|
||||
/**@}*/
|
||||
|
||||
|
||||
|
@ -35,56 +37,48 @@ inline uint32_t __attribute__ ((always_inline)) neorv32_smp_whoami(void) {
|
|||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Get data from core via ICC link.
|
||||
* Get data from other core via ICC link.
|
||||
* Check link status before #neorv32_smp_icc_avail().
|
||||
*
|
||||
* @param[in] hart_sel Source core.
|
||||
* @return Data word (32-bit) received from selected core.
|
||||
* @return Data word (32-bit) received from other core.
|
||||
**************************************************************************/
|
||||
inline uint32_t __attribute__ ((always_inline)) neorv32_smp_icc_get(int hart_sel) {
|
||||
inline uint32_t __attribute__ ((always_inline)) neorv32_smp_icc_get(void) {
|
||||
|
||||
neorv32_cpu_csr_write(CSR_MXICCSR0, (uint32_t)hart_sel);
|
||||
return neorv32_cpu_csr_read(CSR_MXICCRXD);
|
||||
return neorv32_cpu_csr_read(CSR_MXICCDATA);
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Send data to core via ICC link.
|
||||
* Send data to other core via ICC link.
|
||||
* Check link status before #neorv32_smp_icc_free().
|
||||
*
|
||||
* @param[in] hart_sel Destination core.
|
||||
* @param[in] data Data word (32-bit) to be send to selected core.
|
||||
* @param[in] data Data word (32-bit) to be send to other core.
|
||||
**************************************************************************/
|
||||
inline void __attribute__ ((always_inline)) neorv32_smp_icc_put(int hart_sel, uint32_t data) {
|
||||
inline void __attribute__ ((always_inline)) neorv32_smp_icc_put(uint32_t data) {
|
||||
|
||||
neorv32_cpu_csr_write(CSR_MXICCSR0, (uint32_t)hart_sel);
|
||||
neorv32_cpu_csr_write(CSR_MXICCTXD, data);
|
||||
neorv32_cpu_csr_write(CSR_MXICCDATA, data);
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Check if ICC link data is available.
|
||||
*
|
||||
* @param[in] hart_sel Source core.
|
||||
* @return 0 = no data available, nonzero = data available.
|
||||
**************************************************************************/
|
||||
inline int __attribute__ ((always_inline)) neorv32_smp_icc_avail(int hart_sel) {
|
||||
inline int __attribute__ ((always_inline)) neorv32_smp_icc_avail(void) {
|
||||
|
||||
neorv32_cpu_csr_write(CSR_MXICCSR0, (uint32_t)hart_sel);
|
||||
return neorv32_cpu_csr_read(CSR_MXICCSR0) & (1 << CSR_MXICCSR_RX_AVAIL);
|
||||
return neorv32_cpu_csr_read(CSR_MXICCSREG) & (1 << CSR_MXICCSREG_RX_AVAIL);
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Check if free space in ICC link.
|
||||
*
|
||||
* @param[in] hart_sel Destination core.
|
||||
* @return 0 = no free space available, nonzero = free space available.
|
||||
**************************************************************************/
|
||||
inline int __attribute__ ((always_inline)) neorv32_smp_icc_free(int hart_sel) {
|
||||
inline int __attribute__ ((always_inline)) neorv32_smp_icc_free(void) {
|
||||
|
||||
neorv32_cpu_csr_write(CSR_MXICCSR0, (uint32_t)hart_sel);
|
||||
return neorv32_cpu_csr_read(CSR_MXICCSR0) & (1 << CSR_MXICCSR_TX_FREE);
|
||||
return neorv32_cpu_csr_read(CSR_MXICCSREG) & (1 << CSR_MXICCSREG_TX_FREE);
|
||||
}
|
||||
|
||||
#endif // neorv32_smp_h
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
/**
|
||||
* @file neorv32_aux.c
|
||||
* @brief General auxiliary functions source file.
|
||||
* @see https://stnolting.github.io/neorv32/sw/files.html
|
||||
*/
|
||||
|
||||
#include <neorv32.h>
|
||||
|
@ -302,7 +301,7 @@ void neorv32_aux_print_hw_config(void) {
|
|||
neorv32_cpu_csr_read(CSR_MIMPID));
|
||||
// hardware version
|
||||
neorv32_uart0_printf(" (v");
|
||||
neorv32_aux_print_hw_version();
|
||||
neorv32_aux_print_hw_version(neorv32_cpu_csr_read(CSR_MIMPID));
|
||||
neorv32_uart0_printf(")\n");
|
||||
|
||||
// CPU architecture and endianness
|
||||
|
@ -535,9 +534,12 @@ void neorv32_aux_print_hw_config(void) {
|
|||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Print the processor version in human-readable format via UART0.
|
||||
* Print processor version in human-readable format via UART0.
|
||||
*
|
||||
* @param[in] impid BCD-coded implementation ID (aka the version),
|
||||
* typically from the mimpid CSR.
|
||||
**************************************************************************/
|
||||
void neorv32_aux_print_hw_version(void) {
|
||||
void neorv32_aux_print_hw_version(uint32_t impid) {
|
||||
|
||||
uint32_t i;
|
||||
char tmp, cnt;
|
||||
|
@ -545,7 +547,7 @@ void neorv32_aux_print_hw_version(void) {
|
|||
if (neorv32_uart0_available() != 0) { // cannot output anything if UART0 is not implemented
|
||||
for (i=0; i<4; i++) {
|
||||
|
||||
tmp = (char)(neorv32_cpu_csr_read(CSR_MIMPID) >> (24 - 8*i));
|
||||
tmp = (char)(impid >> (24 - 8*i));
|
||||
|
||||
// serial division
|
||||
cnt = 0;
|
||||
|
|
|
@ -19,11 +19,11 @@
|
|||
// RTE private variables and functions
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
// the private trap vector look-up table for each CPU core
|
||||
static uint32_t __neorv32_rte_vector_lut[2][NEORV32_RTE_NUM_TRAPS];
|
||||
// private trap vector look-up table (for all cores!)
|
||||
static volatile uint32_t __neorv32_rte_vector_lut[NEORV32_RTE_NUM_TRAPS];
|
||||
|
||||
// private helper function
|
||||
static void __neorv32_rte_print_hex_word(uint32_t num);
|
||||
// private helper functions
|
||||
static void __neorv32_rte_print_hex(uint32_t num, int digits);
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
@ -34,11 +34,11 @@ static void __neorv32_rte_print_hex_word(uint32_t num);
|
|||
* NEORV32 runtime environment (RTE):
|
||||
* Setup RTE.
|
||||
*
|
||||
* @attention This function must be called on all cores that wish to use the RTE.
|
||||
* @note This function must be called on all cores that wish to use the RTE.
|
||||
*
|
||||
* @note This function installs a debug handler for ALL trap sources, which
|
||||
* gives detailed information about the trap. Actual handlers can be installed afterwards
|
||||
* via neorv32_rte_handler_install(uint8_t id, void (*handler)(void)).
|
||||
* gives detailed information about the trap via UART0 (if available). Actual
|
||||
* handlers can be installed afterwards via #neorv32_rte_handler_install().
|
||||
**************************************************************************/
|
||||
void neorv32_rte_setup(void) {
|
||||
|
||||
|
@ -51,14 +51,15 @@ void neorv32_rte_setup(void) {
|
|||
// disable all IRQ channels
|
||||
neorv32_cpu_csr_write(CSR_MIE, 0);
|
||||
|
||||
// install debug handler for all trap sources
|
||||
int id;
|
||||
for (id = 0; id < ((int)NEORV32_RTE_NUM_TRAPS); id++) {
|
||||
neorv32_rte_handler_uninstall(id); // this will configure the debug handler
|
||||
// install debug handler for all trap sources (only on core 0)
|
||||
if (neorv32_cpu_csr_read(CSR_MHARTID) == 0) {
|
||||
int index;
|
||||
for (index = 0; index < ((int)NEORV32_RTE_NUM_TRAPS); index++) {
|
||||
__neorv32_rte_vector_lut[index] = (uint32_t)(&neorv32_rte_debug_handler);
|
||||
}
|
||||
asm volatile ("fence"); // flush handler table to main memory
|
||||
}
|
||||
|
||||
// flush to main memory
|
||||
asm volatile ("fence");
|
||||
}
|
||||
|
||||
|
||||
|
@ -66,49 +67,37 @@ void neorv32_rte_setup(void) {
|
|||
* NEORV32 runtime environment (RTE):
|
||||
* Install trap handler function (second-level trap handler).
|
||||
*
|
||||
* @attention This function operates on the RTE configuration of the core on which this function is executed.
|
||||
* @note Trap handler installation applies to both cores. Hence, both
|
||||
* cores will execute the same handler for the same trap.
|
||||
*
|
||||
* @param[in] id Identifier (type) of the targeted trap. See #NEORV32_RTE_TRAP_enum.
|
||||
* @param[in] handler The actual handler function for the specified trap (function MUST be of type "void function(void);").
|
||||
* @return 0 if success, -1 if error (invalid id or targeted trap not supported).
|
||||
* @param[in] id Identifier (type) of the targeted trap
|
||||
* See #NEORV32_RTE_TRAP_enum.
|
||||
*
|
||||
* @param[in] handler The actual handler function for the specified trap
|
||||
* (function MUST be of type "void function(void);").
|
||||
*
|
||||
* @return 0 if success, -1 if invalid trap ID.
|
||||
**************************************************************************/
|
||||
int neorv32_rte_handler_install(int id, void (*handler)(void)) {
|
||||
|
||||
// check if invalid trap ID
|
||||
uint32_t index = (uint32_t)id;
|
||||
if (index < ((uint32_t)NEORV32_RTE_NUM_TRAPS)) { // id valid?
|
||||
uint32_t hart_id = neorv32_cpu_csr_read(CSR_MHARTID) & 1;
|
||||
__neorv32_rte_vector_lut[hart_id][index] = (uint32_t)handler; // install handler
|
||||
return 0;
|
||||
if (index >= NEORV32_RTE_NUM_TRAPS) {
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
|
||||
// install handler
|
||||
__neorv32_rte_vector_lut[index] = (uint32_t)handler;
|
||||
asm volatile ("fence"); // flush updated handler table to main memory
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* NEORV32 runtime environment (RTE):
|
||||
* Uninstall trap handler function from NEORV32 runtime environment, which was
|
||||
* previously installed via neorv32_rte_handler_install(uint8_t id, void (*handler)(void)).
|
||||
*
|
||||
* @attention This function operates on the RTE configuration of the core on which this function is executed.
|
||||
*
|
||||
* @param[in] id Identifier (type) of the targeted trap. See #NEORV32_RTE_TRAP_enum.
|
||||
* @return 0 if success, -1 if error (invalid id or targeted trap not supported).
|
||||
**************************************************************************/
|
||||
int neorv32_rte_handler_uninstall(int id) {
|
||||
|
||||
uint32_t index = (uint32_t)id;
|
||||
if (index < ((uint32_t)NEORV32_RTE_NUM_TRAPS)) { // id valid?
|
||||
uint32_t hart_id = neorv32_cpu_csr_read(CSR_MHARTID) & 1;
|
||||
__neorv32_rte_vector_lut[hart_id][index] = (uint32_t)(&neorv32_rte_debug_handler); // use dummy handler in case the trap is accidentally triggered
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* NEORV32 runtime environment (RTE):
|
||||
* This is the core of the NEORV32 RTE (first-level trap handler, executed in machine mode).
|
||||
* This is the core of the NEORV32 RTE (first-level trap handler,
|
||||
* executed in machine mode).
|
||||
**************************************************************************/
|
||||
void __attribute__((__naked__,aligned(4))) neorv32_rte_core(void) {
|
||||
|
||||
|
@ -161,39 +150,42 @@ void __attribute__((__naked__,aligned(4))) neorv32_rte_core(void) {
|
|||
#endif
|
||||
);
|
||||
|
||||
// flush context (stack frame) to main memory
|
||||
// reload trap table from main memory
|
||||
asm volatile ("fence");
|
||||
|
||||
// find according trap handler base address
|
||||
uint32_t hart_id = neorv32_cpu_csr_read(CSR_MHARTID) & 1;
|
||||
uint32_t handler_base;
|
||||
switch (neorv32_cpu_csr_read(CSR_MCAUSE)) {
|
||||
case TRAP_CODE_I_ACCESS: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_I_ACCESS]; break;
|
||||
case TRAP_CODE_I_ILLEGAL: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_I_ILLEGAL]; break;
|
||||
case TRAP_CODE_I_MISALIGNED: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_I_MISALIGNED]; break;
|
||||
case TRAP_CODE_BREAKPOINT: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_BREAKPOINT]; break;
|
||||
case TRAP_CODE_L_MISALIGNED: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_L_MISALIGNED]; break;
|
||||
case TRAP_CODE_L_ACCESS: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_L_ACCESS]; break;
|
||||
case TRAP_CODE_S_MISALIGNED: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_S_MISALIGNED]; break;
|
||||
case TRAP_CODE_S_ACCESS: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_S_ACCESS]; break;
|
||||
case TRAP_CODE_UENV_CALL: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_UENV_CALL]; break;
|
||||
case TRAP_CODE_MENV_CALL: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_MENV_CALL]; break;
|
||||
case TRAP_CODE_MSI: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_MSI]; break;
|
||||
case TRAP_CODE_MTI: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_MTI]; break;
|
||||
case TRAP_CODE_MEI: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_MEI]; break;
|
||||
case TRAP_CODE_FIRQ_0: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_0]; break;
|
||||
case TRAP_CODE_FIRQ_1: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_1]; break;
|
||||
case TRAP_CODE_FIRQ_2: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_2]; break;
|
||||
case TRAP_CODE_FIRQ_3: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_3]; break;
|
||||
case TRAP_CODE_FIRQ_4: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_4]; break;
|
||||
case TRAP_CODE_FIRQ_5: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_5]; break;
|
||||
case TRAP_CODE_FIRQ_6: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_6]; break;
|
||||
case TRAP_CODE_FIRQ_7: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_7]; break;
|
||||
case TRAP_CODE_FIRQ_8: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_8]; break;
|
||||
case TRAP_CODE_FIRQ_9: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_9]; break;
|
||||
case TRAP_CODE_FIRQ_10: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_10]; break;
|
||||
case TRAP_CODE_FIRQ_11: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_11]; break;
|
||||
case TRAP_CODE_FIRQ_12: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_12]; break;
|
||||
case TRAP_CODE_FIRQ_13: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_13]; break;
|
||||
case TRAP_CODE_FIRQ_14: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_14]; break;
|
||||
case TRAP_CODE_FIRQ_15: handler_base = __neorv32_rte_vector_lut[hart_id][RTE_TRAP_FIRQ_15]; break;
|
||||
case TRAP_CODE_I_ACCESS: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_I_ACCESS]; break;
|
||||
case TRAP_CODE_I_ILLEGAL: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_I_ILLEGAL]; break;
|
||||
case TRAP_CODE_I_MISALIGNED: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_I_MISALIGNED]; break;
|
||||
case TRAP_CODE_BREAKPOINT: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_BREAKPOINT]; break;
|
||||
case TRAP_CODE_L_MISALIGNED: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_L_MISALIGNED]; break;
|
||||
case TRAP_CODE_L_ACCESS: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_L_ACCESS]; break;
|
||||
case TRAP_CODE_S_MISALIGNED: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_S_MISALIGNED]; break;
|
||||
case TRAP_CODE_S_ACCESS: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_S_ACCESS]; break;
|
||||
case TRAP_CODE_UENV_CALL: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_UENV_CALL]; break;
|
||||
case TRAP_CODE_MENV_CALL: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_MENV_CALL]; break;
|
||||
case TRAP_CODE_MSI: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_MSI]; break;
|
||||
case TRAP_CODE_MTI: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_MTI]; break;
|
||||
case TRAP_CODE_MEI: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_MEI]; break;
|
||||
case TRAP_CODE_FIRQ_0: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_0]; break;
|
||||
case TRAP_CODE_FIRQ_1: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_1]; break;
|
||||
case TRAP_CODE_FIRQ_2: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_2]; break;
|
||||
case TRAP_CODE_FIRQ_3: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_3]; break;
|
||||
case TRAP_CODE_FIRQ_4: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_4]; break;
|
||||
case TRAP_CODE_FIRQ_5: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_5]; break;
|
||||
case TRAP_CODE_FIRQ_6: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_6]; break;
|
||||
case TRAP_CODE_FIRQ_7: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_7]; break;
|
||||
case TRAP_CODE_FIRQ_8: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_8]; break;
|
||||
case TRAP_CODE_FIRQ_9: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_9]; break;
|
||||
case TRAP_CODE_FIRQ_10: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_10]; break;
|
||||
case TRAP_CODE_FIRQ_11: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_11]; break;
|
||||
case TRAP_CODE_FIRQ_12: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_12]; break;
|
||||
case TRAP_CODE_FIRQ_13: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_13]; break;
|
||||
case TRAP_CODE_FIRQ_14: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_14]; break;
|
||||
case TRAP_CODE_FIRQ_15: handler_base = __neorv32_rte_vector_lut[RTE_TRAP_FIRQ_15]; break;
|
||||
default: handler_base = (uint32_t)(&neorv32_rte_debug_handler); break;
|
||||
}
|
||||
|
||||
|
@ -267,9 +259,11 @@ void __attribute__((__naked__,aligned(4))) neorv32_rte_core(void) {
|
|||
* NEORV32 runtime environment (RTE):
|
||||
* Read register from application context (on stack).
|
||||
*
|
||||
* @attention This function operates on the RTE configuration of the core on which this function is executed.
|
||||
* @note This function operates on the RTE instance of the
|
||||
* core on which this function is executed.
|
||||
*
|
||||
* @param[in] x Register number (0..31, corresponds to register x0..x31).
|
||||
*
|
||||
* @return Content of register x.
|
||||
**************************************************************************/
|
||||
uint32_t neorv32_rte_context_get(int x) {
|
||||
|
@ -289,9 +283,11 @@ uint32_t neorv32_rte_context_get(int x) {
|
|||
* NEORV32 runtime environment (RTE):
|
||||
* Write register to application context (on stack).
|
||||
*
|
||||
* @attention This function operates on the RTE configuration of the core on which this function is executed.
|
||||
* @note This function operates on the RTE instance of the
|
||||
* core on which this function is executed.
|
||||
*
|
||||
* @param[in] x Register number (0..31, corresponds to register x0..x31).
|
||||
*
|
||||
* @param[in] data Data to be written to register x.
|
||||
**************************************************************************/
|
||||
void neorv32_rte_context_put(int x, uint32_t data) {
|
||||
|
@ -311,7 +307,8 @@ void neorv32_rte_context_put(int x, uint32_t data) {
|
|||
* NEORV32 runtime environment (RTE):
|
||||
* Debug trap handler, printing information via UART0.
|
||||
*
|
||||
* @attention This function operates on the RTE configuration of the core on which this function is executed.
|
||||
* @note This function operates on the RTE instance of the
|
||||
* core on which this function is executed.
|
||||
**************************************************************************/
|
||||
void neorv32_rte_debug_handler(void) {
|
||||
|
||||
|
@ -323,12 +320,11 @@ void neorv32_rte_debug_handler(void) {
|
|||
neorv32_uart0_puts("<NEORV32-RTE> ");
|
||||
|
||||
// core ID
|
||||
uint32_t hart_id = neorv32_cpu_csr_read(CSR_MHARTID) & 1;
|
||||
if (hart_id) {
|
||||
neorv32_uart0_puts("core1: ");
|
||||
if (neorv32_cpu_csr_read(CSR_MHARTID) & 1) {
|
||||
neorv32_uart0_puts("[cpu1] ");
|
||||
}
|
||||
else {
|
||||
neorv32_uart0_puts("core0: ");
|
||||
neorv32_uart0_puts("[cpu0] ");
|
||||
}
|
||||
|
||||
// privilege level of the CPU when the trap occurred
|
||||
|
@ -370,25 +366,25 @@ void neorv32_rte_debug_handler(void) {
|
|||
case TRAP_CODE_FIRQ_12:
|
||||
case TRAP_CODE_FIRQ_13:
|
||||
case TRAP_CODE_FIRQ_14:
|
||||
case TRAP_CODE_FIRQ_15: neorv32_uart0_puts("Fast IRQ "); __neorv32_rte_print_hex_word(trap_cause & 0xf); break;
|
||||
default: neorv32_uart0_puts("Unknown trap cause "); __neorv32_rte_print_hex_word(trap_cause); break;
|
||||
case TRAP_CODE_FIRQ_15: neorv32_uart0_puts("Fast IRQ "); __neorv32_rte_print_hex(trap_cause, 1); break;
|
||||
default: neorv32_uart0_puts("Unknown trap cause "); __neorv32_rte_print_hex(trap_cause, 8); break;
|
||||
}
|
||||
|
||||
// instruction address
|
||||
neorv32_uart0_puts(" @ PC=");
|
||||
__neorv32_rte_print_hex_word(neorv32_cpu_csr_read(CSR_MEPC));
|
||||
__neorv32_rte_print_hex(neorv32_cpu_csr_read(CSR_MEPC), 8);
|
||||
|
||||
// trapping instruction
|
||||
neorv32_uart0_puts(", MTINST=");
|
||||
__neorv32_rte_print_hex_word(neorv32_cpu_csr_read(CSR_MTINST));
|
||||
__neorv32_rte_print_hex(neorv32_cpu_csr_read(CSR_MTINST), 8);
|
||||
|
||||
// trap value
|
||||
neorv32_uart0_puts(", MTVAL=");
|
||||
__neorv32_rte_print_hex_word(neorv32_cpu_csr_read(CSR_MTVAL));
|
||||
__neorv32_rte_print_hex(neorv32_cpu_csr_read(CSR_MTVAL), 8);
|
||||
|
||||
// unhandled IRQ - disable interrupt channel
|
||||
if (((int32_t)trap_cause) < 0) { // is interrupt
|
||||
neorv32_uart0_puts(" Disabling IRQ source\n");
|
||||
neorv32_uart0_puts(" Disabling IRQ source");
|
||||
neorv32_cpu_csr_clr(CSR_MIE, 1 << (trap_cause & 0x1f));
|
||||
}
|
||||
|
||||
|
@ -412,11 +408,14 @@ void neorv32_rte_debug_handler(void) {
|
|||
|
||||
/**********************************************************************//**
|
||||
* NEORV32 runtime environment (RTE):
|
||||
* Private function to print 32-bit number as 8-digit hexadecimal value (with "0x" suffix).
|
||||
* Private function to print the lowest 0 to 8 hex characters of a
|
||||
* 32-bit number as hexadecimal value (with "0x" suffix).
|
||||
*
|
||||
* @param[in] num Number to print as hexadecimal via UART0.
|
||||
*
|
||||
* @param[in] digits Number of hexadecimal digits to print (0..8).
|
||||
**************************************************************************/
|
||||
void __neorv32_rte_print_hex_word(uint32_t num) {
|
||||
void __neorv32_rte_print_hex(uint32_t num, int digits) {
|
||||
|
||||
int i;
|
||||
static const char hex_symbols[] = "0123456789ABCDEF";
|
||||
|
@ -425,7 +424,7 @@ void __neorv32_rte_print_hex_word(uint32_t num) {
|
|||
neorv32_uart0_putc('0');
|
||||
neorv32_uart0_putc('x');
|
||||
|
||||
for (i=0; i<8; i++) {
|
||||
for (i=(digits-8); i<8; i++) {
|
||||
uint32_t index = (num >> (28 - 4*i)) & 0xF;
|
||||
neorv32_uart0_putc(hex_symbols[index]);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
@ -9,10 +9,6 @@
|
|||
/**
|
||||
* @file neorv32_sdi.c
|
||||
* @brief Serial data interface controller (SDI) HW driver source file.
|
||||
*
|
||||
* @note These functions should only be used if the SDI unit was synthesized (IO_SDI_EN = true).
|
||||
*
|
||||
* @see https://stnolting.github.io/neorv32/sw/files.html
|
||||
*/
|
||||
|
||||
#include <neorv32.h>
|
||||
|
@ -38,7 +34,7 @@ int neorv32_sdi_available(void) {
|
|||
* Reset, enable and configure SDI controller.
|
||||
* The SDI control register bits are listed in #NEORV32_SDI_CTRL_enum.
|
||||
*
|
||||
* @param[in] irq_mask Interrupt configuration mask (CTRL's irq_* bits).
|
||||
* @param[in] irq_mask Interrupt configuration bit mask (CTRL's irq_* bits).
|
||||
**************************************************************************/
|
||||
void neorv32_sdi_setup(uint32_t irq_mask) {
|
||||
|
||||
|
|
|
@ -8,36 +8,43 @@
|
|||
|
||||
/**
|
||||
* @file neorv32_smp.c
|
||||
* @brief SMP HW driver source file.
|
||||
* @brief Symmetric multiprocessing (SMP) library source file.
|
||||
*/
|
||||
|
||||
#include <neorv32.h>
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Configure and start SMP core.
|
||||
* Configure and start SMP core 1.
|
||||
*
|
||||
* @warning This function can be executed on core 0 only.
|
||||
*
|
||||
* @param[in] hart_id Hart/core select.
|
||||
* @param[in] entry_point Core's main function (must be of type "void entry_point(void)").
|
||||
* @param[in] stack_memory Pointer to beginning of core's stack memory array. Should be at least 512 bytes.
|
||||
* @param[in] stack_size_bytes Core's stack size in bytes.
|
||||
* @return 0 if launching succeeded. -1 if invalid hart ID or CLINT not available. -2 if core is not responding.
|
||||
* @param[in] entry_point Core1's main function;
|
||||
* must be of type "int entry_point(void)".
|
||||
*
|
||||
* @param[in] stack_memory Pointer to beginning of core1's stack memory array.
|
||||
* Should be at least 512 bytes.
|
||||
*
|
||||
* @param[in] stack_size_bytes Core1's stack size in bytes.
|
||||
*
|
||||
* @return 0 if launching succeeded. -1 if invalid hart ID or CLINT not available.
|
||||
* -2 if core1 is not responding.
|
||||
**************************************************************************/
|
||||
int neorv32_smp_launch(int hart_id, void (*entry_point)(void), uint8_t* stack_memory, size_t stack_size_bytes) {
|
||||
int neorv32_smp_launch(int (*entry_point)(void), uint8_t* stack_memory, size_t stack_size_bytes) {
|
||||
|
||||
// sanity checks
|
||||
if ((neorv32_cpu_csr_read(CSR_MHARTID) != 0) || // this can be executed on core 0 only
|
||||
(hart_id == 0) || // we cannot launch core 0
|
||||
(hart_id > (neorv32_sysinfo_get_numcores()-1)) || // selected core not available
|
||||
if ((neorv32_cpu_csr_read(CSR_MHARTID) != 0) || // this can be executed on core0 only
|
||||
(neorv32_sysinfo_get_numcores() < 2) || // core1 not available
|
||||
(neorv32_clint_available() == 0)) { // we need the CLINT
|
||||
return -1;
|
||||
}
|
||||
|
||||
// synchronize data cache with main memory
|
||||
asm volatile ("fence");
|
||||
|
||||
// drain input queue from selected core
|
||||
while (neorv32_smp_icc_avail(hart_id)) {
|
||||
neorv32_smp_icc_get(hart_id);
|
||||
while (neorv32_smp_icc_avail()) {
|
||||
neorv32_smp_icc_get();
|
||||
}
|
||||
|
||||
// align end of stack to 16-bytes according to the RISC-V ABI (#1021)
|
||||
|
@ -45,24 +52,52 @@ int neorv32_smp_launch(int hart_id, void (*entry_point)(void), uint8_t* stack_me
|
|||
|
||||
// send launch configuration
|
||||
const uint32_t magic_number = 0xffab4321u;
|
||||
neorv32_smp_icc_put(hart_id, magic_number); // identifies valid configuration
|
||||
neorv32_smp_icc_put(hart_id, stack_top); // top of core's stack
|
||||
neorv32_smp_icc_put(hart_id, (uint32_t)entry_point); // entry point
|
||||
neorv32_smp_icc_put(magic_number); // identifies valid configuration
|
||||
neorv32_smp_icc_put(stack_top); // top of core1's stack
|
||||
neorv32_smp_icc_put((uint32_t)entry_point); // entry point
|
||||
|
||||
// start core by triggering its software interrupt
|
||||
neorv32_clint_msi_set(hart_id);
|
||||
// start core1 by triggering its software interrupt
|
||||
neorv32_clint_msi_set(1);
|
||||
|
||||
// wait for start acknowledge
|
||||
int cnt = 0;
|
||||
while (1) {
|
||||
if (neorv32_smp_icc_avail(hart_id)) {
|
||||
if (neorv32_smp_icc_get(hart_id) == magic_number) {
|
||||
if (neorv32_smp_icc_avail()) {
|
||||
if (neorv32_smp_icc_get() == magic_number) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (cnt > 1000) {
|
||||
return -2; // timeout; core did not respond
|
||||
return -2; // timeout; core1 did not respond
|
||||
}
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Send data to other core via ICC link (blocking).
|
||||
*
|
||||
* @warning This functions is blocking until data has been send.
|
||||
*
|
||||
* @param[in] data Data word (32-bit) to be send to other core.
|
||||
**************************************************************************/
|
||||
void neorv32_smp_icc_push(uint32_t data) {
|
||||
|
||||
while (neorv32_smp_icc_free() == 0); // wait for free FIFO space
|
||||
neorv32_smp_icc_put(data);
|
||||
}
|
||||
|
||||
|
||||
/**********************************************************************//**
|
||||
* Get data from other core via ICC link (blocking).
|
||||
*
|
||||
* @warning This functions is blocking until data has been received.
|
||||
*
|
||||
* @return Data word (32-bit) received from other core.
|
||||
**************************************************************************/
|
||||
uint32_t neorv32_smp_icc_pop(void) {
|
||||
|
||||
while (neorv32_smp_icc_avail() == 0); // wait until FIFO data is available
|
||||
return neorv32_smp_icc_get();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
@ -9,10 +9,6 @@
|
|||
/**
|
||||
* @file neorv32_spi.c
|
||||
* @brief Serial peripheral interface controller (SPI) HW driver source file.
|
||||
*
|
||||
* @note These functions should only be used if the SPI unit was synthesized (IO_SPI_EN = true).
|
||||
*
|
||||
* @see https://stnolting.github.io/neorv32/sw/files.html
|
||||
*/
|
||||
|
||||
#include <neorv32.h>
|
||||
|
@ -41,7 +37,7 @@ int neorv32_spi_available(void) {
|
|||
* @prama[in] cdiv Clock divider (0..15).
|
||||
* @param[in] clk_phase Clock phase (0=sample on rising edge, 1=sample on falling edge).
|
||||
* @param[in] clk_polarity Clock polarity (when idle).
|
||||
* @param[in] irq_mask Interrupt configuration mask (CTRL's irq_* bits).
|
||||
* @param[in] irq_mask Interrupt configuration bit mask (CTRL's irq_* bits).
|
||||
**************************************************************************/
|
||||
void neorv32_spi_setup(int prsc, int cdiv, int clk_phase, int clk_polarity, uint32_t irq_mask) {
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
@ -9,10 +9,6 @@
|
|||
/**
|
||||
* @file neorv32_uart.c
|
||||
* @brief Universal asynchronous receiver/transmitter (UART0/UART1) HW driver source file.
|
||||
*
|
||||
* @note These functions should only be used if the UART0/UART1 unit was synthesized.
|
||||
*
|
||||
* @see https://stnolting.github.io/neorv32/sw/files.html
|
||||
*/
|
||||
|
||||
#include <neorv32.h>
|
||||
|
@ -69,7 +65,7 @@ int neorv32_uart_available(neorv32_uart_t *UARTx) {
|
|||
*
|
||||
* @param[in,out] UARTx Hardware handle to UART register struct, #neorv32_uart_t.
|
||||
* @param[in] baudrate Targeted BAUD rate (e.g. 19200).
|
||||
* @param[in] irq_mask Interrupt configuration mask (CTRL's irq_* bits).
|
||||
* @param[in] irq_mask Interrupt configuration bit mask (CTRL's irq_* bits).
|
||||
**************************************************************************/
|
||||
void neorv32_uart_setup(neorv32_uart_t *UARTx, uint32_t baudrate, uint32_t irq_mask) {
|
||||
|
||||
|
|
|
@ -1,36 +1,10 @@
|
|||
// #################################################################################################
|
||||
// # << NEORV32: neorv32_xirq.c - External Interrupt controller HW Driver >> #
|
||||
// # ********************************************************************************************* #
|
||||
// # BSD 3-Clause License #
|
||||
// # #
|
||||
// # Copyright (c) 2024, Stephan Nolting. 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. Neither the name of the copyright holder nor the names of its contributors may be used to #
|
||||
// # endorse or promote products derived from this software without specific prior written #
|
||||
// # permission. #
|
||||
// # #
|
||||
// # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS #
|
||||
// # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF #
|
||||
// # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE #
|
||||
// # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, #
|
||||
// # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
|
||||
// # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED #
|
||||
// # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING #
|
||||
// # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
|
||||
// # OF THE POSSIBILITY OF SUCH DAMAGE. #
|
||||
// # ********************************************************************************************* #
|
||||
// # The NEORV32 Processor - https://github.com/stnolting/neorv32 (c) Stephan Nolting #
|
||||
// #################################################################################################
|
||||
// ================================================================================ //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
||||
/**
|
||||
* @file neorv32_xirq.c
|
||||
|
@ -208,7 +182,8 @@ void neorv32_xirq_channel_disable(int channel) {
|
|||
* Install interrupt handler function for XIRQ channel.
|
||||
*
|
||||
* @param[in] channel XIRQ interrupt channel (0..31).
|
||||
* @param[in] handler The actual handler function for the specified interrupt (function MUST be of type "void function(void);").
|
||||
* @param[in] handler The actual handler function for the specified interrupt;
|
||||
* function has to be of type "void function(void)".
|
||||
* @return 0 if success, -1 if invalid channel.
|
||||
**************************************************************************/
|
||||
int neorv32_xirq_install(int channel, void (*handler)(void)) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
override APP_SRC = park_loop.S
|
||||
override MARCH = rv32i_zicsr_zifencei
|
||||
override MARCH = rv32e_zicsr_zifencei
|
||||
override MABI = ilp32e
|
||||
override LD_SCRIPT = ./debug_rom.ld
|
||||
|
||||
# Modify this variable to fit your NEORV32 setup (neorv32 home folder)
|
||||
NEORV32_HOME ?= ../..
|
||||
include $(NEORV32_HOME)/sw/common/common.mk
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
// ================================================================================ //
|
||||
// NEORV32 CPU - park_loop.S - Execution-Based On-Chip Debugger (OCD) Firmware //
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// WARNING! This code only supports up to 4 harts! //
|
||||
// -------------------------------------------------------------------------------- //
|
||||
// The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 //
|
||||
// Copyright (c) NEORV32 contributors. //
|
||||
// Copyright (c) 2020 - 2024 Stephan Nolting. All rights reserved. //
|
||||
// Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. //
|
||||
// Licensed under the BSD-3-Clause license, see LICENSE for details. //
|
||||
// SPDX-License-Identifier: BSD-3-Clause //
|
||||
// ================================================================================ //
|
||||
|
@ -18,6 +16,8 @@
|
|||
.global entry_exception
|
||||
.global entry_park
|
||||
|
||||
// [NOTE] compile this code with minimal ISA configuration: rv32e_zicsr_zifencei
|
||||
|
||||
// debug module (DM) address map
|
||||
.equ DM_CODE_BASE, 0xFFFFFE00 // base address of code ROM (park loop)
|
||||
.equ DM_PBUF_BASE, 0xFFFFFE80 // base address of program buffer
|
||||
|
@ -46,34 +46,34 @@ entry_exception:
|
|||
// BASE + 16: normal entry - halt CPU: ebreak in debug-mode, halt request or return from single-stepped instruction
|
||||
entry_park:
|
||||
csrw dscratch0, x8 // backup x8 to dscratch0 so we have a GPR available
|
||||
csrr x8, mhartid // get hart ID (0..3)
|
||||
csrr x8, mhartid // get hart ID
|
||||
sw x8, (DM_SREG_BASE+ACK_HLT)(zero) // send halt-acknowledge
|
||||
|
||||
// polling loop - waiting for requests
|
||||
park_loop:
|
||||
csrr x8, mhartid // get hart ID (0..3)
|
||||
csrr x8, mhartid // get hart ID
|
||||
lbu x8, DM_SREG_BASE(x8) // read hart-specific byte from request register
|
||||
andi x8, x8, 1 << REQ_EXE // execute-request bit set?
|
||||
bnez x8, execute
|
||||
|
||||
csrr x8, mhartid // get hart ID (0..3)
|
||||
csrr x8, mhartid // get hart ID
|
||||
lbu x8, DM_SREG_BASE(x8) // read hart-specific byte from request register
|
||||
andi x8, x8, 1 << REQ_RES // resume-request bit set?
|
||||
beqz x8, park_loop
|
||||
|
||||
// resume normal operation
|
||||
resume:
|
||||
csrr x8, mhartid // get hart ID (0..3)
|
||||
csrr x8, mhartid // get hart ID
|
||||
sw x8, (DM_SREG_BASE+ACK_RES)(zero) // send resume-acknowledge
|
||||
csrr x8, dscratch0 // restore x8 from dscratch0
|
||||
dret // exit debug mode
|
||||
|
||||
// execute program buffer (implicit ebreak at the end of the buffer will bring us back to "entry_park")
|
||||
execute:
|
||||
csrr x8, mhartid // get hart ID (0..3)
|
||||
csrr x8, mhartid // get hart ID
|
||||
sw x8, (DM_SREG_BASE+ACK_EXE)(zero) // send execute-acknowledge
|
||||
csrr x8, dscratch0 // restore x8 from dscratch0
|
||||
fence.i // synchronize instruction fetch with memory (PBUF)
|
||||
fence.i // synchronize instruction fetch with memory-mapped PBUF
|
||||
jalr zero, zero, %lo(DM_PBUF_BASE) // jump to beginning of program buffer (PBUF)
|
||||
|
||||
// fill remaining ROM space with instructions that cause a debug-mode-internal exception
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue