Skip to content

Commit 22dfcae

Browse files
committed
Update kernel MRI to use mbed::UnbufferedSerial
This change includes: * The DebugSerial constructor now only supports UART as the communication channel. It also only supports instantiation as a global object. * Using mbed::UnbufferedSerial for UART communications instead of the higher level Arduino HardwareSerial classes means a few simplifications can be made: * No longer need to force execution of the serial begin() method from setup(). Can just construct the mbed::UnbufferedSerial object from DebugSerial's constructor. * Hooking UART interrupt to support CTRL+C is done in two stages: * First call UnbufferedSerial::attach() to configure UART for interrupts on received data. * Then call NVIC_SetVector() to replace mbed's ISR with the mriExceptionHandler assembly language routine. * Now running DebugMonitor and UART interrupts at priority level 2. Priority level 1 is the default priority used by the STM USB driver. This allows the PC to have some basic communication with the USB stack while halted in the debugger.
1 parent aa6d2a7 commit 22dfcae

File tree

3 files changed

+34
-160
lines changed

3 files changed

+34
-160
lines changed

libraries/MRI/examples/MRI/MRI.ino

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
#include <MRI.h>
77

8-
DebugSerial debugSerial(Serial1, USART1_IRQn, 230400);
8+
DebugSerial debugSerial(SERIAL1_TX, SERIAL1_RX, USART1_IRQn, 230400);
99

1010
void setup() {
1111
}

libraries/MRI/src/boards/portenta-h7/DebugSerial.cpp

