vim 退出所有打开文件
:qa
systemC 变量的更新
Delta Cycle 的两个主要阶段
每个 Delta Cycle 通常分为以下两个阶段:
(1) Evaluate 阶段(评估阶段)
在这个阶段,仿真内核会执行所有当前时间点上被触发(或敏感)的进程。这些进程包括:
SC_METHOD:敏感于某些事件或信号的函数。
SC_THREAD 或 SC_CTHREAD:处于可运行状态的线程进程。
这些进程会根据当前的输入信号值计算输出,但在这个阶段,信号值的更新并不会立即生效,而是被暂存起来。
Evaluate 阶段是并发的,多个进程可能同时被触发,执行顺序是不确定的(依赖于调度器实现)。
(2) Update 阶段(更新阶段)
在 Evaluate 阶段完成后,仿真内核进入 Update 阶段。
这个阶段会将 Evaluate 阶段中计算出的信号值或状态更新应用到系统中。例如,信号的赋值操作(如 sig.write(value))会在此刻真正生效。
更新完成后,可能会触发新的敏感事件(例如,某个信号的值改变后,依赖该信号的进程被唤醒)。
循环执行
如果 Update 阶段触发了新的事件,仿真内核会返回到 Evaluate 阶段,重新执行被触发的进程。
这个过程会一直重复,直到当前时间点内没有新的进程被触发为止。这种循环就构成了一个完整的 Delta Cycle。
// Learn with Examples, 2020, MIT license
#include <systemc>
using namespace sc_core;
SC_MODULE(SIGNAL) {
sc_signal<int> s;
SC_CTOR(SIGNAL) {
SC_THREAD(readwrite);
}
void readwrite() {
s.write(3);
std::cout << "s = " << s << "; " << s.read() << std::endl;
wait(SC_ZERO_TIME);
std::cout << "after delta_cycle, s = " << s << std::endl;
s = 4;
s = 5;
int tmp = s;
std::cout << "s = " << tmp << std::endl;
wait(SC_ZERO_TIME);
std::cout << "after delta_cycle, s = " << s.read() << std::endl;
}
};
int sc_main(int, char*[]) {
SIGNAL signal("signal");
signal.s = -1;
sc_start();
return 0;
}
s = -1; -1: read() or operator = both returns current value of signal
after delta_cycle, s = 3
s = 3
after delta_cycle, s = 5
在 SystemC 中,sc_thread 中对变量的重新赋值是否立即生效,取决于变量的类型和赋值方式。以下是详细分析:
- 普通变量(非 SystemC 信号)
立即生效:普通 C++ 变量(如 int、bool、自定义类等)在 sc_thread 中的赋值会 立即生效。
但存在风险:如果多个线程或进程同时读写同一变量,可能导致 数据竞态(Data Race),因为 SystemC 的仿真内核是单线程调度,实际不存在硬件并发,但仍需注意逻辑顺序。
- SystemC 信号(sc_signal、sc_buffer)
不立即生效:对 SystemC 信号(如 sc_signal)的写入(write())操作会 延迟到当前 Delta Cycle 结束时生效。
触发事件:信号值变化会在下一个 Delta Cycle 触发 value_changed_event()。
Verdi的使用## 标题
https://www.bilibili.com/video/BV1mj411M7ir/?spm_id_from=333.337.search-card.all.click&vd_source=64c6505c375a29b1e861d999e41cb60f
Shilft +L 重新加载
g
1
.trace signal
2,看波形
添加信号: 先在代码中选中信号,然后ctrl+w 或者在波形窗口菜单栏现在get signal按钮添加。
移动信号: 选中后按鼠标中键来拖动
信号完整路径: 可以在波形窗口左侧信号窗按 h键来切换显示, h可以理解为hide隐藏完整路径
保存信号: 波形窗口上方选file中的save signal,保存信号为rc文件
恢复信号:

光标:可以按鼠标左键和中键来显示
数clk数量: 光标框住后 点击菜单栏上的view-> signal report来直接显示
信号变色: 选中信号,按t, 可以不停变换颜色
bus 的拆分和创建: 可以看单个bit,可以将多个bit合成bus value

3,看source code
查找信号: 直接按? 跳出对话框,不需要按shift
跳转到函数定义: 双击
前进或后退:

always不代码敏感列表加粗样式****
如果always没有敏感列表怎么办?
这个话题比较有意思,你可能说怎么可能没有敏感列表?其实,还真的可以没有敏感列表,这是仿真中的用法。我们经常使用没有敏感列表的always来表示不断的触发,用此特性来生成时钟。
例如:
always #10 clk = ~clk;
其实,always块内的敏感列表就是为了控制内部语句什么时候触发的。那么可以理解为一种定时,如果没有了敏感列表,则为零延迟,那么就会不断的触发,如下:
// always block is started at time 0 units
// But when is it supposed to be repeated ?
// There is no time control, and hence it will stay and
// be repeated at 0 time units only. This continues
// in a loop and simulation will hang !
always clk = ~clk;
上面显示的示例是一个always块,它试图反转信号clk的值。该语句每0个时间单位执行一次。因此,由于语句中没有延迟,因此它将永远执行。
这是没有意义的,我们正确的做法应该给一个定时或延迟:
即使敏感度列表为空,也应该有其他形式的时间延迟。always如下所示,通过构造中的延迟语句来提前仿真时间。现在,每10个时间单位完成一次时钟反转。
always #10 clk = ~clk;
当然,这种没有敏感列表的显示延迟,是不能综合的,只能用于仿真。
————————————————
Verilog移位存放数据
代码分析
478 for (i = 0; i < (data>>1); i = i + 1) begin
479 @(posedge CA[0]);
480 #0.1 CA_in_reg[7:0] <= {CA[1],CA_in_reg[7:1]};
481 @(negedge CA[0]);
482 #0.1 CA_in_reg[7:0] <= {CA[1],CA_in_reg[7:1]};
483 end
详细解释
循环条件:
for (i = 0; i < (data>>1); i = i + 1):
data>>1表示将data右移一位,相当于data除以2并取整。
循环将执行(data>>1)次。
上升沿操作:
@(posedge CA[0]):
等待CA[0]信号的上升沿。
#0.1 CA_in_reg[7:0] <= {CA[1],CA_in_reg[7:1]}:
等待0.1个时间单位。
将CA[1]的值移到CA_in_reg[7],并将CA_in_reg[7:1]的值左移一位。
下降沿操作:
@(negedge CA[0]):
等待CA[0]信号的下降沿。
#0.1 CA_in_reg[7:0] <= {CA[1],CA_in_reg[7:1]}:
等待0.1个时间单位。
将CA[1]的值移到CA_in_reg[7],并将CA_in_reg[7:1]的值左移一位。
代码行为
这段代码的目的是通过CA[0]信号的上升沿和下降沿将CA[1]的值逐位移入CA_in_reg寄存器中。具体来说,每次CA[0]的上升沿和下降沿都会将CA[1]的值左移一位,并将CA[1]的值移到CA_in_reg[7]。
Verilog event触发机制
声明
event var;
触发
->var;
捕获
@(var);
示例:
event occur;//定义了一个可以触发的事件
always@(occur)
a <= b;
initial begin
#10
-> occur;
end
以上代码就是等待10个时间单位后,触发事件occur,之后被always块捕获,将b赋值给a

1万+

被折叠的 条评论
为什么被折叠?



