如何优雅地绘制时序图

news2025/1/12 22:14:56

说到时序图,相信所有从事嵌入式开发的伙伴都非常熟悉,在各种元器件手册以及处理器说明书中,但凡涉及到通信、接口、交互等内容,都会涉及到时序图。时序图可以非常详细且明确地描述硬件及软件接口中各个信号的时序关系,例如时钟信号、数据信号、控制信号等。这些对于确保设备间的正确交互至关重要。可以说能够读懂时序图是每一个嵌入式开发者必备的技能。然而,要想进一步成为资深工程师,那仅仅会看是不够的!

如何既简单又方便还优雅地绘制属于我们自己的时序图呢?如果说要我们用画图软件一条一条线画那是绝对不可能的,因此,我们需要借助一个时序图绘制工具 —— WaveDrom。

WaveDrom 是一个 JavaScript 的开源项目,其使用一种叫 WaveJSON 的格式来描述数字时序图。从其名称就很容易看出,WaveJSON 是基于 JSON 并且有一些保留关键字以及结构限制的格式。因此,如果你拥有 JSON 基础,那么 WaveJSON 会非常容易上手。

WaveDrom 官网如下:

https://wavedrom.com/

其支持下载安装到本地,也支持浏览器在线使用,还集成了一个 VSCode 插件 —— Wavefrom Render,从而支持在代码编辑器中直接绘制图形,整体使用非常灵活方便。

本文使用官方的在线编辑器来演示。在线编辑器地址如下:

https://wavedrom.com/editor.html

打开后的页面如下图所示:

整体界面非常简洁,分为上下两个部分,并且自带一个 Demo 供我们参考。上面是 WaveJSON 的描述文本,下面是基于描述文本渲染出来的时序图。而我们所要做的就是根据我们的需求写出对应的描述文本,最终由 WaveDrom 来生成我们想要的时序图。

到这里不难看出,画好时序图的重点就是写好描述文本。而要写好描述文本,自然是要完全理解并会使用 WaveJSON!庆幸的是,WaveJSON 并不难,其基于 JSON 的特性也让它的组织结构非常清晰并且易于理解。

官方提供了一个上手指南,不过是英文版的,这里我们直接搬运过来并进行翻译,帮助大家快速理解。

1. 信号 (Signal)

在 WaveJSON 中,signal 是根节点,也可以叫做主节点。它是一个由 Wavelane 组成的数组,而 Wavelane 则是用于描述单个信号波形的基本单元,每个 Wavelane 中包含两个必填字段:

  • name:用于指定信号的名称。这个名称存在于时序图信号波形旁边,帮助识别和理解该信号的作用。

  • wave:用于定义信号的状态变化。它由一系列字符组成,每个字符代表一个时间周期内的信号状态。常见的字符包括:

    • 0/1:分别表示信号的低电平和高电平。

    • .:表示信号在该周期内保持前一个状态不变。

    • x:表示信号在该周期内处于任意状态或未知状态。

    • z:表示信号在该周期内处于高阻状态。

    • 2-9:表示数据,每个数字带有不同的颜色

    • =:表示数据,与 2 代表的颜色相同。

    • p/n: 分别表示时钟信号的上升沿和下降沿。

    • P/N:与 p/n 相同但是带有箭头。

    • u/d:分别表示弱上拉和弱下拉。

    • |:用于分隔不同的时钟周期,以便在时序图中清晰地显示时钟周期的边界。

更详细的说明会在后续针对于 WaveJSON 的文章中列举。

我们用一个简单的示例来理解,以下代码将创建一个名为 Alfa 的单比特信号,并且随着时间的推移其状态会发生变化:

{ signal: [{ name: "Alfa", wave: "01.zx=ud.23.456789" }] }

在 wave 字段中,每一个字符都代表一个单一的时间周期,渲染后的时序图如下:

2. 添加时钟

数字时钟是一种特殊的信号类型,它在每个周期内变化两次,并且可以具有正负极性。它还可以在工作边缘添加可选的标记。时钟块可以与其他信号状态混合,以创建时钟门控效果。示例代码如下:

