module uart_rx(
    input wire sys_clk,  // U18
    input wire sys_rst,  //J15

    input wire rxd,  // 连接到了外部的 tx, 持续接收串行信号

    output reg rx_done,  // 接收完毕
    output reg [7:0] rx_data  // 接收到的串行信号转成的并行信号
);

parameter CLK_FREQ = 5000_0000;
parameter BPS = 115200;
// parameter MAX = 434;  // 波特率如果是115200 那就需要  当前50m的时钟计数434次 才能和 1s/115200 相等
localparam B_MAX = CLK_FREQ / BPS;


// 对异步来的数据 打拍变成同步到当前时钟作用域下的数据
reg rxd_d0;
reg rxd_d1;
reg rxd_d2;

always @(posedge sys_clk or negedge sys_rst) begin
    if (sys_rst == 1'b0) begin
        rxd_d0 <= 'b0;
        rxd_d1 <= 'b0;
        rxd_d2 <= 'b0;
    end
    else begin
        rxd_d0 <= rxd;
        rxd_d1 <= rxd_d0;
        rxd_d2 <= rxd_d1;
    end
end

reg rx_busy;  // 是否在接收数据了, 这个在时序图中名字是 rx_flag, 这个在计数到216的时候拉低 (434/2)-1=216
wire start_en = rxd_d2 && !rxd_d1 && !rx_busy;  // 使用组合逻辑(相当于提前一个时钟周期), 只有rx2是高 rx1是低的时候 此时形成rx的下降沿, 且还没有忙碌的时候, 当前信号的高电平只持续一个时钟周期哦, 意味着他的在拉高的下一个时钟周期, 数据就开始传输了
reg [3:0]rx_d_cnt;  // 当前接收数据的数据数量, 注意 首个数据第零位是起始位, 最后末位第九位是结束位, 接收10次数据, 有效数据是1~8 一共8个数据
reg [15:0] baud_cnt;  // 波特率计数器, 在当前第n个时钟周期 代表了传输一位数据, 在开始接收数据rx_busy为高电平的时候 开始计数, 不在接收数据的时候 立刻清零
reg [7:0] temp_rx_data;  // 临时保存数据的寄存器

// rx_d_cnt 和 baud_cnt 会在 rx_busy 拉低的 下一个时钟周期跟着拉低 不再进行各种计数
// start_en 因为是组合逻辑电路会在 结束位之前的一个时钟周期 提前拉高, 使得在结束位结束 相同的时钟周期 busy会立即因为 之前的start_en是高电平而变高
// start_en 会在busy 开始处理数据的时候 进行拉低, 所以该信号只持续一个时钟周期, 只是作为采集 结束位下降沿

always @(posedge sys_clk or negedge sys_rst) begin
    if (sys_rst == 1'b0) begin
        rx_busy <= 'b0;
    end
    else if(start_en) begin  // 如果开始接收数据了, 就改为繁忙(由于rx_busy拉高, 组合逻辑电路会导致start_en立即拉低)
        rx_busy <= 'b1;
    end
    // 如果当前接收到了第9个数据(), 并且 到了最大波特率计数器的一半也就是停止位一半的之前(216)时候, 数据其实就全部接收完毕了
    // 提前拉低该信号, 防止停止位后紧跟这开始位导致采集不到
    else if(rx_d_cnt == 'b1001 && baud_cnt == (B_MAX>>1) - 1) begin  
        rx_busy <= 'b0;
    end
    else begin
        rx_busy <= rx_busy;
    end
end


// 波特率计数器
always @(posedge sys_clk or negedge sys_rst) begin
    if (sys_rst == 1'b0) begin
        baud_cnt <= 'b0;
    end
    // 必须要开始传输数据
    else if (rx_busy) begin
        if (baud_cnt == B_MAX-'b1) begin  // 从0开始计数的 0~433  一共计数434次
            baud_cnt <= 'b0;
        end
        else begin
            baud_cnt <= baud_cnt + 'b1; 
        end
    end
    else begin
        baud_cnt <= 'b0;
    end
end


// 当前接收数据的数据数量
always @(posedge sys_clk or negedge sys_rst) begin
    if (sys_rst == 1'b0) begin
        rx_d_cnt <= 'b0;
    end
    // 必须要开始传输数据
    else if (rx_busy) begin
        // 波特率计数器 累加到最大的时候, 说明当前该bit数据接收完了, 加1 准备接收下一个数据
        if (baud_cnt == B_MAX-'b1) begin
            rx_d_cnt <= rx_d_cnt + 'b1;
        end
        else begin
            rx_d_cnt <= rx_d_cnt;
        end
    end
    else begin
        rx_d_cnt <= 'b0;
    end
end


// 根据 rx_d_cnt 来寄存 rxd端口串行的数据到指定的 bit
// 在中间采样精确一点
always @(posedge sys_clk or negedge sys_rst) begin
    if (sys_rst == 1'b0) begin
        temp_rx_data <= 'b0;
    end
    else if (rx_busy) begin
        // 波特率计数到一半的时候进行采样, 最准确
        if (baud_cnt == (B_MAX>>1) - 1) begin
            case (rx_d_cnt)
                'b0001: temp_rx_data[0] <= rxd_d2;
                'b0010: temp_rx_data[1] <= rxd_d2;
                'b0011: temp_rx_data[2] <= rxd_d2;
                'b0100: temp_rx_data[3] <= rxd_d2;
                'b0101: temp_rx_data[4] <= rxd_d2;
                'b0110: temp_rx_data[5] <= rxd_d2;
                'b0111: temp_rx_data[6] <= rxd_d2;
                'b1000: temp_rx_data[7] <= rxd_d2;
                default :;
            endcase
        end
        else begin
            temp_rx_data <= temp_rx_data;
        end
    end
    else begin
        // 当不再接收的时候 需要清零等待下次接收
        temp_rx_data <= 'b0;
    end
end


// 接收完毕, 给设置相应的信号和数据
always @(posedge sys_clk or negedge sys_rst) begin
    if (sys_rst == 1'b0) begin
        rx_done <= 'b0;
        rx_data <= 'b0;
    end
    // 这个其实就是 busy 拉低的条件, 只要满足busy拉低条件,说明数据已经可用, 那立即拉高done(1个周期)
    else if(rx_d_cnt == 'b1001 && baud_cnt == (B_MAX>>1) - 1) begin
        rx_done <= 'b1;
        rx_data <= temp_rx_data;
    end
    else begin
        // 否则 就是没有完成  done持续的就是一个时钟周期(busy 拉低会让rx_d_cnt和baud_cnt 也清零)
        rx_done <= 'b0;
        // 没有完成 就继续保持data, 只有继续为 done的时候 才会继续更新 rx_data的值, 就是一直保持呗, 防止外部对done打拍, 采集不到data数据
        rx_data <= rx_data;
    end
end


endmodule
