Skip to content

Commit

Permalink
[circt-verilog-lsp-server] Add Verilog Language Server
Browse files Browse the repository at this point in the history
This is a first PR to add a Language Server Protocol (LSP) implementation
for Verilog/SystemVerilog using the slang frontend. This enables IDE features
like syntax error checking and diagnostics.

The server is built as circt-verilog-lsp-server and integrates with CIRCT's
existing Verilog import capabilities. It leverages MLIR's LSP server support
libraries for the protocol implementation.
  • Loading branch information
uenoku committed Feb 13, 2025
1 parent 1892650 commit db2847d
Show file tree
Hide file tree
Showing 25 changed files with 1,102 additions and 30 deletions.
30 changes: 30 additions & 0 deletions cmake/modules/SlangCompilerOptions.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# slang uses exceptions
set(LLVM_REQUIRES_EH ON)
set(LLVM_REQUIRES_RTTI ON)

# For ABI compatibility, define the DEBUG macro in debug builds. Slang sets this
# internally. If we don't set this here as well, header-defined things like the
# destructor of `Driver`, which is generated in ImportVerilog's compilation
# unit, will destroy a different set of fields than what was potentially built
# or modified by code compiled in the Slang compilation unit.
add_compile_definitions($<$<CONFIG:Debug>:DEBUG>)

# Disable some compiler warnings caused by slang headers such that the
# `ImportVerilog` build doesn't spew out a ton of warnings that are not related
# to CIRCT.
if (MSVC)
# No idea what to put here
else ()
# slang uses exceptions; we intercept these in ImportVerilog
add_compile_options(-fexceptions)
add_compile_options(-frtti)
# slang has some classes with virtual funcs but non-virtual destructor.
add_compile_options(-Wno-non-virtual-dtor)
# some other warnings we've seen
add_compile_options(-Wno-c++98-compat-extra-semi)
add_compile_options(-Wno-ctad-maybe-unsupported)
add_compile_options(-Wno-cast-qual)
# visitor switch statements cover all cases but have default
add_compile_options(-Wno-covered-switch-default)
endif ()

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Main entry function for circt-verilog-lsp-server for when built as standalone
// binary.
//
//===----------------------------------------------------------------------===//

#ifndef CIRCT_TOOLS_CIRCT_VERILOG_LSP_SERVER_CIRCTVERILOGLSPSERVERMAIN_H
#define CIRCT_TOOLS_CIRCT_VERILOG_LSP_SERVER_CIRCTVERILOGLSPSERVERMAIN_H
#include "mlir/Support/LLVM.h"
#include "llvm/ADT/StringRef.h"
#include <memory>
#include <optional>
#include <string>
#include <vector>

namespace llvm {
struct LogicalResult;
} // namespace llvm

namespace mlir {
namespace lsp {
class JSONTransport;
} // namespace lsp
} // namespace mlir

namespace circt {
namespace lsp {
struct VerilogServerOptions {
VerilogServerOptions(const std::vector<std::string> &libDirs)
: libDirs(libDirs) {}
/// Additional list of RTL directories to search.
const std::vector<std::string> &libDirs;
};
// namespace lsp

/// Implementation for tools like `circt-verilog-lsp-server`.
llvm::LogicalResult
CirctVerilogLspServerMain(const VerilogServerOptions &options,
mlir::lsp::JSONTransport &transport);

} // namespace lsp
} // namespace circt

#endif // CIRCT_TOOLS_CIRCT_VERILOG_LSP_SERVER_CIRCTVERILOGLSPSERVERMAIN_H
30 changes: 1 addition & 29 deletions lib/Conversion/ImportVerilog/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,32 +1,4 @@
# slang uses exceptions
set(LLVM_REQUIRES_EH ON)
set(LLVM_REQUIRES_RTTI ON)

# For ABI compatibility, define the DEBUG macro in debug builds. Slang sets this
# internally. If we don't set this here as well, header-defined things like the
# destructor of `Driver`, which is generated in ImportVerilog's compilation
# unit, will destroy a different set of fields than what was potentially built
# or modified by code compiled in the Slang compilation unit.
add_compile_definitions($<$<CONFIG:Debug>:DEBUG>)

