uart_debug
,它的作用是通过 UART(串口)实现调试下载功能,比如从 PC 接收程序数据并写入 ROM(起始地址为 0x00000000
)。
🧠 模块总览(功能简述):
该模块实现了一个 串口调试程序下载器。在系统启动后,它会等待串口发来程序数据(一般是 bootloader 发送过来),验证通过后会将这些数据写入存储器中(比如 ROM),可用于系统启动。
🧩 输入输出接口说明:
端口名 | 方向 | 含义 |
---|---|---|
clk | in | 系统时钟 |
rst | in | 复位信号(低电平有效) |
debug_en_i | in | 调试模块使能(高电平有效) |
req_o | out | 请求总线信号(告诉 rib 本模块在访问) |
mem_we_o | out | 写使能(写内存) |
mem_addr_o | out | 访问的目标地址(通常是 uart/rom) |
mem_wdata_o | out | 写入的数据 |
mem_rdata_i | in | 读回的数据 |
🔄 状态机控制(核心逻辑)
代码中通过一个 14 位状态机
控制整个流程,主要状态:
状态常量 | 含义 |
---|---|
S_IDLE | 初始状态,准备初始化 UART 控制器寄存器 |
S_INIT_UART_BAUD | 设置波特率(115200) |
S_REC_FIRST_PACKET | 接收第一包数据(包含文件大小) |
S_CLEAR_UART_RX_OVER_FLAG | 清除 UART 接收完成标志位 |
S_WAIT_BYTE / S_WAIT_BYTE2 | 等待 UART 数据准备好 |
S_GET_BYTE | 读取 UART 的一个字节数据 |
S_CRC_START/CRC_CALC/CRC_END | CRC 校验数据包 |
S_WRITE_MEM | 将接收到的数据写入内存 |
S_SEND_ACK/SEND_NAK | 向上位机返回应答 |
🧾 常量宏定义(寄存器映射)
`define UART_CTRL_REG 32'h3000_0000 // 控制寄存器
`define UART_STATUS_REG 32'h3000_0004 // 状态寄存器
`define UART_BAUD_REG 32'h3000_0008 // 波特率配置寄存器
`define UART_TX_REG 32'h3000_000C // 发送寄存器
`define UART_RX_REG 32'h3000_0010 // 接收寄存器
🧱 数据结构
reg[7:0] rx_data[0:35]; // 接收一包数据(最多 36 字节)
reg[15:0] remain_packet_count; // 剩余包的数量
reg[31:0] fw_file_size; // 固件总大小(从首包中获得)
reg[31:0] write_mem_addr; // 当前写入的目标地址(初始为 0)
🔄 下载流程(串口烧录)
graph TD
A[上位机发送第一包] --> B[包含固件大小 + 数据]
B --> C[模块收到并存入 rx_data[]]
C --> D[CRC 校验]
D -- 通过 --> E[发送 ACK 并进入下一包]
D -- 错误 --> F[发送 NAK 并重传]
E --> G[每包写入 write_mem_addr]
G --> H[地址每次 +4]
H --> I[直到 remain_packet_count == 0,烧录完成]
// 读接收到的串口数据
always @ (posedge clk) begin
if (rst == 1'b0 || debug_en_i == 1'b0) begin
rec_bytes_index <= 8'h0;
end else begin
case (state)
S_GET_BYTE: begin
rx_data[rec_bytes_index] <= mem_rdata_i[7:0];
rec_bytes_index <= rec_bytes_index + 1'b1;
end
S_REC_FIRST_PACKET: begin
rec_bytes_index <= 8'h0;
end
S_REC_REMAIN_PACKET: begin
rec_bytes_index <= 8'h0;
end
endcase
end
end
// 固件大小
always @ (posedge clk) begin
if (rst == 1'b0 || debug_en_i == 1'b0) begin
fw_file_size <= 32'h0;
end else begin
case (state)
S_CRC_START: begin
fw_file_size <= {rx_data[61], rx_data[62], rx_data[63], rx_data[64]};
end
endcase
end
end
// 烧写固件
always @ (posedge clk) begin
if (rst == 1'b0 || debug_en_i == 1'b0) begin
write_mem_addr <= 32'h0;
end else begin
case (state)
S_REC_FIRST_PACKET: begin
write_mem_addr <= `ROM_START_ADDR;
end
S_CRC_END: begin
if (write_mem_addr > 0)
write_mem_addr <= write_mem_addr - 4;
end
S_WRITE_MEM: begin
write_mem_addr <= write_mem_addr + 4;
end
endcase
end
end
always @ (posedge clk) begin
if (rst == 1'b0 || debug_en_i == 1'b0) begin
write_mem_data <= 32'h0;
end else begin
case (state)
S_REC_FIRST_PACKET: begin
write_mem_data <= 32'h0;
end
S_CRC_END: begin
write_mem_data <= {rx_data[4], rx_data[3], rx_data[2], rx_data[1]};
end
S_WRITE_MEM: begin
write_mem_data <= {rx_data[write_mem_byte_index3], rx_data[write_mem_byte_index2], rx_data[write_mem_byte_index1], rx_data[write_mem_byte_index0]};
end
endcase
end
end
always @ (posedge clk) begin
if (rst == 1'b0 || debug_en_i == 1'b0) begin
write_mem_byte_index0 <= 8'h0;
end else begin
case (state)
S_REC_FIRST_PACKET: begin
write_mem_byte_index0 <= 8'h0;
end
S_CRC_END: begin
write_mem_byte_index0 <= 8'h5;
end
S_WRITE_MEM: begin
write_mem_byte_index0 <= write_mem_byte_index0 + 4;
end
endcase
end
end
always @ (posedge clk) begin
if (rst == 1'b0 || debug_en_i == 1'b0) begin
write_mem_byte_index1 <= 8'h0;
end else begin
case (state)
S_REC_FIRST_PACKET: begin
write_mem_byte_index1 <= 8'h0;
end
S_CRC_END: begin
write_mem_byte_index1 <= 8'h6;
end
S_WRITE_MEM: begin
write_mem_byte_index1 <= write_mem_byte_index1 + 4;
end
endcase
end
end
always @ (posedge clk) begin
if (rst == 1'b0 || debug_en_i == 1'b0) begin
write_mem_byte_index2 <= 8'h0;
end else begin
case (state)
S_REC_FIRST_PACKET: begin
write_mem_byte_index2 <= 8'h0;
end
S_CRC_END: begin
write_mem_byte_index2 <= 8'h7;
end
S_WRITE_MEM: begin
write_mem_byte_index2 <= write_mem_byte_index2 + 4;
end
endcase
end
end
always @ (posedge clk) begin
if (rst == 1'b0 || debug_en_i == 1'b0) begin
write_mem_byte_index3 <= 8'h0;
end else begin
case (state)
S_REC_FIRST_PACKET: begin
write_mem_byte_index3 <= 8'h0;
end
S_CRC_END: begin
write_mem_byte_index3 <= 8'h8;
end
S_WRITE_MEM: begin
write_mem_byte_index3 <= write_mem_byte_index3 + 4;
end
endcase
end
end
🧮 CRC 校验逻辑 Cyclic Redundancy Check
使用 Modbus CRC-16
算法(标准工业协议 CRC16),判断一包数据是否有效。
数据包末尾的两个字节用于放校验码,代码对接收的内容逐 bit 校验,对应状态:
S_CRC_START -> S_CRC_CALC -> S_CRC_END
// CRC计算
always @ (posedge clk) begin
if (rst == 1'b0 || debug_en_i == 1'b0) begin
crc_result <= 16'h0;
end else begin
case (state)
S_CRC_START: begin
crc_result <= 16'hffff;
end
S_CRC_CALC: begin
if (crc_bit_index == 4'h0) begin
crc_result <= crc_result ^ rx_data[crc_byte_index];
end else begin
if (crc_bit_index < 4'h9) begin
if (crc_result[0] == 1'b1) begin
crc_result <= {1'b0, crc_result[15:1]} ^ 16'ha001;
end else begin
crc_result <= {1'b0, crc_result[15:1]};
end
end
end
end
endcase
end
end
always @ (posedge clk) begin
if (rst == 1'b0 || debug_en_i == 1'b0) begin
crc_bit_index <= 4'h0;
end else begin
case (state)
S_CRC_START: begin
crc_bit_index <= 4'h0;
end
S_CRC_CALC: begin
if (crc_bit_index < 4'h9) begin
crc_bit_index <= crc_bit_index + 1'b1;
end else begin
crc_bit_index <= 4'h0;
end
end
endcase
end
end
always @ (posedge clk) begin
if (rst == 1'b0 || debug_en_i == 1'b0) begin
crc_byte_index <= 8'h0;
end else begin
case (state)
S_CRC_START: begin
crc_byte_index <= 8'h1;
end
S_CRC_CALC: begin
if (crc_bit_index == 4'h0) begin
crc_byte_index <= crc_byte_index + 1'b1;
end
end
endcase
end
end
模块作用总结
作用:实现 MCU/FPGA 通过串口接收程序(如固件文件),写入到 ROM 中,类似于 bootloader 或串口烧写器。
优点:模块化设计、硬件可控、支持 CRC 校验保障可靠性。
适用场景:SoC 启动加载、裸机系统调试、开发板烧录等。
全部原文可参考https://gitee.com/liangkangnan/tinyriscv/blob/master/rtl/debug/uart_debug.v