打印

关于STM32虚拟内存

[复制链接]
5568|22
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
同志们好!!
俺来提个虚拟内存的问题,看有没有大神知道。

想必很多人知道,IAR FOR STM8 可以使用 __eeprom关键字来声明变量,使变量存放于eeprom之中, 当程序操作变量时,会自动调用相应的eeprom读写函数,实现“透明传输”

这在我的意识里面,是一种虚拟内存。
而今天我有一个想法。在没有FSMC接口的前提条件下,将某些外部存储器,比如PSRAM,或者SPI  FLASH,连接到STM32的IO上, 那么对PSRAM的操作只能用IO模拟时序, 对SPI FLASH的操作只能通过SPI接口。 我想把他们变成虚拟内存, 直接对变量进行存取, 在C代码里隐藏IO调用。 请问大大们可有方法??

曾经幻想过 HardFault或MemManage这类中断或许可以实现,但很难找到当前出错的地址和寄存器,而且频繁中断的效率又低, 故又幻想编译器本身有没有这类功能??
沙发
格物致知| | 2013-1-8 16:22 | 只看该作者
这里没啥人,问了也白问!

使用特权

评论回复
板凳
airwill| | 2013-1-8 16:24 | 只看该作者
我觉得, 如果利用好存储器总线异常, 将芯片内部的 SRAM 作为内存空间, 实现虚拟内存是有可能的.
不过虚拟内存的管理是个庞大的系统, 这套系统放在 FLASH 里也未必够. 另外, STM32 的内部 SRAM 的空间又太小. 导致交换过于频繁.
所以, 可行性不高.

使用特权

评论回复
地板
趴趴熊|  楼主 | 2013-1-8 16:30 | 只看该作者
主要是实现虚拟地址

使用特权

评论回复
5
明月小厨| | 2013-1-8 18:59 | 只看该作者
没方法;如果有方法你就不会提这样的问题了;

使用特权

评论回复
6
明月小厨| | 2013-1-8 19:01 | 只看该作者
IO操作SRAM 会搞死人的;严重消耗系统资源;

使用特权

评论回复
7
myxiaonia| | 2015-2-8 10:46 | 只看该作者
想法不错啊,看权威指南可以看到,确实可以使用中断的方式模拟某些特殊指令,像你说的扩展内存空间的方法,貌似还是种标准的正规做法呢

使用特权

评论回复
8
zchong| | 2015-2-8 13:11 | 只看该作者
你这不是把简单的东西搞复杂了嘛
不能为了函数的接口简单,就弄这么个复杂的东西

使用特权

评论回复
9
科技猎人| | 2015-2-11 14:44 | 只看该作者
没有MMU单元是实现不了虚地址访问的。这也是MCU和MPU最大的差别

使用特权

评论回复
10
turmary| | 2016-4-3 13:32 | 只看该作者
本帖最后由 turmary 于 2016-4-3 13:45 编辑

我有一个方案还没有实现,大体的想法发生保留地址执行(访问)的异常的时候捕获地址,执行相应SRAM地址中的指令。
该SRAM中的指令从其它存储按需加载,对跳转和数据访问要单独解析指令编码然后做仿真处理,以得到应用程序的虚拟PC寄存器值和数据。
说起来就像实现了半个虚拟机,一部分指令使用Cortex-M3实际运行,另一部分使用仿真。
理论上整体运行效率只有实际硬件上运行的1%以下。

使用特权

评论回复
11
turmary| | 2016-4-9 17:05 | 只看该作者
本帖最后由 turmary 于 2016-4-11 00:21 编辑

hello world!
PRIMASK   = 0x00000000
FAULTMASK = 0x00000000
MPU config complete

ROM TABLE ITEMS
ROM[ 0] = 0xE000E000 SCS
ROM[ 1] = 0xE0001000 DWT
ROM[ 2] = 0xE0002000 FPB
ROM[ 3] = 0xE0000000 ITM
ROM[ 4] = 0xE0040000 TPIU
ROM[ 5] = 0xE0041000 ETM

********************fault isr********************
last mode       = Thread
     stack      = PSP
     access     = Privileged
     fault      = BusFault
     sp         = 0x20001198
     MSP        = 0x200009A0
     BFSR       = 0x04 Imprecise_data_bus_err

ep->0 [r0]      = 0x00000005
ep->1 [r1]      = 0x0000F000
ep->2 [r2]      = 0x00000005
ep->3 [r3]      = 0x00000000
ep->4 [r12]     = 0x00000000
ep->5 [lr]      = 0x20008000
ep->6 [pc]      = 0x0000021C
ep->7 [psr]     = 0x61000000 -ZC-- T
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

********************fault isr********************
last mode       = Thread
     stack      = PSP
     access     = Privileged
     fault      = BusFault
     sp         = 0x20001198
     MSP        = 0x200009A0
     BFSR       = 0x82 Precise_data_bus_err BFAR_valid_address
     BFAR       = 0x2000F000

