lin-win-share/da4008_case_gen/da4008_gen_transaction.py

382 lines
15 KiB
Python
Raw Normal View History

2026-03-17 10:39:55 +08:00
#!/usr/bin/env python3
"""
DA4008 波形生成与transaction帧封装工具
支持生成正弦波三角波方波Flattop波形并自动生成对应的控制命令
将命令和波形数据封装为两个transaction帧输出到指定文件
同时生成一个LVDS数据帧包含帧头地址/长度波形数据CRC用于LVDS_DRIVER发送
LVDS帧格式每行一个32位十六进制数
[帧头] 0xBCBCBCBC
[地址/长度] (lvds_addr << 16) | num_words
[波形数据0] ...
[波形数据N]
[CRC32] 从地址/长度字到最后一个数据字的CRC校验
帧格式64位头部 + 数据
W/R=0, ARD_FLAG=0, CID=0, EXADDR=0
cmd帧 ADDR=0x100000, wave帧 ADDR=0x200000
长度字段根据实际数据字节数计算
输出文件每行一个32位十六进制数顺序为
[cmd帧高32位]
[cmd帧低32位]
cmd命令数据每行一个32位命令字
[wave帧高32位]
[wave帧低32位]
wave波形数据每行一个32位字512-bit块打包后的结果
"""
import argparse
import math
import sys
import os
# 常数
FS = 40e9 # 采样率 40 GHz
MAX_ADDR = 8192 # SRAM深度
CMD_WAVE = 0x00000000 # 波形命令标志位bit31=0
CMD_HOLD = 0x80000000 # 保持命令标志位bit31=1
def parse_args():
parser = argparse.ArgumentParser(description='为DA4008生成波形与命令并封装为transaction帧同时生成LVDS数据帧')
parser.add_argument('--type', required=True, choices=['sine', 'triangle', 'pulse', 'flattop'],
help='波形类型: sine, triangle, pulse, flattop')
parser.add_argument('--start_addr', type=int, required=True,
help='波形起始地址 (0-8191)')
parser.add_argument('--length', type=int,
help='波形样本点数 (对sine/triangle/pulse有效必须为正整数)')
parser.add_argument('--cycles', type=int, default=1,
help='循环次数 (0-31, 0表示无限循环, 默认1)')
# 正弦波参数
parser.add_argument('--freq', type=float, help='正弦波频率 (Hz)')
# 三角波参数
parser.add_argument('--step', type=int, help='三角波步进 (1-255)')
parser.add_argument('--duration', type=int, help='三角波每个台阶的持续时间 (时钟周期数)')
# 方波参数
parser.add_argument('--duty', type=float, default=0.5,
help='方波占空比 (0.0-1.0, 默认0.5)')
# Flattop参数
parser.add_argument('--rise_samples', type=int,
help='Flattop上升沿样本数')
parser.add_argument('--hold_cycles', type=int,
help='Flattop平台保持周期数')
parser.add_argument('--fall_samples', type=int,
help='Flattop下降沿样本数')
parser.add_argument('--hold_value', type=int, default=255,
help='Flattop平台值 (0-255, 默认255)')
# 输出文件
parser.add_argument('--output', required=True,
help='输出transaction文件路径')
# LVDS参数
parser.add_argument('--lvds_addr', type=lambda x: int(x, 0), required=True,
help='LVDS帧中的地址 (支持十六进制如0x1234)')
parser.add_argument('--lvds_output', type=str,
help='LVDS数据帧输出文件路径 (默认: 将--output的扩展名改为_lvds.txt)')
return parser.parse_args()
# ================== 波形生成函数 ==================
def generate_sine(num_samples, freq):
"""生成正弦波样本 (8-bit无符号)"""
if freq <= 0 or freq > FS/2:
raise ValueError(f"频率必须介于0和{FS/2:.1f} Hz之间")
samples_per_cycle = FS / freq
if samples_per_cycle < 1:
raise ValueError("频率太高每个周期样本数小于1")
samples = []
for i in range(num_samples):
phase = 2 * math.pi * i / samples_per_cycle
val = int(128 + 127 * math.sin(phase))
samples.append(val)
return samples
def generate_triangle(num_samples, step, duration):
"""生成三角波样本 (8-bit无符号)"""
if step < 1 or step > 255:
raise ValueError("步进必须介于1和255之间")
if duration < 1:
raise ValueError("持续时间必须为正整数")
samples = []
direction = 1
current = 0
cnt = 0
while len(samples) < num_samples:
samples.append(current)
cnt += 1
if cnt >= duration:
cnt = 0
next_val = current + direction * step
if next_val > 255:
next_val = 255
direction = -1
elif next_val < 0:
next_val = 0
direction = 1
current = next_val
return samples
def generate_pulse(num_samples, freq, duty):
"""生成方波样本 (8-bit, 高=255, 低=0)"""
if freq <= 0 or freq > FS/2:
raise ValueError(f"频率必须介于0和{FS/2:.1f} Hz之间")
if duty < 0 or duty > 1:
raise ValueError("占空比必须介于0和1之间")
period = FS / freq
high_samples = int(round(period * duty))
low_samples = int(round(period * (1 - duty)))
if high_samples + low_samples == 0:
raise ValueError("频率和占空比导致零样本周期")
samples = []
while len(samples) < num_samples:
for _ in range(high_samples):
samples.append(255)
if len(samples) >= num_samples:
break
for _ in range(low_samples):
samples.append(0)
if len(samples) >= num_samples:
break
return samples
def generate_flattop(rise_samples, hold_cycles, fall_samples, hold_value=255):
"""生成Flattop波形上升沿和下降沿样本平台不存储
返回 (rise_samples_list, fall_samples_list)
"""
if rise_samples < 1 or fall_samples < 1:
raise ValueError("上升沿和下降沿样本数必须至少为1")
if hold_cycles < 0:
raise ValueError("保持周期数不能为负")
# 上升沿线性从0到hold_value
rise = []
for i in range(rise_samples):
val = int(hold_value * i / (rise_samples - 1))
rise.append(val)
# 下降沿线性从hold_value到0
fall = []
for i in range(fall_samples):
val = int(hold_value * (fall_samples - 1 - i) / (fall_samples - 1))
fall.append(val)
return rise, fall
def pack_samples_to_blocks(samples):
"""将样本列表按每64个一组打包成512-bit字列表
返回列表每个元素为16个32-bit字的列表每个32-bit字以整数表示
"""
blocks = []
if len(samples) == 0:
return blocks
# 补零到64的倍数
if len(samples) % 64 != 0:
samples += [0] * (64 - len(samples) % 64)
for i in range(0, len(samples), 64):
block_samples = samples[i:i+64]
words = []
for j in range(0, 64, 4):
word = (block_samples[j+3] << 24) | (block_samples[j+2] << 16) | \
(block_samples[j+1] << 8) | block_samples[j]
words.append(word)
blocks.append(words)
return blocks
def generate_wave_command(start_addr, length_words, cycles):
"""生成波形命令字 (32-bit)"""
if start_addr < 0 or start_addr >= MAX_ADDR:
raise ValueError(f"起始地址必须介于0和{MAX_ADDR-1}之间")
if length_words < 1 or length_words > MAX_ADDR:
raise ValueError(f"长度必须介于1和{MAX_ADDR}之间")
if start_addr + length_words > MAX_ADDR:
raise ValueError("地址范围超出SRAM深度")
if cycles < 0 or cycles > 31:
raise ValueError("循环次数必须介于0和31之间")
cmd = CMD_WAVE | (cycles << 26) | (start_addr << 13) | length_words
return cmd
def generate_hold_command(hold_cycles):
"""生成保持命令字"""
if hold_cycles < 0 or hold_cycles > 0x7FFFFFFF:
raise ValueError("保持周期数超出范围 (0-2^31-1)")
return CMD_HOLD | hold_cycles
# ================== CRC32 计算 ==================
def crc32_next(crc, data):
"""按位计算CRC32多项式0x04C11DB7MSB优先"""
poly = 0x04C11DB7
new_crc = crc
for i in range(32):
# 取CRC的最高位
bit = (new_crc >> 31) & 1
# 取数据的最高位MSB first
data_bit = (data >> (31 - i)) & 1
b = bit ^ data_bit
new_crc = (new_crc << 1) & 0xFFFFFFFF
if b:
new_crc ^= poly
return new_crc
def crc32_compute(words):
"""计算一组32位字的CRC32不包括初始异或直接返回"""
crc = 0xFFFFFFFF
for w in words:
crc = crc32_next(crc, w)
return crc
# ================== transaction帧生成 ==================
def generate_transaction_header(addr, length_bytes):
"""
生成64位头部拆分为两个32位字
addr: 25位地址
length_bytes: 20位长度字节数
返回 (high32, low32)
"""
# W/R=0, ARD_FLAG=0, CID=0, EXADDR=0
header = (addr << 32) | length_bytes
high = (header >> 32) & 0xFFFFFFFF
low = header & 0xFFFFFFFF
return high, low
def write_transaction_file(filename, cmd_words, wave_words):
"""
将cmd命令列表和wave数据列表写入transaction文件
cmd_words: 整数列表每个元素是一个32位命令字
wave_words: 整数列表每个元素是一个32位波形数据字
"""
cmd_len_bytes = len(cmd_words) * 4
wave_len_bytes = len(wave_words) * 4
high_cmd, low_cmd = generate_transaction_header(0x100000, cmd_len_bytes)
high_wave, low_wave = generate_transaction_header(0x200000, wave_len_bytes)
with open(filename, 'w') as f:
# cmd帧头部
f.write(f"{high_cmd:08x}\n")
f.write(f"{low_cmd:08x}\n")
# cmd数据
for w in cmd_words:
f.write(f"{w:08x}\n")
# wave帧头部
f.write(f"{high_wave:08x}\n")
f.write(f"{low_wave:08x}\n")
# wave数据
for w in wave_words:
f.write(f"{w:08x}\n")
print(f"已生成transaction文件: {filename}")
print(f" cmd帧: {len(cmd_words)} 字, 长度 {cmd_len_bytes} 字节")
print(f" wave帧: {len(wave_words)} 字, 长度 {wave_len_bytes} 字节")
# ================== LVDS帧生成 ==================
def write_lvds_file(filename, lvds_addr, data_words):
"""
将波形数据打包成LVDS帧写入文件
帧格式: [帧头] [地址/长度] [数据字...] [CRC32]
所有字均为32位每行一个十六进制数
"""
if len(data_words) == 0:
print("警告波形数据为空不生成LVDS帧")
return
header = 0xBCBCBCBC
addr_len = (lvds_addr << 16) | len(data_words) # 低16位为字数
crc = crc32_compute([addr_len] + data_words)
with open(filename, 'w') as f:
f.write(f"{header:08x}\n")
f.write(f"{addr_len:08x}\n")
for w in data_words:
f.write(f"{w:08x}\n")
f.write(f"{crc:08x}\n")
print(f"已生成LVDS数据帧文件: {filename}")
print(f" LVDS地址: 0x{lvds_addr:04x}, 数据字数: {len(data_words)}")
# ================== 主流程 ==================
def main():
args = parse_args()
# 确定LVDS输出文件名
if args.lvds_output is None:
base, ext = os.path.splitext(args.output)
args.lvds_output = base + "_lvds" + ext
if args.type in ['sine', 'triangle', 'pulse']:
if args.length is None:
sys.exit(f"波形类型 {args.type} 需要指定 --length")
# 生成样本
if args.type == 'sine':
if args.freq is None:
sys.exit("正弦波需要指定 --freq")
samples = generate_sine(args.length, args.freq)
elif args.type == 'triangle':
if args.step is None or args.duration is None:
sys.exit("三角波需要指定 --step 和 --duration")
samples = generate_triangle(args.length, args.step, args.duration)
else: # pulse
if args.freq is None:
sys.exit("方波需要指定 --freq")
samples = generate_pulse(args.length, args.freq, args.duty)
# 打包波形数据
blocks = pack_samples_to_blocks(samples)
wave_words = [word for block in blocks for word in block] # 展平为32位字列表
num_words = len(blocks) # 512-bit块数
# 检查地址范围
if args.start_addr + num_words > MAX_ADDR:
sys.exit(f"地址 {args.start_addr} + 块数 {num_words} 超出SRAM深度 {MAX_ADDR}")
# 生成波形命令
cmd_wave = generate_wave_command(args.start_addr, num_words, args.cycles)
cmd_words = [cmd_wave]
# 如果需要hold命令添加
if args.hold_cycles is not None:
cmd_hold = generate_hold_command(args.hold_cycles)
cmd_words.append(cmd_hold)
# 写入transaction文件
write_transaction_file(args.output, cmd_words, wave_words)
# 写入LVDS文件
write_lvds_file(args.lvds_output, args.lvds_addr, wave_words)
elif args.type == 'flattop':
if args.rise_samples is None or args.hold_cycles is None or args.fall_samples is None:
sys.exit("Flattop波形需要指定 --rise_samples, --hold_cycles, --fall_samples")
# 生成上升沿和下降沿样本
rise_samples, fall_samples = generate_flattop(args.rise_samples, args.hold_cycles,
args.fall_samples, args.hold_value)
# 打包上升沿
rise_blocks = pack_samples_to_blocks(rise_samples)
rise_words = [word for block in rise_blocks for word in block]
num_rise_words = len(rise_blocks)
# 打包下降沿
fall_blocks = pack_samples_to_blocks(fall_samples)
fall_words = [word for block in fall_blocks for word in block]
num_fall_words = len(fall_blocks)
# 检查地址范围
if args.start_addr + num_rise_words > MAX_ADDR:
sys.exit(f"上升沿地址 {args.start_addr} + 块数 {num_rise_words} 超出SRAM深度 {MAX_ADDR}")
fall_start_addr = args.start_addr + num_rise_words
if fall_start_addr + num_fall_words > MAX_ADDR:
sys.exit(f"下降沿起始地址 {fall_start_addr} + 块数 {num_fall_words} 超出SRAM深度 {MAX_ADDR}")
# 生成命令
cmd_rise = generate_wave_command(args.start_addr, num_rise_words, args.cycles)
cmd_hold = generate_hold_command(args.hold_cycles)
cmd_fall = generate_wave_command(fall_start_addr, num_fall_words, args.cycles)
cmd_words = [cmd_rise, cmd_hold, cmd_fall]
# 波形数据 = 上升沿 + 下降沿
wave_words = rise_words + fall_words
# 写入transaction文件
write_transaction_file(args.output, cmd_words, wave_words)
# 写入LVDS文件
write_lvds_file(args.lvds_output, args.lvds_addr, wave_words)
if __name__ == "__main__":
main()