一、Verilog数据类型

1.1 wire类型

wire类型用于表示硬件电路单元之间的物理连线。

  • 未声明的信号类型默认为wire
  • wire不允许在always块内部定义和赋值,但可以在always块使用非阻塞赋值<=取其数值
  • 常用在always块外部用assign连续赋值(硬件相连)

eg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
module d_flip_flop(		//以D触发器为例,输出信号有reg,也有wire,以说明其赋值操作的区别
input wire clk,
input wire rst_n,
input wire D, //wire型输入
output reg Q_reg, //reg型输出
output reg Q_wire //wire型输出
);

reg Q_0= 1'd0; //定义内部变量

always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin //复位
Q_reg <= 1'd1;
Q_0 <= 1'd0;
end
else begin
Q_reg <= D; //wire类型可以在always块中使用非阻塞赋值取其数值
Q_0 <= !D;
end
end

assign Q_wire = Q_0; //对wire类型的赋值需要在always块外部进行
endmodule

1.2 reg类型、integer整数、real实数

verilog中数值表达形式为:(位宽)’(进制)(数值) 比如【4’d1】。

  • reg 类型的变量只能在 alwaysinitial 中赋值,因此给reg变量赋初值一般在if(!rst_n) begin里面,或者在定义的时候直接赋值,reg [5:0] LED = 6'b000001;
  • reg类型不能在 assign 语句中赋值,因为那是给 wire 类型用的。
b 二进制
o 八进制
d 十进制
h 十六进制
1
2
3
reg d = 4'b0001;
real e = 3.14;
integer f = 3e2; //3*10^2=300

1.3 数组(一维向量、二维向量(类矩阵))

数组问题主要有:

  • 一维数组操作(赋值、按位赋值、拼接)
  • 二维数组操作(赋值、取值)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
module xxx(
input wire [7:0] a, //8位宽数组(一维向量)
output reg [15:0] b, //16位宽数组
output reg [7:0] c //8位宽数组
);
reg [7:0] d [3:0]; //定义4个8位宽数据 d[0]-d[3]

wire [7:0] d2_full;
wire bit_d3_5;
wire [7:0] d0_shifted;

//一维数组操作
always@(*) begin
//一维数组赋值
b = 16'b1111_1110_1101_1100; // a = 8'b1010_1010;这个是非法操作,input不能赋值

//一维数组的按位赋值
c = b[15:8]; // 截取b的高8位输出
c[7:1] = a[6:0]; // 截取赋值

//一维数组拼接
b = {a[7:0] , b[15:8]}; //b=16'b1010_1010_1111_1110
end

//二维数组操作
always@(*) begin
//二维数组赋值
d[0] = 8'b0000_0001;d[1] = 8'b0000_0010;d[2] = 8'b0000_0011;d[3] = 8'b0000_0100; //正常来说应该放到initial块中
end

//二维数组取值
assign d2_full = d[2]; //取d的第二个数据(8位)放到d2_full里
assign bit_d3_5 = d[3][5]; //取d的第三个数据的第五位,即0
assign d0_shifted = d[0] << 1; //将d[0]左移一位放到d0_shifted里

endmodule
1
2
3
4
5
6
7
    // ========== 二维数组初始化(initial 块) ==========
initial begin
d[0] = 8'b0000_0001;
d[1] = 8'b0000_0010;
d[2] = 8'b0000_0011;
d[3] = 8'b0000_0100;
end

1.4 parameter参数、localparam内部参数

parameter用于声明设计参数,是一个常量,不支持小数。当代码多次使用同一个参数时(比如计时器的计数上限值),一个个修改起来较为麻烦,可以通过定义需要修改的变量为 parameter 参数类型,例化模块时直接设置 parameter 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//子模块里面
module timer #(
parameter time_count = 26'd24_999_999, // 计数上限(常量,不能为小数)
parameter width = 8 // 数据位宽
)(
input wire clk,
...
);

//母模块里面例化
timer #(
.time_count (26'd24_999_99) //这里给子模块的parameter赋值
.width (8)
) timer_例化名( //后面的timer_lihua是例化名
.clk (clk),
...
);

此外还有另外一种用 defparam 进行参数设置的方法,例如例化同一模块使用不同的参数:

