From 1f75ebe9e5c0ceab040a0837ee0eb496c9ac8f0a Mon Sep 17 00:00:00 2001 From: Martin Stein Date: Mon, 9 Jan 2012 13:29:03 +0100 Subject: [PATCH] First version of generic MMIO access framework The MMIO access framework consists of an abstraction for a contiguous MMIO area with a base address set dynamically. Within this class 'Mmio' are declarations for 'Register' and 'Subreg'. These two can be parameterized statically via template parameters to create arbitrary MMIO structures. Whereas 'Register' relies to a POD like subregion of 'Mmio', 'Subreg' relies to a MMIO region within a specific 'Register' and therefore is smaller or equal then the storage type of its superior 'Register'. Furthermore with 'Reg_array' and 'Subreg_array', there exists the possibility to handle arrays of uniform contiguous registers or subregs by index. 'Subreg_array' therefore abstracts from the width boundary of its superior 'Register' and handles a steady distance between its members in addition. Both also check array size limits. Related to issue #69. --- base/include/util/mmio.h | 327 ++++++++++++++++++++++++++++++ base/src/test/util_mmio/main.cc | 19 ++ base/src/test/util_mmio/target.mk | 4 + 3 files changed, 350 insertions(+) create mode 100644 base/include/util/mmio.h create mode 100644 base/src/test/util_mmio/main.cc create mode 100644 base/src/test/util_mmio/target.mk diff --git a/base/include/util/mmio.h b/base/include/util/mmio.h new file mode 100644 index 0000000000..d4506fb5e5 --- /dev/null +++ b/base/include/util/mmio.h @@ -0,0 +1,327 @@ +/* + * \brief Generic MMIO accessor framework + * \author Martin stein + * \date 2011-10-26 + */ + +#ifndef _BASE__INCLUDE__UTIL__MMIO_H_ +#define _BASE__INCLUDE__UTIL__MMIO_H_ + +#include + + +namespace Genode +{ + /** + * A continuous MMIO region + */ + class Mmio + { + protected: + + /** + * Write 'value' typed to MMIO base + 'o' + */ + template + 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; + + 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 }; }; + + /** + * A POD-like region at offset 'MMIO_OFFSET' within a MMIO region + */ + template + struct Register + { + typedef STORAGE_T storage_t; + enum { OFFSET = MMIO_OFFSET }; + + /** + * A bitregion within a register + */ + template + struct Subreg + { + enum { + SHIFT = BIT_SHIFT, + WIDTH = BIT_SIZE, + MASK = (1 << WIDTH) - 1, + }; + + /** + * Back reference to containing register + */ + typedef Register Compound_reg; + + /** + * Get a register value with this subreg set to 'value' + * and the rest left zero + */ + static storage_t bits(storage_t const value) + { + return (value & MASK) << SHIFT; + }; + }; + + /** + * An array of 'SUBREGS' many similar bitregions + * 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' + */ + inline static void access_dest(off_t & offset, unsigned long & shift, + unsigned long const index); + }; + }; + + /** + * An array of 'REGS' many similar 'Register's + */ + template + struct Reg_array : public Register, + public Array + { }; + + addr_t const base; + + /** + * Constructor + */ + inline Mmio(addr_t mmio_base); + + /** + * Typed address of register 'REGISTER' + */ + template + inline typename REGISTER::storage_t volatile * typed_addr() const; + + /** + * Read the whole register 'REGISTER' + */ + template + inline typename REGISTER::storage_t read() const; + + /** + * Read the subreg 'SUBREG' + */ + template + inline typename SUBREG::Compound_reg::storage_t read() const; + + /** + * Read the whole register no. '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' + */ + 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); + + /** + * Write 'value' into the bitfield no. 'index' of the array 'SUBREG_ARRAY' + */ + template + inline void write(typename SUBREG_ARRAY::Compound_reg::storage_t const value, + unsigned long const index); + }; +} + + +template +template +void +Genode::Mmio::Register::Subreg_array::access_dest(Genode::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; +} + + +template +void Genode::Mmio::_write(off_t const o, STORAGE_T const value) +{ + *(STORAGE_T volatile *)((addr_t)base + o) = value; +} + + +template +STORAGE_T Genode::Mmio::_read(off_t const o) const +{ + return *(STORAGE_T volatile *)((addr_t)base + o); +} + + +Genode::Mmio::Mmio(addr_t mmio_base) : base(mmio_base) { } + + +template +typename REGISTER::storage_t volatile * Genode::Mmio::typed_addr() const +{ + return (typename REGISTER::storage_t volatile *)base + REGISTER::OFFSET; +} + + +template +typename REGISTER::storage_t Genode::Mmio::read() const +{ + return _read(REGISTER::OFFSET); +} + + +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) +{ + _write(REGISTER::OFFSET, value); +} + + +template +void Genode::Mmio::write(typename REG_ARRAY::storage_t const value, + unsigned long const index) +{ + typedef typename REG_ARRAY::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); +} + + +template +void Genode::Mmio::write(typename SUBREG::Compound_reg::storage_t const value) +{ + typedef typename SUBREG::Compound_reg Register; + 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; + + write(new_reg); +} + + +template +void Genode::Mmio::write(typename SUBREG_ARRAY::Compound_reg::storage_t const value, + 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 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; + + _write(offset, new_field); +} + + +#endif /* _BASE__INCLUDE__UTIL__MMIO_H_ */ diff --git a/base/src/test/util_mmio/main.cc b/base/src/test/util_mmio/main.cc new file mode 100644 index 0000000000..5534805133 --- /dev/null +++ b/base/src/test/util_mmio/main.cc @@ -0,0 +1,19 @@ +/* + * \brief Basic test for MMIO access framework + * \author Christian Helmuth + * \date 2012-01-09 + */ + +/* + * Copyright (C) 2011-2012 Genode Labs GmbH + * + * This file is part of the Genode OS framework, which is distributed + * under the terms of the GNU General Public License version 2. + */ + +#include + + +int main() +{ +} diff --git a/base/src/test/util_mmio/target.mk b/base/src/test/util_mmio/target.mk new file mode 100644 index 0000000000..42246e1dae --- /dev/null +++ b/base/src/test/util_mmio/target.mk @@ -0,0 +1,4 @@ +TARGET = test-util_mmio +SRC_CC = main.cc + +LIBS = cxx env