Welcome to my Blog!
x
Coding Style Notes:
parameter 代表的不是编码(encode),而是对寄存器相应 bit 的索引(index)。
(?)state & next_state
通常可以将输出逻辑移到下一级的输入逻辑。
?
?
- 异步 FIFO 不能使用计数方法,因为读写端口时钟域不同。
- 为了判断空/满状态,必须对读/写指针进行比较。
FIFO 指针常用格雷码计数,每个时钟边沿只会有 1 位变化,避免多 bit CDC 问题。
指针计数是否会在采样间隔中持续变化甚至上溢、下溢?答案是不会。
如果对二进制指针进行采样,并在两个时钟域之间使用握手信号来安全传递,则可以使用二进制指针设计 FIFO 。
优点:
缺点:
方法:
空 empty
满 full
almost empty:rptr+4 赶上 wptr
almost full:wptr+4 赶上 rptr

fifo1.v :顶层模块
fifomem.v :FIFO memory buffer,此处是一个双端口 RAM 。
sync_r2w.v :将 read pointer 同步到 write pointer 时钟域,仅包含触发器。
sync_w2r.v :将 write pointer 同步到 read pointer 时钟域,仅包含触发器。
wptr_full.v :生成 write pointer 和 full flag 。
rptr_empty.v :生成 read pointer 和 empty flag 。
n
n
fifo1.v :顶层模块
fifomem.v :FIFO memory buffer,此处是一个双端口 RAM 。
sync_cmp.v :生成异步的 afull_n 和 aempty_n 信号。
wptr_full.v :生成 write pointer 和 full flag 。
rptr_empty.v :生成 read pointer 和 empty flag 。
afull_n 和 aempty_n 信号。rempty 和 wfull 信号,同步释放。
wfull ,而不清除 rempty 。wptr 和 rptr 。direction 。aemtpy_n ,进而清除 rempty 。
亚稳态:由于不满足建立/保持时间,触发器在时钟边沿采样到变化中的信号,触发器输出信号在一段时间内不处于稳定的 0/1 状态,经过一段稳定时间 tMET 后恢复正常(可能是 0 也可能是 1 )。
亚稳态不能完全避免。
亚稳态会穿越后续电路,导致非法信号值传播到整个设计的其余部分。