ep->0 [r0]      = 0x00008AB8
ep->1 [r1]      = 0x2000F000
ep->2 [r2]      = 0x00000005
ep->3 [r3]      = 0x00000000
ep->4 [r12]     = 0x00000000
ep->5 [lr]      = 0x20008000
ep->6 [pc]      = 0x00000224
ep->7 [psr]     = 0x61000000 -ZC-- T
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[0x2000F000] = 00000005
run [0x00020001]

********************fault isr********************
last mode       = Thread
     stack      = PSP
     access     = Privileged
     fault      = BusFault
     sp         = 0x20001198
     MSP        = 0x200009A0
     BFSR       = 0x01 Instruction_bus_err

ep->0 [r0]      = 0x00000011
ep->1 [r1]      = 0x20001264
ep->2 [r2]      = 0x00000011
ep->3 [r3]      = 0x00002889
ep->4 [r12]     = 0x00000000
ep->5 [lr]      = 0x00000251
ep->6 [pc]      = 0x00020000
ep->7 [psr]     = 0x61000000 -ZC-- T
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
run [0xE00FF000]

********************fault isr********************
last mode       = Thread
     stack      = PSP
     access     = Privileged
     fault      = MemManage
     sp         = 0x20001198
     MSP        = 0x200009A0
     MMFSR      = 0x01 Instruction_access_violation

ep->0 [r0]      = 0x00000011
ep->1 [r1]      = 0x20001264
ep->2 [r2]      = 0x00000011
ep->3 [r3]      = 0x00002889
ep->4 [r12]     = 0x00000000
ep->5 [lr]      = 0x00000263
ep->6 [pc]      = 0xE00FF000
ep->7 [psr]     = 0x60000000 -ZC-- -
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
psp_err_handler(): ### stop ###

大部分内容都解析出来了,几年前的代码了

使用特权

评论回复
12
huangcunxiake| | 2016-4-9 21:59 | 只看该作者
如果可以挂载外部芯片作为内存使用,确实是很好,可是系统可以自动启用这个内存吗

使用特权

评论回复
13
turmary| | 2016-4-9 22:11 | 只看该作者
外部挂SRAM和并行Flash是可以直接访问的,其它接口的恐怕不行了。

使用特权

评论回复
14
zhuomuniao110| | 2016-4-10 11:01 | 只看该作者
因为闪存的读写次数有限,跟电脑硬盘不同,你这样搞,没多久就挂了。

使用特权

评论回复
15
turmary| | 2016-4-10 16:24 | 只看该作者
我们在讨论要实现该怎么做,交换设备不一定是闪存啊,RO从闪存或SD卡加载,RW可以交换到外部RAM的。
这里问题的关键是怎么处理系统异常指令。

简单的例子,现在RAM够用了,这样读写数据不用交换的。
但是ROM太小代码放不下,外扩ROM又不是直接(读写内存地址)访问的,怎么办?

使用特权

评论回复
16
yangshu_mcu| | 2016-4-11 15:57 | 只看该作者
你这是想实现MMU的功能

使用特权

评论回复
17
turmary| | 2016-4-12 22:29 | 只看该作者
本帖最后由 turmary 于 2016-4-12 22:35 编辑

不是的,我要做的是半个虚拟机,在Cortex-M3上仿真Cortex-M3的部分指令,像跳转和数据读写。
其它的指令直接在Cortex-M3上执行。
做了虚拟机之后,就可以运行代码量很大的程序,不受片内Flash大小限制,只是程序实际速度要慢很多。

使用特权

评论回复
18
huangqi412| | 2016-4-24 22:34 | 只看该作者
turmary 发表于 2016-4-24 20:18
Cortex-M3上的Cortex-M3仿真模型已完成,LED闪烁应用程序和emulator直接烧写进片内Flash,运行应用时从内片F ...

能否详细说说cm3上虚拟cm3

使用特权

评论回复
19
turmary| | 2016-4-25 00:25 | 只看该作者
本帖最后由 turmary 于 2016-4-25 19:34 编辑

后面半个月我要写连载,半个月的原理说明。

这个虚拟机实现需要用到保留地址上的两个异常
1. bus_fault子类BFSR_IBUSERR(Instruction bus err)
用于生成地址异常
2. bus_fault子类BFSR_PRECISERR(Precise data bus err)
用于生成数据异常
这两个异常不需要打开MPU。

保留地址取Cortex-M3芯片的保留地址,比如LM3S1138的保留地址如下图

0x1000 0000 - 0x1FFF FFFF 指定为代码空间,在这里执行时产生BFSR_IBUSERR.
0x3000 0000 - 0x3FFF FFFF 指定为数据空间,指令读写这里产生BFSR_PRECISERR.
编译应用程序时代码空间(RO)指定基地址为0x1000 0000,数据(RW)指定基地址为0x3000 0000.

