microbit: Memory Optimisation Mega Update

This release contains a widespread set of updates and optimisations to the micro:bit
runtime, with a view to reducing the SRAM footprint of the whole system. This is to
provide as much usable HEAP storage for application programs as possible.

Specific updates and optimisations include:

  - Additional compilation flags to allow the core micro:bit runtime to be configured.
    These are defined in MicroBitConfig.h

  - A custom heap allocator. This is now included for two reasons:

    1) To provide a simple mechanism to to utilise both the mbed heap space and other memory
       regions (such as unused memory in the SoftDevice region) as a single virtual heap.
    2) To address some issues that have been noted that are attributable to heap fragmentation.
       The micro:bit heap allocator has a simple algorithm, but one that is chosen to respond
       well to the relativelt high 'heap churn' found in the micro:bit environment.

    All micro:bit components and user programs now use this heap allocator trasparently.

  - Updates to BLE services to remove persistent references to their GATT services. This consumes
    vast amounts SRAM, rather unecessarily. Instead only handles to the relevant GATT characteristics
    are now stored. This specifically includes:
      + MicroBitDFUService
      + MicroBitEventService
      + DeviceInformationService

  - Updates to the Fiber scheduler to save SRAM. More specifically:
      + Removed the need to hold an empty processor context to intialise fibers.
      + The IDLE fiber now runs without a stack
      + fiber stacks are now only created when a fiber is descheduled for the first time, thereby reducing heap churn.
      + the 'main' fiber is now recycled into the fiber_pool if it leaves app_main()
      + fibers created through invoke() now only maintains the necessary part of teh parent stack that is needed, thereby
        reducing the stack size of spawned fibers.

  - Updates to the Message Bus to reduce the overall memory footprint of processing events. More specifically:
      + Event handlers are now always called using invoke(), such that non-blocking event handlers no longer need
        a dedicated fiber to execute - thereby saving SRAM and processor time.
      + Processing of events from the event queue is now rate paced. Events only continue to be processed as long as there
        are no fibers on the run queue. i.e. event processing is no longer greedy, thereby reducing the number of fibers
        created on the runqueue.

  - Updates to BLUEZOENE code to bring up core BLE services even if they are not enabled by default. This allows
    programs that do not require BLE to operate to benefit from the full range of SRAM, whilst still allowing the
    device to be programmed over BLE.

  - Updates to the Soft Device initialisation configuration, reducing the size of the GATT table held in the top 1.8K
    of its 8K memory region to around 800 bytes. This is sufficient to run the default set of BLE services on the micro:bit
    so the additional memory is configured as HEAP storage by MicroBitHeapAllocator.

  - Minor changes to a range of components to integrate with the above changes.
    + rename of free() to release() in DynamicPWM to avoid namespace collision with MicroBitHeap free()
    + rename of fork_on_block to invoke() to enhance readbility.

  - Many code cleanups and updates to out of date comments.
This commit is contained in:
Joe Finney 2015-08-31 23:25:10 +01:00
parent 42f43b2255
commit 857a626ed0
27 changed files with 1054 additions and 555 deletions

View file

@ -70,10 +70,10 @@ class DynamicPwm : public PwmOut
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate();
* pwm->free();
* pwm->release();
* @endcode
*/
void free();
void release();
/**
* Retreives the pin name associated with this DynamicPwm instance.

View file

@ -2,14 +2,26 @@
#define ERROR_NO_H
/**
* Error codes using in the Micro:bit runtime.
* Error codes used in the micro:bit runtime.
*/
enum Error{
MICROBIT_INVALID_VALUE = -1, // currently only used in MicroBit.cpp rand function when the max is less or equal to 0.
MICROBIT_IO_OP_NA = -2, // used in MicroBitPin.cpp for when a pin cannot perform a transition. (microbit io operation not allowed)
MICROBIT_COMPASS_IS_CALIBRATING = -3, // used in MicroBitPin.cpp for when a pin cannot perform a transition. (microbit io operation not allowed)
// Invalid parameter given.
MICROBIT_INVALID_VALUE = -1,
// Invalid I/O operation requested.
MICROBIT_IO_OP_NA = -2,
// Device calibration errors
MICROBIT_COMPASS_IS_CALIBRATING = -3,
MICROBIT_COMPASS_CALIBRATE_REQUIRED = -4,
MICROBIT_OOM = 20, // the MicroBit Out of memory error code...
MICROBIT_I2C_LOCKUP = 10 // the MicroBit I2C bus has locked up
// I2C Communication error occured (typically I2C module on processor has locked up.)
MICROBIT_I2C_LOCKUP = 10,
// Out out memory error. Heap storage was requested, but is not available.
MICROBIT_OOM = 20,
// Corruption detected in the micro:bit heap space
MICROBIT_HEAP_ERROR = 30
};
#endif

View file

