mmio: separate MMIO access and register logic

By separating the plain MMIO access implementation from the generic bit
and offset logic of registers, we can now use the latter also with other
types of register access like I2C. The register and MMIO front-ends have
not changed due to the separation.

Ref #2196
This commit is contained in:
Martin Stein 2016-12-09 12:18:18 +01:00 committed by Christian Helmuth
parent 25ac3cdd86
commit 442c24420d
2 changed files with 849 additions and 560 deletions

View File

@ -1,5 +1,5 @@
/*
* \brief Generic MMIO access framework
* \brief Type-safe, fine-grained access to a continuous MMIO region
* \author Martin stein
* \date 2011-10-26
*/
@ -15,590 +15,81 @@
#define _INCLUDE__UTIL__MMIO_H_
/* Genode includes */
#include <util/register.h>
#include <util/register_set.h>
namespace Genode { class Mmio; }
namespace Genode {
class Mmio_plain_access;
class Mmio;
}
/**
* A continuous MMIO region
*
* For correct behavior of the methods of 'Mmio', a class that
* derives from one of the subclasses of 'Mmio' must not define members
* named 'Register_base', 'Bitfield_base', 'Register_array_base' or
* 'Array_bitfield_base'.
* Plain access implementation for MMIO
*/
class Genode::Mmio
class Genode::Mmio_plain_access
{
/**
* Write '_ACCESS_T' typed 'value' to MMIO base + 'o'
*/
template <typename _ACCESS_T>
inline void _write(off_t const o, _ACCESS_T const value)
{
addr_t const dst = (addr_t)base + o;
*(_ACCESS_T volatile *)dst = value;
}
/**
* Read '_ACCESS_T' typed from MMIO base + 'o'
*/
template <typename _ACCESS_T>
inline _ACCESS_T _read(off_t const o) const
{
addr_t const dst = (addr_t)base + o;
_ACCESS_T const value = *(_ACCESS_T volatile *)dst;
return value;
}
friend Register_set_plain_access;
public:
enum { BYTE_WIDTH_LOG2 = 3, BYTE_WIDTH = 1 << BYTE_WIDTH_LOG2 };
/**
* FIXME We keep this public only to stay interface compatible
* but the value should be accessible only through an
* accessor.
*/
addr_t const base;
private:
/**
* An integer like region within a MMIO region.
*
* \param _OFFSET Offset of the region relative to the
* base of the compound MMIO.
* \param _ACCESS_WIDTH Bit width of the region, for a list of
* supported widths see 'Genode::Register'.
* \param _STRICT_WRITE If set to 0, when writing a bitfield, we
* read the register value, update the bits
* on it, and write it back to the register.
* If set to 1 we take an empty register
* value instead, apply the bitfield on it,
* and write it to the register. This can
* be useful if you have registers that have
* different means on reads and writes.
*
* For further details See 'Genode::Register'.
* Write '_ACCESS_T' typed 'value' to MMIO base + 'offset'
*/
template <off_t _OFFSET, unsigned long _ACCESS_WIDTH,
bool _STRICT_WRITE = false>
struct Register : public Genode::Register<_ACCESS_WIDTH>
template <typename ACCESS_T>
inline void _write(off_t const offset, ACCESS_T const value)
{
enum {
OFFSET = _OFFSET,
ACCESS_WIDTH = _ACCESS_WIDTH,
STRICT_WRITE = _STRICT_WRITE,
};
/*
* GCC 4.4, in contrast to GCC versions >= 4.5, can't
* select function templates like 'write(typename
* T::Register::access_t value)' through a given 'T'
* that, in this case, derives from 'Register<X, Y, Z>'.
* It seems this is due to the fact that 'T::Register'
* is a template. Thus we provide some kind of stamp
* that solely must not be redefined by the deriving
* class to ensure correct template selection.
*/
typedef Register<_OFFSET, _ACCESS_WIDTH, _STRICT_WRITE>
Register_base;
/**
* A region within a register
*
* \param _SHIFT Bit shift of the first bit within the
* compound register.
* \param _WIDTH bit width of the region
*
* For details see 'Genode::Register::Bitfield'.
*/
template <unsigned long _SHIFT, unsigned long _WIDTH>
struct Bitfield : public Genode::Register<ACCESS_WIDTH>::
template Bitfield<_SHIFT, _WIDTH>
{
/* analogous to 'Mmio::Register::Register_base' */
typedef Bitfield<_SHIFT, _WIDTH> Bitfield_base;
/* back reference to containing register */
typedef Register<_OFFSET, _ACCESS_WIDTH, _STRICT_WRITE>
Compound_reg;
};
};
addr_t const dst = base + offset;
*(ACCESS_T volatile *)dst = value;
}
/**
* An array of successive equally structured regions, called items
*
* \param _OFFSET Offset of the first item relative to
* the base of the compound MMIO.
* \param _ACCESS_WIDTH Bit width of a single access, must be at
* least the item width.
* \param _ITEMS How many times the item gets iterated
* successively.
* \param _ITEM_WIDTH bit width of an item
* \param _STRICT_WRITE If set to 0, when writing a bitfield, we
* read the register value, update the bits
* on it, and write it back to the register.
* If set to 1, we take an empty register
* value instead, apply the bitfield on it,
* and write it to the register. This can
* be useful if you have registers that have
* different means on reads and writes.
* Please note that ACCESS_WIDTH is decisive
* for the range of such strictness.
*
* The array takes all inner structures, wich are covered by an
* item width and iterates them successively. Such structures that
* are partially exceed an item range are read and written also
* partially. Structures that are completely out of the item range
* are read as '0' and trying to overwrite them has no effect. The
* array is not limited to its access width, it extends to the
* memory region of its successive items. Trying to read out read
* with an item index out of the array range returns '0', trying
* to write to such indices has no effect.
* Read '_ACCESS_T' typed from MMIO base + 'offset'
*/
template <off_t _OFFSET, unsigned long _ACCESS_WIDTH,
unsigned long _ITEMS, unsigned long _ITEM_WIDTH,
bool _STRICT_WRITE = false>
struct Register_array : public Register<_OFFSET, _ACCESS_WIDTH,
_STRICT_WRITE>
template <typename ACCESS_T>
inline ACCESS_T _read(off_t const &offset) const
{
typedef typename Trait::Uint_width<_ACCESS_WIDTH>::
template Divisor<_ITEM_WIDTH> Item;
addr_t const dst = base + offset;
ACCESS_T const value = *(ACCESS_T volatile *)dst;
return value;
}
enum {
STRICT_WRITE = _STRICT_WRITE,
OFFSET = _OFFSET,
ACCESS_WIDTH = _ACCESS_WIDTH,
ITEMS = _ITEMS,
ITEM_WIDTH = _ITEM_WIDTH,
ITEM_WIDTH_LOG2 = Item::WIDTH_LOG2,
MAX_INDEX = ITEMS - 1,
ITEM_MASK = (1ULL << ITEM_WIDTH) - 1,
};
/* analogous to 'Mmio::Register::Register_base' */
typedef Register_array<OFFSET, ACCESS_WIDTH, ITEMS,
ITEM_WIDTH, STRICT_WRITE>
Register_array_base;
typedef typename Register<OFFSET, ACCESS_WIDTH, STRICT_WRITE>::
access_t access_t;
/**
* A bitregion within a register array item
*
* \param _SHIFT bit shift of the first bit within an item
* \param _WIDTH bit width of the region
*
* For details see 'Genode::Register::Bitfield'.
*/
template <unsigned long _SHIFT, unsigned long _SIZE>
struct Bitfield :
public Register<OFFSET, ACCESS_WIDTH, STRICT_WRITE>::
template Bitfield<_SHIFT, _SIZE>
{
/* analogous to 'Mmio::Register::Register_base' */
typedef Bitfield<_SHIFT, _SIZE> Array_bitfield_base;
/* back reference to containing register array */
typedef Register_array<OFFSET, ACCESS_WIDTH, ITEMS,
ITEM_WIDTH, STRICT_WRITE>
Compound_array;
};
/**
* Calculate destination of an array-item access
*
* \param offset Gets overridden with the offset of the
* access type instance, that contains the
* access destination, relative to the MMIO
* base.
* \param shift Gets overridden with the shift of the
* destination within the access type instance
* targeted by 'offset'.
* \param index index of the targeted array item
*/
static inline void dst(off_t & offset,
unsigned long & shift,
unsigned long const index)
{
unsigned long const bit_off = index << ITEM_WIDTH_LOG2;
offset = (off_t) ((bit_off >> BYTE_WIDTH_LOG2)
& ~(sizeof(access_t)-1) );
shift = bit_off - ( offset << BYTE_WIDTH_LOG2 );
offset += OFFSET;
}
/**
* Calc destination of a simple array-item access without shift
*
* \param offset gets overridden with the offset of the the
* access destination, relative to the MMIO base
* \param index index of the targeted array item
*/
static inline void simple_dst(off_t & offset,
unsigned long const index)
{
offset = (index << ITEM_WIDTH_LOG2) >> BYTE_WIDTH_LOG2;
offset += OFFSET;
}
};
addr_t const base; /* base address of targeted MMIO region */
public:
/**
* Constructor
*
* \param mmio_base base address of targeted MMIO region
* \param base base address of targeted MMIO region
*/
inline Mmio(addr_t mmio_base) : base(mmio_base) { }
Mmio_plain_access(addr_t const base) : base(base) { }
};
/*************************
** Access to registers **
*************************/
/**
* Get the address of the register 'T' typed as its access type
*/
template <typename T>
inline typename T::Register_base::access_t volatile * typed_addr() const
{
typedef typename T::Register_base Register;
typedef typename Register::access_t access_t;
return (access_t volatile *)(base + Register::OFFSET);
}
/**
* Read the register 'T'
*/
template <typename T>
inline typename T::Register_base::access_t read() const
{
typedef typename T::Register_base Register;
typedef typename Register::access_t access_t;
return _read<access_t>(Register::OFFSET);
}
/**
* Override the register 'T'
*/
template <typename T>
inline void
write(typename T::Register_base::access_t const value)
{
typedef typename T::Register_base Register;
typedef typename Register::access_t access_t;
_write<access_t>(Register::OFFSET, value);
}
/******************************************
** Access to bitfields within registers **
******************************************/
/**
* Read the bitfield 'T' of a register
*/
template <typename T>
inline typename T::Bitfield_base::Compound_reg::access_t
read() const
{
typedef typename T::Bitfield_base Bitfield;
typedef typename Bitfield::Compound_reg Register;
typedef typename Register::access_t access_t;
return Bitfield::get(_read<access_t>(Register::OFFSET));
}
/**
* Override to the bitfield 'T' of a register
*
* \param value value that shall be written
*/
template <typename T>
inline void
write(typename T::Bitfield_base::Compound_reg::access_t const value)
{
typedef typename T::Bitfield_base Bitfield;
typedef typename Bitfield::Compound_reg Register;
typedef typename Register::access_t access_t;
/* initialize the pattern written finally to the register */
access_t write_value;
if (Register::STRICT_WRITE)
{
/* apply the bitfield to an empty write pattern */
write_value = 0;
} else {
/* apply the bitfield to the old register value */
write_value = read<Register>();
Bitfield::clear(write_value);
}
/* apply bitfield value and override register */
Bitfield::set(write_value, value);
write<Register>(write_value);
}
/*******************************
** Access to register arrays **
*******************************/
/**
* Read an item of the register array 'T'
*
* \param index index of the targeted item
*/
template <typename T>
inline typename T::Register_array_base::access_t
read(unsigned long const index) const
{
typedef typename T::Register_array_base Array;
typedef typename Array::access_t access_t;
/* reads outside the array return 0 */
if (index > Array::MAX_INDEX) return 0;
/* if item width equals access width we optimize the access */
off_t offset;
if (Array::ITEM_WIDTH == Array::ACCESS_WIDTH) {
Array::simple_dst(offset, index);
return _read<access_t>(offset);
/* access width and item width differ */
} else {
long unsigned shift;
Array::dst(offset, shift, index);
return (_read<access_t>(offset) >> shift) &
Array::ITEM_MASK;
}
}
/**
* Override an item of the register array 'T'
*
* \param value value that shall be written
* \param index index of the targeted item
*/
template <typename T>
inline void
write(typename T::Register_array_base::access_t const value,
unsigned long const index)
{
typedef typename T::Register_array_base Array;
typedef typename Array::access_t access_t;
/* ignore writes outside the array */
if (index > Array::MAX_INDEX) return;
/* optimize the access if item width equals access width */
off_t offset;
if (Array::ITEM_WIDTH == Array::ACCESS_WIDTH) {
Array::simple_dst(offset, index);
_write<access_t>(offset, value);
/* access width and item width differ */
} else {
long unsigned shift;
Array::dst(offset, shift, index);
/* insert new value into old register value */
access_t write_value;
if (Array::STRICT_WRITE)
{
/* apply bitfield to an empty write pattern */
write_value = 0;
} else {
/* apply bitfield to the old register value */
write_value = _read<access_t>(offset);
write_value &= ~(Array::ITEM_MASK << shift);
}
/* apply bitfield value and override register */
write_value |= (value & Array::ITEM_MASK) << shift;
_write<access_t>(offset, write_value);
}
}
/*****************************************************
** Access to bitfields within register array items **
*****************************************************/
/**
* Read the bitfield 'T' of a register array
*
* \param index index of the targeted item
*/
template <typename T>
inline typename T::Array_bitfield_base::Compound_array::access_t
read(unsigned long const index) const
{
typedef typename T::Array_bitfield_base Bitfield;
typedef typename Bitfield::Compound_array Array;
return Bitfield::get(read<Array>(index));
}
/**
* Override the bitfield 'T' of a register array
*
* \param value value that shall be written
* \param index index of the targeted array item
*/
template <typename T>
inline void
write(typename T::Array_bitfield_base::Compound_array::access_t const value,
long unsigned const index)
{
typedef typename T::Array_bitfield_base Bitfield;
typedef typename Bitfield::Compound_array Array;
typedef typename Array::access_t access_t;
/* initialize the pattern written finally to the register */
access_t write_value;
if (Array::STRICT_WRITE)
{
/* apply the bitfield to an empty write pattern */
write_value = 0;
} else {
/* apply the bitfield to the old register value */
write_value = read<Array>(index);
Bitfield::clear(write_value);
}
/* apply bitfield value and override register */
Bitfield::set(write_value, value);
write<Array>(write_value, index);
}
/***********************
** Access to bitsets **
***********************/
/**
* Read bitset 'T' (composed of 2 parts)
*/
template <typename T>
inline typename T::Bitset_2_base::access_t const read()
{
typedef typename T::Bitset_2_base::Bits_0 Bits_0;
typedef typename T::Bitset_2_base::Bits_1 Bits_1;
typedef typename T::Bitset_2_base::access_t access_t;
enum { V1_SHIFT = Bits_0::BITFIELD_WIDTH };
access_t const v0 = read<Bits_0>();
access_t const v1 = read<Bits_1>();
return v0 | (v1 << V1_SHIFT);
}
/**
* Override bitset 'T' (composed of 2 parts)
*
* \param v value that shall be written
*/
template <typename T>
inline void write(typename T::Bitset_2_base::access_t v)
{
typedef typename T::Bitset_2_base::Bits_0 Bits_0;
typedef typename T::Bitset_2_base::Bits_1 Bits_1;
write<Bits_0>(v);
write<Bits_1>(v >> Bits_0::BITFIELD_WIDTH);
}
/**
* Read bitset 'T' (composed of 3 parts)
*/
template <typename T>
inline typename T::Bitset_3_base::access_t const read()
{
typedef typename T::Bitset_3_base::Bits_0 Bits_0;
typedef typename T::Bitset_3_base::Bits_1 Bits_1;
typedef typename T::Bitset_3_base::Bits_2 Bits_2;
typedef typename T::Bitset_3_base::access_t access_t;
enum {
BITS_0_WIDTH = Bits_0::BITFIELD_WIDTH,
BITS_1_WIDTH = Bits_1::BITFIELD_WIDTH,
V1_SHIFT = BITS_0_WIDTH + BITS_1_WIDTH,
};
access_t const v0 = read<Bitset_2<Bits_0, Bits_1> >();
access_t const v1 = read<Bits_2>();
return v0 | (v1 << V1_SHIFT);
}
/**
* Override bitset 'T' (composed of 3 parts)
*
* \param v value that shall be written
*/
template <typename T>
inline void write(typename T::Bitset_3_base::access_t v)
{
typedef typename T::Bitset_3_base::Bits_0 Bits_0;
typedef typename T::Bitset_3_base::Bits_1 Bits_1;
typedef typename T::Bitset_3_base::Bits_2 Bits_2;
write<Bitset_2<Bits_0, Bits_1> >(v);
write<Bits_2>(v >> (Bits_0::BITFIELD_WIDTH +
Bits_1::BITFIELD_WIDTH));
}
/*********************************
** Polling for bitfield states **
*********************************/
/**
* Interface for delaying the execution of a calling thread
*/
struct Delayer
{
/**
* Delay execution of the caller for 'us' microseconds
*/
virtual void usleep(unsigned us) = 0;
};
/**
* Wait until register 'T' contains the specified 'value'
*
* \param value value to wait for
* \param delayer sleeping facility to be used when the
* value is not reached yet
* \param max_attempts number of register probing attempts
* \param us number of microseconds between attempts
*/
template <typename T>
inline bool
wait_for(typename T::Register_base::access_t const value,
Delayer & delayer,
unsigned max_attempts = 500,
unsigned us = 1000)
{
typedef typename T::Register_base Register;
for (unsigned i = 0; i < max_attempts; i++, delayer.usleep(us))
{
if (read<Register>() == value) return true;
}
return false;
}
/**
* Wait until bitfield 'T' contains the specified 'value'
*
* \param value value to wait for
* \param delayer sleeping facility to be used when the
* value is not reached yet
* \param max_attempts number of bitfield probing attempts
* \param us number of microseconds between attempts
*/
template <typename T>
inline bool
wait_for(typename T::Bitfield_base::Compound_reg::access_t const value,
Delayer & delayer,
unsigned max_attempts = 500,
unsigned us = 1000)
{
typedef typename T::Bitfield_base Bitfield;
for (unsigned i = 0; i < max_attempts; i++, delayer.usleep(us))
{
if (read<Bitfield>() == value) return true;
}
return false;
}
/**
* Type-safe, fine-grained access to a continuous MMIO region
*
* For further details refer to the documentation of the 'Register_set' class.
*/
struct Genode::Mmio : Mmio_plain_access, Register_set<Mmio_plain_access>
{
/**
* Constructor
*
* \param base base address of targeted MMIO region
*/
Mmio(addr_t const base)
:
Mmio_plain_access(base),
Register_set(*static_cast<Mmio_plain_access *>(this)) { }
};
#endif /* _INCLUDE__UTIL__MMIO_H_ */

