From e1285335abbf21a164418bd4b3d25d01ab843df3 Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Sat, 14 Jan 2012 02:42:45 +0100 Subject: [PATCH] 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. --- base/include/util/mmio.h | 356 +++++++++++++++++++------------- base/include/util/register.h | 18 +- base/src/test/util_mmio/main.cc | 74 ++++++- 3 files changed, 298 insertions(+), 150 deletions(-) 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; }