写了一个使用 verilator 为 verilog 文件提供语法检错的 flymake checker

;;; flymake-verilator --- Flymake for Verilog  -*- lexical-binding: t; -*-
;;; Commentary:
;;; Code:
(require 'flymake)

(defvar verilog--flymake-proc nil
  "A flymake verilog process.")

(defvar verilog-flymake-command '("verilator" "--lint-only" "-Wall")
  "Command for verilog's flymake.")

(defvar verilog--flymake-output-buffer " *stderr of verilog-flymake*"
  "Buffer for verilog's flymake output.")

(defun verilog-flymake-done (report-fn
                             source-buffer
                             output-buffer)
  (with-current-buffer source-buffer
    (save-excursion
      (save-restriction
        (with-current-buffer output-buffer
          (goto-char (point-min))
          (let ((diags))
            (while (search-forward-regexp
                    "^\\(%.*\\): .*:\\([0-9]+\\):\\([0-9]+\\): \\(.*\\)$"
                    nil t)
              (let* ((msg (match-string 4))
                     (level-msg (match-string 1))
                     (line (string-to-number (match-string 2)))
                     (column (string-to-number (match-string 3)))
                     (beg)
                     (end)
                     (level))
                (setq level (cond
                             ((string-match-p "%Error" level-msg) ':error)
                             ((string-match-p "%Warning" level-msg) ':warning)
                             (t :note)))
                (setq beg (with-current-buffer source-buffer
                            (save-excursion
                              (save-restriction
                                (goto-char (point-min))
                                (or (equal line 1)
                                    (forward-line (- line 1)))
                                (- (+ (point) column) 1)))))
                (setq end (if (equal level ':error)
                              (+ beg 1)
                            (+ beg (- (length msg) (string-match ": '" msg) 4))))
                (setq diags
                      (cons (flymake-make-diagnostic
                             source-buffer beg end level msg)
                            diags))
                ))
            (funcall report-fn diags)
            ))))))

(defun verilog-flymake-detect (report-fn &rest _args)
  "A Flymake backend for verilog.
Spawn an verilator process that byte-compiles a file representing the
current buffer state and calls REPORT-FN when done."
  (when verilog--flymake-proc
    (when (process-live-p verilog--flymake-proc)
      (kill-process verilog--flymake-proc)))
  (let ((source-buffer (current-buffer))
        (coding-system-for-write 'utf-8-unix)
        (coding-system-for-read 'utf-8))
    (save-restriction
      (widen)
      (let* ((output-buffer (generate-new-buffer " *verilog-flymake*")))
        (setq verilog--flymake-proc
              (make-process
               :name "verilog-flymake-process"
               :buffer output-buffer
               :command (append verilog-flymake-command
                                (list (buffer-file-name source-buffer)))
               :connection-type 'pipe
               :sentinel
               (lambda (proc _event)
                 (unless (process-live-p proc)
                   (unwind-protect
                       (cond
                        ((not (and (buffer-live-p source-buffer)
                                   (eq proc (with-current-buffer source-buffer
                                              verilog--flymake-proc))))
                         (flymake-log :warning
                                      "verilog-flymake process %s obsolete" proc))
                        ((memq (process-status proc) '(exit signal))
                         (verilog-flymake-done report-fn
                                               source-buffer
                                               verilog--flymake-output-buffer
                                               ))
                        (t
                         (funcall report-fn
                                  :panic
                                  :explanation
                                  (format "process %s died" proc))))
                     (kill-buffer output-buffer)
                     (kill-buffer verilog--flymake-output-buffer)
                     )))
               :stderr verilog--flymake-output-buffer
               :noquery t))))))

(defun verilog-setup-flymake-backend ()
  "Setup `flymake-verilator' for verilog buffer."
  (add-hook 'flymake-diagnostic-functions 'verilog-flymake-detect nil t))

(add-hook 'verilog-mode-hook 'verilog-setup-flymake-backend)

(provide 'flymake-verilator)
;;; flymake-verilator.el ends here.

如果大家有需要,可以直接 copy 上面的代码。使用过程遇到问题或有一些想法也可以在这个帖子下留言,有时间会看看。

3 个赞

发现了一个 bug,修复如下:

diff --git a/flymake-verilator.el b/flymake-verilator.el
index 0e628e2..8b60de3 100644
--- a/flymake-verilator.el
+++ b/flymake-verilator.el
@@ -43,9 +43,8 @@
                                 (or (equal line 1)
                                     (forward-line (- line 1)))
                                 (- (+ (point) column) 1)))))
-                (setq end (if (equal level ':error)
-                              (+ beg 1)
-                            (+ beg (- (length msg) (string-match ": '" msg) 4))))
+                (search-forward-regexp "\\(\\^~*\\)" nil t)
+                (setq end (+ beg (length (match-string 1))))
                 (setq diags
                       (cons (flymake-make-diagnostic
                              source-buffer beg end level msg)

1 个赞

flymake 相比于flycheck有什么优势吗? 我看flycheck 默认就已经支持Verilator 对verilog-mode进行语法检查了

不清楚,只是想尽可能减少要下载的包,毕竟 flymake 是内置的。flycheck 客制化相对简单些,很多个性化需求可以相对简单的实现。

diff --git a/flymake-verilator.el b/flymake-verilator.el
index 0e628e2..92152b1 100644
--- a/flymake-verilator.el
+++ b/flymake-verilator.el
@@ -28,7 +28,8 @@
               (let* ((msg (match-string 4))
                      (level-msg (match-string 1))
                      (line (string-to-number (match-string 2)))
-                     (column (string-to-number (match-string 3)))
+                     (locate-string)
+                     (cal)
                      (beg)
                      (end)
                      (level))
@@ -36,23 +37,31 @@
                              ((string-match-p "%Error" level-msg) ':error)
                              ((string-match-p "%Warning" level-msg) ':warning)
                              (t :note)))
+                (search-forward-regexp "\\(\\^~*\\)" nil t)
+                (setq cal (length (match-string 1)))
+                (let ((current (point))
+                      (line-beginning (line-beginning-position)))
+                  (forward-line -1)
+                  (forward-char (- current line-beginning))
+                  (setq locate-string (buffer-substring-no-properties (- (point) cal) (point))))
                 (setq beg (with-current-buffer source-buffer
                             (save-excursion
                               (save-restriction
                                 (goto-char (point-min))
                                 (or (equal line 1)
                                     (forward-line (- line 1)))
-                                (- (+ (point) column) 1)))))
-                (setq end (if (equal level ':error)
-                              (+ beg 1)
-                            (+ beg (- (length msg) (string-match ": '" msg) 4))))
+                                (search-forward locate-string nil t)
+                                (- (point) cal)
+                                ))))
+                (setq end (+ beg cal))
                 (setq diags
                       (cons (flymake-make-diagnostic
                              source-buffer beg end level msg)
                             diags))
                 ))
             (funcall report-fn diags)
-            ))))))
+            )))))
+  )
 
 (defun verilog-flymake-detect (report-fn &rest _args)
   "A Flymake backend for verilog.
@@ -94,7 +103,6 @@ current buffer state and calls REPORT-FN when done."
                                   :panic
                                   :explanation
                                   (format "process %s died" proc))))
