| // 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 "riscv/riscv_csr.h" |
| |
| #include <cstdint> |
| #include <ios> |
| |
| #include "absl/status/status.h" |
| #include "googlemock/include/gmock/gmock.h" |
| #include "mpact/sim/util/memory/flat_demand_memory.h" |
| #include "riscv/riscv_state.h" |
| |
| namespace { |
| |
| #ifndef EXPECT_OK |
| #define EXPECT_OK(x) EXPECT_TRUE(x.ok()) |
| #endif |
| |
| using ::mpact::sim::riscv::RiscV32SimpleCsr; |
| using ::mpact::sim::riscv::RiscVCsrEnum; |
| using ::mpact::sim::riscv::RiscVShadowCsr; |
| using ::mpact::sim::riscv::RiscVState; |
| using ::mpact::sim::riscv::RiscVXlen; |
| using ::mpact::sim::util::FlatDemandMemory; |
| |
| constexpr char kCsrName0[] = "csr0"; |
| constexpr char kCsrName1[] = "csr1"; |
| constexpr uint32_t kAllOnes = 0xffff'ffff; |
| constexpr uint32_t kDeadBeef = 0xdead'beef; |
| constexpr uint32_t kA5 = 0xaaaa'5555; |
| constexpr uint32_t kReadMask = 0x00ff'ff00; |
| constexpr uint32_t kWriteMask = 0x000f'f000; |
| |
| // Test fixture class. |
| class RiscV32CsrTest : public testing::Test { |
| protected: |
| RiscV32CsrTest() { |
| state_ = new RiscVState("test", RiscVXlen::RV32, &memory_); |
| } |
| ~RiscV32CsrTest() override { delete state_; } |
| |
| FlatDemandMemory memory_; |
| RiscVState *state_; |
| }; |
| |
| // Test that the simple csr constructs properly and with the expected values. |
| TEST_F(RiscV32CsrTest, SimpleCsrConstruction) { |
| auto *csr0 = new RiscV32SimpleCsr(kCsrName0, RiscVCsrEnum::kUScratch, |
| kDeadBeef, state_); |
| EXPECT_EQ(csr0->name(), kCsrName0); |
| EXPECT_EQ(csr0->index(), static_cast<int>(RiscVCsrEnum::kUScratch)); |
| EXPECT_EQ(csr0->read_mask(), kAllOnes); |
| EXPECT_EQ(csr0->write_mask(), kAllOnes); |
| csr0->set_read_mask(kReadMask); |
| csr0->set_write_mask(kWriteMask); |
| EXPECT_EQ(csr0->read_mask(), kReadMask); |
| EXPECT_EQ(csr0->write_mask(), kWriteMask); |
| |
| auto *csr1 = new RiscV32SimpleCsr(kCsrName1, RiscVCsrEnum::kMScratch, kA5, |
| kReadMask, kWriteMask, state_); |
| EXPECT_EQ(csr1->name(), kCsrName1); |
| EXPECT_EQ(csr1->index(), static_cast<int>(RiscVCsrEnum::kMScratch)); |
| EXPECT_EQ(csr1->read_mask(), kReadMask); |
| EXPECT_EQ(csr1->write_mask(), kWriteMask); |
| |
| delete csr0; |
| delete csr1; |
| } |
| |
| // Read and write values from/to the csr. |
| TEST_F(RiscV32CsrTest, SimpleCsrReadWrite) { |
| auto *csr = new RiscV32SimpleCsr(kCsrName1, RiscVCsrEnum::kMScratch, kA5, |
| kReadMask, kWriteMask, state_); |
| EXPECT_EQ(csr->AsUint32(), kA5 & kReadMask); |
| csr->Write(kAllOnes); |
| EXPECT_EQ(csr->AsUint32(), (kA5 & kReadMask) | (kAllOnes & kWriteMask)) |
| << std::hex << csr->AsUint32() |
| << " != " << ((kA5 & kReadMask) | (kAllOnes & kWriteMask)); |
| delete csr; |
| } |
| |
| // Raw read/writes. |
| TEST_F(RiscV32CsrTest, SimpleCsrSetGet) { |
| auto *csr = new RiscV32SimpleCsr(kCsrName1, RiscVCsrEnum::kMScratch, kA5, |
| kReadMask, kWriteMask, state_); |
| EXPECT_EQ(csr->GetUint32(), kA5); |
| csr->Set(kAllOnes); |
| EXPECT_EQ(csr->AsUint32(), (kAllOnes & kReadMask)); |
| EXPECT_EQ(csr->GetUint32(), kAllOnes); |
| delete csr; |
| } |
| |
| // Test the csr-set class. |
| TEST_F(RiscV32CsrTest, CsrSet) { |
| // Allocate new csr. |
| auto *csr = new RiscV32SimpleCsr(kCsrName1, RiscVCsrEnum::kUScratch, kA5, |
| kReadMask, kWriteMask, state_); |
| // Add it to the set, then try to add it again. The second attempt should |
| // fail. |
| EXPECT_OK(state_->csr_set()->AddCsr(csr)); |
| EXPECT_EQ(state_->csr_set()->AddCsr(csr).code(), |
| absl::StatusCode::kAlreadyExists); |
| // Read the csr and validate the value. |
| auto result = |
| state_->csr_set()->GetCsr(static_cast<int>(RiscVCsrEnum::kUScratch)); |
| EXPECT_OK(result.status()); |
| auto *stored_csr = result.value(); |
| EXPECT_EQ(stored_csr, csr); |
| EXPECT_EQ(stored_csr->AsUint32(), (kA5 & kReadMask)); |
| // Getting a different csr should fail. |
| EXPECT_EQ(state_->csr_set()->GetCsr(0xffff).status().code(), |
| absl::StatusCode::kNotFound); |
| delete csr; |
| } |
| |
| // Test that the shadow csr constructs properly and with the expected values. |
| TEST_F(RiscV32CsrTest, ShadowCsrConstruction) { |
| auto *csr0 = new RiscV32SimpleCsr(kCsrName0, RiscVCsrEnum::kMScratch, |
| kDeadBeef, state_); |
| EXPECT_EQ(csr0->name(), kCsrName0); |
| EXPECT_EQ(csr0->index(), static_cast<int>(RiscVCsrEnum::kMScratch)); |
| |
| auto *csr1 = new RiscVShadowCsr<uint32_t>( |
| kCsrName1, RiscVCsrEnum::kUScratch, kReadMask, kWriteMask, state_, csr0); |
| EXPECT_EQ(csr1->name(), kCsrName1); |
| EXPECT_EQ(csr1->index(), static_cast<int>(RiscVCsrEnum::kUScratch)); |
| EXPECT_EQ(csr1->read_mask(), kReadMask); |
| EXPECT_EQ(csr1->write_mask(), kWriteMask); |
| |
| EXPECT_EQ(csr1->AsUint32(), csr0->AsUint32() & kReadMask); |
| csr1->Write(kAllOnes); |
| EXPECT_EQ(csr0->AsUint32(), kDeadBeef | (kAllOnes & kWriteMask)); |
| delete csr0; |
| delete csr1; |
| } |
| |
| } // namespace |