@ -1,30 +1,16 @@
#ifndef MICROBIT_H
#define MICROBIT_H
// DEBUG. Enable this to get debug message routed through the USB serial interface.
//#define MICROBIT_DBG
#include "mbed.h"
#include "MicroBitConfig.h"
#include "MicroBitPanic.h"
#ifndef NO_BLE
#include "ble/BLE.h"
#endif
#include "ble/services/DeviceInformationService.h"
//error number enumeration
#include "ErrorNo.h"
/**
* Displays "=(" and an accompanying status code
* @param statusCode the appropriate status code - 0 means no code will be displayed. Status codes must be in the range 0-255.
*/
void panic(int statusCode);
void reset(int statusCode);
#include "MicroBitMalloc.h"
#include "MicroBitHeapAllocator.h"
#include "MicroBitCompat.h"
#include "MicroBitFiber.h"
#include "ManagedType.h"
@ -55,11 +41,10 @@ void reset(int statusCode);
#define MICROBIT_FLAG_DISPLAY_RUNNING 0x00000004
#define MICROBIT_FLAG_COMPASS_RUNNING 0x00000008
// Random number generator
#define NRF51822_RNG_ADDRESS 0x4000D000
#define MICROBIT_IO_PINS 20 // TODO: Need to change for live, currently 3 for test
#define MICROBIT_IO_PINS 20
// Enumeration of core components.
#define MICROBIT_ID_BUTTON_A 1
@ -73,7 +58,7 @@ void reset(int statusCode);
#define MICROBIT_ID_IO_P0 7 //P0 is the left most pad (ANALOG/DIGITAL)
#define MICROBIT_ID_IO_P1 8 //P1 is the middle pad (ANALOG/DIGITAL)
#define MICROBIT_ID_IO_P2 9 //P2 is the right most pad (ANALOG/DIGITAL)
#define MICROBIT_ID_IO_P3 10 //COL1 (ANALOG/DIGITAL)
#define MICROBIT_ID_IO_P3 10 //COL1 (ANALOG/DIGITAL)
#define MICROBIT_ID_IO_P4 11 //BTN_A
#define MICROBIT_ID_IO_P5 12 //COL2 (ANALOG/DIGITAL)
#define MICROBIT_ID_IO_P6 13 //ROW2
@ -93,14 +78,10 @@ void reset(int statusCode);
#define MICROBIT_ID_BUTTON_AB 26 // Button A+B multibutton
// mBed pin assignments of core components.
//TODO: When platform is built for MB2 - pins will be defined by default, these will change...
#define MICROBIT_PIN_SDA P0_30
#define MICROBIT_PIN_SCL P0_0
#define MICROBIT_SYSTEM_COMPONENTS 10
#define MICROBIT_IDLE_COMPONENTS 6
#ifdef MICROBIT_DEBUG
#ifdef MICROBIT_DBG
extern Serial pc;
#endif
@ -128,7 +109,9 @@ class MicroBit
MicroBitI2C i2c;
// Serial Interface
#ifndef MICROBIT_DBG
MicroBitSerial serial;
#endif
// Array of components which are iterated during a system tick
MicroBitComponent* systemTickComponents[MICROBIT_SYSTEM_COMPONENTS];
@ -151,10 +134,9 @@ class MicroBit
MicroBitIO io;
// Bluetooth related member variables.
BLEDevice *ble;
DeviceInformationService *ble_device_information_service;
MicroBitDFUService *ble_firmware_update_service;
MicroBitEventService *ble_event_service;
BLEDevice *ble;
MicroBitDFUService *ble_firmware_update_service;
MicroBitEventService *ble_event_service;
/**
@ -284,9 +266,16 @@ class MicroBit
// Definition of the global instance of the MicroBit class.
// Using this as a variation on the singleton pattern, just to make
// code integration a little bit easier for 3rd parties.
// code integration a little bit easier for third parties.
extern MicroBit uBit;
//
// BLE callback when an active GATT session with another device is terminated.
// Used to reset state and restart advertising ourselves.
//
void bleDisconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason);
extern char MICROBIT_BLE_DEVICE_NAME[];
// Entry point for application programs. Called after the super-main function
// has initialized the device and runtime environment.
extern "C" void app_main();

119
inc/MicroBitConfig.h Normal file
View file

@ -0,0 +1,119 @@
/**
* Compile time configuration options for the micro:bit runtime.
*/
#ifndef MICROBIT_CONFIG_H
#define MICROBIT_CONFIG_H
#include "mbed.h"
//
// Memory configuration
//
// Physical address of the top of SRAM.
#define MICROBIT_SRAM_END 0x20004000
// Physical address of the top of the system stack (on mbed-classic this is the top of SRAM)
#define CORTEX_M0_STACK_BASE MICROBIT_SRAM_END
// Amount of memory reserved for the stack at the end of memory (bytes).
#define MICROBIT_STACK_SIZE 2000
// Physical address of the end of heap space.
#define MICROBIT_HEAP_END (CORTEX_M0_STACK_BASE - MICROBIT_STACK_SIZE)
// Block size used by the allocator in bytes.
// n.b. Currently only 32 bits (4 bytes) is supported.
#define MICROBIT_HEAP_BLOCK_SIZE 4
// The proportion of SRAM available on the mbed heap to reserve for the micro:bit heap.
#define MICROBIT_HEAP_SIZE 0.95
// if defined, reuse the 8K of SRAM reserved for SoftDevice (Nordic's memory resident BLE stack) as heap memory.
// The amount of memory reused depends upon whether or not BLE is enabled using MICROBIT_BLE_ENABLED.
#define MICROBIT_HEAP_REUSE_SD
// The lowest address of memory that is safe to use as heap storage when BLE is DISABLED
// Used to define the base of the heap when MICROBIT_HEAP_REUSE_SD is defined.
#define MICROBIT_HEAP_BASE_BLE_DISABLED 0x20000008
// The lowest address of memory that is safe to use as heap storage when BLE is ENABLED
// This is permissable if SD is configured to release some of its internal storage that
// is normally reserved for its BLE GATT table.
#define MICROBIT_HEAP_BASE_BLE_ENABLED 0x20001C00
// The highest address of memory normally reserved for Soft Device that is safe to use as heap storage
#define MICROBIT_HEAP_SD_LIMIT 0x20002000
//
// Fiber scheduler configuration
//
// Scheduling quantum (milliseconds)
// Also used to drive the micro:bit runtime system ticker.
#define FIBER_TICK_PERIOD_MS 6
//
// Core micro:bit services
//
// To reduce memory cost and complexity, the micro:bit allows components to register for
// periodic callback events during interrupt context, which occur every scheduling quantum (FIBER_TICK_PERIOD_MS)
// This defines the maximum size of interrupt callback list.
#define MICROBIT_SYSTEM_COMPONENTS 10
// To reduce memory cost and complexity, the micro:bit allows components to register for
// periodic callback events when the processor is idle.
// This defines the maximum size of the idle callback list.
#define MICROBIT_IDLE_COMPONENTS 6
//
// BLE options
//
// The BLE stack is very memory hungry. Each service can therefore be compiled in or out
// by enabling/disabling the options below.
//
// n.b. The minimum set of services to enable over the air programming of the device will
// still be brought up in 'BLUEZONE' mode regardless of the settings below.
//
// Enable/Disable BLE during normal operation.
#define MICROBIT_BLE_ENABLED
// Enable/Disable BLUEZONE mode at power up.
#define MICROBIT_BLE_BLUEZONE
// Enable/Disable BLE Service: MicroBitDFU
// This allows over the air programming during normal operation.
#define MICROBIT_BLE_DFU_SERVICE
// Enable/Disable BLE Service: MicroBitEventService
// This allows routing of events from the micro:bit message bus over BLE.
#define MICROBIT_BLE_EVENT_SERVICE
// Enable/Disable BLE Service: MicroBitDeviceInformationService
// This enables the standard BLE device information service.
#define MICROBIT_BLE_DEVICE_INFORMATION_SERVICE
//
// Panic options
//
// Enable this to invoke a panic on out of memory conditions.
#define MICROBIT_PANIC_HEAP_FULL
//
// Debug options
//
// Enable this to route debug messages through the USB serial interface.
// n.b. This also disables the user serial port 'uBit.serial'.
//#define MICROBIT_DBG
// Enable this to receive diagnostic messages from the heap allocator via the USB serial interface.
// n.b. This requires MICROBIT_DBG to be defined.
//#define MICROBIT_HEAP_DBG
#endif

