blob: 0a739a7bdf54fb7cb5c173dfec5a00d3b25b424e [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <assert.h>
#include <stdio.h>
#include <zircon/types.h>
#include <climits>
#include <limits>
#include <hwreg/bitfields.h>
#include <hwreg/indirect.h>
#include <hwreg/mmio.h>
#include <zxtest/zxtest.h>
// This function exists so that the resulting code can be inspected easily in the
// object file.
[[maybe_unused]] static void compilation_test() {
typedef hwreg::IndirectIo<0x00, 0x04> Io;
volatile struct {
uint32_t index = 0xaa;
uint32_t reserved = 0xff;
uint32_t data = 0x11;
} fake_regs;
auto io = Io(static_cast<volatile void*>(&fake_regs));
io.Write<uint32_t>(1, 0);
io.Read<uint32_t>(0);
}
namespace {
template <typename IntType, typename IndexType>
void basic_access_test() {
typedef struct {
IndexType index;
// Test an indexed register with a reserved field in the middle.
IntType reserved;
IntType data;
} FakeRegs;
volatile FakeRegs fake_regs;
typedef hwreg::IndirectIo<
static_cast<uint32_t>(offsetof(FakeRegs, index)),
static_cast<uint32_t>(offsetof(FakeRegs, data)),
IndexType> Io;
fake_regs.index = 1;
fake_regs.reserved = 3;
fake_regs.data = 2;
Io io(hwreg::RegisterMmio(static_cast<volatile void*>(&fake_regs)));
class Reg : public hwreg::RegisterBase<Reg, IntType> {
public:
static auto Get(uint32_t offset) { return hwreg::RegisterAddr<Reg>(offset); }
};
// Validate that reading from .data works
EXPECT_EQ(2, Reg::Get(0).ReadFrom(&io).reg_value());
EXPECT_EQ(0, fake_regs.index);
EXPECT_EQ(3, fake_regs.reserved);
// That reading from another register updates the index.
fake_regs.data = 6;
EXPECT_EQ(6, Reg::Get(2).ReadFrom(&io).reg_value());
EXPECT_EQ(2, fake_regs.index);
EXPECT_EQ(3, fake_regs.reserved);
// And writing also updates the index.
Reg::Get(0).ReadFrom(&io).set_reg_value(static_cast<IntType>(5)).WriteTo(&io);
EXPECT_EQ(0, fake_regs.index);
EXPECT_EQ(5, fake_regs.data);
EXPECT_EQ(3, fake_regs.reserved);
}
template <typename IntType>
void single_thread_test() {
basic_access_test<IntType, IntType>();
basic_access_test<IntType, uint8_t>();
}
TEST(SingleThreadTestCase, Uint8) { ASSERT_NO_FAILURES(single_thread_test<uint8_t>()); }
TEST(SingleThreadTestCase, Uint16) { ASSERT_NO_FAILURES(single_thread_test<uint16_t>()); }
TEST(SingleThreadTestCase, Uint32) { ASSERT_NO_FAILURES(single_thread_test<uint32_t>()); }
TEST(SingleThreadTestCase, Uint64) { ASSERT_NO_FAILURES(single_thread_test<uint64_t>()); }
TEST(AlignedAccessTest, Uint32) {
typedef struct {
uint16_t index;
uint16_t reserved;
uint32_t data;
} FakeRegs;
volatile FakeRegs fake_regs;
typedef hwreg::IndirectIo<offsetof(FakeRegs, index), offsetof(FakeRegs, data), uint16_t> Io;
fake_regs.index = 0xaaaa;
fake_regs.reserved = 0xffff;
fake_regs.data = 0x12345678;
Io io(hwreg::RegisterMmio(static_cast<volatile void*>(&fake_regs)));
class MatchingReg : public hwreg::RegisterBase<MatchingReg, uint32_t> {
public:
static auto Get(uint32_t offset) { return hwreg::RegisterAddr<MatchingReg>(offset); }
};
EXPECT_EQ(0x12345678, MatchingReg::Get(0).ReadFrom(&io).reg_value());
class SmallReg : public hwreg::RegisterBase<SmallReg, uint16_t> {
public:
static auto Get(uint32_t offset) { return hwreg::RegisterAddr<SmallReg>(offset); }
};
EXPECT_EQ(fake_regs.data & 0xffff, SmallReg::Get(0).ReadFrom(&io).reg_value());
}
} // namespace