uart串口接受模块的编写,宏定义参数由俩个分别是波特率和时钟频率,输入参数由时钟clk,复位rst,校验模式mod,校验模式有3种,当mod为0时是无校验模式,为1时是偶校验模式,为2时是奇校验模式。输入已读标志位read,为1时表示输入的有效信号已经被读取了。串口输入信号data_in。模块输出有两个,串口接受到的数据data_rx,与数据有效标志信号valid。
串口传输每一位所需要的时钟周期等于时钟频率除波特率,所以要定义一个计数器和一个标志位,当计数器计数到最大值时,标志位为高电平,最大值位上述所需要的时钟周期。
给串口输入的数据延迟3个时钟周期,赋值给一个3位的寄存器data,这一步目的时让输入的数据与时钟对其,并且消除亚稳态,此外这个寄存器还有个作用就是判断输入信号的下降沿是否来临,当data[2:1]等于2'b10时,就表示下降沿来临,有可能是接受到数据的起始位。
用状态机的方式表示接受数据的每一位,当接收到下降沿时,经过一个标志位,如果时上升沿,则表示此下降沿不是数据的起始位,因为前面说过每一位数据都会持续一个完整的标志位周期。若判断下降沿是数据的起始位时,后续8位就是发送的数据位了,让状态机给隔一个flag周期向下传递,知道第9位,状态机的第10种状态时,此时接受到时信号是数据的校验位,根据前面选择的检验方式进行校验,若检验成功进入11,校验失败进入12,之后11,12,都进入13。
根据状态机是否到过11为标志,当状态机到达11时,给数据有效位为1。当外部检测到数据有效位为1时读取传出的数据即可。读完后read置1,给数据有效位清零。数据传输的方式就是在状态机rt在2到9时进行传入数据寄存,把串行数据变为并行数据,当通过校验再把数据传输。
module uart_rx#(
parameter FREQ = 50_000_000,
parameter BAUDRATE = 115200
)(
input clk,
input rst,
input [1:0] mod,
input read,
input data_in,
output reg [7:0] data_rx,
output reg valid,
);
reg [31:0] cnt;
wire cnt_f= (cnt == FREQ/BAUDRATE/2 - 1);
reg f;
always@(posedge clk) if(rst) begin cnt<=0; f<=0;end else case(flag) 0: begin f<=0;cnt<=0;end
defautl: if(cnt_f) f=~f;cnt<=0;else cnt<=cnt+1;endcase
always@(posedge clk) data <= 0;else data <= {data[1:0],data_in};
wire flag = cnt_f & (~f);
reg [7:0] data_s;
reg [3;0] rt;
always@(clk) if(rst) rt <= 0;else case(rt)
0: if(data[2:1] == 2'b10) rt <= 1;
1: if(flag) rt <= (data[2]) ? 0 : 2;
2: if(flag) rt <= 3;
3: if(flag) rt <= 4;
4: if(flag) rt <= 5;
5: if(flag) rt <= 6;
6: if(flag) rt <= 7;
7: if(flag) rt <= 8;
8: if(flag) rt <= 9;
9: if(flag) rt <= 10;
10: if(flag)
begin
case(mod)
1: rt <= (data[2] != ^data_s) ? 11 : 12;
2: rt <= (data[2] == ^data_s) ? 11 : 12;
default: rt <= 11;
endcase
end
11: if(flag) rt <= 13;
12: if(flag) rt <= 13;
13: if(flag) rt <= 0;
default: rt <= 0;
endcase
always@(posedge clk) if(rt == 2) data_s[0] <= data[2];
always@(posedge clk) if(rt == 3) data_s[1] <= data[2];
always@(posedge clk) if(rt == 4) data_s[2] <= data[2];
always@(posedge clk) if(rt == 5) data_s[3] <= data[2];
always@(posedge clk) if(rt == 6) data_s[4] <= data[2];
always@(posedge clk) if(rt == 7) data_s[5] <= data[2];
always@(posedge clk) if(rt == 8) data_s[6] <= data[2];
always@(posedge clk) if(rt == 9) data_s[7] <= data[2];
always@(posedge clk) if(rst) valid <= 0;else if(rt == 12) valid <= 1;else if(read) valid <= 0;
always@(posedge clk) if(rst) data_rx <= 0; else if(rt == 11)data_rx <= data_s;
endmodule