集成电路流片随笔15:uart_debug CRC是什么 tinyriscv

uart_debug,它的作用是通过 UART(串口)实现调试下载功能,比如从 PC 接收程序数据并写入 ROM(起始地址为 0x00000000)。


🧠 模块总览(功能简述):

该模块实现了一个 串口调试程序下载器。在系统启动后,它会等待串口发来程序数据(一般是 bootloader 发送过来),验证通过后会将这些数据写入存储器中(比如 ROM),可用于系统启动。


🧩 输入输出接口说明:

端口名方向含义
clkin系统时钟
rstin复位信号(低电平有效)
debug_en_iin调试模块使能(高电平有效)
req_oout请求总线信号(告诉 rib 本模块在访问)
mem_we_oout写使能(写内存)
mem_addr_oout访问的目标地址(通常是 uart/rom)
mem_wdata_oout写入的数据
mem_rdata_iin读回的数据

🔄 状态机控制(核心逻辑)

代码中通过一个 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_ENDCRC 校验数据包
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值