FPGA和其他设备进行通信的时候,如果传输的是大量数据,肯定需要打包(组帧)进行传输,而且都需要有帧头和校验位来确保帧数据传输正确。今天说一下最近自己做的一个项目涉及到的这个问题。


    当FPGA作为接收端去接收帧数据的时候,即使保证一帧数据的帧头是正确的,而且校验位是正确的也不能百分百保证这帧数据正确接收了,可能情况:1、数据传输有错误,但是错误的数据也得到了一样正确的校验位;
2、数据中正好有一位数据是帧头,而我们把这个数据当做了帧头,这个按帧头接收完一帧数据,帧尾的校验位也正好算出来也是对的
,等其他小概率事件。对于第一种情况我们可以通过设计好的校验方式(CRC或者其他校验方式)来使这种概率减低,还有数据是突发的还是一直连续不断发送的,这些都是我们在接收机设计的时候必须考虑的。

   
为了避免这种情况,在每次接收数据的时候,我们设置接收数据的时候有两种状态,一种是同步态,一种是捕获态(如下图)。在同步态连续接收到几帧数据(在此处我们叫做权值)的时候,才能进入捕获态,在捕获态我们接收到的数据才视作正确的数据。这样我们设计的权值越大,那么第二种情况发生的概率越低,但是付出的代价是丢弃了好多数据帧。





    话不多说,程序的状态转移图如图所示:




   
本次是基于Verilog的一个接收机设计,模块按阻塞赋值(顺序执行)的方式。数据是从PC用串口按8位数据的方式传输到FPGA,帧长是344位,即34字节。帧头是两字节,校验位是7位数据,校验和的方式。

[email protected](posedge i_rx_state_1 or negedge
i_reset)//一个i_rx_state_1的上升沿代表接收了一字节数据(阻塞赋值) begin if(!i_reset) begin
rx_frame_temp = 344'd0; rx_frame = 344'd0; work_state = 8'd0; frameheader_cache
= 16'd0; rx_byte = 6'd0; syn_weight = 2'd0; capture_signal = 1'b0; end else
begin case(work_state) syn_check_frameheader: begin syn_weight = 2'd0;
rx_frame_temp = 344'd0; frameheader_cache = frameheader_cache<<8;
frameheader_cache[7:0] = iv_rx_data_1;
if(frameheader_cache==16'hA555)//判断是帧头a555的话,进入同步态,否则继续检测 begin work_state =
synchronous_state; rx_frame_temp[15:0] = frameheader_cache; rx_byte = 6'd2; end
else work_state = syn_check_frameheader; end synchronous_state: begin
frameheader_cache = 16'd0;//清除缓存 if(rx_byte>=6'd43)//如果接收满数据,则rx_byte一直处于满状态
rx_byte = rx_byte; else rx_byte = rx_byte+1'b1; rx_frame_temp =
rx_frame_temp<<8; rx_frame_temp[7:0] = iv_rx_data_1;
if(rx_byte>=6'd43)//接收完一帧数据(满状态) begin check_byte =
;//计算校验位,自己按照自己需求设计,为了节省代码空间,省去
if((rx_frame_temp[7:0]==check_byte)&&(rx_frame_temp[343:328]==16'hA555))//校验位正确,等待权值为2的时候进入捕获态,否则继续接收数据
begin syn_weight = syn_weight+1'b1; if(syn_weight>=2'd2)//连续两个帧数据接收正确进入捕获态
work_state = capture_check_frameheader; else //权值<2,再次检测下一个帧头 work_state =
syn_check_frameheader; end else
//校验位不正确,权值减一;下一次滑动接收数据,并检验帧头和校验位是否正确,不断循环,直到接收到正确的数据 begin
if(syn_weight==2'd0)//权值为0 滑动检测 begin work_state = synchronous_state;
syn_weight = 2'd0; end else begin syn_weight = syn_weight-1'b1; work_state =
syn_check_frameheader; end end end end capture_check_frameheader: begin
syn_weight = 2'd0; rx_frame_temp = 344'd0; capture_signal = 1'b0;//复位捕获信号
rx_byte = 6'd0;//清除接收计数缓存 frameheader_cache =frameheader_cache<<8;
frameheader_cache[7:0]=iv_rx_data_1;
if(frameheader_cache==16'hA555)//判断是帧头的话,进入同步态,否则继续检测 begin work_state =
capture_state; rx_frame_temp[15:0] = frameheader_cache;
                                   rx_byte = 6'd0;//清除接收计数缓存 end else
work_state = capture_check_frameheader; end capture_state: begin rx_byte =
rx_byte+1'b1; rx_frame_temp = rx_frame_temp<<8; rx_frame_temp[7:0] =
iv_rx_data_1; if(rx_byte>=43)//接收完一帧数据 begin capture_signal = 1'b1;//产生捕获信号
check_byte = ;//计算校验位
if((rx_frame_temp[7:0]==check_byte)&&(rx_frame_temp[343:328]==16'hA555)) begin
work_state = capture_check_frameheader; rx_frame = rx_frame_temp; end else
begin rx_byte = 6'd0;//清除接收计数缓存 work_state = syn_check_frameheader;
rx_frame_temp = 344'd0; frameheader_cache = 16'd0; syn_weight = 2'd0; end end
end default: begin rx_byte = 6'd0;//清除接收计数缓存 work_state =
syn_check_frameheader; rx_frame_temp = 344'd0; frameheader_cache = 16'd0;
syn_weight = 2'd0; end endcase end end