blob: 9ee0ed65a4261fc81f1a8ab726c0d81767aaf4b8 [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/generic/decode_cache.h"
#include <cstdint>
#include "googlemock/include/gmock/gmock.h"
#include "googletest/include/gtest/gtest.h"
#include "mpact/sim/generic/decoder_interface.h"
#include "mpact/sim/generic/instruction.h"
namespace mpact {
namespace sim {
namespace generic {
namespace {
// Simple decoder class.
class MockDecoder : public DecoderInterface {
public:
MockDecoder() : num_decoded_(0) {}
~MockDecoder() override {}
Instruction *DecodeInstruction(uint64_t address) override {
num_decoded_++;
Instruction *inst = new Instruction(address, nullptr);
return inst;
}
void set_num_decoded(int val) { num_decoded_ = val; }
int num_decoded() const { return num_decoded_; }
private:
int num_decoded_;
};
// Test fixture for DecodeCacheTest.
class DecodeCacheTest : public testing::Test {
protected:
DecodeCacheTest() { decoder_ = new MockDecoder(); }
~DecodeCacheTest() override { delete decoder_; }
MockDecoder *decoder_;
};
// Test creation and verify basic properties.
TEST_F(DecodeCacheTest, BasicProperties) {
DecodeCacheProperties props;
props.num_entries = 1000;
props.minimum_pc_increment = 4;
DecodeCache *dc = DecodeCache::Create(props, decoder_);
EXPECT_EQ(dc->num_entries(), 1024);
EXPECT_EQ(dc->address_mask(), 0xFFC);
EXPECT_EQ(dc->address_shift(), 2);
EXPECT_EQ(dc->address_inc(), 4);
delete dc;
props.num_entries = 500;
props.minimum_pc_increment = 1;
dc = DecodeCache::Create(props, decoder_);
EXPECT_EQ(dc->num_entries(), 512);
EXPECT_EQ(dc->address_mask(), 0x1FF);
EXPECT_EQ(dc->address_shift(), 0);
EXPECT_EQ(dc->address_inc(), 1);
delete dc;
}
// Test that the decode cache caches a decoded instruction.
TEST_F(DecodeCacheTest, CacheOne) {
DecodeCacheProperties props;
props.num_entries = 1000;
props.minimum_pc_increment = 4;
DecodeCache *dc = DecodeCache::Create(props, decoder_);
Instruction *inst;
EXPECT_EQ(decoder_->num_decoded(), 0);
// Not in cache, decoder will be called.
inst = dc->GetDecodedInstruction(0x1000);
EXPECT_EQ(decoder_->num_decoded(), 1);
// In cache. No call to decoder.
inst = dc->GetDecodedInstruction(0x1000);
EXPECT_EQ(decoder_->num_decoded(), 1);
// Not in cache, decoder will be called.
inst = dc->GetDecodedInstruction(0x1004);
EXPECT_EQ(decoder_->num_decoded(), 2);
inst = dc->GetDecodedInstruction(0x1000);
EXPECT_EQ(decoder_->num_decoded(), 2);
// This will kick out the instruction with address 0x1000.
inst = dc->GetDecodedInstruction(0x2000);
EXPECT_EQ(decoder_->num_decoded(), 3);
// This will need to be re-decoded.
inst = dc->GetDecodedInstruction(0x1000);
EXPECT_EQ(decoder_->num_decoded(), 4);
// This is still in the cache.
inst = dc->GetDecodedInstruction(0x1004);
EXPECT_EQ(decoder_->num_decoded(), 4);
(void)inst;
delete dc;
}
// Test invalidation of single instruction.
TEST_F(DecodeCacheTest, InvalidateOne) {
DecodeCacheProperties props;
props.num_entries = 1000;
props.minimum_pc_increment = 4;
DecodeCache *dc = DecodeCache::Create(props, decoder_);
Instruction *inst;
inst = dc->GetDecodedInstruction(0x1000);
inst = dc->GetDecodedInstruction(0x1004);
inst = dc->GetDecodedInstruction(0x1008);
inst = dc->GetDecodedInstruction(0x100c);
EXPECT_EQ(decoder_->num_decoded(), 4);
dc->Invalidate(0x1008);
inst = dc->GetDecodedInstruction(0x1000);
inst = dc->GetDecodedInstruction(0x1004);
inst = dc->GetDecodedInstruction(0x1008);
inst = dc->GetDecodedInstruction(0x100c);
EXPECT_EQ(decoder_->num_decoded(), 5);
(void)inst;
delete dc;
}
// Test invalidation of range.
TEST_F(DecodeCacheTest, InvalidateRange) {
DecodeCacheProperties props;
props.num_entries = 1000;
props.minimum_pc_increment = 4;
DecodeCache *dc = DecodeCache::Create(props, decoder_);
Instruction *inst;
inst = dc->GetDecodedInstruction(0x1000);
inst = dc->GetDecodedInstruction(0x1004);
inst = dc->GetDecodedInstruction(0x1008);
inst = dc->GetDecodedInstruction(0x100c);
EXPECT_EQ(decoder_->num_decoded(), 4);
dc->InvalidateRange(0x1004, 0x100c);
inst = dc->GetDecodedInstruction(0x1000);
inst = dc->GetDecodedInstruction(0x1004);
inst = dc->GetDecodedInstruction(0x1008);
inst = dc->GetDecodedInstruction(0x100c);
EXPECT_EQ(decoder_->num_decoded(), 6);
(void)inst;
delete dc;
}
// Test invalidate all.
TEST_F(DecodeCacheTest, InvalidateAll) {
DecodeCacheProperties props;
props.num_entries = 1000;
props.minimum_pc_increment = 4;
DecodeCache *dc = DecodeCache::Create(props, decoder_);
Instruction *inst;
inst = dc->GetDecodedInstruction(0x1000);
inst = dc->GetDecodedInstruction(0x1004);
inst = dc->GetDecodedInstruction(0x1008);
inst = dc->GetDecodedInstruction(0x100c);
EXPECT_EQ(decoder_->num_decoded(), 4);
dc->InvalidateAll();
inst = dc->GetDecodedInstruction(0x1000);
inst = dc->GetDecodedInstruction(0x1004);
inst = dc->GetDecodedInstruction(0x1008);
inst = dc->GetDecodedInstruction(0x100c);
EXPECT_EQ(decoder_->num_decoded(), 8);
(void)inst;
delete dc;
}
} // namespace
} // namespace generic
} // namespace sim
} // namespace mpact