xilinx IP 支持 | 增加全局 conf 属性
This commit is contained in:
parent
f055b2bbc3
commit
cbddc07bdc
@ -1,9 +1,10 @@
|
||||
use std::{collections::HashMap, fs::{self}, hash::{DefaultHasher, Hash, Hasher}, path::PathBuf, sync::{Arc, RwLock}};
|
||||
use std::{collections::HashMap, fs::{self}, hash::{DefaultHasher, Hash, Hasher}, path::PathBuf, str::FromStr, sync::{Arc, RwLock}};
|
||||
|
||||
use log::info;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tower_lsp::lsp_types::Url;
|
||||
|
||||
use crate::utils::{file_size_in_kb, get_last_modified_time, k_deserialize, k_serialize};
|
||||
use crate::utils::{file_size_in_kb, get_last_modified_time, k_deserialize, k_serialize, to_escape_path};
|
||||
|
||||
use super::hdlparam::FastHdlparam;
|
||||
|
||||
@ -59,7 +60,17 @@ impl CacheManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_big_file(&self, path: &PathBuf) -> bool {
|
||||
pub fn is_big_file(path: &PathBuf) -> bool {
|
||||
if let Ok(size) = file_size_in_kb(path.to_str().unwrap()) {
|
||||
return size >= 1024;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn uri_is_big_file(uri: &Url) -> bool {
|
||||
let path = PathBuf::from_str(uri.path()).unwrap();
|
||||
let path = to_escape_path(&path);
|
||||
if let Ok(size) = file_size_in_kb(path.to_str().unwrap()) {
|
||||
return size >= 1024;
|
||||
}
|
||||
@ -83,7 +94,7 @@ impl CacheManager {
|
||||
/// * 如果本来就没有缓存
|
||||
/// * 缓冲的 version 对不上
|
||||
pub fn try_get_fast_cache(&self, path: &PathBuf) -> CacheResult<FastHdlparam> {
|
||||
if !self.is_big_file(path) {
|
||||
if !CacheManager::is_big_file(path) {
|
||||
return CacheResult::NotNeedCache
|
||||
}
|
||||
let path_string = path.to_str().unwrap();
|
||||
|
@ -171,6 +171,31 @@ impl Port {
|
||||
port_desc_array.push(self.width.to_string());
|
||||
}
|
||||
|
||||
port_desc_array.push(self.name.to_string());
|
||||
let port_desc = port_desc_array.join(" ");
|
||||
port_desc
|
||||
}
|
||||
pub fn vhdl_to_vlog_description(&self) -> String {
|
||||
let mut port_desc_array = Vec::<String>::new();
|
||||
let dir_type = match self.dir_type.as_str() {
|
||||
"in" => "input",
|
||||
"out" => "output",
|
||||
_ => &self.dir_type
|
||||
};
|
||||
|
||||
port_desc_array.push(dir_type.to_string());
|
||||
if self.net_type != "unknown" {
|
||||
port_desc_array.push(self.net_type.to_string());
|
||||
}
|
||||
|
||||
if self.signed != "unsigned" {
|
||||
port_desc_array.push("signed".to_string());
|
||||
}
|
||||
|
||||
if self.width != "1" {
|
||||
port_desc_array.push(self.width.to_string());
|
||||
}
|
||||
|
||||
port_desc_array.push(self.name.to_string());
|
||||
let port_desc = port_desc_array.join(" ");
|
||||
port_desc
|
||||
|
@ -207,6 +207,8 @@ pub fn goto_module_declaration_definition(
|
||||
token_name: &str
|
||||
) -> Option<GotoDefinitionResponse> {
|
||||
let hdl_param = server.srcs.hdl_param.clone();
|
||||
info!("get into goto_module_declaration_definition");
|
||||
|
||||
if let Some((module, file_type, def_path)) = hdl_param.find_module_context_by_name(token_name) {
|
||||
match file_type.as_str() {
|
||||
"common" => {
|
||||
@ -280,6 +282,7 @@ fn goto_ip_module_declaration_definition(
|
||||
let target_uri = Url::from_file_path(PathBuf::from_str(&def_path).unwrap()).unwrap();
|
||||
|
||||
let target_range = module.range.clone();
|
||||
info!("target range: {:?}", target_range);
|
||||
let target_range = target_range.to_lsp_range();
|
||||
|
||||
let link = vec![LocationLink {
|
||||
|
@ -433,6 +433,8 @@ fn hover_ip_module_declaration(
|
||||
|
||||
if def_path_buf.exists() {
|
||||
// TODO: 当前工具链只支持 Xilinx 下的工具链,所以此处的代码是 vhdl 的代码
|
||||
// 如果未来需要支持其他的工具链,则需要从 server 下读取对应的变量
|
||||
|
||||
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;
|
||||
@ -467,7 +469,7 @@ fn hover_ip_module_declaration(
|
||||
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 module_profile = make_entity_profile_code(&module);
|
||||
let profile_markdown = LanguageString {
|
||||
language: language_id.to_string(),
|
||||
value: module_profile
|
||||
@ -596,11 +598,109 @@ pub fn make_module_profile_code(module: &crate::core::hdlparam::Module) -> Strin
|
||||
codes.push(format!("\t{port_desc},"));
|
||||
}
|
||||
}
|
||||
codes.push(format!(")"));
|
||||
} else {
|
||||
codes.push(format!(")"));
|
||||
}
|
||||
|
||||
codes.push(format!(");"));
|
||||
|
||||
let profile_string = codes.join("\n");
|
||||
profile_string
|
||||
}
|
||||
|
||||
|
||||
/// vhdl 的 entity 的 profile
|
||||
pub fn make_entity_profile_code(module: &crate::core::hdlparam::Module) -> String {
|
||||
let mut codes = Vec::<String>::new();
|
||||
// param 其实就是 generic
|
||||
let param_num = module.params.len();
|
||||
let port_num = module.ports.len();
|
||||
|
||||
codes.push(format!("entity {} is", module.name));
|
||||
// 缩进字符
|
||||
if module.params.len() > 0 {
|
||||
codes.push("\tgeneric (".to_string());
|
||||
let max_param_name_length = module.params.iter().map(|param| param.name.len()).max().unwrap_or(0);
|
||||
let max_param_type_length = module.params.iter().map(|param| param.net_type.len()).max().unwrap_or(0);
|
||||
|
||||
for (i, param) in module.params.iter().enumerate() {
|
||||
let mut param_desc_array = Vec::<String>::new();
|
||||
let param_name_align_spaces = " ".repeat(max_param_name_length - param.name.len() + 1);
|
||||
param_desc_array.push(format!("{}{}:", param.name, param_name_align_spaces));
|
||||
|
||||
let param_type_align_spaces = " ".repeat(max_param_type_length - param.net_type.len() + 1);
|
||||
param_desc_array.push(format!("{}{}", param.net_type, param_type_align_spaces));
|
||||
|
||||
if param.init != "unknown" {
|
||||
param_desc_array.push(format!(":= {}", param.init.to_string()));
|
||||
}
|
||||
|
||||
let param_desc = param_desc_array.join(" ");
|
||||
if i == param_num - 1 {
|
||||
codes.push(format!("\t\t{param_desc}"));
|
||||
} else {
|
||||
codes.push(format!("\t\t{param_desc};"));
|
||||
}
|
||||
}
|
||||
codes.push("\t);".to_string());
|
||||
}
|
||||
|
||||
if module.ports.len() > 0 {
|
||||
codes.push("\tport (".to_string());
|
||||
|
||||
let net_mapper = |net_type: &str| {
|
||||
if net_type == "unknown" {
|
||||
0
|
||||
} else {
|
||||
net_type.len()
|
||||
}
|
||||
};
|
||||
|
||||
let width_mapper = |width: &str| {
|
||||
if width == "1" {
|
||||
0
|
||||
} else {
|
||||
width.len() + 5
|
||||
}
|
||||
};
|
||||
|
||||
let max_port_length = module.ports.iter().map(|port| port.name.len()).max().unwrap_or(0);
|
||||
let max_net_length = module.ports.iter().map(|port| net_mapper(&port.net_type)).max().unwrap_or(0);
|
||||
// let max_width_length = module.ports.iter().map(|port| width_mapper(&port.width)).max().unwrap_or(0);
|
||||
let max_dir_length = module.ports.iter().map(|port| port.dir_type.len()).max().unwrap_or(0);
|
||||
|
||||
for (i, port) in module.ports.iter().enumerate() {
|
||||
let mut port_desc_array = Vec::<String>::new();
|
||||
|
||||
// port 的名字
|
||||
let port_name_align_spaces = " ".repeat(max_port_length - port.name.len() + 1);
|
||||
port_desc_array.push(format!("{}{}: ", port.name, port_name_align_spaces));
|
||||
|
||||
// in, out, inout
|
||||
let port_dir_align_spaces = " ".repeat(max_dir_length - port.dir_type.len() + 1);
|
||||
port_desc_array.push(format!("{}{}", port.dir_type, port_dir_align_spaces));
|
||||
|
||||
// std_logic, signed, unsigned 等等
|
||||
if port.net_type != "unknown" {
|
||||
port_desc_array.push(port.net_type.to_string());
|
||||
}
|
||||
|
||||
// (57 downto 0)
|
||||
if port.width != "1" {
|
||||
// 内部的 width 统一表示成 [57:0] 这样的格式,需要转换一下
|
||||
let width_string = port.width.replace("[", "(").replace("]", ")").replace(":", " downto ");
|
||||
port_desc_array.push(width_string);
|
||||
}
|
||||
let port_desc = port_desc_array.join("");
|
||||
if i == port_num - 1 {
|
||||
codes.push(format!("\t\t{port_desc}"));
|
||||
} else {
|
||||
codes.push(format!("\t\t{port_desc};"));
|
||||
}
|
||||
}
|
||||
codes.push("\t);".to_string());
|
||||
}
|
||||
|
||||
codes.push(format!("end entity {};", module.name));
|
||||
|
||||
let profile_string = codes.join("\n");
|
||||
profile_string
|
||||
}
|
@ -6,7 +6,7 @@ use log::info;
|
||||
use ropey::Rope;
|
||||
use tower_lsp::lsp_types::*;
|
||||
|
||||
use super::to_escape_path;
|
||||
use super::{get_language_id_by_path_str, to_escape_path};
|
||||
|
||||
pub fn inlay_hint(server: &LSPServer, params: &InlayHintParams) -> Option<Vec<InlayHint>> {
|
||||
let uri = ¶ms.text_document.uri;
|
||||
@ -95,7 +95,6 @@ fn find_instport_inlay_hints_position(
|
||||
let end_offset = position_to_index(rope, range.end);
|
||||
let instport_text = rope.slice(start_offset .. end_offset);
|
||||
let instport_text = instport_text.to_string();
|
||||
info!("{:?}", instport_text);
|
||||
|
||||
if let Some(offset) = instport_text.find("(") {
|
||||
let target_offset = start_offset + offset + 1;
|
||||
@ -112,12 +111,13 @@ fn make_instport_hints(
|
||||
instance: &core::hdlparam::Instance,
|
||||
rope: &Rope
|
||||
) -> Vec<InlayHint> {
|
||||
let hdl_param = server.srcs.hdl_param.clone();
|
||||
let mut hints = Vec::<InlayHint>::new();
|
||||
let define_module = server.srcs.hdl_param.find_module_by_name(&instance.inst_type);
|
||||
if define_module.is_none() {
|
||||
let module_context = hdl_param.find_module_context_by_name(&instance.inst_type);
|
||||
if module_context.is_none() {
|
||||
return hints;
|
||||
}
|
||||
let define_module = define_module.unwrap();
|
||||
let (define_module, file_type, def_path) = module_context.unwrap();
|
||||
// 制作 port name 到 port 的映射
|
||||
let mut port_map = HashMap::<String, &core::hdlparam::Port>::new();
|
||||
for port in &define_module.ports {
|
||||
@ -132,15 +132,18 @@ fn make_instport_hints(
|
||||
}
|
||||
let port_info = port.unwrap();
|
||||
let instport_range = port_assigment.range.to_lsp_range();
|
||||
info!("inst name: {:?}, range: {:?}", instance.name, instport_range);
|
||||
if let Some(hint_position) = find_instport_inlay_hints_position(rope, &instport_range) {
|
||||
let port_desc = MarkupContent {
|
||||
kind: MarkupKind::Markdown,
|
||||
value: format!("```verilog\n{}\n```", port_info.to_description())
|
||||
};
|
||||
|
||||
let label = {
|
||||
let dir_type = port_info.dir_type.to_string();
|
||||
|
||||
// vhdl 转成 verilog
|
||||
let dir_type = match dir_type.as_str() {
|
||||
"out" => "output",
|
||||
"in" => "input",
|
||||
_ => &dir_type
|
||||
};
|
||||
|
||||
if dir_type == "output" {
|
||||
format!("{}", dir_type)
|
||||
} else {
|
||||
@ -148,6 +151,28 @@ fn make_instport_hints(
|
||||
}
|
||||
};
|
||||
|
||||
let language_id = get_language_id_by_path_str(&def_path);
|
||||
|
||||
let port_desc_value = match file_type.as_str() {
|
||||
"common" => {
|
||||
format!("```{}\n{}\n```", language_id, port_info.to_description())
|
||||
}
|
||||
"ip" => {
|
||||
// TODO: 支持更多的 IP
|
||||
format!("```verilog\n{}\n```", port_info.vhdl_to_vlog_description())
|
||||
}
|
||||
"primitives" => {
|
||||
format!("```{}\n{}\n```", language_id, port_info.to_description())
|
||||
}
|
||||
_ => {
|
||||
format!("```{}\n{}\n```", language_id, port_info.to_description())
|
||||
}
|
||||
};
|
||||
|
||||
let port_desc = MarkupContent {
|
||||
kind: MarkupKind::Markdown,
|
||||
value: port_desc_value
|
||||
};
|
||||
|
||||
let hint = InlayHint {
|
||||
position: hint_position,
|
||||
|
@ -255,6 +255,23 @@ fn do_vhdl_fast(
|
||||
};
|
||||
hdl_param.update_fast(path.to_string(), ip_fast.clone());
|
||||
return Ok(ip_fast);
|
||||
} else if let Some(design_file) = vhdl_parse(&pathbuf) {
|
||||
if let Some(mut fast) = make_fast_from_design_file(&design_file) {
|
||||
fast.file_type = file_type.to_string();
|
||||
// IP 不需要内部的 instance,其实现是加密的,只需要暴露 module 的接口即可
|
||||
// 所以此处需要清空所有的 module 中的 instance
|
||||
for module in &mut fast.content {
|
||||
module.instances.clear();
|
||||
}
|
||||
hdl_param.update_fast(path.to_string(), fast.clone());
|
||||
return Ok(fast);
|
||||
}
|
||||
} else {
|
||||
return Err(tower_lsp::jsonrpc::Error {
|
||||
code: tower_lsp::jsonrpc::ErrorCode::ParseError,
|
||||
message: Cow::Owned(format!("error happen when parse {path} in [do_vhdl_fast]")),
|
||||
data: None
|
||||
});
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
@ -264,9 +281,6 @@ fn do_vhdl_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);
|
||||
|
@ -69,29 +69,15 @@ pub enum LogLevel {
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct ProjectConfig {
|
||||
// if true, recursively search the working directory for files to run diagnostics on
|
||||
pub auto_search_workdir: bool,
|
||||
// list of directories with header files
|
||||
pub include_dirs: Vec<String>,
|
||||
// list of directories to recursively search for SystemVerilog/Verilog sources
|
||||
pub source_dirs: Vec<String>,
|
||||
// config options for verible tools
|
||||
pub verible: Verible,
|
||||
// config options for verilator tools
|
||||
pub verilator: Verilator,
|
||||
// log level
|
||||
pub log_level: LogLevel,
|
||||
pub workspace_folder: Option<Url>,
|
||||
pub tool_chain: String
|
||||
}
|
||||
|
||||
impl Default for ProjectConfig {
|
||||
fn default() -> Self {
|
||||
ProjectConfig {
|
||||
auto_search_workdir: true,
|
||||
include_dirs: Vec::new(),
|
||||
source_dirs: Vec::new(),
|
||||
verible: Verible::default(),
|
||||
verilator: Verilator::default(),
|
||||
log_level: LogLevel::Info,
|
||||
workspace_folder: None,
|
||||
tool_chain: "xilinx".to_string()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -170,15 +156,23 @@ impl Default for VeribleFormat {
|
||||
|
||||
#[tower_lsp::async_trait]
|
||||
impl LanguageServer for Backend {
|
||||
async fn initialize(&self, _: InitializeParams) -> Result<InitializeResult> {
|
||||
self.server.srcs.init();
|
||||
|
||||
async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
|
||||
// 申明 LSP 的基本信息和提供的能力
|
||||
let server_info = Some(ServerInfo {
|
||||
name: "Digital IDE 专用 LSP 后端服务器".to_string(),
|
||||
version: Some("0.4.0".to_string())
|
||||
});
|
||||
|
||||
let root_uri = ¶ms.root_uri;
|
||||
if root_uri.is_none() {
|
||||
self.client.show_message(MessageType::ERROR, "LSP 启动失败,原因: 没有找到启动的 workspaceFolder").await;
|
||||
return Ok(InitializeResult::default());
|
||||
}
|
||||
|
||||
let mut configure = self.server.conf.write().unwrap();
|
||||
configure.workspace_folder = root_uri.clone();
|
||||
info!("当前客户端配置 workspaceFolder: {:?}", params.root_uri);
|
||||
|
||||
let text_document_sync = TextDocumentSyncCapability::Options(
|
||||
TextDocumentSyncOptions {
|
||||
open_close: Some(true),
|
||||
@ -239,6 +233,11 @@ impl LanguageServer for Backend {
|
||||
}
|
||||
|
||||
async fn did_open(&self, params: DidOpenTextDocumentParams) {
|
||||
// // 如果文件太大则显示错误
|
||||
// if CacheManager::uri_is_big_file(¶ms.text_document.uri) {
|
||||
// self.client.show_message(MessageType::WARNING, "考虑到性能问题,对于大于 1MB 的文件不会主动提供语言服务")
|
||||
// .await;
|
||||
// } else {
|
||||
let diagnostics = self.server.did_open(params);
|
||||
self.client
|
||||
.publish_diagnostics(
|
||||
@ -247,10 +246,17 @@ impl LanguageServer for Backend {
|
||||
diagnostics.version,
|
||||
)
|
||||
.await;
|
||||
// }
|
||||
}
|
||||
|
||||
async fn did_change(&self, params: DidChangeTextDocumentParams) {
|
||||
// // 如果文件太大则显示错误
|
||||
// if CacheManager::uri_is_big_file(¶ms.text_document.uri) {
|
||||
// // self.client.show_message(MessageType::WARNING, "考虑到性能问题,对于大于 1MB 的文件不会主动提供语言服务")
|
||||
// // .await;
|
||||
// } else {
|
||||
self.server.did_change(params);
|
||||
// }
|
||||
}
|
||||
|
||||
async fn did_delete_files(&self, params: DeleteFilesParams) {
|
||||
@ -258,6 +264,9 @@ impl LanguageServer for Backend {
|
||||
}
|
||||
|
||||
async fn did_save(&self, params: DidSaveTextDocumentParams) {
|
||||
// if CacheManager::uri_is_big_file(¶ms.text_document.uri) {
|
||||
|
||||
// } else {
|
||||
let diagnostics = self.server.did_save(params);
|
||||
self.client
|
||||
.publish_diagnostics(
|
||||
@ -266,6 +275,7 @@ impl LanguageServer for Backend {
|
||||
diagnostics.version,
|
||||
)
|
||||
.await;
|
||||
// }
|
||||
}
|
||||
|
||||
async fn completion(&self, params: CompletionParams) -> Result<Option<CompletionResponse>> {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::core::cache_storage::CacheManager;
|
||||
use crate::core::hdlparam::HdlParam;
|
||||
use crate::core::sv_parser::make_fast_from_syntaxtree;
|
||||
use crate::core::vhdl_parser::make_fast_from_design_file;
|
||||
@ -237,35 +238,10 @@ impl Sources {
|
||||
fast_sync_controller: Arc::new(RwLock::new(HashMap::<String, Arc<RwLock<bool>>>::new()))
|
||||
}
|
||||
}
|
||||
pub fn init(&self) {
|
||||
let mut paths: Vec<PathBuf> = Vec::new();
|
||||
for path in &*self.include_dirs.read().unwrap() {
|
||||
paths.push(path.clone());
|
||||
}
|
||||
for path in &*self.source_dirs.read().unwrap() {
|
||||
paths.push(path.clone());
|
||||
}
|
||||
// find and add all source/header files recursively from configured include and source directories
|
||||
let src_paths = find_src_paths(&paths);
|
||||
|
||||
for path in src_paths {
|
||||
let url = unwrap_result!(Url::from_file_path(&path));
|
||||
let text = unwrap_result!(fs::read_to_string(&path));
|
||||
let doc = TextDocumentItem::new(
|
||||
url,
|
||||
"systemverilog".to_string(),
|
||||
-1,
|
||||
text,
|
||||
);
|
||||
self.add(doc);
|
||||
}
|
||||
}
|
||||
|
||||
/// 增加一个 hdl 文件,并为该文件添加单独的解析线程
|
||||
pub fn add(&self, doc: TextDocumentItem) {
|
||||
// use a condvar to synchronize the parse thread
|
||||
// the valid bool decides whether or not the file
|
||||
// needs to be re-parsed
|
||||
// 对于当前的文件增加一个解析线程,不断进行解析和同步
|
||||
#[allow(clippy::mutex_atomic)] // https://github.com/rust-lang/rust-clippy/issues/1516
|
||||
let valid_parse = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let valid_parse2 = valid_parse.clone();
|
||||
@ -689,8 +665,10 @@ pub fn vhdl_parser_pipeline(
|
||||
return;
|
||||
}
|
||||
|
||||
let mut file = source_handle.write().unwrap();
|
||||
// TODO: 通过更加精细的方法获取下面的变量
|
||||
|
||||
|
||||
let mut file = source_handle.write().unwrap();
|
||||
let mut scope_tree = if let Some(design_file) = vhdl_parse(&escape_path) {
|
||||
//let mut design_files = design_file_handle.write().unwrap();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user