{ signal: [
  { name: "pclk", wave: 'p.......' },
  { name: "Pclk", wave: 'P.......' },
  { name: "nclk", wave: 'n.......' },
  { name: "Nclk", wave: 'N.......' },
  {},
  { name: 'clk0', wave: 'phnlPHNL' },
  { name: 'clk1', wave: 'xhlhLHl.' },
  { name: 'clk2', wave: 'hpHplnLn' },
  { name: 'clk3', wave: 'nhNhplPl' },
  { name: 'clk4', wave: 'xlh.L.Hx' },
]}

渲染后的时序图如下:

图片

3. 信号组合

典型的时序图可能会包含时钟和触发信号。需要多个比特组合的数据一般可以通过加入 data 字段来标注:

{ signal: [
  { name: "clk",  wave: "P......" },
  { name: "bus",  wave: "x.==.=x", data: ["head", "body", "tail", "data"] },
  { name: "wire", wave: "0.1..0." }
]}

渲染后的时序图如下:

图片

4. 隔断

使用 | 来分隔不同的时钟周期,以便在时序图中清晰地显示时钟周期的边界:

{ signal: [
  { name: "clk",         wave: "p.....|..." },
  { name: "Data",        wave: "x.345x|=.x", data: ["head", "body", "tail", "data"] },
  { name: "Request",     wave: "0.1..0|1.0" },
  {},
  { name: "Acknowledge", wave: "1.....|01." }
]}

渲染后的时序图如下:

图片

5. 信号分组

多个 Wavelanes 可以组合成命名组,这些组在 WaveJSON 中以数组的形式表示,类似于:['group name', {...}, {...}, ...]。数组的第一个元素是组名,后续元素是属于该组的 Wavelanes 或其他子组,因此也说明,组是可以嵌套的。以下是代码示例:

{ signal: [
  {    name: 'clk',   wave: 'p..Pp..P'},
  ['Master',
    ['ctrl',
      {name: 'write', wave: '01.0....'},
      {name: 'read',  wave: '0...1..0'}
    ],
    {  name: 'addr',  wave: 'x3.x4..x', data: 'A1 A2'},
    {  name: 'wdata', wave: 'x3.x....', data: 'D1'   },
  ],
  {},
  ['Slave',
    ['ctrl',
      {name: 'ack',   wave: 'x01x0.1x'},
    ],
    {  name: 'rdata', wave: 'x.....4x', data: 'Q2'},
  ]
]}

渲染效果如下:

图片

6. 周期和相位

“period” 和 “phase” 这两个参数可以用来调整每个 Wavelane 的显示特性。

  • period(周期):这个参数用于设置波形通道的时间周期长度。例如,如果一个信号的周期被设置为2,那么它的每个时间单位将占据两个默认时间单位的长度。

  • phase(相位):这个参数用于调整波形通道的起始相位。它允许你将信号的起始点提前或延后,具体为:如果一个信号的相位偏移为正,这意味着信号波形会在时间轴上向左移动,从而在时间上提前出现。相反,如果相位偏移为负,则信号波形会在时间轴上向右移动,从而在时间上滞后出现。

下面以 DDR 读时序为例,描述代码为:

{ signal: [
  { name: "CK",   wave: "P.......", period: 2  },
  { name: "CMD",  wave: "x.3x=x4x=x=x=x=x", data: "RAS NOP CAS NOP NOP NOP NOP", phase: 0.5 },
  { name: "ADDR", wave: "x.=x..=x........", data: "ROW COL", phase: 0.5 },
  { name: "DQS",  wave: "z.......0.1010z." },
  { name: "DQ",   wave: "z.........5555z.", data: "D0 D1 D2 D3" }
]}

渲染效果:

图片

7. 配置属性

配置属性 config:{...} 用于控制渲染的不同特征。

hscale

