[sw] Collect various "runtime-ey" functions into lib/runtime.

This directory is made distinct from libbase mostly for semantic
reasons:
- It needs to know timing-ey things about Ibex, which are isolated in
  ibex.h.
- It's more "system-aware" than what we tend to put in libbase.

hart.h contains "execution control" functions that may be
RISC-V-flavored, but independent of ibex.h.

Signed-off-by: Miguel Young de la Sota <mcyoung@google.com>
diff --git a/sw/device/boot_rom/meson.build b/sw/device/boot_rom/meson.build
index 180e591..7f26417 100644
--- a/sw/device/boot_rom/meson.build
+++ b/sw/device/boot_rom/meson.build
@@ -34,6 +34,7 @@
   link_depends: rom_link_deps,
   dependencies: [
     chip_info_h,
+    sw_lib_runtime_hart,
     sw_lib_flash_ctrl,
     sw_lib_pinmux,
     sw_lib_gpio,
diff --git a/sw/device/examples/hello_usbdev/hello_usbdev.c b/sw/device/examples/hello_usbdev/hello_usbdev.c
index 2152ef1..65509aa 100644
--- a/sw/device/examples/hello_usbdev/hello_usbdev.c
+++ b/sw/device/examples/hello_usbdev/hello_usbdev.c
@@ -5,6 +5,7 @@
 #include "sw/device/lib/base/stdasm.h"
 #include "sw/device/lib/common.h"
 #include "sw/device/lib/gpio.h"
+#include "sw/device/lib/runtime/hart.h"
 #include "sw/device/lib/uart.h"
 #include "sw/device/lib/usb_controlep.h"
 #include "sw/device/lib/usb_simpleserial.h"
@@ -32,38 +33,12 @@
 static usb_controlep_ctx_t control_ctx;
 static usb_ss_ctx_t ss_ctx[2];
 
-/**
- * Delay loop executing within 8 cycles on ibex
- */
-static void delay_loop_ibex(unsigned long loops) {
-  int out; /* only to notify compiler of modifications to |loops| */
-  asm volatile(
-      "1: nop             \n"  // 1 cycle
-      "   nop             \n"  // 1 cycle
-      "   nop             \n"  // 1 cycle
-      "   nop             \n"  // 1 cycle
-      "   addi %1, %1, -1 \n"  // 1 cycle
-      "   bnez %1, 1b     \n"  // 3 cycles
-      : "=&r"(out)
-      : "0"(loops));
-}
-
-static int usleep_ibex(unsigned long usec) {
-  unsigned long usec_cycles;
-  usec_cycles = CLK_FIXED_FREQ_HZ * usec / 1000 / 1000 / 8;
-
-  delay_loop_ibex(usec_cycles);
-  return 0;
-}
-
-static int usleep(unsigned long usec) { return usleep_ibex(usec); }
-
 static void test_error(void) {
   while (1) {
     gpio_write_all(0xAA00);  // pattern
-    usleep(200 * 1000);
+    busy_sleep_micros(200 * 1000);
     gpio_write_all(0x5500);  // pattern
-    usleep(100 * 1000);
+    busy_sleep_micros(100 * 1000);
   }
 }
 
@@ -104,7 +79,7 @@
 
   // Give a LED pattern as startup indicator for 5 seconds
   gpio_write_all(0xAA00);  // pattern
-  usleep(1000);
+  busy_sleep_micros(1000);
   gpio_write_all(0x5500);  // pattern
   // usbdev_init here so dpi code will not start until simulation
   // got through all the printing (which takes a while if --trace)
diff --git a/sw/device/examples/hello_usbdev/meson.build b/sw/device/examples/hello_usbdev/meson.build
index 9b50288..dc89956 100644
--- a/sw/device/examples/hello_usbdev/meson.build
+++ b/sw/device/examples/hello_usbdev/meson.build
@@ -7,6 +7,7 @@
   sources: ['hello_usbdev.c'],
   name_suffix: 'elf',
   dependencies: [
+    sw_lib_runtime_hart,
     sw_lib_gpio,
     sw_lib_irq,
     sw_lib_uart,
diff --git a/sw/device/examples/hello_world/hello_world.c b/sw/device/examples/hello_world/hello_world.c
index b1338ca..0e55c53 100644
--- a/sw/device/examples/hello_world/hello_world.c
+++ b/sw/device/examples/hello_world/hello_world.c
@@ -6,37 +6,12 @@
 #include "sw/device/lib/common.h"
 #include "sw/device/lib/gpio.h"
 #include "sw/device/lib/pinmux.h"
+#include "sw/device/lib/runtime/hart.h"
 #include "sw/device/lib/spi_device.h"
 #include "sw/device/lib/uart.h"
 
 #define SPI_MAX 32
 
-/**
- * Delay loop executing within 8 cycles on ibex
- */
-static void delay_loop_ibex(unsigned long loops) {
-  int out; /* only to notify compiler of modifications to |loops| */
-  asm volatile(
-      "1: nop             \n"  // 1 cycle
-      "   nop             \n"  // 1 cycle
-      "   nop             \n"  // 1 cycle
-      "   nop             \n"  // 1 cycle
-      "   addi %1, %1, -1 \n"  // 1 cycle
-      "   bnez %1, 1b     \n"  // 3 cycles
-      : "=&r"(out)
-      : "0"(loops));
-}
-
-static int usleep_ibex(unsigned long usec) {
-  unsigned long usec_cycles;
-  usec_cycles = CLK_FIXED_FREQ_HZ * usec / 1000 / 1000 / 8;
-
-  delay_loop_ibex(usec_cycles);
-  return 0;
-}
-
-static int usleep(unsigned long usec) { return usleep_ibex(usec); }
-
 // called from ctr0 when something bad happens
 // char I=illegal instruction, A=lsu error (address), E=ecall
 void trap_handler(uint32_t mepc, char c) {
@@ -44,9 +19,9 @@
   uart_send_uint(mepc, 32);
   while (1) {
     gpio_write_all(0xAA00);  // pattern
-    usleep(200 * 1000);
+    busy_sleep_micros(200 * 1000);
     gpio_write_all(0x5500);  // pattern
-    usleep(100 * 1000);
+    busy_sleep_micros(100 * 1000);
   }
 }
 
@@ -70,7 +45,7 @@
   // Give a LED pattern as startup indicator for 5 seconds
   gpio_write_all(0xFF00);  // all LEDs on
   for (int i = 0; i < 32; i++) {
-    usleep(100 * 1000);  // 100 ms
+    busy_sleep_micros(100 * 1000);  // 100 ms
 
     gpio_write_bit(8 + (i % 8), (i / 8));
   }
@@ -91,7 +66,7 @@
   spid_send("SPI!", 4);
 
   while (1) {
-    usleep(10 * 1000);  // 10 ms
+    busy_sleep_micros(10 * 1000);  // 10 ms
 
     // report changed switches over UART
     gpio_in = gpio_read() & 0x100FF;  // 0-7 is switch input, 16 is FTDI
diff --git a/sw/device/examples/hello_world/meson.build b/sw/device/examples/hello_world/meson.build
index 6caee00..11c3099 100644
--- a/sw/device/examples/hello_world/meson.build
+++ b/sw/device/examples/hello_world/meson.build
@@ -7,6 +7,7 @@
   sources: ['hello_world.c'],
   name_suffix: 'elf',
   dependencies: [
+    sw_lib_runtime_hart,
     sw_lib_pinmux,
     sw_lib_gpio,
     sw_lib_irq,
diff --git a/sw/device/lib/base/memory.c b/sw/device/lib/base/memory.c
index d403989..177cbfa 100644
--- a/sw/device/lib/base/memory.c
+++ b/sw/device/lib/base/memory.c
@@ -25,8 +25,3 @@
   return dest;
 }
 
-noreturn void abort(void) {
-  while (true) {
-    asm volatile("wfi");
-  }
-}
diff --git a/sw/device/lib/base/memory.h b/sw/device/lib/base/memory.h
index bf1d280..060523c 100644
--- a/sw/device/lib/base/memory.h
+++ b/sw/device/lib/base/memory.h
@@ -94,14 +94,4 @@
  */
 void *memset(void *dest, int value, size_t len);
 
-// TODO: Find a better header for this function to live. See #1207.
-/**
- * Immediately halt program execution.
- *
- * This function conforms to the semantics defined in ISO C11 S7.22.4.1, except
- * that it cannot be pre-empted by signal handlers (which do not exist in this
- * environment).
- */
-noreturn void abort(void);
-
 #endif  // SW_DEVICE_LIB_BASE_MEMORY_H_
diff --git a/sw/device/lib/common.h b/sw/device/lib/common.h
index 3aea0dc..41d3b67 100644
--- a/sw/device/lib/common.h
+++ b/sw/device/lib/common.h
@@ -6,10 +6,8 @@
 #define _COMMON_H_
 
 #ifdef SIMULATION
-#define CLK_FIXED_FREQ_HZ (500 * 1000)
 static const unsigned long UART_BAUD_RATE = 9600;
 #else
-#define CLK_FIXED_FREQ_HZ (50ULL * 1000 * 1000)
 static const unsigned long UART_BAUD_RATE = 230400;
 #endif
 
diff --git a/sw/device/lib/gpio.c b/sw/device/lib/gpio.c
index d8a6f05..8c1d737 100644
--- a/sw/device/lib/gpio.c
+++ b/sw/device/lib/gpio.c
@@ -6,6 +6,7 @@
 
 #include "sw/device/lib/base/memory.h"
 #include "sw/device/lib/common.h"
+#include "sw/device/lib/runtime/hart.h"
 
 void gpio_init(uint32_t oe) { REG32(GPIO_DIRECT_OE(0)) = oe; }
 
diff --git a/sw/device/lib/meson.build b/sw/device/lib/meson.build
index dae41b9..6b139c5 100644
--- a/sw/device/lib/meson.build
+++ b/sw/device/lib/meson.build
@@ -3,6 +3,7 @@
 # SPDX-License-Identifier: Apache-2.0
 
 subdir('base')
+subdir('runtime')
 
 # UART library (sw_lib_uart)
 sw_lib_uart = declare_dependency(
@@ -12,7 +13,8 @@
     sources: [
       hw_ip_uart_reg_h,
       'uart.c',
-    ]
+    ],
+    dependencies: [sw_lib_runtime_ibex],
   )
 )
 
@@ -36,7 +38,8 @@
     sources: [
       hw_ip_gpio_reg_h,
       'gpio.c',
-    ]
+    ],
+    dependencies: [sw_lib_runtime_hart],
   )
 )
 
