1.时序
RS232串口是一位数据的输入,通过多次传输最好传达相应的数据。而接收模块的作用则是将一位一位的输入进行整合,得出输入的最终数据
①当rx输入有第一个个下降沿,传输开始;数据传完给1拉高,结束本次传输
②一共10个数据,第一个0构成下降沿,后面的8个位数据位,最后一位是停止位1
③传输的速率看波特率,根据波特率计算每个数据发送所需要的时间
2.分析
①下降沿开始传输——那么我们就要做一个监沿器来识别下降沿,这里还要额外考虑一个问题,那就是rx信号是由电脑发过来的,和FPGA是不一样的时钟,因此我们要先做异步处理,再对rx进行监沿。所以,这里需要2个D触发器进行异步处理,再加一个D触发器构成监沿器
②根据波特率算出来的时间,用一个计数器计一个数据的时间,再用一个计数器来数数据个数
③计数什么时候开始呢?只有在传输开始到传输结束这段时间进行计数
④将输入给到输出,注意,这里应当用中间取值法,避免边沿的跳变
⑤为了方便看数据,再多加一个传输完成信号
⑥模块大致为:
3.verilog代码
module rx
(
input clk,
input rst_n,
input rx,
output reg [7:0]rx_data,
output reg rx_over
);
//参数
parameter _9600 = 5208; //9600波特率
parameter _8bit = 9; //加上起始位共9位
parameter MIDDLE = _9600/2 - 1; //中间取值
//内部信号
reg rx_ff0;
reg rx_ff1;
reg rx_ff2;
wire pose;
wire nege;
reg flag_rx;
reg [3:0] cnt_8bit;
reg bit_cnt_flag;
reg [12:0] cnt_9600;
//功能块
always@(posedge clk )begin
rx_ff0 <= rx;
rx_ff1 <= rx_ff0;
rx_ff2 <= rx_ff1;
end
assign pose = rx_ff2 == 0 && rx_ff1 == 1;
assign nege = rx_ff2 == 1 && rx_ff1 == 0;
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
flag_rx <= 0;
else if(nege)
flag_rx <= 1;
else if(cnt_8bit == 0 && cnt_9600 == _9600-1 )
flag_rx <= 0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_9600 <= 0;
else if(cnt_9600 == _9600-1)
cnt_9600 <= 0;
else if(flag_rx)
cnt_9600 <= cnt_9600 + 1;
else
cnt_9600 <= 0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
bit_cnt_flag <= 0;
else if(cnt_9600 == MIDDLE)
bit_cnt_flag <= 1;
else
bit_cnt_flag <= 0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_8bit <= 0;
else if(bit_cnt_flag && cnt_8bit == _8bit - 1 )
cnt_8bit <= 0;
else if(bit_cnt_flag)
cnt_8bit <= cnt_8bit + 1;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
rx_over <= 0;
else if(bit_cnt_flag && cnt_8bit == _8bit - 1)
rx_over <= 1;
else
rx_over <= 0;
end
always@(posedge clk or negedge rst_n)begin
if(!rst_n)
rx_data <= 0;
else if(bit_cnt_flag && cnt_8bit >= 1 )
rx_data <= {rx_ff1,rx_data[7:1]};
end
endmodule
4.测试仿真
测试文件的功能在于,一位一位的给出rx信号,通过modelsim对源文件进行验证仿真;
①先用$ readmemh(’’ ./x.txt’’,mem) 命令将txt文本的数据以十六进制读到寄存器mem中
②再一位一位拆分输入的数据
③按照’’ 0 ,8个数据, 1’’ 这样的格式给rx赋值
④注意一个bit持续的时间
代码如下:
`timescale 1 ns/1 ns
module tb_rx();
//输入信号
reg clk ;
reg rst_n;
reg rx;
//输出信号
wire rx_over;
wire[7:0] rx_data;
//8位寄存器 4个
reg [7:0] mem_a[3:0];
//参数
parameter CYCLE = 20;
//待测试的模块例化
rx u1
(
.clk (clk ),
.rst_n (rst_n ),
.rx (rx ),
.rx_data (rx_data ),
.rx_over (rx_over )
);
//生成50M时钟
initial begin
clk = 0;
forever
#(CYCLE/2)
clk=~clk;
end
//产生复位信号
initial begin
rst_n = 1;
#2;
rst_n = 0;
#2
rst_n = 1;
end
//把文件里的数据写入mem_a存储器中
//此文件写好放在sim的文件夹中
initial $readmemh("./rx.txt",mem_a);
initial begin
rx = 0;
#20
rx = 1;
#20
byte(); //task
end
//task1 , 将txt文件的4个数存到寄存器a中
task byte();
integer i;
for(i =0;i<4;i = i+1)begin
bit(mem_a[i]);
end
endtask
//task2 , 将txt的每一个数据分为8位,一位一位取值
task bit(input [7:0] data_inter );
integer i;
for(i =0;i<10;i = i+1)begin
case(i)
0: rx <= 1'b0 ;
1: rx <= data_inter[0];
2: rx <= data_inter[1];
3: rx <= data_inter[2];
4: rx <= data_inter[3];
5: rx <= data_inter[4];
6: rx <= data_inter[5];
7: rx <= data_inter[6];
8: rx <= data_inter[7];
default: rx <= 1;
endcase
#104160; //一个bit数据持续的时间 5208个时钟
end
endtask
endmodule
仿真结果:
我的txt文件的内容:
仿真的结果: