RISC-V debug专栏2 --- Debug Module(DM)

Debug Module(DM)的核心功能

DM 就像一个翻译官,负责把调试器的抽象指令(比如 "暂停处理器")转换成硬件能听懂的具体操作。它必须实现以下基本功能:

  1. 必要功能 (必须实现):
    • 暂停 / 恢复任意处理器核心(hart)。
    • 告诉调试器哪些核心已经暂停。
    • 读写暂停状态下的核心寄存器(比如通用寄存器 GPR)。
    • 提供复位信号,确保能从启动第一条指令开始调试。
  2. 可选功能 (根据设计选择):
    • 直接读写非通用寄存器(如 CSR)。
    • 用 "程序缓冲区" 让核心执行任意指令(比如访问内存)。
    • 同时控制多个核心暂停 / 恢复。
    • 直接访问系统总线(无需通过核心)。

1. DM 的接口(DMI 总线)

DM 通过DMI 总线与调试主机通信,就像电脑通过 USB 连接外设一样:

  • 主设备是调试主机(如运行 GDB 的电脑)。
  • 从设备是 DM 和其他调试模块(如多个 DM 或自定义设备)。
  • 地址空间:DM 的地址从最低位开始,额外地址可分配给其他设备。例如:

2. Reset control

DM 负责管理系统的全局复位信号(ndmreset / non debug module reset)

  • 作用:除 DM 和Debug Transport模块之外的所有组件会被复位或保持在复位状态。具体哪些部件被复位由芯片厂商决定,但必须保证调试器能从程序第一条指令开始调试。

  • 规则

    • DM模块自己的状态和寄存器只有两种情况会被复位:
      a. 系统刚上电时
      b. 当 dmcontrol 寄存器中的 dmactive 位为 0 时(即调试功能未激活)
    • 如果调试功能激活(dmactive=1),系统复位时 HART 的暂停状态会被保留(比如断点位置),但触发相关的 CSR 寄存器可能会被清除
    • 由于时钟和电源域的设计问题,系统复位时不能随意进行 DMI 访问
    • 此时唯一允许的操作是读写 dmcontrol 寄存器,其他 DMI 操作会导致不可预测的结果
  • ndmreset 的操作规则

    • 复位信号的持续时间没有强制要求,但必须满足:
      a. 先写 1 使能复位
      b. 再写 0 触发实际复位
    • 系统恢复时间不确定,通过 allunavail/anyunavail 寄存器报告
  • 单个 HART 的复位方法

    • 通过选择目标 HART,设置并清除 hartreset 位实现复位
    • 实际复位可能包括未被选中的 HART(厂商实现差异)
  • 复位状态检测

    • 调试器可通过以下方式确认复位情况:
      a. anyhavereset:是否有至少一个选中 HART 处于复位状态
      b. allhavereset:是否所有选中 HART 都处于复位状态
  • 复位标志的清除规则

    • 复位后 HART 会设置粘性标志(必须手动清除)
    • 清除方法:向 dmcontrol 的 ackhavereset 位写 1
    • 特殊情况:当 dmactive=0 时,标志可能自动清除
  • 复位后的 HART 行为

    • 如果设置了 haltreq 或 resethaltreq:
      • 复位后立即进入调试模式
    • 否则:
      • 正常执行程序

3. selecting Harts

  • 一个调试模块(DM)最多可以连接 2^20 个硬件线程(hart)。调试器需要先选择一个特定的 hart,之后对这个 hart 发出的暂停、恢复、复位和调试等命令就都只针对这个被选中的 hart 起作用。
  • 调试器如果要列出所有的 hart,需要先确定 HARTSELLEN 的值。
  1. hartsel 寄存器里的所有位都设为 1(假定 hartsel 有最大的位宽),接着读取 hartsel 的值,这样就能知道哪些位实际上被设置了,从而确定 HARTSELLEN(即 hartsel 寄存器里有效位的数量)。
  2. 从索引为 0 的 hart 开始,逐个选择 hart,直到 dmstatus 寄存器里的 anynonexistent 位变为 1(这表明后面没有可用的 hart 了),或者达到了 HARTSELLEN 所限制的最大索引。
  • 调试器可以通过接口读取 mhartid 寄存器,或者读取系统的配置字符串,来了解 hart 索引和 mhartid 之间的映射关系。

