多字节串口收发IP设计(四)串口接收模块设计及联合仿真(含源码)
串口接收模块的设计比较简单,按照串口的接收时序按位将数据接收即可。
本模块比较特殊的是增加了奇偶校验的计算,并判断校验是否正确。
模块接口
信号名 | 位宽 | 说明 |
I_clk | 1 | 模块时钟 |
I_rst | 1 | 模块复位,高电平有效 |
I_uart_baud_cnt | 16 | 波特率计数器,计数单bit时钟数 |
I_uart_check | 2 | 校验位设置:0-无校验,1-奇校验,2-偶校验 |
O_user_rx_data | 8 | 用户接收的数据 |
O_user_rx_valid | 1 | 用户接收数据有效,高电平有效 |
O_user_rx_error | 1 | 奇偶校验错误标志,高电平有效 |
I_uart_rx | 1 | 串行数据接收 |
串口接收时序
源代码
`timescale 1ns / 1psmodule UART_rx#(parameter C_DATA_WIDTH = 8 , //数据位宽parameter C_STOP_WIDTH = 1 //停止位位宽
)(input wire i_clk ,input wire i_rst ,input wire i_uart_rx ,input wire [15:0] i_uart_baud_cnt ,input wire [ 1:0] i_uart_check , //0-no,1-odd,2-evenoutput reg [C_DATA_WIDTH - 1 : 0] o_user_rx_data ,output reg o_user_rx_valid ,output reg o_user_rx_error
);
////////////////////////////////////////////////////////////////////
//parameter define
localparam C_DATA_LEN = 2 + C_DATA_WIDTH + C_STOP_WIDTH ; //start+data+check+stop//reg define
reg [ 5:0] r_uart_rx ;
reg [15:0] r_cnt_baud ;
reg [15:0] r_cnt_bit ;
reg r_rx_check ;
reg r_run ;//wire define
wire w_rx_en ;
wire w_bit_en ;
wire w_start_flag ;////////////////////////////////////////////////////////////////////assign w_rx_en = (r_cnt_baud == (i_uart_baud_cnt >> 1) - 1 );
assign w_bit_en = (r_cnt_baud == i_uart_baud_cnt - 1 );
assign w_start_flag = ~(r_uart_rx[4] | r_uart_rx[3] | r_uart_rx[2] | r_uart_rx[1] | r_uart_rx[0]) ;////////////////////////////////////////////////////////////////////
always @(posedge i_clk )beginif(i_rst)r_uart_rx <= 6'b111111;elser_uart_rx <= {r_uart_rx[4:0],i_uart_rx};
endalways @(posedge i_clk) beginif (i_rst)r_run <= 0 ;else if (o_user_rx_valid)r_run <= 0 ;else if (w_start_flag && !r_run)r_run <= 1 ;
end always @(posedge i_clk) beginif (i_rst)r_cnt_baud <= 0 ;else if (r_run && r_cnt_baud < i_uart_baud_cnt - 1)r_cnt_baud <= r_cnt_baud + 1 ;else r_cnt_baud <= 0 ;
endalways @(posedge i_clk )beginif(i_rst)r_cnt_bit <= 'd0;else if(r_run && r_cnt_bit == C_DATA_LEN - 3 && i_uart_check == 0 && w_bit_en)r_cnt_bit <= 'd0;else if(r_run && r_cnt_bit == C_DATA_LEN - 2 && i_uart_check > 0 && w_bit_en)r_cnt_bit <= 'd0;else if(r_run && w_bit_en)r_cnt_bit <= r_cnt_bit + 1;
endalways @(posedge i_clk )beginif(i_rst)o_user_rx_data <= 'd0;else if(r_run && r_cnt_bit >= 1 && r_cnt_bit <= C_DATA_WIDTH && w_rx_en)o_user_rx_data <= {r_uart_rx[5],o_user_rx_data[C_DATA_WIDTH-1:1]};//LSB first
endalways @(posedge i_clk )beginif(i_rst)o_user_rx_valid <= 'd0;else if(r_run && r_cnt_bit == C_DATA_WIDTH && i_uart_check == 0 && w_bit_en)o_user_rx_valid <= 1'd1;else if(r_run && r_cnt_bit == C_DATA_WIDTH + 1 && w_bit_en)o_user_rx_valid <= 1'd1; else if(r_run && r_cnt_bit == C_DATA_WIDTH + 1 && w_bit_en)o_user_rx_valid <= 1'd1;elseo_user_rx_valid <= 'd0;
endalways @(posedge i_clk )beginif(i_rst)o_user_rx_error <= 'd0;else if(r_run && r_cnt_bit == C_DATA_WIDTH && i_uart_check == 0 && w_bit_en)o_user_rx_error <= 1'd0;else if(r_run && r_cnt_bit == C_DATA_WIDTH + 1 && i_uart_check == 1 && r_uart_rx[5] == r_rx_check && w_bit_en)o_user_rx_error <= 1'd1; else if(r_run && r_cnt_bit == C_DATA_WIDTH + 1 && i_uart_check == 2 && r_uart_rx[5] == ~r_rx_check && w_bit_en)o_user_rx_error <= 1'd1;elseo_user_rx_error <= 'd0;
endalways @(posedge i_clk )beginif(i_rst)r_rx_check <= 'd0;else if(r_run && r_cnt_bit >= 1 && r_cnt_bit <= C_DATA_WIDTH && w_rx_en)r_rx_check <= r_rx_check ^ r_uart_rx[5];else if (!r_run)r_rx_check <= 'd0;
endendmodule
仿真代码
`timescale 1ns / 1psmodule tb_UART( );
reg i_clk ;
reg i_rst ;
reg [31:0] i_uart_baud_cnt ;
reg [ 1:0] i_uart_check ; //0-no,1-odd,2-even
reg [ 7:0] i_user_tx_data ;
reg i_user_tx_valid ;
wire o_user_tx_ready ;
wire o_uart_tx ;
wire [ 7:0] w_usr_rx_data ;
wire w_usr_rx_valid ;
wire o_user_rx_error ;wire w_active = i_user_tx_valid & o_user_tx_ready ;initial begini_clk = 0 ;i_rst = 1 ;i_uart_check = 0 ;i_uart_baud_cnt = 8 ;#100;@(posedge i_clk) i_rst = 0 ;
endalways #5 i_clk = ~i_clk ;always @(posedge i_clk) beginif (i_rst)i_user_tx_data <= 8'h55 ;else if (w_active)i_user_tx_data <= i_user_tx_data + 1 ;
endalways @(posedge i_clk) beginif (i_rst)i_user_tx_valid <= 0 ;else if (w_active)i_user_tx_valid <= 0 ; else if (o_user_tx_ready)i_user_tx_valid <= 1 ;
endUART_tx u_UART_tx(.i_clk (i_clk ),.i_rst (i_rst ),.i_uart_baud_cnt (i_uart_baud_cnt ),.i_uart_check (i_uart_check ),.i_user_tx_data (i_user_tx_data ),.i_user_tx_valid (i_user_tx_valid ),.o_user_tx_ready (o_user_tx_ready ),.o_uart_tx (o_uart_tx )
);UART_rx#(.C_DATA_WIDTH (8 ), //数据位宽.C_STOP_WIDTH (1 ) //停止位位宽
)UART_rx_u
(.i_clk (i_clk ),.i_rst (i_rst ),.i_uart_rx (o_uart_tx ),.i_uart_baud_cnt (i_uart_baud_cnt),.i_uart_check (i_uart_check ),.o_user_rx_data (w_usr_rx_data ),.o_user_rx_valid (w_usr_rx_valid ),.o_user_rx_error (o_user_rx_error));endmodule
与串口发送模块联合仿真结果
无校验
将i_uart_check信号赋值为0,表示无校验,则数据的长度为:起始位+数据位+停止位=10。
通过仿真结果可以看出,接收时序符合串口发送格式。
R_cnt_bit为0时对应起始位,为1~8时对应数据位。
数据接收完成后,输出接收到的数据和有效信号。
奇校验
将i_uart_check信号赋值为1,表示奇校验,则数据的长度为:起始位+数据位+校验位+停止位=11。
接收数据为8’h55时,其二进制为01010101,数据位有4个1,是偶数,则校验位就为1,r_cnt_bit为9时对应校验位,符合预期。
接收到的校验位与计算的校验位一致,无校验错误。
接收数据为8’h57时,其二进制为01010111,数据位有5个1,是奇数,则校验位就为0,r_cnt_bit为9时对应校验位,符合预期。
接收到的校验位与计算的校验位一致,无校验错误。
偶校验
将i_uart_check信号赋值为2,表示偶校验,则数据的长度为:起始位+数据位+校验位+停止位=11。
接收数据为8’h55时,其二进制为01010101,数据位有4个1,是偶数,则校验位就为0,r_cnt_bit为9时对应校验位,符合预期。
接收到的校验位与计算的校验位一致,无校验错误。
接收数据为8’h57时,其二进制为01010111,数据位有5个1,是奇数,则校验位就为1,r_cnt_bit为9时对应校验位,符合预期。
接收到的校验位与计算的校验位一致,无校验错误。
发送奇校验,接收偶校验
将串口发送模块设置为奇校验,串口接收模块设置为偶校验,验证奇偶校验判断的正确性。
通过仿真结果可以看出,校验全部错误,符合预期。
串口收发的基础模块已开发完成,并完成了仿真测试。
由于没有找到一个较好的可以进行奇偶校验位检验的上位机软件,所以暂时不对串口收发模块进行上板测试。
如果有小伙伴进行上板测试发现了问题,欢迎在此评论,大家一起进步。