1. 多协议通信控制器模块 (multi_protocol_controller
)
简要介绍
这是整个设计的顶层模块,承担着整合各个子模块的重要任务,是整个系统的核心枢纽。它负责协调 UART、SPI、I2C 等不同通信协议模块以及 DMA 模块的工作,同时处理不同时钟域之间的信号交互,确保各个模块能够在不同的时钟环境下稳定、高效地协同工作。
原理
- 时钟管理:系统存在两个不同的时钟域,即系统时钟域(
clk_sys
)和外设时钟域(clk_peri
)。不同的模块可能工作在不同的时钟域,这就需要进行跨时钟域处理,以避免数据传输过程中出现亚稳态等问题。 - 信号同步:对于单比特信号(如
dma_start
和dma_done
),使用同步器(sync_module
)进行跨时钟域同步。同步器通过两级触发器的方式,将输入信号在目标时钟域下进行同步,从而避免亚稳态信号传播到后续逻辑中。对于多比特数据的跨时钟域传输,则使用同步 FIFO(sync_fifo
)来缓存和传输数据,确保数据在不同时钟域之间的可靠传输。 - 模块实例化:实例化 UART、SPI、I2C 模块以及 DMA 模块,并将它们的输入输出信号进行合理连接,使得各个模块能够相互协作,完成多协议通信和数据传输的任务。
代码展示:
module multi_protocol_controller #(
parameter SYS_CLK_FREQ = 100_000_000, // 系统时钟频率 (Hz)
parameter PERI_CLK_FREQ = 50_000_000 // 外设时钟频率 (Hz)
)(
// 系统时钟域(高速时钟,如 CPU 时钟)
input wire clk_sys, // 系统时钟(通常 50MHz/100MHz)
input wire rst_n_sys, // 系统时钟域复位(异步,低有效)
// 外设时钟域(低速时钟,如外设接口时钟)
input wire clk_peri, // 外设时钟(通常 50MHz 或更低)
input wire rst_n_peri, // 外设时钟域复位(异步,低有效)
// UART 接口
input wire uart_rx, // UART 接收
output wire uart_tx, // UART 发送
// SPI 接口
input wire spi_miso, // SPI 主入从出
output wire spi_mosi, // SPI 主出从入
output wire spi_sclk, // SPI 时钟
output wire spi_cs_n, // SPI 片选(低有效)
// I2C 接口
inout wire i2c_sda, // I2C 双向数据线
output wire i2c_scl, // I2C 时钟线
// DMA 控制接口(系统时钟域)
input wire dma_start, // DMA 传输启动信号(系统时钟域)
input wire [31:0] dma_src_addr, // 源地址(系统地址空间)
input wire [31:0] dma_dst_addr, // 目的地址(系统地址空间)
input wire [15:0] dma_length, // 传输长度(字节数)
output wire dma_done, // DMA 完成标志(系统时钟域)
// 调试接口
output wire [7:0] debug_info // 预留调试信号
);
// ========== 跨时钟域信号声明 ==========
// 系统时钟域 → 外设时钟域(单比特同步)
wire dma_start_peri; // 同步后的 DMA 启动信号(外设时钟域)
// 外设时钟域 → 系统时钟域(单比特同步)
wire dma_done_sys; // 同步后的 DMA 完成信号(系统时钟域)
// 多比特数据跨时钟域(外设 ↔ 系统)
wire [7:0] dma_data_peri; // 外设时钟域 DMA 数据
wire [7:0] dma_data_sys; // 系统时钟域 DMA 数据
wire dma_data_valid_peri; // 外设时钟域数据有效
wire dma_data_valid_sys; // 系统时钟域数据有效
// ========== 同步器实例化(单比特跨时钟域) ==========
// DMA 启动信号同步(系统 → 外设)
sync_module #(.WIDTH(1)) u_sync_dma_start (
.clk_dst(clk_peri),
.rst_n_dst(rst_n_peri),
.data_in(dma_start),
.data_out(dma_start_peri)
);
// DMA 完成信号同步(外设 → 系统)
sync_module #(.WIDTH(1)) u_sync_dma_done (
.clk_dst(clk_sys),
.rst_n_dst(rst_n_sys),
.data_in(dma_done), // 注意:dma_done 是外设时钟域生成的信号(见 DMA 模块连接)
.data_out(dma_done_sys)
);
assign dma_done = dma_done_sys; // 输出到系统时钟域的完成标志
// ========== 同步 FIFO 实例化(多比特跨时钟域) ==========
sync_fifo #(
.DATA_WIDTH(8),
.DEPTH(64) // 深度扩展至 64
) u_sync_fifo (
.wr_clk(clk_peri), // 写时钟:外设时钟域(协议模块输出数据)
.wr_rst_n(rst_n_peri),
.wr_en(dma_data_valid_peri),
.din(dma_data_peri),
.rd_clk(clk_sys), // 读时钟:系统时钟域(DMA 读取数据)
.rd_rst_n(rst_n_sys),
.rd_en(dma_data_valid_sys),
.dout(dma_data_sys)
);
// ========== 协议模块实例化(外设时钟域) ==========
// UART 模块
uart_module u_uart (
.clk(clk_peri),
.rst_n(rst_n_peri),
.baud_rate_div(26'd500), // 波特率 115200 @ 50MHz 时钟
.rx(uart_rx),
.tx(uart_tx),
.data_out(dma_data_peri), // UART 接收数据作为 DMA 输入(外设 → 系统)
.data_valid(dma_data_valid_peri)
);
// SPI 模块(主设备模式)
spi_module u_spi (
.clk(clk_peri),
.rst_n(rst_n_peri),
.clk_div(8'd10), // SPI 时钟分频(生成 5MHz 时钟 @ 50MHz 外设时钟)
.miso(spi_miso),
.mosi(spi_mosi),
.sclk(spi_sclk),
.cs_n(spi_cs_n),
.data_out(dma_data_peri), // SPI 接收数据作为 DMA 输入
.data_valid(dma_data_valid_peri)
);
// I2C 模块(主设备模式)
i2c_module u_i2c (
.clk(clk_peri),
.rst_n(rst_n_peri),
.CLK_FREQ(PERI_CLK_FREQ),
.I2C_FREQ(400_000), // I2C 总线频率 400kHz
.start(1'b0), // 预留 I2C 启动信号(可扩展)
.i2c_addr(7'd0), // 预留设备地址(可扩展)
.tx_data(8'd0), // 预留发送数据(可扩展)
.tx_en(1'b0), // 预留发送使能(可扩展)
.rx_en(1'b0), // 预留接收使能(可扩展)
.tx_done(), // 预留发送完成(可扩展)
.rx_done(), // 预留接收完成(可扩展)
.rx_data(dma_data_peri), // I2C 接收数据作为 DMA 输入
.ack_error(debug_info[0]),// 调试信号(应答错误)
.sda(i2c_sda),
.scl(i2c_scl)
);
// ========== DMA 模块实例化(双时钟域) ==========
dma_module u_dma (
.clk_sys(clk_sys),
.rst_n_sys(rst_n_sys),
.clk_peri(clk_peri),
.rst_n_peri(rst_n_peri),
.dma_start(dma_start_peri), // 同步后的启动信号(外设时钟域)
.dma_src_addr(dma_src_addr),
.dma_dst_addr(dma_dst_addr),
.dma_length(dma_length),
.dma_data_in(dma_data_sys), // 系统时钟域数据(来自同步 FIFO)
.dma_data_out(dma_data_peri), // 外设时钟域数据(发送到协议模块)
.dma_data_valid_in(dma_data_valid_sys), // 系统时钟域数据有效
.dma_data_valid_out(dma_data_valid_peri),// 外设时钟域数据有效
.dma_done(dma_done) // DMA 完成标志(外设时钟域生成,同步到系统)
);
// ========== 调试信号 ==========
assign debug_info = {
dma_data_valid_peri, // 调试位 0:外设数据有效
dma_data_valid_sys, // 调试位 1:系统数据有效
u_i2c.ack_error, // 调试位 2:I2C 应答错误
5'b0 // 预留其他调试位
};
endmodule
2. 同步器模块 (sync_module
)
简要介绍
该模块主要用于处理单比特信号的跨时钟域同步问题。在数字电路中,当信号从一个时钟域传输到另一个时钟域时,如果不进行同步处理,可能会出现亚稳态现象,导致后续逻辑出现错误。同步器通过简单的两级触发器结构,有效地解决了这个问题。
原理
- 两级触发器结构:同步器内部包含两级触发器,第一级触发器在源时钟域下采样输入信号,将其存储在
sync_reg
中。第二级触发器在目标时钟域下采样sync_reg
的输出,并将其作为最终的同步输出信号dat