C 转储闪存的微小代码

C 转储闪存的微小代码,c,assembly,embedded,arm,firmware,C,Assembly,Embedded,Arm,Firmware,我正试图编写尽可能小的代码来提取英飞凌XMC4500微控制器的固件 代码必须适合30字节缓冲区,该缓冲区允许我使用Thumb 16位指令集拥有15条机器指令 从C开始,我尝试通过一个GPIO引脚转储闪存,这是一个很好的技巧 基本上我正在做的是: 设置要输出的GPIO引脚方向 用时钟(SPI串行时钟)闪烁LED1(引脚1.1) 闪烁LED2(引脚1.0)和数据位(SPI MOSI) 用逻辑分析仪嗅探引脚 编辑: 更新C代码块 添加汇编代码块 #包括“XMC4500.h” void main(){

我正试图编写尽可能小的代码来提取英飞凌XMC4500微控制器的固件

代码必须适合30字节缓冲区,该缓冲区允许我使用Thumb 16位指令集拥有15条机器指令

从C开始,我尝试通过一个GPIO引脚转储闪存,这是一个很好的技巧

基本上我正在做的是:

  • 设置要输出的GPIO引脚方向
  • 用时钟(SPI串行时钟)闪烁LED1(引脚1.1)
  • 闪烁LED2(引脚1.0)和数据位(SPI MOSI)
  • 用逻辑分析仪嗅探引脚
  • 编辑:

  • 更新C代码块
  • 添加汇编代码块
  • #包括“XMC4500.h”
    void main(){
    //在内存地址0x00000000处开始转储
    无符号int*p=(uint32_t*)(0x0u);
    //配置端口1输出(推拉)
    端口1->IOCR0=0x8080u;
    对于(;;){
    int i=32;
    int data=*(p++);
    做{
    //时钟低
    端口1->OUT=0x0;
    //数据位时钟高电平
    端口1->OUT=0x2u |数据;
    数据>>=1;
    }而(-i>0);
    }
    }
    
    然而,代码大小仍然太大,无法满足我的要求


    我能做些什么来进一步缩减我的代码吗?任何可以优化或省去的东西?

    十六条指令并不是全部;我不希望C编译器能够产生足够高效的代码,从而使内存转储发生位爆炸。如果您对输出位模式不太挑剔,我认为32字节就足够了,可以使用以下内容:

        ldr  r1,=Port1      ; Address of IO Port
        mov  r3,#1
        str  r3,[r1+IOCR0]
        lsl  r0,r3,#27
    bytes:
        mov  r5,#9
        strb r5,[r1+OUT]
        add  r0,#1
        ldrb r4,[r0]
    bits:
        strb r4,[r1+OUT]
        lsr  r4,#1
        sub  r5,#1
        bne  bits
        b    bytes
    

    每个字节将作为一个高脉冲输出,然后是八位时间,可能是高或低(取决于数据读取),然后是位时间,该位时间始终为零,以确保下一个高脉冲的上升沿可见。基本上类似于异步串行通信,但级别相反。

    如果端口1的高位在此过程中没有变化,并且您可以确保在时钟变高后读取数据位,您可以尝试以下方法:

    #define P1_DEFAULT =   ?//constant high bits of port 1, zeros in low two bits
    int* dp=0;             //maybe use a register which is known to be zeroed.
    PORT1->IOCR0 = 0x8080;  //should be 3 ins
    for(;;){
      int i=32;            //
      int data=*(dp++);    //LDMIA instruction may do load and increment in 1 step.
      do{
        PORT1->OUT = P1_DEFAULT  #clock low
        PORT1->OUT = P1_DEFAULT + 2+ (data&1); #clock high with data
        data>>=1;
      } while (--i>0);
    }
    
    这将删除三端口读取、一端口写入和一个条件。

    在一个函数中完成这一切,以避免任何调用开销。我将从为此生成的程序集开始,看看您可以做些什么来改进它。

    对于如此严格的要求,您必须在程序集中完成。编译器不会针对空间进行优化(它会针对空间进行速度优化),也不会使用怪癖,因为它们通常会因更新而中断。获取程序,编译并读取/解码程序集输出。能否加载缓冲区,然后重新加载缓冲区?也就是说,您可以使用不同的代码序列将寄存器初始化为已知值,然后运行实际执行数据传输的代码吗?否则不太可能,因为大部分代码都将加载常量。@artlessnoise很遗憾,重新加载缓冲区不是一个选项。您是否测试了更改?
    r12
    始终为0,因此您可以使用它来清除时钟,或者它随着
    ldr.w
    前进,在这种情况下,您可以将其用作
    p
    。不是两者都有。而最新的变化现在也将“随机”数据写入端口1的高位引脚。你确定那不会引起问题吗?@AShelly我的改动很好。1.:r12确实与ldr一起前进。然而,ldr.w将r12增加4,这将始终给我一个偶数。位1将始终为零。在这种情况下,我可以使用r12作为p和清除时钟。2.:为了切换两个引脚,我只需要前两位;所以我想没有必要担心高脚,对吧?谢谢你的提示!代码缩减到44字节!现在,我将进入生成的程序集,并尝试进行更多改进(请参阅上面更新的代码块)。很高兴它有所帮助。假设端口中的所有其他位都是1,用
    PORT1->OUT=0xFFFE |数据替换第二次写入可能会删除其中一个添加。再次感谢!现在我有36字节的代码!还有三条指示要走!请参阅上面更新的代码块。我无法运行您的示例代码。没有输出。你测试过了吗?您的逻辑分析仪配置是什么?我没有测试过,因为我不熟悉您使用的精确控制器;我的假设是,在写入IOCR0的值中设置的任何位都将启用输出,而写入将设置这些IO引脚的高或低状态。
    
    #define P1_DEFAULT =   ?//constant high bits of port 1, zeros in low two bits
    int* dp=0;             //maybe use a register which is known to be zeroed.
    PORT1->IOCR0 = 0x8080;  //should be 3 ins
    for(;;){
      int i=32;            //
      int data=*(dp++);    //LDMIA instruction may do load and increment in 1 step.
      do{
        PORT1->OUT = P1_DEFAULT  #clock low
        PORT1->OUT = P1_DEFAULT + 2+ (data&1); #clock high with data
        data>>=1;
      } while (--i>0);
    }