Files
DTU-HMI/docs/绘图/SPI_Quad.py

300 lines
17 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.
import xml.etree.ElementTree as ET
def make_qspi_quad_io_mxfile():
# ========== 基础参数定义 ==========
# Y坐标高/低电平严格遵循垂直间距100px规范
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
y_io2_high, y_io2_low = 480, 510
y_io3_high, y_io3_low = 580, 610
# 时序参数50px/周期,总周期数=8(命令)+3(地址)+2(空周期)+2(数据)=15
cycle_w = 50
start_x = 100
n_cycles = 15
end_x = start_x + n_cycles * cycle_w # 100 + 15*50 = 850
tail_x = end_x + 50 # 900
# 时钟边沿计算(上升沿=采样沿,下降沿=输出沿)
rise_edges = [start_x + i*cycle_w + cycle_w//2 for i in range(n_cycles)] # 125,175...825
fall_edges = [start_x + (i+1)*cycle_w for i in range(n_cycles)] # 150,200...850
# ========== 数据定义 ==========
# 1. 命令阶段IO0传输0xEB (11101011)MSB firstIO1-IO3 idle(0)
cmd_bits_io0 = [1,1,1,0,1,0,1,1] # 0xEB = b11101011
cmd_bits_io1 = [0]*8
cmd_bits_io2 = [0]*8
cmd_bits_io3 = [0]*8
# 2. 地址阶段3个周期4线并行传输地址位示例地址0x123456的高12位
# 地址0x123456 = 24位0001 0010 0011 0100 0101 0110
# A23=0,A22=0,A21=0,A20=1 | A19=0,A18=0,A17=1,A16=0 | A15=0,A14=0,A13=1,A12=1
addr_bits_io0 = [0, 0, 0] # A23, A19, A15
addr_bits_io1 = [0, 0, 0] # A22, A18, A14
addr_bits_io2 = [0, 1, 1] # A21, A17, A13
addr_bits_io3 = [1, 0, 1] # A20, A16, A12
# 3. 空周期2个周期所有IO idle(0)
dummy_bits = [0]*2
# 4. 数据阶段2个周期传输1字节(0xAA=10101010)4线并行
# D0=1,D1=0,D2=1,D3=0 | D4=1,D5=0,D6=1,D7=0
data_bits_io0 = [1, 1] # D0, D4
data_bits_io1 = [0, 0] # D1, D5
data_bits_io2 = [1, 1] # D2, D6
data_bits_io3 = [0, 0] # D3, D7
# 合并所有阶段的bit序列
all_io0 = cmd_bits_io0 + addr_bits_io0 + dummy_bits + data_bits_io0
all_io1 = cmd_bits_io1 + addr_bits_io1 + dummy_bits + data_bits_io1
all_io2 = cmd_bits_io2 + addr_bits_io2 + dummy_bits + data_bits_io2
all_io3 = cmd_bits_io3 + addr_bits_io3 + dummy_bits + data_bits_io3
# ========== 波形构建函数 ==========
def build_wave_pts(bits, y_hi, y_lo):
"""生成波形点,确保无斜线、边沿对齐"""
y0 = y_hi if bits[0] else y_lo
pts = [(80, y0), (start_x, y0)] # 起始过渡段
for i in range(len(bits)-1):
curr_fall = fall_edges[i]
y_curr = y_hi if bits[i] else y_lo
y_next = y_hi if bits[i+1] else y_lo
# 添加当前bit的水平段终点下降沿
pts.append((curr_fall, y_curr))
# 电平变化则在下降沿垂直跳变
if y_next != y_curr:
pts.append((curr_fall, y_next))
# 最后一个bit的终点 + 尾部过渡段
last_fall = fall_edges[len(bits)-1]
pts.append((last_fall, y_hi if bits[-1] else y_lo))
pts.append((tail_x, pts[-1][1]))
return pts
# ========== 生成XML内容 ==========
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 Quad I/O Fast Read (0xEB)" id="qspi_quad_io">')
lines.append(' <mxGraphModel dx="1000" dy="700" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1000" pageHeight="700" 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="1000" height="700" as="geometry"/>')
lines.append(' </mxCell>')
# 标题
lines.append(' <mxCell id="title" value="QSPI Quad I/O Fast Read (命令 0xEB)" 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="300" y="20" width="400" 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>')
# 信号标签(左侧)
signal_labels = [
("CS", 85, "label_cs"),
("SCLK", 185, "label_sclk"),
("IO0", 275, "label_io0"),
("IO1", 375, "label_io1"),
("IO2", 475, "label_io2"),
("IO3", 575, "label_io3")
]
for label, y, eid in signal_labels:
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"), # 命令结束: 100+400=500
(start_x + 11*cycle_w, "addr_dummy"),# 地址结束: 100+550=650
(start_x + 13*cycle_w, "dummy_data"),# 空周期结束:100+650=750
]
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="620" 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 波形 ==========
io0_pts = build_wave_pts(all_io0, y_io0_high, 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)
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>')
# ========== IO2 波形 ==========
io2_pts = build_wave_pts(all_io2, y_io2_high, y_io2_low)
lines.append(' <mxCell id="io2" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#FFC107;rounded=0;endArrow=none;" edge="1" parent="1">')
lines.append(' <mxGeometry relative="0" as="geometry">')
lines.append(f' <mxPoint x="{io2_pts[0][0]}" y="{io2_pts[0][1]}" as="sourcePoint"/>')
lines.append(f' <mxPoint x="{io2_pts[-1][0]}" y="{io2_pts[-1][1]}" as="targetPoint"/>')
lines.append(' <Array as="points">')
for x, y in io2_pts[1:-1]:
lines.append(f' <mxPoint x="{x}" y="{y}"/>')
lines.append(' </Array>')
lines.append(' </mxGeometry>')
lines.append(' </mxCell>')
# ========== IO3 波形 ==========
io3_pts = build_wave_pts(all_io3, y_io3_high, y_io3_low)
lines.append(' <mxCell id="io3" value="" style="edgeStyle=none;html=1;strokeWidth=2;strokeColor=#607D8B;rounded=0;endArrow=none;" edge="1" parent="1">')
lines.append(' <mxGeometry relative="0" as="geometry">')
lines.append(f' <mxPoint x="{io3_pts[0][0]}" y="{io3_pts[0][1]}" as="sourcePoint"/>')
lines.append(f' <mxPoint x="{io3_pts[-1][0]}" y="{io3_pts[-1][1]}" as="targetPoint"/>')
lines.append(' <Array as="points">')
for x, y in io3_pts[1:-1]:
lines.append(f' <mxPoint x="{x}" y="{y}"/>')
lines.append(' </Array>')
lines.append(' </mxGeometry>')
lines.append(' </mxCell>')
# ========== 阶段名称标注 ==========
stage_labels = [
(start_x + 4*cycle_w, "命令 (0xEB)", "#4CAF50"), # 命令阶段中心
(start_x + 9.5*cycle_w, "地址 (Quad)", "#666666"), # 地址阶段中心
(start_x + 12*cycle_w, "空周期 (2)", "#666666"), # 空周期中心
(start_x + 14*cycle_w, "数据 (Quad 0xAA)", "#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>')
# ========== 数据位标签 ==========
# 1. 命令阶段IO0 bit标签
for i in range(8):
cx = rise_edges[i]
lines.append(f' <mxCell id="cmd_io0_{i}" value="{cmd_bits_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>')
# 2. 地址阶段bit标签
addr_io_labels = [
(8, "A23", "A19", "A15", "#4CAF50"), # IO0
(8, "A22", "A18", "A14", "#9C27B0"), # IO1
(8, "A21", "A17", "A13", "#FFC107"), # IO2
(8, "A20", "A16", "A12", "#607D8B"), # IO3
]
for io_idx, label1, label2, label3, color in addr_io_labels:
# 地址阶段第1个周期总第9个周期
cx = rise_edges[io_idx]
lines.append(f' <mxCell id="addr_io{io_idx-8}_{0}" value="{label1}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontColor={color};" vertex="1" parent="1">')
lines.append(f' <mxGeometry x="{cx-15}" y="{255+100*(io_idx-8)}" width="30" height="20" as="geometry"/>')
lines.append(' </mxCell>')
# 地址阶段第2个周期总第10个周期
cx = rise_edges[io_idx+1]
lines.append(f' <mxCell id="addr_io{io_idx-8}_{1}" value="{label2}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontColor={color};" vertex="1" parent="1">')
lines.append(f' <mxGeometry x="{cx-15}" y="{255+100*(io_idx-8)}" width="30" height="20" as="geometry"/>')
lines.append(' </mxCell>')
# 地址阶段第3个周期总第11个周期
cx = rise_edges[io_idx+2]
lines.append(f' <mxCell id="addr_io{io_idx-8}_{2}" value="{label3}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontColor={color};" vertex="1" parent="1">')
lines.append(f' <mxGeometry x="{cx-15}" y="{255+100*(io_idx-8)}" width="30" height="20" as="geometry"/>')
lines.append(' </mxCell>')
# 3. 数据阶段bit标签
data_io_labels = [
(13, "D0", "D4", "#4CAF50"), # IO0
(13, "D1", "D5", "#9C27B0"), # IO1
(13, "D2", "D6", "#FFC107"), # IO2
(13, "D3", "D7", "#607D8B"), # IO3
]
for io_idx, label1, label2, color in data_io_labels:
# 数据阶段第1个周期总第14个周期
cx = rise_edges[io_idx]
lines.append(f' <mxCell id="data_io{io_idx-13}_{0}" value="{label1}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontColor={color};" vertex="1" parent="1">')
lines.append(f' <mxGeometry x="{cx-10}" y="{255+100*(io_idx-13)}" width="20" height="20" as="geometry"/>')
lines.append(' </mxCell>')
# 数据阶段第2个周期总第15个周期
cx = rise_edges[io_idx+1]
lines.append(f' <mxCell id="data_io{io_idx-13}_{1}" value="{label2}" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=10;fontColor={color};" vertex="1" parent="1">')
lines.append(f' <mxGeometry x="{cx-10}" y="{255+100*(io_idx-13)}" 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_quad_io_mxfile()
output_path = "SPI_Quad.drawio"
with open(output_path, "w", encoding="utf-8") as f:
f.write(xml_content)
print(f"波形图文件已生成: {output_path}")