大数跨境

协程在 RTL 仿真器中的应用:Verilog ↔ Coroutine 逐行对照

协程在 RTL 仿真器中的应用:Verilog ↔ Coroutine 逐行对照 ai算法芯片与系统
2026-09-25
4
导读:协程的挂起与恢复机制与Verilog进程模型天然同构,是RTL仿真器的核心实现方式。调度器借助时间轮、边沿敏感表和电平敏感表管理唤醒条件,initial、always、fork等结构均可映射为协程。

 

📝 摘要

  • • 🎯 主题:协程在 RTL 仿真器中的应用,把 Verilog 里的 initial、always @(posedge clk)、#delay、wait(cond)、fork...join 映射成协程。
  • • 🧠 核心观点:RTL 仿真器本质上就是一个协程调度器,它维护时间轮、边沿敏感表、电平敏感表和就绪队列。
  • • 🛠️ 产出:一份几乎逐行对应的 Verilog / 协程代码,加上一个极简调度器,能真正跑起来。
  • • 🔍 重点:理解 @(posedge clk) 和 wait(ready) 的区别:一个是边沿触发,一个是电平触发。
  • • 🌍 语言无关:Python、C++20、Rust、JS、Lua 都能表达同样的协程模型,本文用 Python 只是因为它最适合教学演示。
  • • ⚠️ 小提醒:无限时钟会让调度器不退出,后文给出小补丁。

🗂️ 目录

  1. 1. 为什么协程适合用于 RTL 仿真器
  2. 2. 仿真器内核的总体架构
  3. 3. Verilog 侧:一个覆盖多种时序控制的 demo
  4. 4. 协程侧:三种 await 原语
  5. 5. 极简调度器:时间、边沿、电平、就绪队列
  6. 6. 进程逐块对应:从 initial 到 always
  7. 7. 逐条对照表
  8. 8. 运行时间线与两个小坑
  9. 9. 关键点总结

1. 为什么协程适合用于 RTL 仿真器

Verilog 里的并发进程,比如:

  • • initial begin ... end
  • • always @(posedge clk) ...
  • • fork ... join

它们看起来像“并行线程”,但又不是操作系统线程。每个进程有自己的程序计数器 PC,能在某个时间点挂起,等条件满足后再从挂起处继续。

这正好就是协程的语义:

  • • 🧩 协程可以顺序执行;
  • • ⏸️ 可以在 await 处挂起;
  • • ▶️ 被调度器唤醒后,从上次挂起的位置继续;
  • • 🧵 多个协程可以并发推进,但底层通常还是单线程事件循环。

所以,用协程模拟 Verilog 时序控制,非常自然——Python、Lua、Rust、C++20、JS 都能做,语言不是重点,模型同构才是重点。

🔍 图说明

  • • 🟦 蓝色框:Verilog 进程。
  • • 🟪 紫色框:协程原语。
  • • 🟨 黄色核心:两者共享的“可挂起/可恢复”模型。
  • • 🟩 绿色框:仿真器内核。
  • • 粗黄线表示 Verilog 与协程在模型层面对齐;粗绿线表示它们都落到同一个调度器上。

2. 仿真器内核的总体架构

先看整个系统长什么样。理解这张图,后面代码就是它的填色版。

🔍 图说明

  • • 🟦 蓝色:用户进程层,等价于 Verilog 里的 initial / always / fork。
  • • 🟧 橙色:就绪队列 _ready,本 delta 可运行的协程。
  • • 🟪 紫色虚线:时间堆 _timers,对应 #N 延时。
  • • 🩷 粉色:边沿敏感表 _edge,对应 @(posedge sig) / @(negedge sig)。
  • • 🟩 青绿:电平敏感表 _level,对应 wait(cond)。
  • • 🟢 浅绿:信号值表 _val,所有信号当前值。
  • • ➡️ 实线粗箭头:await 原语向上进入内核。
  • • ⬅️ 虚线箭头:内核唤醒协程,放回就绪队列。
  • • 🔔 绿色双线:信号 write 触发边沿/电平检查。

🧩 协程的五个状态

一个协程从生到死,会在这几个状态之间来回切换:

🔍 图说明

  • • 🟦 起点:spawn() 把协程塞进 _ready。
  • • 🟧 Ready:等待被调度器挑选。
  • • 🟩 Running:_step() 里执行到下一个 await。
  • • 🟪 三种等待状态互相平行,各自挂到不同的数据结构。
  • • ⬅️ 三条回到 Ready 的边,就是三种唤醒路径:定时器到期 / 边沿跳变 / 谓词变真。
  • • 🩶 灰色终点:StopIteration 表示协程自然结束。

🆚 边沿 vs 电平:两种等待的根本区别

这是初学者最容易混的地方,用一张并排图钉死。

🔍 图说明

  • • 📈 左边(粉色 _edge):只关心跳变的那一刻,0→1 或 1→0 才算命中。
  • • 🟢 右边(青绿 _level):关心当前是否为真,只要谓词为真就放行。
  • • 🔑 一句话记忆:边沿是“瞬间事件”,电平是“持续状态”。
  • • 🧠 这就是为什么调度器里 _edge 和 _level 必须分开维护。

3. Verilog 侧:一个覆盖多种时序控制的 demo


   
   
   
   
    
   
   
   
   // demo.v —— 只用于仿真,目的是覆盖各种时序控制结构
module
 demo #(parameter int N = 3);

    logic
       clk = 0;
    logic
       rst_n = 0;
    logic
       go = 0;
    logic
       ready = 0;
    logic
       req = 0;
    logic
       done = 0;
    logic
 [3:0] ticks = 0;
    logic
       selftest = 0;

    // ---------- 进程 0:时钟 ----------

    initial
 forever #1 clk = ~clk;

    // ---------- 进程 1:initial + #delay ----------

    initial
 begin
        selftest = 0;
        #10;                       // 等 10 个时间单位
        selftest = 1;
        #5;
        selftest = 0;
    end


    // ---------- 进程 2:always @(posedge clk) ----------

    always
 @(posedge clk) begin
        if
 (!rst_n) begin
            req   <= 0;
            done  <= 0;
            ticks <= 0;
        end
 else begin
            done <= 0;
            if
 (go && !req) begin
                req <= 1;
                wait
 (ready);              // 电平敏感:等到 ready==1
                repeat
 (N) @(posedge clk); // 等 N 个上升沿
                req   <= 0;
                done  <= 1;
                ticks <= ticks + 1;
            end

        end

    end


    // ---------- 进程 3:fork ... join ----------

    initial
 begin
        fork

            begin
 : BR1
                #3;
                $display
("[%0t] BR1 done", $time);
            end

            begin
 : BR2
                #7;
                $display
("[%0t] BR2 done", $time);
            end

        join

        $display
("[%0t] both branches joined", $time);
    end


    // ---------- 进程 4:激励 ----------

    initial
 begin
        #2;  rst_n = 1;
        #4;  go = 1;
        #2;  go = 0;
        #5;  ready = 1;
        #20; $finish;
    end


endmodule

🔍 代码说明

  • • 🕒 进程 0:initial forever #1 clk = ~clk; 是一个永远运行的时钟发生器。
  • • ⏳ 进程 1:纯 initial 顺序块,用 #10、#5 控制 selftest 的翻转。
  • • 🔁 进程 2:always @(posedge clk) 是典型同步逻辑,混合了三种时序控制:
    • • wait(ready):电平敏感等待;
    • • repeat (N) @(posedge clk):等 N 个时钟上升沿;
    • • 非阻塞赋值 <=,真实 Verilog 中有 NBA 语义。
  • • 🍴 进程 3:fork ... join 启动两个并行分支,等两个都结束后再打印 joined。
  • • 🎛️ 进程 4:激励进程,依次释放复位、拉高 go、拉高 ready,最后 $finish。

🗺️ Verilog 进程的职责拆解

🔍 图说明

  • • 🧠 这是一张思维导图,把每个 Verilog 进程的职责可视化。
  • • 📌 每个叶子节点,后面都对应一个协程或一段协程逻辑。
  • • 🎯 注意:fork 分支的 #3、#7 是并发等待,不是顺序的。

4. 协程侧:三种 await 原语


   
   
   
   
    
   
   
   
   """
verilog_coroutine.py —— Verilog 时序控制 ↔ 协程 一一对应
"""

import
 heapq
from
 collections import defaultdict


# ============================================================

# 1) 三个原语:协程里 await 它们,对应 Verilog 的三种时序控制

# ============================================================

class
 _Delay:                              # 对应 #N
    def
 __init__(self, sched, dt):
        self