3.1 选择单个hart

  • 所有的调试模块都必须支持选择单个 hart。调试器选择单个 hart 的方法很简单,就是把要选的 hart 的索引值写到 hartsel 寄存器里。hart 的索引是从 0 开始的,而且是连续编号的,一直到最后一个索引。

举个例子,要是你想选择第 3 个 hart(索引为 2,因为索引从 0 开始),就把数值 2 写入 hartsel 寄存器。之后执行的暂停、恢复、复位和调试命令都只会对这个被选中的 hart 起作用。

3.2 选择多个hart

  • 调试模块可以实现一个 Hart 数组掩码寄存器,这样就能一次选择多个 hart 了。这个 Hart 数组掩码寄存器的第 n 位对应索引为 n 的 hart,如果这一位是 1,就表示选中了这个 hart。通常,一个 DM 的 Hart 数组掩码寄存器的位宽刚好能覆盖它所支持的所有 hart,但也允许把其中某些位固定设为 0。
  • 调试器可以通过 hawindowselhawindow 来设置 Hart 数组掩码寄存器里的位,然后设置 hasel 来对所有被选中的 hart 执行操作。如果支持这个功能,就可以同时对多个 hart 进行暂停、恢复和复位操作。而且,设置或清除 hasel 不会影响 Hart 数组掩码寄存器的状态。
  • 只有通过 dmcontrol 发起的操作才能同时作用于多个 hart,而抽象命令(Abstract Commands)只对通过 hartsel 选中的单个 hart 起作用。

4. Hart States

每个可被选中的 Hart(硬件线程)必定处于以下四种状态之一:

  1. 不存在(Nonexistent)
  2. 不可用(Unavailable)
  3. 运行中(Running)
  4. 暂停(Halted)

状态详解

a. 不存在(Nonexistent)
  • 含义:这个 Hart 在系统中根本不存在,无论等多久都不会出现。
  • 例子
    假设系统只有 1 个 Hart(索引 0),那么索引 1 及以上的 Hart 都是 "不存在" 的。
  • 调试器行为
    调试器遇到第一个不存在的 Hart 后,会认为后面所有索引的 Hart 都不存在。
b. 不可用(Unavailable)
  • 含义:Hart 可能暂时无法使用,或者被系统永久禁用(比如工厂关闭)。
  • 可能原因
    • 正在复位(Reset)
    • 暂时断电
    • 未插入系统(如扩展卡未安装)
    • 制造时被永久禁用(导致 Hart 索引不连续)
  • 调试器行为
    即使 Hart 永远不会可用,调试器也必须将其视为 "不可用",以便正确枚举所有可能的 Hart。
c. 运行中(Running)
  • 含义:Hart 正在正常执行程序,就像没有调试器连接一样。
  • 特点
    • 可能处于低功耗模式或等待中断,但调试器可以通过暂停请求(haltreq)让它进入暂停状态。
    • 例如:手机 CPU 在省电模式下运行,但仍能被调试器暂停。
d. 暂停(Halted)
  • 含义:Hart 被调试器暂停,只能执行调试器的命令(如单步执行、读写寄存器)。
  • 特点
    • 处于调试模式,无法自主运行程序。
    • 例如:调试器设置断点后,Hart 会暂停在此处等待调试指令。

复位后的状态变化

  • 复位期间:Hart 可能处于 "不可用" 状态。
  • 复位结束后
    1. Hart 可能短暂进入 "运行中" 状态(比如执行初始化代码)。
    2. 最终根据调试器的配置(haltreqresethaltreq)决定是继续运行还是暂停。

状态判断工具

调试器通过dmstatus寄存器的以下标志位判断状态:

  • allnonexistent:所有 Hart 都不存在(罕见)。
  • anynonexistent:存在至少一个不存在的 Hart。
  • allunavail:所有 Hart 都不可用。
  • anyunavail:存在至少一个不可用的 Hart。
  • allrunning:所有 Hart 都在运行。
  • anyrunning:存在至少一个运行中的 Hart。
  • allhalted:所有 Hart 都被暂停。
  • anyhalted:存在至少一个被暂停的 Hart。

总结

  • 不存在:系统中没有这个 Hart。
  • 不可用:Hart 可能存在,但暂时或永久无法使用。
  • 运行中:Hart 正常执行程序,可被调试器暂停。
  • 暂停:Hart 被调试器控制,只能执行调试命令。

通过这些状态,调试器可以精准控制和监控每个 Hart 的行为。

5. Run Control

基本概念

