208 lines
11 KiB
Python
208 lines
11 KiB
Python
import xml.etree.ElementTree as ET
|
||
|
||
def make_qspi_mxfile():
|
||
# ========== 基础参数 ==========
|
||
y_cs_high, y_cs_low = 80, 110
|
||
y_sclk_high, y_sclk_low = 180, 210
|
||
y_io0_high, y_io0_low = 280, 310
|
||
y_io1_high, y_io1_low = 380, 410
|
||
|
||
# 时序参数:50px/周期,36个周期(8命令+24地址+4数据)
|
||
cycle_w = 50
|
||
start_x = 100
|
||
n_cycles = 36 # 8 + 24 + 4
|
||
end_x = start_x + n_cycles * cycle_w # 1900
|
||
tail_x = end_x + 50 # 1950
|
||
|
||
rise_edges = [start_x + i*cycle_w + cycle_w//2 for i in range(n_cycles)] # 125,175...
|
||
fall_edges = [start_x + (i+1)*cycle_w for i in range(n_cycles)] # 150,200...1900
|
||
|
||
# 数据定义
|
||
cmd_bits = [0,0,1,1,1,0,1,1] # 0x3B MSB first
|
||
addr_bytes = [
|
||
[0,0,0,1,0,0,1,0], # 0x12 A23-A16
|
||
[0,0,1,1,0,1,0,0], # 0x34 A15-A8
|
||
[0,1,0,1,0,1,1,0], # 0x56 A7-A0
|
||
]
|
||
addr_bits = [b for byte in addr_bytes for b in byte]
|
||
data_io0 = [0,0,0,0] # D0,D2,D4,D6 (示例 0xAA)
|
||
data_io1 = [1,1,1,1] # D1,D3,D5,D7
|
||
|
||
all_io0 = cmd_bits + addr_bits + data_io0
|
||
all_io1 = [0]*32 + data_io1 # 命令地址期间 IO1 idle/low
|
||
|
||
lines = []
|
||
lines.append('<?xml version="1.0" encoding="UTF-8"?>')
|
||
lines.append('<mxfile host="app.diagrams.net" modified="2024-05-25T00:00:00.000Z" agent="AI" version="21.0.0" type="device">')
|
||
lines.append(' <diagram name="QSPI Waveform" id="qspi_waveform">')
|
||
lines.append(' <mxGraphModel dx="2000" dy="600" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="2000" pageHeight="600" math="0" shadow="0">')
|
||
lines.append(' <root>')
|
||
lines.append(' <mxCell id="0"/>')
|
||
lines.append(' <mxCell id="1" parent="0"/>')
|
||
|
||
# 背景
|
||
lines.append(' <mxCell id="grid" value="" style="points=[];gridColor=#e0e0e0;gridSize=20;spacingTop=20;spacingLeft=20;spacingBottom=20;spacingRight=20;html=1;" vertex="1" parent="1">')
|
||
lines.append(f' <mxGeometry x="0" y="0" width="2100" height="600" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# 标题
|
||
lines.append(' <mxCell id="title" value="QSPI Fast Read Dual Output (命令 0x3B)" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1;" vertex="1" parent="1">')
|
||
lines.append(' <mxGeometry x="600" y="20" width="500" height="30" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# MSB / LSB
|
||
lines.append(' <mxCell id="msb" value="MSB" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=12;fontColor=#666666;fontStyle=1;" vertex="1" parent="1">')
|
||
lines.append(f' <mxGeometry x="{start_x+10}" y="60" width="40" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
lines.append(' <mxCell id="lsb" value="LSB" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=12;fontColor=#666666;fontStyle=1;" vertex="1" parent="1">')
|
||
lines.append(f' <mxGeometry x="{end_x-40}" y="60" width="40" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# 信号标签
|
||
for label, y, eid in [("CS", 85, "label_cs"), ("SCLK", 185, "label_sclk"),
|
||
("IO0", 275, "label_io0"), ("IO1", 375, "label_io1")]:
|
||
lines.append(f' <mxCell id="{eid}" value="{label}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=14;fontStyle=1;" vertex="1" parent="1">')
|
||
lines.append(f' <mxGeometry x="20" y="{y}" width="50" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# 阶段分界虚线
|
||
boundaries = [
|
||
(start_x + 8*cycle_w, "cmd_addr"), # 500
|
||
(start_x + 16*cycle_w, "addr_mid1"), # 900
|
||
(start_x + 24*cycle_w, "addr_mid2"), # 1300
|
||
(start_x + 32*cycle_w, "addr_data"), # 1700
|
||
]
|
||
for x, eid in boundaries:
|
||
lines.append(f' <mxCell id="vl_{eid}" value="" style="endArrow=none;html=1;strokeWidth=1;strokeColor=#CCCCCC;dashed=1;" edge="1" parent="1">')
|
||
lines.append(' <mxGeometry width="50" height="50" relative="1" as="geometry">')
|
||
lines.append(f' <mxPoint x="{x}" y="180" as="sourcePoint"/>')
|
||
lines.append(f' <mxPoint x="{x}" y="420" as="targetPoint"/>')
|
||
lines.append(' </mxGeometry>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# ========== CS 波形 ==========
|
||
cs_pts = [(80, y_cs_high), (90, y_cs_high), (90, y_cs_low), (tail_x, y_cs_low), (tail_x, y_cs_high), (tail_x+10, y_cs_high)]
|
||
lines.append(' <mxCell id="cs" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#FF5722;rounded=0;endArrow=none;" edge="1" parent="1">')
|
||
lines.append(' <mxGeometry relative="0" as="geometry">')
|
||
lines.append(f' <mxPoint x="{cs_pts[0][0]}" y="{cs_pts[0][1]}" as="sourcePoint"/>')
|
||
lines.append(f' <mxPoint x="{cs_pts[-1][0]}" y="{cs_pts[-1][1]}" as="targetPoint"/>')
|
||
lines.append(' <Array as="points">')
|
||
for x, y in cs_pts[1:-1]:
|
||
lines.append(f' <mxPoint x="{x}" y="{y}"/>')
|
||
lines.append(' </Array>')
|
||
lines.append(' </mxGeometry>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# ========== SCLK 波形 ==========
|
||
sclk_pts = [(80, y_sclk_low), (start_x, y_sclk_low)]
|
||
for i in range(n_cycles):
|
||
rise = rise_edges[i]
|
||
fall = fall_edges[i]
|
||
sclk_pts.extend([
|
||
(rise, y_sclk_low),
|
||
(rise, y_sclk_high),
|
||
(fall, y_sclk_high),
|
||
(fall, y_sclk_low),
|
||
])
|
||
sclk_pts.append((tail_x, y_sclk_low))
|
||
|
||
lines.append(' <mxCell id="sclk" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#2196F3;rounded=0;endArrow=none;" edge="1" parent="1">')
|
||
lines.append(' <mxGeometry relative="0" as="geometry">')
|
||
lines.append(f' <mxPoint x="{sclk_pts[0][0]}" y="{sclk_pts[0][1]}" as="sourcePoint"/>')
|
||
lines.append(f' <mxPoint x="{sclk_pts[-1][0]}" y="{sclk_pts[-1][1]}" as="targetPoint"/>')
|
||
lines.append(' <Array as="points">')
|
||
for x, y in sclk_pts[1:-1]:
|
||
lines.append(f' <mxPoint x="{x}" y="{y}"/>')
|
||
lines.append(' </Array>')
|
||
lines.append(' </mxGeometry>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# ========== IO0 波形 ==========
|
||
def build_wave_pts(bits, y_hi, y_lo, idle_y):
|
||
y0 = y_hi if bits[0] else y_lo
|
||
pts = [(80, y0), (start_x, y0)]
|
||
for i in range(len(bits)-1):
|
||
end_x = fall_edges[i]
|
||
y_curr = y_hi if bits[i] else y_lo
|
||
y_next = y_hi if bits[i+1] else y_lo
|
||
pts.append((end_x, y_curr))
|
||
if y_next != y_curr:
|
||
pts.append((end_x, y_next))
|
||
pts.append((end_x := fall_edges[len(bits)-1], y_hi if bits[-1] else y_lo))
|
||
pts.append((tail_x, pts[-1][1]))
|
||
return pts
|
||
|
||
io0_pts = build_wave_pts(all_io0, y_io0_high, y_io0_low, y_io0_low)
|
||
lines.append(' <mxCell id="io0" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#4CAF50;rounded=0;endArrow=none;" edge="1" parent="1">')
|
||
lines.append(' <mxGeometry relative="0" as="geometry">')
|
||
lines.append(f' <mxPoint x="{io0_pts[0][0]}" y="{io0_pts[0][1]}" as="sourcePoint"/>')
|
||
lines.append(f' <mxPoint x="{io0_pts[-1][0]}" y="{io0_pts[-1][1]}" as="targetPoint"/>')
|
||
lines.append(' <Array as="points">')
|
||
for x, y in io0_pts[1:-1]:
|
||
lines.append(f' <mxPoint x="{x}" y="{y}"/>')
|
||
lines.append(' </Array>')
|
||
lines.append(' </mxGeometry>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# ========== IO1 波形 ==========
|
||
io1_pts = build_wave_pts(all_io1, y_io1_high, y_io1_low, y_io1_low)
|
||
lines.append(' <mxCell id="io1" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#9C27B0;rounded=0;endArrow=none;" edge="1" parent="1">')
|
||
lines.append(' <mxGeometry relative="0" as="geometry">')
|
||
lines.append(f' <mxPoint x="{io1_pts[0][0]}" y="{io1_pts[0][1]}" as="sourcePoint"/>')
|
||
lines.append(f' <mxPoint x="{io1_pts[-1][0]}" y="{io1_pts[-1][1]}" as="targetPoint"/>')
|
||
lines.append(' <Array as="points">')
|
||
for x, y in io1_pts[1:-1]:
|
||
lines.append(f' <mxPoint x="{x}" y="{y}"/>')
|
||
lines.append(' </Array>')
|
||
lines.append(' </mxGeometry>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# ========== 命令 bit 标签(IO0 上方) ==========
|
||
cmd_labels = ["0","0","1","1","1","0","1","1"]
|
||
for i, val in enumerate(cmd_labels):
|
||
cx = rise_edges[i]
|
||
lines.append(f' <mxCell id="cmd{i}" value="{val}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;fontColor=#4CAF50;" vertex="1" parent="1">')
|
||
lines.append(f' <mxGeometry x="{cx-10}" y="255" width="20" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# ========== 阶段名称标签 ==========
|
||
stage_labels = [
|
||
(start_x + 4*cycle_w, "命令 (0x3B)", "#4CAF50"),
|
||
(start_x + 12*cycle_w, "地址 [23:16]", "#666666"),
|
||
(start_x + 20*cycle_w, "地址 [15:8]", "#666666"),
|
||
(start_x + 28*cycle_w, "地址 [7:0]", "#666666"),
|
||
(start_x + 34*cycle_w, "数据 (Dual)", "#2196F3"),
|
||
]
|
||
for x, txt, color in stage_labels:
|
||
lines.append(f' <mxCell id="stage_{txt[:4]}" value="{txt}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=13;fontStyle=1;fontColor={color};" vertex="1" parent="1">')
|
||
lines.append(f' <mxGeometry x="{x-60}" y="235" width="120" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
# ========== 数据阶段 bit 标签 ==========
|
||
data_labels_io0 = ["D0","D2","D4","D6"]
|
||
data_labels_io1 = ["D1","D3","D5","D7"]
|
||
for i in range(4):
|
||
cx = rise_edges[32 + i]
|
||
# IO0
|
||
lines.append(f' <mxCell id="d0_{i}" value="{data_labels_io0[i]}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;fontColor=#4CAF50;" vertex="1" parent="1">')
|
||
lines.append(f' <mxGeometry x="{cx-10}" y="255" width="20" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
# IO1
|
||
lines.append(f' <mxCell id="d1_{i}" value="{data_labels_io1[i]}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=11;fontColor=#9C27B0;" vertex="1" parent="1">')
|
||
lines.append(f' <mxGeometry x="{cx-10}" y="355" width="20" height="20" as="geometry"/>')
|
||
lines.append(' </mxCell>')
|
||
|
||
lines.append(' </root>')
|
||
lines.append(' </mxGraphModel>')
|
||
lines.append(' </diagram>')
|
||
lines.append('</mxfile>')
|
||
|
||
return "\n".join(lines)
|
||
|
||
xml_content = make_qspi_mxfile()
|
||
output_path = "SPI_Waveform_QSPI.drawio"
|
||
with open(output_path, "w", encoding="utf-8") as f:
|
||
f.write(xml_content)
|
||
|
||
print("文件已生成:", output_path)
|