【github 有趣项目】Verible HDL代码工具:Verilog/SV 代码自动格式化 + 语法解析与检查 + 混淆器

想深耕嵌入式?这个专辑值得收藏

MCU、FPGA、工控、传感器一站式学习,实战项目直接抄

  • Verible 是由 Google 发起、Chips Alliance 维护的开源 SystemVerilog / Verilog 开发工具套件,核心是围绕 IEEE 1800-2017 SystemVerilog 语法的完整解析器,提供代码格式化、风格检查、语法验证、语言服务器以及代码混淆(Obfuscate)等功能。

一、Verible 核心工具组件

工具名功能典型用途
verible-verilog-format自动格式化 Verilog/SV 代码统一缩进、对齐、换行,CI 集成保证风格一致
verible-verilog-lint风格与规则检查(Linter)检测命名不规范、禁止语法、未使用信号等
verible-verilog-syntax语法解析与检查验证文件能否被正确解析,可输出语法树 JSON
verible-verilog-obfuscate源码混淆/去标识化把 module/信号名替换为无意义字符串
verible-verilog-lsLSP 语言服务器VS Code / Vim 实时报语法错误、自动补全
  • 对硬件工程师来说,Verible 可用于统一团队代码风格(格式化)+ 给敏感 RTL 做轻量混淆(去标识化)
    在这里插入图片描述

二、安装

  • Linux:从 https://github.com/chipsalliance/verible/releases 下载静态二进制包,解压后将 bin/ 加入 PATH

在这里插入图片描述

  • echo 'export PATH="$PATH:/VERIBLE路径/verible/bin"' >> ~/.bashrc

  • echo 'export PATH="$PATH:~/音乐/verible-v0.0-4080-ga0a8d8eb-linux-static-x86_64/verible-v0.0-4080-ga0a8d8eb/bin"' >> ~/.bashrc

  • Windows:下载 Windows 版 Release 包或 WSL 下用 Linux 版

三、常用命令示例

代码格式化(最常用)

# 格式化单个文件(原地修改)
verible-verilog-format --inplace design.sv

# 批量格式化
verible-verilog-format --inplace $(find . -name "*.v" -o -name "*.sv")

# 指定列宽
verible-verilog-format --column_limit=120 design.sv

在这里插入图片描述

语法检查

$ verible-verilog-syntax  design.sv // 基本语法检查
$ verible-verilog-syntax --printtree design.sv 
2>&1 | head -n 10