将 CDC 信号在接收时钟域同步,是否就不需要在发送时钟域同步寄存?
若快时钟的频率至少是慢时钟的 1.5 倍,则信号一定会被采样 1-2 次,不存在漏采样的问题。
当发送时钟域频率高于接收时钟域,且 CDC 信号脉宽仅为发送时钟域的一个周期:
当发送的脉冲宽度稍大于接收时钟周期:
在超过采样时钟周期的一段时间内断言 CDC 信号,最小脉冲周期是采样时钟周期的 1.5 倍。
将使能控制信号同步到接收时钟周期,再将确认信号同步回发送时钟周期。未收到确认信号之前,发送信号不能改变。
发送时钟域同步输出的多个信号会存在数据变化偏差(skew),可能导致接收时钟域不能同时采样到正确信号。
两个控制信号存在小偏差,导致在接收时钟域的不同时钟沿被采样。
将 en load 合并成 lden 信号。
接收时钟沿可能恰好在两个相移控制信号之间,导致在接收时钟域的使能控制信号链中形成单周期间隙。
合并控制信号,在接收时钟域内生成第二相移流水线使能信号。
发送不同步的数据和同步的控制信号。
优点:
确认反馈信号 b_ack 生成确认脉冲 aack , aack 用作小型 READY-BUSY、1 状态 FSM 的输入,该模块生成就绪信号 aready 以指示现在可以安全地再次更改 adatain 数据和 asend 控制信号。
接收时钟域有一个 WAIT-READY、1 状态 FSM,当数据寄存器的输入上的数据有效时,它会向接收逻辑发送有效信号 bvalid 。在接收逻辑通过断言 bload 信号确认应加载数据之前,不会实际加载数据。在数据加载之前,不会向发送时钟域提供反馈。加载后才会将 b_ack 信号发送回。
对于计数器而言,跨时钟域传输时,通常不需要采样计数的每一个值(允许漏采样)。
二进制计数器在计数时会发生多位同时变化,可能由于数据偏差导致错误采样。
格雷码每次计数仅有一位发生变化,消除了多 bit CDC 问题。
Guidelines:组合逻辑电路用阻塞赋值,时序逻辑电路用非阻塞赋值。
忽略上述准则仍然可以综合出正确的电路,但仿真可能与实际电路的行为不匹配。
在不被任何其他 Verilog 语句中断的情况下,计算 RHS 并更新 LHS 。
当一个过程块中的 RHS 也是另一个过程块中的 LHS 时,并在同一个仿真时间执行,就会出现 Race Condition 。
非阻塞赋值并行执行。
非阻塞赋值仅用于 reg 类型,即只用于过程赋值块内。
<= 配。<= 。= 。<= 。= 和非阻塞赋值 <= 。$strobe 来打印非阻塞赋值 <= 的变量。#0 。
?
1 | module dff( |
Linear Feedback Shift-Register (LFSR)
使用 <=
1 | s |
(✗)在同一个 always 块中对同一个变量进行多次非阻塞赋值 <= 是未定义的行为
(✓)最后一个赋值语句胜出
(✗)$display() 对于非阻塞赋值的变量无效
(✓)非阻塞赋值在 $display() 之后生效,用 $strobe
(✗)#0 语句在每个 time step 的最后生效
(✓)#0 语句在每个 time step 的 Inactive 阶段生效
综合仍会推断出正确的电路(与门),但仿真行为与综合电路不符。
综合仍会推断出正确的电路(与或门),但仿真行为与综合电路不符。
函数会被综合为位组合逻辑,而如果函数中的代码对应锁存器,则综合前仿真视为锁存器,综合后仿真却视为组合逻辑,出现不匹配。
// synopsys full_case 表示所有可能的 case 都已经列出,未列出的都是“不关心”的。
en 信号被优化掉了。
// synopsys parallel_case 告诉综合工具应并行检验所有条件,即使存在重叠的条件。
重叠的条件平常会推导出优先级编码器,但在 parallel case 中会推导为无优先级。
z 的 case 条件优先级高于 y ,所以 y 的条件中必须排除与 z 重叠的部分,推导出优先级。
parallel case 中,各个条件无优先级,故各自 z 和 y 各自推导出 2 输入与门。
casex 将 x 态视作不关心。当信号被初始化为未知状态时,可能会导致 x 态传播,但 casex 不能检测到这样的问题。
en 因为某种原因变为 x 态,casex 仍会根据 addr 进行匹配、执行,掩盖了 x 态的问题。addr 也是一样。
casex ,最好使用 casez 。casez 将 z 态视作不关心,可能会导致 casex 一样的问题。
casez 于建模地址解码器和优先级编码器时非常有用,可以用 ? 去匹配。
x 初始化x 对于verilog仿真器是未知,对于综合工具是不关心(利于综合优化)。
x 可能导致仿真综合不匹配,但是在 FSM 设计中可能有用,可以在仿真时检测错误的状态转换。s == 2'b11 时产生不匹配,但是若 2'b11 是不期望达到的状态,可以用于仿真调试。
translate_on/translate_off 初始化?
translate_on/translate_off 的一般用法translate_on/translate_off 在打印信息的时候很有用,但用于建模功能却很危险。
rstn 和 setn 都拉低后,rstn 再拉高,此时 q 却未被置位。需要使用不可综合的构造才能完全与综合后的电路行为 100% 匹配。
在赋值左侧设置延时可能导致仿真与综合电路行为不匹配。
in 发生改变,经过 65 个时间单位才退出 always 块,期间 in 再次发生改变也不会再次进入 always 块,计时期间其他事件都被忽略。这显然与综合后电路的行为不符。
don't care 优化,则需要复位。always 块中都应用非阻塞赋值 <= 。always 中混合建模同步复位触发器和跟随触发器,导致 rst_n 被推导为跟随触发器的使能信号 ld 。
always 中描述。
d 输入逻辑云的一部分。d 之间必须穿越的逻辑。sync_set_reset 可以帮助推导这种触发器,但是需要注意前后仿不一致的问题。(?)
异步复位最大的问题在于复位释放。
Synopsys 要求,如果敏感列表中有任一个信号是边沿敏感的,则所有信号都必须是边沿敏感的。
复位缓冲树?
rst_n 和 set_n 都拉低后,rst_n 再拉高,此时 q 却未被置位:仿真与综合电路行为不匹配。synopsys translate_off/on 和不可综合的构造才能与综合后的电路行为 100% 匹配。
$recovery $removal $recrem由于复位信号到达不同触发器时间存在偏差,可能导致两个触发器在不同的时钟周期释放复位。
每个使用异步复位的 ASIC 都应该包括一个复位信号同步电路。
必须根据 clk-q 复位树的时序分析时钟树时序。
对于多时钟设计,每个时钟域都应该使用单独的复位同步电路和复位分配树,保证满足复位恢复时间。
(?)
对于一些设计,多个 ASIC 需要准确地在同一时间进行复位释放。可能受板布局、工艺、温度影响。
1 | gcc -E hello.c -o hello.i 预处理,生成.i文件(头文件、宏展开等) |
1 | all: target1 target2 target3 |
:= :直接赋值= :变量的值是整个Makefile中最后被指定的值?= :如果该变量没有被赋值,则赋值予等号后面的值+= :将符号后面的值添加到前面的变量上$(VAL) $< 代表第一个依赖文件$^ 表示所有的依赖文件$@ 表示生成的目标文件1 | SRC = $(wildcard *.c) # 通配符 |
clean)没有依赖文件,只有用make来调用时才会执行。clean同名的文件时,执行make clean就会出现错误,此时需要使用伪目标1 | .PHONY: clean |
makefile里执行的命令都会打印在终端,若不需要打印则在命令前加@
1 | clean: |
。。。
要使用GDB调试某个程序,编译时必须加上编译选项 **
-g**,否则程序不包含调试信息。
run(r) list(l) break(b) b FuncName or b FileName:FuncName :在(指定文件)函数入口添加断点b Line or b FileName:Line:在(指定文件)行号处添加断点b -/+offset :在当前程序暂停位置的前/后 offset 行处下断点info break or i b :显示当前所有断点信息delete(d)/disable/enable BreakPointNum :禁用/启用/删除断点until(u) continue(c) next(n) step(s) jump(j) print(p) watch backtrace(bt) frame(f) return finish(fi) help(h)| 命令名称 | 命令缩写 | 命令说明 |
|---|---|---|
| run | r | 运行程序 |
| list | l | 显示源码 |
| break | b | 添加断点 |
| delete | d | 删除某个断点 |
| enable | enable | 启用某个断点 |
| disable | disable | 禁用某个断点 |
| until | u | 运行到指定行停下来 |
| continue | c | 让暂停的程序继续运行 |
| next | n | 运行到下一行 |
| step | s | 单步执行,遇到函数会进入 |
| jump | j | 将当前程序执行流跳转到指定行或地址 |
| p | 打印变量或寄存器值 | |
| watch | watch | 监视某一个变量或内存地址的值是否发生变化,发生变化时GDB中断 |
| backtrace | bt | 查看当前线程的调用堆栈 |
| frame | f | 切换到当前调用线程的指定堆栈 |
| finish | fi | 执行到当前函数返回后停止,回到上一层调用函数处 |
| return | return | 立即中断当前函数并返回,到上一层函数调用处 |
| thread | thread | 切换到指定线程 |
| tbreak | tb | 添加临时断点 |
| info | i | 查看断点 / 线程等信息 |
| ptype | ptype | 查看变量类型 |
| disassemble | dis | 查看汇编代码 |
| set args | set args | 设置程序启动命令行参数 |
| show args | show args | 查看设置的命令行参数 |

