精华内容
下载资源
问答
  • Spike

    2019-10-24 14:01:10
    Spike
  • 头脑风暴/ SPIKE Prime 该存储库是有关Lego Mindstorms 51515和Lego SPIKE Prime集线器的有用信息的集合。 乐高集线器硬件 部分 描述 中央处理器 STM32F413(架构:ARM Cortex M4,ROM:1M,RAM:320k,时钟:...
  • SPIKE Prime Hub 2集线器通信 一个SPIKE Prime Hub如何与另一个SPIKE Prime Hub通信 一个SPIKE Hub可以通过多种方式与另一个进行通信。 此列表将定期更新。 非常感谢FB社区的反馈和支持,尤其是Robotmak3rs :)不同...
  • 用于测试LEGO SPIKE PRIME Hub的实用程序 从修改的代码 安装Python 3.8.2。 在安装<=非常重要时启用“添加到路径”。 要安装,只需在下载整个存储库后运行install.bat等。 与集线器通信的默认设备地址为/dev/...
  • spike-可用版.zip

    2020-03-01 15:09:12
    从技术上讲,SPIKE实际上是一个模糊器创建工具包,它提供了API,允许用户使用C语言基于网络的协议来创建自己的fuzzer。 SPIKE定义了一些它可供C编码器使用的原语,它允许它构造称为“SPIKES”的模糊消息,这些消息...
  • 20200617-Spike 代码框架及具体实现分析-wangmeng.pdf
  • SPIKE-win-setup-0.31.3.exe

    2020-06-04 19:45:52
    乐高SPIKE套件编程软件。用于SPIKE连接。编程界面类似Scratch 。
  • 乐高最新编程套件 spike-prime 的安装文件,可以通过模块化编程界面学习python。从官网下载比较慢,放在这里供有需要的人下载,
  • Spike是一种交互式的股票筛选器,可以通过短信发送。 通过发送电话短信,您将成为贸易社区的一部分,该贸易社区通过Spike的服务与他们的扫描仪和协议挂钩。 通过这些交互式服务,您将可以接收小组当前正在监视的...
  • Spike DatoCMS插件 快速便捷的DatoCMS内容界面 注意:该项目处于早期开发中,版本控制有些不同。 以获取更多详细信息。 安装 npm install spike-datocms -S 用法 有时,了解如何使用某些东西的最佳方法就是一个...
  • Spike RISC-V ISA模拟器 关于 Spike,RISC-V ISA模拟器,实现了一个或多个RISC-V harts的功能模型。 它以庆祝美国跨大陆铁路竣工的金色尖峰而得名。 Spike支持以下RISC-V ISA功能: RV32I和RV64I基本ISA v2.1 ...
  • Spike.Build是一个命令行界面工具,可以自动为跨平台客户端SDK生成代码。 生成状态: 当前二进制文件: 网站 当前支持的平台: 具有异步/等待功能的.NET / C#5支持非阻塞网络 适用于Android和iOS的.NET ...
  • spike-源码

    2021-03-22 02:18:49
    spike
  • 使用OpenMV的SPIKE配件 颜色传感器(两种不同的模式)。 人脸检测器。 ----------颜色传感器-------------------------------------- -------------------------------------------------- --------------- ...
  • Spike-开源

    2021-04-25 17:28:42
    一种动态的,面向对象的编程语言,具有类似C的语法
  • SPIKE-Lidar-源码

    2021-03-20 00:11:42
    EV3-Lidar本科生研究项目的进展。这些脚本可以协同工作,以交流,调动和可视化LEGO SPIKE Prime机器人平台以及Slamtec RPlidar传感器和充当控制计算机的Raspberry Pi 4。
  • spike_ripple_detector.m 。 此功能接受两个输入,这些输入指定数据(即,EEG的单个通道)和以秒为单位的时间轴。 还可以包括第三个可选输入以指定包络阈值(默认值为0.85)。 该函数返回两个输出。 第一个输出是一...
  • SPIKE 数据集包含带有手动注释的地面实况标签的图像,用于训练和测试卷积神经网络以检测田间小麦的穗状花序。 此处提供的模型文件将与免费提供的 Microsoft Cognitive Toolkit 的 Faster R-CNN 实现一起使用,可在...
  • spike-sorting/matlab文件夹中运行以下命令,以在命令行中将c ++源代码编译为mex函数: ./installmexfiles 提炼 包含C ++应用程序的源代码,用于从原始数据HDF5文件中提取尖峰和噪声片段。 提取内容需要armadillo...
  • spring-mvc-spike-源码

    2021-06-21 00:22:12
    #spring-mvc-spike
  • spike安装包

    2013-10-27 21:06:51
    c++ fuzzing 框架 适用于格式化网络协议fuzzing
  • SPIKE开源模糊测试框架,C语言 unix平台
  • 尖峰时间依赖性可塑性 该程序在突触后放电之前出现突触前尖峰时增强相关突触权重,并在突触后放电后出现突触前尖峰时减弱相关突触权重。 如果突触前和突触后尖峰时间之间的差异幅度很小,则突触权重修改量最大,并且...
  • PLCT_spike扩展方式简介_
  • 分子微服务峰值 该项目是使用微服务框架在Node.js中构建的许多微服务。 这样做的灵感是学习更多关于Moleculer框架的信息,并基于Confluent 博客概念。 此概念验证未使用Kafka Streams或KSQL,而是使用事件源(以...
  • 神经元集群编码与解码是...利用微电极阵列记录并分离脑电信号,然后借助现代密码学的思想,将之与多维随机数列进行比较,得到有效信息量从而在Spike Train中进行神经解码。最后用一个实例来分析了发现模式的具体步骤。
  • 安装和包装 mvn3全新安装软件包 执行 java -jar /您的/目录/测试/目标/测试-1.0-SNAPSHOT.jar 您好教程!
  • matlab代码影响
  • RISCV ISS Spike 介绍

    千次阅读 2020-11-30 16:05:17
    RISCV ISS SPIKE 介绍SpikeSpike的使用Spike项目的目录结构其他一些文件夹的用处riscv文件夹如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何...

    Spike

    Spike是RISC-V的一种仿真器,它可以仿真一个或多个hart

    目前它支持的RISC-V指令集特性有:

    RV32I and RV64I base ISAs, v2.1
    Zifencei extension, v2.0
    Zicsr extension, v2.0
    M extension, v2.0
    A extension, v2.1
    F extension, v2.2
    D extension, v2.2
    Q extension, v2.2
    C extension, v2.0
    V extension, v0.9-draft-20200403, w/ Zvlsseg, w/o Zvamo/Zvediv, (requires a 64-bit host)
    Conformance to both RVWMO and RVTSO (Spike is sequentially consistent)
    Machine, Supervisor, and User modes, v1.11
    Debug v0.14
    

    Spike是官方的对RISC-V的仿真实现,可以说是指令的事实标准(支持手册上的所有指令)

    Spike的使用

    • 作为裸机使用
    • 结合pk(代理内核),可以直接运行C程序

    Spike项目的目录结构

    -------------------------------------------------------------------------------
    Language                     files          blank        comment           code
    -------------------------------------------------------------------------------
    C/C++ Header                   637           1216           3525          13825
    C                              219           3102           6563           9496
    Bourne Shell                     7            973           1352           7682
    C++                             41           1182            281           7615
    m4                               8            101              0            556
    Python                           2             21              9            112
    Assembly                         2             11             25             48
    make                             1              8              2             14
    -------------------------------------------------------------------------------
    SUM:                           917           6614          11757          39348
    -------------------------------------------------------------------------------
    

    其中,Spike的主体为C++代码编写,主要文件夹为riscv和spike_main

    debug_rom/
    dummy_rocc/
    fesvr/
    riscv/
    scripts/
    softfloat/
    spike_main/
    tests/

    其中riscv文件夹为仿真部分,用于模拟RISC-V指令,spike_main主要用于与用户交互,例如启动之前的参数选项,反汇编等。

    其他一些文件夹的用处

    • fesvr文件夹

      该文件夹原本是一个独立于Spike的项目,为RISC-V Frontend Server,目前已为Archived状态

      这里面有个比较重要的东西,为htif(hart interface)的实现,用于与主机交互,例如,printf

      未证实:这个机制似乎要被废弃,tohost与fromhost原本是属于CSR寄存器的,但早被删除

    • softfloat文件夹

      该文件夹就是该项目存在大量C语言代码的原因,用于实现软浮点

      这个文件夹本身不是Spike项目中实现的,而是取自Berkeley SoftFloat

    riscv文件夹

    processor.cc processor.h

    作为一个仿真器,很容易想到,我们应该从这两个文件开始分析,因为spike还是基于硬件进行模拟
    
    该文件中显然class processor_t这个类是核心,这个类比较长,大概有250行
    
    先分析构造函数
    
    processor_t(const char* isa, const char* priv, const char* varch,
                  simif_t* sim, uint32_t id, bool halt_on_reset,
                  FILE *log_file);
    
    isa为指定指令集,默认是 RV64IMAFDC
    
    priv为支持的特权,默认是MSU
    
    varch为vector寄存器的设置
    
    sim的类型是simif_t*,该类型主要是一些仿真接口,定义在simif.h
    
    id为处理器id号,是否就是hart的id?
    
    halt_on_reset 该选项用于指示启动时是否运行,应该就是-d选项的实现
    
    log_file 该参数疑似新添加的,应该用于存放一些log信息
    

    接下来看看它的一些私有成员

    private:
      simif_t* sim;
      mmu_t* mmu; // main memory is always accessed via the mmu
      extension_t* ext;
      disassembler_t* disassembler;
      state_t state;
      uint32_t id;
      unsigned max_xlen;
      unsigned xlen;
      reg_t max_isa;
      std::string isa_string;
      bool histogram_enabled;
      bool log_commits_enabled;
      FILE *log_file;
      bool halt_on_reset;
      std::vector<bool> extension_table;
    ...
    

    可以看到,用于仿真所需的一些参数,其中,比较重要的是state变量,找到它的类型定义

    struct state_t
    {
      void reset(reg_t max_isa);
    
      static const int num_triggers = 4;
    
      reg_t pc;
      regfile_t<reg_t, NXPR, true> XPR;
      regfile_t<freg_t, NFPR, false> FPR;
    
      // control and status registers
      reg_t prv;    // TODO: Can this be an enum instead?
      reg_t misa;
      reg_t mstatus;
      reg_t mepc;
      reg_t mtval;
      reg_t mscratch;
      reg_t mtvec;
      reg_t mcause;
      reg_t minstret;
      reg_t mie;
      reg_t mip;
      reg_t medeleg;
      reg_t mideleg;
      uint32_t mcounteren;
      uint32_t scounteren;
      reg_t sepc;
      reg_t stval;
      reg_t sscratch;
      reg_t stvec;
      reg_t satp;
      reg_t scause;
    
      reg_t dpc;
      reg_t dscratch0, dscratch1;
      dcsr_t dcsr;
      reg_t tselect;
      mcontrol_t mcontrol[num_triggers];
      reg_t tdata2[num_triggers];
      bool debug_mode;
    
      static const int n_pmp = 16;
      uint8_t pmpcfg[n_pmp];
      reg_t pmpaddr[n_pmp];
    
      uint32_t fflags;
      uint32_t frm;
      bool serialized; // whether timer CSRs are in a well-defined state
    
      // When true, execute a single instruction and then enter debug mode.  This
      // can only be set by executing dret.
      enum {
          STEP_NONE,
          STEP_STEPPING,
          STEP_STEPPED
      } single_step;
    
    #ifdef RISCV_ENABLE_COMMITLOG
      commit_log_reg_t log_reg_write;
      commit_log_mem_t log_mem_read;
      commit_log_mem_t log_mem_write;
      reg_t last_inst_priv;
      int last_inst_xlen;
      int last_inst_flen;
    #endif
    };
    

    可以看出,该结构体定义的正是RISC-V指令集中的一些常用寄存器,包括通用整数寄存器,浮点寄存器,以及pc和一些CSR寄存器

    同时,注意到processor_t类是继承自abstract_device_t类,该类非常短。

    class abstract_device_t {
     public:
      virtual bool load(reg_t addr, size_t len, uint8_t* bytes) = 0;
      virtual bool store(reg_t addr, size_t len, const uint8_t* bytes) = 0;
      virtual ~abstract_device_t() {}
    };
    

    也就是load和store两个虚函数,定义在devices.h中,可以看到该文件中定义了多种设备类,例如bus,mem,rom,clint等,其中,clint又是一个有必要提及的东西,后面再讲

    insn文件夹、指令的处理过程

    该文件夹用于存放各种指令的实现。
    打开后可以发现只有一行或者少数几行,显然,实际指令执行的代码还需要生成。
    观察各文件名可以发现,insn_template.cc应该是实际模板。

    reg_t rv32_NAME(processor_t* p, insn_t insn, reg_t pc)
    {
      int xlen = 32;
      reg_t npc = sext_xlen(pc + insn_length(OPCODE));
      #include "insns/NAME.h"
      trace_opcode(p, OPCODE, insn);
      return npc;
    }
    
    reg_t rv64_NAME(processor_t* p, insn_t insn, reg_t pc)
    {
      int xlen = 64;
      reg_t npc = sext_xlen(pc + insn_length(OPCODE));
      #include "insns/NAME.h"
      trace_opcode(p, OPCODE, insn);
      return npc;
    }
    

    可以发现,这两个函数中各有#include,显然就是将insns文件夹中的文件内容导入到这两个函数模板中。

    此时发现另一个问题,指令的模拟有了,Spike又是如何运行指令的

    自然而然想到execute.cc文件

    static reg_t execute_insn(processor_t* p, reg_t pc, insn_fetch_t fetch)
    {
      commit_log_reset(p);
      commit_log_stash_privilege(p);
      reg_t npc;
    
      try {
        npc = fetch.func(p, fetch.insn, pc);
        if (npc != PC_SERIALIZE_BEFORE) {
    
    #ifdef RISCV_ENABLE_COMMITLOG
          if (p->get_log_commits_enabled()) {
            commit_log_print_insn(p, pc, fetch.insn);
          }
    #endif
    
         }
    #ifdef RISCV_ENABLE_COMMITLOG
      } catch(mem_trap_t& t) {
          //handle segfault in midlle of vector load/store
          if (p->get_log_commits_enabled()) {
            for (auto item : p->get_state()->log_reg_write) {
              if ((item.first & 3) == 3) {
                commit_log_print_insn(p, pc, fetch.insn);
                break;
              }
            }
          }
          throw;
    #endif
      } catch(...) {
        throw;
      }
      p->update_histogram(pc);
    
      return npc;
    }
    

    显然,我们需要关心的语句只有一条,即npc = fetch.func(p, fetch.insn, pc);

    这就把问题重新定位到insn_fetch_t类的实现

    找到发现在mmu.h文件夹中

    struct insn_fetch_t
    {
      insn_func_t func;
      insn_t insn;
    };
    

    此时定位到需要知道insn_t的描述,通过搜索,找到定义实现在decode.h中。
    找到后我们发现了一些RISC-V中熟悉的东西

    class insn_t
    {
    public:
      insn_t() = default;
      insn_t(insn_bits_t bits) : b(bits) {}
      insn_bits_t bits() { return b; }
      int length() { return insn_length(b); }
      int64_t i_imm() { return int64_t(b) >> 20; }
      int64_t shamt() { return x(20, 6); }
      int64_t s_imm() { return x(7, 5) + (xs(25, 7) << 5); }
      int64_t sb_imm() { return (x(8, 4) << 1) + (x(25,6) << 5) + (x(7,1) << 11) + (imm_sign() << 12); }
      int64_t u_imm() { return int64_t(b) >> 12 << 12; }
      int64_t uj_imm() { return (x(21, 10) << 1) + (x(20, 1) << 11) + (x(12, 8) << 12) + (imm_sign() << 20); }
      uint64_t rd() { return x(7, 5); }
      uint64_t rs1() { return x(15, 5); }
      uint64_t rs2() { return x(20, 5); }
      uint64_t rs3() { return x(27, 5); }
      uint64_t rm() { return x(12, 3); }
      uint64_t csr() { return x(20, 12); }
    
      int64_t rvc_imm() { return x(2, 5) + (xs(12, 1) << 5); }
      int64_t rvc_zimm() { return x(2, 5) + (x(12, 1) << 5); }
      int64_t rvc_addi4spn_imm() { return (x(6, 1) << 2) + (x(5, 1) << 3) + (x(11, 2) << 4) + (x(7, 4) << 6); }
      int64_t rvc_addi16sp_imm() { return (x(6, 1) << 4) + (x(2, 1) << 5) + (x(5, 1) << 6) + (x(3, 2) << 7) + (xs(12, 1) << 9); }
      int64_t rvc_lwsp_imm() { return (x(4, 3) << 2) + (x(12, 1) << 5) + (x(2, 2) << 6); }
      int64_t rvc_ldsp_imm() { return (x(5, 2) << 3) + (x(12, 1) << 5) + (x(2, 3) << 6); }
      int64_t rvc_swsp_imm() { return (x(9, 4) << 2) + (x(7, 2) << 6); }
      int64_t rvc_sdsp_imm() { return (x(10, 3) << 3) + (x(7, 3) << 6); }
      int64_t rvc_lw_imm() { return (x(6, 1) << 2) + (x(10, 3) << 3) + (x(5, 1) << 6); }
      int64_t rvc_ld_imm() { return (x(10, 3) << 3) + (x(5, 2) << 6); }
      int64_t rvc_j_imm() { return (x(3, 3) << 1) + (x(11, 1) << 4) + (x(2, 1) << 5) + (x(7, 1) << 6) + (x(6, 1) << 7) + (x(9, 2) << 8) + (x(8, 1) << 10) + (xs(12, 1) << 11); }
      int64_t rvc_b_imm() { return (x(3, 2) << 1) + (x(10, 2) << 3) + (x(2, 1) << 5) + (x(5, 2) << 6) + (xs(12, 1) << 8); }
      int64_t rvc_simm3() { return x(10, 3); }
      uint64_t rvc_rd() { return rd(); }
      uint64_t rvc_rs1() { return rd(); }
      uint64_t rvc_rs2() { return x(2, 5); }
      uint64_t rvc_rs1s() { return 8 + x(7, 3); }
      uint64_t rvc_rs2s() { return 8 + x(2, 3); }
    
      uint64_t v_vm() { return x(25, 1); }
      uint64_t v_nf() { return x(29, 3); }
      uint64_t v_simm5() { return xs(15, 5); }
      uint64_t v_zimm5() { return x(15, 5); }
      uint64_t v_zimm11() { return x(20, 11); }
      uint64_t v_lmul() { return 1 << x(20, 2); }
      uint64_t v_sew() { return 1 << (x(22, 3) + 3); }
    
    private:
      insn_bits_t b;
      uint64_t x(int lo, int len) { return (b >> lo) & ((insn_bits_t(1) << len)-1); }
      uint64_t xs(int lo, int len) { return int64_t(b) << (64-lo-len) >> (64-len); }
      uint64_t imm_sign() { return xs(63, 1); }
    };
    

    显然该类中定义了大量的解码操作,因为与verilog设计不同,所以取相关位时用了大量的位运算操作

    可以看到,大部分函数中都用到了x操作,这个操作就是从第lo位开始,去长度为len的位
    这时,需要知道另一个insn_func_t的定义,发现定义在process.h中

    typedef reg_t (*insn_func_t)(processor_t*, insn_t, reg_t);
    

    发现是一个函数指针,看来需要从其他地方入手

    这时显然需要知道传入的参数到底是什么,那么就去找该函数在哪被调用

    发现在void processor_t::step(size_t n)这个函数中被调用,但这个函数异常地长,不好分析。

    这也是循环执行指令的函数,找到被调用的地方

    insn_fetch_t fetch = mmu->load_insn(pc);
    if (debug && !state.serialized)
        disasm(fetch.insn);
    pc = execute_insn(this, pc, fetch);
    advance_pc();
    

    显然,这时问题又回到了mmu,找到

    inline insn_fetch_t load_insn(reg_t addr)
    {
        icache_entry_t entry;
        return refill_icache(addr, &entry)->data;
    }
    

    问题似乎越来越复杂,再次定位,找到

      inline icache_entry_t* refill_icache(reg_t addr, icache_entry_t* entry)
      {
        auto tlb_entry = translate_insn_addr(addr);
        insn_bits_t insn = from_le(*(uint16_t*)(tlb_entry.host_offset + addr));
        int length = insn_length(insn);
    
        if (likely(length == 4)) {
          insn |= (insn_bits_t)from_le(*(const int16_t*)translate_insn_addr_to_host(addr + 2)) << 16;
        } else if (length == 2) {
          insn = (int16_t)insn;
        } else if (length == 6) {
          insn |= (insn_bits_t)from_le(*(const int16_t*)translate_insn_addr_to_host(addr + 4)) << 32;
          insn |= (insn_bits_t)from_le(*(const uint16_t*)translate_insn_addr_to_host(addr + 2)) << 16;
        } else {
          static_assert(sizeof(insn_bits_t) == 8, "insn_bits_t must be uint64_t");
          insn |= (insn_bits_t)from_le(*(const int16_t*)translate_insn_addr_to_host(addr + 6)) << 48;
          insn |= (insn_bits_t)from_le(*(const uint16_t*)translate_insn_addr_to_host(addr + 4)) << 32;
          insn |= (insn_bits_t)from_le(*(const uint16_t*)translate_insn_addr_to_host(addr + 2)) << 16;
        }
    
        insn_fetch_t fetch = {proc->decode_insn(insn), insn};
        entry->tag = addr;
        entry->next = &icache[icache_index(addr + length)];
        entry->data = fetch;
    
        reg_t paddr = tlb_entry.target_offset + addr;;
        if (tracer.interested_in_range(paddr, paddr + 1, FETCH)) {
          entry->tag = -1;
          tracer.trace(paddr, length, FETCH);
        }
        return entry;
      }
    

    显然,我们需要关心的是insn_fetch_t fetch = {proc->decode_insn(insn), insn};
    发现问题又回到processor

    insn_func_t processor_t::decode_insn(insn_t insn)
    {
      // look up opcode in hash table
      size_t idx = insn.bits() % OPCODE_CACHE_SIZE;
      insn_desc_t desc = opcode_cache[idx];
    
      if (unlikely(insn.bits() != desc.match)) {
        // fall back to linear search
        insn_desc_t* p = &instructions[0];
        while ((insn.bits() & p->mask) != p->match)
          p++;
        desc = *p;
    
        if (p->mask != 0 && p > &instructions[0]) {
          if (p->match != (p-1)->match && p->match != (p+1)->match) {
            // move to front of opcode list to reduce miss penalty
            while (--p >= &instructions[0])
              *(p+1) = *p;
            instructions[0] = desc;
          }
        }
    
        opcode_cache[idx] = desc;
        opcode_cache[idx].match = insn.bits();
      }
    
      return xlen == 64 ? desc.rv64 : desc.rv32;
    }
    

    此时回忆一下我们最初的目的,是解析npc = fetch.func(p, fetch.insn, pc);的执行

    也就是说,我们需要知道fetch.func到底是什么,从该函数中可以看出就是返回值

    因为Spike默认是RV64,所以我们直接找desc.rv64

    在process.h中发现

    struct insn_desc_t
    {
      insn_bits_t match;
      insn_bits_t mask;
      insn_func_t rv32;
      insn_func_t rv64;
    };
    

    发现rv64就是前面提到的函数指针,此时不得不去找opcode_cache
    这点再往下可以专门再开一个话题,它与自定义指令也有关系,暂时先到这里

    内存

    虽然Spike没有任何资料说明,但是似乎实际的地址分布就是SiFive公司芯片的
    在这里插入图片描述

    通过debug模式可以看到,Spike启动时的第一条指令的地址就是0x00001000,另外CLINT的地址也与上表相符

    CLINT为core local interrupt controlor,即核局部中断控制器,主要作用为软中断与定时器中断

    clint_t本身定义在devices.h中。

    class clint_t : public abstract_device_t {
     public:
      clint_t(std::vector<processor_t*>&, uint64_t freq_hz, bool real_time);
      bool load(reg_t addr, size_t len, uint8_t* bytes);
      bool store(reg_t addr, size_t len, const uint8_t* bytes);
      size_t size() { return CLINT_SIZE; }
      void increment(reg_t inc);
     private:
      typedef uint64_t mtime_t;
      typedef uint64_t mtimecmp_t;
      typedef uint32_t msip_t;
      std::vector<processor_t*>& procs;
      uint64_t freq_hz;
      bool real_time;
      uint64_t real_time_ref_secs;
      uint64_t real_time_ref_usecs;
      mtime_t mtime;
      std::vector<mtimecmp_t> mtimecmp;
    };
    

    可以看到,主要多了的成员为mtime,mtimecmp

    RISC-V特权级手册中提到,这两个是用于定时器中断的,一开始为CSR寄存器,目前已被改为Memory Map

    clint中除了load和store,还有另一个函数

    void clint_t::increment(reg_t inc)
    {
      if (real_time) {
       struct timeval now;
       uint64_t diff_usecs;
    
       gettimeofday(&now, NULL);
       diff_usecs = ((now.tv_sec - real_time_ref_secs) * 1000000) + (now.tv_usec - real_time_ref_usecs);
       mtime = diff_usecs * freq_hz / 1000000;
      } else {
        mtime += inc;
      }
      for (size_t i = 0; i < procs.size(); i++) {
        procs[i]->state.mip &= ~MIP_MTIP;
        if (mtime >= mtimecmp[i])
          procs[i]->state.mip |= MIP_MTIP;
      }
    }
    

    不难发现,该函数用于实现定时器中断,在每次step后也会执行一次,即增加mtime

    完成后会对比各个核心的mtimecmp,如果触发中断,就对mip进行相应的置位

    HTIF

    这个部分是现在fesvr文件夹中,对Spike而言它的主要功能就是I/O

    因为对于仿真器而言,它至少得能和终端进行交互

    当然fesvr并不只是实现HTIF,还包括加载ELF文件等功能

    如果想使用这个功能,只需要定义tohost域fromhost这两个全局变量,htif_t类的声明中有这么一段

      addr_t tohost_addr;
      addr_t fromhost_addr;
    

    根据推测,这两个地址的获取应该是直接从ELF文件中的段表中得来的

    从它的构造函数中可以发现

    htif_t::htif_t()
      : mem(this), entry(DRAM_BASE), sig_addr(0), sig_len(0),
        tohost_addr(0), fromhost_addr(0), exitcode(0), stopped(false),
        syscall_proxy(this)
    {
      signal(SIGINT, &handle_signal);
      signal(SIGTERM, &handle_signal);
      signal(SIGABRT, &handle_signal); // we still want to call static destructors
    }
    
    htif_t::htif_t(int argc, char** argv) : htif_t()
    {
      parse_arguments(argc, argv);
      register_devices();
    }
    
    htif_t::htif_t(const std::vector<std::string>& args) : htif_t()
    {
      int argc = args.size() + 1;
      char * argv[argc];
      argv[0] = (char *) "htif";
      for (unsigned int i = 0; i < args.size(); i++) {
        argv[i+1] = (char *) args[i].c_str();
      }
    
      parse_arguments(argc, argv);
      register_devices();
    }
    

    构造函数中对tohost和fromhost先初始化为0

    本想从中找出这两个地址到底如何初始化,没有进展,就直接搜索这两个变量,发现

    void htif_t::load_program()
    {
      std::map<std::string, uint64_t> symbols = load_payload(targs[0], &entry);
    
      if (symbols.count("tohost") && symbols.count("fromhost")) {
        tohost_addr = symbols["tohost"];
        fromhost_addr = symbols["fromhost"];
      } else {
        fprintf(stderr, "warning: tohost and fromhost symbols not in ELF; can't communicate with target\n");
      }
    
      // detect torture tests so we can print the memory signature at the end
      if (symbols.count("begin_signature") && symbols.count("end_signature"))
      {
        sig_addr = symbols["begin_signature"];
        sig_len = symbols["end_signature"] - sig_addr;
      }
    
      for (auto payload : payloads)
      {
        reg_t dummy_entry;
        load_payload(payload, &dummy_entry);
      }
    }
    

    发现正是从符号中直接获得的

    自此还有另一个问题,如何通过tohost与fromhost与host进行通信

    然而找不到任何相关资料,但是通过搜索,发现这个问题在issues中有人已经提问过,并且Waterman给出了简略的使用方法。同时,他也提到,这个部分并不是RISC-V标准,而是ucb标准,同时,他也提出,我们可以链接自己的设备来实现I/O。

    63:56位代表设备

    55:48位代表命令

    设备0是系统调用设备,用于模拟Unix系统调用,它只实现命令0,这个命令有两个子功能

    如果位0被清除,那么47:0位代表syscall的结构指针
    如果位0被置位,那么47:1位代表退出代码,0表示成功,其他表示失败
    Waterman也表示这是一个不好的设计

    设备1是块字符设备

    命令0读取一个字符

    命令1向tohost写入一个字符,在最低有效字节的位置

    展开全文
  • 快速的峰值检测功能。 它返回尖峰时间。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,461
精华内容 2,984
关键字:

spike