2026/5/22 20:59:34
网站建设
项目流程
苏中建设集团网站官网,门户网站建设收费,wordpress修改手机模板,优化企业门户网站FPGA教程系列-Vivado AXI串口仿真测试
其实看完了高速接口#xff0c;再返回来看串口#xff0c;有点倒反天罡的意思了#xff0c;不过还是想重新看一下串口#xff0c;另外#xff0c;看下大神是如何编写串口的。
照例放上讲的非常好的原文#xff0c;https://blog.csdn…FPGA教程系列-Vivado AXI串口仿真测试其实看完了高速接口再返回来看串口有点倒反天罡的意思了不过还是想重新看一下串口另外看下大神是如何编写串口的。照例放上讲的非常好的原文https://blog.csdn.net/wuzhikaidetb/article/details/132725767?spm1001.2014.3001.5501UART串口简介串口作为常用的三大低速总线UART、SPI、IIC之一在设计众多通信接口和调试时占有重要地位。 串口UART全称通用异步收发传输器Universal Asynchronous Receiver/Transmitter)主要用于数据间的串行传递是一种全双工传输模式。它在发送数据时将并行数据转换成串行数据来传输在接收数据时将接收到的串行数据转换成并行数据。 “异步”两个字即意味着在数据传递的两个模块之间使用的不是同步时钟。实际上在异步串口的传输中是不需要时钟的而是通过特定的时序来标志传输的开始起始位--由高到低和结束结束位拉高。通信方式在日常的应用中一般分为串行通信serial communication和并行通信parallel communication。并行通信指多比特数据同时通过并行线进行传送一般以字或字节为 单位并行进行传输。这种传输方式用的通信线多、成本高故不宜进行远距离通信因此并行通信一般用 于近距离的通信通常传输距离小于30米。串行通信指数据在一条数据线上一比特接一比特地按顺序传送 的方式这一点与并行通信是不同的。这里我们以传输一个字节8位数据为例在并行通信中一个字 节的数据是在 8 条并行传输线上同时由源地传送到目的地而在串行通信中因为数据是在一条传输线上 一位接一位地顺序传送的所以一个字节的数据要分8次进行传送。如果我们以T为一个时间单位的话 那么并行通信发送一个字节的数据只需要1T的时间而串行通信需要8T的时间由此可以总结出串行通 信的的特点一是节省传输线大大降低了使用成本二是数据传送速度慢这一点在大位宽的数据传输 上尤为明显。综上可知串行通信主要应用于长距离、低速率的通信场合。串行通信一般有 2 种通信方式同步串行通信synchronized serial communication和异步串行通信 asynchronous serial communication。同步串行通信需要通信双方在同一时钟的控制下同步传输数据异 步串行通信是指具有不规则数据段传送特性的串行数据传输。在常见的通信总线协议中I2CSPI 属于同 步通信而UART属于异步通信。同步通信的通信双方必须先建立同步即双方的时钟要调整到同一个频率 收发双方不停地发送和接收连续的同步比特流。异步通信在发送字符时发送端可以在任意时刻开始发送 字符所以在UART通信中数据起始位和停止位是必不可少的。UART是一种采用异步串行通信方式的通用异步收发传输器universal asynchronous receiver-transmitter 它在发送数据时将并行数据转换成串行数据来传输在接收数据时将接收到的串行数据转换成并行数据。 UART 串口通信需要两根信号线来实现一根用于串口发送另外一根负责串口接收如图所示。 对于 PC来说它的TX要和对于FPGA来说的RX连接同样PC的RX要和FPGA的TX连接如果是两 个TX或者两个RX连接那数据就不能正常被发送出去或者接收到。UART 在发送或接收过程中的一帧数据由 4 部分组成起始位、数据位、奇偶校验位和停止位如下 图所示。起始位当不传输数据时UART 数据传输线通常保持高电压电平。若要开始数据传输发送 UART 会将传输线从高电平拉到低电平并保持1个波特率周期。当接收UART检测到高到低电压跃迁时便开始 以波特率对应的频率读取数据帧中的位。数据帧数据帧包含所传输的实际数据。如果使用奇偶校验位数据帧长度可以是5位到8位。如果 不使用奇偶校验位数据帧长度可以是9位。在大多数情况下数据以最低有效位优先方式发送。奇偶校验奇偶性描述数字是偶数还是奇数。通过奇偶校验位接收UART判断传输期间是否有数据 发生改变。电磁辐射、不一致的波特率或长距离数据传输都可能改变数据位。接收UART读取数据帧后 将计数值为1的位检查总数是偶数还是奇数。如果奇偶校验位为0偶数奇偶校验则数据帧中的1或 逻辑高位总计应为偶数。如果奇偶校验位为1奇数奇偶校验则数据帧中的1或逻辑高位总计应为奇数。 当奇偶校验位与数据匹配时UART认为传输未出错。但是如果奇偶校验位为0而总和为奇数或者奇 偶校验位为1而总和为偶数则UART认为数据帧中的位已改变。停止位为了表示数据包结束发送UART将数据传输线从低电压驱动到高电压并保持1到2位时间。UART 通信过程中的数据格式及传输速率是可设置的为了正确的通信收发双方应约定并遵循同样 的设置。数据位可选择为5、6、7、8位其中8位数据位是最常用的在实际应用中一般都选择8位数据 位校验位可选择奇校验、偶校验或者无校验位停止位可选择1位默认1.5或2位。串口通信的速 率用波特率表示它表示每秒传输二进制数据的位数单位是bps位/秒常用的波特率有9600、19200、 38400、57600 以及115200 等。波特率即每秒传输的位数(bit)。一般选波特率都会有960019200115200等 选项。其实意思就是每秒传输这么多个比特位数(bit)。在信息传输通道中携带数据信息的信号单元叫作码 元因为串口是1bit进行传输的所以其码元就代表一个二进制数每秒通过信号传输的码元数称为码 元的传输速率简称“波特率”常用符号“Baud”表示其单位为“波特每秒”Bps。串口常见的波特率有 4800、9600、115200 等。 通信信道每秒传输的信息量称为位传输速率简称“比特率”其单位为“每秒比特数”bps。比特率 可由波特率计算得出公式为比特率波特率×单个调制状态对应的二进制位数。 如果使用的是115200的波特率其串口的比特率为115200Bps×1bit 115200bps由计算得串口发送或 者接收1bit数据的时间为一个波特即1/115200s。在设置好数据格式及传输速率之后UART 负责完成数据的串并转换而信号的传输则由外部驱动电 路实现。电信号的传输过程有着不同的电平标准和接口规范针对异步串行通信的接口标准有RS232、RS422、 RS485 等它们定义了接口不同的电气特性如RS-232是单端输入输出而RS-422/485为差分输入输出等。 RS-232 标准的串口最常见的接口类型为DB9样式如图所示工业控制领域中用到的工控机一 般都配备多个串口很多老式台式机也都配有串口。但是笔记本电脑以及较新一点的台式机都没有串口 它们一般通过USB转串口线来实现与外部设备的串口通信。PsDB9公头与母头的定义一定要注意AXI串口的实现普通的串口已经在网上泛滥了最近发现Alex 大神的一些项目有串口的通信而且是用AXI协议的串口本着向大神学习的念头站在巨人的肩膀好成事。文件结构很简单三个文件一个封装一个发射一个接收。代码网上都有但是测试是用的cocotb暂时没安装测试也不复杂所以简单的贴上testbench。另外程序的解读留待以后的。timescale 1ns / 1ps module tb_uart; // 参数定义 parameter DATA_WIDTH 8; parameter CLK_PERIOD 10; // 100MHz 时钟 // 输入信号 (Reg) reg clk; reg rst; reg [DATA_WIDTH-1:0] s_axis_tdata; reg s_axis_tvalid; reg m_axis_tready; reg [15:0] prescale; // 输出信号 (Wire) wire s_axis_tready; wire [DATA_WIDTH-1:0] m_axis_tdata; wire m_axis_tvalid; wire txd; wire tx_busy; wire rx_busy; wire rx_overrun_error; wire rx_frame_error; // 模拟的回环连接信号 wire rxd; // ---------------------------------------------------------------- // 实例化被测模块 (Unit Under Test - UUT) // 引用源文件 [3], [4], [5] // ---------------------------------------------------------------- uart #( .DATA_WIDTH(DATA_WIDTH) ) uut ( .clk(clk), .rst(rst), // AXI Input (发送端接口) .s_axis_tdata(s_axis_tdata), .s_axis_tvalid(s_axis_tvalid), .s_axis_tready(s_axis_tready), // AXI Output (接收端接口) .m_axis_tdata(m_axis_tdata), .m_axis_tvalid(m_axis_tvalid), .m_axis_tready(m_axis_tready), // UART 物理接口 .rxd(rxd), .txd(txd), // 状态信号 .tx_busy(tx_busy), .rx_busy(rx_busy), .rx_overrun_error(rx_overrun_error), .rx_frame_error(rx_frame_error), // 配置 .prescale(prescale) ); // ---------------------------------------------------------------- // 关键逻辑硬件回环 (Loopback) // 将发送引脚 TX 连接到接收引脚 RX // ---------------------------------------------------------------- assign rxd txd; // 时钟生成 initial begin clk 0; forever #(CLK_PERIOD/2) clk ~clk; end // ---------------------------------------------------------------- // 测试流程 // ---------------------------------------------------------------- initial begin // 1. 初始化信号 rst 1; s_axis_tdata 0; s_axis_tvalid 0; m_axis_tready 0; prescale 16d10; // 设置较小的分频值以加快仿真速度 [2], [1] // 2. 复位释放 #(CLK_PERIOD * 10); rst 0; #(CLK_PERIOD * 10); $display(--- Simulation Start ---); $display(Prescale set to: %d, prescale); // 3. 开启接收端 Ready 信号 (允许接收数据) // 源码 [6] 显示如果不拉高 readyvalid 信号可能无法正确握手清除 m_axis_tready 1; // 4. 发送测试数据 0x55 (二进制 01010101) send_byte(8h55); // 5. 等待并检查接收 check_received_byte(8h55); // 6. 发送测试数据 0xA3 (随机值) send_byte(8hA3); check_received_byte(8hA3); // 7. 发送测试数据 0xFF (全1) send_byte(8hFF); check_received_byte(8hFF); // 结束仿真 #(CLK_PERIOD * 100); $display(--- Test Passed: All bytes match ---); $finish; end // ---------------------------------------------------------------- // 任务发送一个字节 (AXI Stream Master 行为) // 基于 uart_tx 的输入逻辑 [7] // ---------------------------------------------------------------- task send_byte; input [DATA_WIDTH-1:0] data; begin (posedge clk); s_axis_tdata data; s_axis_tvalid 1; // 等待 UUT 的 ready 信号 wait(s_axis_tready); (posedge clk); // 握手完成拉低 valid s_axis_tvalid 0; $display([TX] Sent data: 0x%h at time %t, data, $time); end endtask // ---------------------------------------------------------------- // 任务检查接收到的字节 (AXI Stream Slave 行为) // 基于 uart_rx 的输出逻辑 [8], [6] // ---------------------------------------------------------------- task check_received_byte; input [DATA_WIDTH-1:0] expected_data; begin // 等待数据变为 Valid // 注意由于是串行传输这里需要等待较长时间 (Bit time * 10) wait(m_axis_tvalid); (posedge clk); if (m_axis_tdata expected_data) begin $display([RX] Received Match: 0x%h at time %t, m_axis_tdata, $time); end else begin $display([RX] ERROR: Expected 0x%h, Got 0x%h at time %t, expected_data, m_axis_tdata, $time); $stop; // 遇到错误停止 end // 等待 valid 信号被拉低 (由 m_axis_tready 控制的握手完成) wait(!m_axis_tvalid); end endtask endmodule下面来看下仿真一共发送了3次第一次是01010101。这里有个点串口是低位优先的所以看时序应该反着来而01010101看起来跟高位优先差不多主要看第二组数据10100011.如图所示就能看出来是低位先行了。再仔细看下握手。仿真是做了一个回环的设计。之所以 TX 是从机SlaveRX 是主机Master是因为这里的“主/从”是针对系统内部数据流AXI Stream 总线的方向定义的而不是针对外部 UART 线缆的方向。需要从数据是谁产生的流向哪里这个角度来理解为什么 TX发送模块是 SlaveUART TX 模块的任务是接收来自系统的数据然后把它“消化”掉串行化发送出去。在 AXI Stream 协议中接收数据的一方是 Slave从机发送数据的一方是 Master主机。因此系统或 DMA是 Master它把数据“喂”给 TX 模块而 TX 模块作为 Slave 被动接收。为什么 RX接收模块是 MasterUART RX 模块的任务是产生数据从外部引脚解串出来的然后把这些数据推给系统。在 AXI Stream 协议中发起/输出数据的一方是 Master。因此RX 模块变成了 Master它主动告诉系统“我收到新数据了给你”。为了方便记忆可以使用打印机和扫描仪的类比TX (发送端) 就像打印机打印机是 Slave。它不会自己写文章它必须等待电脑Master发送文件给它它才能打印。RX (接收端) 就像扫描仪扫描仪是 Master。当你扫描一张照片时扫描仪主动产生图片数据并发送给电脑Slave保存。