Skip to content

Commit b17d263

Browse files
committed
Switch ThreadMRI to use mbed serial objects
This commit enables ThreadMRI to use mbed's lower level serial objects instead of the currently used Arduino ones. The ThreadMRI constructor now takes a DebugCommInterface pointer to either a UartDebugCommInterface or UsbDebugCommInterface instantiation. This change allows ThreadMRI to use the attach() method on both UART and USB based connections to support breaking into running programs via CTRL+C. Before it had to insert a shim into the device's vector table. NOTE: I had to buffer the bytes as they were received in the attached routine for UART based communications since if it didn't read out the most recently received byte, the UART receive interrupt line wouldn't be cleared and it would just keep launching the UART ISR.
1 parent 481b06a commit b17d263

File tree

3 files changed

+175
-90
lines changed

3 files changed

+175
-90
lines changed

libraries/ThreadMRI/examples/ThreadMRI/ThreadMRI.ino

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@
44

55
#include <ThreadMRI.h>
66

7-
//ThreadMRI g_debugger(Serial1, USART1_IRQn, 115200, THREADMRI_BREAK_ON_SETUP);
8-
ThreadMRI g_debugger(Serial, OTG_HS_IRQn, 115200, THREADMRI_BREAK_ON_SETUP);
9-
//ThreadMRI g_debugger(Serial, false);
7+
//UartDebugCommInterface g_comm(SERIAL1_TX, SERIAL1_RX, 230400);
8+
//ThreadMRI g_debugSerial(&g_comm, THREADMRI_BREAK_ON_SETUP);
9+
10+
UsbDebugCommInterface g_comm(&SerialUSB);
11+
ThreadMRI g_debugSerial(&g_comm, THREADMRI_BREAK_ON_SETUP);
1012

1113
extern "C" void testContext(void);
1214

libraries/ThreadMRI/src/ThreadMRI.cpp

Lines changed: 107 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,7 @@ extern "C"
7373

7474

7575
// Globals that describe the ThreadMRI singleton.
76-
static HardwareSerial* g_pSerial;
77-
static IRQn_Type g_irq;
78-
static uint32_t g_baudRate;
76+
static DebugCommInterface* g_pComm;
7977
static bool g_breakInSetup;
8078

8179
// The ID of the halted thread being debugged.
@@ -110,8 +108,6 @@ volatile uint32_t mriThreadOrigHardFault;
110108
volatile uint32_t mriThreadOrigMemManagement;
111109
volatile uint32_t mriThreadOrigBusFault;
112110
volatile uint32_t mriThreadOrigUsageFault;
113-
// Address of the original Serial peripheral ISR to be hooked.
114-
void (*g_origSerialISR)(void);
115111

116112
// Entries to track the chunks of the context in a scatter list.
117113
#if MRI_DEVICE_HAS_FPU
@@ -151,9 +147,8 @@ static void resumeApplicationThreads();
151147
static void readThreadContext(osThreadId_t thread);
152148
static void switchRtxHandlersToDebugStubsForSingleStepping();
153149
static void restoreRtxHandlers();
154-
static void callSerialBeginFromSetup();
150+
static void callAttachFromSetup();
155151
static int justEnteredSetupCallback(void* pv);
156-
static void hookSerialISR();
157152
static bool isThreadMode(uint32_t excReturn);
158153
static bool hasEncounteredDebugEvent();
159154
static bool hasControlCBeenDetected();
@@ -174,35 +169,16 @@ static void setControlCFlag();
174169

175170

176171

