嵌入式学习者
实际项目中 mailbox 和 semaphore 用得很频繁,我分享几个具体用法和踩过的坑。
mailbox 除了 scoreboard,在 sequence 和 driver 之间传递 transaction 也靠它,这是 UVM TLM 通信的基础。但要注意,mailbox 传递的是对象的句柄(引用),不是对象拷贝。这意味着如果你把同一个对象句柄 put 进两个不同的 mailbox,两个接收者拿到的是同一个对象,同时修改会冲突。通常做法是 clone 一个副本再放进去。
semaphore 的一个经典场景是控制有限数量的资源。比如你有 4 个物理串口,但跑了 8 个并发序列(sequence),每个序列都需要独占一个串口来收发数据。你可以创建一个初始有 4 把钥匙的 semaphore:semaphore uart_sem = new(4);
每个序列在开始收发前,先 uart_sem.get(1) 申请钥匙,用完后 uart_sem.put(1) 释放。这样最多同时 4 个序列在运行,其他的在排队等钥匙。这比用 fork join 控制并发数更灵活。
死锁的坑要警惕。最常见的是 semaphore 的 get 和 put 不配对,比如某个分支异常返回忘了 put,钥匙就永远少一把。建议用 try_get() 非阻塞获取,如果失败可以先做别的事。还有,避免多个进程按不同顺序申请多个 semaphore 的钥匙,比如进程 A 先拿锁 X 再拿锁 Y,进程 B 先拿锁 Y 再拿锁 X,就容易形成循环等待死锁。尽量让所有进程按固定全局顺序申请锁。
简单例子:一个内存模型被多个 agent 访问。
semaphore mem_sem = new(1);
在访问内存的任务里:
mem_sem.get(1);
// 读写内存操作
mem_sem.put(1);
用 fork join 多起几个并发访问线程,就能看到没有锁的时候数据会乱,有锁就顺序访问了。
