Replace 'Subreg_array' with 'Reg_array'.

'Reg_array' contains items whose width can be the width of the register
storage type at a max. Nethertheless they can be smaller and iterate all
subregs that are covered by the item width. The array uses as much
successive instances of its storage type as needed.

The test 'run/util_mmio' also tests these new features heavily.
This commit is contained in:
Martin Stein 2012-01-14 02:42:45 +01:00 committed by Norman Feske
parent 01bb7536dd
commit e1285335ab
3 changed files with 298 additions and 150 deletions

View File

@ -1,5 +1,5 @@
/*
* \brief Generic MMIO accessor framework
* \brief Generic MMIO access framework
* \author Martin stein
* \date 2011-10-26
*/
@ -15,6 +15,7 @@
#define _BASE__INCLUDE__UTIL__MMIO_H_
#include <util/register.h>
#include <base/printf.h>
namespace Genode
@ -30,23 +31,17 @@ namespace Genode
* Write 'value' typed to MMIO base + 'o'
*/
template <typename STORAGE_T>
void _write(off_t const o, STORAGE_T const value);
inline void _write(off_t const o, STORAGE_T const value);
/**
* Read typed from MMIO base + 'o'
*/
template <typename STORAGE_T>
STORAGE_T _read(off_t const o) const;
inline STORAGE_T _read(off_t const o) const;
public:
enum { BYTE_EXP = 3, BYTE_WIDTH = 8 };
/**
* Holds additional info for an array of the inheriting type
*/
template <unsigned long SIZE>
struct Array { enum { MAX_INDEX = SIZE-1 }; };
enum { BYTE_WIDTH_LOG2 = 3, BYTE_WIDTH = 1 << BYTE_WIDTH_LOG2 };
/**
* A POD-like region at offset 'MMIO_OFFSET' within a MMIO region
@ -58,6 +53,10 @@ namespace Genode
/**
* A bitregion within a register
*
* \detail Subregs are read and written according to their range,
* so if we have a 'Subreg<2,3>' and write '0b11101' to it
* only '0b101' (shiftet by 2 bits) is written
*/
template <unsigned long BIT_SHIFT, unsigned long BIT_SIZE>
struct Subreg : public Genode::Register<STORAGE_T>::template Subreg<BIT_SHIFT, BIT_SIZE>
@ -67,58 +66,80 @@ namespace Genode
*/
typedef Register<OFFSET, STORAGE_T> Compound_reg;
};
/**
* An array of 'SUBREGS' many similar bitregions with distance 'BIT_SHIFT'
* FIXME: Side effects of a combination of 'Reg_array' and 'Subreg_array'
* are not evaluated
*/
template <unsigned long BIT_SHIFT, unsigned long BIT_SIZE, unsigned long SUBREGS>
struct Subreg_array : public Array<SUBREGS>
{
enum {
SHIFT = BIT_SHIFT,
WIDTH = BIT_SIZE,
MASK = (1 << WIDTH) - 1,
ITERATION_WIDTH = (SHIFT + WIDTH),
STORAGE_WIDTH = BYTE_WIDTH * sizeof(STORAGE_T),
ARRAY_SIZE = (ITERATION_WIDTH * SUBREGS) >> BYTE_EXP,
};
/**
* Back reference to containing register
*/
typedef Register<OFFSET, STORAGE_T> Compound_reg;
/**
* Calculate the MMIO-relative offset 'offset' and shift 'shift'
* within the according 'storage_t' value to acces subreg no. 'index'
*/
static void access_dest(off_t & offset, unsigned long & shift,
unsigned long const index)
{
unsigned long const bit_off = (index+1) * ITERATION_WIDTH - WIDTH;
offset = (off_t) ((bit_off / STORAGE_WIDTH) * sizeof(STORAGE_T));
shift = bit_off - ( offset << BYTE_EXP );
offset += Compound_reg::OFFSET;
}
};
};
/**
* An array of 'REGS' many similar 'Register's
* An array of successive similar items
*
* \detail 'STORAGE_T' width must a power of 2. The array
* takes all subregs that are covered by an item width and
* iterates them successive 'ITEMS' times, thus subregs out of
* the range of the first item are not accessible. Furthermore
* the maximum item width is the width of 'STORAGE_T'.
* The array is not limited to one 'STORAGE_T' instance,
* it uses as much successive instances as needed.
*/
template <off_t MMIO_OFFSET, typename STORAGE_T, unsigned long REGS>
struct Reg_array : public Register<MMIO_OFFSET, STORAGE_T>,
public Array<REGS>
{ };
template <off_t MMIO_OFFSET, typename STORAGE_T, unsigned long ITEMS, unsigned long _ITEM_WIDTH_LOG2>
struct Reg_array : public Register<MMIO_OFFSET, STORAGE_T>
{
enum {
MAX_INDEX = ITEMS - 1,
ITEM_WIDTH_LOG2 = _ITEM_WIDTH_LOG2,
ITEM_WIDTH = 1 << ITEM_WIDTH_LOG2,
ITEM_MASK = (1 << ITEM_WIDTH) - 1,
ITEMS_PER_REG = (sizeof(STORAGE_T) << BYTE_WIDTH_LOG2) >> ITEM_WIDTH_LOG2,
ITEM_IS_REG = ITEMS_PER_REG == 1,
STORAGE_WIDTH = BYTE_WIDTH * sizeof(STORAGE_T),
/**
* Static sanity check
*/
ERROR_ITEMS_OVERSIZED = ITEMS_PER_REG / ITEMS_PER_REG,
};
/**
* A bitregion within a register array
*
* \detail Subregs are only written and read as far as the
* item width reaches, i.e. assume a 'Reg_array<0,uint8_t,6,2>'
* that contains a 'Subreg<1,5>' and we write '0b01101' to it, then
* only '0b101' is written and read in consequence
*/
template <unsigned long BIT_SHIFT, unsigned long BIT_SIZE>
struct Subreg : public Register<MMIO_OFFSET, STORAGE_T>::template Subreg<BIT_SHIFT, BIT_SIZE>
{
/**
* Back reference to containing register array
*/
typedef Reg_array<MMIO_OFFSET, STORAGE_T, ITEMS, ITEM_WIDTH_LOG2> Compound_array;
};
/**
* Calculate the MMIO-relative offset 'offset' and shift 'shift'
* within the according 'storage_t' instance to access this subreg
* from item 'index'
*/
static inline void access_dest(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(STORAGE_T)-1) );
shift = bit_off - ( offset << BYTE_WIDTH_LOG2 );
offset += MMIO_OFFSET;
}
};
addr_t const base;
/**
* Constructor
*/
inline Mmio(addr_t mmio_base);
inline Mmio(addr_t mmio_base) : base(mmio_base) { }
/*************************
** Access to registers **
*************************/
/**
* Typed address of register 'REGISTER'
@ -132,6 +153,17 @@ namespace Genode
template <typename REGISTER>
inline typename REGISTER::storage_t read() const;
/**
* Override the register 'REGISTER' with 'value'
*/
template <typename REGISTER>
inline void write(typename REGISTER::storage_t const value);
/****************************************
** Access to subregs within registers **
****************************************/
/**
* Read the subreg 'SUBREG'
*/
@ -139,42 +171,45 @@ namespace Genode
inline typename SUBREG::Compound_reg::storage_t read() const;
/**
* Read the whole register no. 'index' of the array 'REG_ARRAY'
* Override the subreg 'SUBREG' with 'value'
*/
template <typename SUBREG>
inline void write(typename SUBREG::Compound_reg::storage_t const value);
/*******************************
** Access to register arrays **
*******************************/
/**
* Read the whole item 'index' of the array 'REG_ARRAY'
*/
template <typename REG_ARRAY>
inline typename REG_ARRAY::storage_t read(unsigned long const index) const;
/**
* Read the subreg no. 'index' of the array 'SUBREG_ARRAY'
*/
template <typename SUBREG_ARRAY>
inline typename SUBREG_ARRAY::Compound_reg::storage_t read(unsigned long const index);
/**
* Write 'value' into the register 'REGISTER'
*/
template <typename REGISTER>
inline void write(typename REGISTER::storage_t const value);
/**
* Write 'value' into the register no. 'index' of the array 'REG_ARRAY'
* Override item 'index' of the array 'REG_ARRAY' with 'value'
*/
template <typename REG_ARRAY>
inline void write(typename REG_ARRAY::storage_t const value,
unsigned long const index);
/**
* Write 'value' into the subregister 'SUBREG'
*/
template <typename SUBREG>
inline void write(typename SUBREG::Compound_reg::storage_t const value);
/***************************************************
** Access to subregs within register array items **
***************************************************/
/**
* Write 'value' into the bitfield no. 'index' of the array 'SUBREG_ARRAY'
* Read the subreg 'ARRAY_SUBREG' of item 'index' of the compound reg array
*/
template <typename SUBREG_ARRAY>
inline void write(typename SUBREG_ARRAY::Compound_reg::storage_t const value,
unsigned long const index);
template <typename ARRAY_SUBREG>
inline typename ARRAY_SUBREG::Compound_array::storage_t read(unsigned long const index) const;
/**
* Override subreg 'ARRAY_SUBREG' of item 'index' of the compound reg array with 'value'
*/
template <typename ARRAY_SUBREG>
inline void write(typename ARRAY_SUBREG::Compound_array::storage_t const value, long unsigned const index);
};
}
@ -193,7 +228,9 @@ STORAGE_T Genode::Mmio::_read(off_t const o) const
}
Genode::Mmio::Mmio(addr_t mmio_base) : base(mmio_base) { }
/*************************
** Access to registers **
*************************/
template <typename REGISTER>
@ -210,45 +247,6 @@ typename REGISTER::storage_t Genode::Mmio::read() const
}
template <typename SUBREG>
typename SUBREG::Compound_reg::storage_t Genode::Mmio::read() const
{
typedef typename SUBREG::Compound_reg Register;
typedef typename Register::storage_t storage_t;
return (_read<storage_t>(Register::OFFSET) >> SUBREG::SHIFT) & SUBREG::MASK;
}
template <typename REG_ARRAY>
typename REG_ARRAY::storage_t Genode::Mmio::read(unsigned long const index) const
{
typedef typename REG_ARRAY::storage_t storage_t;
if (index > REG_ARRAY::MAX_INDEX) return 0;
addr_t const offset = REG_ARRAY::OFFSET + index*sizeof(storage_t);
return _read<storage_t>(offset);
}
template <typename SUBREG_ARRAY>
typename SUBREG_ARRAY::Compound_reg::storage_t Genode::Mmio::read(unsigned long const index)
{
enum { MASK = SUBREG_ARRAY::MASK };
typedef typename SUBREG_ARRAY::Compound_reg Register;
typedef typename Register::storage_t storage_t;
if (index > SUBREG_ARRAY::MAX_INDEX) return;
off_t const offset = Register::OFFSET + SUBREG_ARRAY::access_off(index);
unsigned long const shift = SUBREG_ARRAY::access_shift(index);
return (_read<storage_t>(offset) >> shift) & MASK;
}
template <typename REGISTER>
void Genode::Mmio::write(typename REGISTER::storage_t const value)
{
@ -256,16 +254,18 @@ void Genode::Mmio::write(typename REGISTER::storage_t const value)
}
template <typename REG_ARRAY>
void Genode::Mmio::write(typename REG_ARRAY::storage_t const value,
unsigned long const index)
/****************************************
** Access to subregs within registers **
****************************************/
template <typename SUBREG>
typename SUBREG::Compound_reg::storage_t Genode::Mmio::read() const
{
typedef typename REG_ARRAY::storage_t storage_t;
typedef typename SUBREG::Compound_reg Register;
typedef typename Register::storage_t storage_t;
if (index > REG_ARRAY::MAX_INDEX) return;
addr_t const offset = REG_ARRAY::OFFSET + index*sizeof(storage_t);
_write<storage_t>(offset, value);
return SUBREG::get(_read<storage_t>(Register::OFFSET));
}
@ -276,34 +276,108 @@ void Genode::Mmio::write(typename SUBREG::Compound_reg::storage_t const value)
typedef typename Register::storage_t storage_t;
storage_t new_reg = read<Register>();
new_reg &= ~(SUBREG::MASK << SUBREG::SHIFT);
new_reg |= (value & SUBREG::MASK) << SUBREG::SHIFT;
SUBREG::clear(new_reg);
SUBREG::set(new_reg, value);
write<Register>(new_reg);
}
template <typename SUBREG_ARRAY>
void Genode::Mmio::write(typename SUBREG_ARRAY::Compound_reg::storage_t const value,
unsigned long const index)
/************************************
** Access to register array items **
************************************/
template <typename REG_ARRAY>
typename REG_ARRAY::storage_t Genode::Mmio::read(unsigned long const index) const
{
enum { MASK = SUBREG_ARRAY::MASK };
typedef typename SUBREG_ARRAY::Compound_reg Register;
typedef typename Register::storage_t storage_t;
if (index > SUBREG_ARRAY::MAX_INDEX) return;
/**
* Handle array overflow
*/
if (index > REG_ARRAY::MAX_INDEX) return 0;
off_t offset;
unsigned long shift;
SUBREG_ARRAY::access_dest(offset, shift, index);
storage_t new_field = _read<storage_t>(offset);
new_field &= ~(MASK << shift);
new_field |= (value & MASK) << shift;
/**
* Optimize access if item width equals storage type width
*/
if (REG_ARRAY::ITEM_IS_REG) {
_write<storage_t>(offset, new_field);
offset = REG_ARRAY::OFFSET + (index << REG_ARRAY::ITEM_WIDTH_LOG2);
return _read<typename REG_ARRAY::storage_t>(offset);
} else {
long unsigned shift;
REG_ARRAY::access_dest(offset, shift, index);
return (_read<typename REG_ARRAY::storage_t>(offset) >> shift) & REG_ARRAY::ITEM_MASK;
}
}
template <typename REG_ARRAY>
void Genode::Mmio::write(typename REG_ARRAY::storage_t const value,
unsigned long const index)
{
/**
* Avoid array overflow
*/
if (index > REG_ARRAY::MAX_INDEX) return;
off_t offset;
/**
* Optimize access if item width equals storage type width
*/
if (REG_ARRAY::ITEM_IS_REG) {
offset = REG_ARRAY::OFFSET + (index << REG_ARRAY::ITEM_WIDTH_LOG2);
_write<typename REG_ARRAY::storage_t>(offset, value);
} else {
long unsigned shift;
REG_ARRAY::access_dest(offset, shift, index);
/**
* Insert new value into old register value
*/
typename REG_ARRAY::storage_t new_reg = _read<typename REG_ARRAY::storage_t>(offset);
new_reg &= ~(REG_ARRAY::ITEM_MASK << shift);
new_reg |= (value & REG_ARRAY::ITEM_MASK) << shift;
_write<typename REG_ARRAY::storage_t>(offset, new_reg);
}
}
/***************************************************
** Access to subregs within register array items **
***************************************************/
template <typename ARRAY_SUBREG>
void Genode::Mmio::write(typename ARRAY_SUBREG::Compound_array::storage_t const value,
long unsigned const index)
{
typedef typename ARRAY_SUBREG::Compound_array Reg_array;
typename Reg_array::storage_t new_reg = read<Reg_array>(index);
ARRAY_SUBREG::clear(new_reg);
ARRAY_SUBREG::set(new_reg, value);
write<Reg_array>(new_reg, index);
}
template <typename ARRAY_SUBREG>
typename ARRAY_SUBREG::Compound_array::storage_t Genode::Mmio::read(long unsigned const index) const
{
typedef typename ARRAY_SUBREG::Compound_array Array;
typedef typename Array::storage_t storage_t;
return ARRAY_SUBREG::get(read<Array>(index));
}

