全志D1程序烧录失败引发的向量指令的学习

电子说

1.2w人已加入

描述

前一段时间遇到给D1下载程序触发异常的问题。研究许久无果,多日后终得解决。

问题
使用全志提供的命令下载的方式,若在第一个线程启动前不做停止操作(添加while(1),延时)等操作,使用该种方式下载会触发异常。

使用全志PhoenixSuit工具下载不会触发异常。

问题定位
最终问题出现定位在第一个线程启动前。可惜水平有限,还是没找到原因。

问题解决
经高人指点,发现mstatus中的vs位在第一个线程启动前被修改。

于是在启动第一个线程前,开始排查修改vs位的地方。

最终发现是在初始化线程的现场时被修改。

rt_uint8_t *rt_hw_stack_init(void *tentry,
void *parameter,
rt_uint8_t *stack_addr,
void *texit)
{
struct rt_hw_stack_frame *frame;
rt_uint8_t *stk;
int i;
extern int __global_pointer$;
stk = stack_addr + sizeof(rt_ubase_t);
stk = (rt_uint8_t *)RT_ALIGN_DOWN((rt_ubase_t)stk, REGBYTES);
stk -= sizeof(struct rt_hw_stack_frame);
frame = (struct rt_hw_stack_frame *)stk;
for (i = 0; i < sizeof(struct rt_hw_stack_frame) / sizeof(rt_ubase_t); i++)
{
((rt_ubase_t )frame)[i] = i;
}
frame->ra = (rt_ubase_t)texit;
frame->gp = (rt_ubase_t)&__global_pointer$;
frame->a0 = (rt_ubase_t)parameter;
frame->epc = (rt_ubase_t)tentry;
frame->x2 = (rt_ubase_t)stk;
/
force to supervisor mode(SPP=1) and set SPIE and SUM to 1 /
#ifdef ENABLE_FPU
frame->mstatus = MSTATUS_VS| MSTATUS_PUM | MSTATUS_FS | MSTATUS_MPP | MSTATUS_MPIE; /
enable FPU */
#else
frame->mstatus = MSTATUS_PUM | MSTATUS_MPP | MSTATUS_MPIE;
#endif
return stk;
}
上述代码修改前,下载会触发异常:

frame- >mstatus = MSTATUS_PUM | MSTATUS_FS | MSTATUS_MPP | MSTATUS_MPIE;    /* enable FPU */

修改后,下载正常:

frame- >mstatus = MSTATUS_VS| MSTATUS_PUM | MSTATUS_FS | MSTATUS_MPP | MSTATUS_MPIE;    /* enable FPU */

为什么这么改?请看下文:

事情到这就清晰了,是因为没有初始化向量指令控制位VS.

RT-Thread

回到最初的问题:

下载主要的操作就是数据拷贝,使用向量指令拷贝会大大加速下载速度,所以全志的下载算法会使用向量指令去拷贝,这里没有初始化VS,当然会触发异常了。

到这还有个疑问:

mstatus的vs在用户程序中修改的,这里按道理已经在用户程序了,整么还在下载,这个问题就不得而知了,你知道吗,反正我不知道

一番操作过后,看看这个向量指令到底是个啥吧:

向量指令
向量指令:单条指令操作多个数据,并行

使用场景:数据拷贝 用并行方式代替循环的方式

向量指令使用举例:
一段上下文切换中的代码:

#ifdef __riscv_vector
addi sp, sp, -(20+20)
csrr t0, vl // 记录 矢量寄存器 能 处理的 数据 个数
sd t0, (0 +0 )(sp) // 备份
csrr t0, vtype // 描述 矢量寄存器 数据 类型
sd t0, (4 +4 )(sp) // 备份
csrr t0, vstart // 向量 起始 索引 向量指令 执行 第一个元素 的 索引
sd t0, (8 +8 )(sp) // 备份
csrr t0, vxsat // 描述 运算 结果 是否 饱和
sd t0, (12 +12 )(sp) // 备份
csrr t0, vxrm // 舍入 模式 类似四舍五入的(略) 那种
sd t0, (16 +16 )(sp) // 备份
addi sp, sp, -(256+256)
vsetvli zero, zero, e8, m8 // 设置 数据宽度8(1字节) 8个向量寄存器为一组
vsb.v v0, (sp) //存储V0 ~ V7 矢量寄存器数据至 内存 存储数量 (128:矢量寄存器宽度 / 8:数据宽度) * 8:矢量寄存器组中的矢量寄存器的个数
addi sp, sp, 128//
vsb.v v8, (sp) // 存储V8 ~ V15
addi sp, sp, 128
vsb.v v16, (sp)// 存储V16 ~ V23
addi sp, sp, 128
vsb.v v24, (sp)// 存储V24 ~ V31
addi sp, sp, -(256+256-128)
#endif
la t2, do_irq// 处理中断
jalr t2
#ifdef __riscv_vector
vsetvli zero, zero, e8, m8 // 采用与之前同样的配置方式
vlb.v v0, (sp)
addi sp, sp, 128//拷贝V0 ~ V7 内存 至矢量寄存器数 拷贝数量 (128:矢量寄存器宽度 / 8:数据宽度) * 8:矢量寄存器组中的矢量寄存器的个数
vlb.v v8, (sp)
addi sp, sp, 128//拷贝V8 ~ V15
vlb.v v16, (sp)
addi sp, sp, 128//拷贝V16 ~ V23
vlb.v v24, (sp)
addi sp, sp, 128//拷贝V24 ~ V31
lwu t0, (0 +0)(sp)// 加载 vl
lwu t1, (4 +4)(sp)// 加载 vtype
lwu t2, (8 +8)(sp)// 加载 vstart
vsetvl zero, t0, t1 // 重新设置 vl vtype
csrw vstart, t2// 加载 vstart
lwu t2, (12 +12)(sp)
csrw vxsat, t2 // 加载 vxsat
lwu t2, (16 +16)(sp)
csrw vxrm, t2 // 加载 vxrm
addi sp, sp, (20+20)
#endif
RISC-V小学生,不住之处请多多指教.

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分