美文网首页
自定义 Opcode

自定义 Opcode

作者: 走在成长的道路上 | 来源:发表于2018-09-25 10:57 被阅读0次

    在 FPGA 中通过扩展指令集来加速计算过程,即将某些函数以CPU指令的方式来执行。然后通过将他们用 C 语言进行封装,从而成为标准C库中的一部分。

    这里通过简单的添加 opcode 的方式来说明自定义 opcode 中对 gcc 的扩展过程。

    环境准备

    • 设置环境变量
    #!/bin/bash
    
    # [1]
    # prepare-env: export required environment variables, create folders.
    # 在环境变量中设置 RISCV 主工程目录
    export RISCV_HOME=~/riscv-home
    export RISCV="${RISCV_HOME}/riscv"
    export PATH="${PATH}:${RISCV}/bin"
    export RISCV_PK="${RISCV}/riscv32-unknown-elf/bin/pk"
    
    # 创建这些目录
    mkdir -p "${RISCV_HOME}" "${RISCV}"
    
    echo '[OK] done'
    
    • 构建 RISCV 工具链
    #!/bin/bash
    
    # [2]
    # download-repos: clone all repositories to $RISCV_HOME.
    
    if [ -z "${RISCV_HOME}" ]; then
        echo '$RISCV_HOME is undefined; run `source prepare-env`'
        exit 1
    fi
    
    dst="${RISCV_HOME}"
    echo "destination folder is ${dst}"
    
    # 基础同步代码函数
    function riscv-clone {
        repo="${1}"
        revision="${2}"
        shift 2
        git clone -b master $@ "https://github.com/riscv/${repo}" "${dst}/${repo}" &&
            cd "${dst}/${repo}" &&
            git checkout "${revision}" &&
            echo "[OK] ${repo} cloning finished"
    }
    
    # 分别同步 riscv-fesvr, riscv-pk, riscv-isa-sim, riscv-opcodes, riscv-tests, riscv-gnu-toolchain 等包
    riscv-clone 'riscv-fesvr' '01932a715edd22ee451d86cde38c7c07dc9bfa7e' &&
        riscv-clone 'riscv-pk' '66701f82f88d08d3700d8b0bc5d5306abfd0044f' &&
        riscv-clone 'riscv-isa-sim' '3a4e89322a8c8dac94185812a238f13789ab392f' &&
        riscv-clone 'riscv-opcodes' 'e0abc2255a71afb0236032ae3d92bea26c15716d' && 
        riscv-clone 'riscv-tests' '9e313f30205b8172290831c3af18b0779e9b15f2' &&
        riscv-clone 'riscv-gnu-toolchain' 'f5fae1c27b2365da773816ddcd92f533867f28ec' --recursive && 
        echo '[OK] done'
    
    • 初始化编译
    #!/bin/bash
    
    # [3]
    # build-repos: build downloaded repositories.
    
    # 安装依赖包
    # Get packages that are required to build the GNU toolchain.
    # The list provided by https://github.com/riscv/riscv-gnu-toolchain README.
    sudo apt-get install \
        autoconf \
        automake \
        autotools-dev \
        curl \
        libmpc-dev \
        libmpfr-dev \
        libgmp-dev \
        gawk \
        build-essential \
        bison \
        flex \
        texinfo \
        gperf \
        libtool \
        patchutils \
        bc \
        zlib1g-dev \
        device-tree-compiler || exit 1
    
    # 编译工具链
    function mk-toolchain {
        cd "${RISCV_HOME}/riscv-gnu-toolchain" &&
            ./configure --prefix="${RISCV}" --with-arch=rv32i &&
            echo 'this can take more than a hour...' &&
            make &&
            echo '[OK] mk-toolchain: done'
    }
    
    # 编译 front-end server
    function mk-fesvr {
        cd "${RISCV_HOME}/riscv-fesvr" &&
            mkdir -p build &&
            cd build &&
            ../configure --prefix="${RISCV}" &&
            make install &&
            echo '[OK] mk-fesvr: done'
    }
    
    # 编译代理 kernel, 及 bootloader
    function mk-pk {
        # See issue https://github.com/riscv/riscv-pk/issues/56.
        # Remove '-m32' occurences from Makefile 
        # to fix "error: unrecognized command line option '-m32'".
        cd "${RISCV_HOME}/riscv-pk" &&
            mkdir -p build &&
            cd build &&
            ../configure --prefix="${RISCV}" --host=riscv32-unknown-elf --enable-32bit &&
            sed -i 's/\-m32//g' Makefile &&
            make &&
            make install &&
            echo '[OK] mk-pk: done'
    }
    
    # 编译 spike 模拟器
    function mk-spike {
        cd "${RISCV_HOME}/riscv-isa-sim" &&
            mkdir -p build &&
            cd build &&
            ../configure --prefix="${RISCV}" --with-fesvr="${RISCV}" &&
            make &&
            sudo make install &&
            echo '[OK] mk-spike: done'
    }
    
    # The order matters.
    mk-toolchain &&
        mk-fesvr &&
        mk-pk &&
        mk-spike &&
        echo '[OK] done'
    
    • 测试
    #!/bin/bash
    
    # [4]
    # check-install: try to find out it installation was successful.
    
    cc='riscv32-unknown-elf-gcc'
    cxx='riscv32-unknown-elf-g++'
    
    # 测试 command 命令
    function check-cmd {
        name="${1}"
        command -v "${name}" >/dev/null 2>&1 ||
            (echo "${name}: command not found" && exit 1)
        echo "[OK] located ${name}"
    }
    
    # 通过spike 模拟器测试执行环境
    function check-spike {
        mkdir -p build &&
            ${cc} data/hello.c -O1 -march=rv32im -o build/cc_hello &&
            echo '[OK] C compiler works' &&
            ${cxx} data/hello.cpp -O1 -march=rv32im -o build/cxx_hello &&
            echo '[OK] C++ compiler works' &&
            spike --isa=RV32IM "${RISCV_PK}" build/cc_hello &&
            spike --isa=RV32IM "${RISCV_PK}" build/cxx_hello &&
            echo '[OK] spike works'
    }
    
    check-cmd "${cc}" &&
        check-cmd "${cxx}" &&
        check-cmd 'spike' &&
        check-spike &&
        echo '[OK] done'
    

    添加 Opcode

    定义新指令

    mac(a, b, c)  =>  c := c + (a * b)
    
    • 创建 riscv/insns/mac.h 文件
      用来描述 mac 指令的功能
    // 'M' extension means we require integer mul/div standard extension.
    require_extension('M');
    // RD = RD + RS1 * RS2
    reg_t tmp = sext_xlen(RS1 * RS2);
    WRITE_RD(sext_xlen(READ_REG(insn.rd()) + tmp));
    
    • 添加Opcode
    cd "${RISCV_HOME}/riscv-opcodes"
    echo -e "mac rd rs1 rs2 31..25=1 14..12=0 6..2=0x1A 1..0=3\n" >> opcodes
    make install
    
    # 将 mac 指令追加到 riscv_insn_list 中去
    sed -i 's/riscv_insn_list = \\/riscv_insn_list = mac\\/g' \
        "${RISCV_HOME}/riscv-isa-sim/riscv/riscv.mk.in"
    
    • 重新编译spike模拟器
    cd "${RISCV}/riscv-isa-sim/build"
    sudo make install
    
    • 测试程序编写
    #include <stdio.h>
    // Needed to verify results.
    int mac_c(int a, int b, int c) {
        a += b * c; // Semantically, it is "mac"
        return a;
    }
    
    // Should not be inlined, because we expect arguments
    // in particular registers.
    __attribute__((noinline))
    int mac_asm(int a, int b, int c) {
        // 0x02C5856B 为 mac 的 16进制 执行编码
        asm __volatile__ (".word 0x02C5856B\n");
        return a;
    }
    int main(int argc, char** argv) {
        int a = 2, b = 3, c = 4;
        printf("%d =?= %d\n", mac_c(a, b, c), mac_asm(a, b, c));
    }
    
    • 编译执行
    riscv32-unknown-elf-gcc test_mac.c -O1 -march=rv32im -o test_mac
    spike --isa=RV32IM "${RISCV_PK}" test_mac
    

    输出: 14 =?= 14

    执行格式说明

    • mac & mul
    # file "riscv-opcodes/opcodes"
    #                                differs
    #                                |
    #                                v
    mac rd rs1 rs2 31..25=1 14..12=0 6..2=0x1A 1..0=3
    mul rd rs1 rs2 31..25=1 14..12=0 6..2=0x0C 1..0=3
    #   ^  ^   ^   ^        ^        ^         ^
    #   |  |   |   |        |        |         |
    #   |  |   |   |        |        |         |
    #   |  |   |   |        |        |         also opcode 3 bits
    #   |  |   |   |        |        opcode 5 bits
    #   |  |   |   |        funct3 3 bits
    #   |  |   |   funct7 7 bits
    #   |  |   rs2 (src2) 5 bits
    #   |  rs1 (src1) 5 bits
    #   dest 5 bits
    
    • 实际执行
    # Encoding used for "mac a0, a1, a2"
    0x02C5856B [base 16]
    == 
    10110001011000010101101011 [base 2]
    == 对齐后
    00000010110001011000010101101011 [base 2]
    # Group by related bit chunks:
    0000001 01100 01011 000 01010 1101011
    ^       ^     ^     ^   ^     ^
    |       |     |     |   |     |
    |       |     |     |   |     opcode (6..2=0x0C 1..0=3)
    |       |     |     |   dest (10 : a0)
    |       |     |     funct3 (14..12=0)
    |       |     src1 (11 : a1)
    |       src2 (12 : a2)
    funct7 (31..25=1)
    

    参考

    RISC-V: custom instruction and its simulation
    gnu-riscv32_ext
    Adding the custom instruction to spike ISA simulator

    相关文章

      网友评论

          本文标题:自定义 Opcode

          本文链接:https://www.haomeiwen.com/subject/vypkoftx.html