460 lines
13 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use tower_lsp::lsp_types::*;
/// 文档IEEE 1364-2005
/// author: LSTM-Kirigaya
/// date: 2024.12.13
pub const VLOG_DIRECTIVES: &[(&str, &str, &str)] = &[
// 1800-2009 22.13 `__FILE__ and `__LINE__
(
"__FILE__",
"`__FILE__",
r#"用于获取当前文件名
```verilog
// 文件名: /path/to/your/project/test.v
`timescale 1ns/1ps
module test;
initial begin
// 输出 当前文件名:/path/to/your/project/test.v
$display("当前文件名: %s", `__FILE__);
end
endmodule
```
"#),
(
"__LINE__",
"`__LINE__",
r#"用于获取当前行号
```verilog
// 文件名: /path/to/your/project/test.v
`timescale 1ns/1ps
module test;
initial begin
// 使用 __FILE__ 和 __LINE__ 宏输出当前文件名和行号
$display("当前文件名: %s, 当前行号: %0d", `__FILE__, `__LINE__);
end
endmodule
```
"#),
// 19.1 `celldefine and `endcelldefine
(
"celldefine",
"`celldefine\n$1\n`endcelldefine",
r#"开始定义单元格
`celldefine` 和 `endcelldefine` 是用于定义单元库模块的编译指令。它们通常用于定义可重用的硬件模块(如标准单元库),并控制这些模块的编译行为。
```verilog
// 使用 celldefine 和 endcelldefine 定义一个单元库模块
`celldefine
module my_cell (
input a,
input b,
output out
);
assign out = a & b; // 简单的与门逻辑
endmodule
`endcelldefine
// 测试模块
module test;
reg a, b;
wire out;
// 实例化单元库模块
my_cell uut (
.a(a),
.b(b),
.out(out)
);
initial begin
// 初始化输入
a = 0;
b = 0;
// 仿真逻辑
#10;
a = 1;
#10;
b = 1;
#10;
$display("输出结果: %b", out);
end
endmodule
```
使用 `celldefine` 的优势:
- 编译器可以将 `celldefine` 模块视为标准单元库的一部分,从而进行更高效的编译优化。例如,编译器可以提前处理这些模块的逻辑,减少仿真时的计算开销。
- 在大型设计中,标准单元库模块可能会被多次实例化。使用 celldefine 可以避免重复编译相同的模块,从而减少编译时间和内存占用。
"#),
(
"endcelldefine",
"`endcelldefine",
"结束单元格定义"
),
// 19.2 `default_nettype
(
"default_nettype",
"`default_nettype $1",
"设置默认的网络类型"
),
// 19.3 `define and `undef
(
"define",
"`define $1 $2",
r#"定义一个宏
### 语法
```verilog
`define MACRO_NAME value
```
### 示例
```verilog
// 使用 `define 定义常量和代码片段
`define PORT_NUM 8 // 定义常量 PORT_NUM 为 8
`define ADD_ONE(x) (x + 1) // 定义带参数的宏 ADD_ONE
module test;
reg [(`PORT_NUM - 1):0] data; // 使用常量 PORT_NUM
integer value;
initial begin
// 使用宏 ADD_ONE
value = 5;
$display("原始值: %0d", value);
value = `ADD_ONE(value);
$display("加一后的值: %0d", value);
// 使用常量 PORT_NUM
data = 8'hFF;
$display("数据宽度: %0d 位", `PORT_NUM);
$display("数据值: %h", data);
end
endmodule
```
"#),
(
"undef",
"`undef $1",
"取消定义一个宏"
),
// 1800-2009 22.5 `define, `undef and `undefineall
(
"undefineall",
"`undefineall",
"取消定义所有宏"
),
// 19.4 `ifdef, `else, `elsif, `endif, `ifndef
(
"ifdef",
"`ifdef $1\n\t//ifdef\n\t$2\n`else\n//else\n\t$3\n`endif",
r#"ifdef、else 和 endif 是条件编译指令,用于根据宏定义的存在与否来选择性地编译代码。它们通常用于实现条件编译逻辑,以便在不同的编译环境下生成不同的代码。
### 语法
```verilog
`ifdef MACRO_NAME
// 如果 MACRO_NAME 已定义,编译此段代码
`else
// 如果 MACRO_NAME 未定义,编译此段代码
`endif
```
### 示例
```verilog
// 使用 `ifdef 和 `else 进行条件编译
`define DEBUG // 定义 DEBUG 宏
module test;
reg [7:0] data;
initial begin
data = 8'hFF;
// 使用 `ifdef 和 `else 进行条件编译
`ifdef DEBUG
$display("调试模式: 数据值 = %h", data);
`else
$display("发布模式: 数据值 = %h", data);
`endif
end
endmodule
```
假设上述代码保存在文件 test.v 中,仿真输出可能如下:
```
调试模式: 数据值 = FF
```
"#
),
(
"else",
"`else",
"条件编译的 else 分支"
),
(
"elsif",
"`elsif $1",
"条件编译的 elsif 分支"
),
(
"endif",
"`endif",
"结束条件编译"
),
(
"ifndef",
"`ifndef $1",
"如果未定义宏,则编译代码"
),
// 19.5 `include
(
"include",
"`include $1",
"包含一个外部文件"
),
// 19.6 `resetall
(
"resetall",
"`resetall",
// resetall 也常用于开始
// 1834-2005中指出建议的用法是将 `resetall 放在每个源文本文件的开头,后面紧跟着文件中所需的指令。
// 通常可用于源文本开头,避免该文件受到其他文件代码的影响。或放置在末尾,避免编译器设置的进一步传递
r#"`resetall` 用于重置所有编译器设置为默认值。通常可用于源文本开头,避免该文件受到其他文件代码的影响。或放置在末尾,避免编译器设置的进一步传递。
1834-2005中指出建议的用法是将 `resetall 放在每个源文本文件的开头,后面紧跟着文件中所需的指令。
```verilog
`timescale 1ns/1ps // 设置时间单位和精度
module test;
// 一些 verilog 代码
endmodule
`resetall // 重置所有编译器设置,上文的 timescale 设置自此开始无效
```
"#
),
// 19.7 `line
(
"line",
"`line $1 $2 $3",
r#"`line` 用于指定当前代码的行号和文件名。它通常用于调试或日志记录,以便在仿真或编译时能够追踪代码的来源。
### 语法
```verilog
`line line_number "filename" level
```
### 示例
```verilog
// 使用 `line 指令指定行号和文件名
`line 10 "example.v" 0
module test;
initial begin
$display("当前行号: %0d", `__LINE__);
$display("当前文件名: %s", `__FILE__);
end
endmodule
```
"#
),
// 19.8 `timescale
(
"timescale",
"`timescale ${1:1ns}/${2:1ps}",
// 原文是The time_precision argument shall be at least as precise as the time_unit argument; it cannot specify a longer unit of time than time_unit.
// 也即是,仿真时间单位必须大于等于时间精度
// `timescale 1ns/1ns不会报错但是`timescale 1ps/1ns会报错
// [Synth 8-522] parsing error: error in timescale or timeprecision statement. <timeprecision> must be at least as precise as <timeunit>
r#"设置时间单位和精度,设置时间单位和精度,格式为 <code> `timescale </code> 仿真时间单位/时间精度,<code>仿真时间单位</code> 必须大于等于 <code>时间精度</code>,例如
```verilog
`timescale 1ns/1ps
module test;
initial begin
$display("当前时间单位: %0t", $time); // 输出当前时间
#10; // 延迟 10 纳秒
$display("延迟 10 纳秒后的时间: %0t", $time);
end
endmodule
```
如果延迟是 #10.5,仿真器会将其解释为 10.5 纳秒,但精度会精确到皮秒级别。
在编译过程中,`timescale` 指令影响这一编译器指令后面所有模块中的时延值,直至遇到另一个 `timescale` 指令 `resetall` 指令。
"#),
// 19.9 `unconnected_drive and `nounconnected_drive
(
"unconnected_drive",
"`unconnected_drive ${1:pull1}\n$2\n`nounconnected_drive\n",
r#"`nounconnected_drive` 和 `unconnected_drive` 是配合使用的编译指令,用于控制未连接端口的驱动行为。它们通常成对出现,分别用于禁用和启用未连接端口的驱动处理。
### 语法
```verilog
`unconnected_drive (pull1 | pull0) // 启用未连接端口的驱动,若为 pull1则默认驱动为高电平若为 pull0则默认为低电平
// 你的 verilog 代码
`nounconnected_drive // 禁用未连接端口的驱动
```
### 示例
```verilog
// 使用 `line 指令指定行号和文件名
`unconnected_drive pull1
module my_and(y, a, b);
output y;
input a, b;
assign y = a & b;
endmodule
module test(y,b);
output y;
input b;
my_and ul(y, ,b);
endmodule
`nounconnected_drive
```
"#
),
(
"nounconnected_drive",
"`nounconnected_drive",
r#"`nounconnected_drive` 和 `unconnected_drive` 是配合使用的编译指令,用于控制未连接端口的驱动行为。它们通常成对出现,分别用于禁用和启用未连接端口的驱动处理。
### 语法
```verilog
`unconnected_drive (pull1 | pull0) // 启用未连接端口的驱动,若为 pull1则默认驱动为高电平若为 pull0则默认为低电平
// 你的 verilog 代码
`nounconnected_drive // 禁用未连接端口的驱动
```
"#
),
// 19.10 `pragma
(
"pragma",
"`pragma $1",
"编译指示,用于传递编译器指令"
),
// 19.11 `begin_keywords, `end_keywords
(
"begin_keywords",
"`begin_keywords\n$1\n`end_keywords ",
// begin_keywords并不是指定Verilog的编译版本而是指定使用的Verilog关键字版本
r#"`begin_keywords` 和 `end_keywords` 是用于指定 Verilog 关键字版本的编译指令。它们允许设计者在同一个文件中使用不同版本的 Verilog 关键字语法,从而实现兼容性或逐步迁移到新版本。
`begin_keywords` 支持以下 Verilog 版本:
- `1364-1995`Verilog-1995 标准。
- `1364-2001`Verilog-2001 标准。
- `1364-2005`Verilog-2005 标准。
- `1800-2009`SystemVerilog-2009 标准。
- `1800-2012`SystemVerilog-2012 标准。
```verilog
// 使用 begin_keywords 和 end_keywords 指定 Verilog 版本
`begin_keywords "1364-2001" // 指定 Verilog-2001 语法
module my_module;
reg [7:0] data;
initial begin
data = 8'hFF; // 使用 Verilog-2001 的语法
$display("Verilog-2001 语法: data = %h", data);
end
endmodule
`end_keywords // 结束 Verilog-2001 语法
// 恢复默认的 Verilog 语法
module another_module;
reg [7:0] data;
initial begin
data = 8'hAA; // 使用默认的 Verilog 语法
$display("默认语法: data = %h", data);
end
endmodule
```
"#),
(
"end_keywords",
"`end_keywords",
"结束关键字版本指定"
),
// Annex D Compiler directives
(
"default_decay_time",
"`default_decay_time $1",
r#"`default_decay_time` 用于设置电荷衰减时间decay time的默认值。它通常用于模拟电荷存储元件如电容或寄生电容的行为。
### 语法
```verilog
`default_decay_time time_value
```
```
"#
),
(
"default_trireg_strength",
"`default_trireg_strength $1",
r#"`default_trireg_strength ` 用于设置三态寄存器trireg的默认驱动强度strength。三态寄存器是一种特殊的寄存器可以存储电荷并且在未被驱动时会衰减。"#
),
(
"delay_mode_distributed",
"`delay_mode_distributed",
r#"`delay_mode_distributed` 用于设置延迟模式的类型。它通常用于控制逻辑门和网络的延迟分布方式。"#
),
(
"delay_mode_path",
"`delay_mode_path",
r#"`delay_mode_path` 用于设置延迟模式的类型。它通常用于控制路径延迟path delay的计算方式特别是在时序逻辑电路中。"#
),
(
"delay_mode_unit",
"`delay_mode_unit",
r#"`delay_mode_unit` 用于设置延迟模式的类型。它通常用于控制逻辑门和网络的延迟计算方式,特别是在仿真中需要精确控制延迟时。"#
),
(
"delay_mode_zero",
"`delay_mode_zero",
r#"`delay_mode_zero` 用于设置延迟模式的类型。它通常用于控制逻辑门和网络的延迟计算方式,特别是在仿真中需要忽略延迟时。"#
),
];
fn make_function_profile(
description: &str
) -> MarkupContent {
MarkupContent {
kind: MarkupKind::Markdown,
value: format!("{}", description)
}
}
pub fn provide_vlog_directives_completions() -> Vec<CompletionItem> {
let mut completion_items: Vec<CompletionItem> = Vec::new();
for (label, snippet_code, description) in VLOG_DIRECTIVES {
let function_profile = make_function_profile(description);
let label_details = CompletionItemLabelDetails {
description: Some("directive".to_string()),
..Default::default()
};
completion_items.push(CompletionItem {
label: label.to_string(),
detail: Some("directive".to_string()),
label_details: Some(label_details),
documentation: Some(Documentation::MarkupContent(function_profile)),
kind: Some(CompletionItemKind::FUNCTION),
insert_text: Some(snippet_code.to_string()),
insert_text_format: Some(InsertTextFormat::SNIPPET),
insert_text_mode: Some(InsertTextMode::ADJUST_INDENTATION),
..CompletionItem::default()
});
}
completion_items
}