@@ -110,6 +113,7 @@
     ],
     dependencies: [
       sw_lib_irq,
+      sw_lib_runtime_ibex,
       sw_lib_uart,
     ],
   )
@@ -123,7 +127,8 @@
     sources: [
       hw_ip_rv_timer_reg_h,
       'rv_timer.c',
-    ]
+    ],
+    dependencies: [sw_lib_runtime_ibex],
   )
 )
 
diff --git a/sw/device/lib/runtime/README.md b/sw/device/lib/runtime/README.md
new file mode 100644
index 0000000..ee9d84a
--- /dev/null
+++ b/sw/device/lib/runtime/README.md
@@ -0,0 +1,5 @@
+# `libruntime`, the OpenTitan Runtime Library
+
+This directory contains machine-aware base libraries for OpenTitan, being more
+aware and able to control the execution environment than the "pure algorithm"
+functions in `libbase`.
diff --git a/sw/device/lib/runtime/hart.c b/sw/device/lib/runtime/hart.c
new file mode 100644
index 0000000..1f02ac9
--- /dev/null
+++ b/sw/device/lib/runtime/hart.c
@@ -0,0 +1,22 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/lib/runtime/hart.h"
+
+#include <stdbool.h>
+
+#include "sw/device/lib/runtime/ibex.h"
+
+extern void wait_for_interrupt(void);
+
+void busy_sleep_micros(size_t microseconds) {
+  size_t cycles = kIbexClockFreqHz * microseconds / 10000000;
+  ibex_busy_loop(cycles);
+}
+
+noreturn void abort(void) {
+  while (true) {
+    wait_for_interrupt();
+  }
+}
diff --git a/sw/device/lib/runtime/hart.h b/sw/device/lib/runtime/hart.h
new file mode 100644
index 0000000..2290076
--- /dev/null
+++ b/sw/device/lib/runtime/hart.h
@@ -0,0 +1,40 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef SW_DEVICE_LIB_RUNTIME_HART_H_
+#define SW_DEVICE_LIB_RUNTIME_HART_H_
+
+#include <stddef.h>
+#include <stdnoreturn.h>
+
+#include "sw/device/lib/base/stdasm.h"
+
+/**
+ * This header provides functions for controlling the excution of a hart, such
+ * as halt-like functionality.
+ */
+
+/**
+ * Hints to the processor that we don't have anything better to be doing, and to
+ * go into low-power mode until an interrupt is serviced.
+ *
+ * This function may behave as if it is a no-op.
+ */
+inline void wait_for_interrupt(void) { asm volatile("wfi"); }
+
+/**
+ * Spin for roughly the given number of microseconds.
+ *
+ * @param microseconds the duration for which to spin.
+ */
+void busy_sleep_micros(size_t microseconds);
+
+/**
+ * Immediately halt program execution.
+ *
+ * This function conforms to the semantics defined in ISO C11 S7.22.4.1.
+ */
+noreturn void abort(void);
+
+#endif  // SW_DEVICE_LIB_RUNTIME_HART_H_
diff --git a/sw/device/lib/runtime/ibex.c b/sw/device/lib/runtime/ibex.c
new file mode 100644
index 0000000..854d33d
--- /dev/null
+++ b/sw/device/lib/runtime/ibex.c
@@ -0,0 +1,13 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/lib/runtime/ibex.h"
+
+#ifdef SIMULATION
+const size_t kIbexClockFreqHz = 500 * 1000;  // 500 kHz
+#else
+const size_t kIbexClockFreqHz = 50 * 1000 * 1000;  // 50 MHz
+#endif
+
+extern void ibex_busy_loop(size_t);
diff --git a/sw/device/lib/runtime/ibex.h b/sw/device/lib/runtime/ibex.h
new file mode 100644
index 0000000..a31d609
--- /dev/null
+++ b/sw/device/lib/runtime/ibex.h
@@ -0,0 +1,41 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef SW_DEVICE_LIB_RUNTIME_IBEX_H_
+#define SW_DEVICE_LIB_RUNTIME_IBEX_H_
+
+#include <stddef.h>
+
+#include "sw/device/lib/base/stdasm.h"
+
+/**
+ * This header provides Ibex-specific functions, such as the clock frequency and
+ * cycle-accurate busy loops.
+ */
+
+/**
+ * The clock frequency of the Ibex core, in hertz.
+ */
+extern const size_t kIbexClockFreqHz;
+
+/**
+ * Spins for roughly the number of |cycles| given. For best results, |cycles|
+ * should be a multiple of eight.
+ *
+ * This function should not be used for time-keeping.
+ *
+ * @param cycles the number of cycles to burn.
+ */
+inline void ibex_busy_loop(size_t cycles) {
+  size_t out;  // Used to create an inout parameter below.
+  asm volatile(
+      "busy_loop%=:"
+      "  nop; nop; nop; nop;"    // 4 cycles.
+      "  addi %1, %1, -8;"       // 1 cycle.
+      "  blez %1, busy_loop%=;"  // 3 cycles.
+      : "=&r"(out)
+      : "0"(cycles));
+}
+
+#endif  // SW_DEVICE_LIB_RUNTIME_IBEX_H_
diff --git a/sw/device/lib/runtime/meson.build b/sw/device/lib/runtime/meson.build
new file mode 100644
index 0000000..6e7e5ab
--- /dev/null
+++ b/sw/device/lib/runtime/meson.build
@@ -0,0 +1,18 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+sw_lib_runtime_ibex = declare_dependency(
+  link_with: static_library(
+    'runtime_ibex_ot',
+    sources: ['ibex.c'],
+  )
+)
+
+sw_lib_runtime_hart = declare_dependency(
+  link_with: static_library(
+    'runtime_hart_ot',
+    sources: ['hart.c'],
+    dependencies: [sw_lib_runtime_ibex],
+  )
+)
diff --git a/sw/device/lib/rv_timer.c b/sw/device/lib/rv_timer.c
index caa8f24..f6f1b4a 100644
--- a/sw/device/lib/rv_timer.c
+++ b/sw/device/lib/rv_timer.c
@@ -5,8 +5,8 @@
 #include "sw/device/lib/rv_timer.h"
 
 #include "rv_timer_regs.h"  // Generated.