调试模块(DM)会为每个 hart 记录 4 个概念上的状态位:

  1. 暂停请求(halt request):就像老师让学生停下来别写作业了。
  2. 恢复确认(resume ack):好比学生收到老师让继续写作业的通知后,给老师一个确认的回复。
  3. 复位时暂停请求(halt - on - reset request):可以想象成在重新开始考试前,老师要求学生先别动笔。
  4. 硬件线程复位(hart reset):类似于把电脑重启一下。

除了恢复确认位可能初始化为 0 或者 1,其他 3 个位初始都为 0。

状态信号

DM 会从每个 hart 接收 "已暂停(halted)""正在运行(running)" 和 "已复位(havereset)" 这些信号。调试器能通过一些寄存器看到恢复确认位的状态,以及 "已暂停""正在运行""已复位" 信号的状态,但其他位的状态不能直接看到。

暂停请求操作

当调试器把 haltreq 设为 1 时,就好像老师喊了一声 "都别写了",每个被选中的 hart 的暂停请求位就会被设置。如果一个 hart 正在运行,或者刚从复位状态恢复,看到自己的暂停请求位被设置了,它就会停下来,就像学生听到老师的话停下手中的笔。同时,它会取消 "正在运行" 信号,发出 "已暂停" 信号。要是 hart 本来就已经暂停了,就会忽略这个暂停请求位,就像已经停下笔的学生不会再因为老师重复 "别写了" 而有额外反应。

恢复请求操作

当调试器把 resumereq 设为 1 时,好比老师说 "可以继续写作业了"。这时,每个被选中的 hart 的恢复确认位会被清除,并且每个被选中且处于暂停状态的 hart 会收到恢复请求。hart 收到请求后,就会继续工作,就像学生继续写作业,同时取消 "已暂停" 信号,发出 "正在运行" 信号。最后,恢复确认位会被设置,表示已经确认恢复。正在运行的 hart 会忽略恢复请求,就像一直在写作业的学生不会因为老师重复 "继续写" 而有额外反应。

响应时间要求

当请求暂停或者恢复时,除非 hart 不可用,否则它必须在 1 秒内做出响应。不过一般来说,可能几个时钟周期就会有反应,就像学生听到老师的话马上就会做出动作。

复位时暂停请求操作

DM 可以为每个 hart 实现可选的 "复位时暂停请求" 位。当 DM 把 hasresethaltreq 设为 1 时,就表示它支持这个功能。调试器把 setresethaltreq 设为 1,就像老师在重新开始考试前说 "大家先别动笔",每个被选中的 hart 的 "复位时暂停请求" 位就会被设置。当这个位被设置后,下次 hart 复位结束,它会马上进入调试模式,不管复位是什么原因引起的。这个位会一直保持设置状态,直到调试器把 clrresethaltreq 设为 1 来清除它,或者 DM 进行复位,就像老师说 "可以开始考试了",学生才会开始动笔。

总结

调试模块通过设置不同的控制位,就像老师给学生发指令一样,来控制 hart 的运行、暂停和复位等状态,并且能根据 hart 反馈的信号了解它的状态。同时,对 hart 的响应时间也有一定要求。

6. Abstract Command

抽象命令概述

调试模块(DM)支持一组抽象命令,不过大部分命令是可选的。这就好比一个工具包,里面有很多工具,但不是每个工具都一定会被用到。

有时候,就算被选中的硬件线程(hart)没有暂停,调试器也能执行某些抽象命令。就好像你在一台电脑运行程序的时候,也能对它进行一些简单的设置操作。

调试器要知道某个 hart 在特定状态下支持哪些抽象命令,只能通过实际去尝试执行这些命令,然后查看 abstractcs 寄存器里的 cmderr 值,看看命令是否执行成功。就像你想知道一个新玩具能做哪些动作,只能亲自去操作一下,看看它能不能按你的要求做出反应。

而且有些命令在设置了某些选项时能支持,设置其他选项时就不支持了。如果设置了不支持的选项,DM 会把 cmderr 设置为 2,表示 "不支持"。比如一个游戏,有些难度级别是支持的,有些难度级别是不支持的,你选了不支持的难度,游戏就会提示你不行。

执行抽象命令的过程