View File

@ -0,0 +1,798 @@
/*
* \brief Set of fine-grained and typesafe accessible registers with offsets
* \author Martin stein
* \date 2011-10-26
*/
/*
* Copyright (C) 2011-2017 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _INCLUDE__UTIL__REGISTER_SET_H_
#define _INCLUDE__UTIL__REGISTER_SET_H_
/* Genode includes */
#include <util/register.h>
#include <base/exception.h>
namespace Genode {
struct Register_set_plain_access;
template <typename> class Register_set;
}
/**
* Interface declaration for implementations of plain access to a register set
*
* This class enables us to use the bit and offset logic of the Register_set
* template with different plain access implementations and, at the same time,
* keep the plain access implementation invisible at the front-end. The class
* circumvents the fact that we can't have virtual method templates nor
* template friends. The plain access implementation should keep its methods
* private and declare this class as friend. It is then handed over as
* argument to the Register_set template.
*/
struct Genode::Register_set_plain_access
{
/**
* Write on plain integer level to a register of the set
*
* \param ACCESS_T plain integer type of the register to access
* \param IMPLEMENTATION implementation of access on the plain integer level
* \param impl instance of the access implementation
* \param offset register offset
* \param value value to be written to the register
*/
template <typename ACCESS_T, typename IMPLEMENTATION>
static inline
void write(IMPLEMENTATION &impl, off_t const offset, ACCESS_T const value) {
impl.template _write<ACCESS_T>(offset, value); }
/**
* Read on plain integer level from a register of the set
*
* \param ACCESS_T plain integer type of the register to access
* \param IMPLEMENTATION implementation of access on the plain integer level
* \param impl instance of the access implementation
* \param offset register offset
* \param value value to be written to the register
*/
template <typename ACCESS_T, typename IMPLEMENTATION>
static inline ACCESS_T read(IMPLEMENTATION &impl, off_t const offset) {
return impl.template _read<ACCESS_T>(offset); }
};
/**
* Set of fine-grained and typesafe accessible registers with offsets
*
* \param PLAIN_ACCESS Implementation of access on the plain integer level.
* Should derive from the Register_set_plain_access
* interface.
*
* A register set consists of individual registers and register arrays with
* different offsets. The class receives the specific implementation of plain
* access as argument. This way, it can be used with different types of IO
* (MMIO, I2C, Port IO, ...) . For correct behavior of the 'Register_set'
* methods, a class that derives from one of the subclasses of 'Register_set'
* must not define members named 'Register_base', 'Bitfield_base',
* 'Register_array_base' or 'Array_bitfield_base'.
*/
template <typename PLAIN_ACCESS>
class Genode::Register_set
{
private:
using Plain_access = Register_set_plain_access;
enum { BYTE_WIDTH_LOG2 = 3, BYTE_WIDTH = 1 << BYTE_WIDTH_LOG2 };
/**
* Return wether one IO condition is met
*
* \param CONDITION A condition subtype of Register, Bitfield, or
* such (for example the Bitfield::Equal type)
* \param condition the condition instance
*/
template <typename CONDITION>
inline bool _conditions_met(CONDITION condition) {
return condition.met(read<typename CONDITION::Object>()); }
/**
* Return wether a list of IO conditions is met
*
* \param CONDITION The type of the head of the condition list.
* A condition subtype of Register, Bitfield, or
* such (for example the Bitfield::Equal type).
* \param CONDITIONS The types of the tail of the condition list.
* condition subtypes of Register, Bitfield, or
* such (for example the Bitfield::Equal type)
* \param head the condition instance
*/
template <typename CONDITION, typename... CONDITIONS>
inline bool _conditions_met(CONDITION head, CONDITIONS... tail) {
return _conditions_met(head) ? _conditions_met(tail...) : false; }
/**
* This template equips registers/bitfields with conditions for polling
*
* \param T register/bitfield type that the conditions shall be about
*
* The condition subtypes enable us to poll for a variable amount of
* conditions for different registers/bitfields at once. They are used
* as input to the 'wait_for' template.
*/
template <typename T>
struct Conditions
{
/**
* Condition that the register/bitfield equals a value
*/
class Equal
{
private:
typedef typename T::access_t access_t;
access_t const _reference_val;
public:
typedef T Object;
/**
* Constructor
*
* \param reference_val reference value
*/
explicit Equal(access_t const reference_val)
: _reference_val(reference_val) { }
/**
* Return whether the condition is met
*
* \param actual_val actual regster/bitfield value
*/
bool met(access_t const actual_val) const {
return _reference_val == actual_val; }
};
};
PLAIN_ACCESS &_plain_access;
public:
/**
* An integer like region at a specific place within a register set
*
* \param _OFFSET Offset of the region in the register set
* \param _ACCESS_WIDTH Bit width of the region, for a list of
* supported widths see 'Genode::Register'.
* \param _STRICT_WRITE If set to 0, when writing a bitfield, we
* read the register value, update the bits
* on it, and write it back to the register.
* If set to 1 we take an empty register
* value instead, apply the bitfield on it,
* and write it to the register. This can
* be useful if you have registers that have
* different means on reads and writes.
*
* For further details See 'Genode::Register'.
*/
template <off_t _OFFSET, unsigned long _ACCESS_WIDTH,
bool _STRICT_WRITE = false>
struct Register
:
public Genode::Register<_ACCESS_WIDTH>,
public Conditions<Register<_OFFSET, _ACCESS_WIDTH, _STRICT_WRITE> >
{
enum {
OFFSET = _OFFSET,
ACCESS_WIDTH = _ACCESS_WIDTH,
STRICT_WRITE = _STRICT_WRITE,
};
/*
* GCC 4.4, in contrast to GCC versions >= 4.5, can't
* select function templates like 'write(typename
* T::Register::access_t value)' through a given 'T'
* that, in this case, derives from 'Register<X, Y, Z>'.
* It seems this is due to the fact that 'T::Register'
* is a template. Thus we provide some kind of stamp
* that solely must not be redefined by the deriving
* class to ensure correct template selection.
*/
typedef Register<_OFFSET, _ACCESS_WIDTH, _STRICT_WRITE>
Register_base;
typedef typename Genode::Register<_ACCESS_WIDTH>::access_t
access_t;
/**
* A region within a register
*
* \param _SHIFT Bit shift of the first bit within the
* compound register.
* \param _WIDTH bit width of the region
*
* For details see 'Genode::Register::Bitfield'.
*/
template <unsigned long _SHIFT, unsigned long _WIDTH>
struct Bitfield
:
public Genode::Register<ACCESS_WIDTH>::
template Bitfield<_SHIFT, _WIDTH>,
public Conditions<Bitfield<_SHIFT, _WIDTH> >
{
/* analogous to 'Register_set::Register::Register_base' */
typedef Bitfield<_SHIFT, _WIDTH> Bitfield_base;
/* back reference to containing register */
typedef Register<_OFFSET, _ACCESS_WIDTH, _STRICT_WRITE>
Compound_reg;
typedef Compound_reg::access_t access_t;
};
};
/**
* An array of successive equally structured regions, called items
*
* \param _OFFSET Offset of the first item in the register set
* \param _ACCESS_WIDTH Bit width of a single access, must be at
* least the item width.
* \param _ITEMS How many times the item gets iterated
* successively.
* \param _ITEM_WIDTH bit width of an item
* \param _STRICT_WRITE If set to 0, when writing a bitfield, we
* read the register value, update the bits
* on it, and write it back to the register.
* If set to 1, we take an empty register
* value instead, apply the bitfield on it,
* and write it to the register. This can
* be useful if you have registers that have
* different means on reads and writes.
* Please note that ACCESS_WIDTH is decisive
* for the range of such strictness.
*
* The array takes all inner structures, wich are covered by an
* item width and iterates them successively. Such structures that
* are partially exceed an item range are read and written also
* partially. Structures that are completely out of the item range
* are read as '0' and trying to overwrite them has no effect. The
* array is not limited to its access width, it extends to the
* memory region of its successive items. Trying to read out read
* with an item index out of the array range returns '0', trying
* to write to such indices has no effect.
*/
template <off_t _OFFSET, unsigned long _ACCESS_WIDTH,
unsigned long _ITEMS, unsigned long _ITEM_WIDTH,
bool _STRICT_WRITE = false>
struct Register_array : public Register<_OFFSET, _ACCESS_WIDTH,
_STRICT_WRITE>
{
typedef typename Trait::Uint_width<_ACCESS_WIDTH>::
template Divisor<_ITEM_WIDTH> Item;
enum {
STRICT_WRITE = _STRICT_WRITE,
OFFSET = _OFFSET,
ACCESS_WIDTH = _ACCESS_WIDTH,
ITEMS = _ITEMS,
ITEM_WIDTH = _ITEM_WIDTH,
ITEM_WIDTH_LOG2 = Item::WIDTH_LOG2,
MAX_INDEX = ITEMS - 1,
ITEM_MASK = (1ULL << ITEM_WIDTH) - 1,
};
/* analogous to 'Register_set::Register::Register_base' */
typedef Register_array<OFFSET, ACCESS_WIDTH, ITEMS,
ITEM_WIDTH, STRICT_WRITE>
Register_array_base;
typedef typename Register<OFFSET, ACCESS_WIDTH, STRICT_WRITE>::
access_t access_t;
/**
* A bit region within a register array item
*
* \param _SHIFT bit shift of the first bit within an item
* \param _WIDTH bit width of the region
*
* For details see 'Genode::Register::Bitfield'.
*/
template <unsigned long _SHIFT, unsigned long _SIZE>
struct Bitfield :
public Register<OFFSET, ACCESS_WIDTH, STRICT_WRITE>::
template Bitfield<_SHIFT, _SIZE>
{
/* analogous to 'Register_set::Register::Register_base' */
typedef Bitfield<_SHIFT, _SIZE> Array_bitfield_base;
/* back reference to containing register array */
typedef Register_array<OFFSET, ACCESS_WIDTH, ITEMS,
ITEM_WIDTH, STRICT_WRITE>
Compound_array;
};
/**
* Calculate destination of an array-item access
*
* \param offset Gets overridden with the offset of the
* access type instance, that contains the
* access destination
* \param shift Gets overridden with the shift of the
* destination within the access type instance
* targeted by 'offset'.
* \param index index of the targeted array item
*/
static inline void dst(off_t & offset,
unsigned long & shift,
unsigned long const index)
{
unsigned long const bit_off = index << ITEM_WIDTH_LOG2;
offset = (off_t) ((bit_off >> BYTE_WIDTH_LOG2)
& ~(sizeof(access_t)-1) );
shift = bit_off - ( offset << BYTE_WIDTH_LOG2 );
offset += OFFSET;
}
/**
* Calc destination of a simple array-item access without shift
*
* \param offset gets overridden with the offset of the the
* access destination
* \param index index of the targeted array item
*/
static inline void simple_dst(off_t & offset,
unsigned long const index)
{
offset = (index << ITEM_WIDTH_LOG2) >> BYTE_WIDTH_LOG2;
offset += OFFSET;
}
};
/**
* Constructor
*
* \param plain_access implementation of plain register access
*/
Register_set(PLAIN_ACCESS &plain_access)
: _plain_access(plain_access) { }
/*************************
** Access to registers **
*************************/
/**
* Read the register 'T'
*/
template <typename T>
inline typename T::Register_base::access_t read() const
{
typedef typename T::Register_base Register;
typedef typename Register::access_t access_t;
return Plain_access::read<access_t>(_plain_access,
Register::OFFSET);
}
/**
* Override the register 'T'
*/
template <typename T>
inline void
write(typename T::Register_base::access_t const value)
{
typedef typename T::Register_base Register;
typedef typename Register::access_t access_t;
Plain_access::write<access_t>(_plain_access, Register::OFFSET,
value);
}
/******************************************
** Access to bitfields within registers **
******************************************/
/**
* Read the bitfield 'T' of a register
*/
template <typename T>
inline typename T::Bitfield_base::Compound_reg::access_t
read() const
{
typedef typename T::Bitfield_base Bitfield;
typedef typename Bitfield::Compound_reg Register;
typedef typename Register::access_t access_t;
return
Bitfield::get(Plain_access::read<access_t>(_plain_access,
Register::OFFSET));
}
/**
* Override to the bitfield 'T' of a register
*
* \param value value that shall be written
*/
template <typename T>
inline void
write(typename T::Bitfield_base::Compound_reg::access_t const value)
{
typedef typename T::Bitfield_base Bitfield;
typedef typename Bitfield::Compound_reg Register;
typedef typename Register::access_t access_t;
/* initialize the pattern written finally to the register */
access_t write_value;
if (Register::STRICT_WRITE)
{
/* apply the bitfield to an empty write pattern */
write_value = 0;
} else {
/* apply the bitfield to the old register value */
write_value = read<Register>();
Bitfield::clear(write_value);
}
/* apply bitfield value and override register */
Bitfield::set(write_value, value);
write<Register>(write_value);
}
/*******************************
** Access to register arrays **
*******************************/
/**
* Read an item of the register array 'T'
*
* \param index index of the targeted item
*/
template <typename T>
inline typename T::Register_array_base::access_t
read(unsigned long const index) const
{
typedef typename T::Register_array_base Array;
typedef typename Array::access_t access_t;
/* reads outside the array return 0 */
if (index > Array::MAX_INDEX) return 0;
/* if item width equals access width we optimize the access */
off_t offset;
if (Array::ITEM_WIDTH == Array::ACCESS_WIDTH) {
Array::simple_dst(offset, index);
return Plain_access::read<access_t>(_plain_access, offset);
/* access width and item width differ */
} else {
long unsigned shift;
Array::dst(offset, shift, index);
return (Plain_access::read<access_t>(_plain_access, offset)
>> shift) & Array::ITEM_MASK;
}
}
/**
* Override an item of the register array 'T'
*
* \param value value that shall be written
* \param index index of the targeted item
*/
template <typename T>
inline void
write(typename T::Register_array_base::access_t const value,
unsigned long const index)
{
typedef typename T::Register_array_base Array;
typedef typename Array::access_t access_t;
/* ignore writes outside the array */
if (index > Array::MAX_INDEX) return;
/* optimize the access if item width equals access width */
off_t offset;
if (Array::ITEM_WIDTH == Array::ACCESS_WIDTH) {
Array::simple_dst(offset, index);
Plain_access::write<access_t>(_plain_access, offset, value);
/* access width and item width differ */
} else {
long unsigned shift;
Array::dst(offset, shift, index);
/* insert new value into old register value */
access_t write_value;
if (Array::STRICT_WRITE)
{
/* apply bitfield to an empty write pattern */
write_value = 0;
} else {
/* apply bitfield to the old register value */
write_value = Plain_access::read<access_t>(_plain_access,
offset);
write_value &= ~(Array::ITEM_MASK << shift);
}
/* apply bitfield value and override register */
write_value |= (value & Array::ITEM_MASK) << shift;
Plain_access::write<access_t>(_plain_access, offset,
write_value);
}
}
/*****************************************************
** Access to bitfields within register array items **
*****************************************************/
/**
* Read the bitfield 'T' of a register array
*
* \param index index of the targeted item
*/
template <typename T>
inline typename T::Array_bitfield_base::Compound_array::access_t
read(unsigned long const index) const
{
typedef typename T::Array_bitfield_base Bitfield;
typedef typename Bitfield::Compound_array Array;
return Bitfield::get(read<Array>(index));
}
/**
* Override the bitfield 'T' of a register array
*
* \param value value that shall be written
* \param index index of the targeted array item
*/
template <typename T>
inline void
write(typename T::Array_bitfield_base::Compound_array::access_t const value,
long unsigned const index)
{
typedef typename T::Array_bitfield_base Bitfield;
typedef typename Bitfield::Compound_array Array;
typedef typename Array::access_t access_t;
/* initialize the pattern written finally to the register */
access_t write_value;
if (Array::STRICT_WRITE)
{
/* apply the bitfield to an empty write pattern */
write_value = 0;
} else {
/* apply the bitfield to the old register value */
write_value = read<Array>(index);
Bitfield::clear(write_value);
}
/* apply bitfield value and override register */
Bitfield::set(write_value, value);
write<Array>(write_value, index);
}
/***********************
** Access to bitsets **
***********************/
/**
* Read bitset 'T' (composed of 2 parts)
*/
template <typename T>
inline typename T::Bitset_2_base::access_t const read()
{
typedef typename T::Bitset_2_base::Bits_0 Bits_0;
typedef typename T::Bitset_2_base::Bits_1 Bits_1;
typedef typename T::Bitset_2_base::access_t access_t;
enum { V1_SHIFT = Bits_0::BITFIELD_WIDTH };
access_t const v0 = read<Bits_0>();
access_t const v1 = read<Bits_1>();
return v0 | (v1 << V1_SHIFT);
}
/**
* Override bitset 'T' (composed of 2 parts)
*
* \param v value that shall be written
*/
template <typename T>
inline void write(typename T::Bitset_2_base::access_t v)
{
typedef typename T::Bitset_2_base::Bits_0 Bits_0;
typedef typename T::Bitset_2_base::Bits_1 Bits_1;
write<Bits_0>(v);
write<Bits_1>(v >> Bits_0::BITFIELD_WIDTH);
}
/**
* Read bitset 'T' (composed of 3 parts)
*/
template <typename T>
inline typename T::Bitset_3_base::access_t const read()
{
typedef typename T::Bitset_3_base::Bits_0 Bits_0;
typedef typename T::Bitset_3_base::Bits_1 Bits_1;
typedef typename T::Bitset_3_base::Bits_2 Bits_2;
typedef typename T::Bitset_3_base::access_t access_t;
enum {
BITS_0_WIDTH = Bits_0::BITFIELD_WIDTH,
BITS_1_WIDTH = Bits_1::BITFIELD_WIDTH,
V1_SHIFT = BITS_0_WIDTH + BITS_1_WIDTH,
};
access_t const v0 = read<Bitset_2<Bits_0, Bits_1> >();
access_t const v1 = read<Bits_2>();
return v0 | (v1 << V1_SHIFT);
}
/**
* Override bitset 'T' (composed of 3 parts)
*
* \param v value that shall be written
*/
template <typename T>
inline void write(typename T::Bitset_3_base::access_t v)
{
typedef typename T::Bitset_3_base::Bits_0 Bits_0;
typedef typename T::Bitset_3_base::Bits_1 Bits_1;
typedef typename T::Bitset_3_base::Bits_2 Bits_2;
write<Bitset_2<Bits_0, Bits_1> >(v);
write<Bits_2>(v >> (Bits_0::BITFIELD_WIDTH +
Bits_1::BITFIELD_WIDTH));
}
/*********************************
** Polling for bitfield states **
*********************************/
struct Polling_timeout : Exception { };
struct Attempts
{
unsigned value;
explicit Attempts(unsigned value) : value(value) { }
};
struct Microseconds
{
unsigned value;
explicit Microseconds(unsigned value) : value(value) { }
};
/**
* Interface for delaying the execution of a calling thread
*/
struct Delayer
{
/**
* Delay execution of the caller for 'us' microseconds
*/
virtual void usleep(unsigned us) = 0;
};
/**
* Wait until a list of IO conditions is met
*
* \param CONDITIONS Types of the of conditions in the condition
* list. Condition subtypes of the IO types. For
* example the Bitfield::Equal type.
* \param attempts maximum number of probing attempts
* \param us number of microseconds between attempts
* \param delayer Sleeping facility to be used when the
* conditions are not met
* \param conditions condition list
*
* \throw Polling_timeout
*/
template <typename... CONDITIONS>
inline void wait_for(Attempts attempts,
Microseconds us,
Delayer &delayer,
CONDITIONS... conditions)
{
for (unsigned i = 0; i < attempts.value; i++,
delayer.usleep(us.value))
{
if (_conditions_met(conditions...)) {
return; }
}
throw Polling_timeout();
}
/**
* Shortcut for 'wait_for' with 'attempts = 500' and 'us = 1000'
*/
template <typename... CONDITIONS>
inline void wait_for(Delayer &delayer, CONDITIONS... conditions)
{
wait_for<CONDITIONS...>(Attempts(500), Microseconds(1000),
delayer, conditions...);
}
/**
* Wait until register 'T' contains the specified 'value'
*
* \param value value to wait for
* \param delayer sleeping facility to be used when the
* value is not reached yet
* \param max_attempts number of register probing attempts
* \param us number of microseconds between attempts
*
* \noapi
* \deprecated use 'template <typename... CONDITIONS> wait_for'
*/
template <typename T>
inline bool
wait_for(typename T::Register_base::access_t const value,
Delayer & delayer,
unsigned max_attempts = 500,
unsigned us = 1000) __attribute__ ((deprecated));
/**
* Wait until bitfield 'T' contains the specified 'value'
*
* \param value value to wait for
* \param delayer sleeping facility to be used when the
* value is not reached yet
* \param max_attempts number of bitfield probing attempts
* \param us number of microseconds between attempts
*
* \noapi
* \deprecated use 'template <typename... CONDITIONS> wait_for'
*/
template <typename T>
inline bool
wait_for(typename T::Bitfield_base::Compound_reg::access_t const value,
Delayer & delayer,
unsigned max_attempts = 500,
unsigned us = 1000) __attribute__ ((deprecated));
};
template <typename PLAIN_ACCESS>
template <typename T>
bool Genode::Register_set<PLAIN_ACCESS>::
wait_for(typename T::Register_base::access_t const value,
Delayer &delayer,
unsigned max_attempts,
unsigned us)
{
typedef typename T::Register_base Register;
for (unsigned i = 0; i < max_attempts; i++, delayer.usleep(us))
{
if (read<Register>() == value) {
return true; }
}
return false;
}
template <typename PLAIN_ACCESS>
template <typename T>
bool Genode::Register_set<PLAIN_ACCESS>::
wait_for(typename T::Bitfield_base::Compound_reg::access_t const value,
Delayer &delayer,
unsigned max_attempts,
unsigned us)
{
typedef typename T::Bitfield_base Bitfield;
for (unsigned i = 0; i < max_attempts; i++, delayer.usleep(us))
{
if (read<Bitfield>() == value) {
return true; }
}
return false;
}
#endif /* _INCLUDE__UTIL__REGISTER_SET_H_ */