1
2
3
4
5
timer timer_例化名(     //先不例化,等后面用defparam例化     
.clk (clk),
...
)
defparam timer_l.time_count = 26'd24_999_999;

localparam 用于在模块内部定义常量,其值在模块实例化时是固定的,无法被修改。用法同parameter 一致。但是与相比 parameter 而言 localparam 更严格,因为它只在定义的模块内部有效,不会影响外部模块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module xxx(
input wire clk,
...
);

// ======= localparam 通常放在端口声明之后,逻辑代码之前 =======
localparam time_count = 26'd24_999_999; // 计数上限(仅模块内部使用)
localparam width = 8; // 数据宽度

// 后续逻辑可以使用 time_count 和 width
reg [width-1:0] counter;

// ...
endmodule

二、运算符

2.1 算数运算符

加:+ 减:- 乘:* 除:/ 取余:%

取余运算两侧数据必须是整数数据。

2.2 赋值运算符(阻塞赋值与非阻塞赋值)

1
2
3
4
5
= : 阻塞赋值	//前面赋值语句会阻塞后面的语句,按照顺序运行
<= : 非阻塞赋值 //所有非阻塞赋值会同时进行

注意:所有阻塞赋值与非阻塞赋值左边必须是变量数据类型,可以是regintegerreal。右边可以是任何有效的表达式或信号
此外还有连续赋值(连线)assign:用于表示组合逻辑,左侧必须是wiretri,右侧值变化,左侧数值立即更新。

2.3 比大小运算符

1
2
3
4
5
6
a < b  :a小于b
a > b :a大于b
a == b :a等于b
a != b :a不等于b
a <= b :a小于等于b
a >= b :a大于等于b

2.4 逻辑运算符

1
2
3
&&:逻辑与:a&&b,a和b同时为真时才为真,否则为假
||:逻辑或:a||b,a和b同时为假时才为假,否则为真
! :a为真时,!a为假

2.5 条件运算符

