blob: 6d4df822a2324cdac47b71055acb04ff50964d00 [file] [log] [blame]
// Copyright 2017 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.
#pragma once
#include <fbl/algorithm.h>
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <zircon/assert.h>
#include <type_traits>
namespace hwreg {
struct EnablePrinter;
namespace internal {
template <typename T> struct IsSupportedInt : std::false_type {};
template <> struct IsSupportedInt<uint8_t> : std::true_type {};
template <> struct IsSupportedInt<uint16_t> : std::true_type {};
template <> struct IsSupportedInt<uint32_t> : std::true_type {};
template <> struct IsSupportedInt<uint64_t> : std::true_type {};
template <class IntType> constexpr IntType ComputeMask(uint32_t num_bits) {
if (num_bits == sizeof(IntType) * CHAR_BIT) {
return static_cast<IntType>(~0ull);
}
return static_cast<IntType>((static_cast<IntType>(1) << num_bits) - 1);
}
class FieldPrinter {
public:
constexpr FieldPrinter() : name_(nullptr), bit_high_incl_(0), bit_low_(0) {
}
constexpr FieldPrinter(const char* name, uint32_t bit_high_incl, uint32_t bit_low)
: name_(name), bit_high_incl_(bit_high_incl), bit_low_(bit_low) {
}
// Prints the field name, and the result of extracting the field from |value| in
// hex (with a left-padding of zeroes to a length matching the maximum number of
// nibbles needed to represent any value the field could take).
void Print(uint64_t value, char* buf, size_t len) const;
private:
const char* name_;
uint32_t bit_high_incl_;
uint32_t bit_low_;
};
// Structure used to reduce the storage cost of the pretty-printing features if
// they are not enabled.
template <typename T, typename IntType> struct FieldPrinterList {
void AppendField(const char* name, uint32_t bit_high_incl, uint32_t bit_low) {
}
};
template <typename IntType> struct FieldPrinterList<EnablePrinter, IntType> {
// These two members are used for implementing the Print() function above.
// They will typically be optimized away if Print() is not used.
FieldPrinter fields[sizeof(IntType) * CHAR_BIT];
unsigned num_fields = 0;
void AppendField(const char* name, uint32_t bit_high_incl, uint32_t bit_low) {
ZX_DEBUG_ASSERT(num_fields < fbl::count_of(fields));
fields[num_fields++] = FieldPrinter(name, bit_high_incl, bit_low);
}
};
// Used to record information about a field at construction time. This enables
// checking for overlapping fields and pretty-printing.
template <class RegType> class Field {
private:
using IntType = typename RegType::ValueType;
public:
Field(RegType* reg, const char* name, uint32_t bit_high_incl, uint32_t bit_low) {
IntType mask = static_cast<IntType>(
internal::ComputeMask<IntType>(bit_high_incl - bit_low + 1) << bit_low);
// Check for overlapping bit ranges
ZX_DEBUG_ASSERT((reg->fields_mask_ & mask) == 0ull);
reg->fields_mask_ = static_cast<IntType>(reg->fields_mask_ | mask);
reg->printer_.AppendField(name, bit_high_incl, bit_low);
}
};
// Used to record information about reserved-zero fields at construction time.
// This enables auto-zeroing of reserved-zero fields on register write.
// Represents a field that must be zeroed on write.
template <class RegType> class RsvdZField {
private:
using IntType = typename RegType::ValueType;
public:
RsvdZField(RegType* reg, uint32_t bit_high_incl, uint32_t bit_low) {
IntType mask = static_cast<IntType>(
internal::ComputeMask<IntType>(bit_high_incl - bit_low + 1) << bit_low);
reg->rsvdz_mask_ = static_cast<IntType>(reg->rsvdz_mask_ | mask);
}
};
// Implementation for RegisterBase::Print, see the documentation there.
// |reg_value| is the current value of the register.
// |fields_mask| is a bitmask with a bit set for each bit that has been defined
// in the register.
template <typename F>
void PrintRegister(F print_fn,
FieldPrinter fields[], size_t num_fields,
uint64_t reg_value, uint64_t fields_mask,
int register_width_bytes) {
char buf[128];
for (unsigned i = 0; i < num_fields; ++i) {
fields[i].Print(reg_value, buf, sizeof(buf));
print_fn(buf);
}
// Check if any unknown bits are set, and if so let the caller know
uint64_t val = reg_value & ~fields_mask;
if (val != 0) {
int pad_len = (register_width_bytes * CHAR_BIT) / 4;
snprintf(buf, sizeof(buf), "unknown set bits: 0x%0*" PRIx64, pad_len, val);
buf[sizeof(buf) - 1] = 0;
print_fn(buf);
}
}
// Utility for the common print function of [](const char* arg) { printf("%s\n", arg); }
void PrintRegisterPrintf(FieldPrinter fields[], size_t num_fields,
uint64_t reg_value, uint64_t fields_mask,
int register_width_bytes);
} // namespace internal
} // namespace hwreg