config:{hscale:#} 用于控制时序图的水平缩放。用户可以设置任何大于零的整数值:

{ signal: [
  { name: "clk",     wave: "p...." },
  { name: "Data",    wave: "x345x",  data: ["head", "body", "tail"] },
  { name: "Request", wave: "01..0" }
  ],
  config: { hscale: 1 }
}
hscale = 1 (默认)

图片

hscale = 2

图片

hscale = 3

图片

skin

config:{skin:'...'} 属性可以用来选择 WaveDrom 的皮肤样式。这个属性只在页面上的第一个时序图中有效。WaveDrom 编辑器包括两种标准的皮肤样式:'default' 和 'narrow'。

head/foot

head:{...} 属性定义了时序图的头部区域。可以在这个区域中添加标题、时间标记等信息。

foot:{...}属性定义了时序图的底部区域。可以在这个区域中添加图例、说明等信息。

tick

添加与垂直标记对齐的时间线标签。

tock

在垂直标记之间添加时间线标签。

title

添加标题或说明文字。

every

在时间轴上,只在每 N 个周期中渲染一次 tick 和 tock 标记。

以下是上述几个属性的示例代码:

{signal: [
  {name:'clk',         wave: 'p....' },
  {name:'Data',        wave: 'x345x', data: 'a b c' },
  {name:'Request',     wave: '01..0' }
],
 head:{
   text:'WaveDrom example',
   tick:0,
   every:2
 },
 foot:{
   text:'Figure 100',
   tock:9
 },
}

渲染效果如下:

图片

注意,head/foot 文本具有 SVG 文本的所有属性。可以使用标准的 SVG tspan 属性来修改文本的默认属性。JsonML 标记语言用于表示 SVG 文本内容。可以使用并混合多种预定义样式:

  • h1、h2、h3、h4、h5、h6 —— 预定义的字体大小。

  • muted、warning、error、info、success —— 字体颜色样式。

其他 SVG tspan 属性可以像下面的示例那样自由使用:

{signal: [
  {name:'clk', wave: 'p.....PPPPp....' },
  {name:'dat', wave: 'x....2345x.....', data: 'a b c d' },
  {name:'req', wave: '0....1...0.....' }
],
head: {text:
  ['tspan',
    ['tspan', {class:'error h1'}, 'error '],
    ['tspan', {class:'warning h2'}, 'warning '],
    ['tspan', {class:'info h3'}, 'info '],
    ['tspan', {class:'success h4'}, 'success '],
    ['tspan', {class:'muted h5'}, 'muted '],
    ['tspan', {class:'h6'}, 'h6 '],
    'default ',
    ['tspan', {fill:'pink', 'font-weight':'bold', 'font-style':'italic'}, 'pink-bold-italic']
  ]
},
foot: {text:
  ['tspan', 'E=mc',
    ['tspan', {dy:'-5'}, '2'],
    ['tspan', {dy: '5'}, '. '],
    ['tspan', {'font-size':'25'}, 'B '],
    ['tspan', {'text-decoration':'overline'},'over '],
    ['tspan', {'text-decoration':'underline'},'under '],
    ['tspan', {'baseline-shift':'sub'}, 'sub '],
    ['tspan', {'baseline-shift':'super'}, 'super ']
  ],tock:-5
}
}

渲染效果如下:

图片

8. 箭头

平滑曲线

 ~    -~
<~>  <-~>
 ~>   -~>  ~->

具体使用形式及渲染图如下:

{ signal: [
  { name: 'A', wave: '01........0....',  node: '.a........j' },
  { name: 'B', wave: '0.1.......0.1..',  node: '..b.......i' },
  { name: 'C', wave: '0..1....0...1..',  node: '...c....h..' },
  { name: 'D', wave: '0...1..0.....1.',  node: '....d..g...' },
  { name: 'E', wave: '0....10.......1',  node: '.....ef....' }
  ],
  edge: [
    'a~b t1', 'c-~a t2', 'c-~>d time 3', 'd~-e',
    'e~>f', 'f->g', 'g-~>h', 'h~>i some text', 'h~->j'
  ]
}

图片

折线

-   -|   -|-
<-> <-|> <-|->
 ->  -|>  -|->  |->
 +

具体使用形式及渲染图如下:

{ signal: [
  { name: 'A', wave: '01..0..',  node: '.a..e..' },
  { name: 'B', wave: '0.1..0.',  node: '..b..d.', phase:0.5 },
  { name: 'C', wave: '0..1..0',  node: '...c..f' },
  {                              node: '...g..h' },
  {                              node: '...I..J',  phase:0.5 },
  { name: 'D', wave: '0..1..0',  phase:0.5 }
  ],
  edge: [
    'b-|a t1', 'a-|c t2', 'b-|-c t3', 'c-|->e t4', 'e-|>f more text',
    'e|->d t6', 'c-g', 'f-h', 'g<->h 3 ms', 'I+J 5 ms'
  ]
}

图片

9. 代码绘制

除了直接提供 WaveJSON 描述文本,还可以直接写代码,这在某些情况下能提供更高的灵活性和便捷性:

function (bits, ticks) {
  var i, t, gray, state, data = [], arr = [];
  for (i = 0; i < bits; i++) {
    arr.push({name: i + '', wave: ''});
    state = 1;
    for (t = 0; t < ticks; t++) {
      data.push(t + '');
      gray = (((t >> 1) ^ t) >> i) & 1;
      arr[i].wave += (gray === state) ? '.' : gray + '';
      state = gray;
    }
  }
  arr.unshift('gray');
  return {signal: [
    {name: 'bin', wave: '='.repeat(ticks), data: data}, arr
  ]};
})(5, 16)