Lines changed: 22 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
See the License for the specific language governing permissions and
1313
limitations under the License.
1414
*/
15-
// GDB debugging of the Arduino Portenta-H7 over a serial connection.
15+
// GDB Kernel debugging of the Arduino Portenta-H7 over a serial UART connection.
1616
#include "DebugSerial.h"
1717
extern "C" {
1818
#include <core/mri.h>
@@ -55,26 +55,13 @@ static DebugSerial* g_pDebugSerial = NULL;
5555
// Will be setting initial breakpoint on setup() routine.
5656
void setup();
5757

58-
// Forward Function Declarations
59-
static uint32_t readRegisterFromContextBuffer(Buffer* pBuffer, uint32_t regIndex);
60-
static void writeRegisterToContextBuffer(Buffer* pBuffer, uint32_t regIndex, uint32_t regValue);
61-
58+
// The debugger uses this handler to catch faults, debug events, etc.
59+
extern "C" void mriExceptionHandler(void);
6260

63-
DebugSerial::DebugSerial(HardwareSerial& serial, IRQn_Type irq, uint32_t baudRate, bool breakInSetup) :
64-
_pContextBuffer(NULL), _commIsr(NULL), _serial(serial), _contextBufferSize(0), _baudRate(baudRate),
65-
_irq(irq), _breakInSetup(breakInSetup)
66-
{
67-
construct();
68-
}
6961

70-
DebugSerial::DebugSerial(HardwareSerial& serial, IRQn_Type irq) :
71-
_pContextBuffer(NULL), _commIsr(NULL), _serial(serial), _contextBufferSize(0), _baudRate(0),
72-
_irq(irq), _breakInSetup(false)
62+
DebugSerial::DebugSerial(PinName txPin, PinName rxPin, IRQn_Type irq, uint32_t baudRate, bool breakInSetup /*=true*/) :
63+
_serial(txPin, rxPin, baudRate), _irq(irq), _breakInSetup(breakInSetup)
7364
{
74-
construct();
75-
}
76-
77-
void DebugSerial::construct() {
7865
// Just return without doing anything if the singleton has already been initialized.
7966
// This ends up using the first initialized DebugSerial object.
8067
if (g_pDebugSerial != NULL) {
@@ -84,14 +71,10 @@ void DebugSerial::construct() {
8471

8572
mriInit("");
8673

87-
if (_baudRate != 0) {
88-
callSerialBeginFromSetup();
89-
}
74+
setupStopInSetup();
9075
}
9176

92-
void DebugSerial::callSerialBeginFromSetup() {
93-
_contextBufferSize = Platform_GetPacketBufferSize();
94-
_pContextBuffer = new char[_contextBufferSize];
77+
void DebugSerial::setupStopInSetup() {
9578
mriCore_SetTempBreakpoint((uint32_t)setup, justEnteredSetupCallback, NULL);
9679
}
9780

@@ -100,66 +83,13 @@ int DebugSerial::justEnteredSetupCallback(void* pv){
10083
}
10184

10285
int DebugSerial::justEnteredSetup() {
103-
Buffer buffer;
104-
105-
// Get current register context. Save away the contents of the registers in the context that we want to modify to
106-
// setup for a call to g_pDebugSerial->initSerial().
107-
Buffer_Init(&buffer, _pContextBuffer, _contextBufferSize);
108-
Platform_CopyContextToBuffer(&buffer);
109-
_lrOrig = readRegisterFromContextBuffer(&buffer, 14);
110-
_pcOrig = readRegisterFromContextBuffer(&buffer, 15);
111-
112-
uint32_t lr = (uint32_t)setup;
113-
uint32_t pc = (uint32_t)_initSerial;
114-
writeRegisterToContextBuffer(&buffer, 14, lr);
115-
writeRegisterToContextBuffer(&buffer, 15, pc);
116-
Buffer_Reset(&buffer);
117-
Platform_CopyContextFromBuffer(&buffer);
118-
119-
mriCore_SetTempBreakpoint((uint32_t)setup, justReturnedFromInitSerialCallback, NULL);
120-
121-
// Return 1 to indicate that we want to resume execution.
122-
return 1;
123-
}
124-
125-
static uint32_t readRegisterFromContextBuffer(Buffer* pBuffer, uint32_t regIndex) {
126-
uint32_t regValue = 0;
127-
uint8_t* pCurr = (uint8_t*)&regValue;
128-
129-
pBuffer->pCurrent = pBuffer->pStart + 8 * regIndex;
130-
for (size_t i = 0 ; i < sizeof(regValue) ; i++) {
131-
*pCurr++ = Buffer_ReadByteAsHex(pBuffer);
132-
}
133-
return regValue;
134-
}
135-
136-
static void writeRegisterToContextBuffer(Buffer* pBuffer, uint32_t regIndex, uint32_t regValue) {
137-
uint8_t* pCurr = (uint8_t*)&regValue;
138-
139-
pBuffer->pCurrent = pBuffer->pStart + 8 * regIndex;
140-
for (size_t i = 0 ; i < sizeof(regValue) ; i++) {
141-
Buffer_WriteByteAsHex(pBuffer, *pCurr++);
142-
}
143-
}
144-
145-
int DebugSerial::justReturnedFromInitSerialCallback(void* pv) {
146-
return g_pDebugSerial->justReturnedFromInitSerial();
147-
}
148-
149-
int DebugSerial::justReturnedFromInitSerial() {
150-
Buffer buffer;
151-
152-
// Restore the registers to the way that they were before we called g_pDebugSerial->initSerial().
153-
Buffer_Init(&buffer, _pContextBuffer, _contextBufferSize);
154-
writeRegisterToContextBuffer(&buffer, 14, _lrOrig);
155-
writeRegisterToContextBuffer(&buffer, 15, _pcOrig);
156-
Buffer_Reset(&buffer);
157-
Platform_CopyContextFromBuffer(&buffer);
86+
initSerial();
15887

15988
// Return 0 to indicate that we want to halt execution at the beginning of setup() or 1 to not force a halt.
16089
return _breakInSetup ? 0 : 1;
16190
}
16291

92+
16393
DebugSerial::~DebugSerial() {
16494
// IMPORTANT NOTE: You are attempting to destroy the connection to GDB which isn't allowed.
16595
// Don't allow your DebugSerial object to go out of scope like this.
@@ -168,56 +98,33 @@ DebugSerial::~DebugSerial() {
16898
// Loop forever.
16999
}
170100
}
101+
171102
void DebugSerial::setSerialPriority(uint8_t priority) {
172103
mriCortexMSetPriority(_irq, priority, 0);
173104
}
174105

175-
void DebugSerial::_initSerial() {
176-
g_pDebugSerial->initSerial();
177-
delay(STARTUP_DELAY_MSEC);
178-
}
179-
180106
void DebugSerial::initSerial() {
181-
_serial.begin(_baudRate);
182-
setSerialPriority(0);
183107

184108
// Hook communication port ISR to allow debug monitor to be awakened when GDB sends a command.
185-
_commIsr = (IsrFunctionPtr) NVIC_GetVector(_irq);
186-
NVIC_SetVector(_irq, (uint32_t)commInterruptHook);
187-
}
188-
189-
void DebugSerial::commInterruptHook(void) {
190-
g_pDebugSerial->pendDebugMonAndRunCommIsr();
191-
}
192-
193-
void DebugSerial::pendDebugMonAndRunCommIsr(void) {
194-
_commIsr();
195-
if (_serial.available()) {
196-
// Pend a halt into the debug monitor if there is now data from GDB ready to be read by it.
197-
setMonitorPending();
198-
}
109+
_serial.attach(mriExceptionHandler);
110+
setSerialPriority(2);
111+
NVIC_SetVector(_irq, (uint32_t)mriExceptionHandler);
199112
}
200113

201114
uint32_t DebugSerial::hasReceiveData(void) {
202-
handleAnyPendingCommInterrupts();
203-
return _serial.available();
204-
}
205-
206-
void DebugSerial::handleAnyPendingCommInterrupts() {
207-
if (NVIC_GetPendingIRQ(_irq)) {
208-
_commIsr();
209-
NVIC_ClearPendingIRQ(_irq);
210-
}
115+
return _serial.readable();
211116
}
212117

213118
int DebugSerial::receiveChar(void) {
214119
while (!hasReceiveData()) {
215120
}
216-
return _serial.read();
121+
uint8_t byte;
122+
_serial.read(&byte, 1);
123+
return byte;
217124
}
218125

219126
void DebugSerial::sendChar(int character) {
220-
_serial.write(character);
127+
_serial.write(&character, 1);
221128
}
222129

223130

@@ -227,9 +134,6 @@ void DebugSerial::sendChar(int character) {
227134
// Global Platform_* functions needed by MRI to initialize and communicate with MRI.
228135
// These functions will perform most of their work through the DebugSerial singleton.
229136
// ---------------------------------------------------------------------------------------------------------------------
230-
// The debugger uses this handler to catch faults, debug events, etc.
231-
extern "C" void mriExceptionHandler(void);
232-
233137
struct SystemHandlerPriorities {
234138
uint8_t svcallPriority;
235139
uint8_t pendsvPriority;
@@ -244,19 +148,18 @@ static void switchFaultHandlersToDebugger();
244148

245149
extern "C" void Platform_Init(Token* pParameterTokens) {
246150
SystemHandlerPriorities origPriorities = getSystemHandlerPrioritiesBeforeMriModifiesThem();
247-
uint8_t debugMonPriority = 1;
151+
uint8_t debugMonPriority = 2;
248152

249153
__try
250154
mriCortexMInit((Token*)pParameterTokens, debugMonPriority, WAKEUP_PIN_IRQn);
251155
__catch
252156
__rethrow;
253157

254-
// UNDONE: Might want to always keep the USB handler at elevated priority.
255-
// Set interrupt used by serial comms (UART or USB) at highest priority.
256-
// Set DebugMonitor interrupt at next highest priority.
158+
// USB defaults to a priority of 1, keep that as highest priority interrupt so that it can respond to PC requests
159+
// while kernel debugging.
160+
// Set interrupts used by UART serial comms and DebugMonitor at next highest priority.
257161
// Set all other external interrupts lower than both serial comms and DebugMonitor.
258162
restoreSystemHandlerPriorities(&origPriorities);
259-
g_pDebugSerial->setSerialPriority(0);
260163

261164
switchFaultHandlersToDebugger();
262165
}

libraries/MRI/src/boards/portenta-h7/DebugSerial.h

Lines changed: 11 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@
1212
See the License for the specific language governing permissions and
1313
limitations under the License.
1414
*/
15-
// GDB debugging of the Arduino Portenta-H7 over a serial connection.
15+
// GDB Kernel debugging of the Arduino Portenta-H7 over a serial connection.
1616
#pragma once
1717

1818
#include <Arduino.h>
19+
#include <mbed.h>
1920

2021
extern "C" {
2122
typedef struct Token Token;
@@ -26,70 +27,40 @@ extern "C" {
2627
}
2728

2829

30+
// UNDONE: Should I put the debugger objects into the Arduino namespace?
2931
namespace arduino {
3032

3133
class DebugSerial {
3234
public:
33-
// You must declare your DebugSerial object as a global or function scoped static so that it doesn't get
34-
// destroyed. Which constructor you use, depends on where you declare it.
35-
36-
// Use this constructor when declaring DebugSerial object as a global outside of any functions.
37-
// You must specify the baudrate so that DebugSerial can call begin() on the specified serial object.
38-
// breakInSetup - should it break at beginning of setup().
39-
//
40-
// Global Example:
41-
// DebugSerial debugSerial(Serial1, USART1_IRQn, 115200);
42-
DebugSerial(HardwareSerial& serial, IRQn_Type IRQn, uint32_t baudRate, bool breakInSetup=true);
43-
44-
// Use this constructor when declaring DebugSerial object as a function scoped static. You must call begin() on
45-
// the serial object before constructing the DebugSerial object.
46-
//
47-
// Function Scoped Static Example:
48-
// #include <MRI.h>
49-
// void setup() {
50-
// Serial1.begin(115200);
51-
// static DebugSerial debugSerial(Serial1, USART1_IRQn);
52-
// __debugbreak();
53-
// }
54-
DebugSerial(HardwareSerial& serial, IRQn_Type IRQn);
35+
// You must declare your DebugSerial object as a global.
36+
// Example:
37+
// DebugSerial debugSerial(SERIAL1_TX, SERIAL1_RX, USART1_IRQn, 230400);
38+
DebugSerial(PinName txPin, PinName rxPin, IRQn_Type irq, uint32_t baudRate, bool breakInSetup=true);
5539

5640
// You should never let your DebugSerial object go out of scope. Make it global or static. To warn you if you do
5741
// let it go out of scope by mistake, this destructor will break into GDB and then enter an infinite loop.
5842
~DebugSerial();
5943

6044
protected:
6145
void construct();
62-
void callSerialBeginFromSetup();
46+
void setupStopInSetup();
6347

6448
// These protected methods are called from global Platform* routines via singleton.
6549
void setSerialPriority(uint8_t priority);
6650
uint32_t hasReceiveData(void);
6751
int receiveChar(void);
6852
void sendChar(int character);
6953

70-
static void _initSerial();
7154
void initSerial();
7255

73-
static void commInterruptHook(void);
74-
void pendDebugMonAndRunCommIsr();
75-
void handleAnyPendingCommInterrupts();
76-
7756
static int justEnteredSetupCallback(void* pvContext);
7857
int justEnteredSetup();
79-
static int justReturnedFromInitSerialCallback(void* pvContext);
80-
int justReturnedFromInitSerial();
8158

8259
typedef void(*IsrFunctionPtr)(void);
8360

84-
char* _pContextBuffer;
85-
IsrFunctionPtr _commIsr;
86-
HardwareSerial& _serial;
87-
uint32_t _contextBufferSize;
88-
uint32_t _baudRate;
89-
uint32_t _lrOrig;
90-
uint32_t _pcOrig;
91-
IRQn_Type _irq;
92-
bool _breakInSetup;
61+
mbed::UnbufferedSerial _serial;
62+
IRQn_Type _irq;
63+
bool _breakInSetup;
9364

9465
friend void ::mriPlatform_Init(Token* pParameterTokens);
9566
friend uint32_t ::mriPlatform_CommHasReceiveData(void);

0 commit comments

Comments
 (0)