本帖最后由 Simon21ic 于 2015-8-6 11:23 编辑
最近有人提起了动态加载,自己也抽空评估了一下这个技术,虽然并没有什么卵用,不过还是玩了一下。
动态加载,顾名思义,就是动态加载应用,应用程序可以不在芯片内部的flash里,比如外接的U盘里,然后载入到内存中运行。然而,一般的嵌入式操作系统,都不具备这种能力。
由于编译器具备ropi选项,所以程序部分,比较容易实现地址无关(不管程序在哪个地址,都可以正常运行)。但是,即便编译器具备rwpi,也一般是通过R9寄存器,来设置rw段的基地址。这样虽然可行,但是会非常麻烦,所有应用中的函数,被调用时,都需要保证R9被正确设置。
VSF构架,由于给应用层的接口都是面向对象的,使得能够非常容易的解决这个问题,就是应用程序不需要静态内存,应用需要的内存资源都使用动态分配。VSF的进程,以及回调函数等等,都可以设置一个用户定义的指针,正好可以用来指向这个分配的内存。
系统代码:
#include "vsf.h"
#include "app_hw_cfg.h"
struct vsfapp_t
{
struct app_hwcfg_t hwcfg;
uint8_t bufmgr_buffer[64 * 1024];
} static app =
{
{
{
{
2, // uint8_t port;
13, // uint8_t pin;
}, // struct usb_pullup;
}, // struct usbd;
{
{3, 4, 3, 6},
{4, 1, 4, 0},
{3, 5, 3, 3},
{3, 9, 3, 10},
{4, 15, 3, 8},
{4, 13, 4, 14},
{4, 11, 4, 12},
{4, 9, 4, 10},
{4, 7, 4, 8},
{3, 0, 3, 1},
{3, 14, 3, 15},
{6, 13, 6, 14},
{4, 6, 4, 2},
{4, 4, 4, 5},
{3, 13, 4, 3},
{3, 11, 3, 12},
{6, 4, 6, 5},
{6, 2, 6, 3},
{6, 0, 6, 1},
{5, 14, 5, 15},
{5, 12, 6, 13},
{5, 4, 5, 5},
{5, 2, 5, 3},
{5, 0, 5, 1},
}, // struct led_t led[24];
}, // struct app_hwcfg_t hwcfg;
};
// tickclk interrupt, simply call vsftimer_callback_int
static void app_tickclk_callback_int(void *param)
{
vsftimer_callback_int();
}
int main(void)
{
int (*app_main)(const struct vsf_t *vsf, struct app_hwcfg_t *hwcfg) = NULL;
vsf_leave_critical();
// system initialize
vsf_bufmgr_init(app.bufmgr_buffer, sizeof(app.bufmgr_buffer));
core_interfaces.core.init(NULL);
core_interfaces.tickclk.init();
core_interfaces.tickclk.start();
vsftimer_init();
core_interfaces.tickclk.set_callback(app_tickclk_callback_int, NULL);
// load and call application
// TODO: try to load app_main address
app_main = (int (*)(const struct vsf_t *vsf, struct app_hwcfg_t *hwcfg))0x08008000;
if (app_main != NULL)
{
app_main(&vsf, &app.hwcfg);
}
while (1)
{
vsfsm_poll();
vsf_enter_critical();
if (!vsfsm_get_event_pending())
{
// sleep, will also enable interrupt
core_interfaces.core.sleep(SLEEP_WFI);
}
else
{
vsf_leave_critical();
}
}
}
应用层序只是做了系统初始化,包括系统初始化,定时器管理初始化,内存管理初始化后,就可以调用应用程序了,这里假定应用程序位于0x08008000的位置。当然,应用程序可以是多个,也可以不在内部flash里(这个就需要先把应用程序复制到内存中)。应用程序的第一个32位数据,是放应用程序用,main函数的偏移。应用中的main函数,参数为vsf和硬件配置。vsf提供了可以使用的系统的所有接口,包括访问硬件的接口,简单的定义如下:
const struct vsf_t vsf =
{
&core_interfaces,
{
vsfsm_init,
vsfsm_pt_init,
vsfsm_post_evt,
vsfsm_post_evt_pending,
vsfsm_enter_critical_internal,
vsfsm_leave_critical_internal,
vsfsm_sem_init_internal,
vsfsm_sem_post_internal,
vsfsm_sem_pend_internal,
vsfsm_crit_init_internal,
vsfsm_crit_enter_internal,
vsfsm_crit_leave_internal,
vsftimer_register,
vsftimer_unregister,
}, // struct vsf_framework_t framework;
{
{
vsf_fifo_init,
vsf_fifo_push8,
vsf_fifo_pop8,
vsf_fifo_push,
vsf_fifo_pop,
vsf_fifo_get_data_length,
vsf_fifo_get_avail_length,
}, // struct fifo
{
vsf_bufmgr_malloc,
vsf_bufmgr_malloc_aligned,
vsf_bufmgr_free,
}, // struct bufmgr;
}, // struct buffer;
};
当然,这个还只是测试的代码,很多协议栈的接口还都没放进去。
应用部分:
应用程序,只需要知道芯片是什么类型的,用来指定编译用的指令集。比如,本例程中芯片使用stm32,所以应用程序的工程可以选择CortexM3的芯片。当然,如果选择CortexM0,那就各种CortexM的处理器都可以兼容。简单的说就是硬件可以使用stm32f1,也可以用新塘的M0,或者atmel的M4,应用程序的二进制文件都不用修改。上代码吧:
#include "vsf.h"
#include "app_hw_cfg.h"
#define LED_EVT_CARRY (VSFSM_EVT_USER_LOCAL + 0)
struct vsfapp_t
{
struct
{
struct vsf_t const *vsf;
} sys;
struct vsfsm_t sm;
struct vsftimer_timer_t timer;
// Application
uint8_t led_num;
struct app_led_t
{
struct led_t const *hw;
bool on;
struct vsfsm_pt_t pt;
struct vsfsm_t *sm_carry;
struct vsfsm_t sm;
struct vsfapp_t *app;
} led[24];
};
static vsf_err_t app_led_thread(struct vsfsm_pt_t *pt, vsfsm_evt_t evt)
{
struct app_led_t *led = (struct app_led_t *)pt->user_data;
struct vsfapp_t *app = led->app;
struct interfaces_info_t const *ifs = app->sys.vsf->ifs;
struct vsf_framework_t const *framework = &app->sys.vsf->framework;
vsfsm_pt_begin(pt);
while (1)
{
vsfsm_pt_wfe(pt, LED_EVT_CARRY);
if (led->on)
{
ifs->gpio.clear(led->hw->hport, 1 << led->hw->hpin);
if (led->sm_carry != NULL)
{
framework->post_evt(led->sm_carry, LED_EVT_CARRY);
}
}
else
{
ifs->gpio.set(led->hw->hport, 1 << led->hw->hpin);
}
led->on = !led->on;
}
vsfsm_pt_end(pt);
return VSFERR_NONE;
}
static struct vsfsm_state_t *
app_evt_handler(struct vsfsm_t *sm, vsfsm_evt_t evt)
{
struct vsfapp_t *app = (struct vsfapp_t *)sm->user_data;
struct interfaces_info_t const *ifs = app->sys.vsf->ifs;
struct vsf_framework_t const *framework = &app->sys.vsf->framework;
struct app_led_t *led;
struct led_t const *hwled;
uint8_t i;
switch (evt)
{
case VSFSM_EVT_INIT:
// Application
for (i = 0; i < app->led_num; i++)
{
led = &app->led[i];
hwled = led->hw;
led->on = false;
led->pt.thread = app_led_thread;
led->pt.user_data = led;
led->sm_carry = (i < (app->led_num - 1)) ? &app->led[i + 1].sm : NULL;
led->app = app;
ifs->gpio.init(hwled->lport);
ifs->gpio.clear(hwled->lport, 1 << hwled->lpin);
ifs->gpio.config_pin(hwled->lport, hwled->lpin, ifs->gpio.constants.OUTPP);
ifs->gpio.init(hwled->hport);
ifs->gpio.clear(hwled->hport, 1 << hwled->hpin);
ifs->gpio.config_pin(hwled->hport, hwled->hpin, ifs->gpio.constants.OUTPP);
framework->pt_init(&led->sm, &led->pt);
}
app->timer.interval = 1;
app->timer.evt = LED_EVT_CARRY;
app->timer.sm = sm;
framework->timer_register(&app->timer);
break;
case LED_EVT_CARRY:
framework->post_evt(&app->led[0].sm, LED_EVT_CARRY);
break;
}
return NULL;
}
vsf_err_t __iar_program_start(struct vsf_t const *vsf, struct app_hwcfg_t const *hwcfg)
{
int i;
struct vsfapp_t *app = vsf->buffer.bufmgr.malloc(sizeof(struct vsfapp_t));
if (NULL == app)
{
return VSFERR_NOT_ENOUGH_RESOURCES;
}
memset(app, 0, sizeof(*app));
app->sys.vsf = vsf;
app->led_num = dimof(hwcfg->led);
for (i = 0; i < app->led_num; i++)
{
app->led[i].hw = &hwcfg->led[i];
}
app->sm.init_state.evt_handler = app_evt_handler;
app->sm.user_data = app;
vsf->framework.sm_init(&app->sm);
return VSFERR_NONE;
}
应用程序里,定义了struct vsfapp_t结构,但是并没有使用静态分配的内存,所以应用程序编译后,只占用flash,不占用ram。运行应用程序的main后,调用vsf提供的内存管理接口,分配app的内存,并设置给相应的结构。应用中,并没有实现main函数,而是实现了__iar_program_start,这个是iar的程序入口,如果实现这个的话,IAR就不会自己添加调用main的代码(因为main是在系统里调用的,不需要程序自己调用),这里可以认为__iar_program_start就是应用的main函数。应用中的其他部分,都只是访问指向app内存的指针,来访问内存资源。当然,还有一个,就是编译后的镜像的第一个32为,需要是main函数的指针,IAR下可以这么实现,在startup.s文件中:
SECTION .entry:CODE:ROOT(4)
EXTERN __iar_program_start
DATA
DCD __iar_program_start
END
并且,在linker文件中,把这个放在地址0。
|