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
 |