diff --git a/base/include/util/mmio.h b/base/include/util/mmio.h index 144de2590..221dc357f 100644 --- a/base/include/util/mmio.h +++ b/base/include/util/mmio.h @@ -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 +#include namespace Genode @@ -30,23 +31,17 @@ namespace Genode * Write 'value' typed to MMIO base + 'o' */ template - 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 - 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 - 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 struct Subreg : public Genode::Register::template Subreg @@ -67,58 +66,80 @@ namespace Genode */ typedef Register 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 - struct Subreg_array : public Array - { - 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 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 - struct Reg_array : public Register, - public Array - { }; + template + struct Reg_array : public Register + { + 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 + struct Subreg : public Register::template Subreg + { + /** + * Back reference to containing register array + */ + typedef Reg_array 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 inline typename REGISTER::storage_t read() const; + /** + * Override the register 'REGISTER' with 'value' + */ + template + 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 + 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 inline typename REG_ARRAY::storage_t read(unsigned long const index) const; /** - * Read the subreg no. 'index' of the array 'SUBREG_ARRAY' - */ - template - inline typename SUBREG_ARRAY::Compound_reg::storage_t read(unsigned long const index); - - /** - * Write 'value' into the register 'REGISTER' - */ - template - 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 inline void write(typename REG_ARRAY::storage_t const value, unsigned long const index); - /** - * Write 'value' into the subregister 'SUBREG' - */ - template - 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 - inline void write(typename SUBREG_ARRAY::Compound_reg::storage_t const value, - unsigned long const index); + template + 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 + 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 @@ -210,45 +247,6 @@ typename REGISTER::storage_t Genode::Mmio::read() const } -template -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(Register::OFFSET) >> SUBREG::SHIFT) & SUBREG::MASK; -} - - -template -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(offset); -} - - -template -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(offset) >> shift) & MASK; -} - - template 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 -void Genode::Mmio::write(typename REG_ARRAY::storage_t const value, - unsigned long const index) +/**************************************** + ** Access to subregs within registers ** + ****************************************/ + + +template +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(offset, value); + return SUBREG::get(_read(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(); - - new_reg &= ~(SUBREG::MASK << SUBREG::SHIFT); - new_reg |= (value & SUBREG::MASK) << SUBREG::SHIFT; + SUBREG::clear(new_reg); + SUBREG::set(new_reg, value); write(new_reg); } -template -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::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(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(offset, new_field); + offset = REG_ARRAY::OFFSET + (index << REG_ARRAY::ITEM_WIDTH_LOG2); + return _read(offset); + + } else { + + long unsigned shift; + REG_ARRAY::access_dest(offset, shift, index); + return (_read(offset) >> shift) & REG_ARRAY::ITEM_MASK; + } + +} + + +template +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(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(offset); + new_reg &= ~(REG_ARRAY::ITEM_MASK << shift); + new_reg |= (value & REG_ARRAY::ITEM_MASK) << shift; + + _write(offset, new_reg); + } +} + + +/*************************************************** + ** Access to subregs within register array items ** + ***************************************************/ + + +template +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(index); + ARRAY_SUBREG::clear(new_reg); + ARRAY_SUBREG::set(new_reg, value); + + write(new_reg, index); +} + + +template +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(index)); } diff --git a/base/include/util/register.h b/base/include/util/register.h index 4bb4111f8..ea17d2541 100644 --- a/base/include/util/register.h +++ b/base/include/util/register.h @@ -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; diff --git a/base/src/test/util_mmio/main.cc b/base/src/test/util_mmio/main.cc index 1300cb15d..8b54d39b7 100644 --- a/base/src/test/util_mmio/main.cc +++ b/base/src/test/util_mmio/main.cc @@ -54,6 +54,12 @@ struct Cpu_state : Register 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(0xa, 0); + mmio.write(0xb, 4); + mmio.write(0xc, 5); + mmio.write(0xdd, 9); + mmio.write(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(0) != 0xa || + mmio.read(4) != 0xb || + mmio.read(5) != 0xc || + mmio.read(9) != 0xd || + mmio.read(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(0x1, 0); + mmio.write(0x2, 0); + mmio.write(0x1, 1); + mmio.write(0x1, 1); + mmio.write(0xf, 4); + mmio.write(0xe, 4); + mmio.write(0xd, 5); + mmio.write(0x1, 8); + mmio.write(0x3, 8); + mmio.write(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(0) != 0x1 || + mmio.read(0) != 0x2 || + mmio.read(1) != 0x1 || + mmio.read(1) != 0x1 || + mmio.read(4) != 0x1 || + mmio.read(4) != 0x2 || + mmio.read(5) != 0x5 || + mmio.read(8) != 0x0 || + mmio.read(8) != 0x3 || + mmio.read(11) != 0 ) + { return test_failed(12); } + printf("Test ended successfully\n"); return 0; }