From 0c23a9b7da2706bef27be934c4c409460770ab4a Mon Sep 17 00:00:00 2001 From: colby-nyce Date: Tue, 10 Sep 2024 14:47:43 -0500 Subject: [PATCH 1/6] Fix typo in comment --- sparta/src/Report.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sparta/src/Report.cpp b/sparta/src/Report.cpp index a9bd554667..b92eb0ddb5 100644 --- a/sparta/src/Report.cpp +++ b/sparta/src/Report.cpp @@ -4,7 +4,7 @@ * \file Report.cpp * \brief Part of the metrics and statistics system. * Contains a Report class which refers to a number of StatisticInstance - * instances of other Reports to present a set of associated simuation metrics + * instances of other Reports to present a set of associated simulation metrics */ #include "sparta/report/Report.hpp" From 0d6d2b2d9ffbe51bfa7f760b6d97a2fab450739d Mon Sep 17 00:00:00 2001 From: colby-nyce Date: Fri, 13 Sep 2024 10:48:26 -0500 Subject: [PATCH 2/6] Create registers from JSON definition --- sparta/scripts/GenRegisterJSON.py | 498 ++++++++++++++++++ .../sparta/functional/RegisterDefnsJSON.hpp | 263 +++++++++ sparta/sparta/functional/RegisterSet.hpp | 75 +++ sparta/test/Register/Register_test.cpp | 87 ++- 4 files changed, 917 insertions(+), 6 deletions(-) create mode 100755 sparta/scripts/GenRegisterJSON.py create mode 100644 sparta/sparta/functional/RegisterDefnsJSON.hpp diff --git a/sparta/scripts/GenRegisterJSON.py b/sparta/scripts/GenRegisterJSON.py new file mode 100755 index 0000000000..45e8c8839d --- /dev/null +++ b/sparta/scripts/GenRegisterJSON.py @@ -0,0 +1,498 @@ +#!/usr/bin/env python3 +"""Helper script for regenerating register definition JSON files. +""" + +from enum import IntEnum +import json +import math +import sys +import pdb + +from RV64_CSR import CSR64_DEFS +from RV32_CSR import CSR32_DEFS + +from RV64_CONSTANT import RV64_CONSTS +from RV64_CONSTANT import ATHENA_INTERNAL_REGISTERS + +class RegisterGroup(IntEnum): + INT = 0 + FP = 1 + VEC = 2 + CSR = 3 + +def GetGroupName(group): + if group == RegisterGroup.INT: + return "INT" + elif group == RegisterGroup.FP: + return "FP" + elif group == RegisterGroup.VEC: + return "VEC" + elif group == RegisterGroup.CSR: + return "CSR" + else: + return "UNKNOWN" + +INT_ALIASES = { + "x0": ["zero"], + "x1": ["ra"], + "x2": ["sp"], + "x3": ["gp"], + "x4": ["tp"], + "x5": ["t0"], + "x6": ["t1"], + "x7": ["t2"], + "x8": ["s0", "fp"], + "x9": ["s1"], + "x10": ["a0"], + "x11": ["a1"], + "x12": ["a2"], + "x13": ["a3"], + "x14": ["a4"], + "x15": ["a5"], + "x16": ["a6"], + "x17": ["a7"], + "x18": ["s2"], + "x19": ["s3"], + "x20": ["s4"], + "x21": ["s5"], + "x22": ["s6"], + "x23": ["s7"], + "x24": ["s8"], + "x25": ["s9"], + "x26": ["s10"], + "x27": ["s11"], + "x28": ["t3"], + "x29": ["t4"], + "x30": ["t5"], + "x31": ["t6"], +} +FP_ALIASES = { + "f0": ["ft0"], + "f1": ["ft1"], + "f2": ["ft2"], + "f3": ["ft3"], + "f4": ["ft4"], + "f5": ["ft5"], + "f6": ["ft6"], + "f7": ["ft7"], + "f8": ["fs0"], + "f9": ["fs1"], + "f10": ["fa0"], + "f11": ["fa1"], + "f12": ["fa2"], + "f13": ["fa3"], + "f14": ["fa4"], + "f15": ["fa5"], + "f16": ["fa6"], + "f17": ["fa7"], + "f18": ["fs2"], + "f19": ["fs3"], + "f20": ["fs4"], + "f21": ["fs5"], + "f22": ["fs6"], + "f23": ["fs7"], + "f24": ["fs8"], + "f25": ["fs9"], + "f26": ["fs10"], + "f27": ["fs11"], + "f28": ["ft8"], + "f29": ["ft9"], + "f30": ["ft10"], + "f31": ["ft11"], +} +VEC_ALIASES = { + "v0": [], + "v1": [], + "v2": [], + "v3": [], + "v4": [], + "v5": [], + "v6": [], + "v7": [], + "v8": [], + "v9": [], + "v10": [], + "v11": [], + "v12": [], + "v13": [], + "v14": [], + "v15": [], + "v16": [], + "v17": [], + "v18": [], + "v19": [], + "v20": [], + "v21": [], + "v22": [], + "v23": [], + "v24": [], + "v25": [], + "v26": [], + "v27": [], + "v28": [], + "v29": [], + "v30": [], + "v31": [], +} + +CSR_BF_HEADER_FILE_NAME = "CSRBitMasks{reg_size}.hpp" + +CSR_HEADER_FILE_NAME = "CSRNums.hpp" +CSR_NUM_FILE_HEADER = """ +#pragma once + +#include + +// +// This is generated file from scripts/GenRegisterJSON.py +// +// DO NOT MODIFY THIS FILE BY HAND +// + +namespace athena +{ +""" + +CSR_NUM_FILE_CSRNUM_COMMENT = """ + // CSR Nums +""" + +CSR_NUM_FILE_CONSTANT_COMMENT = """ + // Constants defined for RISC-V +""" + +CSR_NUM_FILE_INTERNAL_REGS_COMMENT = """ + // Constants defined for RISC-V +""" + +CSR_NUM_FILE_FOOTER = """ +} +""" + +class GenRegisterJSON(): + """Generates register definition JSON files. + + Args: + group_name (RegisterGroup): Name of register group. + num_regs (int): Number of registers in the group. + reg_size (int): Size of the registers in bytes (must be power of 2) + """ + + def __init__(self, group_num, num_regs, reg_size): + self.group_num = int(group_num) + self.group_name = GetGroupName(group_num) + self.num_regs = num_regs + assert math.log(reg_size, 2).is_integer(), 'reg_size must be a power of 2!' + self.reg_size = reg_size + self.reg_defs = [] + + self.readonly_fields = {} + self.readonly_fields["b31_00"] = {"desc": "read-only", + "low_bit": 0, + "high_bit": 31, + "readonly": True} + self.readonly_fields["b63_00"] = {"desc": "read-only", + "low_bit": 0, + "high_bit": 63, + "readonly": True} + + for num in range(0, self.num_regs): + name = self.get_int_reg_name(num) + if group_num is RegisterGroup.INT: + self.gen_int_reg_defs() + elif group_num is RegisterGroup.FP: + self.gen_fp_reg_defs() + elif group_num is RegisterGroup.CSR: + self.gen_csr_reg_defs() + self.gen_csr_header_file() + self.gen_csr_bit_fields() + elif group_num is RegisterGroup.VEC: + self.gen_vec_reg_defs() + + def get_int_reg_name(self, num): + """Returns int register name from register number""" + return "x"+str(num) + + def get_int_reg_desc(self, num): + """Returns int register description from register number""" + return "int register "+str(num) + + def gen_int_reg_defs(self): + """Generates integer register definitions + """ + for num in range(0, self.num_regs): + name = self.get_int_reg_name(num) + alias = INT_ALIASES[name] if (name in INT_ALIASES.keys()) else [] + fields = {} + if (num == 0): + if (self.reg_size == 4): + fields["b31_00"] = self.readonly_fields["b31_00"] + else: + fields["b63_00"] = self.readonly_fields["b63_00"] + + self.reg_defs.append(self.__CreateRegDict({ + "name": name, + "num": num, + "desc": self.get_int_reg_desc(num), + "size": self.reg_size, + "aliases": alias, + "fields": fields, + "initial_value": 0, + "enabled": True})) + + #Include PC, Athena expects the PC to always be 64-bits + num += 1 + self.reg_defs.append(self.__CreateRegDict({ + "name": "pc", + "num": num, + "desc": "Program counter", + "size": 8, + "aliases": [], + "fields": {}, + "initial_value": 0, + "enabled": True})) + + # Load reservation for LR/SC + num += 1 + self.reg_defs.append(self.__CreateRegDict({ + "name": "resv_addr", + "num": num, + "desc": "Load reservation address", + "size": 8, + "aliases": [], + "fields": {}, + "initial_value": 0, + "enabled": True})) + + num += 1 + self.reg_defs.append(self.__CreateRegDict({ + "name": "resv_valid", + "num": num, + "desc": "Load reservation valid", + "size": 8, + "aliases": [], + "fields": {}, + "initial_value": 0, + "enabled": True})) + + def get_fp_reg_name(self, num): + """Returns fp register name from register number""" + return "f"+str(num) + + def get_fp_reg_desc(self, num): + """Returns fp register description from register number""" + return "floating point register "+str(num) + + def gen_fp_reg_defs(self): + """Generates floating point register definitions + """ + + fields = {} + fields["sp"] = {"desc": "single precision", + "low_bit": 0, + "high_bit": 31, + "readonly": False} + fields["dp"] = {"desc": "double precision", + "low_bit": 0, + "high_bit": 63, + "readonly": False} + + for num in range(0, self.num_regs): + name = self.get_fp_reg_name(num) + alias = FP_ALIASES[name] if (name in FP_ALIASES.keys()) else [] + self.reg_defs.append(self.__CreateRegDict({ + "name": name, + "num": num, + "desc": self.get_fp_reg_desc(num), + "size": 8, + "aliases": alias, + "fields": fields, + "initial_value": 0, + "enabled": True})) + + def gen_csr_reg_defs(self): + """Generate control and status register definitions + """ + CSR_DEFS = CSR32_DEFS if (self.reg_size == 4) else CSR64_DEFS + + for k, v in CSR_DEFS.items(): + self.reg_defs.append(self.__CreateRegDict({ + "name": v[0], + "num": k, + "desc": v[1], + "size": 8, + "aliases": ["csr"+str(k)], + "fields": v[2], + "initial_value": v[3], + "enabled": True})) + + def gen_csr_header_file(self): + """Generate the CSR CPP header file + """ + + csr_header_file = open(CSR_HEADER_FILE_NAME, "w") + csr_header_file.write(CSR_NUM_FILE_HEADER) + + # TODO: Can we get keys in JSON to be printed in hex? + CSR_DEFS = CSR32_DEFS.copy() + CSR_DEFS.update(CSR64_DEFS) + CONST = RV64_CONSTS + + csr_header_file.write(CSR_NUM_FILE_CSRNUM_COMMENT) + csr_largest_value = -1; + for k, v in CSR_DEFS.items(): + csr_header_file.write(" static constexpr uint32_t "+ + (v[0]).upper()+ + " = "+ + str(hex(k))+ + "; // "+ + str(k)+ + "\n") + csr_largest_value = max(csr_largest_value, k) + + csr_header_file.write(" static constexpr uint32_t CSR_LARGEST_VALUE = "+ + str(hex(csr_largest_value)) + + ";\n") + + csr_header_file.write(CSR_NUM_FILE_CONSTANT_COMMENT) + for k, v in CONST.items(): + csr_header_file.write(" static constexpr uint64_t "+ + k.upper()+ + " = "+ + str(hex(v[0]))+ + "; // "+ + str(v[1])+ + "\n") + + csr_header_file.write(CSR_NUM_FILE_INTERNAL_REGS_COMMENT) + for k, v in ATHENA_INTERNAL_REGISTERS.items(): + csr_header_file.write(" static constexpr uint64_t "+ + k.upper()+ + " = "+ + str(hex(v[0]))+ + "; // "+ + str(v[1])+ + "\n") + + csr_header_file.write(CSR_NUM_FILE_FOOTER) + csr_header_file.close() + + def gen_csr_bit_fields(self): + """Generate the CSR header file with bitfields + """ + data_width = self.reg_size*8 + csr_bf_header_file = open(CSR_BF_HEADER_FILE_NAME.format(reg_size=data_width), "w") + csr_bf_header_file.write(CSR_NUM_FILE_HEADER) + + CSR_DEFS = CSR32_DEFS if (self.reg_size == 4) else CSR64_DEFS + FILL_MASK = 0xffffffffffffffff + SIZE = 64 + + for k, v in CSR_DEFS.items(): + # Print only if there are bit flags + if v[2]: + csr_bf_header_file.write(" namespace "+ + (v[0]).upper()+ + "_" + str(data_width) + "_bitmasks {\n") + fields = v[2] + for name in fields: + if name.lower() != "resv" and name.lower() != "wpri": + high_bit = fields[name]['high_bit'] + low_bit = fields[name]['low_bit'] + mask = (FILL_MASK >> (SIZE - (high_bit - low_bit + 1))) << low_bit + csr_bf_header_file.write(" static constexpr uint64_t " + + name + " = " + hex(mask) + ";\n") + + csr_bf_header_file.write(" } // namespace "+ + (v[0]).upper()+ + "_" + str(data_width) + "_bitfield\n\n") + + + + csr_bf_header_file.write(CSR_NUM_FILE_FOOTER) + csr_bf_header_file.close() + + + def get_vec_reg_name(self, num): + """Returns v register name from register number""" + return "v"+str(num) + + def get_vec_reg_desc(self, num): + """Returns v register description from register number""" + return "vector register "+str(num) + + def gen_vec_reg_defs(self): + """Generates vector register definitions + """ + + fields = {} + alias = [] + + for num in range(0, self.num_regs): + name = self.get_vec_reg_name(num) + self.reg_defs.append(self.__CreateRegDict({ + "name": name, + "num": num, + "desc": self.get_vec_reg_desc(num), + "size": self.reg_size, + "aliases": alias, + "fields": fields, + "initial_value": 0, + "enabled": True})) + + def write_json(self, filename): + """Write register definitions to a JSON file""" + with open(filename,"w") as fh: + json.dump(self.reg_defs, fh, indent=4) + + def __CreateRegDict(self, reg_dict): + # Remove the 'fields' key if it is empty + if not reg_dict["fields"]: + del reg_dict["fields"] + + # Format the 'initial_value' to hex, or remove it if it is 0 + if reg_dict["initial_value"]: + reg_dict["initial_value"] = hex(reg_dict["initial_value"]) + else: + del reg_dict["initial_value"] + + # Remove the 'enabled' key if it is True + if reg_dict["enabled"]: + del reg_dict["enabled"] + + # Add group num and group name + reg_dict["group_num"] = self.group_num + reg_dict["group_name"] = self.group_name + + return reg_dict + +def main(): + + if len(sys.argv) != 2: + print("Usage: GenRegisterJSON.py ") + exit(1) + + isa_string = sys.argv[1] + + if isa_string not in ["rv32", "rv64"]: + print("Usage: GenRegisterJSON.py ") + exit(1) + + # Default to RV64 + xlen = 4 if "32" in isa_string else 8 + vlen = 32 + + # Generate rv64g int, fp and CSR registers + reg_int = GenRegisterJSON(RegisterGroup.INT, 32, xlen); + reg_fp = GenRegisterJSON(RegisterGroup.FP, 32, xlen); + reg_vec = GenRegisterJSON(RegisterGroup.VEC, 32, vlen) + reg_csr = GenRegisterJSON(RegisterGroup.CSR, 0, xlen); + + isa_string = ''.join(i for i in isa_string if not i.isdigit()) + reg_int.write_json("reg_int.json"); + reg_fp.write_json("reg_fp.json"); + reg_vec.write_json("reg_vec.json") + reg_csr.write_json("reg_csr.json"); + +if __name__ == "__main__": + main() diff --git a/sparta/sparta/functional/RegisterDefnsJSON.hpp b/sparta/sparta/functional/RegisterDefnsJSON.hpp new file mode 100644 index 0000000000..b87c12b89e --- /dev/null +++ b/sparta/sparta/functional/RegisterDefnsJSON.hpp @@ -0,0 +1,263 @@ +#pragma once + +#include +#include +#include +#include + +namespace sparta +{ + +struct RegisterDefn +{ + struct FieldDefn + { + std::string name; + std::string desc; + RegisterBase::size_type low_bit; + RegisterBase::size_type high_bit; + bool readonly; + }; + + RegisterBase::ident_type id; + std::string name; + RegisterBase::group_num_type group_num; + std::string group; + RegisterBase::group_idx_type group_idx; + std::string desc; + RegisterBase::size_type bytes; + std::vector fields; + std::vector bank_membership; + std::vector aliases; + RegisterBase::ident_type subset_of = RegisterBase::INVALID_ID; + RegisterBase::size_type subset_offset = RegisterBase::INVALID_ID; + uint64_t initial_value = 0; + RegisterBase::Definition::HintsT hints = 0; + RegisterBase::Definition::RegDomainT regdomain = 0; +}; + +class RegisterDefnsFromJSON +{ +public: + RegisterDefnsFromJSON(const std::vector& register_defns_json_filenames) + { + for (const auto& filename : register_defns_json_filenames) { + parse(filename); + } + + // Add a definition that indicates the end of the array + register_defns_.push_back(RegisterBase::DEFINITION_END); + } + + RegisterDefnsFromJSON(const std::string& register_defns_json_filename) + { + parse(register_defns_json_filename); + + // Add a definition that indicates the end of the array + register_defns_.push_back(RegisterBase::DEFINITION_END); + } + + RegisterBase::Definition* getAllDefns() { + return register_defns_.data(); + } + +private: + void parse(const std::string& register_defns_json_filename) + { + // Read the file into a string + std::ifstream ifs(register_defns_json_filename); + std::stringstream ss; + ss << ifs.rdbuf(); + std::string json_str = ss.str(); + + // Parse the JSON string + rapidjson::Document document; + document.Parse(json_str.c_str()); + + for (auto& item : document.GetArray()) { + if (item.HasMember("enabled") && !item["enabled"].GetBool()) { + continue; + } + + const RegisterBase::ident_type id = item["num"].GetInt(); + + cached_strings_.emplace_back(item["name"].GetString()); + const char *name = cached_strings_.back().raw(); + + const RegisterBase::group_num_type group_num = item["group_num"].GetInt(); + auto iter = group_idx_map_.find(group_num); + if (iter == group_idx_map_.end()) { + group_idx_map_[group_num] = 0; + } + + RegisterBase::group_idx_type group_idx = group_idx_map_[group_num]++; + cached_strings_.emplace_back(item["group_name"].GetString()); + const char* group = cached_strings_.back().raw(); + + if (std::string(group).empty()) { + group_idx = RegisterBase::GROUP_IDX_NONE; + } + + cached_strings_.emplace_back(item["desc"].GetString()); + const char* desc = cached_strings_.back().raw(); + + const RegisterBase::size_type bytes = item["size"].GetInt(); + + std::vector field_defns; + if (item.HasMember("fields")) { + for (auto it = item["fields"].MemberBegin(); it != item["fields"].MemberEnd(); ++it) { + const char* field_name = it->name.GetString(); + const rapidjson::Value& field_info = it->value; + cached_field_defns_.emplace_back(field_name, field_info); + field_defns.push_back(cached_field_defns_.back().getDefn()); + } + } + + static const std::vector bank_membership; + + std::vector alias_strings; + for (auto& alias : item["aliases"].GetArray()) { + alias_strings.push_back(alias.GetString()); + } + cached_aliases_.emplace_back(alias_strings); + const char** aliases = cached_aliases_.back().raw(); + + constexpr RegisterBase::ident_type subset_of = RegisterBase::INVALID_ID; + constexpr RegisterBase::size_type subset_offset = 0; + + const unsigned char *initial_value = nullptr; + if (item.HasMember("initial_value")) { + cached_initial_values_.emplace_back(item["initial_value"].GetString()); + initial_value = cached_initial_values_.back().raw(); + } + + constexpr RegisterBase::Definition::HintsT hints = 0; + constexpr RegisterBase::Definition::RegDomainT regdomain = 0; + + RegisterBase::Definition defn = { + id, + name, + group_num, + group, + group_idx, + desc, + bytes, + field_defns, + bank_membership, + aliases, + subset_of, + subset_offset, + initial_value, + hints, + regdomain + }; + + register_defns_.push_back(defn); + } + } + + // Converts a string to a const char* pointer + class StringRef + { + public: + StringRef(const std::string& str) : storage_(str) {} + const char* raw() const { return storage_.c_str(); } + private: + std::string storage_; + }; + + // Converts a vector of strings to an array of const char* pointers + class AliasRef + { + public: + AliasRef(const std::vector& aliases) + : storage_(aliases) + { + for (const auto& str : storage_) { + pointers_.push_back(str.c_str()); + } + } + + const char** raw() { + return pointers_.data(); + } + + private: + std::vector storage_; + std::vector pointers_; + }; + + // Converts any hex ("0xdeafbeef") to a const unsigned char* pointer + class InitialValueRef + { + public: + InitialValueRef(const std::string& hex_str) + { + // Remove the "0x" prefix if present + std::string hex = hex_str; + if (hex.substr(0, 2) == "0x") { + hex = hex.substr(2); + } + + // Ensure the hex string has an even length + sparta_assert(hex.length() % 2 == 0, "Hex string must have an even length"); + + // Create a vector to hold the bytes + hex_bytes_.resize(hex.length() / 2); + + // Convert hex string to bytes + for (size_t i = 0; i < hex.length(); i += 2) { + const auto byte_string = hex.substr(i, 2); + std::istringstream iss(byte_string); + int byte; + iss >> std::hex >> byte; + hex_bytes_[i / 2] = static_cast(byte); + } + } + + const unsigned char* raw() const { + return hex_bytes_.data(); + } + + private: + std::vector hex_bytes_; + }; + + // Converts a rapidjson::Value that represents a field to a Field::Definition + class FieldDefnConverter + { + public: + FieldDefnConverter(const std::string& field_name, const rapidjson::Value& field_info) + : field_name_(field_name) + , desc_(field_info["desc"].GetString()) + , field_defn_(field_name_.c_str(), + desc_.c_str(), + field_info["low_bit"].GetInt(), + field_info["high_bit"].GetInt(), + field_info["readonly"].GetBool()) + { + } + + const RegisterBase::Field::Definition& getDefn() const + { + return field_defn_; + } + + private: + std::string field_name_; + std::string desc_; + RegisterBase::Field::Definition field_defn_; + }; + + std::deque cached_strings_; + std::deque cached_aliases_; + std::deque cached_initial_values_; + std::deque cached_field_defns_; + std::vector register_defns_; + + // TODO: Find the official way to handle group_idx. For now we will just use + // a map of auto-incrementing group_idx values for each group_num + std::map group_idx_map_; +}; + +} // namespace sparta diff --git a/sparta/sparta/functional/RegisterSet.hpp b/sparta/sparta/functional/RegisterSet.hpp index 7e8044b8bf..659c2c8551 100644 --- a/sparta/sparta/functional/RegisterSet.hpp +++ b/sparta/sparta/functional/RegisterSet.hpp @@ -14,6 +14,7 @@ #include "sparta/functional/Register.hpp" #include "sparta/functional/RegisterBankTable.hpp" +#include "sparta/functional/RegisterDefnsJSON.hpp" #include "sparta/simulation/TreeNode.hpp" #include "sparta/utils/SpartaException.hpp" #include "sparta/utils/SpartaAssert.hpp" @@ -536,6 +537,26 @@ class RegisterSet : public TreeNode // Handled in delegated consturctor } + template + RegisterSet(TreeNode *parent, + std::unique_ptr defns, + const RegisterProxyBase::Definition *proxy_defs, + CurrentBankFunction cbfxn, + RegisterTypeTag tag) + : RegisterSet(parent, defns->getAllDefns(), proxy_defs, cbfxn, tag) + { + defs_from_json_ = std::move(defns); + } + + template + RegisterSet(TreeNode *parent, + std::unique_ptr defns, + RegisterTypeTag tag) + : RegisterSet(parent, defns->getAllDefns(), nullptr, nullptr, tag) + { + defs_from_json_ = std::move(defns); + } + template static std::unique_ptr create(TreeNode *parent, @@ -555,6 +576,52 @@ class RegisterSet : public TreeNode parent, defs, RegisterTypeTag())); } + template + static std::unique_ptr + create(TreeNode *parent, + const std::string& register_defns_json, + const RegisterProxyBase::Definition *proxy_defs, + CurrentBankFunction cbfxn) + { + auto defns = std::make_unique(register_defns_json); + + return std::unique_ptr(new RegisterSet( + parent, std::move(defns), proxy_defs, cbfxn, RegisterTypeTag())); + } + + template + static std::unique_ptr + create(TreeNode *parent, const std::string& register_defns_json) + { + auto defns = std::make_unique(register_defns_json); + + return std::unique_ptr(new RegisterSet( + parent, std::move(defns), RegisterTypeTag())); + } + + template + static std::unique_ptr + create(TreeNode *parent, + const std::vector& register_defns_jsons, + const RegisterProxyBase::Definition *proxy_defs, + CurrentBankFunction cbfxn) + { + auto defns = std::make_unique(register_defns_jsons); + + return std::unique_ptr(new RegisterSet( + parent, std::move(defns), proxy_defs, cbfxn, RegisterTypeTag())); + } + + template + static std::unique_ptr + create(TreeNode *parent, const std::vector& register_defns_jsons) + { + auto defns = std::make_unique(register_defns_jsons); + + return std::unique_ptr(new RegisterSet( + parent, std::move(defns), RegisterTypeTag())); + } + /*! * \brief Destructor * @@ -1032,6 +1099,14 @@ class RegisterSet : public TreeNode */ CurrentBankFunction cur_bank_fxn_; + /*! + * \brief Register definitions parsed from JSON file(s). We have to hold onto + * this to keep the definitions alive, specifically the various strings that + * are held by the register/field definitions as a const char* (e.g. group + * name, field name, etc.) + */ + std::unique_ptr defs_from_json_; + }; // class RegisterSet /*! diff --git a/sparta/test/Register/Register_test.cpp b/sparta/test/Register/Register_test.cpp index 276cd13533..7d53b3ff8d 100644 --- a/sparta/test/Register/Register_test.cpp +++ b/sparta/test/Register/Register_test.cpp @@ -254,8 +254,66 @@ class CallbackDummy } }; +void dumpRegisterDefnsToJSON(const std::string& filename, Register::Definition* defs) +{ + std::ofstream json_file(filename); + if (!json_file.is_open()) { + throw SpartaException("Failed to open file for writing: ") << filename; + } + + json_file << "[\n"; + for (RegisterBase::Definition* def = defs; def->name != nullptr; ++def) { + json_file << " {\n"; + json_file << " \"name\": \"" << def->name << "\",\n"; + json_file << " \"num\": " << def->id << ",\n"; + json_file << " \"desc\": \"" << def->desc << "\",\n"; + json_file << " \"size\": " << def->bytes << ",\n"; + + json_file << " \"aliases\": ["; + if (def->aliases == nullptr) { + json_file << "],\n"; + } else { + json_file << "\n"; + for (auto alias = def->aliases; alias != nullptr; ++alias) { + json_file << " \"" << *alias << "\""; + if (*(alias + 1) != nullptr) { + json_file << ","; + } + json_file << "\n"; + } + json_file << " ],\n"; + } + + json_file << " \"fields\": {\n"; + for (const auto& field : def->fields) { + json_file << " \"" << field.name << "\": {\n"; + json_file << " \"desc\": \"" << field.desc << "\",\n"; + json_file << " \"low_bit\": " << field.low_bit << ",\n"; + json_file << " \"high_bit\": " << field.high_bit << ",\n"; + json_file << " \"readonly\": " << std::boolalpha << field.read_only << "\n"; + json_file << " }"; + if (&field != &def->fields.back()) { + json_file << ","; + } + json_file << "\n"; + } + json_file << " },\n"; + + json_file << " \"group_name\": \"" << def->group << "\",\n"; + json_file << " \"group_num\": " << def->group_num << "\n"; + + json_file << " }"; + if ((def + 1)->name != nullptr) { + json_file << ","; + } + json_file << "\n"; + } + + json_file << "]\n"; +} + //! Issue 89, test field register writes to large fields -void testFieldRegisterWrite() +void testFieldRegisterWrite(bool use_json = false) { Register::ident_type new_regid = 1000; // Start counting at some unique ID @@ -267,7 +325,13 @@ void testFieldRegisterWrite() }; RootTreeNode root; DummyDevice good_dummy(&root); - std::unique_ptr regs(RegisterSet::create(&good_dummy, good_reg_defs)); + std::unique_ptr regs; + if (!use_json) { + regs = RegisterSet::create(&good_dummy, good_reg_defs); + } else { + dumpRegisterDefnsToJSON("reg_defs.json", good_reg_defs); + regs = RegisterSet::create(&good_dummy, "reg_defs.json"); + } regs->getRegister("fp_reg")->getField("sp")->write(1); regs->getRegister("fp_reg")->getField("dp")->write(1); @@ -280,12 +344,14 @@ void testFieldRegisterWrite() EXPECT_EQUAL(regs->getRegister("fp_reg")->getField("sp")->read(), 0xffffffff); EXPECT_EQUAL(regs->getRegister("fp_reg")->getField("dp")->read(), 0xffffffffffffffff); + std::cout << regs->getRegister("fp_reg")->getGroupName() << std::endl; + root.enterTeardown(); } //! Load up some good regs from a table -void testGoodRegs() +void testGoodRegs(bool use_json = false) { Register::ident_type new_regid = 1000; // Start counting at some unique ID @@ -304,7 +370,13 @@ void testGoodRegs() RootTreeNode root; DummyDevice good_dummy(&root); - std::unique_ptr good_regs(RegisterSet::create(&good_dummy, good_reg_defs)); + std::unique_ptr good_regs; + if (!use_json) { + good_regs = RegisterSet::create(&good_dummy, good_reg_defs); + } else { + dumpRegisterDefnsToJSON("reg_defs.json", good_reg_defs); + good_regs = RegisterSet::create(&good_dummy, "reg_defs.json"); + } #ifndef REGISTER_SET_GET_ARCH_DATA_REMOVED EXPECT_TRUE(good_regs->getArchData().isLaidOut()); std::cout << "Layout of good dummy regs:" << std::endl; @@ -759,8 +831,11 @@ int main() root.bindTreeLate(); // Construct some good and bad regs to test out size constraints - testFieldRegisterWrite(); - testGoodRegs(); + //testRegisterSetFromJSON(); + testFieldRegisterWrite(); // Create registers directly + testFieldRegisterWrite(true); // Create registers from JSON + testGoodRegs(); // Create registers directly + testGoodRegs(true); // Create registers from JSON testBadRegs(); From 3582375015dc35c306f72a0a4c53d9bf5adfa2e2 Mon Sep 17 00:00:00 2001 From: colby-nyce Date: Fri, 13 Sep 2024 10:57:26 -0500 Subject: [PATCH 3/6] Create registers from JSON definition --- .../sparta/functional/RegisterDefnsJSON.hpp | 28 ------------------- 1 file changed, 28 deletions(-) diff --git a/sparta/sparta/functional/RegisterDefnsJSON.hpp b/sparta/sparta/functional/RegisterDefnsJSON.hpp index b87c12b89e..6428b42e66 100644 --- a/sparta/sparta/functional/RegisterDefnsJSON.hpp +++ b/sparta/sparta/functional/RegisterDefnsJSON.hpp @@ -8,34 +8,6 @@ namespace sparta { -struct RegisterDefn -{ - struct FieldDefn - { - std::string name; - std::string desc; - RegisterBase::size_type low_bit; - RegisterBase::size_type high_bit; - bool readonly; - }; - - RegisterBase::ident_type id; - std::string name; - RegisterBase::group_num_type group_num; - std::string group; - RegisterBase::group_idx_type group_idx; - std::string desc; - RegisterBase::size_type bytes; - std::vector fields; - std::vector bank_membership; - std::vector aliases; - RegisterBase::ident_type subset_of = RegisterBase::INVALID_ID; - RegisterBase::size_type subset_offset = RegisterBase::INVALID_ID; - uint64_t initial_value = 0; - RegisterBase::Definition::HintsT hints = 0; - RegisterBase::Definition::RegDomainT regdomain = 0; -}; - class RegisterDefnsFromJSON { public: From 9a1364a4e1ea1640bee112dcbc82e42a9e5143ea Mon Sep 17 00:00:00 2001 From: colby-nyce Date: Fri, 13 Sep 2024 11:02:57 -0500 Subject: [PATCH 4/6] Create registers from JSON definition --- sparta/test/Register/Register_test.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/sparta/test/Register/Register_test.cpp b/sparta/test/Register/Register_test.cpp index 7d53b3ff8d..6be05b030e 100644 --- a/sparta/test/Register/Register_test.cpp +++ b/sparta/test/Register/Register_test.cpp @@ -344,8 +344,6 @@ void testFieldRegisterWrite(bool use_json = false) EXPECT_EQUAL(regs->getRegister("fp_reg")->getField("sp")->read(), 0xffffffff); EXPECT_EQUAL(regs->getRegister("fp_reg")->getField("dp")->read(), 0xffffffffffffffff); - std::cout << regs->getRegister("fp_reg")->getGroupName() << std::endl; - root.enterTeardown(); } From c4c4b1bb2d9d867a667eb509e52f83321b0e67e7 Mon Sep 17 00:00:00 2001 From: colby-nyce Date: Fri, 13 Sep 2024 11:03:37 -0500 Subject: [PATCH 5/6] Create registers from JSON definition --- sparta/test/Register/Register_test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/sparta/test/Register/Register_test.cpp b/sparta/test/Register/Register_test.cpp index 6be05b030e..c23ac5fc8a 100644 --- a/sparta/test/Register/Register_test.cpp +++ b/sparta/test/Register/Register_test.cpp @@ -829,7 +829,6 @@ int main() root.bindTreeLate(); // Construct some good and bad regs to test out size constraints - //testRegisterSetFromJSON(); testFieldRegisterWrite(); // Create registers directly testFieldRegisterWrite(true); // Create registers from JSON testGoodRegs(); // Create registers directly From d659aa377acf879b3d0e741b5e37e5f8ad4c7f4d Mon Sep 17 00:00:00 2001 From: colby-nyce Date: Fri, 13 Sep 2024 11:09:37 -0500 Subject: [PATCH 6/6] Remove unnecessary registers from JSON --- sparta/scripts/GenRegisterJSON.py | 35 ------------------------------- 1 file changed, 35 deletions(-) diff --git a/sparta/scripts/GenRegisterJSON.py b/sparta/scripts/GenRegisterJSON.py index 45e8c8839d..bcaed23b33 100755 --- a/sparta/scripts/GenRegisterJSON.py +++ b/sparta/scripts/GenRegisterJSON.py @@ -240,41 +240,6 @@ def gen_int_reg_defs(self): "initial_value": 0, "enabled": True})) - #Include PC, Athena expects the PC to always be 64-bits - num += 1 - self.reg_defs.append(self.__CreateRegDict({ - "name": "pc", - "num": num, - "desc": "Program counter", - "size": 8, - "aliases": [], - "fields": {}, - "initial_value": 0, - "enabled": True})) - - # Load reservation for LR/SC - num += 1 - self.reg_defs.append(self.__CreateRegDict({ - "name": "resv_addr", - "num": num, - "desc": "Load reservation address", - "size": 8, - "aliases": [], - "fields": {}, - "initial_value": 0, - "enabled": True})) - - num += 1 - self.reg_defs.append(self.__CreateRegDict({ - "name": "resv_valid", - "num": num, - "desc": "Load reservation valid", - "size": 8, - "aliases": [], - "fields": {}, - "initial_value": 0, - "enabled": True})) - def get_fp_reg_name(self, num): """Returns fp register name from register number""" return "f"+str(num)