| // 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/complex_resource.h" |
| |
| #include <ostream> |
| |
| #include "googlemock/include/gmock/gmock.h" |
| #include "googletest/include/gtest/gtest.h" |
| #include "mpact/sim/generic/arch_state.h" |
| #include "mpact/sim/generic/operand_interface.h" |
| |
| namespace { |
| |
| using ::mpact::sim::generic::ArchState; |
| using ::mpact::sim::generic::ComplexResource; |
| using ::mpact::sim::generic::SourceOperandInterface; |
| |
| constexpr int kShiftLimit = 64; |
| constexpr int kShiftMask = 63; |
| 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 kAllOnes256[] = {0xffff'ffff'ffff'ffff, 0xffff'ffff'ffff'ffff, |
| 0xffff'ffff'ffff'ffff, 0xffff'ffff'ffff'ffff}; |
| |
| class MockArchState : public ArchState { |
| public: |
| MockArchState(absl::string_view id, SourceOperandInterface *pc_op) |
| : ArchState(id, pc_op) {} |
| explicit MockArchState(absl::string_view id) : MockArchState(id, nullptr) {} |
| void set_cycle(uint64_t value) { ArchState::set_cycle(value); } |
| }; |
| |
| class ComplexResourceTest : public testing::Test { |
| protected: |
| ComplexResourceTest() { arch_state_ = new MockArchState("TestArchitecture"); } |
| |
| ~ComplexResourceTest() override { delete arch_state_; } |
| |
| MockArchState *arch_state_; |
| }; |
| |
| TEST_F(ComplexResourceTest, Construct) { |
| auto *resource = new ComplexResource(arch_state_, kResourceName, kCycleDepth); |
| EXPECT_EQ(resource->bit_array().size(), (kCycleDepth + 63) / 64); |
| EXPECT_EQ(resource->name(), 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}; |
| 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] |= prev_bit; |
| prev_bit = bit; |
| } |
| } |
| delete resource; |
| } |
| |
| // 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}; |
| 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; |
| prev_bit = bit; |
| } |
| } |
| delete resource; |
| } |
| |
| // Acquire the resource for all the cycles. Then check if it is available and |
| // 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}; |
| for (size_t i = 0; i < kCycleDepth; i++) { |
| EXPECT_FALSE(resource->IsFree(marching_one)) << i; |
| resource->Release(marching_one); |
| EXPECT_TRUE(resource->IsFree(marching_one)); |
| 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; |
| prev_bit = bit; |
| } |
| } |
| delete resource; |
| } |
| |
| // Resource with 64 cycle window. |
| // Acquires the resource for all cycles. Then advances the clock and checks if |
| // 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)); |
| uint64_t mask_array[1] = {0}; |
| uint64_t mask = 0xffff'ffff'ffff'ffff; |
| // Increment cycle by 1. |
| int cycle = 0; |
| for (cycle = 1; cycle < kShiftLimit; cycle++) { |
| mask_array[0] = mask << (kShiftLimit - cycle); |
| EXPECT_FALSE(resource->IsFree(absl::Span<uint64_t>(mask_array))) |
| << absl::StrCat( |
| cycle, ": mask_array[0] = ", absl::Hex(mask_array[0]), "\n", |
| cycle, ": bit_array[0] = ", absl::Hex(resource->bit_array()[0])) |
| << std::endl; |
| arch_state_->set_cycle(cycle); |
| EXPECT_TRUE(resource->IsFree(absl::Span<uint64_t>(mask_array))) |
| << absl::StrCat( |
| cycle, ": mask_array[0] = ", absl::Hex(mask_array[0]), "\n", |
| cycle, ": bit_array[0] = ", absl::Hex(resource->bit_array()[0])) |
| << std::endl; |
| } |
| arch_state_->set_cycle(cycle); |
| EXPECT_TRUE(resource->IsFree(kAllOnes)) << 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)); |
| uint64_t mask_array[1] = {0}; |
| uint64_t mask = 0xffff'ffff'ffff'ffff; |
| // Increment cycle by 3. |
| int cycle = 0; |
| for (cycle = 1; cycle < kShiftLimit; cycle += 3) { |
| mask_array[0] = mask << (kShiftLimit - cycle); |
| EXPECT_FALSE(resource->IsFree(absl::Span<uint64_t>(mask_array))) |
| << absl::StrCat( |
| cycle, ": mask_array[0] = ", absl::Hex(mask_array[0]), "\n", |
| cycle, ": bit_array[0] = ", absl::Hex(resource->bit_array()[0])) |
| << std::endl; |
| arch_state_->set_cycle(cycle); |
| EXPECT_TRUE(resource->IsFree(absl::Span<uint64_t>(mask_array))) |
| << absl::StrCat( |
| cycle, ": mask_array[0] = ", absl::Hex(mask_array[0]), "\n", |
| cycle, ": bit_array[0] = ", absl::Hex(resource->bit_array()[0])) |
| << std::endl; |
| } |
| arch_state_->set_cycle(cycle); |
| EXPECT_TRUE(resource->IsFree(kAllOnes)) << cycle; |
| delete resource; |
| } |
| |
| // Resource with 256 cycle window. |
| // Acquires the resource for all cycles. Then advances the clock and checks if |
| // the resource is free in the last cycle, next to last cycle, etc. |
| TEST_F(ComplexResourceTest, QuadWordBy1) { |
| auto *resource = new ComplexResource(arch_state_, kResourceName, 256); |
| EXPECT_TRUE(resource->IsFree(kAllOnes256)); |
| resource->Acquire(kAllOnes256); |
| EXPECT_FALSE(resource->IsFree(kAllOnes256)); |
| uint64_t mask_array[4] = {0}; |
| uint64_t mask = 0xffff'ffff'ffff'ffff; |
| // Increment cycle by 1. |
| int cycle = 0; |
| for (cycle = 1; cycle < kWindow256; cycle++) { |
| int index = (kWindow256 - cycle) / kShiftLimit; |
| int shift_amount = kShiftLimit - (cycle & kShiftMask); |
| if (shift_amount == kShiftLimit) shift_amount = 0; |
| mask_array[index] = mask << shift_amount; |
| EXPECT_FALSE(resource->IsFree(absl::Span<uint64_t>(mask_array))) |
| << absl::StrCat(cycle, ": mask_array[", index, |
| "] = ", absl::Hex(mask_array[index]), "\n", cycle, |
| ": bit_array[", index, |
| "] = ", absl::Hex(resource->bit_array()[index])) |
| << std::endl; |
| arch_state_->set_cycle(cycle); |
| EXPECT_TRUE(resource->IsFree(absl::Span<uint64_t>(mask_array))) |
| << absl::StrCat(cycle, ": mask_array[", index, |
| "] = ", absl::Hex(mask_array[index]), "\n", cycle, |
| ": bit_array[", index, |
| "] = ", absl::Hex(resource->bit_array()[index])) |
| << std::endl; |
| } |
| arch_state_->set_cycle(cycle); |
| EXPECT_TRUE(resource->IsFree(kAllOnes)); |
| delete resource; |
| } |
| |
| // Same as above, but advances the clock by 7 cycles at a time. |
| TEST_F(ComplexResourceTest, QuadWordBy5) { |
| auto *resource = new ComplexResource(arch_state_, kResourceName, 256); |
| EXPECT_TRUE(resource->IsFree(kAllOnes256)); |
| resource->Acquire(kAllOnes256); |
| EXPECT_FALSE(resource->IsFree(kAllOnes256)); |
| uint64_t mask_array[4] = {0}; |
| uint64_t mask = 0xffff'ffff'ffff'ffff; |
| // Increment cycle by 7. |
| int cycle = 0; |
| for (cycle = 1; cycle < kWindow256; cycle += 7) { |
| int index = (kWindow256 - cycle) / kShiftLimit; |
| int shift_amount = kShiftLimit - (cycle & kShiftMask); |
| if (shift_amount == kShiftLimit) shift_amount = 0; |
| mask_array[index] = mask << shift_amount; |
| EXPECT_FALSE(resource->IsFree(absl::Span<uint64_t>(mask_array))) |
| << absl::StrCat(cycle, ": mask_array[", index, |
| "] = ", absl::Hex(mask_array[index]), "\n", cycle, |
| ": bit_array[", index, |
| "] = ", absl::Hex(resource->bit_array()[index])) |
| << std::endl; |
| arch_state_->set_cycle(cycle); |
| EXPECT_TRUE(resource->IsFree(absl::Span<uint64_t>(mask_array))) |
| << absl::StrCat(cycle, ": mask_array[", index, |
| "] = ", absl::Hex(mask_array[index]), "\n", cycle, |
| ": bit_array[", index, |
| "] = ", absl::Hex(resource->bit_array()[index])) |
| << std::endl; |
| } |
| arch_state_->set_cycle(cycle); |
| EXPECT_TRUE(resource->IsFree(kAllOnes)); |
| delete resource; |
| } |
| |
| } // namespace |