L4Re Operating System Framework
Interface and Usage Documentation
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
hw_register_block
1// vi:set ft=cpp: -*- Mode: C++ -*-
2/*
3 * Copyright (C) 2014-2021, 2023-2024 Kernkonzept GmbH.
4 * Author(s): Alexander Warg <alexander.warg@kernkonzept.com>
5 *
6 * License: see LICENSE.spdx (in this directory or the directories above)
7 */
8#pragma once
9
10#include <l4/sys/types.h>
11#include <l4/cxx/type_traits>
12
13namespace L4drivers {
14
15
16/**
17 * Register block.
18 * \class Register_block
19 * \details Example usage:
20 *
21 * \code{.cpp}
22 * void test()
23 * {
24 * // create a register block reference for max. 16bit accesses, using a
25 * // MMIO register block implementation (at address 0x1000).
26 * Hw::Register_block<16> regs = new Hw::Mmio_register_block<16>(0x1000);
27 *
28 * // Alternatively it is allowed to use an implementation that allows
29 * // wider access than actually needed.
30 * Hw::Register_block<16> regs = new Hw::Mmio_register_block<32>(0x1000);
31 *
32 * // read a 16bit register at offset 8byte
33 * unsigned short x = regs.r<16>(8);
34 * unsigned short x1 = regs[8]; // alternative
35 *
36 * // read an 8bit register at offset 0byte
37 * unsigned v = regs.r<8>(0);
38 *
39 * // do a 16bit write to register at offset 2byte (four variants)
40 * regs[2] = 22;
41 * regs.r<16>(2) = 22;
42 * regs[2].write(22);
43 * regs.r<16>().write(22);
44 *
45 * // do an 8bit write (two variants)
46 * regs.r<8>(0) = 9;
47 * regs.r<8>(0).write(9);
48 *
49 * // do 16bit read-modify-write (two variants)
50 * regs[4].modify(0xf, 3); // clear 4 lowest bits and set them to 3
51 * regs.r<16>(4).modify(0xf, 3);
52 *
53 * // do 8bit read-modify-write
54 * regs.r<8>(0).modify(0xf, 3);
55 *
56 * // fails to compile, because of too wide access
57 * // (32 bit access but regs is Hw::Register_block<16>)
58 * unsigned long v = regs.r<32>(4)
59 * }
60 * \endcode
61 */
62
63
64/**
65 * \brief Abstract register block interface
66 * \tparam MAX_BITS The maximum access width for the registers.
67 *
68 * This interfaces is based on virtual do_read_<xx> and do_write_<xx>
69 * methods that have to be implemented up to the maximum access width.
70 */
71template< unsigned MAX_BITS = 32 >
72struct Register_block_base;
73
74template<>
75struct Register_block_base<8>
76{
77 virtual l4_uint8_t do_read_8(l4_addr_t reg) const = 0;
78 virtual void do_write_8(l4_uint8_t value, l4_addr_t reg) = 0;
79 virtual ~Register_block_base() = 0;
80};
81
82inline Register_block_base<8>::~Register_block_base() {}
83
84template<>
85struct Register_block_base<16> : Register_block_base<8>
86{
87 virtual l4_uint16_t do_read_16(l4_addr_t reg) const = 0;
88 virtual void do_write_16(l4_uint16_t value, l4_addr_t reg) = 0;
89};
90
91template<>
92struct Register_block_base<32> : Register_block_base<16>
93{
94 virtual l4_uint32_t do_read_32(l4_addr_t reg) const = 0;
95 virtual void do_write_32(l4_uint32_t value, l4_addr_t reg) = 0;
96};
97
98template<>
99struct Register_block_base<64> : Register_block_base<32>
100{
101 virtual l4_uint64_t do_read_64(l4_addr_t reg) const = 0;
102 virtual void do_write_64(l4_uint64_t value, l4_addr_t reg) = 0;
103};
104#undef REGBLK_READ_TEMPLATE
105#undef REGBLK_WRITE_TEMPLATE
106
107template<typename CHILD>
108struct Register_block_modify_mixin
109{
110 template< typename T >
111 T modify(T clear_bits, T set_bits, l4_addr_t reg) const
112 {
113 CHILD const *c = static_cast<CHILD const *>(this);
114 T r = (c->template read<T>(reg) & ~clear_bits) | set_bits;
115 c->template write<T>(r, reg);
116 return r;
117 }
118
119 template< typename T >
120 T set(T set_bits, l4_addr_t reg) const
121 { return this->template modify<T>(T(0), set_bits, reg); }
122
123 template< typename T >
124 T clear(T clear_bits, l4_addr_t reg) const
125 { return this->template modify<T>(clear_bits, T(0), reg); }
126};
127
128
129#define REGBLK_READ_TEMPLATE(sz) \
130 template< typename T > \
131 typename cxx::enable_if<sizeof(T) == (sz / 8), T>::type read(l4_addr_t reg) const \
132 { \
133 union X { T t; l4_uint##sz##_t v; } m; \
134 m.v = _b->do_read_##sz (reg); \
135 return m.t; \
136 }
137
138#define REGBLK_WRITE_TEMPLATE(sz) \
139 template< typename T > \
140 void write(T value, l4_addr_t reg, typename cxx::enable_if<sizeof(T) == (sz / 8), T>::type = T()) const \
141 { \
142 union X { T t; l4_uint##sz##_t v; } m; \
143 m.t = value; \
144 _b->do_write_##sz(m.v, reg); \
145 }
146
147/**
148 * \brief Helper template that translates to the Register_block_base
149 * interface.
150 * \tparam BLOCK The type of the Register_block_base interface to use.
151 *
152 * This helper translates read<T>(), write<T>(), set<T>(), clear<T>(),
153 * and modify<T>() calls to BLOCK::do_read_<xx> and BLOCK::do_write_<xx>.
154 */
155template< typename BLOCK >
156class Register_block_tmpl
157: public Register_block_modify_mixin<Register_block_tmpl<BLOCK> >
158{
159private:
160 BLOCK *_b;
161
162public:
163 Register_block_tmpl(BLOCK *blk) : _b(blk) {}
164 Register_block_tmpl() = default;
165
166 operator BLOCK * () const { return _b; }
167
168 REGBLK_READ_TEMPLATE(8)
169 REGBLK_WRITE_TEMPLATE(8)
170 REGBLK_READ_TEMPLATE(16)
171 REGBLK_WRITE_TEMPLATE(16)
172 REGBLK_READ_TEMPLATE(32)
173 REGBLK_WRITE_TEMPLATE(32)
174 REGBLK_READ_TEMPLATE(64)
175 REGBLK_WRITE_TEMPLATE(64)
176};
177
178
179#undef REGBLK_READ_TEMPLATE
180#undef REGBLK_WRITE_TEMPLATE
181
182namespace __Type_helper {
183 template<unsigned> struct Unsigned;
184 template<> struct Unsigned<8> { typedef l4_uint8_t type; };
185 template<> struct Unsigned<16> { typedef l4_uint16_t type; };
186 template<> struct Unsigned<32> { typedef l4_uint32_t type; };
187 template<> struct Unsigned<64> { typedef l4_uint64_t type; };
188};
189
190
191/**
192 * \brief Single read only register inside a Register_block_base interface.
193 * \tparam BITS The access with of the register in bits.
194 * \tparam BLOCK The type for the Register_block_base interface.
195 * \note Objects of this type must be used only in temporary contexts
196 * not in global, class, or object scope.
197 *
198 * Allows simple read only access to a hardware register.
199 */
200template< unsigned BITS, typename BLOCK >
201class Ro_register_tmpl
202{
203protected:
204 BLOCK _b;
205 unsigned _o;
206
207public:
208 typedef typename __Type_helper::Unsigned<BITS>::type value_type;
209
210 Ro_register_tmpl(BLOCK const &blk, unsigned offset) : _b(blk), _o(offset) {}
211 Ro_register_tmpl() = default;
212
213 /**
214 * \brief read the value from the hardware register.
215 * \return value read from the hardware register.
216 */
217 operator value_type () const
218 { return _b.template read<value_type>(_o); }
219
220 /**
221 * \brief read the value from the hardware register.
222 * \return value from the hardware register.
223 */
224 value_type read() const
225 { return _b.template read<value_type>(_o); }
226};
227
228
229/**
230 * \brief Single hardware register inside a Register_block_base interface.
231 * \tparam BITS The access width for the register in bits.
232 * \tparam BLOCK the type of the Register_block_base interface.
233 * \note Objects of this type must be used only in temporary contexts
234 * not in global, class, or object scope.
235 */
236template< unsigned BITS, typename BLOCK >
237class Register_tmpl : public Ro_register_tmpl<BITS, BLOCK>
238{
239public:
240 typedef typename Ro_register_tmpl<BITS, BLOCK>::value_type value_type;
241
242 Register_tmpl(BLOCK const &blk, unsigned offset)
243 : Ro_register_tmpl<BITS, BLOCK>(blk, offset)
244 {}
245
246 Register_tmpl() = default;
247
248 /**
249 * \brief write \a val into the hardware register.
250 * \param val the value to write into the hardware register.
251 */
252 Register_tmpl &operator = (value_type val)
253 { this->_b.template write<value_type>(val, this->_o); return *this; }
254
255 /**
256 * \brief write \a val into the hardware register.
257 * \param val the value to write into the hardware register.
258 */
259 void write(value_type val)
260 { this->_b.template write<value_type>(val, this->_o); }
261
262 /**
263 * \brief set bits in \a set_bits in the hardware register.
264 * \param set_bits bits to be set within the hardware register.
265 *
266 * This is a read-modify-write function that does a logical or
267 * of the old value from the register with \a set_bits.
268 *
269 * \code
270 * unsigned old_value = read();
271 * write(old_value | set_bits);
272 * \endcode
273 */
274 value_type set(value_type set_bits)
275 { return this->_b.template set<value_type>(set_bits, this->_o); }
276
277 /**
278 * \brief clears bits in \a clear_bits in the hardware register.
279 * \param clear_bits bits to be cleared within the hardware register.
280 *
281 * This is a read-modify-write function that does a logical and
282 * of the old value from the register with the negated value of
283 * \a clear_bits.
284 *
285 * \code
286 * unsigned old_value = read();
287 * write(old_value & ~clear_bits);
288 * \endcode
289 */
290 value_type clear(value_type clear_bits)
291 { return this->_b.template clear<value_type>(clear_bits, this->_o); }
292
293 /**
294 * \brief clears bits in \a clear_bits and sets bits in \a set_bits
295 * in the hardware register.
296 * \param clear_bits bits to be cleared within the hardware register.
297 * \param set_bits bits to set in the hardware register.
298 *
299 * This is a read-modify-write function that first does a logical and
300 * of the old value from the register with the negated value of
301 * \a clear_bits and then does a logical or with \a set_bits.
302 *
303 * \code{.c}
304 * unsigned old_value = read();
305 * write((old_value & ~clear_bits) | set_bits);
306 * \endcode
307 */
308 value_type modify(value_type clear_bits, value_type set_bits)
309 { return this->_b.template modify<value_type>(clear_bits, set_bits, this->_o); }
310};
311
312
313/**
314 * \brief Handles a reference to a register block of the given
315 * maximum access width.
316 * \tparam MAX_BITS Maximum access width for the registers in this
317 * block.
318 * \tparam BLOCK Type implementing the register accesses (`read<>()`,
319 * `write<>()`, `modify<>()`, `set<>()`, and `clear<>()`).
320 *
321 * Provides access to registers in this block via r<WIDTH>() and
322 * operator[]().
323 */
324template<
325 unsigned MAX_BITS,
326 typename BLOCK = Register_block_tmpl<
327 Register_block_base<MAX_BITS>
328 >
329>
330class Register_block
331{
332private:
333 template< unsigned B, typename BLK > friend class Register_block;
334 template< unsigned B, typename BLK > friend class Ro_register_block;
335 typedef BLOCK Block;
336 Block _b;
337
338public:
339 Register_block() = default;
340 Register_block(Block const &blk) : _b(blk) {}
341 Register_block &operator = (Block const &blk)
342 { _b = blk; return *this; }
343
344 template< unsigned BITS >
345 Register_block(Register_block<BITS> blk) : _b(blk._b) {}
346
347 typedef Register_tmpl<MAX_BITS, Block> Register;
348 typedef Ro_register_tmpl<MAX_BITS, Block> Ro_register;
349
350 /**
351 * \brief Read only access to register at offset \a offset.
352 * \tparam BITS the access width in bits for the register.
353 * \param offset The offset of the register within the register file.
354 * \return register object allowing read only access with width \a BITS.
355 */
356 template< unsigned BITS >
357 Ro_register_tmpl<BITS, Block> r(unsigned offset) const
358 { return Ro_register_tmpl<BITS, Block>(this->_b, offset); }
359
360 /**
361 * \brief Read only access to register at offset \a offset.
362 * \param offset The offset of the register within the register file.
363 * \return register object allowing read only access with width \a MAX_BITS.
364 */
365 Ro_register operator [] (unsigned offset) const
366 { return this->r<MAX_BITS>(offset); }
367
368
369 /**
370 * \brief Read/write access to register at offset \a offset.
371 * \tparam BITS the access width in bits for the register.
372 * \param offset The offset of the register within the register file.
373 * \return register object allowing read and write access with width \a BITS.
374 */
375 template< unsigned BITS >
376 Register_tmpl<BITS, Block> r(unsigned offset)
377 { return Register_tmpl<BITS, Block>(this->_b, offset); }
378
379 /**
380 * \brief Read/write access to register at offset \a offset.
381 * \param offset The offset of the register within the register file.
382 * \return register object allowing read and write access with
383 * width \a MAX_BITS.
384 */
385 Register operator [] (unsigned offset)
386 { return this->r<MAX_BITS>(offset); }
387};
388
389/**
390 * \brief Handles a reference to a read only register block of the given
391 * maximum access width.
392 * \tparam MAX_BITS Maximum access width for the registers in this block.
393 * \tparam BLOCK Type implementing the register accesses (read<>()),
394 *
395 * Provides read only access to registers in this block via r<WIDTH>()
396 * and operator[]().
397 */
398template<
399 unsigned MAX_BITS,
400 typename BLOCK = Register_block_tmpl<
401 Register_block_base<MAX_BITS> const
402 >
403>
404class Ro_register_block
405{
406private:
407 template< unsigned B, typename BLK > friend class Ro_register_block;
408 typedef BLOCK Block;
409 Block _b;
410
411public:
412 Ro_register_block() = default;
413 Ro_register_block(BLOCK const &blk) : _b(blk) {}
414
415 template< unsigned BITS >
416 Ro_register_block(Register_block<BITS> const &blk) : _b(blk._b) {}
417
418 typedef Ro_register_tmpl<MAX_BITS, Block> Ro_register;
419 typedef Ro_register Register;
420
421 /**
422 * \brief Read only access to register at offset \a offset.
423 * \param offset The offset of the register within the register file.
424 * \return register object allowing read only access with width \a MAX_BITS.
425 */
426 Ro_register operator [] (unsigned offset) const
427 { return Ro_register(this->_b, offset); }
428
429 /**
430 * \brief Read only access to register at offset \a offset.
431 * \tparam BITS the access width in bits for the register.
432 * \param offset The offset of the register within the register file.
433 * \return register object allowing read only access with width \a BITS.
434 */
435 template< unsigned BITS >
436 Ro_register_tmpl<BITS, Block> r(unsigned offset) const
437 { return Ro_register_tmpl<BITS, Block>(this->_b, offset); }
438};
439
440
441/**
442 * \brief Implementation helper for register blocks.
443 * \param BASE The class implementing read<> and write<> template functions
444 * for accessing the registers. This class must inherit from
445 * Register_block_impl.
446 * \param MAX_BITS The maximum access width for the register file.
447 * Supported values are 8, 16, 32, or 64.
448 *
449 *
450 * This template allows easy implementation of register files by providing
451 * read<> and write<> template functions, see Mmio_register_block
452 * as an example.
453 */
454template< typename BASE, unsigned MAX_BITS = 32 >
455struct Register_block_impl;
456
457#define REGBLK_IMPL_RW_TEMPLATE(sz, ...) \
458 l4_uint##sz##_t do_read_##sz(l4_addr_t reg) const override \
459 { return static_cast<BASE const *>(this)->template read<l4_uint##sz##_t>(reg); } \
460 \
461 void do_write_##sz(l4_uint##sz##_t value, l4_addr_t reg) override \
462 { static_cast<BASE*>(this)->template write<l4_uint##sz##_t>(value, reg); }
463
464
465template< typename BASE >
466struct Register_block_impl<BASE, 8> : public Register_block_base<8>
467{
468 REGBLK_IMPL_RW_TEMPLATE(8);
469};
470
471template< typename BASE >
472struct Register_block_impl<BASE, 16> : public Register_block_base<16>
473{
474 REGBLK_IMPL_RW_TEMPLATE(8);
475 REGBLK_IMPL_RW_TEMPLATE(16);
476};
477
478template< typename BASE >
479struct Register_block_impl<BASE, 32> : public Register_block_base<32>
480{
481 REGBLK_IMPL_RW_TEMPLATE(8);
482 REGBLK_IMPL_RW_TEMPLATE(16);
483 REGBLK_IMPL_RW_TEMPLATE(32);
484};
485
486template< typename BASE >
487struct Register_block_impl<BASE, 64> : public Register_block_base<64>
488{
489 REGBLK_IMPL_RW_TEMPLATE(8);
490 REGBLK_IMPL_RW_TEMPLATE(16);
491 REGBLK_IMPL_RW_TEMPLATE(32);
492 REGBLK_IMPL_RW_TEMPLATE(64);
493};
494
495#undef REGBLK_IMPL_RW_TEMPLATE
496
497}