View File

@ -1,5 +1,5 @@
/*
* \brief Generic accessor framework for highly structured memory regions
* \brief Generic access framework for highly structured memory regions
* \author Martin stein
* \date 2011-11-10
*/
@ -33,9 +33,11 @@ namespace Genode
struct Subreg
{
enum {
SHIFT = BIT_SHIFT,
WIDTH = BIT_SIZE,
MASK = (1 << WIDTH) - 1,
SHIFT = BIT_SHIFT,
WIDTH = BIT_SIZE,
MASK = (1 << WIDTH) - 1,
REG_MASK = MASK << SHIFT,
CLEAR_MASK = ~REG_MASK,
};
/**
@ -49,22 +51,22 @@ namespace Genode
* \detail Useful to combine successive access to multiple subregs
* into one operation
*/
static storage_t bits(storage_t const value) { return (value & MASK) << SHIFT; }
static inline storage_t bits(storage_t const value) { return (value & MASK) << SHIFT; }
/**
* Get value of this subreg from 'reg'
*/
static storage_t get(storage_t const reg) { return (reg >> SHIFT) & MASK; }
static inline storage_t get(storage_t const reg) { return (reg >> SHIFT) & MASK; }
/**
* Get registervalue 'reg' with this subreg set to zero
*/
static void clear(storage_t & reg) { reg &= ~(MASK << SHIFT); }
static inline void clear(storage_t & reg) { reg &= CLEAR_MASK; }
/**
* Get registervalue 'reg' with this subreg set to 'value'
*/
static void set(storage_t & reg, storage_t const value = ~0)
static inline void set(storage_t & reg, storage_t const value = ~0)
{
clear(reg);
reg |= (value & MASK) << SHIFT;

View File

@ -54,6 +54,12 @@ struct Cpu_state : Register<uint16_t>
inline static void write(storage_t & v) { cpu_state = v; }
};
struct A : public Mmio {
A(addr_t const base) : Mmio(base) { }
};
/**
* Exemplary MMIO region type
*/
@ -77,8 +83,25 @@ struct Test_mmio : public Mmio
struct Invalid_area : Subreg<6,8> { };
struct Overlapping_area : Subreg<0,6> { };
};
};
struct Array : Reg_array<0x2, uint16_t, 10, 2>
{
struct A : Subreg<0,1> { };
struct B : Subreg<1,2> { };
struct C : Subreg<3,1> { };
struct D : Subreg<1,3> { };
};
};
/* little endian LSB --> MSB */
/* big endian MSB <-- LSB */
/* address 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 */
/* bits 0 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 */
/* bit ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| */
/* 4bit | | | | |R0 |R1 |R2 |R3 |R4 |R5 |-- |-- | | | | | */
/* 8bit (byte)| | | | | | | | | */
/* 16bit | |Int0 |Int1 | | */
/* 32bit | | | */
/* 64bit | | */
/**
* Print out memory content hexadecimal
@ -271,6 +294,55 @@ int main()
|| Cpu_state::Irq::get(state) != 0)
{ return test_failed(10); }
/**
* Test 11, read/write register array items with array- and item overflows
*/
zero_mem(mmio_mem, sizeof(mmio_mem));
mmio.write<Test_mmio::Array>(0xa, 0);
mmio.write<Test_mmio::Array>(0xb, 4);
mmio.write<Test_mmio::Array>(0xc, 5);
mmio.write<Test_mmio::Array>(0xdd, 9);
mmio.write<Test_mmio::Array>(0xff, 11);
static uint8_t mmio_cmpr_11[MMIO_SIZE] = {0,0,0x0a,0,0xcb,0,0xd0,0};
if (compare_mem(mmio_mem, mmio_cmpr_11, sizeof(mmio_mem)) ||
mmio.read<Test_mmio::Array>(0) != 0xa ||
mmio.read<Test_mmio::Array>(4) != 0xb ||
mmio.read<Test_mmio::Array>(5) != 0xc ||
mmio.read<Test_mmio::Array>(9) != 0xd ||
mmio.read<Test_mmio::Array>(11) != 0 )
{ return test_failed(11); }
/**
* Test 12, read/write subregs of register array items with array-, item- and subreg overflows
* also test overlappng subregs
*/
zero_mem(mmio_mem, sizeof(mmio_mem));
mmio.write<Test_mmio::Array::A>(0x1, 0);
mmio.write<Test_mmio::Array::B>(0x2, 0);
mmio.write<Test_mmio::Array::A>(0x1, 1);
mmio.write<Test_mmio::Array::B>(0x1, 1);
mmio.write<Test_mmio::Array::A>(0xf, 4);
mmio.write<Test_mmio::Array::B>(0xe, 4);
mmio.write<Test_mmio::Array::D>(0xd, 5);
mmio.write<Test_mmio::Array::C>(0x1, 8);
mmio.write<Test_mmio::Array::D>(0x3, 8);
mmio.write<Test_mmio::Array::A>(0xf, 11);
static uint8_t mmio_cmpr_12[MMIO_SIZE] = {0,0,0b00110101,0,0b10100101,0,0b00000110,0};
if (compare_mem(mmio_mem, mmio_cmpr_12, sizeof(mmio_mem)) ||
mmio.read<Test_mmio::Array::A>(0) != 0x1 ||
mmio.read<Test_mmio::Array::B>(0) != 0x2 ||
mmio.read<Test_mmio::Array::A>(1) != 0x1 ||
mmio.read<Test_mmio::Array::B>(1) != 0x1 ||
mmio.read<Test_mmio::Array::A>(4) != 0x1 ||
mmio.read<Test_mmio::Array::B>(4) != 0x2 ||
mmio.read<Test_mmio::Array::D>(5) != 0x5 ||
mmio.read<Test_mmio::Array::C>(8) != 0x0 ||
mmio.read<Test_mmio::Array::D>(8) != 0x3 ||
mmio.read<Test_mmio::Array::A>(11) != 0 )
{ return test_failed(12); }
printf("Test ended successfully\n");
return 0;
}