集成电路流片随笔10:UART模块tinyriscv 通信协议和RIB接口总线区别
UART(通用异步收发传输)Universal Asynchronous Receiver-Transmitter模块,用于支持数据的串行通信。模块包括接收和发送数据的逻辑。
模块端口说明:
module uart(input wire clk, // 时钟信号input wire rst, // 复位信号input wire we_i, // 写使能信号input wire[31:0] addr_i, // 地址信号input wire[31:0] data_i, // 写入数据output reg[31:0] data_o, // 读取数据output wire tx_pin, // UART 发送数据引脚input wire rx_pin // UART 接收数据引脚
);
we_i
:写使能信号,控制是否可以向寄存器写数据。addr_i
:寄存器的地址信号,表示要访问哪个寄存器。data_i
:写入的数据,给 UART 寄存器。data_o
:读取的数据输出,来自 UART 寄存器。tx_pin
:UART 发送引脚,用于将数据发送到外部设备。rx_pin
:UART 接收引脚,用于接收外部设备发送的数据。
寄存器和常量定义:
localparam BAUD_115200 = 32'h1B8; // 50MHz时钟,波特率115200bps对应的分频系数
localparam S_IDLE = 4'b0001; // 状态机的空闲状态
localparam S_START = 4'b0010; // 启动状态
localparam S_SEND_BYTE = 4'b0100; // 发送字节状态
localparam S_STOP = 4'b1000; // 停止状态reg tx_data_valid; // 发送数据有效
reg tx_data_ready; // 发送数据准备好reg[3:0] state; // 当前状态
reg[15:0] cycle_cnt; // 计数周期,用于波特率控制
reg[3:0] bit_cnt; // 发送的位计数
reg[7:0] tx_data; // 发送数据
reg tx_reg; // 发送寄存器reg rx_q0; // RX 引脚的延迟信号
reg rx_q1; // RX 引脚的另一个延迟信号
wire rx_negedge;
reg rx_start; // RX使能
reg[3:0] rx_clk_edge_cnt; // RX 时钟边缘计数
reg rx_clk_edge_level; // RX 时钟边缘电平
reg rx_done; // RX 完成标志
reg[15:0] rx_clk_cnt; // RX 时钟计数
reg[15:0] rx_div_cnt; // RX 分频计数
reg[7:0] rx_data; // 接收到的 RX 数据
reg rx_over; // RX 超过标志// 寄存器地址
localparam UART_CTRL = 8'h0;
localparam UART_STATUS = 8'h4;
localparam UART_BAUD = 8'h8;
localparam UART_TXDATA = 8'hc;
localparam UART_RXDATA = 8'h10;
波特率和分频器:
波特率(Baud Rate)表示每秒钟传输的数据位数。在通信系统中,设置合适的波特率非常重要,因为它决定了数据传输的速度。
在此代码中,BAUD_115200 = 32'h1B8
用于设置 UART 模块的波特率为 115200。这是通过分频器来实现的。波特率分频器的作用是根据时钟频率(这里假设时钟频率是 50MHz)生成一个适合于 UART 数据传输的时钟信号。
如何计算波特率:
- 假设系统时钟是 50MHz(50,000,000 Hz),表示每秒钟有 5000 万个时钟周期。
- UART 波特率(115200)意味着每秒钟发送 115200 个数据位(包括起始位、数据位、停止位等)。
- 为了实现这一目标,我们需要将系统时钟频率分频,得到每个数据位传输所需要的时间。
通过公式:
波特率分频器 = 系统时钟频率 波特率 = 50000000 115200 ≈ 434.027 \text{波特率分频器} = \frac{\text{系统时钟频率}}{\text{波特率}} = \frac{50000000}{115200} \approx 434.027 波特率分频器=波特率系统时钟频率=11520050000000≈434.027
这个结果表示我们需要将系统时钟频率分频到 115200 波特率,通常会进行四舍五入,得到分频器值 0x1B8
。
32'h1B8
的含义:
32'h1B8
是一个 32 位十六进制数,它的值等于十进制的440
,这个数字就是分频器的值,表示将时钟频率除以440
后,得到的时钟信号会有 115200 Hz 的频率,这样就达到了设置 115200 波特率的目的。
总结:
BAUD_115200 = 32'h1B8
表示将时钟分频到 115200 波特率所需的分频器值。在实际操作中,这个分频器值会用在生成 UART 波特率时钟的电路中,从而确保数据的正确传输速度。
寄存器控制和状态操作:
always @ (posedge clk) beginif (rst == 1'b0) beginuart_ctrl <= 32'h0;uart_status <= 32'h0;uart_rx <= 32'h0;uart_baud <= BAUD_115200;tx_data_valid <= 1'b0;end else beginif (we_i == 1'b1) begincase (addr_i[7:0])UART_CTRL: beginuart_ctrl <= data_i;endUART_BAUD: beginuart_baud <= data_i;endUART_STATUS: beginuart_status[1] <= data_i[1];endUART_TXDATA: beginif (uart_ctrl[0] == 1'b1 && uart_status[0] == 1'b0) begintx_data <= data_i[7:0];uart_status[0] <= 1'b1;tx_data_valid <= 1'b1;endendendcaseend else begintx_data_valid <= 1'b0;if (tx_data_ready == 1'b1) beginuart_status[0] <= 1'b0;endif (uart_ctrl[1] == 1'b1) beginif (rx_over == 1'b1) beginuart_status[1] <= 1'b1;uart_rx <= {24'h0, rx_data};endendendend
end
- 在时钟上升沿触发时,模块根据复位信号初始化或更新各个寄存器。
- 如果
we_i == 1
且地址匹配相应寄存器,则更新相应寄存器。 UART_TXDATA
:当数据写入UART_TXDATA
寄存器时,如果uart_ctrl[0]
为 1(发送使能),且uart_status[0]
为 0(发送空闲),则将数据写入tx_data
并开始发送。
读取寄存器操作:
always @ (*) beginif (rst == 1'b0) begindata_o = 32'h0;end else begincase (addr_i[7:0])UART_CTRL: begindata_o = uart_ctrl;endUART_STATUS: begindata_o = uart_status;endUART_BAUD: begindata_o = uart_baud;endUART_RXDATA: begindata_o = uart_rx;enddefault: begindata_o = 32'h0;endendcaseend
end
- 这是一个组合逻辑块,用于根据
addr_i
地址读取 UART 寄存器的值并输出到data_o
。
发送数据(TX):
always @ (posedge clk) beginif (rst == 1'b0) beginstate <= S_IDLE;cycle_cnt <= 16'd0;tx_reg <= 1'b0;bit_cnt <= 4'd0;tx_data_ready <= 1'b0;end else beginif (state == S_IDLE) begintx_reg <= 1'b1;tx_data_ready <= 1'b0;if (tx_data_valid == 1'b1) beginstate <= S_START;cycle_cnt <= 16'd0;bit_cnt <= 4'd0;tx_reg <= 1'b0;endend else begincycle_cnt <= cycle_cnt + 16'd1;if (cycle_cnt == uart_baud[15:0]) begincycle_cnt <= 16'd0;case (state)S_START: begintx_reg <= tx_data[bit_cnt];state <= S_SEND_BYTE;bit_cnt <= bit_cnt + 4'd1;endS_SEND_BYTE: beginbit_cnt <= bit_cnt + 4'd1;if (bit_cnt == 4'd8) beginstate <= S_STOP;tx_reg <= 1'b1;end else begin tx_reg <= tx_data[bit_cnt];endendS_STOP: begintx_reg <= 1'b1;state <= S_IDLE;tx_data_ready <= 1'b1;endendcaseendendend
end
- 这是 UART 发送模块,使用状态机来控制数据的发送过程。
- 状态机:包括空闲状态(
S_IDLE
)、起始状态(S_START
)、发送数据状态(S_SEND_BYTE
)和停止状态(S_STOP
)。 cycle_cnt
:计数器用于生成波特率。- 根据状态机的状态,数据位被依次发送到
tx_reg
,并通过tx_pin
引脚发送出去。
接收数据(RX):
assign rx_negedge = rx_q1 && ~rx_q0;
rx_negedge
:表示接收引脚的下降沿。当rx_q1
为 1 而rx_q0
为 0 时,意味着接收到数据的下降沿。rx_q0 是当前时钟周期的接收数据。rx_q1 是前一个时钟周期的接收数据。
接收数据的处理逻辑相对复杂,涉及对 RX 数据的采样、计时、数据转换等操作,通过这些操作来正确接收数据并最终将数据写入 uart_rx
寄存器。
总结:
这个 UART 模块 实现了一个 115200 波特率的串口通信模块。它包括:
- 数据发送和接收的功能。
- 通过控制寄存器设置波特率、使能接收发送等。
- 使用状态机控制数据的发送过程,确保每一位数据都在正确的时钟周期被发送。
广泛应用于串行设备的通信,如计算机与外部设备、嵌入式系统之间的通信等。
其他常见的通信接口,应用场景和通信特性
1. SPI (Serial Peripheral Interface)
- 用途:SPI 是一种同步串行通信协议,通常用于微控制器与外部外设(如传感器、存储设备、显示屏等)之间的数据交换。
- 特点:支持全双工通信,使用多个信号线(如:MOSI、MISO、SCK、CS),速度较快。
- 信号线:
- MOSI (Master Out Slave In):主设备发送数据到从设备。
- MISO (Master In Slave Out):从设备发送数据到主设备。
- SCK (Serial Clock):时钟信号,由主设备提供。
- CS (Chip Select):选择要与主设备通信的从设备。
2. I2C (Inter-Integrated Circuit)
- 用途:I2C 是一种同步串行通信协议,广泛应用于低速设备(如传感器、EEPROM、ADC等)之间的通信。
- 特点:支持多主机和多从机的配置,只需要两条信号线:数据线和时钟线。它是一个半双工通信协议,即数据传输和时钟信号共享。
- 信号线:
- SCL (Serial Clock):时钟信号。
- SDA (Serial Data):数据传输线。
3. CAN (Controller Area Network)
- 用途:CAN 是一种多主机的串行通信协议,常用于汽车电子和工业自动化系统中。它用于设备和控制器之间的通信,尤其在噪声环境下表现良好。
- 特点:支持高速的数据传输,具备错误检测和故障保护机制,可以在多个节点之间传输信息。
- 信号线:
- CAN_H 和 CAN_L:用于差分信号传输。
4. RS-232 (Recommended Standard 232)
- 用途:RS-232 是一种标准的串行通信协议,常用于计算机和外设(如调制解调器、打印机等)之间的连接。
- 特点:使用的信号电平与 TTL 和 CMOS 不同,通常通过 DB9 或 DB25 连接器实现。它的传输速度较低,传输距离有限。
- 信号线:
- TX(Transmitter):数据输出。
- RX(Receiver):数据输入。
- GND(Ground):地线。
5. USB (Universal Serial Bus)
- 用途:USB 是一种广泛使用的串行通信协议,主要用于计算机和外部设备(如键盘、鼠标、打印机、存储设备等)之间的通信。
- 特点:支持即插即用,能够提供电力给外设,数据传输速率较高。
- 信号线:
- D+ 和 D-:数据传输线。
- Vcc:电源线。
- GND:地线。
6. Ethernet (以太网)
- 用途:Ethernet 是一种用于局域网(LAN)通信的标准协议,广泛应用于计算机、路由器、交换机等设备之间的网络通信。
- 特点:支持高速数据传输,广泛应用于家庭、办公和企业网络。
- 信号线:
- Tx+ 和 Tx-:发送数据。
- Rx+ 和 Rx-:接收数据。
7. Bluetooth
- 用途:蓝牙是一种短距离无线通信协议,广泛用于无线设备之间的通信,如耳机、鼠标、手机等。
- 特点:低功耗,适合短距离通信,支持点对点或点对多点通信。
- 信号:无线信号。
8. Wi-Fi
- 用途:Wi-Fi 是一种无线局域网通信协议,广泛用于无线互联网连接,常见于智能手机、笔记本电脑、路由器等设备中。
- 特点:支持高速无线网络通信,适合家庭、办公室等环境。
- 信号:无线信号。
9. MIPI (Mobile Industry Processor Interface)
- 用途:MIPI 是一种高带宽的串行通信接口,广泛应用于移动设备(如智能手机、平板电脑等)中的图像传输、显示、摄像头接口等。
- 特点:支持高数据速率和低功耗,常用于连接图像传感器、显示屏等设备。
- 信号线:
- D-PHY:用于高速数据传输。
- C-PHY:用于低功耗模式下的数据传输。
10. LVDS (Low-Voltage Differential Signaling)
- 用途:LVDS 是一种低电压差分信号传输协议,常用于高速数据传输,例如在显示器、通信设备中。
- 特点:适用于高速信号传输,具有较低的功耗和噪声免疫能力。
- 信号线:
- LVDS+ 和 LVDS-:用于差分信号传输。
他们和RIB-接口总线的区别:
RIB (Request/Response Interface Bus)
1. RIB (Request/Response Interface Bus)
RIB 是一个 请求/响应总线接口,通常在复杂的系统设计中用于 主从结构 的通信。它通常用于在处理器和不同外设、模块之间进行通信。这种接口设计允许主设备(如 CPU 或其他控制器)向从设备发送请求,并等待从设备的响应。RIB 接口通常用于不同模块之间的低级别通信,例如 处理器与外设、内存、IO 设备之间的通信。
RIB 接口的特点:
- 主从通信:一个或多个主设备通过请求/响应的方式与从设备进行通信。
- 简单的请求-响应机制:主设备发出请求,从设备处理并返回响应。
- 支持不同的数据传输模式:可以使用简单的读/写操作进行通信,通常不涉及复杂的协议。
- 灵活性:RIB 可以与多种外设连接,如存储器、外部设备等。
- 接口信号:通常包括地址线、数据线和控制信号(如读写使能)。
RIB 用途示例:
- 内存接口:CPU 通过 RIB 向 RAM 发送读/写请求。
- 外设接口:CPU 向外设发出控制命令,并等待响应数据。
2. 与 UART, SPI, I2C 等接口的区别
-
UART:是一个 异步串行通信协议,常用于计算机与外设之间的数据通信,支持点对点的全双工传输。UART 是标准的串行接口,通常用于通信距离较远的设备(如计算机与调制解调器、传感器等)。
-
SPI:是一种 同步串行接口,用于主设备与多个从设备之间的高速数据传输。SPI 支持全双工通信,但它通常需要更多的信号线(如 MOSI、MISO、SCK 和 CS)。
-
I2C:是一种 同步串行接口,通常用于低速设备之间的通信。I2C 使用两条信号线(SDA 和 SCL)支持多主机和多从机的通信,适合低速设备。
关键区别:
- 协议类型:UART、SPI 和 I2C 都是标准的通信协议(通常涉及数据传输的格式、时序、错误处理等),而 RIB 主要是一个 硬件总线接口,用于简单的请求/响应通信。它通常不涉及复杂的协议。
- 数据传输模式:
- UART 是异步通信,而 SPI 和 I2C 是同步通信。
- RIB 则通常用于较低级别的控制信号和响应,没有复杂的数据协议,主要关注传输控制信号。
- 用途:
- UART、SPI 和 I2C 通常用于外设之间的数据交换,具有明确的协议和数据格式。
- RIB 更多用于 内部模块之间 的简单通信,如处理器与内存、外设之间的低级控制信号传递。
总结:
- RIB 通常用于模块间的低级请求/响应式通信,通常不涉及复杂的协议。它是硬件设计中常见的总线接口,用于控制信号和基本数据的传输。
- UART, SPI, 和 I2C 是不同的通信协议,涉及到更高层次的数据传输,并且通常用于 计算机与外设 或 外设与外设 之间的通信,且每种协议都有明确的通信规范。