Merge branch 'main' into fixed-bl-isr-alignment

This commit is contained in:
Lukas Pajak 2025-01-13 14:49:49 +01:00 committed by GitHub
commit 96b0ec1250
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
78 changed files with 1968 additions and 1556 deletions

View file

@ -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) |

View file

@ -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**

View file

@ -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 =

View file

@ -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

View file

@ -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"]

View file

@ -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`

View file

@ -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]

View file

@ -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

View file

@ -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.
<<<

View file

@ -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)

View file

@ -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`

View file

@ -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
|=======================

View file

@ -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 |

View file

@ -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]

View file

@ -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>>)

View file

@ -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_

View file

@ -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)

View file

@ -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>>)

View file

@ -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]

View file

@ -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)

View file

@ -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

View file

@ -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`

View file

@ -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 |

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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 |

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 |

View file

@ -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.

View file

@ -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**

View file

@ -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>>)

View file

@ -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]

View file

@ -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.

View file

@ -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

View file

@ -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:

View file

@ -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.

View file

@ -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",

View file

@ -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",

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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
-- ****************************************************************************************************************************

View file

@ -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;

View file

@ -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;

View file

@ -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'),

View file

@ -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
);

View file

@ -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

View file

@ -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
}

View file

@ -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 ?= ../..

View file

@ -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

View file

@ -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
}

View file

@ -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) {

View file

@ -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

View 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

View 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;
}

View 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

View 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;
}

View 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
}

View 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

View file

@ -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;
}

View file

@ -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);

View file

@ -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/-)*/
};

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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]);
}

View file

@ -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) {

View file

@ -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();
}

View file

@ -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) {

View file

@ -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) {

View file

@ -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)) {

View file

@ -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

View file

@ -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