调试器执行抽象命令时,就像给 DM 下指令,要把命令写到 command 寄存器里。然后,通过读取 abstractcs 寄存器里的 busy 位,就能知道命令是否执行完了。命令执行完后,cmderr 会告诉你命令是否成功。命令可能会失败,原因可能是 hart 没暂停、没运行、不可用,或者执行过程中遇到了错误。这就好比你让一个工人完成一项任务,你可以通过看他是不是还在忙(busy)来知道任务有没有完成,最后通过任务的结果(cmderr)来判断是否成功。

如果命令需要参数,调试器得在写命令到 command 寄存器之前,先把参数写到数据寄存器里。要是命令有返回结果,DM 得在 busy 位清零之前,把结果放到数据寄存器里。具体用哪些数据寄存器存参数在表格 3.1 里有说明,而且最低有效字要放在编号最小的数据寄存器里。参数的宽度取决于执行的命令,没明确指定时就是 DXLEN。这就好比你给工人布置任务时,要先把任务所需的材料(参数)准备好放在指定的地方,等任务完成后,工人要把结果也放在指定的地方。

抽象命令接口的设计目的

抽象命令接口的设计是为了让调试器能尽快地发出命令,之后再去检查命令是否无错完成。一般情况下,调试器的速度比目标硬件慢很多,而且命令通常能成功执行,这样就能实现最大的吞吐量。要是有命令失败了,这个接口能保证失败的命令之后不会再执行其他命令。调试器要找出是哪个命令失败了,就得查看 DM 的状态(比如 data0 寄存器的内容)或者 hart 的状态(比如被程序缓冲区程序修改的寄存器内容)。这就好比你给一群工人依次布置任务,先快速把任务都布置下去,之后再检查任务完成情况。如果有一个任务失败了,后面的任务就先不执行了,然后通过查看相关的工作记录(DM 或 hart 的状态)来找出是哪个任务出了问题。

执行抽象命令的限制条件

在开始执行抽象命令之前,调试器得确保 haltreqresumereqackhavereset 这几个位都是 0。这就好比你要让工人开始一项新任务,得先确保之前的暂停、恢复和复位相关的事情都处理好了。

当抽象命令在执行时(abstractcs 里的 busy 位为高),调试器不能改变 hartsel(选择的 hart),也不能把 haltreqresumereqackhaveresetsetresethaltreqclrresethaltreq 这些位设置为 1。这就好比工人在干活的时候,你不能随便换工人,也不能突然让他暂停、恢复或者复位。

处理命令执行异常的情况

如果一个抽象命令没在预期时间内完成,看起来像是卡住了,调试器可以尝试下面的步骤来中止这个命令:先复位 hart(用 hartresetndmreset),然后复位 DM(用 dmactive)。这就好比工人干活卡住了,你先把他 "重启" 一下,再把工作环境 "重启" 一下。

如果在所选的 hart 不可用的时候启动抽象命令,或者在执行抽象命令时 hart 变得不可用了,DM 可能会终止这个抽象命令,把 busy 位清零,把 cmderr 设置为 4(表示 "暂停 / 恢复" 问题)。也有可能命令看起来就像卡住了(busy 位一直不清零)。这就好比工人突然生病了(hart 不可用),正在做的任务可能就做不下去了,要么直接停止,要么就一直卡在那里。

6.1 Abstract command list

  • 0x0000 - 0x0fff :放的是 CSRs(控制状态寄存器),这里面可以通过 dpc 这个 "快捷方式" 访问 "PC"(程序计数器,类似记录你正在看哪一页书的标记)。
  • 0x1000 - 0x101f:放的是 GPRs(通用寄存器,像通用抽屉,啥都能临时放一点)。
  • 0x1020 - 0x103f:放的是浮点寄存器(专门放小数相关数据的抽屉)。
  • 0xc000 - 0xffff:留着以后给特殊扩展功能或内部用的,现在先空着。
  • cmdtype:等于 0 时,代表这个命令是 "Access Register"(访问寄存器),就像给调试器一个 "访问寄存器" 的任务标签。
  • aarsize:决定访问寄存器时拿多少数据。2 是拿 32 位(一小份),3 是拿 64 位(中等份),4 是拿 128 位(一大份)。如果要拿的量比寄存器实际能给的多,访问就会失败。
  • aarpostincrement:如果是 1,比如你访问了 0 号寄存器,访问完后,下次自动变成访问 1 号寄存器(像自动翻页)。0 就是没这功能。
  • postexec:如果是 1,在完成数据转移(读或写寄存器)后,去执行 "程序缓冲区" 里的程序(像做完一件事,再顺手做另一件事)。0 就是不做。
  • transfer :配合 write 用。1 表示要按 write 的指示做操作(读或写寄存器)。
  • write :当 transfer 是 1 时,0 表示从寄存器读数据到 arg0(从抽屉拿东西放篮子);1 表示从 arg0 写数据到寄存器(把篮子东西放抽屉)。
  • regno:要访问的寄存器编号,像抽屉号码。

