Skip to content

Commit 8b62f65

Browse files
adamgreenfacchinm
authored andcommitted
ThreadMRI can now be global
ThreadMRI can now be declared as a global like DebugSerial. It will take care of calling begin() on the underlying Serial object. So now if you declare a ThreadMRI object somewhere in your code, that will be enough to pull the MRI debug monitor into your code so that you can use GDB for remote debugging. The implementation of calling HardwareSerial::begin() takes a bit of extra work: * At time the ThreadMRI object's constructor runs, the underlying HardwareSerial object might not yet have been initialized. It is safest to perform this call from setup(). * ThreadMRI sets a temporary breakpoint on setup() from its global constructor. When this breakpoint hits, it can then call the begin() method. By default the ThreadMRI object will cause execution to be halted on the first line of setup() so that the user can connect with GDB and set breakpoints/watchpoints at points of interest before they allow their program to run. This can be disabled via the 4th parameter of ThreadMRI's constructor. I doubled the stack size from 512 to 1024 bytes for mriMain() as the STM code to initialize the baud rate of the serial port is pretty stack hungry and it overflowed at 512 bytes.
1 parent 9c2fca6 commit 8b62f65

File tree

3 files changed

+98
-37
lines changed

3 files changed

+98
-37
lines changed

libraries/ThreadMRI/examples/ThreadMRI/ThreadMRI.ino

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,13 @@
55

66
#include <ThreadMRI.h>
77

8-
ThreadMRI g_debugger;
8+
ThreadMRI g_debugger(Serial1, USART1_IRQn, 115200, true);
99

1010
extern "C" void testContext(void);
1111

1212
void setup() {
13-
// UNDONE: Using Serial1 for now to unblock my progress.
14-
Serial1.begin(115200);
15-
g_debugger.begin();
1613
//testContext();
17-
__debugbreak();
14+
//__debugbreak();
1815
}
1916

2017
void loop() {

libraries/ThreadMRI/src/ThreadMRI.cpp

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,10 @@ extern "C"
2929
#include "architectures/armv7-m/debug_cm3.h"
3030
}
3131

32-
// UNDONE: Switch back to the USB/CDC based serial port.
33-
#undef Serial
34-
#define Serial Serial1
35-
#define MRI_SERIAL_IRQ USART1_IRQn
36-
3732

3833
// Configuration Parameters
3934
// The size of the mriThread() stack in uint64_t objects.
40-
#define MRI_THREAD_STACK_SIZE 64
35+
#define MRI_THREAD_STACK_SIZE 128
4136
// The maximum number of active threads that can be handled by the debugger.
4237
#define MAXIMUM_ACTIVE_THREADS 64
4338

@@ -77,8 +72,11 @@ extern "C"
7772

7873

7974

80-
// Flag to verify that only one ThreadMRI object has been initialized.
81-
static bool g_alreadyInitialized;
75+
// Globals that describe the ThreadMRI singleton.
76+
static HardwareSerial* g_pSerial;
77+
static IRQn_Type g_irq;
78+
static uint32_t g_baudRate;
79+
static bool g_breakInSetup;
8280

8381
// The ID of the halted thread being debugged.
8482
static volatile osThreadId_t g_haltedThreadId;
@@ -143,14 +141,16 @@ static void switchRtxHandlersToDebugStubsForSingleStepping();
143141
static void restoreRtxHandlers();
144142
static void setDebugActiveFlag();
145143
static void clearDebugActiveFlag();
144+
static void callSerialBeginFromSetup();
145+
static int justEnteredSetupCallback(void* pv);
146+
static void hookSerialISR();
146147
static bool isThreadMode(uint32_t excReturn);
147148
static bool hasEncounteredDebugEvent();
148149
static bool hasControlCBeenDetected();
149150
static void recordAndClearFaultStatusBits();
150151
static void wakeMriMainToDebugCurrentThread();
151152
static void stopSingleStepping();
152153
static void recordAndSwitchFaultHandlersToDebugger();
153-
static void hookSerialISR();
154154
static bool isDebugThreadActive();
155155
static void setFaultDetectedFlag();
156156
static bool isImpreciseBusFault();
@@ -164,21 +164,32 @@ static void setControlCFlag();
164164

165165

166166

167-
ThreadMRI::ThreadMRI()
167+
ThreadMRI::ThreadMRI(HardwareSerial& serial, IRQn_Type IRQn, uint32_t baudRate, bool breakInSetup /* =true */)
168168
{
169+
init(serial, IRQn, baudRate, breakInSetup);
169170
}
170171

172+
ThreadMRI::ThreadMRI(HardwareSerial& serial, IRQn_Type IRQn)
173+
{
174+
init(serial, IRQn, 0, false);
175+
}
171176

172-
bool ThreadMRI::begin()
177+
void ThreadMRI::init(HardwareSerial& serial, IRQn_Type IRQn, uint32_t baudRate, bool breakInSetup)
173178
{
174-
if (g_alreadyInitialized) {
179+
if (g_pSerial != NULL) {
175180
// Only allow 1 ThreadMRI object to be initialized.
176-
return false;
181+
return;
177182
}
178183

179-
// UNDONE: Move into constructor.
184+
// Setup the singleton.
185+
g_irq = IRQn;
186+
g_baudRate = baudRate;
187+
g_breakInSetup = breakInSetup;
188+
189+
// Initialize the MRI core.
180190
mriInit("");
181191

192+
// Start the debugger thread.
182193
static uint64_t stack[MRI_THREAD_STACK_SIZE];
183194
static osRtxThread_t threadTcb;
184195
static const osThreadAttr_t threadAttr =
@@ -193,10 +204,13 @@ bool ThreadMRI::begin()
193204
};
194205
mriThreadId = osThreadNew(mriMain, NULL, &threadAttr);
195206
if (mriThreadId == NULL) {
196-
return false;
207+
return;
208+
}
209+
210+
g_pSerial = &serial;
211+
if (g_baudRate != 0) {
212+
callSerialBeginFromSetup();
197213
}
198-
g_alreadyInitialized = true;
199-
return true;
200214
}
201215