渲染效果如下:

图片

最后,我们可以通过在线编辑器的右下角导出时序图:

图片

官方目前提供两种格式,分别为 SVG 和 PNG。其中 SVG 是基于矢量的图形格式,可以在不损失分辨率的情况下放大缩小。导出后就能得到自己的时序图啦。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2275673.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringCloud系列教程:微服务的未来(十一)服务注册、服务发现、OpenFeign快速入门

本篇博客将通过实例演示如何在 Spring Cloud 中使用 Nacos 实现服务注册与发现&#xff0c;并使用 OpenFeign 进行服务间调用。你将学到如何搭建一个完整的微服务通信框架&#xff0c;帮助你快速开发可扩展、高效的分布式系统。 目录 前言 服务注册和发现 服务注册 ​编辑 …

WebGIS在应急灾害中对村庄、风景区、机场的影响范围应用-以日喀则市定日县地震为例

目录 前言 一、关于影响范围 1、震中距离5公里 2、震中20公里范围 3、20到80公里范围 二、空间查询知识 1、相关数据介绍 2、空间数据查询 三、前后端数据查询以及web可视化实现 1、后台API实现 2、WebGIS前端实现 四、Web成果展示 1、空间位置分析 2、包含风景区…

使用网页版Jupyter Notebook和VScode打开.ipynb文件

目录 正文 1、网页版Jupyter Notebook查看 2、VScode查看 因为总是忘记查看文件的网址&#xff0c;收藏了但分类众多每次都找不到……当个记录吧&#xff08;/捂脸哭&#xff09;&#xff01; 正文 此处以gitub中的某个仓库为例&#xff1a; https://github.com/INM-6/mu…

景联文科技提供高质量多模态数据处理服务,驱动AI新时代

在当今快速发展的AI时代&#xff0c;多模态数据标注成为推动人工智能技术进步的关键环节。景联文科技作为行业领先的AI数据服务提供商&#xff0c;专注于为客户提供高质量、高精度的多模态数据标注服务&#xff0c;涵盖图像、语音、文本、视频及3D点云等多种类型的数据。通过专…

Python在Excel工作表中创建数据透视表

在数据处理和分析工作中&#xff0c;Excel作为一个广泛使用的工具&#xff0c;提供了强大的功能来管理和解析数据。当面对大量复杂的数据集时&#xff0c;为了更高效地总结、分析和展示数据&#xff0c;创建数据透视表成为一种不可或缺的方法。通过使用Python这样的编程语言与E…

django基于Python的电影推荐系统

Django 基于 Python 的电影推荐系统 一、系统概述 Django 基于 Python 的电影推荐系统是一款利用 Django 框架开发的智能化应用程序&#xff0c;旨在为电影爱好者提供个性化的电影推荐服务。该系统通过收集和分析用户的观影历史、评分数据、电影的属性信息&#xff08;如类型…

GPT-SoVITS学习01

1.什么是TTS TTS&#xff08;Text-To-Speech&#xff09;这是一种文字转语音的语音合成。类似的还有SVC&#xff08;歌声转换&#xff09;、SVS&#xff08;歌声合成&#xff09;等。 2.配置要求 GPT-SoVITS对电脑配置有较高的要求。 训练&#xff1a;对于Windows电脑&#…

计算机网络 (36)TCP可靠传输的实现

前言 TCP&#xff08;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP通过多种机制实现可靠传输&#xff0c;这些机制主要包括连接管理、序列号和确认应答机制、重传机制、流量控制、拥塞控制等。 一、连接管理 TCP使用三次握手&#xff0…

视频编辑最新SOTA!港中文Adobe等发布统一视频生成传播框架——GenProp

