增加对于 xilinx IP 的 entity 内部 port 的解析

This commit is contained in:
锦恢 2024-11-11 02:06:41 +08:00
parent b14f8bd17f
commit f055b2bbc3
4 changed files with 115 additions and 39 deletions

View File

@ -1,5 +1,6 @@
use std::collections::HashSet;
use std::path::PathBuf;
use log::info;
use vhdl_lang::ast::DesignFile;
use vhdl_lang::{kind_str, Token, VHDLParser, VHDLStandard};
@ -67,7 +68,6 @@ pub fn make_fast_from_design_file(design_file: &DesignFile) -> Option<FastHdlpar
}
#[allow(unused)]
fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
let mut modules = Vec::new();
let mut last_module_name = String::new();
@ -81,24 +81,31 @@ fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
let start_pos = tokens[i].pos.range.start;
i += 1;
let entity_name = get_value(&tokens[i]);
// println!("entity name {:?}", entity_name);
if (i >= 2 as usize) && (kind_str(tokens[i-2].kind) != "use") || (i < 2) {
if (i >= 2 as usize) && (kind_str(tokens[i - 2].kind) != "use") || (i < 2) {
// 遍历整个 entity 内部的 token找到结束的 token 和代表 port 的 token
let mut end = i;
let mut port_begin: usize = 0;
while (
end+1 < tokens.len()) &&
end + 1 < tokens.len()) &&
!(kind_str(tokens[end].kind) == "end" &&
(kind_str(tokens[end+1].kind) == "entity" || get_value(&tokens[end+1]) == entity_name)) {
(kind_str(tokens[end + 1].kind) == "entity" || get_value(&tokens[end + 1]) == entity_name)
) {
if kind_str(tokens[end].kind) == "port" {
port_begin = end;
}
end += 1;
}
let end_pos = if end+1 < tokens.len() && get_value(&tokens[end+1]) == entity_name {
i = end + 1;
tokens[end+2].pos.range.end
let (next_i, end_pos) = if end + 1 < tokens.len() && get_value(&tokens[end + 1]) == entity_name {
(end + 1, tokens[end + 2].pos.range.end)
} else if end + 3 < tokens.len() {
i = end + 2;
tokens[end+3].pos.range.end
(end + 2, tokens[end + 3].pos.range.end)
} else {
tokens[end].pos.range.end
(i, tokens[end].pos.range.end)
};
// 创建一个空的 module 并标记上 last_module_name
let module = Module {
name: entity_name.to_string(),
params: Vec::new(),
@ -117,6 +124,14 @@ fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
};
last_module_name = entity_name.to_string();
modules.push(module);
// 如果内部含有 port则不能直接跳过否则直接跳过
if port_begin > 0 {
i = port_begin;
continue;
} else {
i = next_i;
}
}
}
"architecture" => {
@ -289,7 +304,7 @@ fn parse_tokens(tokens: Vec<Token>) -> Vec<Module> {
let start = params.first().unwrap().range.start.clone();
let end = params.last().unwrap().range.start.clone();
if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {
module.instances.last_mut().unwrap().instparams = Some(Range { start, end });;
module.instances.last_mut().unwrap().instparams = Some(Range { start, end });
}
} else {
if let Some(module) = modules.iter_mut().find(|module| module.name == last_module_name) {

View File

@ -322,7 +322,7 @@ pub fn hover_module_declaration(
#[allow(unused)]
language_id: &str
) -> Option<Hover> {
// info!("token: {:?}, module info: {:?}", token_name, module_info);
// info!("hover_module_declaration token: {:?}", token_name);
// let test = server.srcs.hdl_param.module_name_to_path.read().unwrap();
// info!("module name to path: {:#?}", test);
@ -429,7 +429,60 @@ fn hover_ip_module_declaration(
#[allow(unused)]
def_path: &str
) -> Option<Hover> {
None
let def_path_buf = PathBuf::from_str(def_path).unwrap();
if def_path_buf.exists() {
// TODO: 当前工具链只支持 Xilinx 下的工具链,所以此处的代码是 vhdl 的代码
let path_uri = Url::from_file_path(def_path.to_string()).unwrap().to_string();
let def_row = module.range.start.line;
let def_col = module.range.start.character;
let define_info = format!("Go to [Definition]({path_uri}#L{def_row}:{def_col})");
let port_num = module.ports.len();
let param_num = module.params.len();
let instance_num = module.instances.len();
let port_desc = format!("`port` {port_num}, `generic` {param_num}, `instantiation` {instance_num}");
// 统计 dir
let mut input_count = 0 as u32;
let mut output_count = 0 as u32;
let mut inout_count = 0 as u32;
for port in &module.ports {
match port.dir_type.as_str() {
"input" => input_count += 1,
"output" => output_count += 1,
"inout" => inout_count += 1,
_ => {}
}
}
let io_desc = format!("`input` {input_count}, `output` {output_count}, `inout` {inout_count}");
let mut markdowns = Vec::<MarkedString>::new();
markdowns.push(MarkedString::String(port_desc));
markdowns.push(MarkedString::String(io_desc));
markdowns.push(MarkedString::String(define_info));
markdowns.push(MarkedString::String("---".to_string()));
let language_id = get_language_id_by_path_str(def_path);
let module_profile = make_module_profile_code(&module);
let profile_markdown = LanguageString {
language: language_id.to_string(),
value: module_profile
};
markdowns.push(MarkedString::LanguageString(profile_markdown));
let hover = Hover {
contents: HoverContents::Array(markdowns),
range: None
};
Some(hover)
} else {
None
}
}
fn hover_primitives_module_declaration(

View File

@ -227,38 +227,46 @@ fn do_vhdl_fast(
// TODO: 支持对于 synth 下的 vhdl 文件的解析,从而提供更加丰富的 IP 支持
if file_type == "ip" {
match tool_chain {
// 此时的 pathbuf 类似 {ip_name}/synth/{ip_name}.vhd
"xilinx" => {
let ip_name = pathbuf.file_name().unwrap().to_str().unwrap();
let ip_name = ip_name.strip_suffix(".vhd").unwrap();
info!("get ip_name: {}", ip_name);
let fake_content = vec![
core::hdlparam::Module {
name: ip_name.to_string(),
params: vec![],
ports: vec![],
instances: vec![],
range: core::hdlparam::Range::default()
}
];
let ip_fast = core::hdlparam::FastHdlparam {
fast_macro: core::hdlparam::Macro {
includes: vec![],
defines: vec![],
errors: vec![],
invalid: vec![]
},
file_type: "ip".to_string(),
content: fake_content
};
hdl_param.update_fast(path.to_string(), ip_fast.clone());
return Ok(ip_fast);
// 如果 ip 描述文件存在,则解析它,否则,创建空的写入
// IP 描述文件一般都不会很大,所以不需要缓存
if !pathbuf.exists() {
let ip_name = pathbuf.file_name().unwrap().to_str().unwrap();
let ip_name = ip_name.strip_suffix(".vhd").unwrap();
let fake_content = vec![
core::hdlparam::Module {
name: ip_name.to_string(),
params: vec![],
ports: vec![],
instances: vec![],
range: core::hdlparam::Range::default()
}
];
let ip_fast = core::hdlparam::FastHdlparam {
fast_macro: core::hdlparam::Macro {
includes: vec![],
defines: vec![],
errors: vec![],
invalid: vec![]
},
file_type: "ip".to_string(),
content: fake_content
};
hdl_param.update_fast(path.to_string(), ip_fast.clone());
return Ok(ip_fast);
}
},
_ => {}
}
}
// 没有特殊情况,则正常解析并写入
if let Some(design_file) = vhdl_parse(&pathbuf) {
if let Some(mut fast) = make_fast_from_design_file(&design_file) {
if path.contains("ip") {
info!("vhdl fast: {:?}", fast);
}
fast.file_type = file_type.to_string();
hdl_param.update_fast(path.to_string(), fast.clone());
return Ok(fast);

View File

@ -230,7 +230,7 @@ impl LanguageServer for Backend {
self.client
.log_message(MessageType::INFO, "Digital LSP initialized!")
.await;
// self.client.send_notification::<StringNotification>(StringNotification { content: "hello from lsp server".to_string() }).await;
}