1
2
3
4
5
a = (条件) ? b : c;	//条件为真:a=b,条件为假:a=c
[eg: a = (b <= c) ? 1'b1 : 1'b0; //b小于等于c时,a=1,否则,a=0

比较常用的条件运算符使用情况:
assign a = (b) ? 4'b1 : 4'b0;

2.6 位运算符

1
2
3
4
5
~ : 按位取反	//a=1001 ~a=0110
& : 按位与 //a=1001 b=0001 a&b=0001
| : 按位或 //a=1001 b=0001 a|b=1001
^ : 按位异或xor 两个输入值不同时,输出1,否则输出0,即同为0,异为1 //a=1001 b=0001 a^b=1000
^~ : 按位同或xnor 两个输入值相同时,输出1,否则输出0,即同为1,异为0 //a=1001 b=0001 a^~b=0111

2.7 移位运算符

1
2
3
4
5
6
reg [3:0] a,c;
reg [5:0] b;
a = 4'b1001;
b = a<<2;
c = a>>2;
a = a<<2;

2.8 归约运算符(实现奇偶校验)

1
2
3
4
5
6
归约的操作数只有一个,实现该操作数所有位的运算,得到结果为一位
& : 归约与 a=1101 b = &a (b = 1 & 1 & 0 & 1 = 0
| : 归约或 a=1101 b = |a (b = 1 | 1 | 0 | 1 = 1
^ : 归约异或 a=1101 b = ^a (b = 1 ^ 1 ^ 0 ^ 1 = 1) 从高位开始往低位异或

(归约异或可简单实现奇偶校验的逻辑)

三、语法与语句

3.1 always语句

  • 组合逻辑:always@(*) 或 assign
  • 时序逻辑:always@(posedge clk or negedge rst_n)

3.2 if语句

1
2
3
4
5
6
7
8
9
10
11
12
if (条件1) begin
执行1;
end
else if (条件2) begin
执行2;
end
else if (条件3) begin
执行3;
end
else begin //一定要加else begin,防止综合生成latch锁存器
执行保护语句;
end

3.3 case语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
case(sel)
2'd0 : begin
dout = a;
end
2'd1 : begin
dout = b;
end
2'd2 : begin
dout = c;
end
2'd3 : begin
dout = d;
end
default : begin //其他情况,条件列完写default(里面可写复位操作防止出错)
dout = 0;
end
endcase
//如果执行内容一样可以使用逗号(,)省略写。 eg: 2'd0, 2'd1 : dout = c

3.4 for语句

1
2
3
4
5
6
7
8
9
10
11
12
13
integer i,j;需要先声明整型变量i,j

------------------------------------------普通循环
for (i=0; i<2; i=i+1) begin (循环变量赋初值; 执行条件; 循环变量自增/减)
循环体
end

------------------------------------------嵌套循环
for (j=0; j<2; j=j+1) begin
for (i=0; i<2; i=i+1) begin
循环体
end
end

注意:如果一个语句块有两个 for 循环(比如一个 if 语句 的 begin end内),需要使用不同的循环变量分开(如 i 和 j),但是如果两个for循环若分别在 if 和 else if 中就没问题:

1
2
3
4
5
6
7
8
9
10
11
12
if (条件1) begin
integer i;
for (i = 0; i < 4; i = i + 1) begin
$display("Block A: %d", i);
end
end
else if (条件2) begin
integer i;
for (i = 0; i < 4; i = i + 1) begin
$display("Block B: %d", i); //作用域分开,不冲突
end
end

3.5 typedef、enum、logic语句

  • 这三个语句只能用于.sv文件,即SystemVerilog,.v文件用不了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
typedef:类型重定义关键字 //作用:用于给一个类型取一个别名。
eg:
typedef enum logic [1:0] { ... } state_t; //将一个枚举类型的逻辑值enum logic [1:0]定义成一个新的类型名,叫做state_t,凡是使用state_t进行定义的变量,其取值是一个列表,这个列表即为枚举类型的logic [1:0],类似于单片机的typedef。
state_t current_state, next_state;
———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
enum:枚举类型 // 作用:用于定义一组离散的、有名字的常量值,常用于状态机状态描述。
eg:
enum logic [1:0] {
IDLE = 2'd0,
S1 = 2'd1,
S2 = 2'd2
}
//这里就定义了三个状态,分别命名为 IDLE、S1、S2,它们的值分别是 2'd0、2'd1、2'd2。
//相比传统的 localparam 写法,enum 语义更加清晰,表达的是一组状态集合,易读性和可维护性更高。
———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
logic:新型变量类型 //作用:表示一个可以被多个驱动源控制的逻辑值,替代 Verilog 中的 reg 类型。
eg:
logic [1:0] //等价于传统 Verilog 中的:reg [1:0]
//但 logic 是 SystemVerilog 推荐使用的现代写法,它避免了 reg 在组合逻辑中使用带来的语义混乱,更加通用、规范。
———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
综合起来:
typedef enum logic [1:0] {
IDLE = 2'd0,
S1 = 2'd1,
S2 = 2'd2
} state_t;
state_t current_state, next_state;
//可以翻译为:我定义了一个名字叫 state_t 的类型,它是一个 2 位宽的逻辑枚举类型,表示状态集合:IDLE、S1、S2。

四、三段式状态机

4.1 状态机结构简介

状态机一般有三种编码方式:

  • 一段式状态机
    状态定义、状态转移、输出逻辑全部写在一个 always 块中,代码简单,但不利于维护和综合优化。
  • 二段式状态机
    状态转移与输出逻辑分开写,有两个 always 块,适合纯组合输出的场景。
  • 三段式状态机(推荐使用)
    状态寄存、状态转移、输出逻辑三块分开写,结构清晰,是最常用的写法,也是企业开发中最推荐的规范写法

4.2 三段式状态机思路

三段式状态机包含以下三部分:

  1. 第一段:状态寄存(时序逻辑)
    负责状态的寄存,用于记录当前状态(current_state)。
  2. 第二段:状态转移逻辑(组合逻辑)
    根据输入和当前状态决定下一状态(next_state)。
  3. 第三段:输出逻辑(组合逻辑 或 时序逻辑)
    根据当前状态和输入生成对应输出。

4.3 三段式状态机 Verilog 示例

功能描述:
设计一个有限三段式状态机,输入 in,当检测到连续两个 1 时输出 out=1,否则输出 0

  • 子模块定义:fsm_detector.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
module fsm_detector (
input wire clk,
input wire rst_n,
input wire in,
output reg out
);

// 状态编码(使用 localparam)
localparam IDLE = 2'd0;
localparam S1 = 2'd1;
localparam S2 = 2'd2;

// 状态寄存器定义
reg [1:0] current_state, next_state;

// 第一段:状态寄存(同步时序)
always @(posedge clk or negedge rst_n) begin
if (!rst_n)
current_state <= IDLE;
else
current_state <= next_state;
end

// 第二段:状态转移逻辑(组合逻辑)
always @(*) begin
case (current_state)
IDLE: next_state = (in == 1'b1) ? S1 : IDLE;
S1: next_state = (in == 1'b1) ? S2 : IDLE;
S2: next_state = (in == 1'b1) ? S2 : IDLE;
default: next_state = IDLE;
endcase
end

// 第三段:输出逻辑(组合逻辑 或 时序逻辑)
always @(*) begin //这里也可以改成(posedge clk or negedge rst_n)
case (current_state)
S2: out = 1'b1;
default: out = 1'b0;
endcase
end

endmodule
时钟周期 Current State Input in Next State Output out
1 IDLE 1 S1 0
2 S1 1 S2 0
3 S2 0 IDLE 1
4 IDLE 0 IDLE 0
5 IDLE 1 S1 0
6 S1 1 S2 0
7 S2 1 S2 1
8 S2 0 IDLE 1

logic_chart

五、testbench仿真模块

5.1 语法问题

testbench之前会将之前所有代码看做成一个模块,这个模块里面会层层套很多模块,但是最终形成最大的那个模块会只有n个输入in,和m个输出out,testbench里,我们会将这n个输入都伺候好(一般使用reg来给赋值),并将m个输出都接出来(一般使用wire类型来接),最终通过仿真时序图来观察代码逻辑写的是否正确。

下面是testbench常用的架构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
`timescale 1ns / 1ps           //timescale 时间单位/精度

module tb_module_name(); //通常无输入无输出

1. 声明信号(wire/reg//input对应reg型、output对应wire型

2. 实例化待测模块 //例化

3. 产生时钟

4. 初始化输入信号与开始仿真
initial begin

$finish;
end

endmodule

testbench区别于普通.v和.sv文件,常用tb_xxx.v来命名,其也有一些特殊的语法,常用的有:

指令 功能说明
$display() 单次打印
$monitor() 自动追踪变量变化打印
$time 获取当前仿真时间(64位int型)
$finish 停止仿真
$stop 暂停仿真(可调试)
$dumpfile/$dumpvars 波形输出(配合 GTKWave)
forever =while(1) 会一直执行内部语句
$random/$random %n/{$random} %n 产生随机数/产生-n到n的随机数/产生0到n的随机数
$readmemh/$readmemb 读取文件16进制数据/读取文件2进制数据
eg:$readmemh(“D:/mem.dat”,memory); 读.dat文件并放到reg型memory中

在这之后,还有一些常用的进阶仿真技巧语法:

  1. 自动打印状态值:
1
2
3
initial begin
$monitor("time=%0t | clk=%b rst_n=%b in=%b out=%b", $time, clk, rst_n, in, out);
end
  1. 文件输出波形:
1
2
3
4
initial begin
$dumpfile("waveform.vcd"); // 指定生成的波形文件名(必须是 .vcd 格式)
$dumpvars(0, tb_module_name); //指定要记录的信号层次(0 表示当前模块及其所有子模块)
end

​ 生成后使用命令打开:

1
gtkwave waveform.vcd
  1. for循环生成输入激励
1
2
3
4
5
6
7
8
9
10
11
12
13
14
integer i;
reg [7:0] input_seq = 8'b11001110;

initial begin
rst_n = 0; in = 0;
#20 rst_n = 1;

for (i = 7; i >= 0; i = i - 1) begin //这个for循环可以放到initial begin里面
in = input_seq[i];
#10;
end

$finish;
end
  1. 两种生成50%占空比的时钟
1
2
3
4
5
6
7
8
9
10
11
12
13
第一种:使用forever语句
initial begin
begin
clk_i=0;
forever #(clk_period/2) clk=~clk;
end
end

第二种:使用always
initial begin
clk=0;
end
always #(clk_period/2) clk=~clk;
  1. 使用 $readmemh 读取文件数据(激励文件驱动)
1
2
3
4
reg [7:0] mem [0:15];
initial begin
$readmemh("input_data.txt", mem); //读取input_data的数据放到mem矩阵(二维向量)里
end

5.2 代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
`timescale 1ns / 1ps           //timescale 时间单位/精度

module tb_module_name;

// 1. 声明信号(wire/reg)
reg clk;
reg rst_n;
reg in;
wire out;

// 2. 实例化待测模块
xxx u_xxx (
.clk(clk),
.rst_n(rst_n),
.in(in),
.out(out)
);

// 3. 产生时钟
initial begin
clk = 0;
forever #5 clk = ~clk; // 10ns 时钟周期
end

// 4. 初始化输入信号与测试激励
initial begin
rst_n = 0;
in = 0;
#20;
rst_n = 1;

#10 in = 1;
#10 in = 1;
#10 in = 0;
#10 in = 0;
#10 in = 1;
#10 in = 1;
#10 in = 1;
#10 in = 0;
#10;

$finish;
end

//进阶
// 打印信号变化
initial begin
$monitor("Time: %0t | in=%b | out=%b", $time, in, out);
end

// 输出波形文件
initial begin
$dumpfile("waveform.vcd");
$dumpvars(0, tb_fsm_detector);
end

endmodule

5.3 注意事项

建议 原因
永远不要在 Testbench 中用 always @ 写激励 会影响模拟流程,推荐全部写在 initial
分时序写激励,每段之间 # 模拟时间推进,需要 # 控制节拍
使用 $monitor 跟踪变量变化 方便调试输出
建议所有信号初值在仿真前显式赋值(都要尽量赋初值) 防止仿真中出现 x 状态
注意事项
`timescale 接时间单位/精度,#10即表示延时10个时间单位,支持计算形式:#(2*5)
仿真模块一般没有端口列表(input/output),通常输入信号为自定义为reg类型,而输出则定义为wire类型
模块例化:仿真模块相当于顶层文件,将待仿真模块例化后再进行验证,例化名可自定义

六、xdc文件语法

6.1 时钟语法

1
2
3
create_clock -name <名称> -period <周期> [get_ports <端口名>]

eg: create_clock -name sys_clk -period 10 [get_ports PL_CLK_P]

这句是时序分析的基础设置,告诉 Vivado 工具:PL_CLK_P这个端口是一个时钟源,且时钟频率为100MHz(周期10ns)。

只需对正向时钟(如 PL_CLK_P)写一次 create_clock,负向脚自动识别。

进阶篇(未完善):时序约束与时钟分析

  1. 时钟相关属性
  • create_generated_clock
  • set_clock_groups -asynchronous
  • set_clock_latency, set_clock_uncertainty
  1. 输入/输出时序约束
  • set_input_delay / set_output_delay
  • set_input_jitter, set_input_transition
  • 对 IO 标准与板级延迟的建模
  1. 路径约束(路径排除/限定)
  • set_false_path
  • set_max_delay / set_min_delay
  • set_multicycle_path

6.2 绑定引脚

1
2
3
set_property PACKAGE_PIN <引脚名> [get_ports <端口名>]

eg: set_property PACKAGE_PIN F10 [get_ports {LED[0]}]

绑定引脚是为了将 Verilog 中定义的逻辑端口(v语言定义的)绑定到实际芯片封装上的物理管脚,这是使信号真正进入/输出芯片的关键步骤,没绑定=信号进不来/出不去。

命令名 作用对象 使用阶段 常见用途 示例
get_ports 顶层设计的端口(Verilog 中的 input/output) 综合前(前端约束) 引脚绑定、IO 电压、时钟创建等 get_ports clk
get_cells 设计中的逻辑单元(触发器、模块实例) 综合后(后端约束) 锁定资源、设置属性 get_cells u_led_reg
get_nets 网络连接(连线) 综合后 分析路径、添加布线或时序约束 get_nets clk_net

6.3 IO电平标准定义

1
2
3
4
5
6
7
8
9
10
11
set_property IOSTANDARD <标准名> [get_ports <端口名>]

eg1:单端接口
set_property IOSTANDARD LVCMOS18 [get_ports {LED[0]}]

eg2:差分接口
set_property IOSTANDARD DIFF_SSTL12 [get_ports clk_p]
set_property IOSTANDARD DIFF_SSTL12 [get_ports clk_n]

eg3:同时约束多个端口
set_property IOSTANDARD LVCMOS18 [get_ports {LED[*]}]
名称 电压(V) 差分 用途说明
LVCMOS18 1.8 常用于普通 GPIO,常见于 1.8V 供电系统
LVCMOS33 3.3 通用低速 IO,比如按钮、LED
LVCMOS25 2.5 适用于 2.5V 供电外设
SSTL15 1.5 DDR 控制器,单端 DDR 信号
SSTL18 1.8 DDR2 控制器,非差分使用
SSTL15_II 1.5 类似 SSTL15,带双边驱动
HSTL_I 1.5 高频信号通信
HSTL_II 1.5 高速 SDR/DDR 信号
DIFF_SSTL12 1.2 DDR3/DDR4 差分时钟/命令信号
DIFF_HSTL_I 1.5 差分高速 IO
DIFF_HSTL_II 1.5 DDR 或高性能 ADC 差分信号
LVDS ~1.2 低压差分信号,适合高速数据传输(如 ADC/DAC)
SUBLVDS ~1.2 亚 LVDS,功耗更低,常用于图像传感器
TMDS_33 3.3 HDMI/DVI 数字视频信号
MIPI_DPHY_DCI 1.2 MIPI CSI/DSI 差分接口,用于摄像头、屏幕

6.4 IO口控制

属性名 功能 常用取值 建议用法
PULLUP / PULLDOWN 上/下拉电阻 true / false 输入悬空或按键使用
SLEW 信号边沿速率 FAST / SLOW 高速信号用 FAST,排线建议 SLOW
DRIVE 驱动能力(电流) 2~24(mA) 驱动 LED 或高阻输入
OUTPUT_IMPEDANCE 输出阻抗匹配 50 / 75 等 高速信号布线匹配
DIFF_TERM 差分终端匹配电阻 TRUE / FALSE 差分输入时使用
  1. PULLUP / PULLDOWN

    1
    2
    3
    eg:
    set_property PULLUP true [get_ports btn]
    set_property PULLDOWN true [get_ports rst_n]

    PULLUP:给 IO 口内部接一个上拉电阻(连接到高电平)**
    PULLDOWN:给 IO 口内部接一个下拉电阻(连接到低电平)
    一般在按键输入、复位信号会加PULLUP,将该引脚默认为高电平。

  2. SLEW

    1
    2
    3
    eg:
    set_property SLEW FAST [get_ports {led[0]}]
    set_property SLEW SLOW [get_ports {data[*]}]

    控制输出信号转换速率,一般是 FASTSLOW,低速信号用SLOW,高速信号用FAST。

  3. DRIVE

    1
    2
    eg:
    set_property DRIVE 12 [get_ports led[0]]

    控制 IO 输出时能够提供的电流大小,常见值:2, 4, 6, …, 24 mA,单位是mA。一般驱动较大电流例如LED,选12或16。驱动越大,功耗越高、噪声越大,不是越大越好。某些 IOStandard(如 LVDS)不支持 DRIVE,设置会报错。

  4. OUTPUT_IMPEDANCE

    1
    2
    eg:
    set_property OUTPUT_IMPEDANCE 50 [get_ports clk_out]

    用于设置IO输出的特性阻抗,配合 PCB 匹配阻抗,减少反射,提高信号完整性。

    适用于高速差分信号(如 TMDS、LVDS)等,大多数情况下可以忽略,由布线匹配完成。

  5. DIFF_TERM

    1
    2
    eg:
    set_property DIFF_TERM TRUE [get_ports clk_p]

    在 FPGA 芯片内部的两个差分引脚之间插入一个 100Ω 匹配电阻,主要用于接收差分时钟、LVDS 信号等。

    如果板上已经接了 100Ω 匹配电阻,就不要再设置 DIFF_TERM