Files
BSI-901-fpga-software/tmp/fpga_doc2/add_submodule_detail.py
T

72 lines
7.3 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.
from pathlib import Path
from docx import Document
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.shared import Cm, Pt
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
DOC = Path(r"D:\Project\spinal_stim\详细设计\spinal_stim_FPGA详细设计文档2.docx")
DIAG = Path(r"D:\Project\spinal_stim\pl\spinal_stim_v2\tmp\fpga_doc2\submodule_diagrams")
def shade(cell, fill="D9E2F3"):
tcPr = cell._tc.get_or_add_tcPr(); shd = OxmlElement("w:shd"); shd.set(qn("w:fill"), fill); tcPr.append(shd)
def h(doc, text, level):
import re
text = re.sub(r"^\d+(?:\.\d+)*\s+", "", text)
return doc.add_paragraph(text, style=f"Heading {level}")
def body(doc, text):
p = doc.add_paragraph(text, style="Normal")
p.paragraph_format.first_line_indent = Cm(0.74)
p.paragraph_format.line_spacing = 1.2
return p
def caption(doc, text):
p = doc.add_paragraph(text, style="Caption"); p.alignment = WD_ALIGN_PARAGRAPH.CENTER; return p
def table(doc, headers, rows):
t = doc.add_table(rows=1, cols=len(headers)); t.style="Table Grid"; t.alignment=WD_TABLE_ALIGNMENT.CENTER; t.autofit=False
for c, val in zip(t.rows[0].cells, headers): c.text=str(val); shade(c)
for row in rows:
cells=t.add_row().cells
for c,val in zip(cells,row): c.text=str(val)
return t
mods = [
("eeg_topEEG采集顶层)", "eeg_config、ads1299_spi、ads1299_if、ads1299_mod、ads1299_arrange、eeg_upload", "clk=100 MHzSPI 时钟由 SPI_CLK_DIV=3 分频;BRAM 配置在 clk 域采样;AXI record 在 clk 域输出。", [("bram_addr/din/en/we", "I", "BRAM_CTRL_1配置读写"),("drdy[1:0]", "I", "ADS1299数据就绪"),("cs/sclk/mosi", "O", "两组采集SPI"),("miso[1:0]", "I", "SPI返回数据"),("axis_record_tdata/valid/last", "O", "24 bit通道数据流")]),
("emg_topEMG采集顶层)", "emg_config、ads1299_spi、ads1299_if、ads1299_mod、eeg_upload", "clk=100 MHzSPI 配置事务与采样事务共享状态机;RESET#、START 在配置阶段产生。", [("bram_addr/din/en/we", "I", "BRAM_CTRL_2配置读写"),("drdy_n", "I", "差分前端就绪"),("reset_n/start", "O", "前端复位和转换启动"),("cs/sclk/mosi/miso", "I/O", "ADS1299 SPI"),("axis_record_tdata/valid/last", "O", "EMG数据流")]),
("upload_top(上传封包)", "upload_config、upload_sort、upload_protocol_ql、upload_protocol_intan", "clk=100 MHz;输入与输出均采用 AXI4-Stream 握手;超时计数以 CLK_FREQ 参数换算。", [("record_tdata/valid/last", "I", "采集小包"),("pre_message/ch_mapping2pc", "I", "帧头和通道映射"),("upload_ch", "I", "通道选择位图"),("upload_tdata/valid/last", "O", "封装后字节流"),("interrupt", "O", "上传事件")]),
("eeg_emg_uploadEEG/EMG合帧)", "EEG FIFO、EMG FIFO、CRC16、选择状态机", "clk=100 MHzEEG/EMG 输入为 8 bit 流;stop_sample 异步于数据功能但在 clk 域清零状态。", [("m_axis_eeg_tdata/valid/last", "I", "EEG帧"),("m_axis_emg_tdata/valid/last", "I", "EMG帧"),("eeg_emg_sel", "I", "四种组合模式"),("*_send_package_num", "I", "输出字节数"),("eeg_emg_tdata/valid/last", "O", "合帧输出")]),
("cur_mon_top(电流监测顶层)", "cur_mon_config、ad7689_dual_ctrl、ad7689_dual_if、acq_collect、cur_monitor", "clk=100 MHzCNV/SCK 由计数器产生;采样结果经过 FIFO 后进入滤波和阈值比较。", [("cnv_en/clear_int", "I", "采样使能和清除故障"),("cnv/sck/mosi", "O", "AD7689控制"),("miso", "I", "AD7689结果"),("flt_state/cur_rt", "O", "故障与实时值"),("axi_i_out/wr/tlast", "O", "透明DMA流")]),
("rhs2116_top(刺激控制)", "transmit_config、trans_timer、sine_gen、trans_distribute、transmit_if、stim_monitor、stim_event_collect", "clk=100 MHzSPI_TRANS_PERIOD=1500 nsDDS相位累加和定时器均在 clk 域执行。", [("stim BRAM", "I", "波形/通道/寄存器配置"),("axi_str_txd", "I", "刺激命令流"),("stim_en/cs/sclk/mosi", "O", "RHS2116 SPI"),("miso", "I", "器件回读"),("stim_event/axi_str_rxd", "O", "事件和监控流")]),
("channel_switch(通道开关)", "channel_switch、adg1414_if、spi_full_duplex_master", "clk=100 MHz;PS 的通道选择先同步,再串行移位到模拟开关;更新完成后锁存输出。", [("ch_sel/ch_en", "I", "通道选择"),("spi_sync/sclk/mosi", "O", "ADG1414/ADGS5414接口"),("miso", "I", "可选回读"),("sw_state", "O", "当前开关状态")]),
("trigger_top(触发检测)", "trigger_detection、trigger_top", "clk=100 MHz;输入触发先经过同步器,再由边沿/码型检测状态机处理。", [("trigger_i", "I", "外部触发输入"),("rst", "I", "触发逻辑复位"),("event_code", "O", "触发事件编码"),("interrupt", "O", "触发中断"),("axis_trigger", "O", "触发数据流")]),
]
def main():
d=Document(DOC)
h(d, "子模块详细设计补充", 1)
body(d, "本节按 DTCD PRO-S FPGA 详细设计模板的子模块格式补充说明。每个模块均包含(1)流程图;(2)处理说明;(3)数据流;(4)时钟说明;(5)接口信号。图中方框表示 RTL 状态或处理单元,箭头表示有效数据/控制方向;状态说明与代码中的状态机、计数器和 FIFO 条件对应。")
for idx,(name, children, clock, ports) in enumerate(mods,1):
h(d, f"{idx} {name}", 2)
h(d, "1 流程图", 3)
d.add_picture(str(DIAG/(name.split("")[0]+"_flow.png")), width=Cm(16.0)); caption(d, f"图 {idx}-1 {name}流程图")
body(d, "状态说明:IDLE 等待配置或输入有效;CONFIG 完成寄存器/器件初始化;RUN 在有效握手或 DRDY 条件下处理数据;END 输出 TLAST、锁存状态并返回 IDLE。出现 stop_sample、复位或错误条件时优先回到 IDLE 并清空本模块计数器/FIFO。")
h(d, "2 处理说明", 3)
d.add_picture(str(DIAG/(name.split("")[0]+"_state.png")), width=Cm(16.0)); caption(d, f"图 {idx}-2 {name}处理状态转换图")
body(d, f"处理流程:{children}。首先由配置寄存器装载工作参数;随后等待输入有效(采集模块为 DRDY,上传模块为 TVALID,刺激模块为命令 FIFO,开关模块为选择更新);处理单元按计数器推进,在边界条件产生有效/结束标志;最后将结果送往下一级 FIFO、AXI 流或状态寄存器。")
h(d, "3 数据流", 3)
d.add_picture(str(DIAG/(name.split("")[0]+"_data.png")), width=Cm(16.0)); caption(d, f"图 {idx}-3 {name}数据流图")
body(d, "数据流说明:配置面从 PS BRAM/GPIO/SPI 进入;数据面由外部器件或上一级 AXI-Stream 进入,经本模块寄存器、状态机、FIFO 和校验逻辑处理后输出。跨模块边界使用 valid/ready 或显式 FIFOTLAST 只在包/帧最后一个字节有效。")
h(d, "4 时钟说明", 3); body(d, clock)
h(d, "5 接口信号", 3)
table(d, ["信号", "方向", "功能说明"], ports)
h(d, "5.1 下一级子模块", 4)
body(d, "本模块下一级 RTL 子模块为:" + children + "。下一级模块均在同一时钟域内以同步时序逻辑实现,接口由上一级统一提供复位和使能;其中 SPI/ADC 接口模块负责器件边沿约束,FIFO/封包模块负责数据域隔离。")
d.save(DOC)
print(DOC)
if __name__=="__main__": main()