blob: ec19de8ac34559779ab11f5f4d6e4ac1793532d3 [file]
// 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/overlay.h"
#include <cstdint>
#include <vector>
#include "absl/status/status.h"
#include "absl/strings/str_cat.h"
#include "googlemock/include/gmock/gmock.h"
#include "googletest/include/gtest/gtest.h"
#include "mpact/sim/decoder/bin_format_visitor.h"
#include "mpact/sim/decoder/format.h"
namespace {
using ::mpact::sim::decoder::bin_format::BinaryNum;
using ::mpact::sim::decoder::bin_format::BitRange;
using ::mpact::sim::decoder::bin_format::Format;
using ::mpact::sim::decoder::bin_format::Overlay;
/* The test format is defined as:
*
* fields:
* unsigned func3[3];
* unsigned imm3[3];
* unsigned rs1p[3];
* unsigned imm2[2];
* unsigned rdp[3];
* unsigned op[2];
* overlays:
* unsigned imm_w[7] = imm2[0], imm3, imm2[1], 0b00;
*/
constexpr int kImm3Width = 3;
constexpr char kImm3Name[] = "imm3";
constexpr uint64_t kImm3Mask = 0b000'111'000'00'000'00;
constexpr char kImm2Name[] = "imm2";
constexpr uint64_t kImm2Mask0 = 0b000'000'000'01'000'00;
constexpr int kOverlayWidth = 7;
constexpr char kOverlayName[] = "imm_w";
class OverlayTest : public testing::Test {
protected:
OverlayTest() {
// First create a format for the overlay to refer to.
format_ = new Format("test", 16, nullptr);
(void)format_->AddField("func3", /*is_signed=*/false, /*width=*/3);
(void)format_->AddField(kImm3Name, /*is_signed=*/false, kImm3Width);
(void)format_->AddField("rs1p", /*is_signed=*/false, /*width=*/3);
(void)format_->AddField("imm2", /*is_signed=*/false, /*width=*/2);
(void)format_->AddField("rdp", /*is_signed=*/false, /*width=*/3);
(void)format_->AddField("op", /*is_signed=*/false, /*width=*/2);
(void)format_->ComputeAndCheckFormatWidth();
}
~OverlayTest() override { delete format_; }
Format* format_;
};
// Test construction and initial state of an overlay.
TEST_F(OverlayTest, Constructor) {
auto* overlay =
new Overlay(kOverlayName, /*is_signed=*/false, kOverlayWidth, format_);
EXPECT_EQ(overlay->name(), kOverlayName);
EXPECT_FALSE(overlay->is_signed());
EXPECT_EQ(overlay->declared_width(), 7);
EXPECT_EQ(overlay->computed_width(), 0);
EXPECT_EQ(overlay->mask(), 0);
EXPECT_TRUE(overlay->component_vec().empty());
EXPECT_FALSE(overlay->must_be_extracted());
delete overlay;
}
// Add a full field reference.
TEST_F(OverlayTest, AddFieldReference) {
auto* overlay =
new Overlay(kOverlayName, /*is_signed=*/false, kOverlayWidth, format_);
// Fail to add a reference to an unknown field.
EXPECT_EQ(overlay->AddFieldReference("immXYZ").code(),
absl::StatusCode::kInternal);
ASSERT_TRUE(overlay->AddFieldReference(kImm3Name).ok());
ASSERT_TRUE(overlay->ComputeHighLow().ok());
EXPECT_EQ(overlay->computed_width(), kImm3Width);
EXPECT_EQ(overlay->mask(), kImm3Mask);
EXPECT_EQ(overlay->component_vec().size(), 1);
EXPECT_FALSE(overlay->must_be_extracted());
delete overlay;
}
// Add a field range reference.
TEST_F(OverlayTest, AddFieldRangeReference) {
auto* overlay =
new Overlay(kOverlayName, /*is_signed=*/false, kOverlayWidth, format_);
// Fail to add a reference to an unknown field.
EXPECT_EQ(
overlay
->AddFieldReference("immXYZ", std::vector<BitRange>{BitRange{3, 2}})
.code(),
absl::StatusCode::kInternal);
// Fail to add a reference with an illegal bit range.
EXPECT_EQ(
overlay
->AddFieldReference(kImm2Name, std::vector<BitRange>{BitRange{0, 1}})
.code(),
absl::StatusCode::kInternal);
EXPECT_EQ(
overlay
->AddFieldReference(kImm2Name, std::vector<BitRange>{BitRange{10, 0}})
.code(),
absl::StatusCode::kInternal);
EXPECT_EQ(
overlay
->AddFieldReference(kImm2Name, std::vector<BitRange>{BitRange{0, 10}})
.code(),
absl::StatusCode::kInternal);
ASSERT_TRUE(
overlay
->AddFieldReference(kImm2Name, std::vector<BitRange>{BitRange{0, 0}})
.ok());
ASSERT_TRUE(overlay->ComputeHighLow().ok());
EXPECT_EQ(overlay->computed_width(), 1);
EXPECT_EQ(overlay->mask(), kImm2Mask0);
EXPECT_EQ(overlay->component_vec().size(), 1);
EXPECT_FALSE(overlay->must_be_extracted());
delete overlay;
}
// Add a format range reference.
TEST_F(OverlayTest, AddFormatReference) {
auto* overlay =
new Overlay(kOverlayName, /*is_signed=*/false, kOverlayWidth, format_);
// Fail to add a reference to an unknown field.
EXPECT_EQ(overlay->AddFormatReference(std::vector<BitRange>{BitRange{18, 16}})
.code(),
absl::StatusCode::kInternal);
// Fail to add a reference with an illegal bit range.
EXPECT_EQ(overlay->AddFormatReference(std::vector<BitRange>{BitRange{10, 12}})
.code(),
absl::StatusCode::kInternal);
EXPECT_EQ(overlay->AddFormatReference(std::vector<BitRange>{BitRange{10, 18}})
.code(),
absl::StatusCode::kInternal);
ASSERT_TRUE(
overlay->AddFormatReference(std::vector<BitRange>{BitRange{12, 10}})
.ok());
ASSERT_TRUE(overlay->ComputeHighLow().ok());
EXPECT_EQ(overlay->computed_width(), kImm3Width);
EXPECT_EQ(overlay->mask(), kImm3Mask);
EXPECT_EQ(overlay->component_vec().size(), 1);
EXPECT_FALSE(overlay->must_be_extracted());
delete overlay;
}
// Add a bit constant.
TEST_F(OverlayTest, AddBitConstant) {
auto* overlay =
new Overlay(kOverlayName, /*is_signed=*/false, kOverlayWidth, format_);
overlay->AddBitConstant(BinaryNum{0b00, 2});
ASSERT_TRUE(overlay->ComputeHighLow().ok());
EXPECT_EQ(overlay->computed_width(), 2);
EXPECT_EQ(overlay->mask(), 0);
EXPECT_EQ(overlay->component_vec().size(), 1);
EXPECT_TRUE(overlay->must_be_extracted());
delete overlay;
}
// Full overlay test with value extraction.
TEST_F(OverlayTest, FullOverlay) {
auto* overlay =
new Overlay(kOverlayName, /*is_signed=*/false, kOverlayWidth, format_);
ASSERT_TRUE(
overlay
->AddFieldReference(kImm2Name, std::vector<BitRange>{BitRange{0, 0}})
.ok());
ASSERT_TRUE(overlay->AddFieldReference(kImm3Name).ok());
ASSERT_TRUE(
overlay
->AddFieldReference(kImm2Name, std::vector<BitRange>{BitRange{1, 1}})
.ok());
overlay->AddBitConstant(BinaryNum{0b00, 2});
ASSERT_TRUE(overlay->ComputeHighLow().ok());
EXPECT_EQ(overlay->computed_width(), overlay->declared_width());
auto result = overlay->GetValue(0b000'000'000'01'000'00);
ASSERT_TRUE(result.status().ok());
EXPECT_EQ(result.value(), 0b1'000'0'00);
result = overlay->GetValue(0b000'000'000'10'000'00);
ASSERT_TRUE(result.status().ok());
EXPECT_EQ(result.value(), 0b0'000'1'00);
result = overlay->GetValue(0b000'001'000'00'000'00);
ASSERT_TRUE(result.status().ok());
EXPECT_EQ(result.value(), 0b0'001'0'00);
delete overlay;
}
// Extraction code.
TEST_F(OverlayTest, WriteSimpleExtractor) {
auto* overlay =
new Overlay(kOverlayName, /*is_signed=*/false, kOverlayWidth, format_);
ASSERT_TRUE(
overlay
->AddFieldReference(kImm2Name, std::vector<BitRange>{BitRange{0, 0}})
.ok());
ASSERT_TRUE(overlay->AddFieldReference(kImm3Name).ok());
ASSERT_TRUE(
overlay
->AddFieldReference(kImm2Name, std::vector<BitRange>{BitRange{1, 1}})
.ok());
overlay->AddBitConstant(BinaryNum{0b11, 2});
ASSERT_TRUE(overlay->ComputeHighLow().ok());
auto c_code = overlay->WriteSimpleValueExtractor("value", "result");
EXPECT_THAT(c_code, testing::HasSubstr(
absl::StrCat("result = (value & 0x20) << 1;")));
EXPECT_THAT(c_code, testing::HasSubstr(
absl::StrCat("result |= (value & 0x40) >> 4;")));
EXPECT_THAT(c_code, testing::HasSubstr(
absl::StrCat("result |= (value & 0x1c00) >> 7;")));
delete overlay;
}
TEST_F(OverlayTest, WriteComplexExtractor) {
auto* overlay =
new Overlay(kOverlayName, /*is_signed=*/false, kOverlayWidth, format_);
overlay->AddBitConstant(BinaryNum{0b11, 2});
ASSERT_TRUE(
overlay
->AddFieldReference(kImm2Name, std::vector<BitRange>{BitRange{0, 0}})
.ok());
ASSERT_TRUE(overlay->AddFieldReference(kImm3Name).ok());
ASSERT_TRUE(
overlay
->AddFieldReference(kImm2Name, std::vector<BitRange>{BitRange{1, 1}})
.ok());
ASSERT_TRUE(overlay->ComputeHighLow().ok());
auto c_code =
overlay->WriteComplexValueExtractor("value", "result", "uint64_t");
EXPECT_THAT(c_code, testing::HasSubstr("result = 3 << 5;"));
EXPECT_THAT(
c_code,
testing::HasSubstr("result |= ExtractBits<uint64_t>(value, 5, 1) << 4;"));
EXPECT_THAT(c_code,
testing::HasSubstr(
"result |= ExtractBits<uint64_t>(value, 12, 3) << 1;"));
EXPECT_THAT(c_code, testing::HasSubstr(
"result |= ExtractBits<uint64_t>(value, 6, 1);"));
delete overlay;
}
} // namespace