.sched, self.dt = sched, dt
    def
 __await__(self):
        yield
 self
        return
 None


class
 _Edge:                               # 对应 @(posedge sig) / @(negedge sig)
    def
 __init__(self, sched, sig, posedge):
        self
.sched, self.sig, self.posedge = sched, sig, posedge
    def
 __await__(self):
        yield
 self
        return
 None


class
 _Level:                              # 对应 wait(cond)
    def
 __init__(self, sched, pred):
        self
.sched, self.pred = sched, pred
    def
 __await__(self):
        yield
 self
        return
 None

🔍 代码说明

  • • ⏱️ _Delay 对应 #N:await s.delay(10) 后,协程被放进时间堆。
  • • 📈 _Edge 对应 @(posedge sig) 或 @(negedge sig):协程挂到某个信号的边沿等待表。
  • • 🟢 _Level 对应 wait(cond):协程挂到电平敏感表,谓词为真就放行。
  • • 🧪 三个类都实现 __await__,通过 yield self 把请求交给调度器。
  • • 🧠 这是关键:await 不是阻塞线程,而是把控制权交回调度器。
  • • 🎯 这套原语与语言无关,C++20 的 co_await、JS 的 yield*、Rust 的 Future 都能表达同样的语义。

5. 极简调度器:时间、边沿、电平、就绪队列


   
   
   
   
    
   
   
   
   # ============================================================
# 2) 调度器:等价于 Verilator 的 VlTriggerScheduler

# ============================================================

class
 Scheduler:
    def
 __init__(self):
        self
.now = 0
        self
._seq = 0
        self
._ready  = []                    # 本 delta 可运行
        self
._timers = []                    # (when, seq, coro)
        self
._edge   = defaultdict(list)     # sig -> [(coro, posedge)]
        self
._level  = []                    # [(coro, pred)]
        self
._val    = {}                    # sig -> 值

    # ---- 协程里可以 await 的原语 ----

    def
 delay(self, dt):                       return _Delay(self, dt)
    def
 wait_edge(self, sig, posedge=True):    return _Edge(self, sig, posedge)
    def
 wait_until(self, pred):                return _Level(self, pred)

    # ---- 信号读写;写会触发边沿/电平检查 ----

    def
 read(self, name, default=0):
        return
 self._val.get(name, default)

    def
 write(self, name, v):
        old = self._val.get(name, 0)
        self
._val[name] = v
        if
 old != v:
            self
._on_change(name, old, v)

    # ---- 启动协程 / 主循环 ----

    def
 spawn(self, coro):
        self
._ready.append(coro)

    def
 run(self):
        while
 self._ready or self._timers:
            while
 self._ready:
                self
._step(self._ready.pop(0))
            if
 self._timers:
                t, _, coro = heapq.heappop(self._timers)
                self
.now = t
                self
._ready.append(coro)

    # ---- 内部 ----

    def
 _step(self, coro):
        try
:
            req = coro.send(None)
        except
 StopIteration:
            self
._recheck_levels()            # 协程结束也可能让谓词变真
            return

        if
 isinstance(req, _Delay):
            self
._seq += 1
            heapq.heappush(self._timers, (self.now + req.dt, self._seq, coro))
        elif
 isinstance(req, _Edge):
            self
._edge[req.sig].append((coro, req.posedge))
        elif
 isinstance(req, _Level):
            self
._level.append((coro, req.pred))
        self
._recheck_levels()

    def
 _recheck_levels(self):
        still = []
        for
 coro, pred in self._level:
            if
 pred():
                self
._ready.append(coro)
            else
:
                still.append((coro, pred))
        self
._level = still

    def
 _on_change(self, name, old, new):
        for
 coro, posedge in list(self._edge.get(name, [])):
            hit = (old == 0 and new == 1) if posedge else (old == 1 and new == 0)
            if
 hit:
                self
._edge[name].remove((coro, posedge))
                self
._ready.append(coro)
        self
._recheck_levels()

