目录
1、简述
2、设计
3、实现
4、测试
1、简述
串口作为 CPU 最常使用的外设资源之一,常常出现在各种场合,既然最近在入坑 FPGA,那么先搞一个简单的串口接收机来玩玩;
串口相关的基本知识就不在这里重复议论了,参考我的另一篇文章《STM32F103ZET6 — USART》,总的来说,时序如下所示:
站在硬件的角度上来讲,要实现一个串口接收器,需要考虑一下几点内容:
1、串口通信,默认情况没有数据传送的时候,RX 线上空闲为高电平,出现起始位,线上会被拉低,那么首先考虑做一个下降沿检测器,来检测起始位;
2、发现起始位后,便需要进行线上数据的采样,采样的点,需要在数据的中心点,才能保证数据的稳定,那么就需要根据传送的波特率来计算采样的时间,并将数据采样到内部寄存器;
3、采样数据的个数,根据配置的数据位,校验位和停止位的 bit 数决定,那么就需要根据串口的配置,来对采样数据的个数进行计数,所以需要设计一个数据计时器;
2、设计
好了,简单的进行了需求分析和设计分解后,那么理想中的设计框图为:
1、PLL 接收来自外部的 25MHz 的晶振输入,将输出的时钟 clk 给 UART RX 模块,同时将时钟锁定的信号 Locked 接到 UART RX 的内部复位信号;
2、rx 信号直接接入到下降沿检测电路的输入,进行边沿检测,检测完成后,确定有起始位后,那么告诉波特率发生器,可以产生波特率信号;
3、波特率信号发生器根据输入的时钟和设置的波特率,进行计数,并在需要采样的点的地方,产生采样信号,供给下级电路;
4、状态机根据波特率的采样信号,来进行状态转换,从 IDLE 到 Sampling 状态,并在 Sampling 状态开始采样,并进行采样数据的计数;
5、当采样个数完成后,将数据并行的发送给后继的 FIFO,完成一帧数据的接收;
为了简化设计和仿真, 这里假设了波特率的 Counter 为 50(视具体波特率和输入时钟计算),8个数据位,没有校验位,1个停止位,并将采样后的数据直接以并行的形式输出;
3、实现
Verilog 源码设计如下(0 warning,0 error):
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 22:27:09 12/04/2019
// Design Name:
// Module Name: uart_rx
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module uart_rx(input clk,input rst_n,input rx,output reg [9:0] data_out);// State Machine Definationparameter IDLE = 2'b01;parameter SAMP = 2'b10;// UART Configure Definationparameter START_BIT = 1;parameter DATA_BIT = 8;parameter STOP_BIT = 1;parameter PARI_BIT = 0;parameter RECV_BIT = START_BIT + DATA_BIT + STOP_BIT + PARI_BIT;// Negedge Detection Filterreg [3:0] data_in;
always @ (posedge clk or negedge rst_n)if (!rst_n) begindata_in[0] <= 1'b0;data_in[1] <= 1'b0;data_in[2] <= 1'b0;data_in[3] <= 1'b0;endelse begindata_in[0] <= rx;data_in[1] <= data_in[0];data_in[2] <= data_in[1];data_in[3] <= data_in[2];endwire rx_neg = data_in[3] & data_in[2] & (~data_in[1]) & (~data_in[0]);reg [1:0] current_state, next_state;// Current State To Next Statealways @ (posedge clk or negedge rst_n)if(!rst_n)current_state <= IDLE;elsecurrent_state <= next_state;// Statereg sample_finish;always @ (current_state or sample_finish or rx_neg)beginnext_state = 2'bx;case(current_state)IDLE : beginif (rx_neg) next_state = SAMP;else next_state = IDLE;endSAMP : beginif (sample_finish) next_state = IDLE;else next_state = SAMP;enddefault : next_state = IDLE;endcaseendreg [3:0] recv_cnt;reg sample_en;reg [RECV_BIT - 1 : 0] data_temp;always @ (posedge clk or negedge rst_n)if (!rst_n) begindata_out <= 10'bx;data_temp <= 10'bx;sample_finish <= 1'b0;sample_en <= 1'b0;recv_cnt <= 4'b0;endelse begincase (next_state)IDLE : begin//data_out <= 10'bx;data_temp <= 10'bx;sample_finish <= 1'b0;sample_en <= 1'b0;recv_cnt <= 4'b0;endSAMP : beginif (recv_cnt == RECV_BIT) begindata_out <= data_temp;data_temp <= 10'bx;sample_finish <= 1'b1;sample_en <= 1'b0;recv_cnt <= 4'b0;endelse beginsample_en <= 1'b1;if (baud_clk) begindata_out <= data_out;data_temp[recv_cnt] <= rx;sample_finish <= 1'b0;recv_cnt <= recv_cnt + 1'b1;endelse begindata_out <= data_out;data_temp <= data_temp;sample_finish <= sample_finish;recv_cnt <= recv_cnt;endendenddefault: begindata_out <= 10'bx;sample_finish <= 1'b0;sample_en <= 1'b0;endendcaseend// Sample Counter Signal Generatorparameter BAUD_MAX = 50;reg [5:0] baud_cnt;always @ (posedge clk or negedge rst_n)if (!rst_n) beginbaud_cnt <= 6'd0;endelse beginif (sample_en) beginif (baud_cnt == BAUD_MAX - 1) baud_cnt <= 6'd0;else baud_cnt <= baud_cnt + 1'b1;endelse baud_cnt <= 6'd0;end// Sample Clock Signal Generatorparameter BAUD_CNT_H = (BAUD_MAX / 2);wire baud_clk = (baud_cnt == BAUD_CNT_H) ? (1'b1) : (1'b0);
endmodule
4、测试
测试的 testbench 为:
`timescale 1ns / 1ps// Company:
// Engineer:
//
// Create Date: 14:03:06 12/05/2019
// Design Name: uart_rx
// Module Name: D:/Xlinx_ISE_Projects/testbench/uart_rx_tb.v
// Project Name: test
// Target Device:
// Tool versions:
// Description:
//
// Verilog Test Fixture created by ISE for module: uart_rx
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// module uart_rx_tb;// Inputsreg clk;reg rst_n;reg rx;// Outputswire [9:0] data_out;// Instantiate the Unit Under Test (UUT)uart_rx uut (.clk(clk), .rst_n(rst_n), .rx(rx), .data_out(data_out));
always #1 clk = ~clk;initial begin// Initialize Inputsclk = 0;rst_n = 0;rx = 1;// Wait 100 ns for global reset to finish#20;// Add stimulus hererst_n = 1;#20;// Generate Start bit#50 rx = 1'b0;// 8 data bits#100 rx = 1'b1;#100 rx = 1'b0;#100 rx = 1'b1;#100 rx = 1'b0;#100 rx = 1'b1;#100 rx = 1'b0;#100 rx = 1'b1;#100 rx = 1'b0;// Generate Stop bit#100 rx = 1'b1;#600;#100 rx = 1'b0;#100 rx = 1'b0;#100 rx = 1'b1;#100 rx = 1'b0;#100 rx = 1'b1;#100 rx = 1'b1;#100 rx = 1'b0;#100 rx = 1'b1;endendmodule
输出的波形为:
后一段为:
采样点为红色,均在数据中间,黄色为采样完成的信号;