项目简介

这是一个用Python实现的Verilog信号管脚映射工具,可以将Verilog代码中的IO信号定义转换为FPGA/CPLD开发工具可用的管脚约束文件。目前支持生成以下两种格式:

  1. Intel Quartus Prime的QSF格式管脚约束文件(io.qsf

  2. ADC格式的管脚约束文件(io.adc

git链接:OpenGit - io_map.py

功能特点

  • 支持input、output和inout类型的信号定义

  • 支持reg、wire类型声明

  • 自动进行信号名和管脚格式的合法性检查

  • 完整的日志记录系统,支持文件和控制台双重输出

  • 详细的错误处理和统计信息

使用方法

1. 输入文件格式

io_config.txt中按以下格式定义信号:

input  signal_name      // pin_number
output reg signal_name  // pin_number
inout  signal_name      // pin_number

示例:

output reg GPU_X100_VDD_EN,     // H3
input  PCIE_X100_REFCLK_N,      // A28
inout  PCIE_X100_REFCLK_P,      // A29

2. 运行程序

python io_mapper.py

3. 输出文件

程序会生成以下文件:

  • io.qsf:Intel Quartus Prime格式的管脚约束

  • io.adc:ADC格式的管脚约束

  • io_mapping.log:详细的处理日志

实现细节

1. 信号格式解析

使用正则表达式进行信号定义解析,支持以下格式:

  • 信号类型:input、output、inout

  • 可选的reg、wire声明

  • 信号名:必须以字母或下划线开头

  • 管脚号:字母+1~2位数字(如:A1、H12)

  • 支持带逗号和不带逗号的行

  • 支持行尾注释

2. 有效性检查

  • 信号名格式检查:必须符合Verilog标识符规范

  • 管脚格式检查:必须符合字母+数字的格式

  • 注释格式检查:支持标准注释和四斜杠注释

3. 输出格式

QSF格式

set_location_assignment PIN_H3 -to GPU_X100_VDD_EN

ADC格式

set_pin_assignment { GPU_X100_VDD_EN } { LOCATION = H3; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 20; PULLTYPE = NONE; }

4. 日志系统

  • 记录所有处理过程

  • 包含成功映射、警告和错误信息

  • 提供详细的统计数据

  • 支持同时输出到文件和控制台

错误处理

  1. 跳过以下类型的行:

    • 包含四个斜杠(////)的注释行

    • 格式不匹配的行

    • 非法信号名(不符合Verilog标识符规范)

    • 非法管脚格式

  2. 异常处理:

    • 文件不存在异常

    • 文件读写异常

    • 其他运行时异常

统计信息

程序运行完成后会输出以下统计数据:

  • 总处理行数

  • 成功映射数量

  • 跳过的行数

  • 错误行数

  • 生成的文件列表

注意事项

  1. 输入文件必须使用UTF-8编码

  2. 信号名区分大小写(遵循Verilog规范)

  3. 管脚号会自动转换为大写

  4. 程序会自动跳过无法解析的行,并在日志中记录详细信息

开发环境

  • Python 3.6+

  • 依赖模块:

    • re(正则表达式)

    • logging(日志系统)

    • datetime(时间戳)

代码

import re
import logging
from datetime import datetime


def setup_logger():
    """配置日志系统,同时输出到文件和控制台"""
    logger = logging.getLogger("io_mapping")
    logger.setLevel(logging.INFO)

    formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")

    # 文件处理器
    file_handler = logging.FileHandler("io_mapping.log", encoding="utf-8")
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)

    # 控制台处理器
    console_handler = logging.StreamHandler()
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)

    return logger


def process_io_config():
    # 初始化日志系统
    logger = setup_logger()
    logger.info("开始处理IO映射配置")

    # 存储QSF和ADC语句
    qsf_lines = []
    adc_lines = []
    total_lines = 0
    success_count = 0
    skip_count = 0
    error_count = 0

    # 定义正则表达式模式
    pattern = r"""
        ^\s*                         # 行首空白
        (input|output|inout)\s+      # 输入输出类型(Group1)
        (reg\s+|wire\s+)?           # 可选reg或wire声明(Group2)
        (\w+)                        # 信号名称(Group3)
        \s*(?:,.*?)?                 # 可选逗号及后续内容
        //\s*                        # 注释开始符
        ([A-Za-z]\d{1,2})            # 管脚位置(Group4)
        (?=\s|$)                     # 确保管脚后是空格或行尾
    """

    try:
        # 读取输入文件
        with open("io_config.txt", "r", encoding="utf-8") as f:
            lines = f.readlines()

        # 逐行处理
        for line_num, line in enumerate(lines, 1):
            total_lines += 1
            line = line.strip()

            # 跳过4斜杠注释行
            if "//" in line and line.count("/") >= 4:
                logger.info(f"跳过映射 @ Line {line_num}: 4斜杠注释行 - {line}")
                skip_count += 1
                continue

            # 使用正则表达式匹配
            match = re.search(pattern, line, re.VERBOSE)
            if not match:
                logger.warning(f"跳过映射 @ Line {line_num}: 格式不匹配 - {line}")
                error_count += 1
                continue

            # 提取信号名和管脚
            io_type = match.group(1)
            signal = match.group(3)
            pin = match.group(4).upper()

            # 信号名有效性检查
            if not re.match(r"^[a-zA-Z_]\w*$", signal):
                logger.warning(
                    f"跳过映射 @ Line {line_num}: 非法信号名 '{signal}' - 信号名必须以字母或下划线开头"
                )
                error_count += 1
                continue

            # 管脚格式检查
            if not re.match(r"^[A-Za-z]\d{1,2}$", pin):
                logger.warning(
                    f"跳过映射 @ Line {line_num}: 非法管脚格式 '{pin}' - 管脚格式必须为字母+1~2位数字"
                )
                error_count += 1
                continue

            # 生成QSF和ADC语句
            qsf_line = f"set_location_assignment PIN_{pin} -to {signal}\n"
            adc_line = f"set_pin_assignment {{ {signal} }} {{ LOCATION = {pin}; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 20; PULLTYPE = NONE; }}\n"
            
            qsf_lines.append(qsf_line)
            adc_lines.append(adc_line)
            success_count += 1
            logger.info(f"成功映射: {io_type} {signal} -> PIN_{pin}")

        # 写入QSF输出文件
        with open("io.qsf", "w", encoding="utf-8") as f:
            f.writelines(qsf_lines)

        # 写入ADC输出文件
        with open("io.adc", "w", encoding="utf-8") as f:
            f.writelines(adc_lines)

        # 输出统计信息
        logger.info("处理完成!统计信息:")
        logger.info(f"- 总处理行数:{total_lines}")
        logger.info(f"- 成功映射数:{success_count}")
        logger.info(f"- 跳过行数:{skip_count}")
        logger.info(f"- 错误行数:{error_count}")
        logger.info(f"- 生成文件:io.qsf, io.adc")

    except FileNotFoundError:
        logger.error("错误:找不到输入文件 io_config.txt")
    except Exception as e:
        logger.error(f"处理过程中发生错误:{str(e)}")
    finally:
        # 关闭日志处理器
        for handler in logger.handlers[:]:
            handler.close()
            logger.removeHandler(handler)


if __name__ == "__main__":
    process_io_config()

许可证

MIT License


本站由 小马 使用 Stellar 创建。