View file

@ -13,7 +13,6 @@
#define MICROBIT_DFU_HISTOGRAM_WIDTH 5
#define MICROBIT_DFU_HISTOGRAM_HEIGHT 5
// UUIDs for our service and characteristics
extern const uint8_t MicroBitDFUServiceUUID[];
extern const uint8_t MicroBitDFUServiceControlCharacteristicUUID[];
@ -81,13 +80,10 @@ class MicroBitDFUService
// memory for our 8 bit control characteristics.
uint8_t controlByte;
// Opcodes can be issued here to control the MicroBitDFU Service, as defined above.
WriteOnlyGattCharacteristic<uint8_t> microBitDFUServiceControlCharacteristic;
// Read/Write characteristic to enable unlocking and discovery of the MicroBit's flashcode.
GattCharacteristic microBitDFUServiceFlashCodeCharacteristic;
GattAttribute::Handle_t microBitDFUServiceControlCharacteristicHandle;
GattAttribute::Handle_t microBitDFUServiceFlashCodeCharacteristicHandle;
// Displays the device's ID code as a histogram on the LED matrix display.
void showNameHistogram();

View file

@ -49,9 +49,8 @@ class MicroBitEventService
EventServiceEvent clientEventBuffer;
EventServiceEvent microBitEventBuffer;
// BLE GATT Characteristics for sending and receiving events to an attached device
GattCharacteristic microBitEventCharacteristic;
GattCharacteristic clientEventCharacteristic;
GattAttribute::Handle_t microBitEventCharacteristicHandle;
GattAttribute::Handle_t clientEventCharacteristicHandle;
};

View file

@ -11,24 +11,16 @@
#define MICROBIT_FIBER_H
#include "mbed.h"
#include "MicroBitConfig.h"
#include "MicroBitMessageBus.h"
// Typical stack size of each fiber.
// A physical stack of anything less than 1024 will likely hit overflow issues during ISR/mBed calls.
// However, as we're running a co=operative fiber scheduler, the size of the stack at the point of
// context switching is normally *very* small (circa 64 bytes!). Also, as we're likely to have many short lived threads
// being used, be actually perform a stack duplication on context switch, which keeps the RAM footprint of a fiber
// down to a minimum, without constraining what can be done insode a fiber context.
//
// TODO: Consider a split mode scheduler, that monitors used stack size, and maintains a dedicated, persistent
// stack for any long lived fibers with large stacks.
//
#define FIBER_STACK_SIZE 64
#define FIBER_TICK_PERIOD_MS 6
#define CORTEX_M0_STACK_BASE (0x20004000 - 4)
// stack for any long lived fibers with large stack
#define MICROBIT_FLAG_DATA_READ 0x01
// Fiber Scheduler Flags
#define MICROBIT_FLAG_DATA_READY 0x01
// Fiber Flags
#define MICROBIT_FIBER_FLAG_FOB 0x01
#define MICROBIT_FIBER_FLAG_PARENT 0x02
#define MICROBIT_FIBER_FLAG_CHILD 0x04
@ -39,7 +31,6 @@
* This is probably overkill, but the ARMCC compiler uses a lot register optimisation
* in its calling conventions, so better safe than sorry. :-)
*
* TODO: Check with ARM guys to see is they have suggestions to optimize this context block
*/
struct Cortex_M0_TCB
{
@ -58,6 +49,7 @@ struct Cortex_M0_TCB
uint32_t R12;
uint32_t SP;
uint32_t LR;
uint32_t stack_base;
};
/**
@ -66,8 +58,9 @@ struct Cortex_M0_TCB
struct Fiber
{
uint32_t stack_top, stack_bottom; // Address of this Fiber's stack. Stack is heap allocated, and full descending.
Cortex_M0_TCB tcb; // Thread context when last scheduled out.
uint32_t stack_bottom; // The start sddress of this Fiber's stack. Stack is heap allocated, and full descending.
uint32_t stack_top; // The end address of this Fiber's stack.
uint32_t context; // Context specific information.
uint32_t flags; // Information about this fiber.
Fiber **queue; // The queue this fiber is stored on.
@ -88,17 +81,18 @@ void scheduler_init();
* Any fiber reaching the end of its entry function will return here for recycling.
*/
void release_fiber(void);
/**
* Exit point for parameterised fibers.
* A wrapper around release_fiber() to enable transparent operaiton.
*/
void release_fiber(void *param);
/**
* Launches a fiber
*/
void launch_new_fiber()
*/
void launch_new_fiber(void (*ep)(void), void (*cp)(void))
#ifdef __GCC__
__attribute__((naked))
#endif
;
void launch_new_fiber_param(void (*ep)(void *), void (*cp)(void *), void *pm)
#ifdef __GCC__
__attribute__((naked))
#endif
@ -113,14 +107,7 @@ void launch_new_fiber()
*/
Fiber *create_fiber(void (*entry_fn)(void), void (*completion_fn)(void) = release_fiber);
/**
* Launches a paramaterised fiber
*/
void launch_new_fiber_param()
#ifdef __GCC__
__attribute__((naked))
#endif
;
/**
* Creates a new parameterised Fiber, and launches it.
*
@ -173,7 +160,7 @@ void scheduler_tick();
void fiber_wait_for_event(uint16_t id, uint16_t value);
/**
* Executes the given function asynchronously.
* Executes the given function asynchronously if necessary.
*
* Fibers are often used to run event handlers, however many of these event handlers are very simple functions
* that complete very quickly, bringing unecessary RAM overhead.
@ -183,10 +170,10 @@ void fiber_wait_for_event(uint16_t id, uint16_t value);
*
* @param entry_fn The function to execute.
*/
void fork_on_block(void (*entry_fn)(void));
void invoke(void (*entry_fn)(void));
/**
* Executes the given function asynchronously.
* Executes the given function asynchronously if necessary.
*
* Fibers are often used to run event handlers, however many of these event handlers are very simple functions
* that complete very quickly, bringing unecessary RAM. overhead
@ -197,17 +184,19 @@ void fork_on_block(void (*entry_fn)(void));
* @param entry_fn The function to execute.
* @param param an untyped parameter passed into the entry_fn anf completion_fn.
*/
void fork_on_block(void (*entry_fn)(void *), void *param);
void invoke(void (*entry_fn)(void *), void *param);
/**
* Resizes the stack allocation of the current fiber if necessary to hold the system stack.
*
* If the stack allocaiton is large enough to hold the current system stack, then this function does nothing.
* Otherwise, the the current allocation of the fiber is freed, and a larger block is allocated.
*
* @param f The fiber context to verify.
* @return The stack depth of the given fiber.
*/
inline void verify_stack_size(Fiber *f);
/**
* Event callback. Called from the message bus whenever an event is raised.
* Checks to determine if any fibers blocked on the wait queue need to be woken up
@ -215,6 +204,11 @@ inline void verify_stack_size(Fiber *f);
*/
void scheduler_event(MicroBitEvent evt);
/**
* Determines if any fibers are waiting to be scheduled.
* @return The number of fibers currently on the run queue
*/
int scheduler_runqueue_empty();
/**
* Utility function to add the currenty running fiber to the given queue.

View file

@ -0,0 +1,86 @@
/**
* A simple 32 bit block based memory allocator. This allows one or more memory segments to
* be designated as heap storage, and is designed to run in a static memory area or inside the standard C
* heap for use by the micro:bit runtime. This is required for several reasons:
*
* 1) It reduces memory fragmentation due to the high churn sometime placed on the heap
* by ManagedTypes, fibers and user code. Underlying heap implentations are often have very simplistic
* allocation pilicies and suffer from fragmentation in prolonged use - which can cause programs to
* stop working after a period of time. The algorithm implemented here is simple, but highly tolerant to
* large amounts of churn.
*
* 2) It allows us to reuse the 8K of SRAM set aside for SoftDevice as additional heap storage
* when BLE is not in use.
*
* 3) It gives a simple example of how memory allocation works! :-)
*
* N.B. The need for this should be reviewed in the future, should a different memory allocator be
* made availiable in the mbed platform.
*
* P.S. This is a very simple allocator, therefore not without its weaknesses. Why don't you consider
* what these are, and consider the tradeoffs against simplicity...
*
* TODO: Consider caching recently freed blocks to improve allocation time.
*/
#ifndef MICROBIT_HEAP_ALLOCTOR_H
#define MICROBIT_HEAP_ALLOCTOR_H
#include "Microbit.h"
#include <new>
// The number of heap segments created.
#define MICROBIT_HEAP_COUNT 2
// Flag to indicate that a given block is FREE/USED
#define MICROBIT_HEAP_BLOCK_FREE 0x80000000
int microbit_heap_init();
void *microbit_malloc(size_t size);
void microbit_free(void *mem);
/*
* Wrapper function to ensure we have an explicit handle on the heap allocator provided
* by our underlying platform.
*
* @param size The amount of memory to allocate.
* @return A pointer to the memory allocated. NULL if no memory is available.
*/
inline void *native_malloc(size_t size)
{
return malloc(size);
}
/*
* Wrapper function to ensure we have an explicit handle on the heap allocator provided
* by our underlying platform.
*
* @param p Pointer to the memory to be freed.
*/
inline void native_free(void *p)
{
free(p);
}
/**
* Overrides the 'new' operator globally, and redirects calls to the micro:bit theap allocator.
*/
inline void* operator new(size_t size) throw(std::bad_alloc)
{
return microbit_malloc(size);
}
/**
* Overrides the 'delete' operator globally, and redirects calls to the micro:bit theap allocator.
*/
inline void operator delete(void *ptr) throw()
{
microbit_free(ptr);
}
// Macros to override overrides the 'malloc' and 'delete' functions globally, and redirects calls
// to the micro:bit theap allocator.
#define malloc(X) microbit_malloc( X )
#define free(X) microbit_free( X )
#endif

View file

@ -1,39 +0,0 @@
#ifndef MICROBIT_MALLOC_H
#define MICROBIT_MALLOC_H
#include "MicroBit.h"
#include <new>
/**
\brief Overrides malloc globally, and fires the panic function if we run out of memory!
*/
inline void* ubit_malloc(size_t size)
{
void *ptr;
ptr = malloc(size);
if(ptr == NULL)
panic(MICROBIT_OOM);
return ptr;
}
/**
\brief Overrides malloc globally, and fires the panic function if we run out of memory!
*/
inline void* operator new(size_t size) throw(std::bad_alloc)
{
void *ptr;
ptr = malloc(size);
if(ptr == NULL)
panic(MICROBIT_OOM);
return ptr;
}
#define malloc(X) ubit_malloc( X ) //macro! Override malloc! Hehehe
#endif

View file

@ -37,12 +37,6 @@ struct MicroBitListener
MicroBitListener(uint16_t id, uint16_t value, void *handler, void* arg);
};
struct MicroBitMessageBusCache
{
int seq;
MicroBitListener *ptr;
};
struct MicroBitEventQueueItem
{
MicroBitEvent evt;
@ -53,7 +47,7 @@ struct MicroBitEventQueueItem
* Creates a new MicroBitEventQueueItem.
* @param evt The event that is to be queued.
*/
MicroBitEventQueueItem(MicroBitEvent &evt);
MicroBitEventQueueItem(MicroBitEvent evt);
};
/**
@ -85,12 +79,11 @@ class MicroBitMessageBus : public MicroBitComponent
MicroBitMessageBus();
/**
* Send the given event to all regstered recipients.
* Queues the given event to be sent to all registered recipients.
*
* @param The event to send. This structure is assumed to be heap allocated, and will
* be automatically freed once all recipients have been notified.
* @param The event to send.
*
* THIS IS NOW WRAPPED BY THE MicroBitEvent CLASS FOR CONVENIENCE...
* n.b. THIS IS NOW WRAPPED BY THE MicroBitEvent CLASS FOR CONVENIENCE...
*
* Example:
* @code
@ -100,17 +93,18 @@ class MicroBitMessageBus : public MicroBitComponent
* MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_DOWN);
* @endcode
*/
void send(MicroBitEvent &evt);
void send(MicroBitEvent evt);
/**
* Send the given event to all regstered recipients, using a cached entry to minimize lookups.
* This is particularly useful for optimizing sensors that frequently send to the same channel.
* Internal function, used to deliver the given event to all relevant recipients.
* Normally, this is called once an event has been removed from the event queue.
*
* IT IS RECOMMENDED THAT ALL EXTERNAL CODE USE THE send() FUNCTIONS INSTEAD OF THIS FUNCTION.
*
* @param evt The event to send. This structure is assumed to be heap allocated, and will
* be automatically freed once all recipients have been notified.
* @param c Cache entry to reduce lookups for commonly used channels.
* @param evt The event to send.
* @param c The cache entry to reduce lookups for commonly used channels.
*/
void send(MicroBitEvent &evt, MicroBitMessageBusCache *c);
void process(MicroBitEvent evt);
/**
* Register a listener function.

17
inc/MicroBitPanic.h Normal file
View file

@ -0,0 +1,17 @@
#ifndef MICROBIT_PANIC_H
#define MICROBIT_PANIC_H
/**
* Displays "=(" and an accompanying status code.
* @param statusCode the appropriate status code - 0 means no code will be displayed. Status codes must be in the range 0-255.
*/
void panic(int statusCode);
/**
* Resets the micro:bit.
* @param statusCode the appropriate status code. Status codes must be in the range 0-255.
*/
void reset(int statusCode);
#endif

View file