202216
static __NO_RETURN void mriMain(void *pv)
@@ -335,6 +349,37 @@ static void clearDebugActiveFlag()
335349
mriCortexMState.flags &= ~CORTEXM_FLAGS_ACTIVE_DEBUG;
336350
}
337351

352+
static void callSerialBeginFromSetup()
353+
{
354+
mriCore_SetTempBreakpoint((uint32_t)setup, justEnteredSetupCallback, NULL);
355+
}
356+
357+
static int justEnteredSetupCallback(void* pv)
358+
{
359+
g_pSerial->begin(g_baudRate);
360+
hookSerialISR();
361+
362+
// Return 0 to indicate that we want to halt execution at the beginning of setup() or 1 to not force a halt.
363+
return g_breakInSetup ? 0 : 1;
364+
}
365+
366+
static void hookSerialISR()
367+
{
368+
g_origSerialISR = (void(*)(void))NVIC_GetVector(g_irq);
369+
NVIC_SetVector(g_irq, (uint32_t)serialISRHook);
370+
}
371+
372+
373+
ThreadMRI::~ThreadMRI() {
374+
// IMPORTANT NOTE: You are attempting to destroy the connection to GDB which isn't allowed.
375+
// Don't allow your ThreadMRI object to go out of scope like this.
376+
__debugbreak();
377+
for (;;) {
378+
// Loop forever.
379+
}
380+
}
381+
382+
338383

339384

340385
// ---------------------------------------------------------------------------------------------------------------------
@@ -351,7 +396,6 @@ void Platform_Init(Token* pParameterTokens)
351396
g_enableDWTandFPB = 0;
352397
mriThreadSingleStepThreadId = NULL;
353398
recordAndSwitchFaultHandlersToDebugger();
354-
hookSerialISR();
355399
}
356400

357401
static void recordAndSwitchFaultHandlersToDebugger()
@@ -368,31 +412,25 @@ static void recordAndSwitchFaultHandlersToDebugger()
368412
NVIC_SetVector(DebugMonitor_IRQn, (uint32_t)mriDebugMonitorHandlerStub);
369413
}
370414

371-
static void hookSerialISR()
372-
{
373-
g_origSerialISR = (void(*)(void))NVIC_GetVector(MRI_SERIAL_IRQ);
374-
NVIC_SetVector(MRI_SERIAL_IRQ, (uint32_t)serialISRHook);
375-
}
376-
377415

378416

379417

380418
uint32_t Platform_CommHasReceiveData(void)
381419
{
382-
return Serial.available();
420+
return g_pSerial->available();
383421
}
384422

385423
int Platform_CommReceiveChar(void)
386424
{
387-
while (!Serial.available()) {
425+
while (!g_pSerial->available()) {
388426
// Busy wait.
389427
}
390-
return Serial.read();
428+
return g_pSerial->read();
391429
}
392430

393431
void Platform_CommSendChar(int character)
394432
{
395-
Serial.write(character);
433+
g_pSerial->write(character);
396434
}
397435

398436
int Platform_CommCausedInterrupt(void)
@@ -632,7 +670,7 @@ static bool isInstruction32Bit(uint16_t firstWordOfInstruction)
632670
static void serialISRHook()
633671
{
634672
g_origSerialISR();
635-
if (!isDebuggerActive() && Serial.available() > 0) {
673+
if (!isDebuggerActive() && g_pSerial->available() > 0) {
636674
// Pend a halt into the debug monitor now that there is data from GDB ready to be read by it.
637675
setControlCFlag();
638676
setMonitorPending();

libraries/ThreadMRI/src/ThreadMRI.h

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,35 @@
1919

2020
class ThreadMRI {
2121
public:
22-
ThreadMRI();
23-
24-
bool begin();
22+
// You must declare your ThreadMRI object as a global or function scoped static so that it doesn't get
23+
// destroyed. Which constructor you use, depends on where you declare it.
24+
25+
// Use this constructor when declaring ThreadMRI object as a global outside of any functions.
26+
// You must specify the baudrate so that ThreadMRI can call begin() on the specified serial object.
27+
// breakInSetup - should it break at beginning of setup().
28+
//
29+
// Global Example:
30+
// ThreadMRI threadMRI(Serial1, USART1_IRQn, 115200);
31+
ThreadMRI(HardwareSerial& serial, IRQn_Type IRQn, uint32_t baudRate, bool breakInSetup=true);
32+
33+
// Use this constructor when declaring ThreadMRI object as a function scoped static. You must call begin() on
34+
// the serial object before constructing the ThreadMRI object.
35+
//
36+
// Function Scoped Static Example:
37+
// #include <MRI.h>
38+
// void setup() {
39+
// Serial1.begin(115200);
40+
// static ThreadMRI threadMRI(Serial1, USART1_IRQn);
41+
// __debugbreak();
42+
// }
43+
ThreadMRI(HardwareSerial& serial, IRQn_Type IRQn);
44+
45+
// You should never let your ThreadMRI object go out of scope. Make it global or static. To warn you if you do
46+
// let it go out of scope by mistake, this destructor will break into GDB and then enter an infinite loop.
47+
~ThreadMRI();
48+
49+
protected:
50+
void init(HardwareSerial& serial, IRQn_Type IRQn, uint32_t baudRate, bool breakInSetup);
2551
};
2652

2753
// Use to insert a hardcoded breakpoint into your code.

0 commit comments

Comments
 (0)