🔍 代码说明

  • • 📦 _ready:当前 delta 周期内可运行的协程队列。
  • • ⏰ _timers:最小堆,保存 (唤醒时间, 序号, 协程),对应 #delay。
  • • 📌 _edge:边沿等待表,sig -> [(coro, posedge)],对应 @(posedge/negedge sig)。
  • • 🟢 _level:电平等待表,保存 (coro, pred),对应 wait(cond)。
  • • 🗂️ _val:信号当前值表,read/write 模拟信号读写。
  • • 🚀 run():主循环,先清空 _ready,再从 _timers 取出最早到期的协程,推进 now。
  • • 🔔 _on_change():信号变化时检查边沿等待者,同时重新检查电平等待者。
  • • 🧠 这就是简化版 RTL 仿真器内核:时间轮 + 敏感表 + 就绪队列。

🔄 调度器主循环的流程

🔍 图说明

  • • 🟢 绿色起点:进入主循环。
  • • 🟨 黄色菱形:所有判断节点。
  • • 🟩 绿色步骤:执行协程 _step()。
  • • 🟪 紫色虚线:入时间堆。
  • • 🩷 粉色:边沿表。🟩 青绿:电平表。
  • • 🩶 灰色:协程结束。
  • • 🟧 橙色:从时间堆取出下一个协程,放回就绪队列。
  • • 🎯 整个循环就是一个事件驱动引擎:清就绪队列 → 取最早定时器 → 回到就绪队列。

6. 进程逐块对应:从 initial 到 always

6.1 时钟、initial、always


   
   
   
   
    
   
   
   
   # ============================================================
# 3) 进程:与上面的 Verilog 逐块对应

# ============================================================


# ---- 进程 0:initial forever #1 clk = ~clk; ----

async
 def clock_gen(s):
    while
 True:
        s.write('clk', 1)
        await
 s.delay(1)
        s.write('clk', 0)
        await
 s.delay(1)


# ---- 进程 1:initial + #delay ----

async
 def proc_initial(s):
    s.write('selftest', 0)
    await
 s.delay(10)                # #10
    s.write('selftest', 1)
    await
 s.delay(5)                 # #5
    s.write('selftest', 0)


# ---- 进程 2:always @(posedge clk) ----

async
 def proc_fsm(s):
    s.write('req', 0)
    s.write('done', 0)
    s.write('ticks', 0)

    while
 True:
        await
 s.wait_edge('clk', posedge=True)          # @(posedge clk)
        if
 not s.read('rst_n'):
            s.write('req', 0)
            s.write('done', 0)
            s.write('ticks', 0)
            continue

        s.write('done', 0)
        if
 s.read('go') and not s.read('req'):
            s.write('req', 1)
            await
 s.wait_until(lambda: s.read('ready'))  # wait (ready)
            for
 _ in range(3):                           # repeat (N=3)
                await
 s.wait_edge('clk', posedge=True)
            s.write('req', 0)
            s.write('done', 1)
            s.write('ticks', s.read('ticks') + 1)

🔍 代码说明

  • • 🕒 clock_gen 对应 initial forever #1 clk = ~clk;,是一个无限循环协程。
  • • ⏳ proc_initial 对应纯 initial 块,顺序执行,遇到 await s.delay(...) 就挂起。
  • • 🔁 proc_fsm 对应 always @(posedge clk):
    • • 外层 while True 表示永远循环;
    • • await s.wait_edge('clk', posedge=True) 表示等上升沿;
    • • wait_until(lambda: s.read('ready')) 对应 wait(ready);
    • • for _ in range(3) 对应 repeat (N) @(posedge clk)。
  • • 🧠 注意:这里 write 是立即更新,简化了 Verilog 的非阻塞赋值 NBA 和 delta 周期。
  • • ⚠️ 原版 clock_gen 在 t=0 就写了 clk=1,和 Verilog 初始 clk=0、第一个上升沿在 t=1 的相位不完全一致。严格对齐可改成先 await delay(1) 再翻转。

6.2 fork...join 与激励


   
   
   
   
    
   
   
   
   # ---- 进程 3:fork ... join ----
async
 def fork_join(s, *branches):
    """fork ... join:所有分支结束后才返回。"""

    state = {'remaining': len(branches)}

    async
 def run_one(p):
        await
 p
        state['remaining'] -= 1

    for
 p in branches:
        s.spawn(run_one(p))                              # fork
    await
 s.wait_until(lambda: state['remaining'] == 0)  # join


async
 def branch1(s):
    await
 s.delay(3)                                     # #3
    print
(f"[{s.now}] BR1 done")

async
 def branch2(s):
    await
 s.delay(7)                                     # #7
    print