"Access Register" 命令就像调试器当 "寄存器管理员",对 CPU 寄存器进行操作,还能执行一个小任务(程序缓冲区)。举例:

  • 读寄存器 :比如你想看看 3 号寄存器(regno = 3)里的内容。设置 write = 0(不写),transfer = 1(要操作)。调试器就像从 3 号抽屉里把东西拿出来,放到 arg0 这个篮子里。

  • 写寄存器 :如果想把 arg0 篮子里的东西放到 5 号寄存器(regno = 5)。设置 write = 1(写),transfer = 1(要操作)。调试器就把篮子东西放进 5 号抽屉。

  • 自动递增编号 :如果 aarpostincrement = 1,假设先访问 2 号寄存器,访问完后,下次 regno 自动变成 3 号(像自动从第 2 页翻到第 3 页)。

  • 执行程序缓冲区 :如果 postexec = 1,在完成读或写寄存器后,调试器会去执行 "程序缓冲区" 里的程序(像做完作业,再去做手工)。

  • 调试模块必须实现这个命令。而且当硬件线程(hart)"暂停"(像上课喊停,学生不动)时,必须支持读写所有 GPRs(通用寄存器,那些通用抽屉)。

  • 对于其他寄存器(比如浮点寄存器),支持情况不一定。可能有的寄存器只能读不能写,或者在 hart 运行时(像学生正常上课)也能访问,也可能不行。每个寄存器在 "读、写、hart 状态(暂停或运行)" 下的支持情况都可能不同。

  • 如果操作中任何一步失败(比如要访问的寄存器根本不存在),cmderr 会被设置(像报错),后面步骤也不做了。比如要开一个不存在的抽屉,直接报错,不再继续开其他抽屉。

通过这些操作,调试器就能灵活地和 CPU 寄存器 "打交道",就像管理员管理抽屉一样,按需读写,还能在适当时候执行小任务。

6.2 快速访问

cmdtype 为 1 时,表示这是一个 "Quick Access command(快速访问命令)

命令操作通俗解释

  1. 检查 hart 是否已暂停
    假如把 hart 想象成一个正在工作的小机器人。如果这个小机器人已经停下来(暂停)了,就像机器人正在休息,这时这个命令会给 cmderr 设置为 "halt/resume"(暂停 / 恢复相关的错误标记),然后不再继续下面的操作。就好比你看到一个人已经坐下休息了,还非要让他坐下,那就会报错说 "别重复啦"。
  2. 尝试让 hart 暂停
    现在要让这个小机器人停下来。但如果小机器人因为其他原因(比如遇到了一个 "小陷阱"------ 断点)自己停下来了,那这个命令也会给 cmderr 设置为 "halt/resume",不再继续下面的操作。这就像你让朋友帮忙去买东西,结果朋友因为路上看到喜欢的店自己先停下了,那你的任务就没法按计划进行,得报错。
  3. 执行程序缓冲区
    让小机器人去执行一个 "小任务包"(程序缓冲区)。如果在执行这个 "小任务包" 时出了问题(异常),就像小机器人在执行任务时摔了一跤,这时会给 cmderr 设置为 "exception"(异常标记),"小任务包" 的执行结束,但这个快速访问命令还会继续往下走。就好比你让朋友去取快递,朋友取快递时遇到点问题,但你还是让朋友继续后面的行程。
  4. 恢复 hart 运行
    最后让小机器人重新开始工作(恢复运行)。

总结

这个 "Quick Access command" 是一个可选的命令(不是必须有的功能),而且在整个过程中,它不会去动数据寄存器(就像一个人只负责发号施令让机器人动,但不碰机器人携带的包裹 ------ 数据寄存器)。通过这几个步骤,调试器可以在一定条件下控制 hart 的暂停、执行特定任务包,然后再恢复运行,但每一步都有相应的规则和错误处理机制。

6.3 内存访问

