diff --git a/examples/dm_paranut/CMakeLists.txt b/examples/dm_paranut/CMakeLists.txt new file mode 100644 index 00000000..b6bcd11e --- /dev/null +++ b/examples/dm_paranut/CMakeLists.txt @@ -0,0 +1,49 @@ +# This file is part of the ParaNut project. + +# Copyright (C) 2022 Marco Milenkovic +# Hochschule Augsburg, University of Applied Sciences + +# 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. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, 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. + + +# Example Paranut DebugModule +project(DebugModule) + +# Add alle the nessesery files here +add_executable(DebugModule dm_tb.cpp dm.cpp base.cpp) +target_include_directories(DebugModule PUBLIC "${PROJECT_SOURCE_DIR}") + +# use cmake -DSYN=ON to enable the debug flag or OFF to disable it +OPTION(SYN "set the __SYNTHESIS__ Flag" ON) # Enabled by default + +# Add compilation options +# target_compile_options(${PROJECT_NAME} PUBLIC -Wall) +IF(SYN) + target_compile_definitions(DebugModule PUBLIC -D__SYNTHESIS__) +ENDIF(SYN) + +# svc_target will create ${PROJECT_NAME}_sctool executable that runs code generation +# and ${PROJECT_NAME} that runs general SystemC simulation +# ELAB_TOP parameter accepts hierarchical name of design +# (that is SystemC name, returned by sc_object::name() method) +svc_target(DebugModule INIT_LOCAL_VARS ELAB_TOP tb.dm) + diff --git a/examples/dm_paranut/base.cpp b/examples/dm_paranut/base.cpp new file mode 100644 index 00000000..148435f3 --- /dev/null +++ b/examples/dm_paranut/base.cpp @@ -0,0 +1,337 @@ +/************************************************************************* + + This file is part of the ParaNut project. + + Copyright (C) 2010-2022 Alexander Bahle + Gundolf Kiefer + Christian H. Meyer + Hochschule Augsburg, University of Applied Sciences + + 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, 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. + + *************************************************************************/ + + +#include "base.h" + +#include +#include +#include +#include + +#include + +// *********** Dynamic Configuration ************ + + +int pn_cfg_vcd_level = 0; +int pn_cfg_insn_trace = 0; +bool pn_cfg_disable_cache = 0; +bool pn_cfg_debug_mode = 0; + + +// **************** Tracing ********************* + + +bool pn_trace_verbose = false; + + +std::string pn_GetTraceName (sc_object *obj, const char *name, int dim, int arg1, int arg2) { + std::string ret; + + if (dim < 0 || dim > 2) + PN_ERRORF (("pn_GetTraceName: Parameter dim outside of range (0-2): %d", dim)); + + // Read full object name and get the base module name + ret = obj->name (); + ret = ret.substr (0, ret.find_last_of ('.') + 1); + + // Add name... + ret += name; + + // Add first dimension... + if (dim > 0) + ret += "(" + std::to_string (arg1) + ")"; + + // Add second dimension... + if (dim == 2) + ret += "(" + std::to_string (arg2) + ")"; + + return ret; +} + + +// **************** Testbench helpers *********** + + +sc_trace_file *pn_trace_file = NULL; + + +char *pn_TbPrintf (const char *format, ...) { + static char buf[200]; + + va_list ap; + va_start (ap, format); + vsprintf (buf, format, ap); + return buf; +} + + +void pn_TbAssert (bool cond, const char *msg, const char *filename, const int line) { + if (!cond) { + fprintf (stderr, "ASSERTION FAILURE: %s, %s:%i", sc_time_stamp ().to_string ().c_str (), filename, line); + if (msg) + fprintf (stderr, ": %s\n", msg); + else + fprintf (stderr, "\n"); + if (pn_trace_file) sc_close_vcd_trace_file (pn_trace_file); + abort (); + } +} + + +void pn_TbInfo (const char *msg, const char *filename, const int line) { + int time_size = sc_time_stamp ().to_double () == 0.0 ? 1 : 15; + fprintf (stderr, "(INFO): %*s, %s:%i: %s\n", time_size, sc_time_stamp ().to_string ().c_str (), filename, line, msg); +} + + +void pn_TbWarning (const char *msg, const char *filename, const int line) { + int time_size = sc_time_stamp ().to_double () == 0.0 ? 1 : 12; + fprintf (stderr, "(WARNING): %*s, %s:%i: %s\n", time_size, sc_time_stamp ().to_string ().c_str (), filename, line, msg); +} + + +void pn_TbError (const char *msg, const char *filename, const int line) { + int time_size = sc_time_stamp ().to_double () == 0.0 ? 1 : 14; + fprintf (stderr, "(ERROR): %*s, %s:%i: %s\n", time_size, sc_time_stamp ().to_string ().c_str (), filename, line, msg); + if (pn_trace_file) sc_close_vcd_trace_file (pn_trace_file); + exit (3); +} + + +// **************** DisAss ********************** + + +char *pn_DisAss (TWord insn) { + static char ret[80] = ""; + TWord opcode, funct3, funct7, rs1, rs2, rd, bit30, bit20, bit21, bit25, bit28, bit29, itype, utype, btype, jtype, stype; + sc_uint<32> inst = insn; + + opcode = insn & 0x7f; + funct3 = (insn >> 12) & 0x7; + funct7 = (insn >> 25); + + rd = (insn >> 7) & 0x1f; + rs1 = (insn >> 15) & 0x1f; + rs2 = (insn >> 20) & 0x1f; + + bit20 = (insn >> 20) & 0x1; + bit21 = (insn >> 21) & 0x1; + bit25 = (insn >> 25) & 0x1; + bit28 = (insn >> 28) & 0x1; + bit29 = (insn >> 28) & 0x1; + bit30 = (insn >> 30) & 0x1; + + itype = ((insn >> 20) ^ 0x800) - 0x800; + utype = insn & 0xFFFFF000; + btype = ((sc_uint<32>)(inst[31], inst[7], inst (30, 25), inst (11, 8), 0) ^ 0x1000) - 0x1000; + jtype = ((sc_uint<32>)(inst[31], inst (19, 12), inst[20], inst (30, 25), inst (24, 21), 0) ^ 0x100000) - 0x100000; + stype = ((((sc_uint<32>)(inst (31, 25), 0, 0, 0, 0, 0)).value () + rd) ^ 0x800) - 0x800; + + strcpy (ret, " "); + + // ALU instructions... + if (opcode == 0x13) { // OP_IMM + const char *table[] = { "addi", "slli", "slti", "sltiu", "xori", "srli", "ori", "andi" }; + if (funct3 == 5 || funct3 == 1) { + if (bit30) table[funct3] = "srai"; + itype &= 0x1F; // shamt + } + sprintf (ret + 2, "%s r%i, r%i, 0x%x", table[funct3], rd, rs1, itype); + } else if (opcode == 0x33 && !bit25) { // OP (Bit 25 is on for M-Extension (DIV/MUL...) + const char *table[] = { "add", "sll", "slt", "sltu", "xor", "srl", "or", "and" }; + if (funct3 == 0) { + if (bit30) table[funct3] = "sub"; + } else if (funct3 == 5) { + if (bit30) table[funct3] = "sra"; + } + sprintf (ret + 2, "%s r%i, r%i, r%i", table[funct3], rd, rs1, rs2); + } else if ((opcode == 0x33 && bit25)) { // OP - M-Extension + static const char *table[] = { "mul", "mulh", "mulhsu", "mulhu", + "div", "divu", "rem", "remu" }; + sprintf (ret + 2, "%s r%i, r%i, r%i", table[funct3], rd, rs1, rs2); + } else if (opcode == 0x17) { // AUIPC + sprintf (ret + 2, "auipc r%i, 0x%x", rd, utype); + } else if (opcode == 0x37) { // LUI + sprintf (ret + 2, "lui r%i, 0x%x", rd, utype); + } else if (opcode == 0x63) { // BRANCH + const char *table[] = { "beq", "bneq", "INV_SUB", "INV_SUB", "blt", "bge", "bltu", "bgeu" }; + sprintf (ret + 2, "%s r%i, r%i, 0x%x", table[funct3], rs1, rs2, btype); + } else if (opcode == 0x6F) { // JAL + sprintf (ret + 2, "jal r%i, 0x%x", rd, jtype); + } else if (opcode == 0x67) { // JALR + sprintf (ret + 2, "jalr r%i, r%i, 0x%x", rd, rs1, itype); + } else if (opcode == 0x03) { // LOAD + static const char *table[] = { "lb", "lh", "lw", "INV_SUB", "lbu", "lhu" }; + sprintf (ret + 2, "%s r%i, 0x%x(r%i)", table[funct3], rd, itype, rs1); + } else if (opcode == 0x23) { // STORE + static const char *table[] = { "sb", "sh", "sw" }; + sprintf (ret + 2, "%s r%i, 0x%x(r%i)", table[funct3], rs2, stype, rs1); + } else if (opcode == 0x73) { // SYSTEM + const char *table[] = { "ecall", "csrrw", "csrrs", "csrrc", + "INV_SUB", "csrrwi", "csrrsi", "csrrci" }; + if (bit25 && bit28) table[0] = "sfence.vma"; + if (bit21) table[0] = bit28 ? bit29 ? "mret" : "sret" : "uret"; + if (bit20) table[0] = "ebreak"; + if (funct3 == 0) + sprintf (ret + 2, "%s", table[funct3]); + else if (funct3 < 4) + sprintf (ret + 2, "%s r%i, 0x%x, r%i", table[funct3], rd, itype, rs1); + else + sprintf (ret + 2, "%s r%i, 0x%x, 0x%x", table[funct3], rd, itype, rs1); + } else if (opcode == 0x0F) { + sprintf (ret + 2, "fence"); + } else if (opcode == 0xB) { // PARA + static const char *table[] = { "halt", "cinvalidate", "cwriteback", "cflush" }; + if (funct3 == 0) + sprintf (ret + 2, "%s", table[funct3]); + else + sprintf (ret + 2, "%s 0x%x(r%i) ", table[funct3], itype, rs1); + } else if (opcode == 0x2F) { // AMO + if (funct3 == 2 && (funct7 >> 2) == 2) // LR.W + sprintf (ret + 2, "lr.w r%i, (r%i)", rd, rs1); + else if (funct3 == 2 && (funct7 >> 2) == 3) // SC.W + sprintf (ret + 2, "sc.w r%i, r%i, (r%i) ", rd, rs2, rs1); + else + sprintf (ret + 2, "AMO_INVALID r%i, r%i, (r%i) ", rd, rs2, rs1); + } else + sprintf (ret, "? 0x%08x ?", insn); + + return ret; +} + + +// **************** Performance measuring ***************** + + +void CPerfMon::Init (int events, CEventDef *ev_tab) { + events_ = events; + ev_tab_ = ev_tab; + count_tab_ = new int[events]; + time_tab_ = new double[events]; + min_tab_ = new double[events]; + max_tab_ = new double[events]; + Reset (); +} + + +void CPerfMon::Done () { + if (events_ > 0) { + delete[] count_tab_; + delete[] time_tab_; + delete[] min_tab_; + delete[] max_tab_; + } +} + + +void CPerfMon::Reset () { + last_no_ = -1; + for (int n = 0; n < events_; n++) { + count_tab_[n] = 0; + time_tab_[n] = max_tab_[n] = 0.0; + min_tab_[n] = DBL_MAX; + } +} + + +void CPerfMon::Count (int ev_no) { + double curStamp = sc_time_stamp ().to_double (); + + if (last_no_ >= 0) { + if (ev_tab_[last_no_].is_timed) { + double t = curStamp - last_stamp_; + if (t == 0) // Time of 0 between two events is not plausible + return; + time_tab_[last_no_] += t; + if (t < min_tab_[last_no_]) + min_tab_[last_no_] = t; + if (t > max_tab_[last_no_]) + max_tab_[last_no_] = t; + } + } + + count_tab_[ev_no]++; + if (ev_tab_[ev_no].is_timed) last_stamp_ = curStamp; + last_no_ = ev_no; +} + + +static void DisplayLine (const char *name, int count, int avg_count, double total, double min, double max, bool is_timed) { + if (avg_count > 0 && is_timed) + fprintf (stderr, "(PERF): %-10s %7i %8.1lf %8.1lf %8.1lf %11.1lf\n", name, count, min, + total / avg_count, max, total); + else + fprintf (stderr, "(PERF): %-10s %7i\n", name, count); +} + + +void CPerfMon::Display (const char *name) { + double time_total, min_total, max_total; + int count_total, avg_count_total; + + fprintf (stderr, "(PERF):\n" + "(PERF): ********** Performance statics "); + if (name) fprintf (stderr, "of unit '%s'", name); + fprintf (stderr, "\n" + "(PERF):\n" + "(PERF): Time [ns]\n" + "(PERF): Event Count min avg max Total\n" + "(PERF): -----------------------------------------------------------\n"); + count_total = avg_count_total = 0; + time_total = max_total = 0.0; + min_total = DBL_MAX; + for (int n = 0; n < events_; n++) { + DisplayLine (ev_tab_[n].name, count_tab_[n], count_tab_[n], time_tab_[n], min_tab_[n], + max_tab_[n], ev_tab_[n].is_timed); + count_total += count_tab_[n]; + if (ev_tab_[n].is_timed) avg_count_total += count_tab_[n]; + time_total += time_tab_[n]; + if (min_tab_[n] < min_total) min_total = min_tab_[n]; + if (max_tab_[n] > max_total) max_total = max_tab_[n]; + } + fprintf (stderr, "(PERF): -----------------------------------------------------------\n"); + DisplayLine ("Total", count_total, avg_count_total, time_total, min_total, max_total, true); + fprintf (stderr, "(PERF):\n"); +} + + +// ***** CPerfMonCPU ***** + + +void CPerfMonCPU::Init () { + static CEventDef CPU_events[] = { + { "ALU", true }, { "Load", true }, { "Store", true }, { "Jump", true }, { "Other", true }, { "IFetch", true } + }; + CPerfMon::Init (6, CPU_events); +} diff --git a/examples/dm_paranut/base.h b/examples/dm_paranut/base.h new file mode 100644 index 00000000..6fea8a22 --- /dev/null +++ b/examples/dm_paranut/base.h @@ -0,0 +1,599 @@ +/************************************************************************ +* + This file is part of the ParaNut project. + + Copyright (C) 2010-2022 Alexander Bahle + Gundolf Kiefer + Christian H. Meyer + Hochschule Augsburg, University of Applied Sciences + + Description: + This module contains various types, constants and helper functions + for the SystemC model of ParaNut. + + 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, 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. + + *************************************************************************/ + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @mainpage +/// This is the documentation for the **SystemC model** of the ParaNut processor. It +/// is meant as a reference for developers interested in +/// - creating ParaNut comaptible Wishbone bus hardware, +/// - improving the SystemC simulation model capabilities and +/// - adding functionality to the ParaNut processor. +/// +/// The main docoumentation containing the definition of the ParaNut architecture and design rules +/// can be found in the [*ParaNut Manual*](../../doc/paranut-manual.pdf). If you only want to +/// develop software for a system featuring ParaNut processor take a look at the libparanut +/// documentation [*libparanut Manual*](../../doc/libparanut_manual.pdf). +/// +/// To get started, navigate to the [Modules](modules.html) page. +/// +//////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef _BASE_ +#define _BASE_ + +/// @file +/// @brief Helpers, Makros and performance measuring Classes used in most *ParaNut* files. +/// @defgroup helpers Helpers +/// @brief Helpers, Makros and performance measuring Classes used in most *ParaNut* files. +/// @{ + + +#include +#include + +// Print useful information during HLS. The tool analyses the source code once without the +// __SYNTHESIS__ define set and once with the define set. This printed info helps to discern the two. +// Uses #warning, because Vivado HLS does not print #info messages +#ifndef SIMBUILD +#ifdef __SYNTHESIS__ +#warning "INFO: __SYNTHESIS__ is set! This is just a ParaNut debug info." +#else +#warning "INFO: __SYNTHESIS__ is not set! This is just a ParaNut debug info." +#endif +#endif + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @name Static Configuration... +/// @{ + +/// @brief System endianess configuration +/// +/// Can be used to switch between 0 - little endian and 1 - big endian for testing purposes. +/// +/// __CAUTION:__ For RISC-V this should always be 0. +#define PN_BIG_ENDIAN 0 + +/// @} // @name Static Configuration... +//////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @name Dynamic Configuration.... +/// @{ + +/// @brief VCD trace level. +/// +/// Holds the VCD trace level (0 = no VCD file) set by the "-t" command line option supplied to the +/// MParaNutSystem constructor. +extern int pn_cfg_vcd_level; + +/// @brief Internal simulation instruction trace level. +/// +/// Holds the internal simulation instruction trace level set by the "-i" command line option supplied to the +/// MParaNutSystem constructor. +/// \arg 0 - no trace +/// \arg 1 - instruction trace (shows register changes) +/// \arg >2 - same as 1 + prints complete register file after each instruction +extern int pn_cfg_insn_trace; + +/// @brief Cache enable override. +/// +/// Holds the disable cache bit set by the "-c" command line option supplied to the MParaNutSystem constructor. +/// If set, disables instruction and data caching irrespective of the value in the pncache CSR. +extern bool pn_cfg_disable_cache; + +/// @brief Interactive debug mode enable. +/// +/// Holds the debug mode enable bit set by the "-d" command line option supplied to the MParaNutSystem +/// constructor. If set, the MParaNutSystem will set up a remote bitbang interface on port 9824 and waits +/// for you to connect to it. Use OpenOCD and GDB to debug software running in the SystemC model. +/// +/// See doc/paranut_manual.pdf for more information. +extern bool pn_cfg_debug_mode; + +///@} // @name Dynamic Configuration.... +//////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @name Basic types and constants... +/// @{ + +/// @brief Number of Bytes in a Kilobyte. +#define KB 1024 +/// @brief Number of Bytes in a Megabyte. +#define MB (1024 * 1024) +/// @brief Number of Bytes in a Gigabyte. +#define GB (1024 * 1024 * 1024) + +/// @brief Byte type (8 Bit). +typedef unsigned char TByte; +/// @brief Half word type (16 Bit). +typedef unsigned short THalfWord; +/// @brief Word type (32 Bit). +typedef unsigned TWord; +/// @brief Double word type (64 Bit). +typedef unsigned long long TDWord; + +/// @brief Minimum of A and B. +#define MIN(A, B) ((A) < (B) ? (A) : (B)) +/// @brief Maximum of A and B. +#define MAX(A, B) ((A) > (B) ? (A) : (B)) +/// @brief Number of bits necessary to encode A. +#define NUM_BITS(A) ((int)(ceil(log2 (A)))) +/// @brief Convenient macro to compare a member variable with the member of input t +#define C(MEMBER) (MEMBER == t.MEMBER) +/// @brief Same as C(MEMBER), but for arrays +#define C_ARR(MEMBER, NUM) ({ bool ret = 1; for (int n = 0; n < NUM; ++n) if (!C(MEMBER[n])) ret = 0; ret; }) + + +/// @brief Number of instruction/register Bits. +#define XLEN 32 + +///@} // @name Basic types and constants... +//////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @name SystemC tracing... +/// @{ + +/// @brief Output verbose tracing information. +/// +/// If set, the system modules will output verbose information when they trace their internal +/// signals. +extern bool pn_trace_verbose; + + +/// @brief Generates and returns a trace file compatible string. +/// +/// Generates and returns a trace file compatible string using the supplied information to +/// parse multidimensional inputs. +/// \param obj is the sc_object (port/signal) pointer. +/// \param name is the base name appearing in the trace file. +/// \param dim is the number of dimensions the obj has (only 0 to 2 are supported). +/// \param arg1 is the number to add to the base name to represent the first dimension +/// (should be 0 for dim < 1). +/// \param arg2 is the number to add to the base name + first dimension to represent the second +/// dimension (should be 0 for dim < 2). +/// \return A std::string formatted for the sc_trace() function (e.g. MODULE.name(arg1)(arg2)). +#ifndef __SYNTHESIS__ +std::string pn_GetTraceName (sc_object *obj, const char *name, int dim, int arg1, int arg2); +#else +// std::string pn_GetTraceName (...) { +// return "\0"; +// } +#endif + +/// @brief Add sc_object OBJ to the trace file TF. +/// +/// Macro for easily adding the sc_object (port/signal) to a trace file. Uses pn_GetTraceName to +/// generate the correct object name. +/// \param TF is the trace file. +/// \param OBJ is the sc_object (port/signal). +#define PN_TRACE(TF, OBJ) \ + { \ + if (TF) sc_trace (TF, OBJ, pn_GetTraceName (&(OBJ), #OBJ, 0, 0, 0)); \ + if (!TF || pn_trace_verbose) cout << " " #OBJ " = '" << (OBJ).name () << "'\n"; \ + } + +/// @brief Add each sc_object of OBJ array/bus to the trace file TF. +/// +/// Macro for easily adding the array/bus of sc_objects (port/signal) to a trace file. Uses +/// pn_GetTraceName to generate the correct object name. +/// \param TF is the trace file. +/// \param OBJ is the array/bus of sc_objects (port/signal). +/// \param N_MAX is number of array/bus elements. +#define PN_TRACE_BUS(TF, OBJ, N_MAX) \ + { \ + for (int n = 0; n < N_MAX; n++) { \ + if (TF) sc_trace (TF, (OBJ)[n], pn_GetTraceName (&(OBJ)[n], #OBJ, 1, n, 0)); \ + if (!TF || pn_trace_verbose) \ + cout << " " #OBJ "[" << n << "] = '" << (OBJ)[n].name () << "'\n"; \ + } \ + } + +/// @brief Add each sc_object of 2D OBJ array/bus to the trace file TF. +/// +/// Macro for easily adding the two dimensional array/bus of sc_objects (port/signal) to a trace file. +/// Uses pn_GetTraceName to generate the correct object name. +/// \param TF is the trace file. +/// \param OBJ is the array/bus of sc_objects (port/signal). +/// \param N_MAX is number of array/bus elements in the first dimension. +/// \param K_MAX is number of array/bus elements in the second dimension. +#define PN_TRACE_BUS_BUS(TF, OBJ, N_MAX, K_MAX) \ + { \ + for (int n = 0; n < N_MAX; n++) \ + for (int k = 0; k < K_MAX; k++) { \ + if (TF) sc_trace (TF, (OBJ)[n][k], pn_GetTraceName (&(OBJ)[n][k], #OBJ, 2, n, k)); \ + if (!TF || pn_trace_verbose) \ + cout << " " #OBJ "[" << n << "][" << k << "] = '" << (OBJ)[n][k].name () << "'\n"; \ + } \ + } + +/// @brief Helper macro for recursively calling sc_trace in own types/structs. +/// +/// Macro for easily adding a sc_object member (port/signal) of an sc_object to a trace file. +/// Use PN_TRACE_R when overloading sc_trace for own types/structs to easily trace its members. +/// \param TF is the trace file. +/// \param OBJ is the sc_objects having the member MEMBER. +/// \param MEMBER is the member sc_object (port/signal). +/// \param STR is the calling sc_trace string to which the member name will be appended. +#define PN_TRACE_R(TF, OBJ, MEMBER, STR) \ + { \ + if (TF) sc_trace (TF, (OBJ).MEMBER, STR + "."#MEMBER); \ + } + +/// @brief Helper macro for recursively calling sc_trace in own types/structs. +/// +/// Macro for easily adding each sc_object of an array member (ports/signals) of an sc_object to a +/// trace file. +/// Use PN_TRACE_R_BUS when overloading sc_trace for own types/structs to easily trace its members. +/// \param TF is the trace file. +/// \param OBJ is the sc_objects having the member MEMBER. +/// \param MEMBER is the member sc_object (port/signal). +/// \param STR is the calling sc_trace string to which the member name + index will be appended. +/// \param N_MAX is number of MEMBER array/bus elements. +#define PN_TRACE_R_BUS(TF, OBJ, MEMBER, STR, N_MAX) \ + { \ + std::stringstream ss; \ + for (int n = 0; n < N_MAX; n++) { \ + ss << n; \ + if (TF) sc_trace (TF, (OBJ).MEMBER[n], STR + "."#MEMBER"(" + ss.str().c_str() + ")"); \ + } \ + } + +/// @brief Helper macro for recursively calling sc_trace in own types/structs. +/// +/// Macro for easily adding each sc_object of an two-dimensional array member (ports/signals) of an sc_object to a +/// trace file. +/// Use PN_TRACE_R_BUS_BUS when overloading sc_trace for own types/structs to easily trace its members. +/// \param TF is the trace file. +/// \param OBJ is the sc_objects having the member MEMBER. +/// \param MEMBER is the member sc_object (port/signal). +/// \param STR is the calling sc_trace string to which the member name + index will be appended. +/// \param N_MAX is number of array/bus elements in the first dimension. +/// \param K_MAX is number of array/bus elements in the second dimension. +#define PN_TRACE_R_BUS_BUS(TF, OBJ, MEMBER, STR, N_MAX, K_MAX) \ + { \ + std::stringstream ss_n, ss_k; \ + for (int n = 0; n < N_MAX; n++) { \ + for (int k = 0; k < K_MAX; k++) { \ + ss_n << n; \ + ss_k << k; \ + if (TF) sc_trace (TF, (OBJ).MEMBER[n][k], STR + "."#MEMBER"(" + ss_n.str().c_str() + ")(" + ss_k.str().c_str() + ")"); \ + } \ + } \ + } + +///@} // @name SystemC tracing... +//////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @name Testbench helpers... +/// @{ + +/// @brief ParaNut trace file pointer. +/// +/// If set the trace file is closed using sc_close_vcd_trace_file() after errors and asserts. +extern sc_trace_file *pn_trace_file; + +/// @brief Testbench printf helper. +/// +/// Returns a C string formatted according to supplied format and addistional arguments. +/// +/// \param format is a C string that contains the text to be formatted. +/// \param ... (additional arguments) are the parameters according to the format string. +/// \return formatted C string according to supplied format and additional arguments. +char *pn_TbPrintf (const char *format, ...); +/// @brief Testbench assert helper. +/// +/// Asserts condition cond and if it fails prints the supplied msg, filename and line to stderr, closes +/// the pn_trace_file and aborts program execution. +/// +/// Should be called through the PN_ASSERT(), PN_ASSERTF() and PN_ASSERTM() macros. +/// +/// \param cond is the condition to assert. +/// \param msg is a C string to print if the assertion fails. +/// \param filename is a C string to print if the assertion fails containig the source filename. +/// \param line is the line number in the source file to print if the assertion fails. +void pn_TbAssert (bool cond, const char *msg, const char *filename, const int line); +/// @brief Testbench information helper. +/// +/// Prints the supplied msg to stderr with a defined header containing the current SytemC simulation +/// time stamp, filename and line and the "(INFO):" keyword. +/// +/// Should be called through the PN_INFO() and PN_INFOF() macros. +/// +/// \param msg is a C string to print. +/// \param filename is a C string to print containig the source filename. +/// \param line is the line number in the source file to print. +void pn_TbInfo (const char *msg, const char *filename, const int line); +/// @brief Testbench warning helper. +/// +/// Prints the supplied msg to stderr with a defined header containing the current SytemC simulation +/// time stamp, filename and line and the "(WARNING):" keyword. +/// +/// Should be called through the PN_WARNING() and PN_WARNINGF() macros. +/// +/// \param msg is a C string to print. +/// \param filename is a C string to print containig the source filename. +/// \param line is the line number in the source file to print. +void pn_TbWarning (const char *msg, const char *filename, const int line); +/// @brief Testbench error helper. +/// +/// Prints the supplied msg to stderr with a defined header containing the current SytemC simulation +/// time stamp, filename and line and the "(ERROR):" keyword. Also closes the pn_trace_file and +/// exits the program execution with an error. +/// +/// Should be called through the PN_ERROR() and PN_ERRORF() macros. +/// +/// \param msg is a C string to print. +/// \param filename is a C string to print containig the source filename. +/// \param line is the line number in the source file to print +void pn_TbError (const char *msg, const char *filename, const int line); + + +#ifndef __SYNTHESIS__ +/// @brief Testbench assert without message. +/// +/// Calls pn_TbAssert() with the condition COND, an empty message and the filename and line macros. +/// +/// \param COND is the condition to assert. +#define PN_ASSERT(COND) pn_TbAssert (COND, NULL, __FILE__, __LINE__) +/// @brief Testbench assert with formatted message. +/// +/// Calls pn_TbAssert() with the condition COND, the formatted message according to FMT and the +/// filename and line macros. +/// +/// __NOTE__: You need to put the FMT argument in brackets (e.g. PN_ASSERTF(1, ("E: %d", 5));) +/// +/// \param COND is the condition to assert. +/// \param FMT is the format C string and all additional arguments needed to print if the assertion fails. +#define PN_ASSERTF(COND, FMT) pn_TbAssert (COND, pn_TbPrintf FMT, __FILE__, __LINE__) +/// @brief Testbench assert with message. +/// +/// Calls pn_TbAssert() with the condition COND, the supplied message MSG and the +/// filename and line macros. +/// +/// +/// \param COND is the condition to assert. +/// \param MSG is the C string to print if the assertion fails. +#define PN_ASSERTM(COND, MSG) pn_TbAssert (COND, MSG, __FILE__, __LINE__) + +/// @brief Testbench info with message. +/// +/// Calls pn_TbInfo() with the supplied message MSG and the filename and line macros. +/// +/// \param MSG is the C string to print. +#define PN_INFO(MSG) pn_TbInfo (MSG, __FILE__, __LINE__) +/// @brief Testbench info with formatted message. +/// +/// Calls pn_TbInfo() with the formatted message according to FMT and the filename and line macros. +/// +/// __NOTE__: You need to put the FMT argument in brackets (e.g. PN_INFO(("Val: %d", 5));) +/// +/// \param FMT is the format C string and all additional arguments needed to print. +#define PN_INFOF(FMT) pn_TbInfo (pn_TbPrintf FMT, __FILE__, __LINE__) + +/// @brief Testbench warning with message. +/// +/// Calls pn_TbWarning() with the supplied message MSG and the filename and line macros. +/// +/// \param MSG is the C string to print. +#define PN_WARNING(MSG) pn_TbWarning (MSG, __FILE__, __LINE__) +/// @brief Testbench warning with formatted message. +/// +/// Calls pn_TbWarning() with the formatted message according to FMT and the filename and line macros. +/// +/// __NOTE__: You need to put the FMT argument in brackets (e.g. PN_WARNINGF(("Val: %d", 5));) +/// +/// \param FMT is the format C string and all additional arguments needed to print. +#define PN_WARNINGF(FMT) pn_TbWarning (pn_TbPrintf FMT, __FILE__, __LINE__) + +/// @brief Testbench error with message. +/// +/// Calls pn_TbError() with the supplied message MSG and the filename and line macros. +/// +/// \param MSG is the C string to print. +#define PN_ERROR(MSG) pn_TbError (MSG, __FILE__, __LINE__) +/// @brief Testbench error with formatted message. +/// +/// Calls pn_TbError() with the formatted message according to FMT and the filename and line macros. +/// +/// __NOTE__: You need to put the FMT argument in brackets (e.g. PN_ERRORF(("Val: %d", 5));) +/// +/// \param FMT is the format C string and all additional arguments needed to print. +#define PN_ERRORF(FMT) pn_TbError (pn_TbPrintf FMT, __FILE__, __LINE__) + + +/// @brief vh_open struct (vhdl open equivalent) +/// +/// This static constant struct can be used to connect unused ports to "nothing" to avoid elaboration +/// warnings and errors. +static struct { + template operator sc_core::sc_signal_inout_if & () const { + return *(new sc_core::sc_signal (sc_core::sc_gen_unique_name ("vh_open"))); + } + +} const vh_open = {}; + +/// @brief vvh_const type/struct (vhdl constant value equivalent) +/// +/// This type can be used to easily connect ports or signals to a "constant" value. +/// +/// @tparam typename is the type the constant value will be applied to. +/// @param v is the constant value. +/// @todo The current implementation has a memory leak. +template +sc_core::sc_signal_in_if const &vh_const (T const &v) // keep the name consistent with vh_open +{ + // Yes, this is an (elaboration-time) memory leak. You can avoid it with some extra effort + sc_core::sc_signal *sig_p = + new sc_core::sc_signal (sc_core::sc_gen_unique_name ("vh_const")); + sig_p->write (v); + return *sig_p; +} + +#else // #ifdef __SYNTHESIS__ +#define PN_ASSERT(COND) +#define PN_ASSERTF(COND, FMT) +#define PN_ASSERTM(COND, MSG) + +#define PN_INFO(MSG) +#define PN_INFOF(FMT) + +#define PN_WARNING(MSG) +#define PN_WARNINGF(FMT) + +#define PN_ERROR(MSG) +#define PN_ERRORF(FMT) +#endif // #ifndef __SYNTHESIS__ + + +/// @brief Dissassemble RISC-V instructions to C string. +/// +/// Takes the 32 Bit RISC-V instruction insn and disassembles it into a human readable C string. +/// Disassembles all instructions the ParaNut processor can execute (RV32IMA currently). +/// Returns "? 0xHEX_VALUE_OF_INSN" if the instruction is unknown or invalid. +/// +/// __NOTE:__ The returned string is only valid until the next call to this function. +/// @param insn is the instruction to disassemble. +/// @return A human readable C string representing the disassemble of insn. +char *pn_DisAss (TWord insn); + +///@} // @name Testbench helpers... +//////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @name Performance measuring... +/// @{ + +/// @brief Event definition class. +class CEventDef { +public: + /// @brief Event name for displaying. + const char *name; + /// @brief 0 - event is just counted, 1 - event is timed. + bool is_timed; +}; + + +/// @brief Performance monitor class. +class CPerfMon { +public: + /// @brief Default constructor, 0 events and no event definitions. + CPerfMon () { Init (0, NULL); } + /// @brief Constructor for supplying number of events and event definition(s). + CPerfMon (int events, CEventDef *ev_tab) { Init (events, ev_tab); } + /// @brief Destructor. + ~CPerfMon () { Done (); } + + /// @brief Init performance monitor for supplied number of events and event definition(s). + void Init (int events, CEventDef *ev_tab); + /// @brief Free reserved memory for performance monitoring. + void Done (); + + /// @brief Reset performance monitor (counted events/times, not number of events and event definition(s)). + void Reset (); + /// @brief Count an event at current simulation time. + void Count (int ev_no); + + /// @brief Display the collected performance information. + void Display (const char *name = NULL); + +protected: + /// @brief Number of events this performance monitor monitors. + int events_; + /// @brief Pointer to event definition(s). + CEventDef *ev_tab_; + /// @brief Event counter table. + int *count_tab_; + /// @brief Event total time table. + double *time_tab_, + /// @brief Event minimal time table. + *min_tab_, + /// @brief Event maximum time table. + *max_tab_; + + /// @brief Time stamp of last event. + double last_stamp_; + /// @brief Event number of last event. + int last_no_; +}; + + +// ***** CPerfMonCPU ***** +/// @brief CPU performance monitor events enum. +typedef enum { EV_ALU = 0, EV_LOAD, EV_STORE, EV_JUMP, EV_OTHER, EV_IFETCH} EEventsCPU; + +/// @brief CPU performance monitor class. +#ifndef __SYNTHESIS__ +class CPerfMonCPU : public CPerfMon { +public: + /// @brief Default constructor for @ref EEventsCPU events and event definitions. + CPerfMonCPU () { Init (); } + /// @brief Call CPerfMon::Init() for @ref EEventsCPU events and event definitions. + void Init (); + + /// @brief Call CPerfMon::Count() after casting @ref EEventsCPU to int. + void Count (EEventsCPU ev_no) { CPerfMon::Count ((int)ev_no); } +}; +#else +class CPerfMonCPU : public CPerfMon{ +public: + CPerfMonCPU () { Init(); } + + void Display () { /* nothing */ + } + void Count (EEventsCPU ev_no) { /* nothing */ + } + void Init (); +}; +#endif + +///@} // @name Performance measuring... +//////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef __SC_TOOL__ +#define PN_CLOCK_TRIGGERED(method_name) SC_CTHREAD(method_name, clk.pos()); +#else +#define PN_CLOCK_TRIGGERED(method_name) SC_METHOD(method_name) sensitive << clk.pos(); +#endif +/// @} // @file +#endif diff --git a/examples/dm_paranut/debug_rom.h b/examples/dm_paranut/debug_rom.h new file mode 100644 index 00000000..c447682a --- /dev/null +++ b/examples/dm_paranut/debug_rom.h @@ -0,0 +1,94 @@ +/************************************************************************* + + This file is part of the ParaNut project. + + Copyright (C) 2019-2020 Alexander Bahle + Hochschule Augsburg, University of Applied Sciences + + Description: + This file contains the instructions contained in the Debug Modules + Debug Rom + + 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, 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. + + *************************************************************************/ + +const unsigned char debug_rom_bin[] = { + 0x6f, 0x00, 0xc0, 0x00, // 0x00 jal zero, _entry + 0x6f, 0x00, 0x80, 0x03, // 0x04 jal zero, _resume + 0x6f, 0x00, 0x40, 0x04, // 0x08 jal zero, _exception + // _entry: + 0x0f, 0x00, 0xf0, 0x0f, // 0x0c fence + 0x73, 0x10, 0x24, 0x7b, // 0x10 csrw CSR_DSCRATCH, s0 + // entry_loop: + 0x73, 0x24, 0x40, 0xf1, // 0x14 csrr s0, CSR_MHARTID + 0x23, 0x20, 0x80, 0x30, // 0x18 sw s0, HALTED(zero) + 0x03, 0x44, 0x04, 0x10, // 0x1c lbu s0, FLAGS(s0) + 0x13, 0x74, 0x34, 0x00, // 0x20 andi s0, s0, (1 << FLAG_GO) | (1 << FLAG_RESUME) + 0xe3, 0x08, 0x04, 0xfe, // 0x24 beqz s0, entry_loop + + 0x13, 0x74, 0x14, 0x00, // 0x28 andi s0, s0, (1 << FLAG_GO) + 0x63, 0x08, 0x04, 0x00, // 0x2c beqz s0, _resume + + 0x73, 0x24, 0x20, 0x7b, // 0x30 csrr s0, CSR_DSCRATCH + 0x23, 0x22, 0x00, 0x30, // 0x34 sw zero, GOING(zero) + 0x67, 0x00, 0x00, 0x20, // 0x38 jalr zero, zero, 0x200 + // _resume: + 0x73, 0x24, 0x40, 0xf1, // 0x3c csrr s0, CSR_MHARTID + 0x23, 0x24, 0x80, 0x30, // 0x40 sw s0, RESUMING(zero) + 0x73, 0x24, 0x20, 0x7b, // 0x44 csrr s0, CSR_DSCRATCH + 0x73, 0x00, 0x20, 0x7b, // 0x48 dret + // _exception: + 0x23, 0x26, 0x00, 0x30, // 0x4c sw zero, EXCEPTION(zero) + 0x73, 0x00, 0x10, 0x00 // 0x50 ebreak +}; +unsigned int debug_rom_bin_len = 84; + +const unsigned int debug_rom[] = { + 0x00c0006f, // 0x00 jal zero, _entry + 0x0380006f, // 0x04 jal zero, _resume + 0x0440006f, // 0x08 jal zero, _exception + // _entry: + 0x0ff0000f, // 0x0c fence + 0x7b241073, // 0x10 csrw CSR_DSCRATCH, s0 + // entry_loop: + 0xf1402473, // 0x14 csrr s0, CSR_MHARTID + 0x30802023, // 0x18 sw s0, HALTED(zero) + 0x10044403, // 0x1c lbu s0, FLAGS(s0) + 0x00347413, // 0x20 andi s0, s0, (1 << FLAG_GO) | (1 << FLAG_RESUME) + 0xfe0408e3, // 0x24 beqz s0, entry_loop + + 0x00147413, // 0x28 andi s0, s0, (1 << FLAG_GO) + 0x00040863, // 0x2c beqz s0, _resume + + 0x7b202473, // 0x30 csrr s0, CSR_DSCRATCH + 0x30002223, // 0x34 sw zero, GOING(zero) + 0x20000067, // 0x38 jalr zero, zero, 0x200 + // _resume: + 0xf1402473, // 0x3c csrr s0, CSR_MHARTID + 0x30802423, // 0x40 sw s0, RESUMING(zero) + 0x7b202473, // 0x44 csrr s0, CSR_DSCRATCH + 0x7b200073, // 0x48 dret + // _exception: + 0x30002623, // 0x4c sw zero, EXCEPTION(zero) + 0x00100073, // 0x50 ebreak +}; diff --git a/examples/dm_paranut/dm.cpp b/examples/dm_paranut/dm.cpp new file mode 100644 index 00000000..04151059 --- /dev/null +++ b/examples/dm_paranut/dm.cpp @@ -0,0 +1,592 @@ +/************************************************************************* + + This file is part of the ParaNut project. + + Copyright (C) 2019 Alexander Bahle + 2022 Marco Milenkovic + Hochschule Augsburg, University of Applied Sciences + + 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, 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. + + *************************************************************************/ + + +#include "dm.h" +#include "debug_rom.h" + +#ifndef __SYNTHESIS__ +void MDebugModule::Trace (sc_trace_file *tf, int level) { + if (!tf || pn_trace_verbose) printf ("\nSignals of Module \"%s\":\n", name ()); + + // Ports... + PN_TRACE (tf, clk_i); + PN_TRACE (tf, rst_i); + // to EXU ... + PN_TRACE (tf, dbg_reset); + // WB + PN_TRACE (tf, ack_o); + PN_TRACE (tf, rty_o); + PN_TRACE (tf, err_o); + PN_TRACE (tf, dat_o); + PN_TRACE (tf, dat_i); + PN_TRACE (tf, adr_i); + PN_TRACE (tf, stb_i); + PN_TRACE (tf, cyc_i); + PN_TRACE (tf, we_i); + + // internal registers/signals... + PN_TRACE_BUS (tf, dm_regs, DBG_NUM_REGISTERS); + PN_TRACE_BUS (tf, dm_flags, CFG_NUT_CPU_CORES); + PN_TRACE (tf, reg_sel); + PN_TRACE (tf, reg_in); + PN_TRACE (tf, reg_write); + PN_TRACE (tf, dm_state); + PN_TRACE (tf, dbg_request); + PN_TRACE (tf, command_written); + PN_TRACE (tf, flag_go); + + // abstracts + PN_TRACE (tf, reg_abstracts_cmderr); + PN_TRACE (tf, reg_abstracts_busy); + PN_TRACE (tf, abstracts_cmderr); + PN_TRACE (tf, abstracts_busy); + + // abstractauto + PN_TRACE (tf, abstractauto_autoexecdata); + + // command + PN_TRACE (tf, cmd); + + // dmcontrol + PN_TRACE (tf, dmcontrol_hartsel); + PN_TRACE (tf, dmcontrol_haltreq); + PN_TRACE (tf, dmcontrol_active); + PN_TRACE (tf, dmcontrol_ndmreset); + + // dmstatus + PN_TRACE (tf, dmstatus_allhalted); + PN_TRACE (tf, dmstatus_allresumeack); + + // haltsum0 + PN_TRACE (tf, haltsum); + + // DMI + PN_TRACE (tf, dmi_adr_i); + PN_TRACE (tf, dmi_dat_i); + PN_TRACE (tf, dmi_dat_o); + PN_TRACE (tf, dmi_rd); + PN_TRACE (tf, dmi_wr); + + // WB + PN_TRACE (tf, wb_ack_o); +} +#endif + + +void MDebugModule::TransitionMethod () { + const sc_int<21> progbuf_jump = -DBG_PROGBUF_JUMP; + sc_uint<32> cmd_var; + sc_uint<8> cmd_type; + sc_uint<7> cmd_size; + sc_uint<16> cmd_regno; + bool cmd_write, cmd_transfer, cmd_postexec; + sc_uint<5> hartsel; + sc_uint haltsum_var, dbg_request_var; + + // Read input signals/ports + cmd_var = cmd.read (); + hartsel = dmcontrol_hartsel.read (); + haltsum_var = haltsum.read (); + + // Command: + cmd_type = cmd_var (31, 24); + cmd_size = cmd_var (22, 20); + cmd_regno = cmd_var (15, 0); + cmd_write = cmd_var[16]; + cmd_transfer = cmd_var[17]; + cmd_postexec = cmd_var[18]; + + // Towards EXU... + for (unsigned int i = 0; i < CFG_NUT_CPU_CORES; i++) + if (i == hartsel) + dbg_request_var[i] = dmcontrol_haltreq.read (); + else + dbg_request_var[i] = 0; + dbg_reset = dmcontrol_ndmreset.read (); + dbg_request = dbg_request_var; + + // Preset control signals + reg_sel = 0; + reg_in = 0; + reg_write = 0; + flag_go = 0; + abstracts_cmderr = reg_abstracts_cmderr.read (); + abstracts_busy = reg_abstracts_busy.read (); + + dm_state_next = dm_state.read (); + + // State Transition + switch (dm_state.read ()) { + case Idle: // Idle: Wait for abstract command from host + // Only accept a command if we did not encouter an error before + if (command_written && reg_abstracts_cmderr.read () == CMDERR_NONE) { + // Currently busy? + if (reg_abstracts_busy) + abstracts_cmderr = CMDERR_BUSY; + else + dm_state_next = CMD; + } + break; + case CMD: // CMD: + // Reset busy flag (might be set from earlier debug session) + abstracts_busy = 0; +// PN_INFOF(("DM Command:0x%08x = type: %d, size: %d, regno: 0x%04x, write: %d transfer: %d, postexec: %d", +// (uint32_t) cmd_var, (uint32_t) cmd_type, (uint32_t) cmd_size, (uint32_t) cmd_regno, (uint32_t) cmd_write, +// (uint32_t) cmd_transfer, (uint32_t) cmd_postexec)); + if (cmd_type == 0 && cmd_size == 2 && cmd_regno >= 0x1000 && cmd_regno <= 0x101f) { + // Access Register Command with size 2 + if (haltsum_var[hartsel] == 0) { + // PN_ERROR: Hart is not halted + PN_WARNINGF (("DM: Selected hart is not halted!")); + abstracts_cmderr = CMDERR_HALTRESUME; + dm_state_next = Idle; + } else { + dm_state_next = CMD_ACCESSR; + } + } else { + // PN_ERROR: cmd not supported + PN_WARNINGF (("DM: Abstract Command of tpye %d with size %d not supported!", + cmd_type.value (), cmd_size.value ())); + abstracts_cmderr = CMDERR_NOTSUP; + dm_state_next = Idle; + } + break; + case CMD_ACCESSR: // CMD_ACCESSR: + // Set busy bit + abstracts_busy = 1; + // Write either LW or SW instruction to abstract0 + reg_sel = 0; // abstract0 + reg_write = 1; + if (cmd_transfer) { + if (cmd_write) + // lw regno, data0(x0) + reg_in = (sc_uint<12> (0), sc_uint<5> (0), sc_uint<3> (2), cmd_regno (4, 0), + sc_uint<7> (0x03)); + else + // sw regno, data0(x0) + reg_in = (sc_uint<7> (0), cmd_regno (4, 0), sc_uint<5> (0), sc_uint<3> (2), + sc_uint<5> (reg_data0 * 4), sc_uint<7> (0x23)); + } else { + // nop (addi x0, x0, 0) + reg_in = 0x13; + } + dm_state_next = CMD_POSTEXEC; + break; + case CMD_POSTEXEC: // CMD_POSTEXEC + // Set busy bit + abstracts_busy = 1; + // Write either J or EBREAK instruction to abstract1 + reg_sel = 1; // abstract1 + reg_write = 1; + if (cmd_postexec) + // j r0, progbuf0 + reg_in = (progbuf_jump[20], progbuf_jump (10, 1), progbuf_jump[11], + progbuf_jump (19, 12), sc_uint<5> (0), sc_uint<7> (0x6F)); + else + // ebreak + reg_in = sc_uint<32> ((sc_uint<11> (1), sc_uint<13> (0), sc_uint<7> (0x73))); + dm_state_next = CMD_GO; + break; + case CMD_GO: // CMD_GO: + // Set busy bit + abstracts_busy = 1; + // Set go flag and go back to Idle + flag_go = 1; + dm_state_next = Idle; + break; + default: + break; + } + + // WB acknowlegde port (only active during stb & cyc) + ack_o = wb_ack_o & stb_i & cyc_i; +} + +void MDebugModule::RegisterMethod () { +#pragma HLS ARRAY_PARTITION variable = dm_regs complete dim = 1 +#pragma HLS ARRAY_PARTITION variable = dm_flags complete dim = 1 +#pragma HLS ARRAY_PARTITION variable = debug_rom complete dim = 1 +#pragma HLS ARRAY_PARTITION variable = dmi_wr_last complete dim = 1 +#pragma HLS ARRAY_PARTITION variable = dmi_rd_last complete dim = 1 +#pragma HLS ARRAY_PARTITION variable = dmi_adr_last complete dim = 1 +#pragma HLS ARRAY_PARTITION variable = dmi_dat_i_last complete dim = 1 + // DMI variables + sc_uint dmi_adr; + sc_uint<32> dmi_dat_in, dmi_dat_out; + // WB variables + sc_uint<32> wb_adr, wb_dat_i, wb_out; + sc_uint wb_sel; + sc_uint<11> wb_offset; + sc_uint<3> wb_reg_offset; + // Internal variables + sc_uint haltsum_var; + sc_uint hartsel, new_hartsel, wb_hartsel; + sc_uint<20> hartsel_out; + sc_uint<8> flags_var[CFG_NUT_CPU_CORES]; +#pragma HLS ARRAY_PARTITION variable = flags_var complete dim = 1 // At 8 cores the flags_var will be mapped to BRAM without this pragma + sc_uint regSel_var; + sc_uint<32> regIn_var; + bool reg_abstracts_busy_var; + sc_uint<3> reg_abstracts_cmderr_var; + + // Reset Area + + for (unsigned int n = 0; n < DBG_NUM_REGISTERS; n++) dm_regs[n] = 0; + for (unsigned int n = 0; n < DBG_FLAG_SIZE; n++) dm_flags[n] = 0; + + command_written = 0; + cmd = 0; + haltsum = 0; + + dmcontrol_hartsel = 0; + dmcontrol_haltreq = 0; + dmcontrol_active = 0; + dmcontrol_ndmreset = 0; + + dmstatus_allhalted = 0; + dmstatus_allresumeack = 0; + + abstractauto_autoexecdata = 0; + + reg_abstracts_cmderr = 0; + reg_abstracts_busy = 0; + + for (int n = 0; n < REG_RD_WR_STAGES; n++) { + dmi_wr_last[n] = 0; + dmi_rd_last[n] = 0; + } + + for (int n = 0; n < REG_STAGES; n++) { + dmi_adr_last[n] = 0; + dmi_dat_i_last[n] = 0; + } + + dm_state = Idle; + + // Reset output port registers + dat_o = 0x0; + wb_ack_o = 0; + rty_o = 0; + err_o = 0; + + dmi_dat_o = 0x0; + + wait(); + + while(true){ // infinity loop + // Read input signals + // DMI + dmi_adr = dmi_adr_last[REG_ID_LAST].read (); + dmi_dat_in = dmi_dat_i_last[REG_ID_LAST].read (); + // WB + wb_sel = sel_i.read (); + #if CFG_MEMU_BUSIF_WIDTH == 64 + // This implementation assumes that either top or bottom 32 bits are read/written never both! + bool top_word = wb_sel.range(7, 4).or_reduce(); + wb_dat_i = top_word ? dat_i.read().range(63, 32) : dat_i.read().range(31, 0); + wb_adr = top_word ? adr_i.read() + 4 : adr_i.read(); + #else + wb_dat_i = dat_i.read (); + wb_adr = adr_i.read (); + #endif + hartsel = dmcontrol_hartsel.read (); + regSel_var = reg_sel.read (); + regIn_var = reg_in.read (); + if (CFG_NUT_CPU_CORES_LD == 0) + new_hartsel = 0; // Hartsel is fixed to 0 if CFG_NUT_CPU_CORES_LD == 0 + else + new_hartsel = (sc_uint)(dmi_dat_in (15, 6), + dmi_dat_in (25, 16)); + haltsum_var = haltsum.read (); + + for (unsigned int i = 0; i < CFG_NUT_CPU_CORES; i++) { + flags_var[i] = dm_flags[i].read (); + } + reg_abstracts_busy_var = reg_abstracts_busy.read (); + reg_abstracts_cmderr_var = reg_abstracts_cmderr.read (); + + + // Preset output ports/variables + dmi_dat_out = 0; + hartsel_out = hartsel; + + wb_hartsel = wb_dat_i; + wb_offset = wb_adr (10, 0); // implementation specific + wb_reg_offset = wb_offset (4, 2); // implementation specific + wb_out = 0; + + wb_ack_o = 0; + err_o = 0; + rty_o = 0; + + command_written = 0; + + // Preset cmderr and busy flags + reg_abstracts_cmderr = abstracts_cmderr.read (); + reg_abstracts_busy = abstracts_busy.read (); + + // Write go flag into flags register + if (flag_go) flags_var[hartsel] = 1; + // Handle DMI Read/Write + if (dmi_rd_last[1] & !dmi_rd_last[2]) { + // READ ACCESS + switch (dmi_adr) { + case data0: + if (reg_abstracts_busy_var) { + // Read to data0 during abstract command sets abstract_cmderr to CMDERR_BUSY + reg_abstracts_cmderr = CMDERR_BUSY; + dmi_dat_out = -1; + } else { + // If not busy and abstractauto_autoexecdata is set, set command_written + command_written = abstractauto_autoexecdata.read (); + dmi_dat_out = dm_regs[reg_data0].read (); + } + break; + case dmcontrol: + dmi_dat_out = (dmcontrol_haltreq.read (), + sc_uint<5> (0), + hartsel_out (9, 0), + hartsel_out (19, 10), + sc_uint<4> (0), + dmcontrol_ndmreset.read (), + dmcontrol_active.read ()); + break; + case dmstatus: + dmi_dat_out = (sc_uint<14> (0), + dmstatus_allresumeack.read (), + dmstatus_allresumeack.read (), + sc_uint<4> (0), // no CPU should be nonexistant/unavailable + !dmstatus_allhalted.read (), + !dmstatus_allhalted.read (), + dmstatus_allhalted.read (), + dmstatus_allhalted.read (), + sc_uint<1> (1), // authenticated + sc_uint<3> (0), + sc_uint<4> (2)); // Version 1.3 + break; + case abstracts: + dmi_dat_out = (sc_uint<3> (0), + sc_uint<5> (DBG_NUM_PROGBUF), + sc_uint<11> (0), + reg_abstracts_busy_var, + sc_uint<1> (0), + reg_abstracts_cmderr_var, + sc_uint<4> (0), + sc_uint<4> (DBG_NUM_DATA)); + break; + case progbuf0: + dmi_dat_out = dm_regs[reg_progbuf0].read (); + break; + case progbuf1: + dmi_dat_out = dm_regs[reg_progbuf1].read (); + break; + case progbuf2: + dmi_dat_out = dm_regs[reg_progbuf2].read (); + break; + case haltsum0: + dmi_dat_out = haltsum.read (); + break; + default: + dmi_dat_out = 0x0; + PN_WARNINGF (("DM DMI READ to unknown address: (0x%08x)", (TWord)dmi_adr)); + break; + } + dmi_dat_o = dmi_dat_out; + } else if (dmi_wr_last[1] & !dmi_wr_last[2]) { + // DMI WRITE ACCESS + switch (dmi_adr) { + case data0: + if (reg_abstracts_busy_var) { + // Write to data0 during abstract command sets abstract_cmderr to CMDERR_BUSY + reg_abstracts_cmderr = CMDERR_BUSY; + } else { + dm_regs[reg_data0] = dmi_dat_in; + // If abstractauto_autoexecdata is set write to data0 triggers command_execution + command_written = abstractauto_autoexecdata.read (); + } + break; + case dmcontrol: + dmcontrol_hartsel = new_hartsel; + dmcontrol_active = dmi_dat_in[0]; + dmcontrol_ndmreset = dmi_dat_in[1]; + dmcontrol_haltreq = dmi_dat_in[31]; + // Reset resumeack on haltreq + if (dmi_dat_in[31]) dmstatus_allresumeack = 0; + // Set resume flag if bit resumereq is set and haltreq == 0 + if (dmi_dat_in[30] && !dmi_dat_in[31]) { + flags_var[new_hartsel] = 1U << 1; + dmstatus_allresumeack = 0; + } + // PN_INFOF ((" (%s) DMCONTROL: hartsel: %d, active: %d, ndmreset: %d, haltreq: %d", + // strrchr (name (), '.') + 1, new_hartsel.value (), (bool)dmi_dat[0], + // (bool)dmi_dat[1], (bool)dmi_dat[31])); + break; + case abstracts: + // Clear cmderr flags + reg_abstracts_cmderr = reg_abstracts_cmderr_var & ~(__uint8_t) (dmi_dat_in (10, 8)); + break; + case command: + cmd = dmi_dat_in; + command_written = 1; + break; + case abstractauto: + abstractauto_autoexecdata = dmi_dat_in[0]; + break; + case progbuf0: + dm_regs[reg_progbuf0] = dmi_dat_in; + break; + case progbuf1: + dm_regs[reg_progbuf1] = dmi_dat_in; + break; + case progbuf2: + dm_regs[reg_progbuf2] = dmi_dat_in; + break; + default: + // Nothing + PN_WARNINGF (("DM DMI WRITE to unknown address: (0x%08x):=0x%08x", (TWord)dmi_adr, (TWord)dmi_dat_in)); + break; + } + } else if (stb_i == 1 && cyc_i == 1 && dm_state.read () == Idle) { + // Wishbone Read/Write (only allowed if DM is in idle state) + if (IsAdressed (wb_adr)) { + #ifndef __SYNTHESIS__ + // Warn if the debug mode is not enabled and the DMs memory is accessed by the processor. + // -> This should not happen and is probably an error in the software that is being executed + if (!pn_cfg_debug_mode) + PN_WARNINGF (("DM: Debug Module memory accessed while not running in debug mode: %s to 0x%08x", we_i.read () ? "write" : "read", (TWord)wb_adr)); + #endif + // Handle wishbone read/write + if (we_i) { // WRITE ACCESS + // DBG REG & OTHER + switch (wb_offset) { + case 0x0: // data0 address from WB is 0x0! + dm_regs[reg_data0] = wb_dat_i; + break; + case DBG_HALTED_OFFSET: + // Set halted bit in haltsum + haltsum_var[wb_dat_i.range(CFG_NUT_CPU_CORES-1, 0)] = 1; + // If this CPU is selected and has no GO flag pending abstratcs_busy flag can be reset + if (wb_hartsel == hartsel) { + if (flags_var[wb_hartsel][0] == 0) reg_abstracts_busy = 0; + } + break; + case DBG_GOING_OFFSET: + // EXU is going + flags_var[hartsel] = 0; // Reset flags for this EXU + break; + case DBG_RESUMING_OFFSET: + // EXU is resuming + flags_var[hartsel] = 0; // Reset flags for this EXU + haltsum_var[hartsel] = 0; // Reset haltsum + dmstatus_allresumeack = 1; // Acknowledge the resume + break; + case DBG_EXCEPTION_OFFSET: + // EXU encountered an exception during execution of abstract commands or progbuf + reg_abstracts_cmderr = CMDERR_EXCEPTION; + break; + default: + PN_WARNINGF (("DM WISHBONE WRITE to write only or unknown address: (0x%08x):=0x%08x, " + "reg_offset: (0x%x) ", + (TWord)wb_adr, (TWord)wb_dat_i, (TWord)wb_offset)); + break; + } + } else { // READ ACCESS + if (wb_offset >= DBG_MEMORY_OFFSET && wb_offset <= DBG_MEMORY_OFFSET + DBG_MEMORY_SIZE) { + // DBG ROM: + wb_out = debug_rom[wb_offset (6, 2)]; + } else if (wb_offset >= DBG_FLAG_OFFSET && wb_offset <= DBG_FLAG_OFFSET + DBG_FLAG_SIZE) { + wb_out = (sc_uint<24> (0), dm_flags[(wb_offset ^ DBG_FLAG_OFFSET)].read ()); // << 24; + // PN_INFOF(("DM FLAG READ: (0x%x) = 0x%08x", (TWord)offset, out)); + } else if (wb_offset >= DBG_ABSTRACT_OFFSET && + wb_offset <= DBG_ABSTRACT_OFFSET + DBG_ABSTRACT_NUM * 4) { + wb_out = dm_regs[DBG_NUM_DATA + DBG_NUM_PROGBUF + wb_reg_offset].read (); + // PN_INFOF(("DM ABSTRACT READ: (0x%x) = 0x%08x", (TWord)reg_offset, out)); + } else if (wb_offset == DBG_ABSTRACT_OFFSET + 0xc) { + ; // workaround to suppress warning caused by instruction buffer prefetching non-existent address + } else if (wb_offset < DBG_NUM_REGISTERS * 4) { + wb_out = dm_regs[wb_reg_offset].read (); + // PN_INFOF(("DM REG READ: (0x%x) = 0x%08x", (TWord)reg_offset, out)); + } else { + PN_WARNINGF (("DM WISHBONE READ to unknown address: (0x%x), returning 0x0", (TWord)wb_adr)); + } + } + + #if CFG_MEMU_BUSIF_WIDTH == 64 + dat_o = top_word ? (wb_out, sc_uint<32>(0)) : (sc_uint<32>(0), wb_out); + #else + dat_o = wb_out; + #endif + wb_ack_o = 1; + } + } + + // Writeback + for (unsigned int i = 0; i < CFG_NUT_CPU_CORES; i++) { + dm_flags[i] = flags_var[i]; + } + + // Write regFile from TransitionMethod + if (reg_write) { + switch (regSel_var) { + case 0: + dm_regs[reg_abstract0] = regIn_var; + break; + case 1: + dm_regs[reg_abstract1] = regIn_var; + break; + default: + PN_WARNINGF (("DM ABSTRACT WRITE to unknown register address: (0x%x) = 0x%x", + (TWord)regSel_var, (TWord)reg_in.read ())); + break; + } + } + dmstatus_allhalted = haltsum_var[hartsel]; + haltsum = haltsum_var; + + // Register stages for change of clock domain between JTAG and DM + for (int i = REG_RD_WR_STAGES - 1; i > 0; i--) { + dmi_wr_last[i] = dmi_wr_last[i - 1].read (); + dmi_rd_last[i] = dmi_rd_last[i - 1].read (); + } + dmi_wr_last[0] = dmi_wr.read (); + dmi_rd_last[0] = dmi_rd.read (); + dmi_dat_i_last[1] = dmi_dat_i_last[0].read (); + dmi_dat_i_last[0] = dmi_dat_i.read (); + dmi_adr_last[1] = dmi_adr_last[0].read (); + dmi_adr_last[0] = dmi_adr_i.read (); + + // State Transition + dm_state = dm_state_next.read (); + wait(); + } +} + diff --git a/examples/dm_paranut/dm.h b/examples/dm_paranut/dm.h new file mode 100644 index 00000000..d5145398 --- /dev/null +++ b/examples/dm_paranut/dm.h @@ -0,0 +1,253 @@ +/************************************************************************* + + This file is part of the ParaNut project. + + Copyright (C) 2019 Alexander Bahle + 2022 Marco Milenkovic + Hochschule Augsburg, University of Applied Sciences + + Description: + This is a SystemC model of a Debug Module compatible with the + RISC-V External Debug Support Version 0.13 + + 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, 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. + + *************************************************************************/ + + +#ifndef _DM_ +#define _DM_ + +#include "base.h" +#include "paranut-config.h" + +#include + +// **************** Defines ************* +#define DTM_ADDR_WIDTH 6 +#define DTM_IR_WIDTH 5 + +#define DBG_CMD_WIDTH 32 +#define DBG_ADDRESS 0x00000000 + +#define DBG_REG_OFFSET 0x0 +#define DBG_REG_SIZE 0x40 +#define DBG_NUM_DATA 1U // Number of 32 bit registers +#define DBG_NUM_PROGBUF 3 // Number of 32 bit registers + +#define DBG_FLAG_OFFSET 0x100 +#define DBG_FLAG_SIZE CFG_NUT_CPU_CORES // One byte per CPU +#define DBG_FLAG_REG_OFFSET 0x10 + +#define DBG_ABSTRACT_OFFSET 0x200 +#define DBG_ABSTRACT_REG_OFFSET (DBG_NUM_DATA + DBG_NUM_PROGBUF) +#define DBG_ABSTRACT_NUM_LD 1U +#define DBG_ABSTRACT_NUM (1U << DBG_ABSTRACT_NUM_LD) +#define DBG_PROGBUF_JUMP (DBG_ABSTRACT_OFFSET + (DBG_ABSTRACT_NUM-1)*4 - DBG_NUM_DATA*4) + +#define DBG_HALTED_OFFSET 0x300 +#define DBG_GOING_OFFSET 0x304 +#define DBG_RESUMING_OFFSET 0x308 +#define DBG_EXCEPTION_OFFSET 0x30C + +#define DBG_MEMORY_OFFSET 0x400 +#define DBG_MEMORY_SIZE 0x54 +#define DBG_ROM_ADDRESS (DBG_ADDRESS + DBG_MEMORY_OFFSET) + +#define DBG_NUM_REGISTERS (DBG_NUM_DATA + DBG_NUM_PROGBUF + DBG_ABSTRACT_NUM ) +#define DBG_NUM_REGISTES_BITS 3 // log2(DBG_NUM_REGISTERS) + +#define REG_RD_WR_STAGES 3 +#define REG_STAGES 2 +#define REG_ID_LAST 1 + +// **************** DM Register Addresses ************* +typedef enum { + data0 = 0x4, + + dmcontrol = 0x10, + dmstatus = 0x11, + + abstracts = 0x16, + command = 0x17, + abstractauto = 0x18, + + progbuf0 = 0x20, + progbuf1, + progbuf2, + progbuf15 = 0x2f, + + haltsum0 = 0x40, +} EDMRegAddress; + +typedef enum { + reg_data0, + + reg_progbuf0, + reg_progbuf1, + reg_progbuf2, + + reg_abstract0, + reg_abstract1, +} EDMRegs; + +// **************** DM Command Error Codes ************* +typedef enum { + CMDERR_NONE = 0, + CMDERR_BUSY = 1, + CMDERR_NOTSUP = 2, + CMDERR_EXCEPTION = 3, + CMDERR_HALTRESUME = 4, + CMDERR_OTHER = 7, +} ECMDErr; + +// **************** DM States ************* +typedef enum { + Idle, + CMD, + CMD_ACCESSR, + CMD_POSTEXEC, + CMD_GO, +} EDMState; + + +// **************** MDebugModule ************* +class MDebugModule : ::sc_core::sc_module { +public: + + // Ports (WISHBONE slave)... + sc_in_clk clk_i{"clk_i"}; // clock input + sc_in rst_i{"rst_i"}; // reset + + sc_in stb_i{"stb_i"}; // strobe input + sc_in cyc_i{"cyc_i"}; // cycle valid input + sc_in we_i{"we_i"}; // indicates write transfer + sc_in > sel_i{"sel_i"}; // byte select inputs + sc_out ack_o{"ack_o"}; // normal termination + sc_out err_o{"err_o"}; // termination w/ error + sc_out rty_o{"rty_o"}; // termination w/ retry + + sc_in > adr_i{"adr_i"}; // address bus inputs + sc_in > dat_i{"dat_i"}; // input data bus + sc_out > dat_o{"dat_o"}; // output data bus + + // to EXU + // Debug request: EXUs jump to DBG_ROM_ADDRESS upon handling this signal + sc_out > dbg_request{"dbg_request"}; + // Debug reset: Gets set through the ndmreset bit of dmcontrol register and + // resets all modules (except DM and DTM) + sc_out dbg_reset{"dbg_reset"}; + + // from DTM + sc_in > dmi_adr_i{"dmi_adr_i"}; + sc_in > dmi_dat_i{"dmi_dat_i"}; + sc_out > dmi_dat_o{"dmi_dat_o"}; + sc_in dmi_rd{"dmi_rd"}, + dmi_wr{"dmi_wr"}; + + // Constructor... + SC_HAS_PROCESS (MDebugModule); + MDebugModule (sc_module_name name) + : sc_module (name) { + SC_METHOD (TransitionMethod); + sensitive << dm_state << command_written + << reg_abstracts_busy << reg_abstracts_cmderr + << dmcontrol_haltreq << dmcontrol_hartsel << dmcontrol_ndmreset + << haltsum << cmd + << wb_ack_o << stb_i << cyc_i; + + SC_CTHREAD (RegisterMethod, clk_i.pos()); + async_reset_signal_is(rst_i, true); + // sensitive << clk_i.pos (); + } + + + // Functions... + void Trace (sc_trace_file *tf, int levels = 1); + static inline bool IsAdressed (TWord adr) { return (adr & 0xffff0000) == DBG_ADDRESS; } + +#ifndef __SYNTHESIS__ + void SetNdmreset(bool val) { dmcontrol_ndmreset = val; } + void SetHaltreq(bool val) { dmcontrol_haltreq = val; } +#endif + + // Processes... + void TransitionMethod (); + void RegisterMethod (); + +protected: + // Registers ... + sc_signal > + dm_state, + dm_state_next; + sc_signal + dmi_wr_last[REG_RD_WR_STAGES], + dmi_rd_last[REG_RD_WR_STAGES]; + sc_signal > dmi_dat_i_last[REG_STAGES]; + sc_signal > dmi_adr_last[REG_STAGES]; + + sc_signal > dm_regs[DBG_NUM_REGISTERS]; + sc_signal > dm_flags[CFG_NUT_CPU_CORES]; + + // dmcontrol: + sc_signal > dmcontrol_hartsel; + sc_signal + dmcontrol_haltreq, + dmcontrol_active, + dmcontrol_ndmreset; + + // dmstatus: + sc_signal + dmstatus_allhalted, + dmstatus_allresumeack; + + // abstracts: + sc_signal + abstracts_busy, + reg_abstracts_busy; + sc_signal > + abstracts_cmderr, + reg_abstracts_cmderr; + + // abstractauto + sc_signal abstractauto_autoexecdata; + + // haltsum0: // todo: add measures for more than 32 cores + sc_signal > haltsum; + + // commmand: + sc_signal > cmd; + + + // Internal signals ... + sc_signal + command_written, + flag_go, + wb_ack_o; + sc_signal > reg_sel; + sc_signal reg_write; + sc_signal > reg_in; + +}; + + +#endif diff --git a/examples/dm_paranut/dm_tb.cpp b/examples/dm_paranut/dm_tb.cpp new file mode 100644 index 00000000..df55f0cc --- /dev/null +++ b/examples/dm_paranut/dm_tb.cpp @@ -0,0 +1,367 @@ +/************************************************************************* + + This file is part of the ParaNut project. + + Copyright (C) 2010-2019 Alexander Bahle + Gundolf Kiefer + 2022 Marco Milenkovic + Hochschule Augsburg, University of Applied Sciences + + Description: + This is a testbench for the ParaNut. + + 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, 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. + + *************************************************************************/ + +#include "dm.h" + +#include + +#include +#include + + +#define CLK_PERIOD 10.0 +// ICSC requires DUT top should be instantiated inside wrapper (typically TB) +// and all DUT ports are bound. +struct Tb : sc_module +{ + + // **************** Signals ********************* + + // DMI + sc_signal dmi_rd{"dmi_rd"}, dmi_wr{"dmi_wr"}; + sc_signal > dmi_adr{"dmi_adr"}; + sc_signal > dmi_dat_o{"dmi_dat_o"}; + sc_signal > dmi_dat_i{"dmi_dat_i"}; + // DBG + sc_signal> dbg_request{"dbg_request"}; + sc_signal dbg_reset{"dbg_reset"}; + // WB + sc_in_clk clk{"clk"}; + sc_signal reset{"reset"}; + sc_signal wb_stb{"wb_stb"}, wb_cyc{"wb_cyc"}, wb_we{"wb_we"}, wb_ack{"wb_ack"}, wb_err{"wb_err"}, wb_rty{"wb_rty"}; + sc_signal > wb_sel{"wb_sel"}; + sc_signal> wb_adr{"wb_adr"}; + sc_signal > wb_dat_w{"wb_dat_w"}, wb_dat_r{"wb_dat_r"}; + + MDebugModule dm {"dm"}; + + + + + // *************** Constructor ******************* + + SC_CTOR(Tb) + { + // JTAG DTM + + dm.clk_i (clk); + dm.rst_i (reset); + dm.stb_i (wb_stb); + dm.cyc_i (wb_cyc); + dm.we_i (wb_we); + dm.sel_i (wb_sel); + dm.ack_o (wb_ack); + dm.err_o (wb_err); + dm.rty_o (wb_rty); + dm.adr_i (wb_adr); + dm.dat_i (wb_dat_w); + dm.dat_o (wb_dat_r); + dm.dbg_request (dbg_request); + dm.dbg_reset (dbg_reset); + dm.dmi_adr_i (dmi_adr); + dm.dmi_dat_i (dmi_dat_i); + dm.dmi_dat_o (dmi_dat_o); + dm.dmi_wr (dmi_wr); + dm.dmi_rd (dmi_rd); + + SC_CTHREAD(test_proc, clk.pos()); + + + } + + + // **************** Helpers ********************* + + void InitDMIRead (uint32_t adr) { + dmi_adr.write(adr); + dmi_dat_i.write(0); + dmi_wr.write(0); + dmi_rd.write(1); + } + + uint32_t GetDMIData () { return dmi_dat_o.read (); } + + uint32_t CompleteDMIRead (uint32_t adr) { + InitDMIRead (adr); + + wait (2); + dmi_rd.write(0); + wait (REG_STAGES); + return GetDMIData (); + } + + void InitDMIWrite (uint32_t adr, uint32_t val) { + dmi_adr = adr; + dmi_dat_i = val; + dmi_wr = 1; + dmi_rd = 0; + } + + void CompleteDMIWrite (uint32_t adr, uint32_t val) { + InitDMIWrite (adr, val); + wait (1); + dmi_wr = 0; + wait (REG_STAGES); + } + + void InitWBWrite (uint32_t adr, uint32_t val) { + wb_stb = 1; + wb_cyc = 1; + wb_we = 1; + wb_sel = 0xf; + wb_dat_w = val; + wb_adr = adr; + } + + void CompleteWBWrite (uint32_t adr, uint32_t val) { + InitWBWrite (adr, val); + + while (!wb_ack.read ()) wait (1); + + wb_stb = 0; + wb_cyc = 0; + wb_we = 0; + } + + // **************Testbench function*************** + + void test_proc(){ + + #ifndef __SYNTHESIS__ + // Set cfg_debug_mode to suppress some simulation warnings + pn_cfg_debug_mode = true; + // Trace file ... + sc_trace_file *tf; + if (pn_cfg_vcd_level > 0) { + tf = sc_create_vcd_trace_file ("dm_tb"); + tf->delta_cycles (false); + + PN_TRACE (tf, reset); + PN_TRACE (tf, wb_stb); + PN_TRACE (tf, wb_cyc); + PN_TRACE (tf, wb_adr); + PN_TRACE (tf, wb_dat_w); + PN_TRACE (tf, wb_dat_r); + PN_TRACE (tf, wb_we); + PN_TRACE (tf, wb_ack); + PN_TRACE (tf, wb_rty); + PN_TRACE (tf, wb_err); + PN_TRACE (tf, wb_sel); + PN_TRACE (tf, dbg_request); + PN_TRACE (tf, dbg_reset); + PN_TRACE (tf, dmi_adr); + PN_TRACE (tf, dmi_dat_i); + PN_TRACE (tf, dmi_dat_o); + PN_TRACE (tf, dmi_rd); + PN_TRACE (tf, dmi_wr); + + + dm.Trace (tf, pn_cfg_vcd_level); + } else { + fprintf (stderr, "Tracing is disabled.\n"); + tf = NULL; + } + #endif + + // SystemC elaboration... + fprintf (stderr, "(sim) Starting SystemC elaboration...\n"); + + + + // Run simulation... + fprintf (stderr, "(sim) Starting SystemC simulation...\n\n"); + + + + PN_INFO ("Reset..."); + reset = 1; + wait (5); + PN_INFO ("Running..."); + reset = 0; + + uint32_t ret; + wait(); + while(1){ //infinity Loop + // DMI read: + // -------------------- + PN_INFO ("DMI read test:"); + // Read dmcontrol + ret = CompleteDMIRead (dmcontrol); + PN_INFOF (("dmcontrol: \t0x%08x", ret)); + PN_ASSERTM (ret == 0x0, "Could not read correct dmcontrol"); + // Read dmstatus + wait (1); + ret = CompleteDMIRead (dmstatus); + PN_INFOF (("dmstatus: \t0x%08x", ret)); + PN_ASSERTM (ret == 0x00000c82, "Could not read correct dmstatus"); + // Read abstracts + wait (1); + ret = CompleteDMIRead (abstracts); + PN_INFOF (("abstracts: \t0x%08x", ret)); + PN_ASSERTM (ret == (sc_uint<3> (0), sc_uint<5> (DBG_NUM_PROGBUF), sc_uint<11> (0), sc_uint<5> (0), + sc_uint<4> (0), sc_uint<4> (DBG_NUM_DATA)), + "Could not read correct abstracts"); + + // DMI read multicycle: + // -------------------- + PN_INFO ("DMI read multicycle test:"); + // Read dmstatus + wait (1); + InitDMIRead (dmstatus); + wait (5); + ret = CompleteDMIRead (dmstatus); + PN_INFOF (("dmstatus: \t0x%08x", ret)); + PN_ASSERTM (ret == 0x00000c82, "Could not read correct dmstatus"); + + // DMI write: + // -------------------- + PN_INFO ("DMI write test:"); + // Write dmcontrol (haltreq, ndmreset and active is set) + CompleteDMIWrite (dmcontrol, 0x80000003); + ret = CompleteDMIRead (dmcontrol); + PN_INFOF (("dmcontrol: \t0x%08x", ret)); + PN_ASSERTM (ret == 0x80000003, "Could not read correct dmcontrol"); + PN_ASSERTM (dbg_request.read () == 1, "dbg_request signal is not set"); + PN_ASSERTM (dbg_reset.read () == 1, "dbg_reset signal is not set"); + + // DMI write multicycle: + // -------------------- + PN_INFO ("DMI write multicycle test:"); + // Write dmcontrol (haltreq, ndmreset and active is set) + InitDMIWrite (dmcontrol, 0x00000001); + wait (2); + // Change value -> should not be written + dmi_dat_i = 0x80000002; + wait (2); + ret = CompleteDMIRead (dmcontrol); + PN_INFOF (("dmcontrol: \t0x%08x", ret)); + PN_ASSERTM (ret == 0x00000001, "Could not read correct dmcontrol"); + PN_ASSERTM (dbg_request.read () == 0, "dbg_request signal is not zero"); + PN_ASSERTM (dbg_reset.read () == 0, "dbg_reset signal is not zero"); + + // WB write: + // ------------------- + PN_INFO ("WB write test:"); + // Write halted + CompleteWBWrite (DBG_HALTED_OFFSET, 0x0); + // Read dmstatus + ret = CompleteDMIRead (dmstatus); + PN_INFOF (("dmstatus: \t0x%08x", ret)); + PN_ASSERTM (ret == (sc_uint<16> (0), + sc_uint<4> (0), // no CPU should be nonexistant/unavailable + sc_uint<2> (0), + sc_uint<2> (3), // allhalted, anyhalted + sc_uint<1> (1), // authenticated + sc_uint<3> (0), sc_uint<4> (2)), + "Could not read correct dmstatus"); + + // WB write during DMI write: + // -------------------- + PN_INFO ("WB and DMI write test:"); + // Write dmcontrol (haltreq, ndmreset and active is set) + InitDMIWrite (progbuf0, 0xCAFEBABE); + CompleteWBWrite (DBG_RESUMING_OFFSET, 0x0); + // wait(DMI_CYCLES); + ret = CompleteDMIRead (progbuf0); + PN_INFOF (("progbuf0: \t0x%08x", ret)); + PN_ASSERTM (ret == 0xCAFEBABE, "Could not read correct progbuf0"); + ret = CompleteDMIRead (dmstatus); + PN_INFOF (("dmstatus: \t0x%08x", ret)); + PN_ASSERTM (ret == (sc_uint<14> (0), + sc_uint<2> (3), // allresumeack, anyresumeack + sc_uint<4> (0), // no CPU should be nonexistant/unavailable + sc_uint<2> (3), // allrunning, anyrunning + sc_uint<2> (0), + sc_uint<1> (1), // authenticated + sc_uint<3> (0), sc_uint<4> (2)), + "Could not read correct dmstatus"); + + wait (5); + PN_INFO ("Simulation finished."); + sc_stop(); + wait(); + #ifndef __SYNTHESIS__ + if (tf) sc_close_vcd_trace_file (tf); + #endif + } + + } + + + +}; +// **************** Main ************************ + +int sc_main (int argc, char *argv[]) { + int arg, cfg_help = 0; + + // Parse command line... + arg = 1; + while (arg < argc && argv[arg][0] == '-') { + switch (argv[arg][1]) { + case 't': + pn_cfg_vcd_level = MAX (0, MIN (9, argv[arg][2] - '0')); + fprintf (stderr, "(cfg) vcdLevel = %i\n", pn_cfg_vcd_level); + break; + case 'h': + cfg_help = 1; + break; + default: + printf ("PN_ERROR: Unknown option '%s'.\n", argv[arg]); + arg = argc; + } + arg++; + } + if (cfg_help) { + puts ("Usage: dm_tb []\n" + "\n" + "Options:\n" + " -t: set VCD trace level (0 = no trace file; default = 2)\n"); + return 3; + } + + + sc_clock clk{"clk", sc_time(CLK_PERIOD, SC_NS)}; + Tb tb("tb"); + tb.clk(clk); + sc_start (800, SC_NS); + cout <<"\n\t\t*****Simulation complete*****" << endl; + #ifdef __SYNTHESIS__ + cout << "\n*** To get a simulation output here, run \"cmake -DSYN=OFF ../\" in terminal ***" << endl; + #endif + + + return 0; +} diff --git a/examples/dm_paranut/paranut-config.h b/examples/dm_paranut/paranut-config.h new file mode 100644 index 00000000..df67b06b --- /dev/null +++ b/examples/dm_paranut/paranut-config.h @@ -0,0 +1,299 @@ +/************************************************************************* + + This file is part of the ParaNut project. + + Copyright (C) 2019-2022 Alexander Bahle + Christian H. Meyer + Hochschule Augsburg, University of Applied Sciences + + Description: + This file contains the configuration options for the ParaNut. + + 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. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, 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. + + *************************************************************************/ + +#ifndef _CONFIG_ +#define _CONFIG_ + +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +// Warning: Don't change this file manually! +// Use the "../../config" file located +// at the root of the project +// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +/// @file +/// @brief Configuration Makros used in most *ParaNut* files. +/// @defgroup config Configuration +/// @brief Configuration Makros used in most *ParaNut* files. +/// @{ + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @defgroup config_simulation Simulation +/// @brief SystemC Simulation options. @{ +/// @name SystemC Simulation options... + +/// @brief Simulation clock speed in Hz. @brief +/// Defines the simulation clock speed in Hz. The configured value can be read from the +/// pnclockinfo CSR. +#define CFG_NUT_SIM_CLK_SPEED 25000000 + +/// @brief mtimer timebase. @brief +/// Defines the mtime timer timebase in us +#define CFG_NUT_MTIMER_TIMEBASE_US 1000 + +/// @brief mtimer base address. @brief +/// Defines the address at which the mtimer will be added to the system interconnect +#define CFG_NUT_MTIMER_ADDR 0x80000000 + +/// @brief Simulation memory address. @brief +/// Defines the start address (reset address) of the *ParaNut* during simulation and +/// the address at which the main memory will be added to the system interconnect +/// (MParaNutSystem::MParaNutSystem()). +/// Also used to determine the cacheable memory addresses. +#define CFG_NUT_RESET_ADDR 0x10000000 + +/// @brief Simulation maximum peripherals number. @brief +/// Defines the maximum number of peripherals that can be connected to the systems Wishbone +/// interconnect (MInterconnect). +#define CFG_NUT_SIM_MAX_PERIPHERY 5 + +/// @} // @name SystemC Simulation options... +/// @} // @defgroup config_simulation Simulation +//////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @defgroup config_general General +/// @brief General options. @{ +/// @name General options... + +/// @brief Hardware version derived from git @brief +/// This Machine Architecture ID is storde in csr marchid. +#define CFG_NUT_MIMPID 16777330 + +/// @brief Number of cores overall as log2. @brief +/// Defines the log2 of the overall number of *ParaNut* cores (ExUs). +#define CFG_NUT_CPU_CORES_LD 2 +/// @brief Number of cores overall (derived). @brief +#define CFG_NUT_CPU_CORES (1 << CFG_NUT_CPU_CORES_LD) +/// @brief Number of cpu groups (derived). @brief +#define CFG_NUT_CPU_GROUPS ((CFG_NUT_CPU_CORES-1)/32) + +/// @brief Number of cores (ExUs) with mode capability = 1 (linked). @brief +/// Defines the number of *ParaNut* cores (ExUs) that have a mode capability of 1 and thus can only +/// operate in mode 1 (linked mode). +#define CFG_NUT_CPU_CAP1_CORES 0 + +/// @brief Number of cores (ExUs) with mode capability >= 2 (thread) (derived). @brief +/// Defines the number of *ParaNut* cores (ExUs) that have a mode capability of >= 2 and thus can +/// operate in mode 1 (linked mode) and mode 2 (thread mode). +#define CFG_NUT_CPU_CAP2_CORES (CFG_NUT_CPU_CORES - CFG_NUT_CPU_CAP1_CORES) + +/// @brief ParaNut mode 2 capability value (derived). @brief +/// Contains a bitmask of all mode 2 capable cores. The configured value can be read from the +/// pnm2cp CSR. +#define CFG_EXU_PNM2CAP ~(0xffffffff << CFG_NUT_CPU_CAP2_CORES) + +/// @brief System memory size. @brief +/// Defines the system memory size in Bytes. 8MB is recommended for simulation, otherwise +/// needs to match the memory size of the used board. +/// Also used to determine the cacheable memory addresses. +#define CFG_NUT_MEM_SIZE (256 * MB) + +/// @brief Number of external interrupt lines. @brief +/// Defines the number of external interrupt lines. Needs to be at least equal to 1. +#define CFG_NUT_EX_INT 2 + +/// @} // @name General options... +/// @} // @defgroup config_general General +//////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @defgroup config_exu Execution Unit +/// @brief Execution Unit options. @{ +/// @name Execution Unit options... + +/// @brief RISC-V M-Extension. @brief +/// Defines if the RISC-V M-Extension hardware is enabled/disabled. +/// @param 0 - extension disabled +/// @param 1 - extension enabled +#define CFG_EXU_M_EXTENSION 1 +/// @brief RISC-V A-Extension. @brief +/// Defines if the RISC-V A-Extension hardw +/// @param 0 - extension disabled +/// @param 1 - extension enabled +#define CFG_EXU_A_EXTENSION 1 + +/// @brief Privilege Mode options. @brief +/// Defines the RISC-V privilege mode capability of the ParaNut. Do NOT use any other value! +/// @param 1 - only M-Mode is available +/// @param 2 - M- and U-Mode are available +/// @param 3 - M-, S- and U-Mode available, enable the Memory Management Unit (MMU) +#define CFG_PRIV_LEVELS 1 + +/// @brief Performance counter enable. @brief +/// Defines if the hardware performance counters are enabled/disabled. +/// @param 0 - no performance counters +/// @param 1 - performance counter and 64bit cycle counter is added +#define CFG_EXU_PERFCOUNT_ENABLE 1 +/// @brief Performance counter register width. @brief +/// Defines the number of bits for the hardware performance counters. +/// @warning Has to be between 33 and 64. +#define CFG_EXU_PERFCOUNTER_BITS 40 +/// @brief Performance counter number of registers as log2. @brief +/// Defines the log2 of the number of hardware performance counters. +/// @warning 3 is the only supported value for now (see exu.cpp for implemented counters) +#define CFG_EXU_PERFCOUNTERS_LD 3 +/// @brief Performance counter number of registers (derived). @brief +#define CFG_EXU_PERFCOUNTERS (1 << CFG_EXU_PERFCOUNTERS_LD) + +/// @} // @name Execution Unit options... +/// @} // @defgroup config_exu Execution Unit +//////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @defgroup config_memu Memory Unit +/// @brief Memory Unit options. @{ +/// @name Memory Unit options... + +/// @brief Number of cache banks as log2. @brief +/// Defines the log2 of the number of cache banks. +/// A cache line has a size of @ref CFG_MEMU_CACHE_BANKS words. A good starting point is 2 (4 banks). +#define CFG_MEMU_CACHE_BANKS_LD 2 +/// @brief Number of cache banks (derived). @brief +#define CFG_MEMU_CACHE_BANKS (1 << CFG_MEMU_CACHE_BANKS_LD) + +/// @brief Number of cache sets as log2. @brief +/// Defines the log2 of the number of cache sets. +/// A bank has @ref CFG_MEMU_CACHE_SETS * @ref CFG_MEMU_CACHE_WAYS words. +#define CFG_MEMU_CACHE_SETS_LD 9 +/// @brief Number of cache sets (derived). @brief +#define CFG_MEMU_CACHE_SETS (1 << CFG_MEMU_CACHE_SETS_LD) + +/// @brief Number of cache ways as log2. @brief +/// Defines the log2 of the number of cache ways (cache associativity). +/// A bank has @ref CFG_MEMU_CACHE_SETS * @ref CFG_MEMU_CACHE_WAYS words. +/// @param 0 - 1-way set-associativity +/// @param 1 - 2-way set-associativity +/// @param 2 - 4-way set-associativity +#define CFG_MEMU_CACHE_WAYS_LD 2 +/// @brief Number of cache ways (derived). @brief +#define CFG_MEMU_CACHE_WAYS (1 << CFG_MEMU_CACHE_WAYS_LD) + +/// @brief Number of ports per bank. @brief +/// Since every bank is implemented as one Block +/// RAM, for maximum performance this should be set to the maximum number of +/// Block RAM ports supported by the target device (i.e. 2) but can be +/// reduced in order to save some area. +#define CFG_MEMU_BANK_RAM_PORTS 2 + +/// @brief Cache replacement method. @brief +/// Defines the cache replacement method/strategy, either pseudo-random or least recently used (LRU). +/// @param 0 - random replacement +/// @param 1 - LRU replacement +#define CFG_MEMU_CACHE_REPLACE_LRU 1 + +/// @brief Arbiter Method. @brief +/// Defines the MemU arbitration method/strategy, either a round-robin or pseudo-random arbitration. +/// @param >0 - round-robin arbitration, switches every (1 << CFG_MEMU_ARBITER_METHOD) clocks +/// @param <0 - pseudo-random arbitration (LFSR-based) +#define CFG_MEMU_ARBITER_METHOD 7 + +/// @brief Busif Data Width. @brief +/// Defines the width of the master Wishbone data bus. +/// @param 32 - 32 Bit data width +/// @param 64 - 64 Bit data width +#define CFG_MEMU_BUSIF_WIDTH 32 + +/// @brief Overall cache size in Bytes (derived). @brief +#define CFG_MEMU_CACHE_SIZE (CFG_MEMU_CACHE_SETS * CFG_MEMU_CACHE_WAYS * CFG_MEMU_CACHE_BANKS * 4) + +/// @brief Number of write ports (WPORTS) in the MemU (derived). @brief +#define CFG_MEMU_WPORTS CFG_NUT_CPU_CORES +/// @brief Number of read ports (RPORTS) in the MemU (derived). @brief +#define CFG_MEMU_RPORTS (2 * CFG_NUT_CPU_CORES) + +/// @} // @name Memory Unit options... +/// @} // @defgroup config_memu Memory Unit +//////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @defgroup config_mmu Memory Management Unit (MMU) +/// @brief Memory Management Unit (MMU) options. @{ +/// @name Memory Management Unit (MMU) options... + +/// @brief TLB enable @brief +/// Enable or disable the Translation Lookaside Buffer (TLB) +/// @param 0 - TLB disabled +/// @param 1 - TLB enabled +#define CFG_MMU_TLB_ENABLE 0 + +/// @brief Number of TLB entries as log2. @brief +/// Defines the log2 of the number of TLB entries +#define CFG_MMU_TLB_ENTRIES_LD 2 +/// @brief Number of TLB entries (derived). @brief +#define CFG_MMU_TLB_ENTRIES (1 << CFG_MMU_TLB_ENTRIES_LD) + +/// @name Memory Management Unit (MMU) options... +/// @defgroup config_mmu Memory Management Unit (MMU) +//////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @defgroup config_ifu Instruction Fetch Unit +/// @brief Instruction Fetch Unit options. @{ +/// @name Instruction Fetch Unit options... + +/// @brief Instruction buffer size as log2. @brief +/// Defines the log2 of the number of instruction buffer elements (max. number of prefetched +/// instructions). +#define CFG_IFU_IBUF_SIZE_LD 2 +/// @brief Instruction buffer size (derived). @brief +#define CFG_IFU_IBUF_SIZE (1 << CFG_IFU_IBUF_SIZE_LD) + +/// @} // @name Instruction Fetch Unit options... +/// @} // @defgroup config_ifu Instruction Fetch Unit +//////////////////////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////////////////////// +/// @defgroup config_lsu Load Store Unit +/// @brief Load Store Unit options. @{ +/// @name Load Store Unit options... + +/// @brief LSU write buffer size as log2. @brief +/// Defines the log2 of the number of write buffer elements in the LSU. +#define CFG_LSU_WBUF_SIZE_LD 2 +/// @brief LSU write buffer size (derived). @brief +#define CFG_LSU_WBUF_SIZE (1 << CFG_LSU_WBUF_SIZE_LD) + +/// @} // @name Load Store Unit options... +/// @} // @defgroup config_lsu Load Store Unit +//////////////////////////////////////////////////////////////////////////////////////////////// + +/// @} // @defgroup config Configuration +#endif +