RISC-V: A Bare-metal Introduction using C++. Machine Mode Timer.

The Machine Level ISA Timer

Timekeeping in Modern C++

namespace driver {
struct default_timer_config {
static constexpr unsigned int MTIME_FREQ_HZ=32768;
template<class CONFIG=default_timer_config> class timer {
/** Duration of each timer tick */
using timer_ticks = std::chrono::duration<int,
std::ratio<1, CONFIG::MTIME_FREQ_HZ>>;
uint64_t value_from_mtime = ...;
auto value_in_ms =
auto time_offset = std::chrono::microseconds(???);
uint64_t value_of_mtimecmp = std::chrono::duration_cast<timer_ticks>

Reading/Writing MMIO Registers in C++

struct mtimer_address_spec {
static constexpr std::uintptr_t
MTIMECMP_ADDR = 0x2000000 + 0x4000;
static constexpr std::uintptr_t
MTIME_ADDR = 0x2000000 + 0xBFF8;
template<class ADDRESS_SPEC=mtimer_address_spec>
void set_raw_time_cmp(uint64_t clock_offset) {
// Single bus access
auto mtimecmp = reinterpret_cast<volatile std::uint64_t *>
*mtimecmp = *mtimecmp + clock_offset;


  • MMIO access and static polymorphism.
  • Hardware real-time clocks.
  • Converting clock frequencies and periods to human-readable units.
  • Configuring drivers via templates and constexpr.

64 Bit Registers Access on a 32 Bit Bus

  1. The mtime is 0x0000_0000_FFFF_FFFF.
  2. We read the top 32 bits, 0x0000_0000
  3. We save this into our register t0.
  4. The real time clock ticks.
  5. The mtime is 0x0000_0001_0000_0000.
  6. We read the bottom 32 bits, 0x0000_0000.
  7. We save this into our register t1.
  8. We check the time in t0:t1, it’s 0x0000_0000_0000_0000!



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Phil Mulholland

Phil Mulholland

Experienced in Distributed Systems, Event-Driven Systems, Firmware for SoC/MCU, Systems Simulation, Network Monitoring and Analysis, Automated Testing and RTL.