1. 程式人生 > >【厲害了FPGA】Verilog實現接收幀資料的一種方法(幀資料同步搜尋檢測)

【厲害了FPGA】Verilog實現接收幀資料的一種方法(幀資料同步搜尋檢測)

    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