一、实验原理:
奇校验原理:
前8位是数据位,第九位是校验位,数据位中的1是奇数个,则校验位是0,保证数据位和校验位异或的结果是1;数据位中的1是偶数个,则校验位为1,保证数据位和校验位异或的结果是1。
1bit | 2bit | 3bit | 4bit | 5bit | 6bit | 7bit | 8bit | 9bit |
1 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 |
1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 1 |
偶校验原理:
前8位是数据位,第九位是校验位,数据位中1的个数是奇数个,则校验位是1,保证数据位和校验位异或的结果是0;数据位中1的个数是偶数个,则校验位为0,保证数据位和校验位异或的结果是0。
1bit | 2bit | 3bit | 4bit | 5bit | 6bit | 7bit | 8bit | 9bit |
1 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 |
1 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 0 |
二、程序设计
TOP层:
`timescale 1ns / 1ps
module TOP(
input sys_clk ,
input rst_n ,
input rx_data ,
output tx_data
);
wire[7:0] uart_data ;
wire rx_done ;
uart_rx uart_rx_u1(
. sys_clk ( sys_clk ) ,
. rst_n ( rst_n ) ,
. rx_data ( rx_data ) , //输入串行数据
. uart_data ( uart_data) , // 输出并行数据
. rx_done ( rx_done ) //数据传输完成结束信号
);
wire tx_done ;
send send_u1(
. sys_clk (sys_clk ) ,
. rst_n (rst_n ) ,
. uart_data (uart_data) ,
. rx_done (rx_done ) ,
. tx_data (tx_data ) ,
. tx_done (tx_done )
);
endmodule
接收端:一般由串口工具增加校验位,串口工具根据发送的数据和校验方式选择“0”或者“1”,在仿真中,我们只能选择固定的数字“0”或者“1”进行校验,所以串口中无论发送什么数字,都有返回值,但是在仿真中,只能返回部分符合筛选机制的值。
`timescale 1ns / 1ps
/串口接收端 串行转并行
module uart_rx(
input sys_clk ,
input rst_n ,
input rx_data , //输入串行数据
output reg[7:0] uart_data , // 输出并行数据
output reg rx_done //数据传输完成结束信号
);
parameter SYSCLK = 50_000_000 ;
parameter Baud = 115200 ;
parameter COUNT = SYSCLK/Baud ;//434 传输1比特所需要的时钟周期
parameter MID = COUNT/2 ;
parameter MODE_CHECK = 0 ;
///产生开始信号(检测下降沿-----二级寄存)
reg rx_reg1 ;
reg rx_reg2 ;
wire start_flag ; 开始信号
reg rx_flag ;
reg [4:0] cnt_bit ;///0~10
reg [9:0] cnt ;///434
reg [8:0] data_reg ;
reg check ;
always@(posedge sys_clk)
if(!rst_n)begin
rx_reg1 <= 1 ; //处于空闲位 数据线上无数据
rx_reg2 <= 1 ; //处于空闲位 数据线上无数据
end
else
begin
rx_reg1 <= rx_data ;
rx_reg2 <= rx_reg1 ;
end
assign start_flag = ~rx_reg1 & rx_reg2 ;
//rx_flag
always@(posedge sys_clk )
if(!rst_n)
rx_flag <= 0 ;
else if (start_flag)
rx_flag <= 1 ;
else if ( cnt_bit == 10 && cnt == MID -1 )
rx_flag <= 0 ;
else
rx_flag <= rx_flag ;
cnt 434
always@(posedge sys_clk )
if(!rst_n)
cnt <= 0;
else if ( rx_flag == 1 )begin
if ( cnt == COUNT -1) ///一定要减一,如果不减一,实际会计到435次,反算回去波特率就不是115200了
cnt <= 0;
else
cnt <= cnt +1 ;
end
else
cnt <= 0 ;
/计数器
always@(posedge sys_clk )
if(!rst_n)
cnt_bit <= 0 ;
else if ( rx_flag )begin
if ( cnt == COUNT -1)begin
if(cnt_bit == 10)
cnt_bit <= 0 ;
else
cnt_bit <= cnt_bit +1 ;
end
else
cnt_bit <= cnt_bit ;
end
else
cnt_bit <= 0 ;
[7:0]uart_data
/*
cnt_bit = 0 起始位
cnt_bit = 1-8 数据位
cnt_bit = 9 停止位
但是 [7:0]uart_data 没有uart[8]
所以
if ( cnt == MID -1 && cnt_bit > 0 && cnt_bit < 9 )
uart_data[cnt_bit -1] <= rx_data ;
*/
// always@(posedge sys_clk )
// if(!rst_n)
// uart_data <= 0 ;
// else if ( rx_flag )begin
// if ( cnt == MID -1 && cnt_bit > 0 && cnt_bit < 9 )
// uart_data[cnt_bit -1] <= rx_data ; //这里uart_data是不断随着cnt_bit变化的,只有在第九位的时候才有正确的最终值
// else
// uart_data <= uart_data ;
// end
// else
// uart_data <= 0 ;
因为这里要让uart_data只在rx_done的时候有值,所以定义一个中间寄存器data_reg
always@(posedge sys_clk )
if(!rst_n)
data_reg <= 0 ;
else if ( rx_flag )begin
if ( cnt == MID -1 && cnt_bit > 0 && cnt_bit < 10 )
data_reg[cnt_bit -1] <= rx_data ; //这里uart_data是不断随着cnt_bit变化的,只有在第九位的时候才有正确的最终值
else
data_reg <= data_reg ;
end
else
data_reg <= 0 ;
// 其他赋值方法1
// always@(posedge sys_clk )
// if(!rst_n)
// uart_data <= 0;
// else if (cnt == MID -1)begin
// case(cnt_bit)
// 0: uart_data <= 0 ;
// 1: uart_data [0] <= rx_data ;
// 2: uart_data [1] <= rx_data ;
// 3: uart_data [2] <= rx_data ;
// 4: uart_data [3] <= rx_data ;
// 5: uart_data [4] <= rx_data ;
// 6: uart_data [5] <= rx_data ;
// 7: uart_data [6] <= rx_data ;
// 8: uart_data [7] <= rx_data ;
// 9: uart_data <= uart_data ; ///停止位的时候数据传完,保持
// endcase
// end
// else
// uart_data <= uart_data ;
// 其他赋值方法2
// always@(posedge sys_clk )
// if(!rst_n)
// uart_data <= 0 ;
// else if (cnt == MID -1 && cnt_bit > 0 && cnt_bit < 9)
// uart_data <= {rx_data,uart_data[7:1]} ;
// else
// uart_data <= uart_data ;
check
always@(posedge sys_clk)
if(!rst_n)
check <= 0 ;
else if (rx_flag )begin
if ( cnt_bit == 10 )
check <= ^data_reg ;
else
check <= check ;
end
else
check <= 0 ;
给uart_data赋值
always@(posedge sys_clk )
if(!rst_n)
uart_data <= 0 ;
else if (rx_flag)begin
if (cnt_bit == 10 && cnt == 3 && check == MODE_CHECK)
uart_data <= data_reg ;
else
uart_data <= uart_data ;
end
else
uart_data <= uart_data ; 可以保持到下一个数据到来
// uart_data <= 0 ; 只保持在rx_done处于高电平的时候
/rx_done
always@(posedge sys_clk )
if(!rst_n)
rx_done <= 0 ;
else if (rx_flag)begin
if ( cnt_bit == 10 && cnt == MID/2 -1)
rx_done <= 1 ;
else
rx_done <= 0 ;
end
else
rx_done <= 0 ;
endmodule
发送端:
`timescale 1ns / 1ps
/*
校验位:
奇校验:去保证[数据位+校验位]中1的个数为奇数
偶校验:去保证[数据位+校验位]中1的个数为偶数
eg:偶校验
1100_1010 0
0 0_ 1 1
0 0
0 0
0
1010_1110 1
1 1_0 1
0 1
1 1
0
偶校验:^[数据位+校验位] = 0
奇校验:^[数据位+校验位] = 1
-------------------------------
接收端加校验位:^[数据位+校验位] = 0,则输出并行数据
发送端加校验位:把校验位放到数据线上发送,保证[数据位+校验位]中1的个数为偶数
*/
module send(
input sys_clk ,
input rst_n ,
input [7:0] uart_data ,
input rx_done ,
output reg tx_data ,
output reg tx_done
);
parameter SYSCLK = 50_000_000 ;
parameter Baud = 115200 ;
parameter COUNT = SYSCLK/Baud ;//434 传输1比特所需要的时钟周期
parameter MID = COUNT/2 ;
wire tx_start ;
reg tx_flag ;
reg tx_reg1 ;
reg tx_reg2 ;
reg[4:0] cnt_bit ;
reg[10:0] cnt ;
tx_start
always@(posedge sys_clk)
if(!rst_n)begin
tx_reg1 <= 0 ;
tx_reg2 <= 0 ;
end
else begin
tx_reg1 <= rx_done ;
tx_reg2 <= tx_reg1 ;
end
assign tx_start = tx_reg1 & ~tx_reg2 ;
///tx_flag
always@(posedge sys_clk)
if(!rst_n)
tx_flag <= 0 ;
else if ( tx_start)
tx_flag <= 1 ;
else if ( cnt == MID -1 && cnt_bit == 10)
tx_flag <= 0 ;
else
tx_flag <= tx_flag ;
///计时器
cnt 434
always@(posedge sys_clk )
if(!rst_n)
cnt <= 0;
else if ( tx_flag == 1 )begin
if ( cnt == COUNT -1) ///一定要减一,如果不减一,实际会计到435次,反算回去波特率就不是115200了
cnt <= 0;
else
cnt <= cnt +1 ;
end
else
cnt <= 0 ;
/计数器
always@(posedge sys_clk )
if(!rst_n)
cnt_bit <= 0 ;
else if ( tx_flag )begin
if ( cnt == COUNT -1)begin
if(cnt_bit == 10)///0123456789 10
cnt_bit <= 0 ;
else
cnt_bit <= cnt_bit +1 ;
end
else
cnt_bit <= cnt_bit ;
end
else
cnt_bit <= 0 ;
parameter MODE_CHECK = 0 ;
赋值
always@(posedge sys_clk )
if(!rst_n)
tx_data <= 1 ; //表示没有数据
else if ( tx_flag )begin
if ( cnt_bit > 0 && cnt_bit < 9 )
///cnt_bit 0123456789
///tx_data 0123456789
///uart_data 12345678
tx_data <= uart_data [cnt_bit-1]; //这里uart_data是不断随着cnt_bit变化的,只有在第九位的时候才有正确的最终值
else if(cnt_bit == 0)
tx_data <= 0 ;
else if(cnt_bit == 9)
tx_data <= (MODE_CHECK == 0)? ^uart_data: ~^uart_data;
/*
MODE_CHECK == 0是偶校验,假如uart_data是1110_0000,其异或的结果
是1,将异或的结果作为校验位,让数据位和校验位异或的结果为0,满足偶校验。
假如uart_data是1110_1000,其异或的结果是0,将异或的结果作为校验位,
让数据位和校验位异或的结果为0,满足偶校验。奇校验则相反。
*/
else if (cnt_bit == 10)///停止位
tx_data <= 1 ;
else
tx_data <= tx_data ;
end
else
tx_data <= 1 ;
always@(posedge sys_clk )
if(!rst_n)
tx_done <= 0 ;
else if (tx_flag)begin
if ( cnt_bit == 10 && cnt == MID/2 -1)
tx_done <= 1 ;
else
tx_done <= 0 ;
end
else
tx_done <= 0 ;
endmodule
三、仿真设计
`timescale 1ns / 1ps
module test_bench( );
reg sys_clk ;
reg rst_n ;
reg rx_data ; //输入串行数据
wire tx_data ;
parameter SYSCLK = 50_000_000 ;
parameter Baud = 115200 ;
parameter COUNT = SYSCLK/Baud ;//434 传输1比特所需要的时钟周期
parameter MID = COUNT/2 ;
initial
begin
sys_clk = 0 ;
rst_n = 0 ;
#10
rst_n = 1 ;
end
always #1 sys_clk = ~sys_clk ; //2ns
initial
begin
uart_out (8'h31);
uart_out (8'hef);
uart_out (8'h10);
uart_out (8'h78);
uart_out (8'h66);
end
//任务函数
task uart_out ;
input [7:0] DATA ;
begin
rx_data = 1 ;///空闲位初始
#20
rx_data = 0 ;///起始位
///传输1bit的计时次数*1周期时间=总时间
#(COUNT*2) rx_data = DATA[0] ;///数据位第一位
#(COUNT*2) rx_data = DATA[1] ;///数据位第二位
#(COUNT*2) rx_data = DATA[2] ;
#(COUNT*2) rx_data = DATA[3] ;
#(COUNT*2) rx_data = DATA[4] ;
#(COUNT*2) rx_data = DATA[5] ;
#(COUNT*2) rx_data = DATA[6] ;
#(COUNT*2) rx_data = DATA[7] ;
#(COUNT*2) rx_data = 0 ;
/*这一步相当于代替了串口调试工具,串口调试工具根据选择的“偶校验”
自动在数据位后面生成“0”或者“1”,我们这边只能赋值一个数“0”或者“1”。
我选择赋值为0,所以注定了“8'h31”“8'hef”“8'h10”是通不过的。
*/
#(COUNT*2) rx_data = 1 ;
#(COUNT*2) ;//停止位也需要时间
end
endtask
TOP TOP_U1(
. sys_clk (sys_clk) ,
. rst_n (rst_n ) ,
. rx_data (rx_data) ,
. tx_data (tx_data)
);
endmodule
仿真结果:
实验结果:
管脚绑定:
set_property PACKAGE_PIN T17 [get_ports rst_n]
set_property PACKAGE_PIN K17 [get_ports sys_clk]
set_property PACKAGE_PIN W18 [get_ports rx_data]
set_property PACKAGE_PIN V18 [get_ports tx_data]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property IOSTANDARD LVCMOS33 [get_ports rx_data]
set_property IOSTANDARD LVCMOS33 [get_ports sys_clk]
set_property IOSTANDARD LVCMOS33 [get_ports tx_data]
实验结果:
回环测试成功