SOPHGO SM5智算模组二次开发参考方案

本文档提供了SOPHGOSM5智算模组的详细开发指导,包括本地和网络刷机方案、获得图形界面的方法、使用Perfetto工具进行性能分析、提升业务程序实时性的策略、安装4G模块的步骤、增加板类型的方法、实现定制化rootfs的过程以及使用BM1684芯片内的eFuse和SPACC进行加解密的具体操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本地、网络刷机方案

参考 https://blog.youkuaiyun.com/weixin_44087610/article/details/126282282 SOPHGO SM5智算模组本地刷机方案、网络刷机方案

SOPHGO SM5获得图形界面

SM5可以通过如下几种方式得到图形界面,有时可以用来方便在板上做一些开发工作或者快速原型验证:

  1. 通过PCIe连接SM750/768芯片的显卡
  2. 通过USB连接FL2000芯片的HDMI dongle
  3. 不连接外部显示设备,在另一台主机上通过VNC登录

前两种连接外部显示设备的方案,可以通过如下方式来使能图形界面(建议使用xfce4,Debian 9仓库里的lxde可能会遇到拖拽窗口时死机的问题):

sudo apt update
sudo apt install xfce4
sudo reboot

待重启后应该就可以看到图形界面了,通过USB键鼠即可操作,需要再通过如下命令关闭休眠机制,以免遇到唤醒问题:

xset s off -dpms

这两种方式相比,更推荐SM750/768的方案,CPU占用率低。FL2000方案会至少用掉一个CPU core来刷新图像。 最后一种VNC的方式通过如下步骤来使能:

sudo apt update
sudo apt install xserver-xorg-video-dummy x11vnc xfce4
sudo cp xorg.conf /etc/X11/
sudo systemctl disable gfx-feeder
sudo reboot
sudo x11vnc -display :0 -auth /var/lib/lightdm/.Xauthority &

然后就可以在另一台主机上用$sm5_ip:0地址来VNC了
上述用到的xorg.conf文件如下:

Section "Device"
    Identifier  "Configured Video Device"
    Driver      "dummy"
    VideoRam 256000
EndSection

Section "Monitor"
    Identifier  "Configured Monitor"
    HorizSync 5.0 - 1000.0
    VertRefresh 5.0 - 200.0
    ModeLine "1920x1080" 148.50 1920 2448 2492 2640 1080 1084 1089 1125 +Hsync +Vsync
#    Modeline "1280x800" 24.15 1280 1312 1400 1432 800 819 822 841
EndSection

Section "Screen"
    Identifier  "Default Screen"
    Monitor     "Configured Monitor"
    Device      "Configured Video Device"
    DefaultDepth 24
    SubSection "Display"
    Depth 24
    Modes "1920x1080"
#    Modes "1280x800"
    EndSubSection
EndSection

SM5使用Perfetto工具分析性能