cmdtype:值为 2,代表这是 "访问内存命令(Access Memory Command)"

  • aamvirtual
    • 0:表示访问的是物理地址(就像直接按实际房间号找房间),操作直接在硬件线程(hart)对应的物理内存上进行。
    • 1:表示访问的是虚拟地址,且会像在机器模式(M - mode)下设置了 MPRV 那样进行地址转换(类似通过门牌号对应的虚拟映射找房间)。
  • aamsize :决定访问内存区域的大小。
    • 0:访问内存位置的最低 8 位(1 字节,像从一个大箱子里拿 1 小格东西)。
    • 1:访问最低 16 位(2 字节,拿 2 小格)。
    • 2:访问最低 32 位(4 字节,拿 4 小格)。
    • 3:访问最低 64 位(8 字节,拿 8 小格)。
    • 4:访问最低 128 位(16 字节,拿 16 小格)。
  • aampostincrement
    • 1:内存访问完成后,arg1(存地址的地方)会按 aamsize 编码的字节数增加(比如 aamsize 是 2,访问 4 字节,arg1 地址就 +4,像自动翻到下一页地址)。
    • 0:无此效果。
  • write
    • 0:从 arg1 指定的内存位置读数据到 arg0(像从书架上拿书到自己手里)。
    • 1:把 arg0 的数据写到 arg1 指定的内存位置(像把手里的书放回书架)。
  • target - specific:留给特定目标用的保留位(暂时不用管,像预留的特殊通道)。

命令操作通俗解释(结合例子)

"访问内存命令(Access Memory)" 让调试器能像被选中的硬件线程(hart)一样访问内存,包括 hart 本地的内存映射寄存器等(好比调试器 "模仿" hart 去开它能开的抽屉)。

  • 读操作示例
    假设 aamsize = 2(访问 32 位,4 字节),write = 0(读),arg1 里存的地址是 0x1000。调试器就从内存地址 0x1000 处读 4 字节数据到 arg0(像从 0x1000 这个抽屉里拿 4 件东西放到篮子 arg0 里)。如果 aampostincrement = 1,读完后 arg1 会变成 0x1004(地址自动加 4,准备好下次读下一个抽屉)。
  • 写操作示例
    write = 1(写),arg0 里有数据,arg1 地址是 0x2000aamsize = 3(访问 64 位,8 字节)。调试器会把 arg0 的数据写到内存地址 0x2000 处(像把篮子 arg0 里的东西放到 0x2000 这个抽屉里)。
  • 失败处理
    如果这些操作中任何一步失败(比如 hart 在机器模式下执行相同访问也会失败的情况),就设置 cmderr(报错),后面步骤不再执行(像发现抽屉坏了,马上停止操作并标记问题)。

命令支持情况

调试模块可以选择实现这个命令,并且可以支持在 hart 运行或暂停时访问内存位置。如果支持 hart 运行时的内存访问,那也必须支持 hart 暂停时的内存访问(好比如果允许在白天用某工具,那晚上也得允许用)。这个命令只有在读内存时会修改 arg0,只有设置了 aampostincrement 才会修改 arg1,其他数据寄存器不受影响(像只动特定的篮子和抽屉编号,其他篮子不动)。

相关推荐
百里杨2 天前
编译玄铁处理器RISC-V指令测试用例
测试用例·risc-v·玄铁
CodingCos4 天前
【RISC-V CPU Debug 专栏 1 -- RISCV 相关文档的区别与联系】
risc-v
Hug Freedom.6 天前
RISC-V AIA学习3---APLIC 第一部分
学习·risc-v
国科安芯6 天前
国产RISC-V车规芯片当前现状分析——从市场与技术角度出发
网络·人工智能·嵌入式硬件·架构·汽车·risc-v
Hug Freedom.6 天前
RISC-V AIA学习3---APLIC 第二部分(APLIC 中断域的内存映射控制区域)
学习·risc-v
Hug Freedom.7 天前
RISC-V AIA学习3---APLIC第三部分
学习·risc-v
Hug Freedom.8 天前
CHI协议——retry
开发语言·前端·硬件工程·risc-v
Jason_zhao_MR10 天前
国产开发板—米尔全志T113-i如何实现ARM+RISC-V+DSP协同计算?
arm开发·嵌入式硬件·mcu·嵌入式·risc-v
易板11 天前
CH32V208蓝牙内部带运放32位RISC-V工业级微控制器CH32V208CBU6、CH32V208GBU6开发板原理图和PCB
单片机·蓝牙·risc-v·开发板