确保你的Cortex-M3芯片在以上地址生成相应的异常,可以区分执行指令的异常和读写数据的异常并可以截获数据地址。
如果保留地址(无MPU)方案不符合异常要求,可以打开并设置MPU,这样需要处理的异常类型是mem_manage_fault的两个子类。

使用特权

评论回复
20
turmary| | 2016-9-13 23:15 | 只看该作者
本帖最后由 turmary 于 2016-9-14 00:15 编辑


不处于(取指和数据)异常的环境是应用程序环境,因为在不存在的虚拟地址取指,Cortex-M3会产生取指异常,进入异常后的环境是仿真环境。
仿真环境的代码都在有效ROM地址范围,即仿真环境本身不能再触发异常,否则就会死循环了。

发生异常后,Cortex-M3硬件自动保存的堆栈exception_t到PSP指向的RAM,
typedef struct {
    unsigned r0;
    unsigned r1;
    unsigned r2;
    unsigned r3;
    unsigned r12;
    unsigned lr;
    unsigned pc;
    unsigned psr;
} exception_t;
这时已进入仿真环境,首先需要用汇编处理硬件堆栈,整理出应用程序的寄存器环境exception2_t
typedef struct {
    unsigned r0;        // 0x00
    unsigned r1;
    unsigned r2;
    unsigned r3;
    unsigned r4;        // 0x10
    unsigned r5;
    unsigned r6;
    unsigned r7;
    unsigned r8;        // 0x20
    unsigned r9;
    unsigned r10;
    unsigned r11;
    unsigned r12;        // 0x30
    unsigned sp;
    unsigned lr;
    unsigned pc;
    unsigned psr;        // 0x40
    unsigned exc_return;
} exception2_t;
所有应用环境寄存器里的地址都是虚拟地址。

后面仿真环境全用C代码处理喽,无非是获取PC处指令的二进制代码,分析后做相应动作,并且不断重复分析、执行这两个动作。
int fault_with_ibus(const m3env_t* env, volatile unsigned virt_pc) {
    opcode_t* op = opcode_buf;
    unsigned long pc;

#if ACCELERATE_IBUS_INNER_LOOP
next_ibus_fault:
#endif
    pc = virt2phy(virt_pc);

    // maybe an unaligned access
    op->binary = unaligned_long(pc);
    opcode_parse(op);

    opcode_exec(op, env);
    if (op->attr & OPCODE_ATTR_EMUL) {
        // if soft emulation then

        #if ACCELERATE_IBUS_INNER_LOOP
        // remove Thumb state
        virt_pc = (env->ep2->pc & ~0x1UL) + INSTRUCTION_SIZE(op);
        if (!interrupt_pending() && IN_TEXT_AREA(virt_pc)) {
            env->ep2->pc = virt_pc;
            goto next_ibus_fault;
        } else
        #endif
        {
            return INSTRUCTION_SIZE(op);
        }
    }
    // if hard_exec then
    return 0;
}

与地址无关的指令可以直接使用硬件仿真,这里的办法很简单,
把这个指令放进某个RAM单元 --- exec_space[0],下个RAM单元 --- exec_space[1]放一个断点指令。
保存下一个应用程序指令的地址<1>,设置异常返回后的PC指向exec_space[0],并从异常返回。
就时处于应用环境,执行exec_space[0]里的指令,之后执行exec_space[1]里的指令将触发调试异常,
在调试异常中(仿真环境)恢复地址<1>到PC,异常返回后回到应用环境,一个应用程序指令(硬件)仿真周期完成。
#define BKPT_INST        0xBE01UL
#define CSPC_INSTRUCTION    0
#define    CSPC_BKPT        1
unsigned exec_space[] = {
    (BKPT_INST << 16) | BKPT_INST,
    (BKPT_INST << 16) | BKPT_INST,
};
static uint32_t real_pc[] = {0, 0};

int hard_exec_entry(const m3env_t* env, const opcode_t* op) {
    unsigned next_pc;

    if (IS_INST_32BIT(op)) {
        next_pc = env->ep2->pc + 4;
        exec_space[0] = op->binary;
    } else {
        next_pc = env->ep2->pc + 2;
        exec_space[0] = (BKPT_INST << 16) | (op->binary & 0xFFFFUL);
    }

    real_pc[is_app_in_intr()] = next_pc;
    env->ep2->pc = (unsigned)&exec_space[0];

    /*
    printf("exec_space[0] = 0x%.8X\n", exec_space[0]);
    printf("exec_space[1] = 0x%.8X\n", exec_space[1]);
    */
    return 0;
}
这时应用环境在下一条指令的虚拟地址取指,再次触发取指异常,新的应用指令仿真周期开始。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:STM8技术交流QQ群(超级群):79723989

0

主题

33

帖子

1

粉丝