You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
5.8 KiB
Verilog
157 lines
5.8 KiB
Verilog
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
|