@ -18,6 +18,10 @@ swap_context
; Write our core registers into the TCB
; First, store the general registers
; Skip this is we're given a NULL parameter for the TCB
CMP R0, #0
BEQ store_context_complete
STR R0, [R0,#0]
STR R1, [R0,#4]
STR R2, [R0,#8]
@ -47,16 +51,15 @@ swap_context
MOV R4, LR
STR R4, [R0,#56]
; Finally, Copy the stack. We do this to reduce RAM footprint, as stackis usually very small at the point
; of sceduling, but we need a lot of capacity for interrupt handling and other functions.
store_context_complete
; Finally, Copy the stack. We do this to reduce RAM footprint, as stack is usually very small at the point
; of scheduling, but we need a lot of capacity for interrupt handling and other functions.
MOVS R7, #0x20 ; Load R8 with top of System Stack space.
LSLS R7, #24
MOVS R4, #0x40
LSLS R4, #8
ORRS R7, R4
MOV R4, R7
; Skip this is we're given a NULL parameter for the stack.
CMP R2, #0
BEQ store_stack_complete
LDR R4, [R0,#60] ; Load R4 with the fiber's defined stack_base.
store_stack
SUBS R4, #4
SUBS R2, #4
@ -66,7 +69,9 @@ store_stack
CMP R4, R6
BNE store_stack
store_stack_complete
;
; Now page in the new context.
; Update all registers except the PC. We can also safely ignore the STATUS register, as we're just a fiber scheduler.
@ -79,9 +84,12 @@ store_stack
; Copy the stack in.
; n.b. we do this after setting the SP to make comparisons easier.
MOV R4, R7 ; Load R4 with top of System Stack space.
; Skip this is we're given a NULL parameter for the stack.
CMP R3, #0
BEQ restore_stack_complete
LDR R4, [R1,#60] ; Load R4 with the fiber's defined stack_base.
restore_stack
SUBS R4, #4
SUBS R3, #4
@ -92,6 +100,7 @@ restore_stack
CMP R4, R6
BNE restore_stack
restore_stack_complete
LDR R4, [R1, #48]
MOV R12, R4
LDR R4, [R1, #44]
@ -116,8 +125,6 @@ restore_stack
BX LR
; R0 Contains a pointer to the TCB of the fibre to snapshot
; R1 Contains a pointer to the base of the stack of the fibre being snapshotted
@ -158,13 +165,8 @@ save_context
; Finally, Copy the stack. We do this to reduce RAM footprint, as stackis usually very small at the point
; of sceduling, but we need a lot of capacity for interrupt handling and other functions.
MOVS R5, #0x20 ; Load R8 with top of System Stack space.
LSLS R5, #24
MOVS R4, #0x40
LSLS R4, #8
ORRS R5, R4
MOV R4, R5
LDR R4, [R0,#60] ; Load R4 with the fiber's defined stack_base.
store_stack1
SUBS R4, #4
SUBS R1, #4
@ -186,8 +188,7 @@ store_stack1
BX LR
; R0 Contains a pointer to the TCB of the fibre to snapshot
; R0 Contains a pointer to the TCB of the fiber to snapshot
save_register_context
; Write our core registers into the TCB

View file

@ -20,6 +20,10 @@ swap_context:
@ Write our core registers into the TCB
@ First, store the general registers
@ Skip this is we're given a NULL parameter for the TCB
CMP R0, #0
BEQ store_context_complete
STR R0, [R0,#0]
STR R1, [R0,#4]
STR R2, [R0,#8]
@ -49,16 +53,15 @@ swap_context:
MOV R4, LR
STR R4, [R0,#56]
@ Finally, Copy the stack. We do this to reduce RAM footprint, as stackis usually very small at the point
@ of sceduling, but we need a lot of capacity for interrupt handling and other functions.
store_context_complete:
@ Finally, Copy the stack. We do this to reduce RAM footprint, as stack is usually very small at the point
@ of scheduling, but we need a lot of capacity for interrupt handling and other functions.
MOVS R7, #0x20 @ Load R8 with top of System Stack space.
LSLS R7, #24
MOVS R4, #0x40
LSLS R4, #8
ORRS R7, R4
MOV R4, R7
@ Skip this is we're given a NULL parameter for the stack.
CMP R2, #0
BEQ store_stack_complete
LDR R4, [R0,#60] @ Load R4 with the fiber's defined stack_base.
store_stack:
SUBS R4, #4
SUBS R2, #4
@ -68,7 +71,9 @@ store_stack:
CMP R4, R6
BNE store_stack
store_stack_complete:
@
@ Now page in the new context.
@ Update all registers except the PC. We can also safely ignore the STATUS register, as we're just a fiber scheduler.
@ -81,9 +86,12 @@ store_stack:
@ Copy the stack in.
@ n.b. we do this after setting the SP to make comparisons easier.
MOV R4, R7 @ Load R4 with top of System Stack space.
@ Skip this is we're given a NULL parameter for the stack.
CMP R3, #0
BEQ restore_stack_complete
LDR R4, [R1,#60] @ Load R4 with the fiber's defined stack_base.
restore_stack:
SUBS R4, #4
SUBS R3, #4
@ -94,6 +102,7 @@ restore_stack:
CMP R4, R6
BNE restore_stack
restore_stack_complete:
LDR R4, [R1, #48]
MOV R12, R4
LDR R4, [R1, #44]
@ -118,8 +127,6 @@ restore_stack:
BX LR
@ R0 Contains a pointer to the TCB of the fibre to snapshot
@ R1 Contains a pointer to the base of the stack of the fibre being snapshotted
@ -160,13 +167,8 @@ save_context:
@ Finally, Copy the stack. We do this to reduce RAM footprint, as stackis usually very small at the point
@ of sceduling, but we need a lot of capacity for interrupt handling and other functions.
MOVS R5, #0x20 @ Load R8 with top of System Stack space.
LSLS R5, #24
MOVS R4, #0x40
LSLS R4, #8
ORRS R5, R4
MOV R4, R5
LDR R4, [R0,#60] @ Load R4 with the fiber's defined stack_base.
store_stack1:
SUBS R4, #4
SUBS R1, #4
@ -186,9 +188,9 @@ store_stack1:
@ Return to caller (scheduler).
BX LR
@ R0 Contains a pointer to the TCB of the fibre to snapshot
@ R0 Contains a pointer to the TCB of the fiber to snapshot
save_register_context:
@ Write our core registers into the TCB

View file

@ -1,3 +1,4 @@
#include "MicroBitHeapAllocator.h"
#include "DynamicPwm.h"
@ -126,12 +127,11 @@ DynamicPwm* DynamicPwm::allocate(PinName pin, PwmPersistence persistence, int pe
* Example:
* @code
* DynamicPwm* pwm = DynamicPwm::allocate();
* pwm->free();
* pwm->release();
* @endcode
*/
void DynamicPwm::free()
void DynamicPwm::release()
{
//free the pwm instance.
NRF_GPIOTE->CONFIG[(uint8_t) _pwm.pwm] = 0;
pwmout_free(&_pwm);

View file

@ -1,5 +1,7 @@
#include <string.h>
#include <stdlib.h>
#include "mbed.h"
#include "MicroBitHeapAllocator.h"
#include "MicroBitCompat.h"
#include "ManagedString.h"

View file

@ -1,12 +1,15 @@
#include "MicroBit.h"
char MICROBIT_BLE_DEVICE_NAME[] = "BBC MicroBit [xxxxx]";
#if defined (MICROBIT_BLE_ENABLED) && defined (MICROBIT_BLE_DEVICE_INFORMATION_SERVICE)
const char MICROBIT_BLE_MANUFACTURER[] = "The Cast of W1A";
const char MICROBIT_BLE_MODEL[] = "Microbit SB2";
const char MICROBIT_BLE_SERIAL[] = "SN1";
const char MICROBIT_BLE_HARDWARE_VERSION[] = "0.2";
const char MICROBIT_BLE_FIRMWARE_VERSION[] = "1.1";
const char MICROBIT_BLE_SOFTWARE_VERSION[] = "1.0";
#endif
/**
* custom function for panic for malloc & new due to scoping issue.
@ -21,7 +24,7 @@ void panic(int statusCode)
*/
void bleDisconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason)
{
uBit.ble->startAdvertising(); // restart advertising!
uBit.ble->startAdvertising();
}
/**
@ -46,7 +49,9 @@ void bleDisconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t r
MicroBit::MicroBit() :
flags(0x00),
i2c(MICROBIT_PIN_SDA, MICROBIT_PIN_SCL),
#ifndef MICROBIT_DBG
serial(USBTX, USBRX),
#endif
MessageBus(),
display(MICROBIT_ID_DISPLAY, MICROBIT_DISPLAY_WIDTH, MICROBIT_DISPLAY_HEIGHT),
buttonA(MICROBIT_ID_BUTTON_A,MICROBIT_PIN_BUTTON_A),
@ -88,20 +93,35 @@ void MicroBit::init()
// Seed our random number generator
seedRandom();
#ifndef NO_BLE
#ifdef MICROBIT_BLE_ENABLED
// Start the BLE stack.
ble = new BLEDevice();
ble = new BLEDevice();
ble->init();
ble->onDisconnection(bleDisconnectionCallback);
// Add our auxiliary services.
// Bring up any configured auxiliary services.
#ifdef MICROBIT_BLE_DFU_SERVICE
ble_firmware_update_service = new MicroBitDFUService(*ble);
ble_device_information_service = new DeviceInformationService(*ble, MICROBIT_BLE_MANUFACTURER, MICROBIT_BLE_MODEL, MICROBIT_BLE_SERIAL, MICROBIT_BLE_HARDWARE_VERSION, MICROBIT_BLE_FIRMWARE_VERSION, MICROBIT_BLE_SOFTWARE_VERSION);
ble_event_service = new MicroBitEventService(*ble);
// Compute our auto-generated MicroBit device name.
ble_firmware_update_service->getName(MICROBIT_BLE_DEVICE_NAME+14);
#endif
#ifdef MICROBIT_BLE_DEVICE_INFORMATION_SERVICE
#ifdef OLD
ble_device_information_service = new DeviceInformationService(*ble, MICROBIT_BLE_MANUFACTURER, MICROBIT_BLE_MODEL, MICROBIT_BLE_SERIAL, MICROBIT_BLE_HARDWARE_VERSION, MICROBIT_BLE_FIRMWARE_VERSION, MICROBIT_BLE_SOFTWARE_VERSION);
#endif
DeviceInformationService ble_device_information_service (*ble, MICROBIT_BLE_MANUFACTURER, MICROBIT_BLE_MODEL, MICROBIT_BLE_SERIAL, MICROBIT_BLE_HARDWARE_VERSION, MICROBIT_BLE_FIRMWARE_VERSION, MICROBIT_BLE_SOFTWARE_VERSION);
#endif
#ifdef MICROBIT_BLE_EVENT_SERVICE
ble_event_service = new MicroBitEventService(*ble);
#endif
// Setup advertising.
ble->accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
@ -109,12 +129,7 @@ void MicroBit::init()
ble->setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
ble->setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(1000));
ble->startAdvertising();
#else
ble = NULL;
ble_firmware_update_service = NULL;
ble_device_information_service = NULL;
ble_event_service = NULL;
#endif
#endif
// Start refreshing the Matrix Display
systemTicker.attach(this, &MicroBit::systemTick, MICROBIT_DISPLAY_REFRESH_PERIOD);
@ -239,7 +254,7 @@ void MicroBit::systemTick()
for(int i = 0; i < MICROBIT_IDLE_COMPONENTS; i++)
if(idleThreadComponents[i] != NULL && idleThreadComponents[i]->isIdleCallbackNeeded())
{
fiber_flags |= MICROBIT_FLAG_DATA_READ;
fiber_flags |= MICROBIT_FLAG_DATA_READY;
break;
}
@ -259,7 +274,7 @@ void MicroBit::systemTasks()
if(idleThreadComponents[i] != NULL)
idleThreadComponents[i]->idleTick();
fiber_flags &= ~MICROBIT_FLAG_DATA_READ;
fiber_flags &= ~MICROBIT_FLAG_DATA_READY;
}
/**

View file

@ -1,6 +1,4 @@
#include "inc/MicroBit.h"
#include "inc/MicroBitButton.h"
#include "inc/MicroBitMessageBus.h"
#include "MicroBit.h"
/**
* Constructor.

View file

@ -1,4 +1,4 @@
#include "inc/MicroBit.h"
#include "MicroBit.h"
/**
* Constructor.

View file

@ -23,11 +23,15 @@
* @param messageBus callback function to receive MicroBitMessageBus events.
*/
MicroBitDFUService::MicroBitDFUService(BLEDevice &_ble) :
ble(_ble),
microBitDFUServiceControlCharacteristic(MicroBitDFUServiceControlCharacteristicUUID, &controlByte),
microBitDFUServiceFlashCodeCharacteristic(MicroBitDFUServiceFlashCodeCharacteristicUUID, (uint8_t *)&flashCode, 0, sizeof(uint32_t),
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY)
ble(_ble)
{
// Opcodes can be issued here to control the MicroBitDFU Service, as defined above.
WriteOnlyGattCharacteristic<uint8_t> microBitDFUServiceControlCharacteristic(MicroBitDFUServiceControlCharacteristicUUID, &controlByte);
// Read/Write characteristic to enable unlocking and discovery of the MicroBit's flashcode.
GattCharacteristic microBitDFUServiceFlashCodeCharacteristic(MicroBitDFUServiceFlashCodeCharacteristicUUID, (uint8_t *)&flashCode, 0, sizeof(uint32_t),
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
authenticated = false;
flashCodeRequested = false;
@ -39,6 +43,9 @@ MicroBitDFUService::MicroBitDFUService(BLEDevice &_ble) :
ble.addService(service);
microBitDFUServiceControlCharacteristicHandle = microBitDFUServiceControlCharacteristic.getValueHandle();
microBitDFUServiceFlashCodeCharacteristicHandle = microBitDFUServiceFlashCodeCharacteristic.getValueHandle();
ble.onDataWritten(this, &MicroBitDFUService::onDataWritten);
}
@ -69,10 +76,6 @@ int MicroBitDFUService::getName(char *name)
int d = MICROBIT_DFU_HISTOGRAM_HEIGHT;
int h;
#ifdef MICROBIT_DEBUG
pc.printf("MicroBitDFUService::getName: Called [%.8x]\n",n);
#endif
for (int i=0; i<MICROBIT_DFU_HISTOGRAM_WIDTH;i++)
{
h = (n % d) / ld;
@ -91,10 +94,6 @@ int MicroBitDFUService::getName(char *name)
*/
void MicroBitDFUService::pair()
{
#ifdef MICROBIT_DEBUG
pc.printf("MicroBitDFUService::pair: Called\n");
#endif
ManagedString blueZoneString("BLUE ZONE...");
ManagedString pairString("PAIR?");
@ -142,29 +141,10 @@ void MicroBitDFUService::pair()
*/
void MicroBitDFUService::onDataWritten(const GattWriteCallbackParams *params)
{
#ifdef MICROBIT_DEBUG
pc.printf("MicroBitDFUService::onDataWritten: Called... ");
#endif
if (params->handle == microBitDFUServiceControlCharacteristic.getValueHandle()) {
#ifdef MICROBIT_DEBUG
pc.printf("Control Point:\n ");
#endif
if (params->handle == microBitDFUServiceControlCharacteristicHandle)
{
if (params->len < 1)
{
#ifdef MICROBIT_DEBUG
pc.printf(" invalid. Ignoring.\n");
#endif
return;
}
#ifdef MICROBIT_DEBUG
for (int i=0; i<params->len; i++)
pc.printf("%.2x ", params->data[i]);
pc.printf("\n");
#endif
switch(params->data[0])
{
@ -172,7 +152,7 @@ void MicroBitDFUService::onDataWritten(const GattWriteCallbackParams *params)
if (authenticated)
{
#ifdef MICROBIT_DEBUG
#ifdef MICROBIT_DBG
pc.printf(" ACTIVATING BOOTLOADER.\n");
#endif
bootloader_start();
@ -181,35 +161,25 @@ void MicroBitDFUService::onDataWritten(const GattWriteCallbackParams *params)
break;
case MICROBIT_DFU_OPCODE_START_PAIR:
#ifdef MICROBIT_DEBUG
pc.printf(" START_PAIR: ");
#endif
flashCodeRequested = true;
break;
}
}
if (params->handle == microBitDFUServiceFlashCodeCharacteristic.getValueHandle()) {
#ifdef MICROBIT_DEBUG
pc.printf("FlashCode\n\n");
#endif
if (params->handle == microBitDFUServiceFlashCodeCharacteristicHandle)
{
if (params->len >= 4)
{
uint32_t lockCode=0;
memcpy(&lockCode, params->data, 4);
if (lockCode == NRF_FICR->DEVICEID[0])
{
#ifdef MICROBIT_DEBUG
pc.printf("AUTHENTICATED\n");
#ifdef MICROBIT_DBG
pc.printf("MicroBitDFU: FLASHCODE AUTHENTICATED\n");
#endif
authenticated = true;
}else{
#ifdef MICROBIT_DEBUG
pc.printf("NOT AUTHENTICATED: %8x\n", lockCode);
#endif
authenticated = false;
}
}
@ -236,10 +206,6 @@ void MicroBitDFUService::showTick()
*/
void MicroBitDFUService::showNameHistogram()
{
#ifdef MICROBIT_DEBUG
pc.printf("MicroBitDFUService::showNameHistogram: Called\n");
#endif
uBit.display.resetAnimation(0);
uint32_t n = NRF_FICR->DEVICEID[1];
@ -266,11 +232,9 @@ void MicroBitDFUService::showNameHistogram()
*/
void MicroBitDFUService::releaseFlashCode()
{
#ifdef MICROBIT_DEBUG
pc.printf("MicroBitDFUService::releaseFlashCode: Called\n");
#endif
flashCode = NRF_FICR->DEVICEID[0];
ble.updateCharacteristicValue(microBitDFUServiceFlashCodeCharacteristic.getValueHandle(), (uint8_t *)&flashCode, sizeof(uint32_t));
ble.updateCharacteristicValue(microBitDFUServiceFlashCodeCharacteristicHandle, (uint8_t *)&flashCode, sizeof(uint32_t));
}
/**

View file

@ -15,12 +15,14 @@
* @param _ble The instance of a BLE device that we're running on.
*/
MicroBitEventService::MicroBitEventService(BLEDevice &_ble) :
ble(_ble),
microBitEventCharacteristic(MicroBitEventServiceMicroBitEventCharacteristicUUID, (uint8_t *)&microBitEventBuffer, 0, sizeof(EventServiceEvent),
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY),
clientEventCharacteristic(MicroBitEventServiceClientEventCharacteristicUUID, (uint8_t *)&clientEventBuffer, 0, sizeof(EventServiceEvent),
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE)
ble(_ble)
{
GattCharacteristic microBitEventCharacteristic(MicroBitEventServiceMicroBitEventCharacteristicUUID, (uint8_t *)&microBitEventBuffer, 0, sizeof(EventServiceEvent),
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);
GattCharacteristic clientEventCharacteristic(MicroBitEventServiceClientEventCharacteristicUUID, (uint8_t *)&clientEventBuffer, 0, sizeof(EventServiceEvent),
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE);
clientEventBuffer.type = 0x00;
clientEventBuffer.reason = 0x00;
@ -32,6 +34,9 @@ MicroBitEventService::MicroBitEventService(BLEDevice &_ble) :
ble.addService(service);
microBitEventCharacteristicHandle = microBitEventCharacteristic.getValueHandle();
clientEventCharacteristicHandle = clientEventCharacteristic.getValueHandle();
ble.onDataWritten(this, &MicroBitEventService::onDataWritten);
}
@ -44,7 +49,7 @@ void MicroBitEventService::onDataWritten(const GattWriteCallbackParams *params)
int len = params->len;
EventServiceEvent *e = (EventServiceEvent *)params->data;
if (params->handle == clientEventCharacteristic.getValueHandle()) {
if (params->handle == clientEventCharacteristicHandle) {
// Read and fire all events...
while (len >= 4)
@ -67,7 +72,7 @@ void MicroBitEventService::onMicroBitEvent(MicroBitEvent evt)
e->type = evt.source;
e->reason = evt.value;
ble.updateCharacteristicValue(microBitEventCharacteristic.getValueAttribute().getHandle(), (const uint8_t *)e, sizeof(EventServiceEvent));
ble.updateCharacteristicValue(microBitEventCharacteristicHandle, (const uint8_t *)e, sizeof(EventServiceEvent));
}
}

View file

@ -13,27 +13,27 @@
* Statically allocated values used to create and destroy Fibers.
* required to be defined here to allow persistence during context switches.
*/
Fiber *currentFiber = NULL; // The context in which the current fiber is executing.
Fiber *forkedFiber = NULL; // The context in which a newly created child fiber is executing.
Fiber *idle = NULL; // IDLE task - performs a power efficient sleep, and system maintenance tasks.
/*
* Scheduler state.
*/
Fiber *currentFiber = NULL; // The context in which the current fiber is executing.
Fiber *forkedFiber = NULL; // The context in which a newly created child fiber is executing.
Fiber *runQueue = NULL; // The list of runnable fibers.
Fiber *sleepQueue = NULL; // The list of blocked fibers waiting on a fiber_sleep() operation.
Fiber *waitQueue = NULL; // The list of blocked fibers waiting on an event.
Fiber *idle = NULL; // IDLE task - performs a power efficient sleep, and system maintenance tasks.
Fiber *fiberPool = NULL; // Pool of unused fibers, just waiting for a job to do.
Cortex_M0_TCB *emptyContext = NULL; // Initialized context for fiber entry state.
/*
* Time since power on. Measured in milliseconds.
* When stored as an unsigned long, this gives us approx 50 days between rollover, which is ample. :-)
*/
unsigned long ticks = 0;
/*
* Scheduler wide flags
*/
uint8_t fiber_flags = 0;
/**
@ -49,14 +49,30 @@ void queue_fiber(Fiber *f, Fiber **queue)
{
__disable_irq();
// Record which queue this fiber is on.
f->queue = queue;
f->next = *queue;
f->prev = NULL;
if(*queue != NULL)
(*queue)->prev = f;
*queue = f;
// Add the fiber to the tail of the queue. Although this involves scanning the
// list, it results in fairer scheduling.
if (*queue == NULL)
{
f->next = NULL;
f->prev = NULL;
*queue = f;
}
else
{
// Scan to the end of the queue.
// We don't maintain a tail pointer to save RAM (queues are nrmally very short).
Fiber *last = *queue;