// vi:set ft=cpp: -*- Mode: C++ -*-
/*
 * Copyright (C) 2013 Technische Universität Dresden.
 *
 * License: see LICENSE.spdx (in this directory or the directories above)
 */

#pragma once

namespace cxx {

/**
 * Read the value at an address at most once.
 *
 * The read might be omitted if the result is not used by any code unless
 * `typename` contains `volatile`. If the read operation has side effects and
 * must not be omitted, use different means like L4drivers::Mmio_register_block
 * or similar.
 *
 * The compiler is disallowed to reuse a previous read at the same address, for
 * example:
 * ```
 * val1 = *a;
 * val2 = access_once(a);  // compiler may not replace this by val2 = val1;
 * ```
 *
 * The compiler is also disallowed to repeat the read, for example:
 * ```
 * val1 = access_once(a);
 * val2 = val1;  // compiler may not replace this by val2 = *a;
 * ```
 *
 * The above implies that the compiler is also disallowed to move the read out
 * of or into loops.
 *
 * \note The read might still be moved relative to other code.
 * \note The value might be read from a hardware cache, not from RAM.
 */
template< typename T > inline
T access_once(T const *a)
{
#if 1
  __asm__ __volatile__ ( "" : "=m"(*const_cast<T*>(a)));
  T tmp = *a;
  __asm__ __volatile__ ( "" : "=m"(*const_cast<T*>(a)));
  return tmp;
#else
  return *static_cast<T const volatile *>(a);
#endif
}

/**
 * Write a value at an address exactly once.
 *
 * The compiler is disallowed to skip the write, for example:
 * ```
 * *a = val;
 * write_now(a, val);  // compiler may not skip this line
 * ```
 *
 * The compiler is also disallowed to repeat the write.
 *
 * The above implies that the compiler is also disallowed to move the write out
 * of or into loops.
 *
 * \note The write might still be moved relative to other code.
 * \note The value might be written just to a hardware cache for the moment, not
 *       immediately to RAM.
 */
template< typename T, typename VAL > inline
void write_now(T *a, VAL &&val)
{
  __asm__ __volatile__ ( "" : "=m"(*a));
  *a = val;
  __asm__ __volatile__ ( "" : : "m"(*a));
}


}