-
 #include "sw/device/lib/common.h"
+#include "sw/device/lib/runtime/ibex.h"
 
 #define RV_TIMER0_BASE_ADDR 0x40080000
 #define HART_CFG_ADDR_GAP 0x100
@@ -14,7 +14,7 @@
 static const uint32_t NS_IN_S = 1000 * 1000 * 1000;
 
 void rv_timer_set_us_tick(uint32_t hart) {
-  uint32_t ticks_per_us = (uint32_t)((1000 * CLK_FIXED_FREQ_HZ) / NS_IN_S) - 1;
+  uint32_t ticks_per_us = (uint32_t)((1000 * kIbexClockFreqHz) / NS_IN_S) - 1;
 
   REG32(RV_TIMER_CFG0(0) + hart * 4) =
       (ticks_per_us & RV_TIMER_CFG0_PRESCALE_MASK) |
diff --git a/sw/device/lib/uart.c b/sw/device/lib/uart.c
index 5fba477..ffa936c 100644
--- a/sw/device/lib/uart.c
+++ b/sw/device/lib/uart.c
@@ -5,10 +5,11 @@
 #include "sw/device/lib/uart.h"
 
 #include "sw/device/lib/common.h"
+#include "sw/device/lib/runtime/ibex.h"
 
 inline void uart_init(unsigned int baud) {
   // nco = 2^20 * baud / fclk
-  uint64_t uart_ctrl_nco = ((uint64_t)baud << 20) / CLK_FIXED_FREQ_HZ;
+  uint64_t uart_ctrl_nco = ((uint64_t)baud << 20) / kIbexClockFreqHz;
   REG32(UART_CTRL(0)) =
       ((uart_ctrl_nco & UART_CTRL_NCO_MASK) << UART_CTRL_NCO_OFFSET) |
       (1 << UART_CTRL_TX) | (1 << UART_CTRL_RX);
diff --git a/sw/device/tests/flash_ctrl/flash_test.c b/sw/device/tests/flash_ctrl/flash_test.c
index 478c16e..6df32dc 100644
--- a/sw/device/tests/flash_ctrl/flash_test.c
+++ b/sw/device/tests/flash_ctrl/flash_test.c
@@ -6,40 +6,15 @@
 #include "sw/device/lib/common.h"
 #include "sw/device/lib/flash_ctrl.h"
 #include "sw/device/lib/gpio.h"
+#include "sw/device/lib/runtime/hart.h"
 #include "sw/device/lib/uart.h"
 
-/**
- * Delay loop executing within 8 cycles on ibex
- */
-static void delay_loop_ibex(unsigned long loops) {
-  int out; /* only to notify compiler of modifications to |loops| */
-  asm volatile(
-      "1: nop             \n"  // 1 cycle
-      "   nop             \n"  // 1 cycle
-      "   nop             \n"  // 1 cycle
-      "   nop             \n"  // 1 cycle
-      "   addi %1, %1, -1 \n"  // 1 cycle
-      "   bnez %1, 1b     \n"  // 3 cycles
-      : "=&r"(out)
-      : "0"(loops));
-}
-
-static int usleep_ibex(unsigned long usec) {
-  unsigned long usec_cycles;
-  usec_cycles = CLK_FIXED_FREQ_HZ * usec / 1000 / 1000 / 8;
-
-  delay_loop_ibex(usec_cycles);
-  return 0;
-}
-
-static int usleep(unsigned long usec) { return usleep_ibex(usec); }
-
 static void break_on_error(uint32_t error) {
   if (error) {
     // inifinitely fetch instructions, will flag an assertion error
     uart_send_str("FAIL!\r\n");
     while (1) {
-      usleep(100);
+      busy_sleep_micros(100);
     }
   }
 }
diff --git a/sw/device/tests/flash_ctrl/meson.build b/sw/device/tests/flash_ctrl/meson.build
index d1c60f8..4be3f42 100644
--- a/sw/device/tests/flash_ctrl/meson.build
+++ b/sw/device/tests/flash_ctrl/meson.build
@@ -7,6 +7,7 @@
   sources: ['flash_test.c'],
   name_suffix: 'elf',
   dependencies: [
+    sw_lib_runtime_hart,
     sw_lib_flash_ctrl,
     sw_lib_gpio,
     sw_lib_irq,