Merge branch 'main' into mpact-deps-repo
diff --git a/external/BUILD.antlr4 b/external/BUILD.antlr4
index 994787d..920a68d 100644
--- a/external/BUILD.antlr4
+++ b/external/BUILD.antlr4
@@ -27,6 +27,9 @@
cache_entries = {
"CMAKE_CXX_FLAGS": "-std=c++17",
},
+ generate_args = [
+ "-G Ninja",
+ ],
lib_source = ":all_srcs",
tags = ["requires-network"],
out_static_libs = ["libantlr4-runtime.a"],
diff --git a/mpact/sim/decoder/bin_decoder.cc b/mpact/sim/decoder/bin_decoder.cc
index 04baa27..3eb1786 100644
--- a/mpact/sim/decoder/bin_decoder.cc
+++ b/mpact/sim/decoder/bin_decoder.cc
@@ -17,7 +17,6 @@
#include <string>
#include "mpact/sim/decoder/bin_encoding_info.h"
-#include "mpact/sim/decoder/instruction_encoding.h"
#include "mpact/sim/decoder/instruction_group.h"
namespace mpact {
@@ -38,7 +37,7 @@
instruction_group_vec_.clear();
}
-void BinDecoder::SelectInstructionGroupForDecoder(InstructionGroup *group) {
+void BinDecoder::AddInstructionGroup(InstructionGroup *group) {
instruction_group_vec_.push_back(group);
}
diff --git a/mpact/sim/decoder/bin_decoder.h b/mpact/sim/decoder/bin_decoder.h
index 285b8ff..e096ab8 100644
--- a/mpact/sim/decoder/bin_decoder.h
+++ b/mpact/sim/decoder/bin_decoder.h
@@ -46,7 +46,7 @@
// Checks for invalid encodings, such as some duplicates.
void CheckEncodings();
// Select instruction group for decoder generation.
- void SelectInstructionGroupForDecoder(InstructionGroup *group);
+ void AddInstructionGroup(InstructionGroup *group);
// Accessors.
const std::string &name() const { return name_; }
diff --git a/mpact/sim/decoder/bin_encoding_info.cc b/mpact/sim/decoder/bin_encoding_info.cc
index 435f447..ab700f1 100644
--- a/mpact/sim/decoder/bin_encoding_info.cc
+++ b/mpact/sim/decoder/bin_encoding_info.cc
@@ -33,7 +33,13 @@
DecoderErrorListener *error_listener)
: opcode_enum_(opcode_enum), error_listener_(error_listener) {}
-BinEncodingInfo::~BinEncodingInfo() { delete decoder_; }
+BinEncodingInfo::~BinEncodingInfo() {
+ delete decoder_;
+ for (auto &[unused, format_ptr] : format_map_) {
+ delete format_ptr;
+ }
+ format_map_.clear();
+}
// Add name of an include file to be included in the generated code.
void BinEncodingInfo::AddIncludeFile(std::string include_file) {
@@ -45,7 +51,7 @@
int width) {
// Verify that the format name hasn't been used.
if (format_map_.contains(name)) {
- return absl::InternalError(
+ return absl::AlreadyExistsError(
absl::StrCat("Error: format '", name, "' already defined"));
}
auto format = new Format(name, width, this);
@@ -58,7 +64,7 @@
std::string parent_name) {
// Verify that the format name hasn't been used.
if (format_map_.contains(name)) {
- return absl::InternalError(
+ return absl::AlreadyExistsError(
absl::StrCat("Error: format '", name, "' already defined"));
}
auto format = new Format(name, width, parent_name, this);
@@ -78,7 +84,7 @@
absl::StatusOr<InstructionGroup *> BinEncodingInfo::AddInstructionGroup(
std::string name, int width, std::string format_name) {
if (instruction_group_map_.contains(name)) {
- return absl::InternalError(
+ return absl::AlreadyExistsError(
absl::StrCat("Error: instruction group '", name, "' already defined"));
}
auto group =
@@ -89,7 +95,7 @@
// Top level method that calls the checking method of each format. This is
// called after all the formats have been added.
-absl::Status BinEncodingInfo::CheckFormats() {
+void BinEncodingInfo::PropagateExtractors() {
for (auto &[unused, format] : format_map_) {
// For the base formats (those who do not inherit from another format).
if (format->base_format() == nullptr) {
@@ -102,7 +108,6 @@
format->PropagateExtractorsDown();
}
}
- return absl::OkStatus();
}
BinDecoder *BinEncodingInfo::AddBinDecoder(std::string name) {
diff --git a/mpact/sim/decoder/bin_encoding_info.h b/mpact/sim/decoder/bin_encoding_info.h
index ee3cdff..6e3cee2 100644
--- a/mpact/sim/decoder/bin_encoding_info.h
+++ b/mpact/sim/decoder/bin_encoding_info.h
@@ -16,7 +16,6 @@
#define MPACT_SIM_DECODER_BIN_ENCODING_INFO_H_
#include <string>
-#include <vector>
#include "absl/container/btree_map.h"
#include "absl/container/btree_set.h"
@@ -59,10 +58,8 @@
absl::StatusOr<InstructionGroup *> AddInstructionGroup(
std::string name, int width, std::string format_name);
- // Performs a consistency check on the formats and handles any out of order
- // declarations - there is no rule that a format has to be declared before it
- // is used.
- absl::Status CheckFormats();
+ // Propagates bitfield extractors where possible.
+ void PropagateExtractors();
// Create and add binary decoder descriptor.
BinDecoder *AddBinDecoder(std::string name);
diff --git a/mpact/sim/decoder/bin_format_gen_main.cc b/mpact/sim/decoder/bin_format_gen_main.cc
index 3333d2e..30494fc 100644
--- a/mpact/sim/decoder/bin_format_gen_main.cc
+++ b/mpact/sim/decoder/bin_format_gen_main.cc
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <fstream>
#include <iostream>
#include <ostream>
#include <string>
@@ -39,11 +38,10 @@
int main(int argc, char **argv) {
auto arg_vec = absl::ParseCommandLine(argc, argv);
- std::string file_name;
+ std::vector<std::string> file_names;
- // Open input file as stream if specified.
- if (arg_vec.size() > 1) {
- file_name = arg_vec[1];
+ for (int i = 1; i < arg_vec.size(); ++i) {
+ file_names.push_back(arg_vec[i]);
}
::mpact::sim::decoder::bin_format::BinFormatVisitor visitor;
@@ -61,7 +59,7 @@
exit(-1);
}
- auto status = visitor.Process(file_name, decoder_name, prefix, include_roots,
+ auto status = visitor.Process(file_names, decoder_name, prefix, include_roots,
output_dir);
if (!status.ok()) {
LOG(ERROR) << status.message();
diff --git a/mpact/sim/decoder/bin_format_visitor.cc b/mpact/sim/decoder/bin_format_visitor.cc
index c3fff36..468542c 100644
--- a/mpact/sim/decoder/bin_format_visitor.cc
+++ b/mpact/sim/decoder/bin_format_visitor.cc
@@ -27,7 +27,7 @@
#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
-#include "absl/strings/str_split.h"
+#include "antlr4-runtime/ParserRuleContext.h"
#include "mpact/sim/decoder/bin_decoder.h"
#include "mpact/sim/decoder/bin_encoding_info.h"
#include "mpact/sim/decoder/bin_format_contexts.h"
@@ -93,7 +93,7 @@
using ::mpact::sim::machine_description::instruction_set::ToHeaderGuard;
absl::Status BinFormatVisitor::Process(
- const std::string &file_name, const std::string &decoder_name,
+ const std::vector<std::string> &file_names, const std::string &decoder_name,
absl::string_view prefix, const std::vector<std::string> &include_roots,
absl::string_view directory) {
decoder_name_ = decoder_name;
@@ -105,22 +105,22 @@
std::istream *source_stream = &std::cin;
- if (!file_name.empty()) {
- source_stream = new std::fstream(file_name, std::fstream::in);
+ if (!file_names.empty()) {
+ source_stream = new std::fstream(file_names[0], std::fstream::in);
}
// Create an antlr4 stream from the input stream.
BinFmtAntlrParserWrapper parser_wrapper(source_stream);
// Create and add the error listener.
set_error_listener(std::make_unique<decoder::DecoderErrorListener>());
- error_listener_->set_file_name(file_name);
+ error_listener_->set_file_name(file_names[0]);
parser_wrapper.parser()->removeErrorListeners();
parser_wrapper.parser()->addErrorListener(error_listener());
// Parse the file and then create the data structures.
TopLevelCtx *top_level = parser_wrapper.parser()->top_level();
(void)top_level;
- if (!file_name.empty()) {
+ if (!file_names.empty()) {
delete source_stream;
source_stream = nullptr;
}
@@ -129,7 +129,13 @@
return absl::InternalError("Errors encountered - terminating.");
}
// Visit the parse tree starting at the namespaces declaration.
- auto encoding_info = VisitTopLevel(top_level, decoder_name);
+ VisitTopLevel(top_level);
+ // Process additional source files.
+ for (int i = 1; i < file_names.size(); ++i) {
+ ParseIncludeFile(top_level, file_names[i], {});
+ }
+ // Process the parse tree.
+ auto encoding_info = ProcessTopLevel(decoder_name);
// Include files may generate additional syntax errors.
if (encoding_info == nullptr) {
LOG(ERROR) << "No encoding specified";
@@ -295,11 +301,14 @@
return ret_val;
}
-std::unique_ptr<BinEncodingInfo> BinFormatVisitor::VisitTopLevel(
- TopLevelCtx *ctx, const std::string &decoder_name) {
+void BinFormatVisitor::VisitTopLevel(TopLevelCtx *ctx) {
PreProcessDeclarations(ctx->declaration_list());
- // At this point we have all the formats, instruction groups and decoders.
- // First make sure that the named decoder exists.
+}
+
+std::unique_ptr<BinEncodingInfo> BinFormatVisitor::ProcessTopLevel(
+ const std::string &decoder_name) {
+ // At this point we have the contexts for all slots, bundles and isas.
+ // First make sure the named isa (decoder) has been defined.
auto decoder_iter = decoder_decl_map_.find(decoder_name);
if (decoder_iter == decoder_decl_map_.end()) {
error_listener()->semanticError(
@@ -308,11 +317,7 @@
}
// Visit the decoder.
auto bin_encoding_info = VisitDecoderDef(decoder_iter->second);
- auto status = bin_encoding_info->CheckFormats();
- if (!status.ok()) {
- error_listener_->semanticError(nullptr, status.message());
- return nullptr;
- }
+ bin_encoding_info->PropagateExtractors();
return bin_encoding_info;
}
@@ -386,20 +391,30 @@
return;
}
}
+ ParseIncludeFile(ctx, file_name, include_dir_vec_);
+}
+
+void BinFormatVisitor::ParseIncludeFile(antlr4::ParserRuleContext *ctx,
+ const std::string &file_name,
+ std::vector<std::string> const &dirs) {
std::fstream include_file;
// Open include file.
- for (auto const &dir : include_dir_vec_) {
- std::string include_name = absl::StrCat(dir, "/", file_name);
- include_file.open(include_name, std::fstream::in);
- if (include_file.is_open()) break;
- }
+ include_file.open(file_name, std::fstream::in);
if (!include_file.is_open()) {
- // Try a local file.
- include_file.open(file_name, std::fstream::in);
+ // Try each of the include file directories.
+ for (auto const &dir : dirs) {
+ std::string include_name = absl::StrCat(dir, "/", file_name);
+ include_file.open(include_name, std::fstream::in);
+ if (include_file.is_open()) break;
+ }
if (!include_file.is_open()) {
- error_listener()->semanticError(
- ctx->start, absl::StrCat("Failed to open '", file_name, "'"));
- return;
+ // Try a local file.
+ include_file.open(file_name, std::fstream::in);
+ if (!include_file.is_open()) {
+ error_listener()->semanticError(
+ ctx->start, absl::StrCat("Failed to open '", file_name, "'"));
+ return;
+ }
}
}
std::string previous_file_name = error_listener()->file_name();
@@ -411,6 +426,7 @@
// Add the error listener.
include_parser->parser()->removeErrorListeners();
include_parser->parser()->addErrorListener(error_listener());
+ // Start parsing at the declaratition_list_w_eof rule.
DeclarationListCtx *declaration_list =
include_parser->parser()->declaration_list_w_eof()->declaration_list();
include_file.close();
@@ -788,7 +804,7 @@
continue;
}
group_name_set.insert(group_name);
- decoder->SelectInstructionGroupForDecoder(inst_group);
+ decoder->AddInstructionGroup(inst_group);
continue;
}
@@ -868,7 +884,7 @@
}
}
group_name_set.insert(parent_group->name());
- decoder->SelectInstructionGroupForDecoder(parent_group);
+ decoder->AddInstructionGroup(parent_group);
continue;
}
}
diff --git a/mpact/sim/decoder/bin_format_visitor.h b/mpact/sim/decoder/bin_format_visitor.h
index 87d4bff..b2638bc 100644
--- a/mpact/sim/decoder/bin_format_visitor.h
+++ b/mpact/sim/decoder/bin_format_visitor.h
@@ -16,7 +16,6 @@
#define MPACT_SIM_DECODER_BIN_FORMAT_VISITOR_H_
#include <deque>
-#include <list>
#include <memory>
#include <string>
#include <utility>
@@ -24,6 +23,7 @@
#include "absl/container/flat_hash_map.h"
#include "absl/status/status.h"
+#include "antlr4-runtime/ParserRuleContext.h"
#include "mpact/sim/decoder/BinFormatLexer.h"
#include "mpact/sim/decoder/antlr_parser_wrapper.h"
#include "mpact/sim/decoder/bin_encoding_info.h"
@@ -67,7 +67,7 @@
// Entry point for processing a source_stream input, generating any output
// files in the given directory. Returns OK if no errors were encountered.
- absl::Status Process(const std::string &file_name,
+ absl::Status Process(const std::vector<std::string> &file_names,
const std::string &decoder_name,
absl::string_view prefix,
const std::vector<std::string> &include_roots,
@@ -88,14 +88,18 @@
BitRange GetBitIndexRange(BitIndexRangeCtx *ctx);
int ConvertToInt(NumberCtx *ctx);
// Methods that visit the nodes of the parse tree.
- std::unique_ptr<BinEncodingInfo> VisitTopLevel(
- TopLevelCtx *ctx, const std::string &decoder_name);
+ void VisitTopLevel(TopLevelCtx *ctx);
+ std::unique_ptr<BinEncodingInfo> ProcessTopLevel(
+ const std::string &decoder_name);
void PreProcessDeclarations(DeclarationListCtx *ctx);
void VisitDeclarations(DeclarationListCtx *ctx,
BinEncodingInfo *encoding_info);
void VisitFormatDef(FormatDefCtx *ctx, BinEncodingInfo *encoding_info);
void VisitFieldDef(FieldDefCtx *ctx, Format *format);
void VisitIncludeFile(IncludeFileCtx *ctx);
+ void ParseIncludeFile(antlr4::ParserRuleContext *ctx,
+ const std::string &file_name,
+ const std::vector<std::string> &dirs);
void VisitOverlayDef(OverlayDefCtx *ctx, Format *format);
void VisitOverlayBitField(BitFieldCtx *ctx, Overlay *overlay);
InstructionGroup *VisitInstructionGroupDef(InstructionGroupDefCtx *ctx,
diff --git a/mpact/sim/decoder/decoder_error_listener.cc b/mpact/sim/decoder/decoder_error_listener.cc
index 0bf5c2b..9a6e4c1 100644
--- a/mpact/sim/decoder/decoder_error_listener.cc
+++ b/mpact/sim/decoder/decoder_error_listener.cc
@@ -67,16 +67,21 @@
bool exact,
const antlrcpp::BitSet &ambigAlts,
antlr4::atn::ATNConfigSet *configs) {
+ // Empty.
}
void DecoderErrorListener::reportAttemptingFullContext(
antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex,
size_t stopIndex, const antlrcpp::BitSet &conflictingAlts,
- antlr4::atn::ATNConfigSet *configs) {}
+ antlr4::atn::ATNConfigSet *configs) {
+ // Empty.
+}
void DecoderErrorListener::reportContextSensitivity(
antlr4::Parser *recognizer, const antlr4::dfa::DFA &dfa, size_t startIndex,
- size_t stopIndex, size_t prediction, antlr4::atn::ATNConfigSet *configs) {}
+ size_t stopIndex, size_t prediction, antlr4::atn::ATNConfigSet *configs) {
+ // Empty.
+}
} // namespace decoder
} // namespace sim
diff --git a/mpact/sim/decoder/decoder_gen_main.cc b/mpact/sim/decoder/decoder_gen_main.cc
index cdc7842..fe4588f 100644
--- a/mpact/sim/decoder/decoder_gen_main.cc
+++ b/mpact/sim/decoder/decoder_gen_main.cc
@@ -12,7 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#include <fstream>
#include <iostream>
#include <ostream>
#include <string>
@@ -57,11 +56,10 @@
int main(int argc, char **argv) {
auto arg_vec = absl::ParseCommandLine(argc, argv);
- std::string file_name;
+ std::vector<std::string> file_names;
- // Open input file as stream if specified.
- if (arg_vec.size() > 1) {
- file_name = arg_vec[1];
+ for (int i = 1; i < arg_vec.size(); ++i) {
+ file_names.push_back(arg_vec[i]);
}
mpact::sim::machine_description::instruction_set::InstructionSetVisitor
@@ -80,6 +78,11 @@
exit(-1);
}
auto status =
- visitor.Process(file_name, prefix, isa_name, include_roots, output_dir);
- return status.ok() ? 0 : -1;
+ visitor.Process(file_names, prefix, isa_name, include_roots, output_dir);
+
+ if (!status.ok()) {
+ LOG(ERROR) << status.message();
+ return -1;
+ }
+ return 0;
}
diff --git a/mpact/sim/decoder/encoding_group.cc b/mpact/sim/decoder/encoding_group.cc
index 9bdab50..1261de9 100644
--- a/mpact/sim/decoder/encoding_group.cc
+++ b/mpact/sim/decoder/encoding_group.cc
@@ -272,7 +272,7 @@
"None,");
}
- // Break the line every 4 iterms.
+ // Break the line every 4 items.
if ((i % per_line) == per_line - 1) {
absl::StrAppend(initializers_ptr, "\n");
}
diff --git a/mpact/sim/decoder/format.cc b/mpact/sim/decoder/format.cc
index 3563c38..604a8f5 100644
--- a/mpact/sim/decoder/format.cc
+++ b/mpact/sim/decoder/format.cc
@@ -31,6 +31,16 @@
using ::mpact::sim::machine_description::instruction_set::ToSnakeCase;
+FieldOrFormat::~FieldOrFormat() {
+ if (is_field_) {
+ delete field_;
+ } else {
+ delete format_;
+ }
+ field_ = nullptr;
+ format_ = nullptr;
+}
+
// The equality operator compares to verify that the field/format definitions
// are equivalent, i.e., refers to the same bits.
bool FieldOrFormat::operator==(const FieldOrFormat &rhs) const {
@@ -61,9 +71,9 @@
encoding_info_(encoding_info) {}
Format::~Format() {
- for (auto &[unused, field_ptr] : field_map_) {
- delete field_ptr;
- }
+ // for (auto &[unused, field_ptr] : field_map_) {
+ // delete field_ptr;
+ // }
field_map_.clear();
for (auto &[unused, overlay_ptr] : overlay_map_) {
delete overlay_ptr;
@@ -247,7 +257,7 @@
// promoted.
if (overlay_ptr == nullptr) continue;
auto iter = base_format_->overlay_extractors_.find(name);
- // If it isn't in the partent, add it.
+ // If it isn't in the parent, add it.
if (iter == base_format_->overlay_extractors_.end()) {
base_format_->overlay_extractors_.insert(
std::make_pair(name, overlay_ptr));
diff --git a/mpact/sim/decoder/format.h b/mpact/sim/decoder/format.h
index 9d15d09..4cb1765 100644
--- a/mpact/sim/decoder/format.h
+++ b/mpact/sim/decoder/format.h
@@ -72,6 +72,7 @@
explicit FieldOrFormat(Field *field) : is_field_(true), field_(field) {}
FieldOrFormat(std::string fmt_name, int size, antlr4::Token *ctx)
: is_field_(false), format_name_(fmt_name), size_(size), ctx_(ctx) {}
+ ~FieldOrFormat();
bool is_field() const { return is_field_; }
Field *field() const { return field_; }
diff --git a/mpact/sim/decoder/instruction_encoding.cc b/mpact/sim/decoder/instruction_encoding.cc
index 6db1035..f951c40 100644
--- a/mpact/sim/decoder/instruction_encoding.cc
+++ b/mpact/sim/decoder/instruction_encoding.cc
@@ -53,11 +53,37 @@
// Check if the field name is indeed a field.
auto *field = format_->GetField(field_name);
if (field != nullptr) {
- if (value >= (1 << field->width)) {
- return absl::InternalError(
- absl::StrCat("Constraint value (", value,
- ") exceeds field width for field '", field_name, "'"));
+ bool is_signed = field->is_signed;
+ if (!is_signed) {
+ if (value >= (1 << field->width) || (value < 0)) {
+ return absl::OutOfRangeError(absl::StrCat(
+ "Constraint value (", value, ") out of range for unsigned field '",
+ field_name, "'"));
+ }
+ } else { // Field is signed.
+ // Only eq and ne constraints are allowed on signed fields.
+ if (type != ConstraintType::kEq && type != ConstraintType::kNe) {
+ return absl::InvalidArgumentError(
+ absl::StrCat("Only eq and ne constraints allowed on signed field: ",
+ field_name));
+ }
+ // Check that the value is in range.
+ if (value < 0) {
+ int64_t min_value = -(1 << (field->width - 1));
+ if (value < min_value) {
+ return absl::OutOfRangeError(absl::StrCat(
+ "Constraint value (", value, ") out of range for signed field '",
+ field_name, "'"));
+ }
+ } else {
+ if (value >= (1 << (field->width - 1))) {
+ return absl::OutOfRangeError(absl::StrCat(
+ "Constraint value (", value, ") out of range for signed field '",
+ field_name, "'"));
+ }
+ }
}
+ value &= (1 << field->width) - 1;
auto *constraint = new Constraint();
constraint->type = type;
constraint->field = field;
@@ -68,14 +94,40 @@
auto *overlay = format_->GetOverlay(field_name);
if (overlay == nullptr) {
// If neither, it's an error.
- return absl::InternalError(absl::StrCat(
+ return absl::NotFoundError(absl::StrCat(
"Format '", format_->name(),
"' does not contain a field or overlay named ", field_name));
}
- if (value >= (1 << overlay->computed_width())) {
- return absl::InternalError(absl::StrCat(
- "Constraint value exceeds field width for field '", field_name, "'"));
+ int width = overlay->computed_width();
+ bool is_signed = overlay->is_signed();
+ if (!is_signed) {
+ if ((value >= (1 << width)) || (value < 0)) {
+ return absl::OutOfRangeError(absl::StrCat(
+ "Constraint value exceeds field width for field '", field_name, "'"));
+ }
+ } else {
+ if (type != ConstraintType::kEq && type != ConstraintType::kNe) {
+ return absl::InvalidArgumentError(
+ absl::StrCat("Only eq and ne constraints allowed on signed overlay: ",
+ field_name));
+ } // Check that the value is in range.
+ if (value < 0) {
+ int64_t min_value = -(1 << (width - 1));
+ if (value < min_value) {
+ return absl::OutOfRangeError(absl::StrCat(
+ "Constraint value (", value, ") out of range for signed overlay '",
+ field_name, "'"));
+ }
+ value = value & ((1 << width) - 1);
+ } else {
+ if (value >= (1 << (width - 1))) {
+ return absl::OutOfRangeError(absl::StrCat(
+ "Constraint value (", value, ") out of range for signed overlay '",
+ field_name, "'"));
+ }
+ }
}
+ value &= (1 << width) - 1;
auto *constraint = new Constraint();
constraint->type = type;
constraint->overlay = overlay;
diff --git a/mpact/sim/decoder/instruction_encoding.h b/mpact/sim/decoder/instruction_encoding.h
index 4c9d7b0..47fcc17 100644
--- a/mpact/sim/decoder/instruction_encoding.h
+++ b/mpact/sim/decoder/instruction_encoding.h
@@ -85,8 +85,8 @@
const std::vector<Constraint *> &equal_extracted_constraints() const {
return equal_extracted_constraints_;
}
- // The vector of not-equal constraints that have to be satisfied for an
- // instruction to match this encoding.
+ // The vector of not-equal, greater, less, etc., constraints that have to be
+ // satisfied for an instruction to match this encoding.
const std::vector<Constraint *> &other_constraints() const {
return other_constraints_;
}
diff --git a/mpact/sim/decoder/instruction_group.cc b/mpact/sim/decoder/instruction_group.cc
index 33b99a9..7971fc8 100644
--- a/mpact/sim/decoder/instruction_group.cc
+++ b/mpact/sim/decoder/instruction_group.cc
@@ -53,6 +53,10 @@
}
encoding_map_.clear();
encoding_vec_.clear();
+ for (auto *group : encoding_group_vec_) {
+ delete group;
+ }
+ encoding_group_vec_.clear();
}
// Add an instruction encoding into the current group. Check that the format
diff --git a/mpact/sim/decoder/instruction_set_visitor.cc b/mpact/sim/decoder/instruction_set_visitor.cc
index eed28fc..a4f33dd 100644
--- a/mpact/sim/decoder/instruction_set_visitor.cc
+++ b/mpact/sim/decoder/instruction_set_visitor.cc
@@ -27,10 +27,8 @@
#include <vector>
#include "absl/container/flat_hash_set.h"
-#include "absl/log/log.h"
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
-#include "antlr4-runtime/antlr4-runtime.h"
#include "mpact/sim/decoder/decoder_error_listener.h"
#include "mpact/sim/decoder/format_name.h"
#include "mpact/sim/decoder/template_expression.h"
@@ -75,7 +73,7 @@
// Main entry point for processing the file.
absl::Status InstructionSetVisitor::Process(
- const std::string &file_name, const std::string &prefix,
+ const std::vector<std::string> &file_names, const std::string &prefix,
const std::string &isa_name, const std::vector<std::string> &include_roots,
absl::string_view directory) {
if (isa_name.empty()) {
@@ -83,7 +81,6 @@
return absl::InvalidArgumentError("Isa name cannot be empty");
}
- include_dir_vec_.push_back(".");
for (auto &include_root : include_roots) {
include_dir_vec_.push_back(include_root);
}
@@ -91,21 +88,21 @@
std::string isa_prefix = prefix;
std::istream *source_stream = &std::cin;
- if (!file_name.empty()) {
- source_stream = new std::fstream(file_name, std::fstream::in);
+ if (!file_names.empty()) {
+ source_stream = new std::fstream(file_names[0], std::fstream::in);
}
// Create an antlr4 stream from the input stream.
IsaAntlrParserWrapper parser_wrapper(source_stream);
// Create and add the error listener.
set_error_listener(std::make_unique<decoder::DecoderErrorListener>());
- error_listener()->set_file_name(file_name);
+ error_listener()->set_file_name(file_names[0]);
parser_wrapper.parser()->removeErrorListeners();
parser_wrapper.parser()->addErrorListener(error_listener());
// Parse the file and then create the data structures.
TopLevelCtx *top_level = parser_wrapper.parser()->top_level();
- if (!file_name.empty()) {
+ if (!file_names.empty()) {
delete source_stream;
source_stream = nullptr;
}
@@ -115,7 +112,13 @@
}
// Visit the parse tree starting at the namespaces declaration.
- auto instruction_set = VisitTopLevel(top_level, isa_name);
+ VisitTopLevel(top_level);
+ // Process additional source files.
+ for (int i = 1; i < file_names.size(); ++i) {
+ ParseIncludeFile(top_level, file_names[i], {});
+ }
+ // Now process the parse tree.
+ auto instruction_set = ProcessTopLevel(isa_name);
// Include files may generate additional syntax errors.
if (error_listener()->HasError() > 0) {
return absl::InternalError("Errors encountered - terminating.");
@@ -135,8 +138,16 @@
}
// If the prefix is empty, use the source file name.
- if (isa_prefix.empty()) {
- isa_prefix = ToSnakeCase(std::filesystem::path(file_name).stem().string());
+ if (isa_prefix.empty() && file_names.empty()) {
+ error_listener()->semanticError(nullptr,
+ "No prefix or file name specified");
+ } else if (isa_prefix.empty()) {
+ isa_prefix =
+ ToSnakeCase(std::filesystem::path(file_names[0]).stem().string());
+ }
+ // Check for additional errors.
+ if (error_listener()->HasError() > 0) {
+ return absl::InternalError("Errors encountered - terminating.");
}
std::string encoding_type_name =
absl::StrCat(ToPascalCase(isa_name), "EncodingBase");
@@ -220,8 +231,7 @@
return absl::OkStatus();
}
-std::unique_ptr<InstructionSet> InstructionSetVisitor::VisitTopLevel(
- TopLevelCtx *ctx, const std::string &isa_name) {
+void InstructionSetVisitor::VisitTopLevel(TopLevelCtx *ctx) {
auto declarations = ctx->declaration();
// Process disasm widths. Only the one in the top level file is used if there
@@ -243,7 +253,10 @@
// Parse, but don't process all the slots, bundles, isas and include files.
PreProcessDeclarations(declarations);
+}
+std::unique_ptr<InstructionSet> InstructionSetVisitor::ProcessTopLevel(
+ absl::string_view isa_name) {
// At this point we have the contexts for all isas, bundles, and slots.
// First make sure that the named isa has been defined.
auto isa_ptr = isa_decl_map_.find(isa_name);
@@ -455,17 +468,32 @@
return;
}
}
+ ParseIncludeFile(ctx, file_name, include_dir_vec_);
+}
+
+void InstructionSetVisitor::ParseIncludeFile(
+ antlr4::ParserRuleContext *ctx, const std::string &file_name,
+ const std::vector<std::string> &dirs) {
std::fstream include_file;
// Open include file.
- for (auto const &dir : include_dir_vec_) {
- std::string include_name = absl::StrCat(dir, "/", file_name);
- include_file.open(include_name, std::fstream::in);
- if (include_file.is_open()) break;
- }
+ include_file.open(file_name, std::fstream::in);
if (!include_file.is_open()) {
- error_listener()->semanticError(
- ctx->start, absl::StrCat("Failed to open '", file_name, "'"));
- return;
+ // Try each of the include file directories.
+ for (auto const &dir : dirs) {
+ std::string include_name = dir + "/" + file_name;
+ include_file.open(include_name, std::fstream::in);
+ if (include_file.is_open()) break;
+ }
+ if (!include_file.is_open()) {
+ if (ctx != nullptr) {
+ error_listener()->semanticError(
+ ctx->start, absl::StrCat("Failed to open '", file_name, "'"));
+ } else {
+ error_listener()->semanticError(
+ nullptr, absl::StrCat("Failed to open '", file_name, "'"));
+ }
+ return;
+ }
}
std::string previous_file_name = error_listener()->file_name();
error_listener()->set_file_name(file_name);
@@ -477,9 +505,7 @@
// Add the error listener.
include_parser->parser()->removeErrorListeners();
include_parser->parser()->addErrorListener(error_listener());
- // Note, since include statements can only occur after the isa_def, don't
- // parse starting at the top level rule, instead start at the
- // declaration() rule.
+ // Start parsing at the include_top_level rule.
auto declaration_vec =
include_parser->parser()->include_top_level()->declaration();
include_file.close();
diff --git a/mpact/sim/decoder/instruction_set_visitor.h b/mpact/sim/decoder/instruction_set_visitor.h
index a29d178..5c529f7 100644
--- a/mpact/sim/decoder/instruction_set_visitor.h
+++ b/mpact/sim/decoder/instruction_set_visitor.h
@@ -16,8 +16,6 @@
#define MPACT_SIM_DECODER_INSTRUCTION_SET_VISITOR_H_
#include <deque>
-#include <iostream>
-#include <istream>
#include <memory>
#include <optional>
#include <string>
@@ -29,6 +27,7 @@
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
+#include "antlr4-runtime/ParserRuleContext.h"
#include "mpact/sim/decoder/InstructionSetLexer.h"
#include "mpact/sim/decoder/InstructionSetParser.h"
#include "mpact/sim/decoder/antlr_parser_wrapper.h"
@@ -67,8 +66,8 @@
// Entry point for processing a source_stream input, generating any output
// files in the given directory. Returns OK if no errors were encountered.
- absl::Status Process(const std::string &file_name, const std::string &prefix,
- const std::string &isa_name,
+ absl::Status Process(const std::vector<std::string> &file_names,
+ const std::string &prefix, const std::string &isa_name,
const std::vector<std::string> &include_roots,
absl::string_view directory);
@@ -123,8 +122,7 @@
// The following methods visits the parts of the parse tree indicated by
// the method name and builds up the internal representation used for
// decoder generation.
- std::unique_ptr<InstructionSet> VisitTopLevel(TopLevelCtx *ctx,
- const std::string &isa_name);
+ void VisitTopLevel(TopLevelCtx *ctx);
std::unique_ptr<InstructionSet> VisitIsaDeclaration(IsaDeclCtx *ctx);
void VisitConstantDef(ConstantDefCtx *ctx);
@@ -154,6 +152,10 @@
Slot *slot, Instruction *inst, ResourceItemCtx *resource_item);
void VisitResourceDetailsLists(ResourceDetailsCtx *ctx, Slot *slot,
Instruction *inst, ResourceSpec *spec);
+ std::unique_ptr<InstructionSet> ProcessTopLevel(absl::string_view isa_name);
+ void ParseIncludeFile(antlr4::ParserRuleContext *ctx,
+ const std::string &file_name,
+ const std::vector<std::string> &dirs);
DestinationOperand *FindDestinationOpInExpression(
ExpressionCtx *ctx, const Slot *slot, const Instruction *inst) const;
void PerformOpcodeOverrides(
diff --git a/mpact/sim/decoder/mpact_sim_isa.bzl b/mpact/sim/decoder/mpact_sim_isa.bzl
index c0f0f03..2639933 100644
--- a/mpact/sim/decoder/mpact_sim_isa.bzl
+++ b/mpact/sim/decoder/mpact_sim_isa.bzl
@@ -51,21 +51,31 @@
data = data,
)
-def mpact_isa_decoder(name, src, includes, deps = [], isa_name = "", prefix = ""):
+def mpact_isa_decoder(name, includes, src = "", srcs = [], deps = [], isa_name = "", prefix = ""):
"""Generates the C++ source corresponding to an MPACT Isa decoder definition.
Args:
name: The name of the package to use for the cc_library.
- src: The .isa file containing the Isa rules.
+ src: The .isa file containing the Isa rules (or use srcs).
+ srcs: The .isa files containing the Isa rules (if src is not specified).
deps: Dependencies for the cc_library.
includes: Include .isa files.
isa_name: Name of isa to generate code for.
prefix: File prefix for the generated files (otherwise uses base name of src file
"""
- if not src.endswith(".isa"):
- fail("Grammar must end with .isa", "src")
+
+ # if src is not empty, prepend it to the srcs list.
+ if src != "":
+ isa_srcs = [src] + srcs
+ else:
+ isa_srcs = srcs
+
+ for f in isa_srcs:
+ if not f.endswith(".isa"):
+ fail("Grammar file '" + f + "' must end with .isa", "src")
+
if prefix == "":
- file_prefix = src[:-4]
+ file_prefix = isa_srcs[0][:-4]
base_file_prefix = _strip_path(file_prefix)
else:
base_file_prefix = prefix
@@ -80,15 +90,15 @@
# The command to generate the files.
command = ";\n".join([
- _make_isa_tool_invocation_command(base_file_prefix, isa_name),
+ _make_isa_tool_invocation_command(len(isa_srcs), base_file_prefix, isa_name),
])
# The rule for the generated sources.
native.genrule(
name = name + "_source",
- # Add includes to srcs to ensure they are added to the blaze build sandbox where
+ # Add includes to isa_srcs to ensure they are added to the blaze build sandbox where
# they can be found.
- srcs = [src] + includes,
+ srcs = isa_srcs + includes,
outs = out_files,
cmd = command,
heuristic_label_expansion = 0,
@@ -108,19 +118,28 @@
] + deps,
)
-def mpact_bin_fmt_decoder(name, src, includes, deps = [], decoder_name = "", prefix = ""):
+def mpact_bin_fmt_decoder(name, includes, src = "", srcs = [], deps = [], decoder_name = "", prefix = ""):
"""Generates the C++ source corresponding to an MPACT Bin Format decoder definition.
Args:
name: The name of the package to use for the cc_library.
- src: The .bin_fmt file containing the Bin Format rules.
includes: Include .bin_fmt files.
+ src: The .bin_fmt file containing the Bin Format rules.
+ srcs: List of .bin_fmt files containing the Bin Format rules.
deps: Dependencies for the cc_library
decoder_name: Name of decoder from .bin_fmt file to generate
prefix: File prefix for the generated files (otherwise uses base name of src file
"""
- if not src.endswith(".bin_fmt"):
- fail("Grammar must end with .bin_fmt", "src")
+
+ if src != "":
+ bin_srcs = [src] + srcs
+ else:
+ bin_srcs = srcs
+
+ for f in bin_srcs:
+ if not f.endswith(".bin_fmt"):
+ fail("Grammar file '" + f + "' must end with .bin_fmt", "srcs")
+
if prefix == "":
file_prefix = src[:-8]
base_file_prefix = _strip_path(file_prefix)
@@ -135,7 +154,7 @@
# The command to generate the files.
command = ";\n".join([
- _make_bin_tool_invocation_command(base_file_prefix, decoder_name),
+ _make_bin_tool_invocation_command(len(bin_srcs), base_file_prefix, decoder_name),
])
# The rule for the generated sources.
@@ -171,26 +190,32 @@
return text
return text[pos + 1:]
-# Create the decoder_gen command with arguments, Since the srcs had all the files, including
-# those that will be included, the command includes creating a bash array from $(SRCS), then
-# instead of using $(SRCS) in the command, it uses only the first element of that array.
-def _make_isa_tool_invocation_command(prefix, isa_name):
- cmd = "ARR=($(SRCS)); $(location @@com_google_mpact-sim//mpact/sim/decoder:decoder_gen) " + \
- "$${ARR[0]}" + \
- " --prefix " + prefix + \
- " --output_dir $(@D)"
+# Create the decoder_gen command with arguments, Since the srcs had all the
+# files, including those that will be included, the command includes creating
+# a bash array from $(SRCS), then instead of using $(SRCS) in the command, it
+# uses only the first element of that array.
+def _make_isa_tool_invocation_command(num_srcs, prefix, isa_name):
+ cmd = "ARR=($(SRCS)); $(location //external:decoder_gen) "
+
+ # Add the sources that are not in includes.
+ for i in range(0, num_srcs):
+ cmd += "$${ARR[" + str(i) + "]} "
+ cmd += "--prefix " + prefix + " --output_dir $(@D)"
if isa_name != "":
cmd += " --isa_name " + isa_name
+
return cmd
# Create the bin_format_gen command with arguments, Since the srcs had all the files, including
# those that will be included, the command includes creating a bash array from $(SRCS), then
# instead of using $(SRCS) in the command, it uses only the first element of that array.
-def _make_bin_tool_invocation_command(prefix, decoder_name):
- cmd = "ARR=($(SRCS)); $(location @@com_google_mpact-sim//mpact/sim/decoder:bin_format_gen) " + \
- "$${ARR[0]}" + \
- " --prefix " + prefix + \
- " --output_dir $(@D)"
+def _make_bin_tool_invocation_command(num_srcs, prefix, decoder_name):
+ cmd = "ARR=($(SRCS)); $(location //external:bin_format_gen) "
+
+ # Add the sources that are not in includes.
+ for i in range(0, num_srcs):
+ cmd += "$${ARR[" + str(i) + "]} "
+ cmd += " --prefix " + prefix + " --output_dir $(@D)"
if decoder_name != "":
cmd += " --decoder_name " + decoder_name
return cmd
diff --git a/mpact/sim/decoder/test/BUILD b/mpact/sim/decoder/test/BUILD
index ba3adad..91222dd 100644
--- a/mpact/sim/decoder/test/BUILD
+++ b/mpact/sim/decoder/test/BUILD
@@ -82,6 +82,21 @@
)
mpact_cc_test(
+ name = "resource_test",
+ size = "small",
+ srcs = [
+ "resource_test.cc",
+ ],
+ deps = [
+ "//mpact/sim/decoder:isa_parser",
+ "@com_google_absl//absl/container:btree",
+ "@com_google_absl//absl/status:statusor",
+ "@com_google_googletest//:gtest",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+mpact_cc_test(
name = "template_expression_test",
size = "small",
srcs = [
@@ -137,6 +152,19 @@
isa_name = "Example",
)
+mpact_isa_decoder(
+ name = "combined_isa",
+ srcs = [
+ "testfiles/bundle_b.isa",
+ "testfiles/part1.isa",
+ ],
+ includes = [
+ "//mpact/sim/decoder/test/testfiles/include:bundle_a.isa",
+ "//mpact/sim/decoder/test/testfiles/include:empty_include.isa",
+ ],
+ isa_name = "Example",
+)
+
mpact_cc_library(
name = "riscv32i",
hdrs = ["testfiles/riscv32i.h"],
@@ -155,10 +183,10 @@
deps = ["riscv32i"],
)
-# The point of this test is to actually run the generator on the testfiles/example.isa
-# grammar file, generate source files, and then compile the generated files and
-# link into this test case. Provided that succeeds, this test case succeeds. See the
-# :example_isa build rule for build options.
+# The point of this test is to actually run the generator on the
+# testfiles/example.isa grammar file, generate source files, and then compile
+# the generated files and link into this test case. Provided that succeeds,
+# this test case succeeds. See the :example_isa build rule for build options.
mpact_cc_test(
name = "example_decoder_test",
size = "small",
@@ -175,6 +203,26 @@
],
)
+# The point of this test is to actually run the generator on the
+# testfiles/part1.isa grammar file, generate source files, and then compile
+# the generated files and link into this test case. Provided that succeeds,
+# this test case succeeds. See the :example_isa build rule for build options.
+mpact_cc_test(
+ name = "combined_decoder_test",
+ size = "small",
+ srcs = [
+ "combined_decoder_test.cc",
+ ],
+ deps = [
+ ":combined_isa",
+ "//mpact/sim/generic:arch_state",
+ "//mpact/sim/generic:instruction",
+ "@com_google_absl//absl/container:flat_hash_map",
+ "@com_google_googletest//:gtest",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
mpact_cc_test(
name = "extract_bits_test",
size = "small",
@@ -233,3 +281,61 @@
"@com_google_googletest//:gtest_main",
],
)
+
+mpact_cc_test(
+ name = "bin_encoding_info_test",
+ size = "small",
+ srcs = [
+ "bin_encoding_info_test.cc",
+ ],
+ deps = [
+ "//mpact/sim/decoder:bin_format_visitor",
+ "//mpact/sim/decoder:decoder_error_listener",
+ "@com_google_absl//absl/container:btree",
+ "@com_google_absl//absl/log:check",
+ "@com_google_absl//absl/status",
+ "@com_google_absl//absl/status:statusor",
+ "@com_google_absl//absl/strings",
+ "@com_google_googletest//:gtest",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+mpact_cc_test(
+ name = "instruction_encoding_test",
+ size = "small",
+ srcs = [
+ "instruction_encoding_test.cc",
+ ],
+ deps = [
+ "//mpact/sim/decoder:bin_format_visitor",
+ "//mpact/sim/decoder:decoder_error_listener",
+ "@com_google_absl//absl/status",
+ "@com_google_absl//absl/status:statusor",
+ "@com_google_googletest//:gtest",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
+
+mpact_cc_test(
+ name = "bin_format_visitor_test",
+ size = "small",
+ srcs = [
+ "bin_format_visitor_test.cc",
+ ],
+ data = [
+ # Local include files.
+ "testfiles/empty_file.bin_fmt",
+ "testfiles/riscv32_top.bin_fmt",
+ "testfiles/riscv32g.bin_fmt",
+ "testfiles/riscv32c.bin_fmt",
+ ],
+ deps = [
+ "//mpact/sim/decoder:bin_format_visitor",
+ "@com_google_absl//absl/flags:flag",
+ "@com_google_absl//absl/status",
+ "@com_google_absl//absl/strings",
+ "@com_google_googletest//:gtest",
+ "@com_google_googletest//:gtest_main",
+ ],
+)
diff --git a/mpact/sim/decoder/test/bin_encoding_info_test.cc b/mpact/sim/decoder/test/bin_encoding_info_test.cc
new file mode 100644
index 0000000..b555daa
--- /dev/null
+++ b/mpact/sim/decoder/test/bin_encoding_info_test.cc
@@ -0,0 +1,148 @@
+#include "mpact/sim/decoder/bin_encoding_info.h"
+
+#include "absl/log/check.h"
+#include "absl/status/status.h"
+#include "googletest/include/gtest/gtest.h"
+#include "mpact/sim/decoder/decoder_error_listener.h"
+#include "mpact/sim/decoder/format.h"
+#include "mpact/sim/decoder/instruction_group.h"
+
+// This file provides for testing of the BinEncodingInfo class interfaces, with
+// the exception of PropagateExtractors, as that cannot be tested in isolation.
+
+namespace {
+
+using ::mpact::sim::decoder::DecoderErrorListener;
+using ::mpact::sim::decoder::bin_format::BinEncodingInfo;
+
+constexpr char kOpcodeEnumName[] = "OpcodeEnumName";
+constexpr char kIncludeFile0[] = "IncludeFile0";
+constexpr char kIncludeFile1[] = "IncludeFile1";
+constexpr char kIncludeFile2[] = "IncludeFile2";
+constexpr const char *kIncludeFiles[] = {kIncludeFile0, kIncludeFile1,
+ kIncludeFile2};
+constexpr char kFormat0[] = "Format0";
+constexpr char kFormat1[] = "Format1";
+constexpr char kFormat2[] = "Format2";
+constexpr char kGroup0[] = "Group0";
+constexpr char kBinDecoder[] = "BinDecoder";
+constexpr int kFormatWidth32 = 32;
+constexpr int kFormatWidth16 = 16;
+
+class BinEncodingInfoTest : public ::testing::Test {
+ protected:
+ BinEncodingInfoTest() {
+ error_listener_ = new DecoderErrorListener();
+ bin_encoding_info_ = new BinEncodingInfo(kOpcodeEnumName, error_listener_);
+ }
+ ~BinEncodingInfoTest() override {
+ for (auto &[name, instruction_group_ptr] :
+ bin_encoding_info_->instruction_group_map()) {
+ delete instruction_group_ptr;
+ }
+ delete bin_encoding_info_;
+ delete error_listener_;
+ }
+
+ DecoderErrorListener *error_listener_ = nullptr;
+ BinEncodingInfo *bin_encoding_info_ = nullptr;
+};
+
+// Test for proper initialization of object.
+TEST_F(BinEncodingInfoTest, Constructed) {
+ EXPECT_FALSE(error_listener_->HasError());
+ EXPECT_TRUE(bin_encoding_info_->format_map().empty());
+ EXPECT_TRUE(bin_encoding_info_->include_files().empty());
+ EXPECT_TRUE(bin_encoding_info_->instruction_group_map().empty());
+ EXPECT_EQ(bin_encoding_info_->decoder(), nullptr);
+ EXPECT_EQ(bin_encoding_info_->error_listener(), error_listener_);
+}
+
+// Test that include files are properly added/kept.
+TEST_F(BinEncodingInfoTest, AddIncludeFile) {
+ EXPECT_TRUE(bin_encoding_info_->include_files().empty());
+ for (auto const &include_file : kIncludeFiles) {
+ bin_encoding_info_->AddIncludeFile(include_file);
+ }
+ EXPECT_FALSE(bin_encoding_info_->include_files().empty());
+ for (auto const &include_file : kIncludeFiles) {
+ EXPECT_TRUE(bin_encoding_info_->include_files().contains(include_file));
+ }
+ EXPECT_FALSE(bin_encoding_info_->include_files().contains("NoIncludeFile"));
+}
+
+// Test that formats are properly added/kept.
+TEST_F(BinEncodingInfoTest, AddFormat) {
+ // Adding a new format should work.
+ auto res0 = bin_encoding_info_->AddFormat(kFormat0, kFormatWidth32);
+ EXPECT_TRUE(res0.status().ok());
+ auto *format = res0.value();
+ EXPECT_EQ(format->name(), kFormat0);
+ EXPECT_EQ(format->declared_width(), kFormatWidth32);
+
+ // Make sure we get the format back when calling GetFormat.
+ auto *get_format = bin_encoding_info_->GetFormat(kFormat0);
+ EXPECT_EQ(get_format, format);
+ // Adding the same format again should fail.
+ res0 = bin_encoding_info_->AddFormat(kFormat0, kFormatWidth32);
+ EXPECT_EQ(absl::StatusCode::kAlreadyExists, res0.status().code());
+
+ // Add a different format should work.
+ auto res1 = bin_encoding_info_->AddFormat(kFormat1, kFormatWidth16);
+ EXPECT_TRUE(res1.status().ok());
+ format = res1.value();
+ EXPECT_EQ(format->name(), kFormat1);
+ EXPECT_EQ(format->declared_width(), kFormatWidth16);
+
+ // Add format with parent.
+ auto res2 = bin_encoding_info_->AddFormat(kFormat2, kFormatWidth32, kFormat0);
+ EXPECT_TRUE(res2.status().ok());
+ format = res2.value();
+ EXPECT_EQ(format->name(), kFormat2);
+ EXPECT_EQ(format->declared_width(), kFormatWidth32);
+
+ // Can't add the same format twice.
+ res2 = bin_encoding_info_->AddFormat(kFormat2, kFormatWidth32, kFormat0);
+ EXPECT_EQ(absl::StatusCode::kAlreadyExists, res2.status().code());
+
+ // Format map. Verify that the formats are in the map.
+ auto &format_map = bin_encoding_info_->format_map();
+ EXPECT_EQ(format_map.size(), 3);
+ EXPECT_NE(format_map.find(kFormat0), format_map.end());
+ EXPECT_NE(format_map.find(kFormat1), format_map.end());
+ EXPECT_NE(format_map.find(kFormat2), format_map.end());
+}
+
+// Instruction groups.
+TEST_F(BinEncodingInfoTest, AddInstructionGroup) {
+ EXPECT_TRUE(bin_encoding_info_->instruction_group_map().empty());
+
+ // Add an instruction group.
+ auto res = bin_encoding_info_->AddInstructionGroup(kGroup0, kFormatWidth32,
+ kFormat0);
+ EXPECT_TRUE(res.status().ok());
+ auto *instruction_group = res.value();
+ EXPECT_EQ(instruction_group->name(), kGroup0);
+ EXPECT_EQ(instruction_group->width(), kFormatWidth32);
+ EXPECT_EQ(instruction_group->format_name(), kFormat0);
+ EXPECT_EQ(instruction_group->opcode_enum(), kOpcodeEnumName);
+
+ // Adding it a second time doesn't work.
+ res = bin_encoding_info_->AddInstructionGroup(kGroup0, kFormatWidth32,
+ kFormat0);
+ EXPECT_EQ(absl::StatusCode::kAlreadyExists, res.status().code());
+}
+
+// Bin decoder.
+TEST_F(BinEncodingInfoTest, AddDecoder) {
+ // Add BinDecoder.
+ auto *bin_dec = bin_encoding_info_->AddBinDecoder(kBinDecoder);
+ EXPECT_NE(bin_dec, nullptr);
+ EXPECT_FALSE(error_listener_->HasError());
+ // Try adding it again.
+ bin_dec = bin_encoding_info_->AddBinDecoder(kBinDecoder);
+ EXPECT_EQ(bin_dec, nullptr);
+ EXPECT_TRUE(error_listener_->HasError());
+}
+
+} // namespace
diff --git a/mpact/sim/decoder/test/bin_format_visitor_test.cc b/mpact/sim/decoder/test/bin_format_visitor_test.cc
new file mode 100644
index 0000000..7cee644
--- /dev/null
+++ b/mpact/sim/decoder/test/bin_format_visitor_test.cc
@@ -0,0 +1,95 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mpact/sim/decoder/bin_format_visitor.h"
+
+#include <string>
+
+#include "absl/strings/str_cat.h"
+#include "googletest/include/gtest/gtest.h"
+
+namespace {
+
+constexpr char kTestUndeclaredOutputsDir[] = "TEST_UNDECLARED_OUTPUTS_DIR";
+
+constexpr char kEmptyDecoderName[] = "Empty";
+constexpr char kEmptyBaseName[] = "empty_file";
+constexpr char kRiscVDecoderName[] = "RiscV32G";
+constexpr char kRiscVBaseName[] = "riscv32";
+constexpr char kRiscVTopName[] = "riscv32_top.bin_fmt";
+constexpr char kRiscV32GName[] = "riscv32g.bin_fmt";
+constexpr char kRiscV32CName[] = "riscv32c.bin_fmt";
+
+using ::mpact::sim::decoder::bin_format::BinFormatVisitor;
+
+// The depot path to the test directory.
+constexpr char kDepotPath[] = "mpact/sim/decoder/test/";
+
+std::string OutputDir() { return "./"; }
+
+static bool FileExists(const std::string &name) {
+ std::ifstream file(name);
+ return file.good();
+}
+
+class BinFormatParserTest : public testing::Test {
+ protected:
+ BinFormatParserTest() {}
+
+ std::vector<std::string> paths_;
+};
+
+TEST_F(BinFormatParserTest, NullFileParsing) {
+ // Set up input and output file paths.
+ std::vector<std::string> input_files = {
+ absl::StrCat(kDepotPath, "testfiles/", kEmptyBaseName, ".bin_fmt")};
+ ASSERT_TRUE(FileExists(input_files[0]));
+ std::string output_dir = OutputDir();
+
+ BinFormatVisitor visitor;
+ // Parse and process the input file.
+ EXPECT_FALSE(visitor
+ .Process(input_files, kEmptyBaseName, kEmptyDecoderName,
+ paths_, output_dir)
+ .ok());
+}
+
+TEST_F(BinFormatParserTest, BasicParsing) {
+ // Make sure the visitor can read and parse the input file.
+ // Set up input and output file paths.
+ std::vector<std::string> input_files = {
+ absl::StrCat(kDepotPath, "testfiles/", kRiscVTopName),
+ absl::StrCat(kDepotPath, "testfiles/", kRiscV32GName),
+ absl::StrCat(kDepotPath, "testfiles/", kRiscV32CName),
+ };
+ ASSERT_TRUE(FileExists(input_files[0]));
+ ASSERT_TRUE(FileExists(input_files[1]));
+ ASSERT_TRUE(FileExists(input_files[2]));
+ std::string output_dir = getenv(kTestUndeclaredOutputsDir);
+
+ BinFormatVisitor visitor;
+ // Parse and process the input file.
+ EXPECT_TRUE(visitor
+ .Process(input_files, kRiscVDecoderName, kRiscVBaseName,
+ paths_, output_dir)
+ .ok());
+ // Verify that the decoder files _decoder.{.h,.cc} files were generated.
+ EXPECT_TRUE(FileExists(
+ absl::StrCat(output_dir, "/", kRiscVBaseName, "_bin_decoder.h")));
+
+ EXPECT_TRUE(FileExists(
+ absl::StrCat(output_dir, "/", kRiscVBaseName, "_bin_decoder.cc")));
+}
+
+} // namespace
diff --git a/mpact/sim/decoder/test/combined_decoder_test.cc b/mpact/sim/decoder/test/combined_decoder_test.cc
new file mode 100644
index 0000000..6c20fd8
--- /dev/null
+++ b/mpact/sim/decoder/test/combined_decoder_test.cc
@@ -0,0 +1,25 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "googlemock/include/gmock/gmock.h"
+#include "googletest/include/gtest/gtest.h"
+
+namespace {
+
+// Trivial case that succeeds as long as the generated code compiles, including
+// base classes, as this test file build dependency includes the .isa grammar
+// file. See the BUILD file for details.
+TEST(CombinedDecoderTest, Trivial) { EXPECT_TRUE(true); }
+
+} // namespace
diff --git a/mpact/sim/decoder/test/instruction_encoding_test.cc b/mpact/sim/decoder/test/instruction_encoding_test.cc
new file mode 100644
index 0000000..dace875
--- /dev/null
+++ b/mpact/sim/decoder/test/instruction_encoding_test.cc
@@ -0,0 +1,220 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "mpact/sim/decoder/instruction_encoding.h"
+
+#include <string>
+
+#include "absl/status/status.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "googletest/include/gtest/gtest.h"
+#include "mpact/sim/decoder/bin_encoding_info.h"
+#include "mpact/sim/decoder/bin_format_visitor.h"
+#include "mpact/sim/decoder/decoder_error_listener.h"
+#include "mpact/sim/decoder/format.h"
+
+namespace {
+
+// This file contains the unit tests for the InstructionEncoding class.
+
+using ::mpact::sim::decoder::DecoderErrorListener;
+using ::mpact::sim::decoder::bin_format::BinEncodingInfo;
+using ::mpact::sim::decoder::bin_format::ConstraintType;
+using ::mpact::sim::decoder::bin_format::Format;
+using ::mpact::sim::decoder::bin_format::InstructionEncoding;
+
+constexpr char kITypeEncodingName[] = "i_test_encoding";
+
+constexpr int kFunc3Value = 0b101;
+constexpr int kFunc3Mask = 0b111'00000'000'0000;
+
+class InstructionEncodingTest : public ::testing::Test {
+ protected:
+ InstructionEncodingTest() {
+ error_listener_ = new DecoderErrorListener();
+ encoding_info_ = new BinEncodingInfo("OpcodeEnumName", error_listener_);
+ i_type_fmt_ = new Format("IType", 32, encoding_info_);
+ (void)i_type_fmt_->AddField("imm12", /*is_signed*/ true, 12);
+ (void)i_type_fmt_->AddField("rs1", /*is_signed*/ false, 5);
+ (void)i_type_fmt_->AddField("func3", /*is_signed*/ false, 3);
+ (void)i_type_fmt_->AddField("rd", /*is_signed*/ false, 5);
+ (void)i_type_fmt_->AddField("opcode", /*is_signed*/ false, 7);
+ (void)i_type_fmt_->AddFieldOverlay("uspecial", /*is_signed*/ false, 10);
+ auto *overlay = i_type_fmt_->GetOverlay("uspecial");
+ (void)overlay->AddFieldReference("rs1");
+ (void)overlay->AddFieldReference("rd");
+ (void)i_type_fmt_->AddFieldOverlay("sspecial", /*is_signed*/ true, 10);
+ overlay = i_type_fmt_->GetOverlay("sspecial");
+ (void)overlay->AddFieldReference("rs1");
+ (void)overlay->AddFieldReference("rd");
+ (void)i_type_fmt_->ComputeAndCheckFormatWidth();
+ i_type_ = new InstructionEncoding(kITypeEncodingName, i_type_fmt_);
+ }
+
+ ~InstructionEncodingTest() override {
+ delete error_listener_;
+ delete i_type_fmt_;
+ delete encoding_info_;
+ delete i_type_;
+ }
+
+ DecoderErrorListener *error_listener_;
+ BinEncodingInfo *encoding_info_;
+ Format *i_type_fmt_;
+ InstructionEncoding *i_type_;
+};
+
+TEST_F(InstructionEncodingTest, Basic) {
+ EXPECT_EQ(i_type_->name(), kITypeEncodingName);
+ EXPECT_EQ(i_type_->GetValue(), 0);
+ EXPECT_EQ(i_type_->GetMask(), 0);
+ EXPECT_EQ(i_type_->GetCombinedMask(), 0);
+ EXPECT_TRUE(i_type_->equal_constraints().empty());
+ EXPECT_TRUE(i_type_->equal_extracted_constraints().empty());
+ EXPECT_TRUE(i_type_->other_constraints().empty());
+}
+
+TEST_F(InstructionEncodingTest, BadConstraintName) {
+ // Wrong field names.
+ auto status = i_type_->AddEqualConstraint("NotAName", 0);
+ // Verify error message, and that nothing has changed.
+ EXPECT_TRUE(absl::IsNotFound(status));
+ EXPECT_EQ(i_type_->GetValue(), 0);
+ EXPECT_EQ(i_type_->GetMask(), 0);
+ EXPECT_EQ(i_type_->GetCombinedMask(), 0);
+ EXPECT_TRUE(i_type_->equal_constraints().empty());
+ EXPECT_TRUE(i_type_->equal_extracted_constraints().empty());
+ EXPECT_TRUE(i_type_->other_constraints().empty());
+
+ // Check for other constraint type.
+ status = i_type_->AddOtherConstraint(ConstraintType::kNe, "NotAName", 0);
+ EXPECT_TRUE(absl::IsNotFound(status));
+ EXPECT_EQ(i_type_->GetValue(), 0);
+ EXPECT_EQ(i_type_->GetMask(), 0);
+ EXPECT_EQ(i_type_->GetCombinedMask(), 0);
+ EXPECT_TRUE(i_type_->equal_constraints().empty());
+ EXPECT_TRUE(i_type_->equal_extracted_constraints().empty());
+ EXPECT_TRUE(i_type_->other_constraints().empty());
+}
+
+TEST_F(InstructionEncodingTest, OutOfRangeUnsignedField) {
+ // Correct field name, but value out of range.
+ auto status = i_type_->AddEqualConstraint("func3", 8);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+ status = i_type_->AddEqualConstraint("func3", -5);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+ status = i_type_->AddOtherConstraint(ConstraintType::kLt, "func3", 8);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+ status = i_type_->AddOtherConstraint(ConstraintType::kLe, "func3", -5);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+}
+
+TEST_F(InstructionEncodingTest, OutOfRangeSignedField) {
+ auto status = i_type_->AddEqualConstraint("imm12", 1 << 11);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+ status = i_type_->AddEqualConstraint("imm12", -(1 << 11) - 1);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+ status = i_type_->AddOtherConstraint(ConstraintType::kNe, "imm12", 1 << 11);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+ status =
+ i_type_->AddOtherConstraint(ConstraintType::kNe, "imm12", -(1 << 11) - 1);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+}
+
+TEST_F(InstructionEncodingTest, OutOfRangeUnsignedOverlay) {
+ // Correct field name, but value out of range.
+ auto status = i_type_->AddEqualConstraint("uspecial", 1024);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+ status = i_type_->AddEqualConstraint("uspecial", -5);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+ status = i_type_->AddOtherConstraint(ConstraintType::kNe, "uspecial", 1024);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+ status = i_type_->AddOtherConstraint(ConstraintType::kNe, "uspecial", -5);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+}
+
+TEST_F(InstructionEncodingTest, OutOfRangeSignedOverlay) {
+ auto status = i_type_->AddEqualConstraint("sspecial", 1 << 10);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+ status = i_type_->AddEqualConstraint("sspecial", -(1 << 10) - 1);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+ status =
+ i_type_->AddOtherConstraint(ConstraintType::kNe, "sspecial", 1 << 10);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+ status = i_type_->AddOtherConstraint(ConstraintType::kNe, "sspecial",
+ -(1 << 10) - 1);
+ EXPECT_TRUE(absl::IsOutOfRange(status));
+}
+
+TEST_F(InstructionEncodingTest, IllegalSignedConstraints) {
+ // Field.
+ auto status = i_type_->AddOtherConstraint(ConstraintType::kLt, "imm12", 5);
+ EXPECT_TRUE(absl::IsInvalidArgument(status));
+ status = i_type_->AddOtherConstraint(ConstraintType::kLe, "imm12", 5);
+ EXPECT_TRUE(absl::IsInvalidArgument(status));
+ status = i_type_->AddOtherConstraint(ConstraintType::kGt, "imm12", 5);
+ EXPECT_TRUE(absl::IsInvalidArgument(status));
+ status = i_type_->AddOtherConstraint(ConstraintType::kGe, "imm12", 5);
+ EXPECT_TRUE(absl::IsInvalidArgument(status));
+ // Overlay.
+ status = i_type_->AddOtherConstraint(ConstraintType::kLt, "sspecial", 5);
+ EXPECT_TRUE(absl::IsInvalidArgument(status));
+ status = i_type_->AddOtherConstraint(ConstraintType::kLe, "sspecial", 5);
+ EXPECT_TRUE(absl::IsInvalidArgument(status));
+ status = i_type_->AddOtherConstraint(ConstraintType::kGt, "sspecial", 5);
+ EXPECT_TRUE(absl::IsInvalidArgument(status));
+ status = i_type_->AddOtherConstraint(ConstraintType::kGe, "sspecial", 5);
+ EXPECT_TRUE(absl::IsInvalidArgument(status));
+}
+
+TEST_F(InstructionEncodingTest, AddEqualUnsignedConstraint) {
+ auto status = i_type_->AddEqualConstraint("func3", kFunc3Value);
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(i_type_->GetValue(), kFunc3Value << 12);
+ EXPECT_EQ(i_type_->GetMask(), kFunc3Mask);
+ EXPECT_EQ(i_type_->GetCombinedMask(), kFunc3Mask);
+ EXPECT_EQ(i_type_->equal_constraints().size(), 1);
+ auto *constraint = i_type_->equal_constraints()[0];
+ EXPECT_EQ(constraint->type, ConstraintType::kEq);
+ EXPECT_EQ(constraint->field, i_type_fmt_->GetField("func3"));
+ EXPECT_EQ(constraint->value, kFunc3Value);
+ EXPECT_EQ(constraint->overlay, nullptr);
+ EXPECT_EQ(constraint->can_ignore, false);
+
+ // Other constraints are unaffected.
+ EXPECT_TRUE(i_type_->equal_extracted_constraints().empty());
+ EXPECT_TRUE(i_type_->other_constraints().empty());
+}
+
+TEST_F(InstructionEncodingTest, AddOtherConstraints) {
+ auto status =
+ i_type_->AddOtherConstraint(ConstraintType::kGe, "func3", kFunc3Value);
+ EXPECT_TRUE(status.ok());
+ EXPECT_EQ(i_type_->GetValue(), 0);
+ EXPECT_EQ(i_type_->GetMask(), 0);
+ EXPECT_EQ(i_type_->GetCombinedMask(), kFunc3Mask);
+ EXPECT_EQ(i_type_->other_constraints().size(), 1);
+ auto *constraint = i_type_->other_constraints()[0];
+ EXPECT_EQ(constraint->type, ConstraintType::kGe);
+ EXPECT_EQ(constraint->field, i_type_fmt_->GetField("func3"));
+ EXPECT_EQ(constraint->value, kFunc3Value);
+ EXPECT_EQ(constraint->overlay, nullptr);
+ EXPECT_EQ(constraint->can_ignore, false);
+
+ // Other constraints are unaffected.
+ EXPECT_TRUE(i_type_->equal_extracted_constraints().empty());
+ EXPECT_TRUE(i_type_->equal_constraints().empty());
+}
+
+} // namespace
diff --git a/mpact/sim/decoder/test/instruction_set_visitor_test.cc b/mpact/sim/decoder/test/instruction_set_visitor_test.cc
index f7fe3d4..65a32f3 100644
--- a/mpact/sim/decoder/test/instruction_set_visitor_test.cc
+++ b/mpact/sim/decoder/test/instruction_set_visitor_test.cc
@@ -59,15 +59,15 @@
// An empty file should fail.
TEST_F(InstructionSetParserTest, NullFileParsing) {
// Set up input and output file paths.
- const std::string input_file =
- absl::StrCat(kDepotPath, "testfiles/", kEmptyBaseName, ".isa");
- ASSERT_TRUE(FileExists(input_file));
+ std::vector<std::string> input_files = {
+ absl::StrCat(kDepotPath, "testfiles/", kEmptyBaseName, ".isa")};
+ ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = OutputDir();
InstructionSetVisitor visitor;
// Parse and process the input file.
EXPECT_FALSE(visitor
- .Process(input_file, kEmptyBaseName, kEmptyIsaName, paths_,
+ .Process(input_files, kEmptyBaseName, kEmptyIsaName, paths_,
output_dir)
.ok());
}
@@ -75,30 +75,30 @@
// Make sure recursive includes cause a failure.
TEST_F(InstructionSetParserTest, RecursiveInclude) {
// Set up input and output file paths.
- const std::string input_file =
- absl::StrCat(kDepotPath, "testfiles/", kRecursiveExampleBaseName, ".isa");
- ASSERT_TRUE(FileExists(input_file));
+ std::vector<std::string> input_files = {absl::StrCat(
+ kDepotPath, "testfiles/", kRecursiveExampleBaseName, ".isa")};
+ ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = OutputDir();
InstructionSetVisitor visitor;
EXPECT_FALSE(visitor
- .Process(input_file, kRecursiveExampleBaseName,
+ .Process(input_files, kRecursiveExampleBaseName,
kExampleIsaName, paths_, output_dir)
.ok());
-}
+} // namespace
// Make sure the visitor can read and parse the input file.
TEST_F(InstructionSetParserTest, BasicParsing) {
// Set up input and output file paths.
- const std::string input_file =
- absl::StrCat(kDepotPath, "testfiles/", kExampleBaseName, ".isa");
- ASSERT_TRUE(FileExists(input_file));
+ std::vector<std::string> input_files = {
+ absl::StrCat(kDepotPath, "testfiles/", kExampleBaseName, ".isa")};
+ ASSERT_TRUE(FileExists(input_files[0]));
std::string output_dir = getenv(kTestUndeclaredOutputsDir);
InstructionSetVisitor visitor;
// Parse and process the input file.
EXPECT_TRUE(visitor
- .Process(input_file, kExampleBaseName, kExampleIsaName,
+ .Process(input_files, kExampleBaseName, kExampleIsaName,
paths_, output_dir)
.ok());
// Verify that the decoder files _decoder.{.h,.cc} files were generated.
diff --git a/mpact/sim/decoder/test/resource_test.cc b/mpact/sim/decoder/test/resource_test.cc
new file mode 100644
index 0000000..a8c8159
--- /dev/null
+++ b/mpact/sim/decoder/test/resource_test.cc
@@ -0,0 +1,65 @@
+#include "mpact/sim/decoder/resource.h"
+
+#include "absl/status/statusor.h"
+#include "googlemock/include/gmock/gmock.h"
+#include "googletest/include/gtest/gtest.h"
+
+namespace {
+
+// This file contains tests for Resource and ResourceFactory.
+
+using ::mpact::sim::machine_description::instruction_set::ResourceFactory;
+
+constexpr char kResource1PascalName[] = "Resource1Name";
+constexpr char kResource1Name[] = "resource_1_name";
+constexpr char kResource2Name[] = "resource_2_name";
+
+class ResourceTest : public ::testing::Test {
+ protected:
+ ResourceTest() {}
+ ~ResourceTest() override {}
+
+ ResourceFactory factory_;
+};
+
+// Test that ResourceFactory works as expected.
+TEST_F(ResourceTest, Factory) {
+ auto result = factory_.CreateResource(kResource1Name);
+ EXPECT_TRUE(result.status().ok());
+ auto result2 = factory_.CreateResource(kResource1Name);
+ EXPECT_TRUE(absl::IsAlreadyExists(result2.status()));
+ auto *resource1 = factory_.GetOrInsertResource(kResource1Name);
+ EXPECT_EQ(result.value(), resource1);
+ auto *resource2 = factory_.GetOrInsertResource(kResource2Name);
+ EXPECT_NE(resource2, nullptr);
+ EXPECT_NE(resource2, resource1);
+ auto result3 = factory_.CreateResource(kResource2Name);
+ EXPECT_TRUE(absl::IsAlreadyExists(result3.status()));
+
+ EXPECT_EQ(factory_.resource_map().find(kResource1Name)->second, resource1);
+ EXPECT_EQ(factory_.resource_map().find(kResource2Name)->second, resource2);
+}
+
+// Test initial state of a new resource.
+TEST_F(ResourceTest, ResourceInitial) {
+ auto *resource = factory_.GetOrInsertResource(kResource1Name);
+ EXPECT_TRUE(resource->is_simple());
+ EXPECT_FALSE(resource->is_multi_valued());
+ EXPECT_EQ(resource->name(), kResource1Name);
+ EXPECT_EQ(resource->pascal_name(), kResource1PascalName);
+}
+
+// Test resource setters.
+TEST_F(ResourceTest, ResourceSetters) {
+ auto *resource = factory_.GetOrInsertResource(kResource1Name);
+ resource->set_is_simple(false);
+ resource->set_is_multi_valued(true);
+ EXPECT_FALSE(resource->is_simple());
+ EXPECT_TRUE(resource->is_multi_valued());
+ resource->set_is_simple(true);
+ resource->set_is_multi_valued(false);
+ EXPECT_TRUE(resource->is_simple());
+ EXPECT_FALSE(resource->is_multi_valued());
+}
+
+} // namespace
diff --git a/mpact/sim/decoder/test/testfiles/empty_file.bin_fmt b/mpact/sim/decoder/test/testfiles/empty_file.bin_fmt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mpact/sim/decoder/test/testfiles/empty_file.bin_fmt
diff --git a/mpact/sim/decoder/test/testfiles/part1.isa b/mpact/sim/decoder/test/testfiles/part1.isa
new file mode 100644
index 0000000..3bfc3f0
--- /dev/null
+++ b/mpact/sim/decoder/test/testfiles/part1.isa
@@ -0,0 +1,109 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Define an isa with two bundles.
+isa Example {
+ namespace sim::example::isa;
+ bundles {
+ bundle_a;
+ bundle_b;
+ }
+}
+
+int const1 = 1;
+int my_const = const1 + 2;
+
+// Try some include files.
+#include "mpact/sim/decoder/test/testfiles/include/empty_include.isa"
+#include "mpact/sim/decoder/test/testfiles/include/bundle_a.isa"
+
+template<int base, int mult>
+slot a_side_ops {
+ int base_plus_1 = base + 1;
+ int mult_plus_2 = mult + my_const;
+ default attributes = { one, two = 3, three = base, four = base_plus_1, five = 0};
+ opcodes {
+ vctsf{(pred : sy : dest(base_plus_1))};
+ cvtfs{(pred : sy : dest(base + 1))};
+ adds{(pred : sy, sx : dest(base))},
+ attributes: {five = 1, six};
+ addf{(pred : sy, sx : dest(base))};
+ subf{(pred : sy, sx : dest(base))};
+ mulf{(pred : sy, sx : dest(mult_plus_2))};
+ mulu{(pred : sy, sx : dest(mult + 1))};
+ }
+}
+
+slot a_side {
+ opcodes {
+ nop{()};
+ delay{(pred : const)};
+ settag{()};
+ fence{()};
+ }
+}
+
+slot a_side_0 : a_side, a_side_ops<2, 2> {
+ default opcode =
+ semfunc: "[](Instruction *) {}";
+ opcodes {
+ settag = delete;
+ addf = delete;
+ subf = delete;
+ br_abs{()};
+ br_rel{()};
+ br_ind{()};
+ call_abs{()};
+ call_rel{()};
+ call_ind{()};
+ }
+}
+
+slot a_side_1 : a_side, a_side_ops<1, 4> {
+ default opcode =
+ semfunc: "[](Instruction *) {}";
+ opcodes {
+ ld{(pred : yop : ),(: : dest)},
+ disasm:"%dest = sld %pred [smem:%yop]";
+ ld_offset{(pred : xop, yop : dest(abs(-2)))};
+ st{(pred)};
+ mulf = delete;
+ mulu = delete;
+ }
+}
+
+slot other {
+ default opcode =
+ semfunc: "[](Instruction *) {}";
+}
+
+slot b_side_alu[4] {
+ default opcode =
+ semfunc: "[](Instruction *) {}";
+}
+
+slot b_side_store {
+ default opcode =
+ semfunc: "[](Instruction *) {}";
+}
+
+slot b_side_load[2] {
+ default opcode =
+ semfunc: "[](Instruction *) {}";
+}
+
+slot b_side_load_2 {
+ default opcode =
+ semfunc: "[](Instruction *) {}";
+}
diff --git a/mpact/sim/decoder/test/testfiles/riscv32_top.bin_fmt b/mpact/sim/decoder/test/testfiles/riscv32_top.bin_fmt
new file mode 100644
index 0000000..6eb694d
--- /dev/null
+++ b/mpact/sim/decoder/test/testfiles/riscv32_top.bin_fmt
@@ -0,0 +1,24 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// RiscV 32 bit G instruction decoder.
+decoder RiscV32G {
+ namespace mpact::sim::riscv::encoding;
+ opcode_enum = "isa32::OpcodeEnum";
+ includes {
+ #include "third_party/mpact_riscv/riscv32g_decoder.h"
+ }
+ RiscVGInst32;
+ RiscVCInst16;
+};
diff --git a/mpact/sim/decoder/test/testfiles/riscv32c.bin_fmt b/mpact/sim/decoder/test/testfiles/riscv32c.bin_fmt
new file mode 100644
index 0000000..d7aef71
--- /dev/null
+++ b/mpact/sim/decoder/test/testfiles/riscv32c.bin_fmt
@@ -0,0 +1,176 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Compact instruction formats.
+
+format Inst16Format[16] {
+ fields:
+ unsigned func3[3];
+ unsigned bits[11];
+ unsigned op[2];
+};
+
+format CSS[16] : Inst16Format {
+ fields:
+ unsigned func3[3];
+ unsigned imm6[6];
+ unsigned rs2[5];
+ unsigned op[2];
+ overlays:
+ unsigned css_imm_w[8] = imm6[1..0], imm6[5..2], 0b00;
+ unsigned css_imm_d[9] = imm6[2..0], imm6[5..3], 0b000;
+};
+
+format CL[16] : Inst16Format {
+ fields:
+ unsigned func3[3];
+ unsigned imm3[3];
+ unsigned rs1p[3];
+ unsigned imm2[2];
+ unsigned rdp[3];
+ unsigned op[2];
+ overlays:
+ unsigned cl_rs1[5] = 0b01, rs1p;
+ unsigned cl_rd[5] = 0b01, rdp;
+ unsigned cl_imm_w[7] = imm2[0], imm3, imm2[1], 0b00;
+ unsigned cl_imm_d[8] = imm2, imm3, 0b000;
+};
+
+format CS[16] : Inst16Format {
+ fields:
+ unsigned func3[3];
+ unsigned imm3[3];
+ unsigned rs1p[3];
+ unsigned imm2[2];
+ unsigned rs2p[3];
+ unsigned op[2];
+ overlays:
+ unsigned cs_rs1[5] = 0b01, rs1p;
+ unsigned cs_rs2[5] = 0b01, rs2p;
+ unsigned cs_imm_w[7] = imm2[0], imm3, imm2[1], 0b00;
+ unsigned cs_imm_d[8] = imm2, imm3, 0b000;
+};
+
+format CJ[16] : Inst16Format {
+ fields:
+ unsigned func3[3];
+ unsigned imm11[11];
+ unsigned op[2];
+ overlays:
+ signed jimm[12] = imm11[10, 6, 8..7, 4, 5, 0, 9, 3..1], 0b0;
+};
+
+format CR[16] : Inst16Format {
+ fields:
+ unsigned func4[4];
+ unsigned rs1[5];
+ unsigned rs2[5];
+ unsigned op[2];
+};
+
+format CB[16] : Inst16Format {
+ fields:
+ unsigned func3[3];
+ unsigned imm3[3];
+ unsigned rs1p[3];
+ unsigned imm5[5];
+ unsigned op[2];
+ overlays:
+ unsigned func2[2] = [11, 10];
+ unsigned func5[5] = [12..10, 6..5];
+ unsigned shamt[6] = [12, 6..2];
+ unsigned rs2p[3] = [4..2];
+ unsigned rs2[5] = 0b10, [4..2];
+ signed bimm[9] = imm3[2], imm5[4..3, 0], imm3[1..0], imm5[2..1], 0b0;
+};
+
+format CI[16] : Inst16Format {
+ fields:
+ unsigned func3[3];
+ unsigned imm1[1];
+ unsigned rs1[5];
+ unsigned imm5[5];
+ unsigned op[2];
+ overlays:
+ unsigned rd[5] = rs1;
+ signed imm6[6] = imm1, imm5;
+ unsigned uimm6[6] = imm1, imm5;
+ signed imm18[18] = imm1, imm5, 0b0000'0000'0000;
+ signed ci_imm10[10] = imm1, imm5[2..1, 3, 0, 4], 0b0000;
+ unsigned ci_imm_w[8] = imm5[1..0], imm1, imm5[4..2], 0b00;
+ unsigned ci_imm_d[9] = imm5[2..0], imm1, imm5[4..3], 0b000;
+};
+
+format CIW[16] : Inst16Format {
+ fields:
+ unsigned func3[3];
+ unsigned imm8[8];
+ unsigned rdp[3];
+ unsigned op[2];
+ overlays:
+ unsigned rd[5] = 0b01, rdp;
+ unsigned ciw_imm10[10] = imm8[5..2, 7..6, 0, 1], 0b00;
+};
+
+format CA[16] : Inst16Format {
+ fields:
+ unsigned func6[6];
+ unsigned rs1p[3];
+ unsigned func2[2];
+ unsigned fs2p[3];
+ unsigned op[2];
+ overlays:
+ unsigned rs1[5] = 0b01, rs1p;
+ unsigned rs2[5] = 0b01, fs2p;
+ unsigned rd[5] = 0b01, rs1p;
+};
+
+// Compact instruction encodings.
+instruction group RiscVCInst16[16] : Inst16Format {
+ caddi4spn : CIW: func3 == 0b000, op == 0b00, imm8 != 0;
+ cfld : CL : func3 == 0b001, op == 0b00;
+ clw : CL : func3 == 0b010, op == 0b00;
+ cflw : CL : func3 == 0b011, op == 0b00;
+ cfsd : CS : func3 == 0b101, op == 0b00;
+ csw : CS : func3 == 0b110, op == 0b00;
+ cfsw : CS : func3 == 0b111, op == 0b00;
+ cnop : CI : func3 == 0b000, imm1 == 0, rs1 == 0, imm5 == 0, op == 0b01;
+ caddi : CI : func3 == 0b000, imm6 != 0, rd != 0, op == 0b01;
+ cjal : CJ : func3 == 0b001, op == 0b01;
+ cli : CI : func3 == 0b010, rd != 0, op == 0b01;
+ caddi16sp : CI : func3 == 0b011, rd == 2, op == 0b01;
+ clui : CI : func3 == 0b011, rd != 0, rd != 2, op == 0b01;
+ csrli : CB : func3 == 0b100, imm3 == 0b000, op == 0b01;
+ csrai : CB : func3 == 0b100, imm3 == 0b001, op == 0b01;
+ candi : CB : func3 == 0b100, func2 == 0b10, op == 0b01;
+ csub : CA : func6 == 0b100'011, func2 == 0b00, op == 0b01;
+ cxor : CA : func6 == 0b100'011, func2 == 0b01, op == 0b01;
+ cor : CA : func6 == 0b100'011, func2 == 0b10, op == 0b01;
+ cand : CA : func6 == 0b100'011, func2 == 0b11, op == 0b01;
+ cj : CJ : func3 == 0b101, op == 0b01;
+ cbeqz : CB : func3 == 0b110, op == 0b01;
+ cbnez : CB : func3 == 0b111, op == 0b01;
+ cslli : CI : func3 == 0b000, imm1 == 0, rs1 != 0, op == 0b10;
+ cfldsp : CI : func3 == 0b001, op == 0b10;
+ clwsp : CI : func3 == 0b010, rd != 0, op == 0b10;
+ cflwsp : CI : func3 == 0b011, op == 0b10;
+ cjr : CR : func4 == 0b1000, rs1 != 0, rs2 == 0, op == 0b10;
+ cmv : CR : func4 == 0b1000, rs1 != 0, rs2 != 0, op == 0b10;
+ cebreak : CR : func4 == 0b1001, rs1 == 0, rs2 == 0, op == 0b10;
+ cjalr : CR : func4 == 0b1001, rs1 != 0, rs2 == 0, op == 0b10;
+ cadd : CR : func4 == 0b1001, rs1 != 0, rs2 != 0, op == 0b10;
+ cfsdsp : CSS: func3 == 0b101, op == 0b10;
+ cswsp : CSS: func3 == 0b110, op == 0b10;
+ cfswsp : CSS: func3 == 0b111, op == 0b10;
+};
diff --git a/mpact/sim/decoder/test/testfiles/riscv32g.bin_fmt b/mpact/sim/decoder/test/testfiles/riscv32g.bin_fmt
new file mode 100644
index 0000000..b05875a
--- /dev/null
+++ b/mpact/sim/decoder/test/testfiles/riscv32g.bin_fmt
@@ -0,0 +1,268 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// RiscV 32 bit instructions.
+format Inst32Format[32] {
+ fields:
+ unsigned bits[25];
+ unsigned opcode[7];
+};
+
+format RType[32] : Inst32Format {
+ fields:
+ unsigned func7[7];
+ unsigned rs2[5];
+ unsigned rs1[5];
+ unsigned func3[3];
+ unsigned rd[5];
+ unsigned opcode[7];
+ overlays:
+ unsigned r_uimm5[5] = rs2;
+};
+
+format R4Type[32] : Inst32Format {
+ fields:
+ unsigned rs3[5];
+ unsigned func2[2];
+ unsigned rs2[5];
+ unsigned rs1[5];
+ unsigned func3[3];
+ unsigned rd[5];
+ unsigned opcode[7];
+};
+
+format IType[32] : Inst32Format {
+ fields:
+ signed imm12[12];
+ unsigned rs1[5];
+ unsigned func3[3];
+ unsigned rd[5];
+ unsigned opcode[7];
+ overlays:
+ unsigned u_imm12[12] = imm12;
+ unsigned i_uimm5[5] = rs1;
+};
+
+format SType[32] : Inst32Format {
+ fields:
+ unsigned imm7[7];
+ unsigned rs2[5];
+ unsigned rs1[5];
+ unsigned func3[3];
+ unsigned imm5[5];
+ unsigned opcode[7];
+ overlays:
+ signed s_imm[12] = imm7, imm5;
+};
+
+format BType[32] : Inst32Format {
+ fields:
+ unsigned imm7[7];
+ unsigned rs2[5];
+ unsigned rs1[5];
+ unsigned func3[3];
+ unsigned imm5[5];
+ unsigned opcode[7];
+ overlays:
+ signed b_imm[13] = imm7[6], imm5[0], imm7[5..0], imm5[4..1], 0b0;
+};
+
+format UType[32] : Inst32Format {
+ fields:
+ unsigned imm20[20];
+ unsigned rd[5];
+ unsigned opcode[7];
+ overlays:
+ unsigned u_imm[32] = imm20, 0b0000'0000'0000;
+};
+
+format JType[32] : Inst32Format {
+ fields:
+ unsigned imm20[20];
+ unsigned rd[5];
+ unsigned opcode[7];
+ overlays:
+ signed j_imm[21] = imm20[19, 7..0, 8, 18..9], 0b0;
+};
+
+format Fence[32] : Inst32Format {
+ fields:
+ unsigned fm[4];
+ unsigned pred[4];
+ unsigned succ[4];
+ unsigned rs1[5];
+ unsigned func3[3];
+ unsigned rd[5];
+ unsigned opcode[7];
+};
+
+format AType[32] : Inst32Format {
+ fields:
+ unsigned func5[5];
+ unsigned aq[1];
+ unsigned rl[1];
+ unsigned rs2[5];
+ unsigned rs1[5];
+ unsigned func3[3];
+ unsigned rd[5];
+ unsigned opcode[7];
+};
+
+instruction group RiscVGInst32[32] : Inst32Format {
+ lui : UType : opcode == 0b011'0111;
+ auipc : UType : opcode == 0b001'0111;
+ jal : JType : rd != 0, opcode == 0b110'1111;
+ j : JType : rd == 0, opcode == 0b110'1111;
+ jalr : IType : rd != 0, func3 == 0b000, opcode == 0b110'0111;
+ jr : IType : rd == 0, func3 == 0b000, opcode == 0b110'0111;
+ beq : BType : func3 == 0b000, opcode == 0b110'0011;
+ bne : BType : func3 == 0b001, opcode == 0b110'0011;
+ blt : BType : func3 == 0b100, opcode == 0b110'0011;
+ bge : BType : func3 == 0b101, opcode == 0b110'0011;
+ bltu : BType : func3 == 0b110, opcode == 0b110'0011;
+ bgeu : BType : func3 == 0b111, opcode == 0b110'0011;
+ lb : BType : func3 == 0b000, opcode == 0b000'0011;
+ lh : BType : func3 == 0b001, opcode == 0b000'0011;
+ lw : BType : func3 == 0b010, opcode == 0b000'0011;
+ lbu : BType : func3 == 0b100, opcode == 0b000'0011;
+ lhu : BType : func3 == 0b101, opcode == 0b000'0011;
+ sb : SType : func3 == 0b000, opcode == 0b010'0011;
+ sh : SType : func3 == 0b001, opcode == 0b010'0011;
+ sw : SType : func3 == 0b010, opcode == 0b010'0011;
+ addi : IType : func3 == 0b000, opcode == 0b001'0011;
+ slti : IType : func3 == 0b010, opcode == 0b001'0011;
+ sltiu : IType : func3 == 0b011, opcode == 0b001'0011;
+ xori : IType : func3 == 0b100, opcode == 0b001'0011;
+ ori : IType : func3 == 0b110, opcode == 0b001'0011;
+ andi : IType : func3 == 0b111, opcode == 0b001'0011;
+ slli : RType : func7 == 0b000'0000, func3==0b001, opcode == 0b001'0011;
+ srli : RType : func7 == 0b000'0000, func3==0b101, opcode == 0b001'0011;
+ srai : RType : func7 == 0b010'0000, func3==0b101, opcode == 0b001'0011;
+ add : RType : func7 == 0b000'0000, func3==0b000, opcode == 0b011'0011;
+ sub : RType : func7 == 0b010'0000, func3==0b000, opcode == 0b011'0011;
+ sll : RType : func7 == 0b000'0000, func3==0b001, opcode == 0b011'0011;
+ slt : RType : func7 == 0b000'0000, func3==0b010, opcode == 0b011'0011;
+ sltu : RType : func7 == 0b000'0000, func3==0b011, opcode == 0b011'0011;
+ xor : RType : func7 == 0b000'0000, func3==0b100, opcode == 0b011'0011;
+ srl : RType : func7 == 0b000'0000, func3==0b101, opcode == 0b011'0011;
+ sra : RType : func7 == 0b010'0000, func3==0b101, opcode == 0b011'0011;
+ or : RType : func7 == 0b000'0000, func3==0b110, opcode == 0b011'0011;
+ and : RType : func7 == 0b000'0000, func3==0b111, opcode == 0b011'0011;
+ fence : Fence : func3 == 0b000, opcode == 0b000'1111;
+ ecall : Inst32Format : bits == 0b0000'0000'0000'00000'000'00000, opcode == 0b111'0011;
+ ebreak : Inst32Format : bits == 0b0000'0000'0001'00000'000'00000, opcode == 0b111'0011;
+ // RiscV32 Instruction fence.
+ fencei : IType : func3 == 001, opcode == 0b000'1111;
+ // RiscV32 multiply divide.
+ mul : RType : func7 == 0b000'0001, func3 == 0b000, opcode == 0b011'0011;
+ mulh : RType : func7 == 0b000'0001, func3 == 0b001, opcode == 0b011'0011;
+ mulhsu : RType : func7 == 0b000'0001, func3 == 0b010, opcode == 0b011'0011;
+ mulhu : RType : func7 == 0b000'0001, func3 == 0b011, opcode == 0b011'0011;
+ div : RType : func7 == 0b000'0001, func3 == 0b100, opcode == 0b011'0011;
+ divu : RType : func7 == 0b000'0001, func3 == 0b101, opcode == 0b011'0011;
+ rem : RType : func7 == 0b000'0001, func3 == 0b110, opcode == 0b011'0011;
+ remu : RType : func7 == 0b000'0001, func3 == 0b111, opcode == 0b011'0011;
+ // RiscV32 atomic instructions.
+ lrw : AType : func5 == 0b0'0010, rs2 == 0, func3 == 0b010, opcode == 0b010'1111;
+ scw : AType : func5 == 0b0'0011, func3 == 0b010, opcode == 0b010'1111;
+ amoswapw : AType : func5 == 0b0'0001, func3 == 0b010, opcode == 0b010'1111;
+ amoaddw : AType : func5 == 0b0'0000, func3 == 0b010, opcode == 0b010'1111;
+ amoxorw : AType : func5 == 0b0'0100, func3 == 0b010, opcode == 0b010'1111;
+ amoandw : AType : func5 == 0b0'1100, func3 == 0b010, opcode == 0b010'1111;
+ amoorw : AType : func5 == 0b0'1000, func3 == 0b010, opcode == 0b010'1111;
+ amominw : AType : func5 == 0b1'0000, func3 == 0b010, opcode == 0b010'1111;
+ amomaxw : AType : func5 == 0b1'0100, func3 == 0b010, opcode == 0b010'1111;
+ amominuw : AType : func5 == 0b1'1000, func3 == 0b010, opcode == 0b010'1111;
+ amomaxuw : AType : func5 == 0b1'1100, func3 == 0b010, opcode == 0b010'1111;
+ // RiscV32 single precision floating point instructions.
+ flw : IType : func3 == 0b010, opcode == 0b000'0111;
+ fsw : SType : func3 == 0b010, opcode == 0b010'0111;
+ fmadd_s : R4Type : func2 == 0b00, opcode == 0b100'0011;
+ fmsub_s : R4Type : func2 == 0b00, opcode == 0b100'0111;
+ fnmsub_s : R4Type : func2 == 0b00, opcode == 0b100'1011;
+ fnmadd_s : R4Type : func2 == 0b00, opcode == 0b100'1111;
+ fadd_s : RType : func7 == 0b000'0000, opcode == 0b101'0011;
+ fsub_s : RType : func7 == 0b000'0100, opcode == 0b101'0011;
+ fmul_s : RType : func7 == 0b000'1000, opcode == 0b101'0011;
+ fdiv_s : RType : func7 == 0b000'1100, opcode == 0b101'0011;
+ fsqrt_s : RType : func7 == 0b010'1100, rs2 == 0, opcode == 0b101'0011;
+ fsgnj_s : RType : func7 == 0b001'0000, func3 == 0b000, opcode == 0b101'0011;
+ fsgnjn_s : RType : func7 == 0b001'0000, func3 == 0b001, opcode == 0b101'0011;
+ fsgnjx_s : RType : func7 == 0b001'0000, func3 == 0b010, opcode == 0b101'0011;
+ fmin_s : RType : func7 == 0b001'0100, func3 == 0b000, opcode == 0b101'0011;
+ fmax_s : RType : func7 == 0b001'0100, func3 == 0b001, opcode == 0b101'0011;
+ fcvt_ws : RType : func7 == 0b110'0000, rs2 == 0, opcode == 0b101'0011;
+ fcvt_wus : RType : func7 == 0b110'0000, rs2 == 1, opcode == 0b101'0011;
+ fmv_xw : RType : func7 == 0b111'0000, rs2 == 0, func3 == 0b000, opcode == 0b101'0011;
+ fcmpeq_s : RType : func7 == 0b101'0000, func3 == 0b010, opcode == 0b101'0011;
+ fcmplt_s : RType : func7 == 0b101'0000, func3 == 0b001, opcode == 0b101'0011;
+ fcmple_s : RType : func7 == 0b101'0000, func3 == 0b000, opcode == 0b101'0011;
+ fclass_s : RType : func7 == 0b111'0000, rs2 == 0, func3 == 0b001, opcode == 0b101'0011;
+ fcvt_sw : RType : func7 == 0b110'1000, rs2 == 0, opcode == 0b101'0011;
+ fcvt_swu : RType : func7 == 0b110'1000, rs2 == 1, opcode == 0b101'0011;
+ fmv_wx : RType : func7 == 0b111'1000, rs2 == 0, func3 == 0b000, opcode == 0b101'0011;
+ // RiscV32 double precision floating point instructions.
+ fld : IType : func3 == 0b011, opcode == 0b000'0111;
+ fsd : SType : func3 == 0b011, opcode == 0b010'0111;
+ fmadd_d : R4Type : func2 == 0b01, opcode == 0b100'0011;
+ fmsub_d : R4Type : func2 == 0b01, opcode == 0b100'0111;
+ fnmsub_d : R4Type : func2 == 0b01, opcode == 0b100'1011;
+ fnmadd_d : R4Type : func2 == 0b01, opcode == 0b100'1111;
+ fadd_d : RType : func7 == 0b000'0001, opcode == 0b101'0011;
+ fsub_d : RType : func7 == 0b000'0101, opcode == 0b101'0011;
+ fmul_d : RType : func7 == 0b000'1001, opcode == 0b101'0011;
+ fdiv_d : RType : func7 == 0b000'1101, opcode == 0b101'0011;
+ fsqrt_d : RType : func7 == 0b010'1101, rs2 == 0, opcode == 0b101'0011;
+ fsgnj_d : RType : func7 == 0b001'0001, func3 == 0b000, opcode == 0b101'0011;
+ fsgnjn_d : RType : func7 == 0b001'0001, func3 == 0b001, opcode == 0b101'0011;
+ fsgnjx_d : RType : func7 == 0b001'0001, func3 == 0b010, opcode == 0b101'0011;
+ fmin_d : RType : func7 == 0b001'0101, func3 == 0b000, opcode == 0b101'0011;
+ fmax_d : RType : func7 == 0b001'0101, func3 == 0b001, opcode == 0b101'0011;
+ fcvt_sd : RType : func7 == 0b010'0000, rs2 == 1, opcode == 0b101'0011;
+ fcvt_ds : RType : func7 == 0b010'0001, rs2 == 0, opcode == 0b101'0011;
+ fcmpeq_d : RType : func7 == 0b101'0001, func3 == 0b010, opcode == 0b101'0011;
+ fcmplt_d : RType : func7 == 0b101'0001, func3 == 0b001, opcode == 0b101'0011;
+ fcmple_d : RType : func7 == 0b101'0001, func3 == 0b000, opcode == 0b101'0011;
+ fclass_d : RType : func7 == 0b111'0001, rs2 == 0, func3 == 0b001, opcode == 0b101'0011;
+ fcvt_wd : RType : func7 == 0b110'0001, rs2 == 0, opcode == 0b101'0011;
+ fcvt_wud : RType : func7 == 0b110'0001, rs2 == 1, opcode == 0b101'0011;
+ fcvt_dw : RType : func7 == 0b110'1001, rs2 == 0, opcode == 0b101'0011;
+ fcvt_dwu : RType : func7 == 0b110'1001, rs2 == 1, opcode == 0b101'0011;
+ // RiscV32 CSR manipulation instructions.
+ csrrw : IType : func3 == 0b001, rd != 0, opcode == 0b111'0011;
+ csrrs : IType : func3 == 0b010, rs1 != 0, rd != 0, opcode == 0b111'0011;
+ csrrc : IType : func3 == 0b011, rs1 != 0, rd != 0, opcode == 0b111'0011;
+ csrrs_nr : IType : func3 == 0b010, rs1 != 0, rd == 0, opcode == 0b111'0011;
+ csrrc_nr : IType : func3 == 0b011, rs1 != 0, rd == 0, opcode == 0b111'0011;
+ csrrw_nr : IType : func3 == 0b001, rd == 0, opcode == 0b111'0011;
+ csrrs_nw : IType : func3 == 0b010, rs1 == 0, opcode == 0b111'0011;
+ csrrc_nw : IType : func3 == 0b011, rs1 == 0, opcode == 0b111'0011;
+ csrrwi : IType : func3 == 0b101, rd != 0, opcode == 0b111'0011;
+ csrrsi : IType : func3 == 0b110, rs1 != 0, rd != 0, opcode == 0b111'0011;
+ csrrci : IType : func3 == 0b111, rs1 != 0, rd != 0, opcode == 0b111'0011;
+ csrrsi_nr: IType : func3 == 0b110, rs1 != 0, rd == 0, opcode == 0b111'0011;
+ csrrci_nr: IType : func3 == 0b111, rs1 != 0, rd == 0, opcode == 0b111'0011;
+ csrrwi_nr: IType : func3 == 0b101, rd == 0, opcode == 0b111'0011;
+ csrrsi_nw: IType : func3 == 0b110, rs1 == 0, opcode == 0b111'0011;
+ csrrci_nw: IType : func3 == 0b111, rs1 == 0, opcode == 0b111'0011;
+ // RiscV32 Privileged instructions.
+ uret : Inst32Format : bits == 0b000'0000'00010'00000'000'00000, opcode == 0b111'0011;
+ sret : Inst32Format : bits == 0b000'1000'00010'00000'000'00000, opcode == 0b111'0011;
+ mret : Inst32Format : bits == 0b001'1000'00010'00000'000'00000, opcode == 0b111'0011;
+ wfi : Inst32Format : bits == 0b000'1000'00101'00000'000'00000, opcode == 0b111'0011;
+ sfence_vma_zz : RType : func7 == 0b000'1001, rs2 == 0, rs1 == 0, func3 == 0, rd == 0, opcode == 0b111'0011;
+ sfence_vma_zn : RType : func7 == 0b000'1001, rs2 != 0, rs1 == 0, func3 == 0, rd == 0, opcode == 0b111'0011;
+ sfence_vma_nz : RType : func7 == 0b000'1001, rs2 == 0, rs1 != 0, func3 == 0, rd == 0, opcode == 0b111'0011;
+ sfence_vma_nn : RType : func7 == 0b000'1001, rs2 != 0, rs1 != 0, func3 == 0, rd == 0, opcode == 0b111'0011;
+};
diff --git a/mpact/sim/generic/complex_resource.cc b/mpact/sim/generic/complex_resource.cc
index 971eb04..fcf54e9 100644
--- a/mpact/sim/generic/complex_resource.cc
+++ b/mpact/sim/generic/complex_resource.cc
@@ -37,7 +37,7 @@
size_t mod = cycle_depth_ & kLowBitMask;
if (mod > 0) {
// Clear bits outside the cycle_depth from the last mask.
- mask_array_[array_size_ - 1] >>= kNumBitsPerWord - mod;
+ mask_array_[array_size_ - 1] <<= kNumBitsPerWord - mod;
}
}
@@ -58,21 +58,21 @@
return;
}
int num_longwords = cycles >> 6;
- // If shift amount is greater than 64.
+ // If shift amount is greater or equal to 64.
if (num_longwords > 0) {
- for (unsigned i = 0; i < array_size_; i++) {
- unsigned index = i + num_longwords;
- bit_array_[i] = (index < array_size_) ? bit_array_[index] : 0;
+ for (int i = array_size_ - 1; i >= 0; i--) {
+ int index = i - num_longwords;
+
+ bit_array_[i] = (index < 0) ? 0 : bit_array_[index];
}
}
cycles &= kLowBitMask;
// If cycles is now zero, return (i.e., if cycles was a multiple of 64).
if (cycles == 0) return;
- // The number of words we have to shift within the array. Anything beyond
- // bit_array_[max - 1] was zeroed in the previous step.
- unsigned max = array_size_ - num_longwords;
- for (unsigned i = 0; i < max; i++) {
+ // The number of remaining words we have to shift within the array.
+ // Anything before bit_array_[num_longwords] were zeroed in the previous step.
+ for (unsigned i = num_longwords; i < array_size_; i++) {
// For each array word, shift right by the number of remaining cycles to
// advance. This gets rid of resource reservations from 0..cycles - 1. Then
// or in the low "cycles" bits from the next word into the high "cycles"
diff --git a/mpact/sim/generic/counters.h b/mpact/sim/generic/counters.h
index 2f9c2bd..bf5b7ff 100644
--- a/mpact/sim/generic/counters.h
+++ b/mpact/sim/generic/counters.h
@@ -86,6 +86,12 @@
// to Initialize(..) is required before it can be added to a component.
CounterValueOutputBase() : is_enabled_(false), is_initialized_(false) {}
// Constructs and initializes the counter.
+ CounterValueOutputBase(std::string name, std::string about, const T initial)
+ : name_(std::move(name)),
+ about_(about),
+ is_enabled_(true),
+ is_initialized_(true),
+ value_(std::move(initial)) {}
CounterValueOutputBase(std::string name, const T initial)
: name_(std::move(name)),
about_(),
@@ -198,8 +204,12 @@
// Constructor and destructor.
SimpleCounter() : CounterValueOutputBase<T>() {}
+ SimpleCounter(std::string name, std::string about, const T &initial)
+ : CounterValueOutputBase<T>(std::move(name), std::move(about), initial) {}
SimpleCounter(std::string name, const T &initial)
: CounterValueOutputBase<T>(std::move(name), initial) {}
+ SimpleCounter(std::string name, std::string about)
+ : SimpleCounter(std::move(name), std::move(about), T()) {}
explicit SimpleCounter(std::string name)
: SimpleCounter(std::move(name), T()) {}
SimpleCounter &operator=(const SimpleCounter &) = delete;
diff --git a/mpact/sim/generic/fifo.h b/mpact/sim/generic/fifo.h
index d821d6c..0d22168 100644
--- a/mpact/sim/generic/fifo.h
+++ b/mpact/sim/generic/fifo.h
@@ -20,6 +20,7 @@
#include <deque>
#include <memory>
#include <string>
+#include <type_traits>
#include <utility>
#include <vector>
@@ -258,13 +259,28 @@
typename std::enable_if<!std::is_integral<T>::value, void>::type;
// Helper function used in the partial specialization below.
-template <typename F, typename T>
+template <
+ typename F, typename T,
+ typename std::enable_if<std::is_signed<T>::value, T>::type * = nullptr>
inline T HelperAs(const FifoBase *fifo, int i) {
DataBuffer *db = fifo->Front();
if (nullptr == db) {
return static_cast<T>(0);
}
- return static_cast<T>(db->Get<F>(i));
+
+ return static_cast<T>(db->Get<typename std::make_signed<F>::type>(i));
+}
+
+template <
+ typename F, typename T,
+ typename std::enable_if<std::is_unsigned<T>::value, T>::type * = nullptr>
+inline T HelperAs(const FifoBase *fifo, int i) {
+ DataBuffer *db = fifo->Front();
+ if (nullptr == db) {
+ return static_cast<T>(0);
+ }
+
+ return static_cast<T>(db->Get<typename std::make_unsigned<F>::type>(i));
}
template <typename T, typename Enable>
diff --git a/mpact/sim/generic/test/complex_resource_operand_test.cc b/mpact/sim/generic/test/complex_resource_operand_test.cc
index adcecdb..3532c6f 100644
--- a/mpact/sim/generic/test/complex_resource_operand_test.cc
+++ b/mpact/sim/generic/test/complex_resource_operand_test.cc
@@ -34,16 +34,18 @@
// Bits 100-107 are cleared in this bit vector.
constexpr uint64_t kFree100To107[] = {
0xffff'ffff'ffff'ffff, 0xffff'f00f'ffff'ffff, 0xffff'ffff'ffff'ffff,
- 0x0000'03ff'ffff'ffff};
+ 0xffff'ffff'ffc0'0000};
// Longer than what is supported.
constexpr uint64_t kTooLong[5] = {0xffff, 0, 0, 0, 0};
+// Longer than 234 bits.
+constexpr uint64_t kOnesTooFar[4] = {0, 0, 0, 0xffff'ffff'fff0'0000};
// All zeros, no cycle is reserved.
constexpr uint64_t kAllZeros[4] = {0};
// The request vector corresponding to kFree100To107
-constexpr uint64_t kAcquire100To107[] = {~kFree100To107[0], ~kFree100To107[1],
- ~kFree100To107[2],
- ~kFree100To107[3] & 0x3ff'ffff'ffff};
+constexpr uint64_t kAcquire100To107[] = {
+ ~kFree100To107[0], ~kFree100To107[1], ~kFree100To107[2],
+ ~kFree100To107[3] & 0xffff'ffff'ffc0'0000};
// ArchState derived class that is passed in to the resource (so that it can
// access the clock.
@@ -76,6 +78,11 @@
ComplexResourceOperand *operand_;
};
+// Create and check name.
+TEST_F(ComplexResourceOperandTest, Create) {
+ EXPECT_EQ(operand_->AsString(), resource_->name());
+}
+
// Check error status from setting the cycle mask.
TEST_F(ComplexResourceOperandTest, CycleMask) {
auto *op = new ComplexResourceOperand(nullptr);
@@ -89,7 +96,9 @@
testing::ElementsAreArray(absl::MakeSpan(kAcquire100To107)
.first(operand_->bit_array().size())));
// Now try setting using arrays.
+ EXPECT_TRUE(absl::IsInternal(op->SetCycleMask(kAcquire100To107)));
EXPECT_TRUE(absl::IsInvalidArgument(operand_->SetCycleMask(kTooLong)));
+ EXPECT_TRUE(absl::IsInvalidArgument(operand_->SetCycleMask(kOnesTooFar)));
EXPECT_TRUE(absl::IsInvalidArgument(operand_->SetCycleMask(kAllZeros)));
EXPECT_TRUE(operand_->SetCycleMask(kAcquire100To107).ok());
delete op;
diff --git a/mpact/sim/generic/test/complex_resource_test.cc b/mpact/sim/generic/test/complex_resource_test.cc
index 24fb61d..ef400ef 100644
--- a/mpact/sim/generic/test/complex_resource_test.cc
+++ b/mpact/sim/generic/test/complex_resource_test.cc
@@ -32,10 +32,12 @@
constexpr int kWindow256 = 256;
constexpr size_t kCycleDepth = 234;
constexpr char kResourceName[] = "my_resource";
-uint64_t kAllOnes[] = {0xffff'ffff'ffff'ffff, 0xffff'ffff'ffff'ffff,
- 0xffff'ffff'ffff'ffff, 0x0000'03ff'ffff'ffff};
+uint64_t kAllOnes234[] = {0xffff'ffff'ffff'ffff, 0xffff'ffff'ffff'ffff,
+ 0xffff'ffff'ffff'ffff, 0xffff'ffff'ffc0'0000};
uint64_t kAllOnes256[] = {0xffff'ffff'ffff'ffff, 0xffff'ffff'ffff'ffff,
0xffff'ffff'ffff'ffff, 0xffff'ffff'ffff'ffff};
+uint64_t kAllOnes64[] = {0xffff'ffff'ffff'ffff, 0, 0, 0};
+uint64_t kAllOnes96[] = {0xffff'ffff'ffff'ffff, 0, 0, 0};
class MockArchState : public ArchState {
public:
@@ -58,19 +60,20 @@
auto *resource = new ComplexResource(arch_state_, kResourceName, kCycleDepth);
EXPECT_EQ(resource->bit_array().size(), (kCycleDepth + 63) / 64);
EXPECT_EQ(resource->name(), kResourceName);
+ EXPECT_EQ(resource->AsString(), kResourceName);
delete resource;
}
// Verify that all bits are free to start out with.
TEST_F(ComplexResourceTest, IsFreeMarchingOne) {
auto *resource = new ComplexResource(arch_state_, kResourceName, kCycleDepth);
- uint64_t marching_one[4] = {1, 0, 0, 0};
+ uint64_t marching_one[4] = {0x8000'0000'0000'0000, 0, 0, 0};
for (size_t i = 0; i < kCycleDepth; i++) {
EXPECT_TRUE(resource->IsFree(marching_one)) << i;
uint64_t prev_bit = 0;
for (int j = 0; j < 4; j++) {
uint64_t bit = (marching_one[j] >> 63) & 0x1;
- marching_one[j] <<= 1;
+ marching_one[j] >>= 1;
marching_one[j] |= prev_bit;
prev_bit = bit;
}
@@ -81,15 +84,15 @@
// Reserve all bits, verify that they are set.
TEST_F(ComplexResourceTest, IsBusyMarchingOne) {
auto *resource = new ComplexResource(arch_state_, kResourceName, kCycleDepth);
- resource->Acquire(kAllOnes);
- uint64_t marching_one[4] = {1, 0, 0, 0};
+ resource->Acquire(kAllOnes234);
+ uint64_t marching_one[4] = {0x8000'0000'0000'0000, 0, 0, 0};
for (size_t i = 0; i < kCycleDepth; i++) {
EXPECT_FALSE(resource->IsFree(marching_one)) << i;
uint64_t prev_bit = 0;
for (int j = 0; j < 4; j++) {
- uint64_t bit = (marching_one[j] >> 63) & 0x1;
- marching_one[j] <<= 1;
- marching_one[j] |= prev_bit;
+ uint64_t bit = (marching_one[j]) & 0x1;
+ marching_one[j] >>= 1;
+ marching_one[j] |= (prev_bit << 63);
prev_bit = bit;
}
}
@@ -100,8 +103,8 @@
// try to acquire it. Release the resource for that cycle, then try again.
TEST_F(ComplexResourceTest, AcquireRelease) {
auto *resource = new ComplexResource(arch_state_, kResourceName, kCycleDepth);
- resource->Acquire(kAllOnes);
- uint64_t marching_one[4] = {1, 0, 0, 0};
+ resource->Acquire(kAllOnes234);
+ uint64_t marching_one[4] = {0x8000'0000'0000'0000, 0, 0, 0};
for (size_t i = 0; i < kCycleDepth; i++) {
EXPECT_FALSE(resource->IsFree(marching_one)) << i;
resource->Release(marching_one);
@@ -109,9 +112,9 @@
resource->Acquire(marching_one);
uint64_t prev_bit = 0;
for (int j = 0; j < 4; j++) {
- uint64_t bit = (marching_one[j] >> 63) & 0x1;
- marching_one[j] <<= 1;
- marching_one[j] |= prev_bit;
+ uint64_t bit = (marching_one[j]) & 0x1;
+ marching_one[j] >>= 1;
+ marching_one[j] |= (prev_bit << 63);
prev_bit = bit;
}
}
@@ -123,9 +126,9 @@
// the resource is free in the last cycle, next to last cycle, etc.
TEST_F(ComplexResourceTest, SingleWordBy1) {
auto *resource = new ComplexResource(arch_state_, kResourceName, 64);
- EXPECT_TRUE(resource->IsFree(kAllOnes));
- resource->Acquire(kAllOnes);
- EXPECT_FALSE(resource->IsFree(kAllOnes));
+ EXPECT_TRUE(resource->IsFree(kAllOnes234));
+ resource->Acquire(kAllOnes234);
+ EXPECT_FALSE(resource->IsFree(kAllOnes234));
uint64_t mask_array[1] = {0};
uint64_t mask = 0xffff'ffff'ffff'ffff;
// Increment cycle by 1.
@@ -145,16 +148,16 @@
<< std::endl;
}
arch_state_->set_cycle(cycle);
- EXPECT_TRUE(resource->IsFree(kAllOnes)) << cycle;
+ EXPECT_TRUE(resource->IsFree(kAllOnes234)) << cycle;
delete resource;
}
// Same as above, but advanced the clock by 3.
TEST_F(ComplexResourceTest, SingleWordBy3) {
auto *resource = new ComplexResource(arch_state_, kResourceName, 64);
- EXPECT_TRUE(resource->IsFree(kAllOnes));
- resource->Acquire(kAllOnes);
- EXPECT_FALSE(resource->IsFree(kAllOnes));
+ EXPECT_TRUE(resource->IsFree(kAllOnes234));
+ resource->Acquire(kAllOnes234);
+ EXPECT_FALSE(resource->IsFree(kAllOnes234));
uint64_t mask_array[1] = {0};
uint64_t mask = 0xffff'ffff'ffff'ffff;
// Increment cycle by 3.
@@ -174,7 +177,7 @@
<< std::endl;
}
arch_state_->set_cycle(cycle);
- EXPECT_TRUE(resource->IsFree(kAllOnes)) << cycle;
+ EXPECT_TRUE(resource->IsFree(kAllOnes234)) << cycle;
delete resource;
}
@@ -210,7 +213,7 @@
<< std::endl;
}
arch_state_->set_cycle(cycle);
- EXPECT_TRUE(resource->IsFree(kAllOnes));
+ EXPECT_TRUE(resource->IsFree(kAllOnes234));
delete resource;
}
@@ -244,7 +247,29 @@
<< std::endl;
}
arch_state_->set_cycle(cycle);
- EXPECT_TRUE(resource->IsFree(kAllOnes));
+ EXPECT_TRUE(resource->IsFree(kAllOnes256));
+ delete resource;
+}
+
+// Check that advancing clock by more than 256 yields free resource.
+TEST_F(ComplexResourceTest, ShiftGreaterThan256) {
+ auto *resource = new ComplexResource(arch_state_, kResourceName, 256);
+ EXPECT_TRUE(resource->IsFree(kAllOnes256));
+ resource->Acquire(kAllOnes256);
+ EXPECT_FALSE(resource->IsFree(kAllOnes256));
+ arch_state_->set_cycle(300);
+ EXPECT_TRUE(resource->IsFree(kAllOnes256));
+ delete resource;
+}
+
+// Verify that shifts over 64 bits work.
+TEST_F(ComplexResourceTest, ShiftGreaterThan64) {
+ auto *resource = new ComplexResource(arch_state_, kResourceName, 256);
+ EXPECT_TRUE(resource->IsFree(kAllOnes256));
+ resource->Acquire(kAllOnes256);
+ EXPECT_FALSE(resource->IsFree(kAllOnes64));
+ arch_state_->set_cycle(96);
+ EXPECT_TRUE(resource->IsFree(kAllOnes96));
delete resource;
}
diff --git a/mpact/sim/generic/test/component_test.cc b/mpact/sim/generic/test/component_test.cc
index 84a2682..7ed65fc 100644
--- a/mpact/sim/generic/test/component_test.cc
+++ b/mpact/sim/generic/test/component_test.cc
@@ -61,6 +61,48 @@
}
)pb";
+constexpr char kImportProtoMalformed[] = R"pb(
+ name: "top"
+ configuration { sint64_value: -654 }
+ statistics { name: "int64_counter" sint64_value: -321 }
+ component_data {
+ name: "child"
+ configuration { name: "uint64_config" uint64_value: 321 }
+ statistics { name: "uint64_counter" uint64_value: 654 }
+ }
+)pb";
+
+constexpr char kImportProtoChildNameMissing[] = R"pb(
+ name: "top"
+ configuration { name: "int64_config" sint64_value: -654 }
+ statistics { name: "int64_counter" sint64_value: -321 }
+ component_data {
+ configuration { name: "uint64_config" uint64_value: 321 }
+ statistics { name: "uint64_counter" uint64_value: 654 }
+ }
+)pb";
+
+constexpr char kImportProtoNameMissing[] = R"pb(
+ configuration { name: "int64_config" sint64_value: -654 }
+ statistics { name: "int64_counter" sint64_value: -321 }
+ component_data {
+ name: "child"
+ configuration { name: "uint64_config" uint64_value: 321 }
+ statistics { name: "uint64_counter" uint64_value: 654 }
+ }
+)pb";
+
+constexpr char kImportProtoNameMismatch[] = R"pb(
+ name: "not_top"
+ configuration { name: "int64_config" sint64_value: -654 }
+ statistics { name: "int64_counter" sint64_value: -321 }
+ component_data {
+ name: "child"
+ configuration { name: "uint64_config" uint64_value: 321 }
+ statistics { name: "uint64_counter" uint64_value: 654 }
+ }
+)pb";
+
// Test fixture. Allocates two components, two counters and two config items.
// The counters and config items are not added to the components in the fixture,
// but rather in each test as needed.
@@ -78,6 +120,9 @@
Component &child() { return child_; }
SimpleCounter<int64_t> &int64_counter() { return int64_counter_; }
SimpleCounter<uint64_t> &uint64_counter() { return uint64_counter_; }
+ SimpleCounter<uint64_t> &uninitialized_counter() {
+ return uninitialized_counter_;
+ }
Config<int64_t> &int64_config() { return int64_config_; }
Config<uint64_t> &uint64_config() { return uint64_config_; }
@@ -86,6 +131,7 @@
Component child_;
SimpleCounter<int64_t> int64_counter_;
SimpleCounter<uint64_t> uint64_counter_;
+ SimpleCounter<uint64_t> uninitialized_counter_;
Config<int64_t> int64_config_;
Config<uint64_t> uint64_config_;
};
@@ -113,9 +159,11 @@
TEST_F(ComponentTest, ComponentsWithCounters) {
auto *top_counter = &int64_counter();
auto *child_counter = &uint64_counter();
+ auto *uninit_counter = &uninitialized_counter();
EXPECT_TRUE(top().AddCounter(top_counter).ok());
EXPECT_TRUE(child().AddCounter(child_counter).ok());
+ EXPECT_TRUE(absl::IsInvalidArgument(top().AddCounter(uninit_counter)));
EXPECT_EQ(top().GetCounter(kInt64CounterName), top_counter);
EXPECT_EQ(top().GetCounter(kUint64CounterName), nullptr);
@@ -165,6 +213,84 @@
auto exported_proto = std::make_unique<ComponentData>();
EXPECT_TRUE(top().Export(exported_proto.get()).ok());
+
+ EXPECT_TRUE(absl::IsInvalidArgument(top().Export(nullptr)));
+}
+
+// Failed import due to missing top-level name.
+TEST_F(ComponentTest, ImportTestNameMissing) {
+ auto *top_config = &int64_config();
+ auto *child_config = &uint64_config();
+ EXPECT_TRUE(top().AddConfig(top_config).ok());
+ EXPECT_TRUE(child().AddConfig(child_config).ok());
+
+ auto *top_counter = &int64_counter();
+ auto *child_counter = &uint64_counter();
+ EXPECT_TRUE(top().AddCounter(top_counter).ok());
+ EXPECT_TRUE(child().AddCounter(child_counter).ok());
+
+ ComponentData from_text;
+ EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(
+ kImportProtoNameMissing, &from_text));
+ // Perform the import, expect failure.
+ EXPECT_TRUE(absl::IsInternal(top().Import(from_text)));
+}
+
+// Failed import due to malformed component data.
+TEST_F(ComponentTest, ImportTestMalformed) {
+ auto *top_config = &int64_config();
+ auto *child_config = &uint64_config();
+ EXPECT_TRUE(top().AddConfig(top_config).ok());
+ EXPECT_TRUE(child().AddConfig(child_config).ok());
+
+ auto *top_counter = &int64_counter();
+ auto *child_counter = &uint64_counter();
+ EXPECT_TRUE(top().AddCounter(top_counter).ok());
+ EXPECT_TRUE(child().AddCounter(child_counter).ok());
+
+ ComponentData from_text;
+ EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(
+ kImportProtoMalformed, &from_text));
+ // Perform the import, expect failure.
+ EXPECT_TRUE(absl::IsInternal(top().Import(from_text)));
+}
+
+// Failed import due to missing child name.
+TEST_F(ComponentTest, ImportTestChildNameMissing) {
+ auto *top_config = &int64_config();
+ auto *child_config = &uint64_config();
+ EXPECT_TRUE(top().AddConfig(top_config).ok());
+ EXPECT_TRUE(child().AddConfig(child_config).ok());
+
+ auto *top_counter = &int64_counter();
+ auto *child_counter = &uint64_counter();
+ EXPECT_TRUE(top().AddCounter(top_counter).ok());
+ EXPECT_TRUE(child().AddCounter(child_counter).ok());
+
+ ComponentData from_text;
+ EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(
+ kImportProtoChildNameMissing, &from_text));
+ // Perform the import, expect failure.
+ EXPECT_TRUE(absl::IsInternal(top().Import(from_text)));
+}
+
+// Failed import due to top level name mismatch.
+TEST_F(ComponentTest, ImportTestNameMismatch) {
+ auto *top_config = &int64_config();
+ auto *child_config = &uint64_config();
+ EXPECT_TRUE(top().AddConfig(top_config).ok());
+ EXPECT_TRUE(child().AddConfig(child_config).ok());
+
+ auto *top_counter = &int64_counter();
+ auto *child_counter = &uint64_counter();
+ EXPECT_TRUE(top().AddCounter(top_counter).ok());
+ EXPECT_TRUE(child().AddCounter(child_counter).ok());
+
+ ComponentData from_text;
+ EXPECT_TRUE(google::protobuf::TextFormat::ParseFromString(
+ kImportProtoNameMismatch, &from_text));
+ // Perform the import, expect failure.
+ EXPECT_TRUE(absl::IsInternal(top().Import(from_text)));
}
// Import a proto into the components. Verify that the value of config entries
diff --git a/mpact/sim/generic/test/config_test.cc b/mpact/sim/generic/test/config_test.cc
index 5ee3b23..e2ad820 100644
--- a/mpact/sim/generic/test/config_test.cc
+++ b/mpact/sim/generic/test/config_test.cc
@@ -77,14 +77,19 @@
TEST(ConfigTest, BaseConstruction) {
Config<bool> bool_config(kBoolConfigName);
EXPECT_EQ(bool_config.name(), kBoolConfigName);
+ EXPECT_FALSE(bool_config.HasConfigValue());
Config<int64_t> int64_config(kInt64ConfigName);
EXPECT_EQ(int64_config.name(), kInt64ConfigName);
+ EXPECT_FALSE(int64_config.HasConfigValue());
Config<uint64_t> uint64_config(kUint64ConfigName);
EXPECT_EQ(uint64_config.name(), kUint64ConfigName);
+ EXPECT_FALSE(uint64_config.HasConfigValue());
Config<double> double_config(kDoubleConfigName);
EXPECT_EQ(double_config.name(), kDoubleConfigName);
+ EXPECT_FALSE(double_config.HasConfigValue());
Config<std::string> string_config(kStringConfigName);
EXPECT_EQ(string_config.name(), kStringConfigName);
+ EXPECT_FALSE(string_config.HasConfigValue());
}
// Testing that the value can be set and retrieved using the variant type.
@@ -95,30 +100,35 @@
Config<bool> bool_config(kBoolConfigName);
input_value = ConfigValue(kBoolValue);
EXPECT_TRUE(bool_config.SetConfigValue(input_value).ok());
+ EXPECT_TRUE(bool_config.HasConfigValue());
output_value = bool_config.GetConfigValue();
EXPECT_EQ(std::get<bool>(output_value), kBoolValue);
Config<int64_t> int64_config(kInt64ConfigName);
input_value = ConfigValue(kInt64Value);
EXPECT_TRUE(int64_config.SetConfigValue(input_value).ok());
+ EXPECT_TRUE(int64_config.HasConfigValue());
output_value = int64_config.GetConfigValue();
EXPECT_EQ(std::get<int64_t>(output_value), kInt64Value);
Config<uint64_t> uint64_config(kUint64ConfigName);
input_value = ConfigValue(kUint64Value);
EXPECT_TRUE(uint64_config.SetConfigValue(input_value).ok());
+ EXPECT_TRUE(uint64_config.HasConfigValue());
output_value = uint64_config.GetConfigValue();
EXPECT_EQ(std::get<uint64_t>(output_value), kUint64Value);
Config<double> double_config(kDoubleConfigName);
input_value = ConfigValue(kDoubleValue);
EXPECT_TRUE(double_config.SetConfigValue(input_value).ok());
+ EXPECT_TRUE(double_config.HasConfigValue());
output_value = double_config.GetConfigValue();
EXPECT_EQ(std::get<double>(output_value), kDoubleValue);
Config<std::string> string_config(kStringConfigName);
input_value = ConfigValue(std::string(kStringValue));
EXPECT_TRUE(string_config.SetConfigValue(input_value).ok());
+ EXPECT_TRUE(string_config.HasConfigValue());
output_value = string_config.GetConfigValue();
EXPECT_EQ(std::get<std::string>(output_value), kStringValue);
}
diff --git a/mpact/sim/generic/test/counters_test.cc b/mpact/sim/generic/test/counters_test.cc
index b949ddf..045fe8d 100644
--- a/mpact/sim/generic/test/counters_test.cc
+++ b/mpact/sim/generic/test/counters_test.cc
@@ -97,7 +97,7 @@
TEST(CountersTest, CounterBaseInterface) {
std::string myname("this_is_a_name");
char myname2[] = "this_is_also_a_name";
- SimpleCounter<int64_t> counterone(myname);
+ SimpleCounter<int64_t> counterone(myname, "about this counter");
SimpleCounter<int64_t> countertwo(myname2);
SimpleCounter<int64_t> int64_counter(kSimpleCounterName);
EXPECT_EQ(int64_counter.GetName(), kSimpleCounterName);
@@ -157,6 +157,10 @@
CounterValue cv = int64_counter.GetCounterValue();
EXPECT_EQ(std::get<int64_t>(cv), kMinusFive);
EXPECT_EQ(int64_counter.ToString(), absl::StrCat(kMinusFive));
+ EXPECT_EQ(static_cast<mpact::sim::generic::CounterValueOutputBase<int64_t> *>(
+ &int64_counter)
+ ->ToString(),
+ absl::StrCat(kMinusFive));
}
// Tests the SetValue call in the input interface.
@@ -223,11 +227,13 @@
}
// Verify that the element count is the same as the vector size.
EXPECT_EQ(count.GetValue(), values.size());
+ EXPECT_EQ(count.ToString(), absl::StrCat(values.size()));
}
// Tests export of counter values to proto message.
TEST(CountersTest, ExportTest) {
- SimpleCounter<uint64_t> uint64_counter(kUint64CounterName, kUint64Value);
+ SimpleCounter<uint64_t> uint64_counter(kUint64CounterName,
+ "About this counter", kUint64Value);
SimpleCounter<int64_t> int64_counter(kInt64CounterName, kInt64Value);
SimpleCounter<double> double_counter(kDoubleCounterName, kDoubleValue);
diff --git a/mpact/sim/generic/test/fifo_operand_test.cc b/mpact/sim/generic/test/fifo_operand_test.cc
index 2f94ec1..839560c 100644
--- a/mpact/sim/generic/test/fifo_operand_test.cc
+++ b/mpact/sim/generic/test/fifo_operand_test.cc
@@ -35,6 +35,8 @@
using Vector8Fifo = VectorFifo<uint32_t, 8>;
constexpr int kFifoCapacity = 3;
+constexpr char kScalarFifoName[] = "S0";
+constexpr char kVectorFifoName[] = "S0";
// Define a class that derives from ArchState since constructors are
// protected.
@@ -51,8 +53,8 @@
protected:
FifoOperandTest() {
arch_state_ = new MockArchState("MockArchState");
- sfifo_ = new ScalarFifo(arch_state_, "S0", kFifoCapacity);
- vfifo_ = new Vector8Fifo(arch_state_, "V0", kFifoCapacity);
+ sfifo_ = new ScalarFifo(arch_state_, kScalarFifoName, kFifoCapacity);
+ vfifo_ = new Vector8Fifo(arch_state_, kVectorFifoName, kFifoCapacity);
}
~FifoOperandTest() override {
@@ -72,11 +74,19 @@
EXPECT_EQ(std::any_cast<FifoBase *>(s_src_op->GetObject()),
static_cast<FifoBase *>(sfifo_));
EXPECT_EQ(s_src_op->shape(), sfifo_->shape());
+ EXPECT_EQ(s_src_op->AsString(), kScalarFifoName);
+
+ s_src_op = std::make_unique<FifoSourceOperand<uint32_t>>(sfifo_, "Fifo");
+ EXPECT_EQ(s_src_op->AsString(), "Fifo");
auto v_src_op = std::make_unique<FifoSourceOperand<uint32_t>>(vfifo_);
EXPECT_EQ(std::any_cast<FifoBase *>(v_src_op->GetObject()),
static_cast<FifoBase *>(vfifo_));
EXPECT_EQ(v_src_op->shape(), vfifo_->shape());
+ EXPECT_EQ(v_src_op->AsString(), kVectorFifoName);
+
+ v_src_op = std::make_unique<FifoSourceOperand<uint32_t>>(vfifo_, "Fifo");
+ EXPECT_EQ(v_src_op->AsString(), "Fifo");
}
// Tests that the fifo destination operands are initialized correctly.
@@ -84,10 +94,26 @@
auto s_dst_op = std::make_unique<FifoDestinationOperand<uint32_t>>(sfifo_, 1);
EXPECT_EQ(s_dst_op->latency(), 1);
EXPECT_EQ(s_dst_op->shape(), sfifo_->shape());
+ EXPECT_EQ(s_dst_op->CopyDataBuffer(), nullptr);
+ EXPECT_EQ(std::any_cast<FifoBase *>(s_dst_op->GetObject()),
+ static_cast<FifoBase *>(sfifo_));
+ EXPECT_EQ(s_dst_op->AsString(), kScalarFifoName);
+
+ s_dst_op =
+ std::make_unique<FifoDestinationOperand<uint32_t>>(sfifo_, 1, "Fifo");
+ EXPECT_EQ(s_dst_op->AsString(), "Fifo");
auto v_dst_op = std::make_unique<FifoDestinationOperand<uint32_t>>(vfifo_, 4);
EXPECT_EQ(v_dst_op->latency(), 4);
EXPECT_EQ(v_dst_op->shape(), vfifo_->shape());
+ EXPECT_EQ(v_dst_op->CopyDataBuffer(), nullptr);
+ EXPECT_EQ(std::any_cast<FifoBase *>(v_dst_op->GetObject()),
+ static_cast<FifoBase *>(vfifo_));
+ EXPECT_EQ(v_dst_op->AsString(), kVectorFifoName);
+
+ v_dst_op =
+ std::make_unique<FifoDestinationOperand<uint32_t>>(vfifo_, 1, "Fifo");
+ EXPECT_EQ(v_dst_op->AsString(), "Fifo");
}
// Tests that a destination fifo operand can update a fifo so that
@@ -105,11 +131,19 @@
arch_state_->AdvanceDelayLines();
// Verify that the source operand can read the new value.
+ EXPECT_EQ(src_op->AsBool(0), true);
+ EXPECT_EQ(src_op->AsInt8(0), static_cast<int8_t>(0xEF));
+ EXPECT_EQ(src_op->AsUint8(0), 0xEF);
+ EXPECT_EQ(src_op->AsInt16(0), static_cast<int16_t>(0xBEEF));
+ EXPECT_EQ(src_op->AsUint16(0), 0xBEEF);
+ EXPECT_EQ(src_op->AsInt32(0), static_cast<int32_t>(0xDEADBEEF));
EXPECT_EQ(src_op->AsUint32(0), 0xDEADBEEF);
+ EXPECT_EQ(src_op->AsInt64(0), static_cast<int32_t>(0xDEADBEEF));
+ EXPECT_EQ(src_op->AsUint64(0), 0xDEADBEEF);
- // Get a new data buffer and initialize it to zero.
+ // Get a new data buffer and initialize it to a new value.
DataBuffer *db2 = dst_op->AllocateDataBuffer();
- db2->Set<uint32_t>(0, 0);
+ db2->Set<uint32_t>(0, 0xA5A55A5A);
// Submit the data buffer and advance the delay line 1 cycle.
db2->Submit();
@@ -120,7 +154,12 @@
// Pop the fifo and verify the new value is there.
std::any_cast<FifoBase *>(src_op->GetObject())->Pop();
+ EXPECT_EQ(src_op->AsUint32(0), 0xA5A55A5A);
+
+ // Pop the fifo. It is now empty, so attempting to get a value will return 0.
+ std::any_cast<FifoBase *>(src_op->GetObject())->Pop();
EXPECT_EQ(src_op->AsUint32(0), 0);
+ EXPECT_EQ(src_op->AsInt32(0), 0);
delete dst_op;
delete src_op;
diff --git a/mpact/sim/util/memory/test/flat_demand_memory_test.cc b/mpact/sim/util/memory/test/flat_demand_memory_test.cc
index 660b220..1b82e25 100644
--- a/mpact/sim/util/memory/test/flat_demand_memory_test.cc
+++ b/mpact/sim/util/memory/test/flat_demand_memory_test.cc
@@ -14,6 +14,7 @@
#include "mpact/sim/util/memory/flat_demand_memory.h"
+#include <cstdint>
#include <cstring>
#include <memory>