Parse Tree:
Node @0 (tag: kDescriptionList) {
  Node @0 (tag: kModuleDeclaration) {
    Node @0 (tag: kModuleHeader) {
      Leaf @0 (#"module" @1581-1587: "module")
      Leaf @2 (#SymbolIdentifier @1588-1601: "RS232Driver_s")
      Node @5 (tag: kParenGroup) {
        Leaf @0 (#'(' @1602-1603: "(")
        Node @1 (tag: kPortDeclarationList) {
$ verible-verilog-syntax --printtokens design.sv // 打印 token(看词法分析)
Lexed and filtered tokens:
(#"module" @1581-1587: "module")
(#SymbolIdentifier @1588-1601: "RS232Driver_s")
(#'(' @1602-1603: "(")
(#SymbolIdentifier @1788-1791: "clk")
(#',' @1791-1792: ",")
(#SymbolIdentifier @1820-1826: "nReset")
(#',' @1826-1827: ",")
(#SymbolIdentifier @2066-2074: "RS232TXD")
(#',' @2074-2075: ",")
(#SymbolIdentifier @2106-2114: "RS232RXD")
(#',' @2114-2115: ",")
(#SymbolIdentifier @2379-2387: "tDataReq")
(#',' @2387-2388: ",")
(#SymbolIdentifier @2434-2439: "tData")
(#',' @2439-2440: ",")
(#SymbolIdentifier @2480-2485: "tBusy")
(#',' @2485-2486: ",")
(#SymbolIdentifier @2786-2793: "rDataEn")
(#',' @2793-2794: ",")
(#SymbolIdentifier @2859-2864: "rData")
$ verible-verilog-syntax --helpfull
verible-verilog-syntax: usage: verible-verilog-syntax [options] <file> [<file>...]

  Flags from external/abseil-cpp~/absl/flags/parse.cc:
    --flagfile (comma-separated list of files to load flags from); default: ;
    --fromenv (comma-separated list of flags to set from the environment [use
      'export FLAGS_flag1=value']); default: ;
    --tryfromenv (comma-separated list of flags to try to set from the
      environment if present); default: ;
    --undefok (comma-separated list of flag names that it is okay to specify on
      the command line even if the program does not define a flag with that
      name); default: ;


  Flags from verible/verilog/parser/verilog-parser.cc:
    --verilog_trace_parser (Trace verilog parser); default: false;


  Flags from verible/verilog/tools/syntax/verilog-syntax.cc:
    --error_limit (Limit the number of syntax errors reported. (0: unlimited));
      default: 0;
    --export_json (Uses JSON for output. Intended to be used as an input for
      other tools.); default: false;
    --lang (Selects language variant to parse. Options:
      auto: SystemVerilog-2017, but may auto-detect alternate parsing modes
      sv: strict SystemVerilog-2017, with explicit alternate parsing modes
      lib: Verilog library map language (LRM Ch. 33)
      ); default: auto;
    --printrawtokens (Prints all lexed tokens, including filtered ones.);
      default: false;
    --printtokens (Prints all lexed and filtered tokens); default: false;
    --printtree (Whether or not to print the tree); default: false;
    --show_diagnostic_context (prints an additional line on which the diagnostic
      was found,followed by a line with a position marker); default: false;
    --verifytree (Verifies that all tokens are parsed into tree, prints
      unmatched tokens); default: false;

Try --helpfull to get a list of all flags or --help=substring shows help for
flags which include specified substring in either in the name, or description or
path.

风格检查(Lint)

$verible-verilog-lint  design.sv
design.sv:32:8-20: Declared module does not match the first dot-delimited component of file name: "design" [Style: file-names] [module-filename]
design.sv:85:13-26: Explicitly define a storage type for every parameter and localparam, (pRS232BandRate). [Style: constants] [explicit-parameter-storage-type]
design.sv:86:13-31: Explicitly define a storage type for every parameter and localparam, (pRS232BandRate_half). [Style: constants] [explicit-parameter-storage-type]
  • 文件名和module名不一致
  • 参数没有指定类型

代码混淆(Obfuscate)

$ verible-verilog-obfuscate  --helpfull
verible-verilog-obfuscate: usage: verible-verilog-obfuscate [options] < original > output

verilog_obfuscate mangles Verilog code by changing identifiers.
All whitespaces and identifier lengths are preserved.
Output is written to stdout.


  Flags from external/abseil-cpp~/absl/flags/parse.cc:
    --flagfile (comma-separated list of files to load flags from); default: ;
    --fromenv (comma-separated list of flags to set from the environment [use
      'export FLAGS_flag1=value']); default: ;
    --tryfromenv (comma-separated list of flags to try to set from the
      environment if present); default: ;
    --undefok (comma-separated list of flag names that it is okay to specify on
      the command line even if the program does not define a flag with that
      name); default: ;


  Flags from verible/verilog/parser/verilog-parser.cc:
    --verilog_trace_parser (Trace verilog parser); default: false;


  Flags from verible/verilog/tools/obfuscator/verilog-obfuscate.cc:
    --decode (If true, when used with --load_map, apply the translation
      dictionary in reverse to de-obfuscate the source code, and do not
      obfuscate any unseen identifiers. There is no need to --save_map with this
      option, because no new substitutions are established.); default: false;
    --load_map (If provided, pre-load an existing translation dictionary
      (written by --save_map). This is useful for applying pre-existing
      transforms.); default: "";
    --preserve_builtin_functions (If true, preserve built-in function names such
      as sin(), ceil()..); default: true;
    --preserve_interface (If true, module name, port names and parameter names
      will be preserved. The translation map saved with --save_map will have
      identity mappings for these identifiers. When used with --load_map, the
      mapping explicitly specified in the map file will have higher priority
      than this option.); default: false;
    --save_map (If provided, save the translation to a dictionary for reuse in a
      future obfuscation with --load_map.); default: "";

Try --helpfull to get a list of all flags or --help=substring shows help for
flags which include specified substring in either in the name, or description or
path.
  • Verible 混淆 ≠ 加密,不防专业逆向分析,只是去掉可读语义,适合低敏感场景
  • Vivado 、Quartus 和 Microchip Libero SoC 本身都不内置 Verilog 源代码"混淆器(Obfuscator)"。它们自带IEEE P1735加密(加密后不可读,不是混淆)和 网表/DCP 黑盒交付。
Flag作用
--save_map保存「原名字 → 混淆名字」映射表
--load_map用已有的映射表混淆
--decode反混淆(还原)
--preserve_interface保留 module / port / parameter 名字
--preserve_builtin_functions保留 $sin$ceil 等系统函数
verible-verilog-obfuscate < design.sv > design_obf.sv  // 基础混淆

在这里插入图片描述

  • 删除注释可以使用 : sed -i 's/\/\/.*$//' test.sv
verible-verilog-obfuscate  --save_map=map.txt  < design.sv > design_obf.sv  // 保存映射表

在这里插入图片描述

verible-verilog-obfuscate   --load_map=map.txt   < fifo.sv > fifo_obf.sv  // 可用同一个映射表混淆多个文件
verible-verilog-obfuscate --load_map=map.txt --decode   < design_obf.sv > design_recovered.sv // 反混淆(还原代码)
verible-verilog-obfuscate --preserve_interface --save_map=map.txt  < ip_top.sv > ip_top_obf.sv  // 保留接口(对外 IP 交付常用)

使用AI反混淆(也可正常编译):

module uart_rs232 (
    input        clk,        // 20MHz
    input        rst_n,      // 低有效复位

    output       txd,        // RS232 TX
    input        rxd,        // RS232 RX

    input        tx_req,     // 发送请求
    input  [7:0] tx_data,    // 待发送数据
    output       tx_busy,    // 发送忙

    output       rx_valid,   // 接收数据有效
    output [7:0] rx_data     // 接收数据
);

    //==================== 参数 ====================
    parameter BAUD_CNT_MAX = 12'd278;   // 115200 @20MHz
    parameter BAUD_HALF    = 12'd139;

    //==================== 内部信号 ====================
    reg [11:0] baud_cnt;
    reg        baud_tick;

    reg [9:0]  tx_shift_reg;
    reg        tx_state;
    reg [3:0]  tx_bit_cnt;

    reg        rxd_sync_0;
    reg        rxd_sync_1;
    wire       rxd_negedge;

    reg [1:0]  rx_state;
    reg [11:0] rx_baud_cnt;
    reg [6:0]  rx_shift_reg;
    reg [2:0]  rx_bit_cnt;

    reg        tx_busy;
    reg        txd;

    reg        rx_valid;
    reg [7:0]  rx_data;

    //==================== TX 波特率生成 ====================
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            baud_cnt  <= 0;
            baud_tick <= 0;
        end else if (baud_cnt == BAUD_CNT_MAX) begin
            baud_cnt  <= 0;
            baud_tick <= 1'b1;
        end else begin
            baud_cnt  <= baud_cnt + 1;
            baud_tick <= 1'b0;
        end
    end

    //==================== TX 发送逻辑 ====================
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            tx_state   <= 0;
            tx_busy    <= 0;
            txd        <= 1'b1;
            tx_bit_cnt <= 0;
        end else begin
            case (tx_state)
                1'b0: begin
                    txd     <= 1'b1;
                    tx_busy <= 0;
                    if (tx_req) begin
                        tx_state      <= 1'b1;
                        tx_busy       <= 1'b1;
                        tx_shift_reg  <= {1'b1, tx_data, 1'b0};
                        tx_bit_cnt    <= 0;
                    end
                end
                1'b1: begin
                    if (baud_tick) begin
                        txd <= tx_shift_reg[0];
                        tx_shift_reg <= {1'b1, tx_shift_reg[9:1]};
                        tx_bit_cnt   <= tx_bit_cnt + 1;
                        if (tx_bit_cnt == 4'd9)
                            tx_state <= 1'b0;
                    end
                end
            endcase
        end
    end

    //==================== RX 同步 & 下降沿 ====================
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rxd_sync_0 <= 0;
            rxd_sync_1 <= 0;
        end else begin
            rxd_sync_0 <= rxd;
            rxd_sync_1 <= rxd_sync_0;
        end
    end

    assign rxd_negedge = rxd_sync_1 && !rxd_sync_0;

    //==================== RX 波特率计数 ====================
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n)
            rx_baud_cnt <= 0;
        else if (rx_state == 0)
            rx_baud_cnt <= 0;
        else if (rx_baud_cnt == BAUD_CNT_MAX)
            rx_baud_cnt <= 0;
        else
            rx_baud_cnt <= rx_baud_cnt + 1;
    end

    //==================== RX 接收状态机 ====================
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            rx_state   <= 0;
            rx_bit_cnt <= 0;
            rx_valid   <= 0;
            rx_shift_reg <= 0;
            rx_data    <= 0;
        end else begin
            case (rx_state)
                2'd0: begin
                    rx_valid <= 0;
                    if (rxd_negedge)
                        rx_state <= 2'd1;
                end
                2'd1: begin
                    if (rx_baud_cnt == BAUD_HALF)
                        rx_state <= 2'd2;
                end
                2'd2: begin
                    if (rx_baud_cnt == BAUD_HALF) begin
                        if (rx_bit_cnt == 3'd7) begin
                            rx_data  <= {rxd_sync_0, rx_shift_reg};
                            rx_valid <= 1;
                            rx_state <= 2'd3;
                        end else begin
                            rx_shift_reg <= {rxd_sync_0, rx_shift_reg[6:1]};
                            rx_bit_cnt   <= rx_bit_cnt + 1;
                        end
                    end
                end
                2'd3: begin
                    rx_valid <= 0;
                    rx_state <= 0;
                end
            endcase
        end
    end

endmodule

Verible插件

在这里插入图片描述
在这里插入图片描述

想深耕嵌入式?这个专辑值得收藏

MCU、FPGA、工控、传感器一站式学习,实战项目直接抄

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值