文章链接&#xff1a;https://arxiv.org/pdf/2412.19761 项目链接&#xff1a;https://genprop.github.io 亮点直击 定义了一个新的生成视频传播问题&#xff0c;目标是利用 I2V 模型的生成能力&#xff0c;将视频第一帧的各种变化传播到整个视频中。 精心设计了模型 GenProp&…

make工程管理器与Makefile

目录 一、介绍 1、make工程管理器 2、Makefile 二、Makefile语法规则 1、Makefile语法格式 2、Makefile中特殊处理与伪目标 3、变量、规则与函数 (1)自定义变量使用示例 (2)自动变量使用示例 一、介绍 1、make工程管理器 定义&#xff1a; make是一个命令工具&…

【git】-2 分支管理

目录 一、分支的概念 二、查看、创建、切换分支 1、查看分支-git branch 2、创建分支- git branch 分支名 3、切换分支- git checkout 分支名 三、git指针 -实现分支和版本间的切换 四、普通合并分支 git merge 文件名 五、冲突分支合并 ​​​​​​【git】-初始gi…

3DGabor滤波器实现人脸特征提取

import cv2 import numpy as np# 定义 Gabor 滤波器的参数 kSize 31 # 滤波器核的大小 g_sigma 3.0 # 高斯包络的标准差 g_theta np.pi / 4 # Gabor 函数的方向 g_lambda 10.0 # 正弦波的波长 g_gamma 0.5 # 空间纵横比 g_psi np.pi / 2 # 相位偏移# 生成 Gabor 滤…

接口项目架构流程图-thinkphp6-rabbitmq

一、整个系统流程 第一步&#xff1a;平台在创建好后开启消息队列&#xff1b; 第二步&#xff1a;平台为需要服务的客户开好账号并传输对应的公私钥文件&#xff1b; 第三步&#xff1a;客户通过平台分享的接口连接地址采用开户时的手机号查看&#xff1b; 第四步&#xff1a;…

Vue3初学之组件通信

一起进行学习&#xff1a; 在 Vue 3 中&#xff0c;组件通信是一个非常重要的概念&#xff0c;它决定了如何在父子组件之间、兄弟组件之间以及跨层级组件之间传递数据和事件。以下是 Vue 3 中常见的组件通信方式&#xff1a; 父子组件通信 1.1 父组件向子组件传递数据&#x…

2025年第三届“华数杯”国际大学生数学建模竞赛【A题】Problem A: Can He Swim Faster

问题1&#xff1a;运动员的出色比赛表现通常得益于艰苦且持续的专业训练&#xff0c;这不仅提升了游泳技能&#xff0c;也增强了生理储备。比赛中&#xff0c;科学控制游泳速度是关键&#xff0c;包括保持个人节奏、寻求最佳身体状态节奏和合理分配体力。针对自由泳项目&#x…

【计算机网络】lab4 Ipv4(IPV4的研究)

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;计算机网络_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2.…

ELFK日志采集实战

一、日志分析概述 日志分析是运维工程师解决系统故障&#xff0c;发现问题的主要手段 日志主要包括系统日志、应用程序日志和安全日志 系统运维和开发人员可以通过日志了解服务器软硬件信息、检查配置过程中的错误及错误发生的原因 经常分析日志可以了解服务器的负荷&#x…

辅助--Inspector

辅助–Inspector 1.Introduction This manual explains how to use the Inspector. 1.1.Overview Inspector is a Qt-based library that provides functionality to interactively inspect low-level content of the OCAF data model, OCCT viewer and Modeling Data. Thi…

如何播放视频文件

文章目录 1. 概念介绍2. 使用方法2.1 实现步骤2.2 具体细节3. 示例代码4. 内容总结我们在上一章回中介绍了"如何获取文件类型"相关的内容,本章回中将介绍如何播放视频.闲话休提,让我们一起Talk Flutter吧。 1. 概念介绍 播放视频是我们常用的功能,不过Flutter官方…

R数据分析:多分类问题预测模型的ROC做法及解释

有同学做了个多分类的预测模型,结局有三个类别,做的模型包括多分类逻辑回归、随机森林和决策树,多分类逻辑回归是用ROC曲线并报告AUC作为模型评估的,后面两种模型报告了混淆矩阵,审稿人就提出要统一模型评估指标。那么肯定是统一成ROC了,刚好借这个机会给大家讲讲ROC在多…