从2.3.1版本开始,SM5预装了Perfetto工具,可以通过如下步骤使用:

  1. 写一个Perfetto的配置文件,放到SM5上。配置文件描述了要抓取什么样的系统信息。可以参考下面的scheduling.cfg文件写法,这个配置文件会抓取系统进程调度的信息:
    # One buffer allocated within the central tracing binary for the entire trace,
    # shared by the two data sources below.
    buffers {
    size_kb: 20480
    fill_policy: DISCARD
    }
    
    # Ftrace data from the kernel, mainly the process scheduling events.
    data_sources {
    config {
        name: "linux.ftrace"
        target_buffer: 0
        ftrace_config {
        ftrace_events: "sched_switch"
        ftrace_events: "sched_waking"
        ftrace_events: "sched_wakeup_new"
    
        ftrace_events: "task_newtask"
        ftrace_events: "task_rename"
    
        ftrace_events: "sched_process_exec"
        ftrace_events: "sched_process_exit"
        ftrace_events: "sched_process_fork"
        ftrace_events: "sched_process_free"
        ftrace_events: "sched_process_hang"
        ftrace_events: "sched_process_wait"
        }
    }
    }
    
    # Resolve process commandlines and parent/child relationships, to better
    # interpret the ftrace events, which are in terms of pids.
    data_sources {
    config {
        name: "linux.process_stats"
        target_buffer: 0
    }
    }
    
    # 10s trace, but can be stopped prematurely.
    duration_ms: 10000
    
    具体信息可以参考Perfetto的官方文档,https://perfetto.dev/docs/data-sources/cpu-scheduling,也可以在这里找到更多的配置文件参考:https://github.com/google/perfetto/tree/master/test/configs
  2. 在SM5上执行sudo perfetto –txt -c scheduling.cfg -o scheduling.pftrace即开始抓取,当config文件中定义的duration_ms时间到后会自动结束,或ctrl+c杀掉perfetto进程也可以结束抓取。结束后会生成一个 scheduling.pftrace 文件。 打开https://ui.perfetto.dev,点左边的 Open trace file,选择上面生成的 scheduling.pftrace 文件即可。w键放大,s键缩小,a键左平移,d键右平移,点击进程或cpu的色块可以在下面看到具体的描述:
    请添加图片描述
    如果想在应用程序里打点,跟上面的调度信息显示在同一个视图里,可以参考下面的写法,在你想要标记的段落开头调用atrace_begin_body(“foo_bar”),结尾调用atrace_end_body。在配置文件中,增加一行 atrace_categories: “*”,比如加在scheduling.cfgftrace_config段落。
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <sys/ioctl.h>
    #include <math.h>
    
    #define ATRACE_MESSAGE_LENGTH 1024
    
    int atrace_marker_fd = -1;
    
    define WRITE_MSG(format_begin, format_end, name, value) { \
        char buf[ATRACE_MESSAGE_LENGTH];     \
        int pid = getpid(); \
        int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
            name, value); \
        if (len >= (int) sizeof(buf)) { \
            /* Given the sizeof(buf), and all of the current format buffers, \
            * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
            int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
            /* Truncate the name to make the message fit. */ \
            printf("Truncated name in %s: %s\n", __FUNCTION__, name); \
            len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
                name_len, name, value); \
        } \
        write(atrace_marker_fd, buf, len); \
    }
    void atrace_begin_body(const char* name)
    {
        WRITE_MSG("B|%d|", "%s", name, "");
    }
    
    void atrace_end_body()
    {
        WRITE_MSG("E|%d", "%s", "", "");
    }
    
    static void atrace_init_once()
    {
        atrace_marker_fd = open("/sys/kernel/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
        if (atrace_marker_fd < 0) {
            // try debugfs
            atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
            if (atrace_marker_fd < 0) {
                printf("trace_marker file not found\n");
            }
        }
    }
    
    int main(int argc, char const *argv[])
    {
        int i;
    
        atrace_init_once();
        if (atrace_marker_fd < 0)
            return atrace_marker_fd;
    
        for (i = 0; i < 10; i++) {
            atrace_begin_body("user_foo_bar");
            usleep(500 * 1000);
            atrace_end_body();
            usleep(500 * 1000);
        }
        close(atrace_marker_fd);
        return 0;
    }
    

用perfetto抓取trace后,就可以找到你的tag叠加到时间轴上显示了,如下面user_foo_bar标记:
请添加图片描述
如果想在内核态驱动里打点,可以参考如下的做法:

#include <linux/trace_events.h>
在想要标记的段落开头调用atrace_begin_body("foo_bar"),结尾调用atrace_end_body()

在配置文件中,增加一行 atrace_categories: “*”,比如加在scheduling.cfgftrace_config段落。用perfetto抓取trace后,效果跟上面用户态的一样。

SM5提高业务程序的实时性的优化方案

  1. Linux内核使用虚拟内存,对用户空间的内存(包括栈、代码段、数据段以及使用函数malloc或mmap动态分配的内存)使用惰性分配的策略, 如果实时进程访问的虚拟页没有映射到物理页,那么会触发页错误异常,影响实时性。所以需要尽量避免频繁申请释放内存,malloc出来的内存可以先做一次memset再送进实时性敏感的流程。

  2. 对于有明显周期性的实时性敏感业务,可以考虑使用dealine调度器:

    struct sched_attr attr;
    
     memset(&attr, 0, sizeof(attr));
     attr.size = sizeof(attr);
     attr.sched_policy = SCHED_DEADLINE;//设置调度类型
     attr.sched_runtime = 700000000;//设置runtime,纳秒
     attr.sched_deadline = attr.sched_period = 2000000000;//设置deadline和period,纳秒
     ret = sched_setattr(0, &attr, flags);
     if (ret < 0) {
         perror("sched_setattr failed");
         exit(-1);
     }
    
  3. 打开内核抢占,在linux-linaro-stable/arch/arm64/configs/bitmain_bm1684_asic_defconfig文件末尾新增:

    # CONFIG_PREEMPT_NONE is not set
    # CONFIG_PREEMPT_VOLUNTARY is not set
    CONFIG_PREEMPT=y
    

    重新编译内核并替换到板子上重启后,输入uname -a,应该显示:

    Linux bm1684 4.9.38-bm1684-v10.4.0-00550-g4ad0f96b7016-dirty #4 SMP PREEMPT Thu Mar 10 08:56:25 CST 2022 aarch64 GNU/Linux
    

    (默认非抢占内核显示:Linux bm1684 4.9.38-bm1684-v10.4.0-00550-g4ad0f96b7016-dirty #5 SMP Thu Mar 10 09:44:04 CST 2022 aarch64 GNU/Linux)

  4. 将某几个CPU核从系统隔离出来,然后将实时性敏感人物绑定到这些核上,可以尽量避免这些任务被系统的其它事物干扰。以CPU5和6为例,大致有如下步骤:

    1. 修改u-boot的内核启动参数,增加“isolcpus=5,6”字段,这样除了最基础的一些系统任务外,调度器不会主动把任务分配到这两个CPU上。可以通过如下命令确认:cat /sys/devices/system/cpu/isolated
    2. 修改irqbalance的规则文件 (/etcdefault/irqbalance):
      IRQBALANCE_BANNED_CPUS="E0" (原值为80,因为我们默认将PCIe的中断单独绑定到了CPU7上)
      重启后生效,可以通过如下命令观察效果:cat /proc/interrupts
    3. 通过taskset -cp 5,6 $pid命令将制定的进程绑定到这两个CPU上,设置完后要等到下次这个进程被调度执行时才会生效。如果是多线程, 每个线程的ID要单独设置(可以通过ps -T查看)
      ps -o pid,spid,psr,stat,comm -A  #可以在PSR列显示进程最近一次执行时所在的CPU
      

SM5安装4G模块方案

SM5适配的4G模块是广和通NL668。可以直接购买带4G模组的设备,也可以自行单独购买。
请添加图片描述

安装指南

软件配置

  1. 检查4G模块服务是否存在: cat /etc/systemd/system/lteModemManager.service
    如下图所示:
    请添加图片描述
  2. 修改“WorkingDirectory=”后面路径下的cfg.ini文件:修改为Ittefirst = true
  3. 打开4G拨号服务:
    sudo systemctl enable lteModemManager.service
    sudo systemctl restart lteModemManager.service
    
  4. 拨号成功后sudo ifconfig会出现ppp0的网卡并带有IP
  5. route -n 会看到:
    linaro@bm1684:/etc/systemd/system$ sudo route -n
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    0.0.0.0         *.*.*.*     0.0.0.0         UG    0      0        0  ppp0
    
    目前自动拨号服务支持中国电信,联调,移动的SIM卡。 由于没有测试完全,有可能会出现没有覆盖的sim卡类型。如果出现需要修改 lteModem.py 以支持。

SM5增加板类型

  1. 客户修改控制单板PCB版本和BOM版本的硬件电阻。MCU代码会采集电压值确定硬件的版本,并将该值写入到指定地址,使BM1684可以获取。

  2. 由于当前arm-trusted-firmware部分代码未开源,需要联系我们提供适配新的PCB版本和BOM版本的bl2.bin。获取后替换bsp-sdk/install/soc_bm1684_asic/prebuilt/bl2.bin

  3. 修改BSP SDK中的u-boot目录下代码,在bsp-sdk/u-boot/include/configs/bm1684-asic.h中增加新的板类型, 然后修改 bsp-sdk/u-boot/board/bitmain/bm1684/vexpress64.c 中的 pcb_info数组和 dtb_mapping数组,增加该pcb对应的板类型及该板类型在u-boot中使用的dtb,如下图所示:
    请添加图片描述

  4. 修改BSP SDK中的linux-linaro-stable目录下代码,新增对应的dts,并修改makefile。以增加SA5 16G DDR板类型为例, 在bsp-sdk/linux-linaro-stable/arch/arm64/boot/dts/bitmain目录下新增bm1684_asic_hds_drank.dts文件,内容为相应的配置

    #include "bm1684.dtsi"
    #include "bm1684_mm_16g.dtsi"
    #include "bm1684_asic_hds.dtsi"
    

    然后在bsp-sdk/linux-linaro-stable/arch/arm64/boot/dts/bitmain/Makefile目录新增bm1684_asic_hds_drank.dts的编译选项,如下图所示:
    请添加图片描述

  5. 修改BSP SDK中的bsp-sdk/ramdisk/build/bm1684_asic/workspace/multi.its.base,新增dts的fdt和config描述。如下图所示,在红色框上面,绿色部分为新增代码。
    请添加图片描述

SM5实现定制化rootfs

  1. x86环境安装qemu-user-static

sudo apt-get install qemu-user-static

  1. 准备arm linux根文件系统 arm linux根文件系统可以直接下载干净的版本(从我们gitee获取) 或者从运行的arm单板上进行备份获取。 arm单板上备份根文件系统命令:

    sudo tar -cvpzf rootfs.tgz --exclude=/proc --exclude=/mnt --exclude=/sys --exclude=/rootfs.tgz /
    

    解压根文件系统命令:

    tar -xvpfz rootfs.tgz -C ./rootfs
    
  2. 构建虚拟机

    创建rootfs目录,在rootfs下执行根文件系统解压命令,解压完成后,copy qemu执行命令到arm文件系统中

    sudo cp /usr/bin/qemu-arm-static usr/bin/
    sudo cp /usr/bin/qemu-aarch64-static usr/bin/
    

    在rootfs录下创建proc、sys、host目录 在rootfs所在目录下创建ch-mount.sh文件

    #!/bin/bash
    
    function mnt() {
        echo "MOUNTING"
        sudo mount -t proc /proc ${2}proc
        sudo mount -t sysfs /sys ${2}sys
        sudo mount -o bind /dev ${2}dev
        sudo mount -o bind /run ${2}run
        sudo mount --bind / ${2}host
        #sudo mount -vt tmpfs shm ${2}dev/shm
        #sudo mount -t /dev/shm ${2}dev/shm
        sudo chroot ${2}
    }
    
    function umnt() {
        echo "UNMOUNTING"
        sudo umount ${2}proc
        sudo umount ${2}sys
        #sudo umount ${2}dev/shm
        sudo umount ${2}dev
        sudo umount ${2}run
        sudo umount ${2}host
    }
    
    
    if [ "$1" == "-m" ] && [ -n "$2" ] ;
    then
        mnt $1 $2
    elif [ "$1" == "-u" ] && [ -n "$2" ];
    then
        umnt $1 $2
    else
        echo ""
        echo "Either 1'st, 2'nd or both parameters were missing"
        echo ""
        echo "1'st parameter can be one of these: -m(mount) OR -u(umount)"
        echo "2'nd parameter is the full path of rootfs directory(with trailing '/')"
        echo ""
        echo "For example: ch-mount -m /media/sdcard/"
        echo ""
        echo 1st parameter : ${1}
        echo 2nd parameter : ${2}
    

    执行ch-mount.sh,创建虚拟机:

    sudo ./ch-mount.sh -m rootfs/
    

    虚拟机准备完毕,可以在虚拟的arm环境上进行相应的操作:创建用户、编译arm版本、安装软件等。

  3. 卸载虚拟机 在虚拟机环境中执行exit退出。然后执行命令卸载挂载的相关文件:

    sudo ./ch-mount.sh -u rootfs/
    

SM5使用BM1684芯片内的eFuse和SPACC进行加解密

BM1684芯片内的eFuse的基础使用请参考 算丰SM5系列AI计算模组的SOC模式软件开发指南_V1.5.pdf (sophon.cn) 的3.6节。

用户可以在secure key和其副本区域分别烧写同一个密钥,然后在地址1中的bit[0]和bit[1]烧写1以实现secure key功能,这样之后软件将不再能从secure key区域读出密钥原文,这个密钥只能被SPACC硬件模块用作AES、DES加解密之用。以上操作都是不可逆的,即secure key无法被改写,这个功能也无法被关闭。

因为早期硬件上的一个小问题,欲使用此功能的话,请先做如下检查:

#在板子上执行
sudo busybox devmem 0x50010004
#返回值是个16进制字串,如0x00018A05
#只看最后一位数字,如果是1,则可以正常使用此功能,
#如果是5,需要对板子做一下rework,请联系技术支持返厂修改

在烧写时,如果先烧写了secure key enable bit,则secure key区域马上变成不能被读出(总是读出为0,但仍可以写入)。如果先烧写了secure key,此时它仍可以被读出,直到烧写secure key enable bit后才变成不能被读出。Secure Key在eFuse中的字节序如下,以128bit的AES key举例。现有一128bit的AES key如下:

0x00 0x01 0x02 0x03 0x10 0x11 0x12 0x13 0x20 0x21 0x22 0x23 0x30 0x31 0x32 0x33

在eFuse中的存储顺序为:

地址内容(HEX)
20x03020100
30x13121110
40x23222120
5x33323230

如果想要使用底层接口编程读写eFuse(这个并不是必须的,也可以直接使用SPACC提供的接口,详见后面SPACC的部分),打开/dev/bm_efuse节点,通过如下两个ioctl对eFuse原始数据进行读写。ioctl参数为一个结构体,其中addr即为0到127的eFuse地址,val用于存放读取和写入的32bit值:

#define EFUSE_IOCTL_READ    _IOWR('y', 0x20, struct efuse_ioctl_data)
#define EFUSE_IOCTL_WRITE   _IOWR('y', 0x21, struct efuse_ioctl_data)

struct efuse_ioctl_data {
    uint32_t addr;
    uint32_t val;
};

读取:

int file = open("/dev/bm_efuse", O_RDWR);
struct efuse_ioctl_data data;
int ret = 0;

data.addr = address;
ret = ioctl(file, EFUSE_IOCTL_READ, &data);
if (ret < 0) {
    ERR("EFUSE_IOCTL_READ fail,errno=0x%x", errno);
    close(file);
    return ret;
}
close(file);
return data.val;

写入:

int file = open("/dev/bm_efuse", O_RDWR);
struct efuse_ioctl_data data;
int ret = 0;

data.addr = address;
data.val = val;
ret = ioctl(file, EFUSE_IOCTL_WRITE, &data);
if (ret < 0)
    ERR("EFUSE_IOCTL_WRITE fail ,errno=0x%x\n", errno);

close(file);

SPACC是BM1684芯片内置的一个硬件加速模块,支持AES、DES、SM4和SHA1、SHA256、Base64。
这里介绍SPACC搭配eFuse使用的一个特殊功能:客户可以在eFuse的secure key区域烧录一把密钥,这把密钥写入后是不能再被软件读出的,只能被SPACC用作加解密运算。结合客户自定义ID使用,可以用作产品授权等功能。以下是eFuse和SPACC结合使用时的API:

  1. 查看secure key使能状态和客户自定义ID:
    #查看secure key使能状态
    sudo cat /sys/devices/platform/50028000.efuse/secure-key
    
    #查看客户自定义ID
    sudo cat /sys/devices/platform/50028000.efuse/uid
    
  2. 烧写secure key和客户自定义ID 为方便使用,烧写secure key和客户自定义ID,可以不适用前述的RAW API,而是使用下面的nvmem接口,重启后生效:
    #切换到root用户
    sudo -i
    #解锁eFuse烧录限制
    echo 0 > /sys/devices/platform/50028000.efuse/nvmem-lock
    #烧录key
    echo 000102030405060708090a0b0c0d0e0f111213141516171819101a1b1c1d1e1f > /sys/devices/platform/50028000.efuse/secure-key
    #烧录客户自定义ID
    echo 0123456789abcdef0123456789abcdef > /sys/devices/platform/50028000.efuse/uid
    # 如您的key为128bit长度,可仅烧写128bit,如:
    echo 000102030405060708090a0b0c0d0e0f > /sys/devices/platform/50028000.efuse/secure-key
    # 客户自定义ID可烧录少于128bit的任意长度,如烧录4byte ID:
    echo 01234567 > /sys/devices/platform/50028000.efuse/uid
    
  3. 如何使用SPACC SPACC已经集成到Linux AF_ALG Crypto Framework中,关于如何使用Linux Crypto Framework,可参考 Linux Crypto Framework 。代码可参考 libkcapi ,也可直接使用libkcapi。 如需使用secure key加密,请在调用socket函数时,将salg_name写成带有secure key后缀的cipher名称,如:
    struct sockaddr_alg sa = {
            .salg_family = AF_ALG,
            .salg_type = "skcipher",
            .salg_name = "cbc(aes)"
    };
    
    更改为:
    struct sockaddr_alg sa = {
            .salg_family = AF_ALG,
            .salg_type = "skcipher",
            .salg_name = "cbc(aes-secure-key)"
    };
    
    如您使用libkcapi,请在调用初始化函数时,将ciphername参数设置为带有secure-key后缀的cipher类型,如:
    kcapi_cipher_init(&handle,"cbc(aes)", 0);
    
    更改为:
    kcapi_cipher_init(&handle,"cbc(aes-secure-key)", 0);
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值