177-
178-
ThreadMRI::ThreadMRI(HardwareSerial& serial, IRQn_Type IRQn, uint32_t baudRate, bool breakInSetup /* =true */)
179-
{
180-
init(serial, IRQn, baudRate, breakInSetup);
181-
}
182-
183-
184-
ThreadMRI::ThreadMRI(HardwareSerial& serial, bool breakInSetup /* =true */)
172+
ThreadMRI::ThreadMRI(DebugCommInterface* pCommInterface, bool breakInSetup /*=true*/)
185173
{
186-
init(serial, SysTick_IRQn, 115200, breakInSetup);
187-
}
188-
189-
190-
ThreadMRI::ThreadMRI(HardwareSerial& serial, IRQn_Type IRQn)
191-
{
192-
init(serial, IRQn, 0, false);
193-
}
194-
195-
void ThreadMRI::init(HardwareSerial& serial, IRQn_Type IRQn, uint32_t baudRate, bool breakInSetup)
196-
{
197-
if (g_pSerial != NULL) {
174+
if (g_pComm != NULL) {
198175
// Only allow 1 ThreadMRI object to be initialized.
199176
return;
200177
}
201178

202179
// Setup the singleton.
203-
g_irq = IRQn;
204-
g_baudRate = baudRate;
205180
g_breakInSetup = breakInSetup;
181+
g_pComm = pCommInterface;
206182

207183
// Initialize the MRI core.
208184
mriInit("");
@@ -225,10 +201,7 @@ void ThreadMRI::init(HardwareSerial& serial, IRQn_Type IRQn, uint32_t baudRate,
225201
return;
226202
}
227203

228-
g_pSerial = &serial;
229-
if (g_baudRate != 0) {
230-
callSerialBeginFromSetup();
231-
}
204+
callAttachFromSetup();
232205
}
233206

234207
static __NO_RETURN void mriMain(void *pv)
@@ -364,32 +337,19 @@ static void restoreRtxHandlers()
364337
NVIC_SetVector(SysTick_IRQn, mriThreadOrigSysTick);
365338
}
366339

367-
static void callSerialBeginFromSetup()
340+
static void callAttachFromSetup()
368341
{
369342
mriCore_SetTempBreakpoint((uint32_t)setup, justEnteredSetupCallback, NULL);
370343
}
371344

372345
static int justEnteredSetupCallback(void* pv)
373346
{
374-
g_pSerial->begin(g_baudRate);
375-
//while (!g_pSerial) {}
376-
377-
if (g_irq != SysTick_IRQn) {
378-
hookSerialISR();
379-
} else {
380-
(static_cast<USBSerial*>(g_pSerial))->attach(serialISRHook);
381-
}
347+
g_pComm->attach(serialISRHook);
382348

383349
// Return 0 to indicate that we want to halt execution at the beginning of setup() or 1 to not force a halt.
384350
return g_breakInSetup ? 0 : 1;
385351
}
386352

387-
static void hookSerialISR()
388-
{
389-
g_origSerialISR = (void(*)(void))NVIC_GetVector(g_irq);
390-
NVIC_SetVector(g_irq, (uint32_t)serialISRHook);
391-
}
392-
393353