-                     (kill-buffer output-buffer)
                      (kill-buffer verilog--flymake-output-buffer)
                      )))
                :stderr verilog--flymake-output-buffer

在 verilator 检错返回时会展开代码,这导致一些错误标记不准确,现在标记的位置对了。看了一下 flycheck 的代码,发现他们没解决这个问题,很无奈,在 Emacs 上 用 Verilog 这门语言的人少,细节找磨总是差一些意思,大家都用其他更成熟的商用开发工具。

最近在做一些 verilog 的活,感谢!

之前有参考你的配置,发现你也使用 Verilog,有一个问题,请问你为什么把缩进都调为 4,还有一些缩进的设置,请问是有什么考量吗?

以前一直想把Verilog的LSP用起来, 一直没成功(或者说是功能很烂,不好用), 后面勉强用flycheck, 也很不满意, 现在直接用python来代替sv写test case了, 爽多了, 哈哈哈

我用的是 C 写 testcase,但是 Verilog 开发在 Emacs 上的体验对比其他语言差距还是有,很多需求都没有实现,针对 Verilog 的各个 language server 都有各自的问题,最看好 verible,但它不提供补全 :joy:。所以想开发一个针对 Verilog 的 language server,但是不是很清楚所需要的技术栈。

针对纯verilog我的方案是lsp-bridge+citre+各种hack(包括各种根据上下文来确定当前光标处的信号类型然后决定跳到什么位置等等, 几乎可以做到直接跳转到定义的地方, 等等), 补全和代码跳转以及类型提示都还可以