(f"[{s.now}] BR2 done")

async
 def proc_fork(s):
    await
 fork_join(s, branch1(s), branch2(s))
    print
(f"[{s.now}] both branches joined")


# ---- 进程 4:激励 ----

async
 def stimulus(s):
    await
 s.delay(2);  s.write('rst_n', 1)
    await
 s.delay(4);  s.write('go', 1)
    await
 s.delay(2);  s.write('go', 0)
    await
 s.delay(5);  s.write('ready', 1)
    await
 s.delay(20)
    print
(f"[{s.now}] $finish")

🔍 代码说明

  • • 🍴 fork_join 对应 fork ... join:每个分支 s.spawn(run_one(p)) 启动成独立协程,父协程用 wait_until 等计数器归零。
  • • 🧵 branch1 和 branch2 分别对应 #3 和 #7。
  • • 🎛️ stimulus 对应激励进程,按时间依次设置 rst_n、go、ready。
  • • 🛑 原版 $finish 只是打印,没有真正停止调度器;因为 clock_gen 是无限循环,s.run() 不会自动退出。

🍴 fork...join 的结构

🔍 图说明

  • • 🟧 橙色:父协程 proc_fork。
  • • 🟦 蓝色:两个 fork 出来的子协程。
  • • 🟩 绿色:分支完成,计数器递减。
  • • 🟨 黄色菱形:join 的判定条件。
  • • ⏸️ 灰色虚线:谓词为假时父协程挂在 _level。
  • • 🎯 结论:fork...join = 多个子协程 + 一个计数器谓词,完全不需要线程。

6.3 顶层启动


   
   
   
   
    
   
   
   
   # ============================================================
# 4) 顶层

# ============================================================

def
 main():
    s = Scheduler()
    for
 name in ('clk', 'rst_n', 'go', 'ready',
                 'req'
, 'done', 'ticks', 'selftest'):
        s._val[name] = 0

    s.spawn(clock_gen(s))        # initial forever
    s.spawn(proc_initial(s))     # initial begin ... end
    s.spawn(proc_fsm(s))         # always @(posedge clk)
    s.spawn(proc_fork(s))        # initial fork ... join
    s.spawn(stimulus(s))         # 激励

    s.run()


if
 __name__ == '__main__':
    main()

🔍 代码说明

  • • 🧱 初始化所有信号为 0,对应 Verilog 中声明时的初值。
  • • 🚀 用 s.spawn(...) 启动五个协程,对应五个 Verilog 进程。
  • • ▶️ s.run() 进入调度循环,直到 _ready 和 _timers 都为空。
  • • ⚠️ 因为时钟协程永不结束,实际会一直有定时器,所以需要手动加停止机制才能真正退出。

7. 逐条对照表

Verilog
协程
调度器里对应
initial begin ... end async def proc_...
一个协程
always @(posedge clk) while True: await s.wait_edge('clk', posedge=True)
每次边沿被唤醒
#10 await s.delay(10) _timers
 里的 (now+10, ...)
@(posedge clk) await s.wait_edge('clk', True) _edge['clk']
 里挂一项
@(negedge clk) await s.wait_edge('clk', False)
同上,判 1→0
wait (ready) await s.wait_until(lambda: s.read('ready')) _level
 列表,每次信号变/协程推进都重判
repeat (N) @(posedge clk) for _ in range(N): await s.wait_edge('clk', True)
循环里重复登记
fork ... join fork_join(s, a, b)
多个子协程 + 计数谓词
fork ... join_none s.spawn(p)
 直接返回
子协程独立调度
#1 clk = ~clk s.write('clk', ...)
 + await s.delay(1)
_on_change
 触发等待者
$finish s.run()
 里 _ready/_timers 都空时退出
—
敏感表 / 事件队列
_edge
 / _level / _timers
就是 VlTriggerScheduler 的核心数据

🔍 表格说明

  • • 📌 这张表是整篇博客的“速查卡”。
  • • 🧠 最关键的是:@(posedge) 是边沿敏感,wait(cond) 是电平敏感,它们在调度器里必须分开维护。
  • • 🍴 fork...join 不是魔法,就是“启动多个协程 + 等计数器归零”。

8. 运行时间线与两个小坑

跑一遍,fork...join 部分会看到:


   
   
   
   
    
   
   
   
   [3] BR1 done
