diff --git a/py/mpconfig.h b/py/mpconfig.h
index ea90ec9841761e20ba4df4c2a6bbc3861fbbc301..7a71ebd950bae941af7f8b014d24342cee9e71a1 100644
--- a/py/mpconfig.h
+++ b/py/mpconfig.h
@@ -446,6 +446,11 @@
 #   endif
 #endif
 
+// Whether to provide the mp_kbd_exception object
+#ifndef MICROPY_KBD_EXCEPTION
+#define MICROPY_KBD_EXCEPTION (0)
+#endif
+
 // Prefer to raise KeyboardInterrupt asynchronously (from signal or interrupt
 // handler) - if supported by a particular port.
 #ifndef MICROPY_ASYNC_KBD_INTR
diff --git a/py/mpstate.h b/py/mpstate.h
index 439ed660662ee14145f4d2e4bb47aa0aa6bf32af..91fb68b3ad6b87143a7829cf6d4dc425205fb7f3 100644
--- a/py/mpstate.h
+++ b/py/mpstate.h
@@ -118,6 +118,11 @@ typedef struct _mp_state_vm_t {
     #endif
     #endif
 
+    #if MICROPY_KBD_EXCEPTION
+    // exception object of type KeyboardInterrupt
+    mp_obj_exception_t mp_kbd_exception;
+    #endif
+
     // dictionary with loaded modules (may be exposed as sys.modules)
     mp_obj_dict_t mp_loaded_modules_dict;
 
diff --git a/py/runtime.c b/py/runtime.c
index e7e35a081afcc3a92cf7c118a21006b508c2d054..8b4420926ce1bb34d68092bf11d0238cbb1cfafc 100644
--- a/py/runtime.c
+++ b/py/runtime.c
@@ -68,6 +68,15 @@ void mp_init(void) {
     mp_init_emergency_exception_buf();
 #endif
 
+    #if MICROPY_KBD_EXCEPTION
+    // initialise the exception object for raising KeyboardInterrupt
+    MP_STATE_VM(mp_kbd_exception).base.type = &mp_type_KeyboardInterrupt;
+    MP_STATE_VM(mp_kbd_exception).traceback_alloc = 0;
+    MP_STATE_VM(mp_kbd_exception).traceback_len = 0;
+    MP_STATE_VM(mp_kbd_exception).traceback_data = NULL;
+    MP_STATE_VM(mp_kbd_exception).args = mp_const_empty_tuple;
+    #endif
+
     // call port specific initialization if any
 #ifdef MICROPY_PORT_INIT_FUNC
     MICROPY_PORT_INIT_FUNC;