logic、reg、net(wire)、integer、real、time、realtime
bit
byte、shortint、int、longint、shortreal
byte、shortint、int、longint 默认有符号
unsigned 表示无符号
使用 $isunknown() 可检查二态值是否出现 X 或 Z
1 | // cast |
访问数组越界地址将返回缺省值(四值为 X,二值为 0)。
很多仿真器采用 32 比特的字边界存放数组。
当需要用@等待数组的变化,则必须使用合并数组。
维度声明
int array [0:3][0:7]; or int array [4][8];
存放格式
bit[7:0] b_unpack[3];
初始化
int array [4] = '{0,1,2,3}
array = '{4{1}}
array = '{0,1,default:2}
array [0:2] = '{0,1,2}
对某些数据类型,有时候希望作为整体访问,有时候则希望分解为更小单元访问,可使用合并数组。
数组大小声明
bit [3:0] [7:0] bytes; (bit [size] 不可以)
存放格式
bit [3:0] [7:0] bytes;
bit [3:0] [7:0] barray[3];

1 | initial begin |
1 | initial begin |
定义
bit [3:0] d_array[];
分配大小、初始化
d_array = new[3]; d_array = '{0, 1, 2};
方法
1 | d_array.size() // return 0 if not created |
追加元素
1 | d_array = new [d_array.size() + N] (d_array); // 保留原有元素 |
定义
1 | enum {RED, YELLOW, GREEN} color_1; // 默认 int, RED = 0, YELLOW = 1, GREEN = 2 |
enum 命名不能以数字开头
方法
1 | typedef enum {RED, YELLOW, GREEN} colors; |
类型检查
1 | typedef enum bit[1:0] {RED, YELLOW, GREEN} colors; |
1 |
Definition
1 | // WrapCounter counts up to a max value based on a bit size |
Instance
1 | val x = new WrapCounter(2) |
1 | // These are normal functions. |
1 | def times2(x: Int): Int = 2 * x |
1 | /** Prints a triangle made of "X"s |
1 | val list1 = List(1, 2, 3) |
._1 takes the first element of tuple.
1 | (1, 2, 3)._2 // 2 |
1 | if (done) { |
1 | for (i <- 0 to 7) { print(i + " ") } // 0 - 7 |
package
1 | package mytools |
import
1 | import mytools.Tool1 |
- Variables are objects.
- Constants in the sense of Scala’s
valdeclarative are also objects.- Even literal values are objects.
- Even functions themselves are objects. More on this later.
- Objects are instances of classes.
- In fact, in just about every way that matters in Scala, the object in Objected Oriented will be called an instance.
- In defining classes, the programmer specifies:
- The data (
val,var) associated with the class.- The operations, called methods or functions, that instances of the class can perform.
- Classes can extend other classes.
- The class being extended is the superclass; the extendee is the subclass.
- In this case, the subclass inherits the data and methods from the superclass.
- There are many useful but controlled ways in which a class may extend or override inherited properties.
- Classes may inherit from traits. Think of traits as lightweight classes that allow specific, limited ways of inheriting from more than one superclass.
- (Singleton) Objects are a special kind of Scala class.
- They are not objects as above. Remember, we’re calling those instances.