请问你愿意分享你的配置吗?

citre 版本是V0.2.1 lsp-bridge 是最新的, 需要改一下 acm-backend-citre.el 的第186行处

    (let* ((collection (unless (string-empty-p acm-backend-citre-search-keyword)
                         (my/get-citre-completions keyword)
                         ;; (let ((cands (citre-get-completions)))
                         ;;   (delete-dups (mapcar #'citre-capf--make-candidate (nth 2 cands))))
                         )))

(defun my/get-citre-completions (kw)
  " 仅在verilog 模式下才从tags 取补全信息 "
  (interactive)
  (when (or (string= major-mode "verilog-mode") (string= major-mode "verilog-ts-mode"))
    (delete-dups (citre-capf--get-collection kw))))
(defvar citre-show-signature-help-timer nil
  " 用于延时显示 signature-help 的定时器 ")

(defcustom citre-show-signature-help-time 0.3
  " 用于延时显示 signature-help "
  :type 'float
  :group 'lsp-bridge)

(use-package! citre
  :defer t
  :load-path "~/.doom.d/elisp/citre"
  :init
  ;; This is needed in `:init' block for lazy load to work.
  (require 'citre-config)     ;; 需要打里面的 (add-hook 'find-file-hook #'citre-auto-enable-citre-mode) 注释掉, 不然打开某些多 `define 的 verilog 文件会导致Emacs 卡死
  ;; Bind your frequently used commands.  Alternatively, you can define them
  ;; in `citre-mode-map' so you can only use them when `citre-mode' is enabled.
  :config


  (remove-hook 'find-file-hook #'citre-auto-enable-citre-mode)    ;; 不然打开某些多 `define 的 verilog 文件会导致Emacs 卡死
  (setq citre-enable-capf-integration nil)
  ;; (add-hook 'verilog-mode-hook '(lambda () (setq acm-enable-citre t)))
  (setq acm-enable-citre t)     ;; alwasy enable
  (after! vertico
    ;; 当使用 SPC q r 重启时 corfu 可能先于 vertico 初始化 最后导致 completion-at-point-function 被覆盖
    ;; 当使用 cape-dabbrev 补全时是在minibuffer 中显示 这里重新设一下修复这个问题
    ;; 为了不影响其他功能 还是先放citre 里吧 放到 corfu 里不确定会不会有其他影响
    (setq-local completion-in-region-function #'corfu--in-region))
  ;; Set these if readtags/ctags is not in your path.
  (cond ((file-exists-p "E:\\work\\tools\\ctags-2022-07-15_p5.9.20220710.0-18-gce18a79-clang-x64")
         (setq
          citre-readtags-program "E:\\work\\tools\\ctags-2022-07-15_p5.9.20220710.0-18-gce18a79-clang-x64\\readtags.exe"
          citre-ctags-program "E:\\work\\tools\\ctags-2022-07-15_p5.9.20220710.0-18-gce18a79-clang-x64\\ctags.exe"))
        ((file-exists-p "/usr/bin/ctags")
         (setq
          citre-readtags-program "/usr/bin/readtags"
          citre-ctags-program "/usr/bin/ctags"))
        ((file-exists-p "/usr/local/bin/ctags")
         (setq
          citre-readtags-program "/usr/local/bin/readtags"
          citre-ctags-program "/usr/local/bin/ctags"))
        (t
         (setq
          citre-readtags-program "readtags"
          citre-ctags-program "ctags")))

  (setq
   ;; Set this if you use project management plugin like projectile.  It's
   ;; used for things like displaying paths relatively, see its docstring.
   citre-project-root-function #'projectile-project-root
   ;; Set this if you want to always use one location to create a tags file.
   citre-default-create-tags-file-location 'global-cache
   ;; See the "Create tags file" section above to know these options
   citre-use-project-root-when-creating-tags t
   citre-prompt-language-for-ctags-command t
   ;; By default, when you open any file, and a tags file can be found for it,
   ;; `citre-mode' is automatically enabled.  If you only want this to work for
   ;; certain modes (like `prog-mode'), set it like this.
   ;; 
   citre-auto-enable-citre-mode-modes '(verilog-mode)
   citre-edit-cmd-buf-default-cmd "ctags\n-o\n%TAGSFILE%\n--languages=[verilog,systemverilog]\n--kinds-all=*\n--fields=*\n--extras=*\n
;; exclude file or dir use --exclude=   can use multi line
-R\n;; add dirs/files to scan here, one line per dir/file\n"
   )

  (defun my/citre-jump-completing-read (definitions symbol)
    "Select an element in DEFINITIONS, with SYMBOL as a prompt.
This uses the `completing-read' interface.  See
`citre-jump-select-definition-function' for the use of this function.
增加list 去重, 解决跳转到某些 parameter 的时候 误进入completing-read
"
    (delete-dups definitions)   ;; 偶尔有时候会产生重复的LIST 导致误进入 completing-read
    (pcase (length definitions)
      (1 (car definitions))
      (_ (let ((collection
                (lambda (str pred action)
                  (if (eq action 'metadata)
                      '(metadata
                        (category . citre-jump)
                        (cycle-sort-function . identity)
                        (display-sort-function . identity))
                    (complete-with-action action definitions str pred)))))
           (completing-read (format "%s: " symbol) collection nil t)))))

  (defun my/define-filter (definitions str-name root)
    " 保留匹配的项 "
    (let* ((result nil))
      (catch 'foo
        (dolist (each-list definitions)
          (let* ((tag-string (citre-make-tag-str
                              each-list nil
                              '(annotation)
                              `(location :suffix ":" :root ,root)
                              '(content :ensure t))))
            ;; (message "tag-string : %s  str-name : %s" tag-string str-name)
            (when (string-match-p (format "%s" str-name) tag-string)
              (push each-list result)
              (throw 'foo t)))))
      (unless result
        (setq result (seq-copy definitions)))
      result))

  (defun my/define-filter_n (definitions str-name root)
    " 去掉匹配的项 "
    (let* ((result nil))
      (dolist (each-list definitions)
        (let* ((tag-string (citre-make-tag-str
                            each-list nil
                            '(annotation)
                            `(location :suffix ":" :root ,root)
                            '(content :ensure t))))
          ;; (message "tag-string : %s  str-name : %s" tag-string str-name)
          (unless (string-match-p (format "%s" str-name) tag-string)
            (push each-list result)
            )))
      (unless result
        (setq result (seq-copy definitions)))
      result))

  (defun my/citre-filter-definitions (&optional no-update)
    " 根据当前point 下的symbol 来过滤 citre 返回的定义, 尽量把不相关的过滤掉 "
    (ignore-errors 
      (let* ( (symbol (citre-get-symbol))
              ;; (tmp (message "symbol : %S" symbol))
              (definitions (citre-get-definitions-maybe-update-tags-file symbol nil no-update))
              
              (root (funcall citre-project-root-function))
              (current-thing (thing-at-point 'line))
              (module-name nil)
              (current-pos (point))
              (module-pos1 nil)
              (module-pos2 nil)
              )
        (unless definitions
          (user-error "Can't find definition for %s" symbol))
        (unless no-update
          (message "current-thing : %S" current-thing)
          (message "symbol : %S" symbol)
          (message "definitions : %S" definitions)
          )
        (cond
         ((string-match-p (format "\\s-*[a-zA-Z0-9_]+_if\\s-+.*?\\s-*(.*)\\s-*;" symbol) current-thing)
         
          ;; interface
          (unless no-update
            (message " found interface "))
          (setq definitions (my/define-filter definitions (format ":\\s-*interface\\s-+%s" symbol) root)))
         ((or (string-match-p (format "\\s-*%s\\s-+#\\s-*(" symbol) current-thing)
              (string-match-p (format "\\s-*%s\\s-+[^ \t\\.]+\\s-*(" symbol) current-thing)
              (string-match-p (format "\\s-*%s\\.[^ ]+\\s-*" symbol) current-thing))
          ;; delay m0_scl (scl0_oen ? 1'bz : scl0_o, scl),
          ;; i2c_slave_model #(SADR) i2c_slave (
          ;; control    controt_inst(
          ;; controt_inst.xxx
          (unless no-update
            (message " found module "))
          (setq definitions (my/define-filter definitions (format ":\\s-*module\\s-+%s" symbol) root)))
         ((save-excursion
            (goto-char (car (bounds-of-thing-at-point `symbol)))
            (let* ((line1 (current-line))
                   (line2 0))
              (evil-backward-word-begin)
              (setq line2 (current-line))
              (if (and (string= (string (following-char)) "(") (= line1 line2))
                  t
                nil)))
          (unless no-update
            (message "found net"))
          (setq definitions (my/define-filter definitions (format "net@.*%s" (file-name-nondirectory (buffer-file-name))) root))
          (when (> (length definitions) 1)
            ;; 可能是class 
            (message "maybe it is class")))

         ((or (string-match-p (format "\\s-*\\.%s\\s-*(\\s-*[^ ]*\\s-*)\\s-*,?" symbol) current-thing)
              (string-match-p (format "\\s-*,?\\s-*\\.%s\\s-*(\\s-*[^ ]*\\s-*)\\s-*" symbol) current-thing))
          (unless no-update
            (message "try found port"))
          (save-excursion
            (setq module-pos1 (re-search-backward "^\\s-*\\([^ )/]+\\)\\s-+#\\s-*(" nil t))
            (when module-pos1
              (setq module-name (match-string 1)))
            (unless no-update
              (message "module-name : %s" module-name))
            (goto-char current-pos)
            (setq module-pos2 (re-search-backward "^\\s-*\\([^ )/\\.]+\\)\\s-+[^ \\.]+\\s-*(" nil t))
            (unless no-update
              (message "(point): %d" (point)))
            (when (or (null module-name) (and module-pos1 module-pos2 (> module-pos2 module-pos1)))
              (setq module-name (match-string 1))))
          (unless no-update
            (message "module-name : %S" module-name)
            (message " found port "))
          (setq definitions (my/define-filter definitions (format "port@module:%s" module-name) root)))
         ((or (string-match-p (format "\\s-*%s(.*)\\s-*;" symbol) current-thing)
              (string-match-p (format "\\s-*[^ ]+\\s-*=\\s-*%s(.*)\\s-*;" symbol) current-thing))
          
          (unless no-update
            (message " found task or function "))
          (setq definitions (my/define-filter definitions (format "\\(function\\|task\\)@.*%s" symbol) root)))
         ((or
           (string-match-p (format "\\s-*[^ ]+\\s-*<?=\\s-*%s\\s-*" symbol) current-thing)
           (string-match-p (format "\\s-*[^ ]+\\s-*<?=\\s-*[^/;]+%s\\s-*" symbol) current-thing)
           (string-match-p (format "^\\s-*%s\\s-*<?=\\s-*" symbol) current-thing)
           (string-match-p (format "assign\\s-*%s\\s-*<?=\\s-*" symbol) current-thing))
          (unless no-update
            (message "found register or port or net or enum"))
          ;; 都在本文件内找
          (setq definitions (my/define-filter definitions (format "\\(register\\|net\\|port\\|enum\\)@.*%s" (file-name-nondirectory (buffer-file-name))) root)))
         ((and (string-match (format "\\s-*\\([^ ]+\\)\\.%s\\s-*" symbol) current-thing)
               (setq module-name (match-string 1 current-thing)))
          ;; force i2c_slave.debug = 1'b0; // disable i2c_slave debug information
          (unless no-update
            (message "found module register or port or wire "))
          (setq definitions (my/define-filter definitions (format "module:%s" module-name) root)))
         ((and (string-match (format "\\s-*\\([^ ]+\\)\\s-+\\([^ ]+\\)\\s-*;" symbol) current-thing)
               (setq module-name (match-string 1 current-thing)))
          
          (unless no-update
            (message "found class or type "))
          (setq definitions (my/define-filter_n definitions (format "typedef.*?\\s-+%s" module-name) root)))
         (t
          (unless no-update
            (message "default"))))
        definitions)))

  (defun my/citre-jump ()
    "Jump to the definition of the symbol at point.
        When there's multiple definitions, it lets you pick one using the
        `completing-read' UI, or you could use your own UI by customizing
        `citre-select-definition-function'.
     append add goto keyword pos "
    (interactive)
    (let* ( result tag-string (marker (point-marker))
            (symbol (citre-get-symbol))
            (definitions (my/citre-filter-definitions))
            (root (funcall citre-project-root-function)))

      (citre-jump-show symbol definitions marker root)
      ;; 跳到symbol 处
      (beginning-of-line)
      (search-forward symbol (line-end-position) t)
      (backward-char)
      (unless (citre-tags-file-path)
        (setq citre--tags-file
              (with-current-buffer (marker-buffer marker)
                (citre-tags-file-path))))))


  (defun my/citre-completion-at-point ()
    "Function used for `completion-at-point-functions'."
    (when-let* ((symbol (citre-get-symbol))
                (bounds (citre-get-property 'bounds symbol))
                (start (car bounds))
                (end (cdr bounds))
                (collection (citre-capf--get-collection symbol))
                (collection
                 (lambda (str pred action)
                   (if (eq action 'metadata)
                       '(metadata
                         (category . citre-completion)
                         (cycle-sort-function . identity)
                         (display-sort-function . identity))
                     (complete-with-action action collection str pred))))
                (get-docsig
                 (lambda (cand)
                   (citre-get-property 'signature cand))))
      (list start end collection
            :annotation-function #'citre-capf--get-annotation
            :company-docsig get-docsig
            ;; This makes our completion function a "non-exclusive" one, which
            ;; means to try the next completion function when current completion
            ;; table fails to match the text at point (see the docstring of
            ;; `completion-at-point-functions').  This is the desired behavior
            ;; but actually it breaks our substring completion.  This is a bug of
            ;; Emacs, see the FIXME comment in the code of
            ;; `completion--capf-wrapper'.  I believe I've fixed it, so let's
            ;; leave this line commented rather than delete it, and see if my
            ;; patch will get itself into Emacs
            ;; (https://debbugs.gnu.org/cgi/bugreport.cgi?bug=39600).

            ;; It actually doesn't cause much inconvenience.  Our completion
            ;; function works well, and the only problem is it won't fallback to
            ;; the next one when no tags are matched, which I believe to also
            ;; happen in other completion functions.
            ;; 当 citre 返回失败的时候  company 接收其他补全后端的结果 
            :exclusive 'no
            )))


  (defun my/citre-get-definitions-maybe-update-tags-file (&optional symbol tagsfile no-update)
    "Get definitions of SYMBOL from TAGSFILE.
When the definitions are not found, and
`citre-update-tags-file-when-no-definitions' is non-nil, update
TAGSFILE if it contains recipe for updating, and try again.  If
still no definitions found, return nil.

See `citre-get-definitions' to know the behavior of \"getting
definitions\"."
    (let ((tagsfile (or tagsfile (citre-tags-file-path))))
      (or
       ;; 解决频繁调用会卡死的问题
       (pcase (while-no-input
                (citre-get-definitions symbol tagsfile))
         ('t nil)
         (val val))
       (when (and citre-update-tags-file-when-no-definitions (not no-update)
                  (citre-tags-file-updatable-p tagsfile)
                  (y-or-n-p "Can't find definition.  \
Update the tags file and search again? "))
         (citre-update-tags-file tagsfile 'sync)
         ;; WORKAROUND: If we don't sit for a while, the readtags process will
         ;; freeze.  See the comment above `citre-core-write-pseudo-tag'.
         (sit-for 0.01)
         (citre-get-definitions symbol tagsfile)))))

  (defun my/citre-show-signature-help (&optional root)
    (interactive)
    (when (and lsp-bridge-mode acm-enable-citre (evil-normal-state-p))
      (let* ((tags (my/citre-filter-definitions t))
             (loc-alist
              (mapcar (lambda (tag)
                        (cons (citre-make-tag-str
                               tag nil
                               '(annotation)
                               `(location :suffix ":" :root ,root)
                               '(content :ensure t))
                              tag))
                      tags))
             (definitions (delete-dups (mapcar #'car loc-alist))))
        ;; (message "definitions : %S" definitions)
        ;; (message "length definitions: %d" (length definitions))
        (when (= (length definitions) 1)
          (let* ((definition-string (replace-regexp-in-string "\\s-+" " " (car definitions)))
                 (simple-definition (if (or (string-match "([0-9]+): \\(.*\\)//" definition-string) (string-match "([0-9]+): \\(.*\\)" definition-string))
                                        (match-string 1 definition-string)
                                      definition-string))
                 (definition (s-split " " (s-trim-left simple-definition) 'omit-nulls))
                 (type (nth 0 definition))
                 (describe (cl-subseq definition 1))
                 )
            ;; (message "type : %s" type)
            ;; (message "describe : %S" describe)
            ;; (message "length is 1")
            ;; (message "definitions : %s" definition-string)
            ;; (message "simple-definition : %s" simple-definition)
            ;; (message "definition : %s" definition)
            ;; 
            ;; (lsp-bridge-signature-help-update (list type (s-join "" describe)) 0)
            (lsp-bridge-signature-help--update (list simple-definition) 0))))))


  (defun my/enable-citre-signture-help ()
    (interactive)
    (setq citre-show-signature-help-timer (run-with-idle-timer citre-show-signature-help-time t 'my/citre-show-signature-help)))

  (defun my/disable-citre-signture-help ()
    (interactive)
    (cancel-timer citre-show-signature-help-timer))


  (my/enable-citre-signture-help)
  (advice-add #'citre-jump :override #'my/citre-jump)
  (advice-add #'citre-jump-completing-read :override #'my/citre-jump-completing-read)
  ;; 用于使 completion-at-point-functions 在citre 没有补全结果的时候可以接收其他后端的结果
  (advice-add #'citre-completion-at-point :override #'my/citre-completion-at-point)
  (advice-add #'citre-get-definitions-maybe-update-tags-file :override #'my/citre-get-definitions-maybe-update-tags-file)

  (defun my/abort-citre-peek ()
    (interactive)
    ;; (message "my/abort-citre-peek")
    (when citre-peek--mode
      (citre-peek-abort)))

  (advice-add #'evil-force-normal-state :before #'my/abort-citre-peek)   ;; 使用 ESC 退出 peek
  )

  (map!
   (:map verilog-mode-map
    ;; Navigation
    "C-/"     #'citre-peek
    [remap lsp-bridge-find-def]     #'citre-jump
    [remap lsp-bridge-find-def-return]    #'citre-jump-back
    ;; "M-<f12>"     #'helm-gtags-find-rtag
    ;; "C-M-]"     #'helm-gtags-find-tag
    ;; "C-M-t"     #'helm-gtags-find-tag-other-window)
    ;; "C-c c" #'my/verilog-compile-only
    "C-c c" #'my/run-vcs-with-compile
    "C-c a" #'my/verilog-compile-download
    "C-c d" #'my/verilog-download
    "C-c D" #'my/verilog-download-flash
    "C-c l" 'my/find-vcs-sim-log-file
    "C-c L" 'my/switch-Async-ssh-buffer
    "C-c r" 'my/compile-quartus-prj
    "C-c R" 'my/compile-and-download-quartus-prj
    "C-c m" 'my/run-modelsim-nogui
    "C-c M" 'my/run-modelsim-gui
    "C-c s" 'my/run-vcs-with-nogui
    "C-c S" 'my/run-vcs-with-gui
    "C-c v" 'my/run-vcs-verdi-fsdb
    "C-c V" 'my/run-verdi
    "C-c k" 'my/kill-vcs-modelsim-gui-process
    "C-c A" 'my/open-anlogic-prj
    "C-c q" 'my/open-quartus-prj
    "C-c u" 'citre-update-this-tags-file))

  (use-package! verilog-ext
    :load-path "~/.doom.d/elisp/verilog-ext"
    :after verilog-mode
    :demand
    :hook ((verilog-mode . verilog-ext-mode))
    ;; :mode (("\\.v\\'"   . verilog-ts-mode)
    ;;        ("\\.sv\\'"  . verilog-ts-mode)
    ;;        ("\\.vh\\'"  . verilog-ts-mode)
    ;;        ("\\.svh\\'" . verilog-ts-mode))
    :init
    ;; Can also be set through `M-x RET customize-group RET verilog-ext':
    ;;  - Verilog Ext Feature List (provides info of different features)
    ;; Comment out/remove the ones you do not need
    (setq verilog-ext-feature-list
          '(
            ;; font-lock
            xref
            ;; capf
            hierarchy
            ;; eglot
            ;; lsp
            flycheck
            beautify
            navigation
            template
            formatter
            ;; compilation
            imenu
            which-func
            hideshow
            typedefs
            time-stamp
            block-end-comments
            ;; company-keywords
            ports))
    :config
    (verilog-ext-mode-setup))



(provide 'verilog-init)

1 个赞

这个好像功能比较多,扩展了verilog-mode的功能

1 个赞

这个之前我也发现了,它没有 flymake 的支持,不过提供了许多 verilog 的 language server 在 Emacs 的支持。

感谢大佬提供的代码,有两个小小优化的点,

  1. 增加一个可以传参数的变量,
+;; (setq verilog-flymake-lib-option "-y" "xxx/dir/" "-f" "xxx/filelist.lst")
+(defcustom verilog-flymake-specify-option '()
+  "verilog library used for verilog's flymake.
+  -f lib_filelist.f -y lib_directory")
+

-               :command (append verilog-flymake-command
-                                (list (buffer-file-name source-buffer)))
+               :command (append verilog-flymake-command verilog-flymake-specify-option
+                                (list (buffer-file-name source-buffer)) nil)

  1. 只在当前文件中,显示当前文件的Error/Warning,
-            (while (search-forward-regexp
-                    "^\\(%.*\\): .*:\\([0-9]+\\):\\([0-9]+\\): \\(.*\\)$"
-                    nil t)
+            (while (search-forward-regexp
+                     ;; "^\\(%.*\\):.*:\\([0-9]+\\):\\([0-9]+\\): \\(.*\\)$" nil t)
+                      (concat "^\\(%.*\\):.*" (file-name-base (buffer-file-name source-buffer)) ".*:\\([0-9]+\\):\\([0-9]+\\): \\(.*\\)$") nil t)

1 个赞

verilator很早就听过,bluespec的默认仿真引擎就是verilator, 但是verilator但是在实际ASIC开发中用得很少, 所以请教一下: 实际在大型ASIC开发的时候(VCS编译至少半个小时),verilator的性能怎么样?

我现在才 FPGA 入门,因为学校的安排有需要学习而已,所以没有能力回答你这个问题,抱歉。

感谢,其实我才 FPGA 入门,所以之前没有考虑到这个需求,应该其他人会有这个需求。

新版verilator 加入了很多优化,多线程,编译仿真应该都不慢。 用verilator 做过vu9p FPGA开发,没问题

因为大部分时候习惯了 4 spc 缩进……