394354
ThreadMRI::~ThreadMRI() {
395355
// IMPORTANT NOTE: You are attempting to destroy the connection to GDB which isn't allowed.
@@ -441,20 +401,20 @@ static void recordAndSwitchFaultHandlersToDebugger()
441401

442402
uint32_t Platform_CommHasReceiveData(void)
443403
{
444-
return g_pSerial->available();
404+
return g_pComm->available();
445405
}
446406

447407
int Platform_CommReceiveChar(void)
448408
{
449-
while (!g_pSerial->available()) {
409+
while (!g_pComm->available()) {
450410
// Busy wait.
451411
}
452-
return g_pSerial->read();
412+
return g_pComm->read();
453413
}
454414

455415
void Platform_CommSendChar(int character)
456416
{
457-
g_pSerial->write(character);
417+
g_pComm->write(character);
458418
}
459419

460420

@@ -693,10 +653,7 @@ static void clearFaultStatusBits()
693653

694654
static void serialISRHook()
695655
{
696-
if (g_origSerialISR) {
697-
g_origSerialISR();
698-
}
699-
if (!isDebuggerActive() && g_pSerial->available() > 0) {
656+
if (!isDebuggerActive() && g_pComm->available()) {
700657
// Pend a halt into the debug monitor now that there is data from GDB ready to be read by it.
701658
setControlCFlag();
702659
setMonitorPending();
@@ -712,3 +669,97 @@ static void setControlCFlag()
712669
{
713670
mriCortexMFlags |= CORTEXM_FLAGS_CTRL_C;
714671
}
672+
673+
674+
675+
676+
DebugCommInterface::~DebugCommInterface()
677+
{
678+
}
679+
680+
UartDebugCommInterface::UartDebugCommInterface(PinName txPin, PinName rxPin, uint32_t baudRate) :
681+
_pCallback(NULL), _serial(txPin, rxPin, baudRate), _read(0), _write(0)
682+
{
683+
}
684+
685+
UartDebugCommInterface::~UartDebugCommInterface()
686+
{
687+
}
688+
689+
bool UartDebugCommInterface::available()
690+
{
691+
return _read != _write;
692+
}
693+
694+
uint8_t UartDebugCommInterface::read()
695+
{
696+
// This read should never block since Platform_CommReceiveChar() always checks available() first.
697+
ASSERT ( available() );
698+
699+
uint8_t byte = _queue[_read];
700+
_read = wrappingIncrement(_read);
701+
return byte;
702+
}
703+
704+
uint32_t UartDebugCommInterface::wrappingIncrement(uint32_t val)
705+
{
706+
return (val + 1) & (sizeof(_queue) - 1);
707+
}
708+
709+
void UartDebugCommInterface::write(uint8_t c)
710+
{
711+
_serial.write(&c, 1);
712+
}
713+
714+
void UartDebugCommInterface::attach(void (*pCallback)())
715+
{
716+
_pCallback = pCallback;
717+
_serial.attach(mbed::callback(this, &UartDebugCommInterface::onReceivedData));
718+
}
719+
720+
void UartDebugCommInterface::onReceivedData()
721+
{
722+
while (_serial.readable()) {
723+
uint8_t byte;
724+
_serial.read(&byte, 1);
725+
if (wrappingIncrement(_write) != _read) {
726+
// _queue isn't full so we can add this byte to it.
727+
_queue[_write] = byte;
728+
_write = wrappingIncrement(_write);
729+
}
730+
}
731+
732+
ASSERT ( _pCallback != NULL );
733+
_pCallback();
734+
}
735+
736+
737+
738+
UsbDebugCommInterface::UsbDebugCommInterface(arduino::USBSerial* pSerial) :
739+
_pSerial(pSerial)
740+
{
741+
}
742+
743+
UsbDebugCommInterface::~UsbDebugCommInterface()
744+
{
745+
}
746+
747+
bool UsbDebugCommInterface::available()
748+
{
749+
return _pSerial->available() > 0;
750+
}
751+
752+
uint8_t UsbDebugCommInterface::read()
753+
{
754+
return _pSerial->read();
755+
}
756+
757+
void UsbDebugCommInterface::write(uint8_t c)
758+
{
759+
_pSerial->write(c);
760+
}
761+
762+
void UsbDebugCommInterface::attach(void (*pCallback)())
763+
{
764+
_pSerial->attach(pCallback);
765+
}

libraries/ThreadMRI/src/ThreadMRI.h

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,46 +16,78 @@
1616
// the USB serial connection.
1717
#pragma once
1818

19+
#include <mbed.h>
20+
21+
22+
class DebugCommInterface {
23+
public:
24+
virtual ~DebugCommInterface() = 0;
25+
26+
virtual bool available() = 0;
27+
virtual uint8_t read() = 0;
28+
virtual void write(uint8_t c) = 0;
29+
virtual void attach(void (*pCallback)()) = 0;
30+
};
31+
32+
class UartDebugCommInterface : public DebugCommInterface {
33+
public:
34+
UartDebugCommInterface(PinName txPin, PinName rxPin, uint32_t baudRate);
35+
virtual ~UartDebugCommInterface();
36+
37+
virtual bool available();
38+
virtual uint8_t read();
39+
virtual void write(uint8_t c);
40+
virtual void attach(void (*pCallback)());
41+
42+
protected:
43+
uint32_t wrappingIncrement(uint32_t val);
44+
void onReceivedData();
45+
46+
void (* _pCallback)();
47+
mbed::UnbufferedSerial _serial;
48+
volatile uint32_t _read;
49+
volatile uint32_t _write;
50+
uint8_t _queue[8];
51+
};
52+
53+
class UsbDebugCommInterface : public DebugCommInterface {
54+
public:
55+
UsbDebugCommInterface(arduino::USBSerial*);
56+
virtual ~UsbDebugCommInterface();
57+
58+
virtual bool available();
59+
virtual uint8_t read();
60+
virtual void write(uint8_t c);
61+
virtual void attach(void (*pCallback)());
62+
63+
protected:
64+
arduino::USBSerial* _pSerial;
65+
};
66+
1967

2068
// Pass THREADMRI_BREAK_ON_SETUP as breakInSetup parameter of ThreadMRI constructors to halt in setup().
21-
#define THREADMRI_BREAK_ON_SETUP true
69+
#define THREADMRI_BREAK_ON_SETUP true
70+
// Pass THREADMRI_NO_BREAK_ON_SETUP as breakInSetup parameter of ThreadMRI constructors to NOT halt in setup().
71+
#define THREADMRI_NO_BREAK_ON_SETUP false
2272

2373

2474
class ThreadMRI {
2575
public:
26-
// You must declare your ThreadMRI object as a global or function scoped static so that it doesn't get
27-
// destroyed. Which constructor you use, depends on where you declare it.
28-
29-
// Use this constructor when declaring ThreadMRI object as a global outside of any functions.
30-
// You must specify the baudrate so that ThreadMRI can call begin() on the specified serial object.
31-
// breakInSetup - should it break at beginning of setup().
32-
//
33-
// Global Example:
34-
// ThreadMRI threadMRI(Serial1, USART1_IRQn, 115200);
35-
ThreadMRI(HardwareSerial& serial, IRQn_Type IRQn, uint32_t baudRate, bool breakInSetup=true);
36-
37-
// Use this constructor when declaring ThreadMRI object as a function scoped static. You must call begin() on
38-
// the serial object before constructing the ThreadMRI object.
39-
//
40-
// Function Scoped Static Example:
41-
// #include <MRI.h>
42-
// void setup() {
43-
// Serial1.begin(115200);
44-
// static ThreadMRI threadMRI(Serial1, USART1_IRQn);
45-
// __debugbreak();
46-
// }
47-
ThreadMRI(HardwareSerial& serial, IRQn_Type IRQn);
48-
49-
ThreadMRI(HardwareSerial& serial, bool breakInSetup=true);
50-
51-
// You should never let your ThreadMRI object go out of scope. Make it global or static. To warn you if you do
52-
// let it go out of scope by mistake, this destructor will break into GDB and then enter an infinite loop.
53-
~ThreadMRI();
76+
// You must declare your ThreadMRI object as a global.
77+
// Examples:
78+
// UartDebugCommInterface g_comm(SERIAL1_TX, SERIAL1_RX, 230400);
79+
// ThreadMRI g_debugSerial(&g_comm, THREADMRI_BREAK_ON_SETUP);
80+
// -- OR --
81+
// UsbDebugCommInterface g_comm(&SerialUSB);
82+
// ThreadMRI g_debugSerial(&g_comm, THREADMRI_BREAK_ON_SETUP);
83+
ThreadMRI(DebugCommInterface* pCommInterface, bool breakInSetup=THREADMRI_BREAK_ON_SETUP);
5484

55-
protected:
56-
void init(HardwareSerial& serial, IRQn_Type IRQn, uint32_t baudRate, bool breakInSetup);
85+
// Your ThreadMRI object should never go out of scope. To warn you if you do let it go out of scope by mistake,
86+
// this destructor will break into GDB and then enter an infinite loop.
87+
~ThreadMRI();
5788
};
5889

90+
5991
// Use to insert a hardcoded breakpoint into your code.
6092
#ifndef __debugbreak
6193
#define __debugbreak() { __asm volatile ("bkpt #0"); }

0 commit comments

Comments
 (0)