# Disable some compiler warnings caused by slang headers such that the
# `ImportVerilog` build doesn't spew out a ton of warnings that are not related
# to CIRCT.
if (MSVC)
# No idea what to put here
else ()
# slang uses exceptions; we intercept these in ImportVerilog
add_compile_options(-fexceptions)
add_compile_options(-frtti)
# slang has some classes with virtual funcs but non-virtual destructor.
add_compile_options(-Wno-non-virtual-dtor)
# some other warnings we've seen
add_compile_options(-Wno-c++98-compat-extra-semi)
add_compile_options(-Wno-ctad-maybe-unsupported)
add_compile_options(-Wno-cast-qual)
# visitor switch statements cover all cases but have default
add_compile_options(-Wno-covered-switch-default)
endif ()
include(SlangCompilerOptions)

add_circt_translation_library(CIRCTImportVerilog
Expressions.cpp
Expand Down
4 changes: 4 additions & 0 deletions lib/Tools/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
add_subdirectory(circt-bmc)
add_subdirectory(circt-lec)
add_subdirectory(rtgtool)

if(CIRCT_SLANG_FRONTEND_ENABLED)
add_subdirectory(circt-verilog-lsp-server)
endif()
14 changes: 14 additions & 0 deletions lib/Tools/circt-verilog-lsp-server/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
add_subdirectory(VerilogServerImpl)

add_circt_library(CIRCTVerilogLspServerLib
CirctVerilogLspServerMain.cpp
LSPServer.cpp
LSPUtils.cpp

ADDITIONAL_HEADER_DIRS
${MLIR_MAIN_INCLUDE_DIR}/circt/Tools/crct-verilog-lsp-server

LINK_LIBS PUBLIC
CIRCTVerilogLspServerImpl
MLIRLspServerSupportLib
)
22 changes: 22 additions & 0 deletions lib/Tools/circt-verilog-lsp-server/CirctVerilogLspServerMain.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "circt/Tools/circt-verilog-lsp-server/CirctVerilogLspServerMain.h"
#include "LSPServer.h"
#include "VerilogServer.h"
#include "mlir/Tools/lsp-server-support/Transport.h"

using namespace mlir;
using namespace mlir::lsp;

llvm::LogicalResult circt::lsp::CirctVerilogLspServerMain(
const circt::lsp::VerilogServerOptions &options,
mlir::lsp::JSONTransport &transport) {
circt::lsp::VerilogServer server(options);
return circt::lsp::runVerilogLSPServer(server, transport);
}
163 changes: 163 additions & 0 deletions lib/Tools/circt-verilog-lsp-server/LSPServer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "LSPServer.h"
#include "VerilogServer.h"
#include "mlir/Tools/lsp-server-support/Protocol.h"
#include "mlir/Tools/lsp-server-support/Transport.h"
#include <optional>

#define DEBUG_TYPE "circt-verilog-lsp-server"

using namespace mlir;
using namespace mlir::lsp;

//===----------------------------------------------------------------------===//
// LSPServer
//===----------------------------------------------------------------------===//

namespace {
struct LSPServer {
LSPServer(circt::lsp::VerilogServer &server, JSONTransport &transport)
: server(server), transport(transport) {}

//===--------------------------------------------------------------------===//
// Initialization
//===--------------------------------------------------------------------===//

void onInitialize(const InitializeParams &params,
Callback<llvm::json::Value> reply);
void onInitialized(const InitializedParams &params);
void onShutdown(const NoParams &params, Callback<std::nullptr_t> reply);

//===--------------------------------------------------------------------===//
// Document Change
//===--------------------------------------------------------------------===//

void onDocumentDidOpen(const DidOpenTextDocumentParams &params);
void onDocumentDidClose(const DidCloseTextDocumentParams &params);
void onDocumentDidChange(const DidChangeTextDocumentParams &params);

//===--------------------------------------------------------------------===//
// Fields
//===--------------------------------------------------------------------===//

circt::lsp::VerilogServer &server;
JSONTransport &transport;

/// An outgoing notification used to send diagnostics to the client when they
/// are ready to be processed.
OutgoingNotification<PublishDiagnosticsParams> publishDiagnostics;

/// Used to indicate that the 'shutdown' request was received from the
/// Language Server client.
bool shutdownRequestReceived = false;
};
} // namespace

//===----------------------------------------------------------------------===//
// Initialization
//===----------------------------------------------------------------------===//

void LSPServer::onInitialize(const InitializeParams &params,
Callback<llvm::json::Value> reply) {
// Send a response with the capabilities of this server.
llvm::json::Object serverCaps{
{"textDocumentSync",
llvm::json::Object{
{"openClose", true},
{"change", (int)TextDocumentSyncKind::Incremental},
{"save", true},
}}};

llvm::json::Object result{
{{"serverInfo", llvm::json::Object{{"name", "circt-verilog-lsp-server"},
{"version", "0.0.1"}}},
{"capabilities", std::move(serverCaps)}}};
reply(std::move(result));
}
void LSPServer::onInitialized(const InitializedParams &) {}
void LSPServer::onShutdown(const NoParams &, Callback<std::nullptr_t> reply) {
shutdownRequestReceived = true;
reply(nullptr);
}

//===----------------------------------------------------------------------===//
// Document Change
//===----------------------------------------------------------------------===//

void LSPServer::onDocumentDidOpen(const DidOpenTextDocumentParams &params) {
PublishDiagnosticsParams diagParams(params.textDocument.uri,
params.textDocument.version);
server.addDocument(params.textDocument.uri, params.textDocument.text,
params.textDocument.version, diagParams.diagnostics);

// Publish any recorded diagnostics.
publishDiagnostics(diagParams);
}

void LSPServer::onDocumentDidClose(const DidCloseTextDocumentParams &params) {
std::optional<int64_t> version =
server.removeDocument(params.textDocument.uri);
if (!version)
return;

// Empty out the diagnostics shown for this document. This will clear out
// anything currently displayed by the client for this document (e.g. in the
// "Problems" pane of VSCode).
publishDiagnostics(
PublishDiagnosticsParams(params.textDocument.uri, *version));
}

void LSPServer::onDocumentDidChange(const DidChangeTextDocumentParams &params) {
PublishDiagnosticsParams diagParams(params.textDocument.uri,
params.textDocument.version);
server.updateDocument(params.textDocument.uri, params.contentChanges,
params.textDocument.version, diagParams.diagnostics);

// Publish any recorded diagnostics.
publishDiagnostics(diagParams);
}

//===----------------------------------------------------------------------===//
// Entry Point
//===----------------------------------------------------------------------===//

LogicalResult circt::lsp::runVerilogLSPServer(VerilogServer &server,
JSONTransport &transport) {
LSPServer lspServer(server, transport);
MessageHandler messageHandler(transport);

// Initialization
messageHandler.method("initialize", &lspServer, &LSPServer::onInitialize);
messageHandler.notification("initialized", &lspServer,
&LSPServer::onInitialized);
messageHandler.method("shutdown", &lspServer, &LSPServer::onShutdown);

// Document Changes
messageHandler.notification("textDocument/didOpen", &lspServer,
&LSPServer::onDocumentDidOpen);
messageHandler.notification("textDocument/didClose", &lspServer,
&LSPServer::onDocumentDidClose);
messageHandler.notification("textDocument/didChange", &lspServer,
&LSPServer::onDocumentDidChange);

// Diagnostics
lspServer.publishDiagnostics =
messageHandler.outgoingNotification<PublishDiagnosticsParams>(
"textDocument/publishDiagnostics");

// Run the main loop of the transport.
if (llvm::Error error = transport.run(messageHandler)) {
Logger::error("Transport error: {0}", error);
llvm::consumeError(std::move(error));
return failure();
}

return success(lspServer.shutdownRequestReceived);
}
35 changes: 35 additions & 0 deletions lib/Tools/circt-verilog-lsp-server/LSPServer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef LIB_CIRCT_TOOLS_CIRCT_VERILOG_LSP_LSPSERVER_H
#define LIB_CIRCT_TOOLS_CIRCT_VERILOG_LSP_LSPSERVER_H

#include <memory>

namespace llvm {
struct LogicalResult;
} // namespace llvm
namespace mlir {
namespace lsp {
class JSONTransport;
} // namespace lsp
} // namespace mlir

namespace circt {
namespace lsp {
class VerilogServer;

/// Run the main loop of the LSP server using the given Verilog server and
/// transport.
llvm::LogicalResult runVerilogLSPServer(VerilogServer &server,
mlir::lsp::JSONTransport &transport);

} // namespace lsp
} // namespace circt

#endif // LIB_CIRCT_TOOLS_CIRCT_VERILOG_LSP_LSPSERVER_H
26 changes: 26 additions & 0 deletions lib/Tools/circt-verilog-lsp-server/LSPUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains utilities for CIRCT Verilog LSP server.
//
//===----------------------------------------------------------------------===//

#include "LSPUtils.h"
#include "mlir/Tools/lsp-server-support/Logging.h"

void circt::lsp::Logger::error(Twine message) {
mlir::lsp::Logger::error("{}", message);
}

void circt::lsp::Logger::info(Twine message) {
mlir::lsp::Logger::info("{}", message);
}

void circt::lsp::Logger::debug(Twine message) {
mlir::lsp::Logger::debug("{}", message);
}
Loading

0 comments on commit db2847d

Please sign in to comment.