[7] BR2 done
[7] both branches joined

🔍 说明

  • • 🕒 BR1 在 t=3 完成。
  • • 🕖 BR2 在 t=7 完成。
  • • 🤝 join 必须等两个分支都完成,所以在 t=7 才打印 both branches joined。

⏱️ 完整时间线

🔍 图说明

  • • 🟥 crit 红色:时钟和 $finish 是全局性事件。
  • • 🟩 done 绿色:分支等待区间,milestone 是完成时刻。
  • • 🟦 active 蓝色:激励信号依次设置。
  • • 📌 从图上可以清楚看到:BR1 在 t=3 到达,BR2 在 t=7 到达,join 也在 t=7 通过。
  • • 🕒 仿真在 t=33 结束(2+4+2+5+20)。

⚠️ 两个小坑

  • • ⚠️ 坑 1:无限时钟导致调度器不退出。
    • • clock_gen 是 while True,会不断产生新的定时器。
    • • s.run() 的退出条件是 _ready 和 _timers 都为空,因此永远不会自然退出。
    • • 若只想看 demo,可以加一个 stop 标志,或抛异常终止。
  • • ⚠️ 坑 2:时钟相位与原 Verilog 不完全一致。
    • • 原版 clock_gen 在 t=0 就 write('clk', 1),相当于 t=0 产生上升沿。
    • • Verilog 中 clk 初始为 0,第一个上升沿在 t=1。
    • • 严格对齐可写成:

   
   
   
   
    
   
   
   
   async def clock_gen(s):
    while
 True:
        await
 s.delay(1)
        s.write('clk', 1 - s.read('clk'))

🔍 小补丁说明

  • • 🛑 如果要真正 $finish,可以自定义 StopSim 异常,在 stimulus 最后抛出,并在 run() 中捕获。
  • • 🕒 修正时钟后,clk 的相位会更接近 Verilog 的 initial forever #1 clk = ~clk;。
  • • 🧪 这些不影响理解协程调度器的核心结构。

9. 关键点总结

  • • 🧩 initial 就是协程:顺序执行、可中断、有独立 PC。
  • • 🔁 always @(posedge clk) 也是协程:每次边沿被唤醒,跑完继续等下一次边沿。
  • • ⏳ #N 是定时器:调度器用最小堆维护唤醒时间。
  • • 📈 @(posedge sig) 是边沿敏感:只有 0→1 跳变那一刻才放行。
  • • 🟢 wait(cond) 是电平敏感:谓词为真就放行,每次信号变化或协程推进都要重判。
  • • 🍴 fork...join 是 gather 语义:spawn 多个子协程,父协程等计数归零。
  • • 🚀 信号写操作驱动调度器:write 会触发 _on_change,检查边沿表和电平表。
  • • 🧠 RTL 仿真器内核 ≈ 协程调度器:时间轮、敏感表、就绪队列、信号值表。
  • • 🌍 与语言无关:Python、C++20、Rust、JS、Lua 都能表达同样的模型。
  • • 🛠️ 这个极简实现适合教学,但还没有处理 NBA、delta 周期、多驱动、未知态、join_any/join_none 等真实仿真器细节。
  • • 📚 下一步可以扩展:
    • • 支持 join_any / join_none;
    • • 支持非阻塞赋值和 delta 周期;
    • • 支持 fork...join 的异常传播;
    • • 支持 $finish / $stop;
    • • 支持信号位宽和 x/z 状态。

🔍 图说明

  • • 🟩 绿色节点:你现在掌握的内容。
  • • 🟦 蓝色虚线节点:可以继续深入的方向。
  • • 🎯 每条虚线代表一个扩展点,互不依赖,可按需选择。

🎉 一句话总结:Verilog 的时序控制,本质上是在描述“进程何时挂起、何时唤醒”;协程正好提供了同构的挂起/恢复机制,而调度器则负责时间、边沿和电平的敏感判断。理解了这个映射,你就理解了 RTL 仿真器的心脏。

 


【声明】内容源于网络
0
0
ai算法芯片与系统
长期关注ai领域,算法,芯片,软件(系统,框架,编译器,算子库)等联合设计
内容 221
粉丝 0
ai算法芯片与系统 长期关注ai领域,算法,芯片,软件(系统,框架,编译器,算子库)等联合设计
总阅读5.5k
粉丝0
内容221