lin-win-share/da4008_case_gen/da4008_gen_transaction.py

382 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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()