让我试着解释一下我在找什么,因为我找不到更好的标题措辞。
假设我正在对 RP2040 微控制器进行编程,我可以使用
gdb
和 openocd
与它建立调试会话。 (请注意,即使我在这里通过一个具体的 MCU 平台进行讨论,我也很想知道这种方法是否可以实现一般 - 使用任何类型的“外部微控制器”,gdb
可能能够瞄准)
现在假设我想用外部硬件做一些(相对简单的)过程:举个例子,假设我想打开一些 GPIO 引脚,等待 2000 个 CPU 周期,然后关闭相同的 GPIO。即使是这样一个简单的例子,这也需要硬件初始化,所以总而言之,在固件代码中我必须做类似的事情(C 使用 pico-sdk):
#define MY_PIN_NR 12
static inline void my_hardware_init(void) {
gpio_init(MY_PIN_NR);
gpio_set_dir(MY_PIN_NR, GPIO_OUT);
}
static inline void my_hardware_do_process(void) {
// raise pin high:
gpio_put(MY_PIN_NR, 1);
// wait for 2000 CPU cycles
uint16_t cycles_to_wait = 2000;
while(cycles_to_wait--) {
asm volatile("nop");
}
// set pin low:
gpio_put(MY_PIN_NR, 0);
}
void my_hardware_full_process(void) {
// ensure hardware is initialized
my_hardware_init();
// do process:
my_hardware_do_process();
}
如果这是在固件中编译并刻录在闪存中,我可以在 GDB 会话中直接在目标微控制器上调用它,say:
(gdb) call my_hardware_full_process()
(甚至只是
p my_hardware_full_process()
);那么即使调试器让微控制器在断点处暂停,函数仍然执行,然后返回到调试器。
现在,这意味着闪存上烧录了实际代码(从
gdb
解析为符号 my_hardware_full_process
的位置开始)。
所以,我的问题是——我能以某种方式做类似的事情吗,也就是说,执行与
my_hardware_full_process
中相同的代码,但是如果微控制器闪存被完全擦除/未初始化? (这意味着微控制器没有可运行的代码,因此不会运行任何代码 - 注意 gdb
通过 openocd
仍然可以挂接到此状态)。在这种情况下,即使gdb
从.elf文件中获取到my_hardware_full_process
的地址,它仍然是一个不包含可运行代码的地址,因此使用(gdb) call function-symbol()
的方法失败。
考虑到这一点,我在猜测,也许可以编译一个“二进制 blob”,它将包含
my_hardware_full_process()
函数的程序集 - 例如,arm-none-eabi-objdump -S --disassemble=my_hardware_full_process firmware.elf
这里会给出:
Disassembly of section .text:
10000310 <my_hardware_full_process>:
}
// set pin low:
gpio_put(MY_PIN_NR, 0);
}
void my_hardware_full_process(void) {
10000310: b510 push {r4, lr}
gpio_init(MY_PIN_NR);
10000312: 200c movs r0, #12
10000314: f003 fcf2 bl 10003cfc <gpio_init>
* Switch all GPIOs in "mask" to output
*
* \param mask Bitmask of GPIO to set to output, as bits 0-29
*/
static inline void gpio_set_dir_out_masked(uint32_t mask) {
sio_hw->gpio_oe_set = mask;
10000318: 23d0 movs r3, #208 ; 0xd0
1000031a: 061b lsls r3, r3, #24
1000031c: 2280 movs r2, #128 ; 0x80
1000031e: 0152 lsls r2, r2, #5
10000320: 625a str r2, [r3, #36] ; 0x24
sio_hw->gpio_set = mask;
10000322: 615a str r2, [r3, #20]
uint16_t cycles_to_wait = 2000;
10000324: 22fa movs r2, #250 ; 0xfa
10000326: 00d2 lsls r2, r2, #3
while(cycles_to_wait--) {
10000328: e001 b.n 1000032e <my_hardware_full_process+0x1e>
asm volatile("nop");
1000032a: 46c0 nop ; (mov r8, r8)
while(cycles_to_wait--) {
1000032c: 001a movs r2, r3
1000032e: 1e53 subs r3, r2, #1
10000330: b29b uxth r3, r3
10000332: 2a00 cmp r2, #0
10000334: d1f9 bne.n 1000032a <my_hardware_full_process+0x1a>
sio_hw->gpio_clr = mask;
10000336: 23d0 movs r3, #208 ; 0xd0
10000338: 061b lsls r3, r3, #24
1000033a: 2280 movs r2, #128 ; 0x80
1000033c: 0152 lsls r2, r2, #5
1000033e: 619a str r2, [r3, #24]
// ensure hardware is initialized
my_hardware_init();
// do process:
my_hardware_do_process();
}
10000340: bd10 pop {r4, pc}
Disassembly of section .data:
所以,基本上,我需要这段代码,加上
<gpio_init>
和依赖项跳转到的地方 - 本质上,PC 上已知的“静态构建”。原则上,我可以想象一个“静态构建”blob,它“包含”运行(在这种情况下)my_hardware_full_process
函数所需的所有要求/依赖项。
那么问题就变成了:我能否以某种方式使用
gdb
读取 PC 上的这种“静态构建二进制 blob”文件,然后以某种方式将指令及其数据“推送”到微控制器,并让 blob 的在那里执行的指令(即“即时”),因此硬件执行预期的功能(之后,控制返回到gdb
提示符)——即使闪存已被完全擦除?
如果是这样,我怎么能创建这样一个“静态构建二进制 blob”——我怎么能指示
gdb
在目标微控制器上运行它?