项目简介
这是一个用Python实现的Verilog信号管脚映射工具,可以将Verilog代码中的IO信号定义转换为FPGA/CPLD开发工具可用的管脚约束文件。目前支持生成以下两种格式:
Intel Quartus Prime的QSF格式管脚约束文件(
io.qsf)ADC格式的管脚约束文件(
io.adc)
git链接:
功能特点
支持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, // A292. 运行程序
python io_mapper.py3. 输出文件
程序会生成以下文件:
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_ENADC格式
set_pin_assignment { GPU_X100_VDD_EN } { LOCATION = H3; IOSTANDARD = LVCMOS33; DRIVESTRENGTH = 20; PULLTYPE = NONE; }4. 日志系统
记录所有处理过程
包含成功映射、警告和错误信息
提供详细的统计数据
支持同时输出到文件和控制台
错误处理
跳过以下类型的行:
包含四个斜杠(////)的注释行
格式不匹配的行
非法信号名(不符合Verilog标识符规范)
非法管脚格式
异常处理:
文件不存在异常
文件读写异常
其他运行时异常
统计信息
程序运行完成后会输出以下统计数据:
总处理行数
成功映射数量
跳过的行数
错误行数
生成的文件列表
注意事项
输入文件必须使用UTF-8编码
信号名区分大小写(遵循Verilog规范)
管脚号会自动转换为大写
程序会自动跳过无法解析的行,并在日志中记录详细信息
开发环境
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