加入星計劃,您可以享受以下權(quán)益:

  • 創(chuàng)作內(nèi)容快速變現(xiàn)
  • 行業(yè)影響力擴散
  • 作品版權(quán)保護
  • 300W+ 專業(yè)用戶
  • 1.5W+ 優(yōu)質(zhì)創(chuàng)作者
  • 5000+ 長期合作伙伴
立即加入
  • 正文
    • 理論基礎(chǔ)
    • 二、M序列
    • 三、漢明碼
    • 四、系統(tǒng)結(jié)構(gòu)
    • 系統(tǒng)的?verilog?實現(xiàn)
    • 一、數(shù)據(jù)傳輸過程
    • 二、MCU模塊
    • 三、coder?模塊
    • 四、add_noise?模塊
    • 五、decoder?模塊
    • 六、correct?模塊
    • 七、Correct_Decoder?模塊
    • 八、slaver?模塊
    • 九、Top?模塊
    • 仿真
    • 一、模塊的建立及其仿真環(huán)境的生成
    • 二、模塊仿真
  • 相關(guān)推薦
  • 電子產(chǎn)業(yè)圖譜
申請入駐 產(chǎn)業(yè)圖譜

基于FPGA的直接擴頻通信系統(tǒng)設(shè)計(附代碼)

2023/01/23
1930
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

大俠好,歡迎來到FPGA技術(shù)江湖,江湖偌大,相見即是緣分。大俠可以關(guān)注FPGA技術(shù)江湖,在“闖蕩江湖”、"行俠仗義"欄里獲取其他感興趣的資源,或者一起煮酒言歡。

今天給大俠帶來直接擴頻通信,由于篇幅較長,話不多說,上貨。

本篇適用于有一定通信基礎(chǔ)的大俠,本篇使用的理論不僅僅是擴頻通信。為了便于學(xué)習(xí),本章將會以實戰(zhàn)的方式,對整個工程的仿真。并對一些關(guān)鍵的仿真結(jié)果進行說明。各位大俠可依據(jù)自己的需要進行閱讀,參考學(xué)習(xí)。

理論基礎(chǔ)

一、擴頻通信

香農(nóng)(E.Shannon)在 1945 年、1948 年和 1949 年連續(xù)發(fā)表了有關(guān)信息論和通信加密以及系統(tǒng)安全性等 3 篇論文。最后給出信道容量的數(shù)學(xué)計算公式:

( C:信道容量;B:帶寬大??;S:信號能量;N:噪聲能量 )

根據(jù)香農(nóng)最后給出的信道容量公式(C=B·log2(1+S/N))可知,信道容量與帶寬大小正好成正比,不論信噪比(S/N)有多小(但不會為零),只要帶寬足夠大,信道容量就足夠大。根據(jù)這個結(jié)論,引出了擴頻通信技術(shù)。

擴頻通信,即擴展頻譜通信技術(shù)(Spread Spectrum Communication),通過擴頻調(diào)制用一個更高頻率的偽隨機碼將基帶信號擴展到一個更寬的頻帶內(nèi),使發(fā)射信號的能量被擴展到一個更寬的頻帶內(nèi),從而看來如同噪聲一樣,使該系統(tǒng)更具隱藏性和抗干擾性。接收端則采用相同的偽隨機碼進行解擴,從而恢復(fù)出原始信息數(shù)據(jù)。按照頻譜擴展的方式的不同,現(xiàn)有的擴頻通信系統(tǒng)可以分為直接序列擴頻(Direct Sequence Spectrum)工作方式(簡稱直接擴頻方式)、跳變頻率(Frequency Hopping)方式(簡稱跳頻方式)和混合方式四種[1]。本文所設(shè)計的使用直接序列擴頻方式。

直接序列擴頻通信是將帶傳輸?shù)亩M制信息數(shù)據(jù)用高速的偽隨機碼(PN 碼)直接調(diào)制,實現(xiàn)頻譜擴展后傳輸,在接收端使用相逆方式進行解擴,從而可以恢復(fù)信源的信息。最能體現(xiàn)擴頻通信的特點就是它具有優(yōu)異的抗干擾能力。所以它常常被運用于一些干擾性很強的通信領(lǐng)域中。比如無線通信。

二、M序列

2.1 偽隨機碼概述

偽隨機碼也稱為偽隨機序列。是模仿隨機序列的隨機特性而產(chǎn)生的一種碼字,也稱為偽噪聲序列或者偽噪聲嗎。直接擴頻通信的性能取決于其偽隨機序列的性能,偽隨機碼序列是一種規(guī)律難以發(fā)現(xiàn)、具有類似白噪聲統(tǒng)計特性的編碼信號。所以,偽隨機序列通常有以下要求:

a. ‘0’和’1’的個數(shù)基本相等,具有良好的隨機性(由于數(shù)字通信通常以二進制位多,所以要‘0’的概率和’1’的概率基本相等);

b. 具有尖銳的自相關(guān)特性,以保證通過同步偽隨機序列完成擴頻信號的解擴;

c. 不同的 PN 序列具有很小的互相關(guān)特性,以防止通過不同的 PN 序列擴頻后的信號被此干擾;

d. 不同的 PN 序列具有很小的互相關(guān)特性,以防止通過不同的 PN 序列擴頻后的信號被此干擾;

e. PN 序列總量大,以滿足多用戶需求

2.2 偽隨機碼選型

根據(jù)上述要求,常用的序列有包括:m 序列、gold 序列和 Walsh 序列等,m 序列通常容易硬件直接硬件實現(xiàn);gold 序列自相關(guān)性差;Walsh 序列一般使用寫入雙口 RAM 中,然后啟動讀取邏輯序列產(chǎn)生,但耗費大量的硬件邏輯單元。故本設(shè)計選用了 m 序列作為系統(tǒng)的偽隨機碼。

2.3 m 序列產(chǎn)生

m?序列是最長線性反饋移位寄存器序列的簡稱,它是最常用的一種偽隨機序列。由?n?級串聯(lián)寄存器組成,通過反饋邏輯的移位寄存器設(shè)定初始狀態(tài)后,在時鐘的觸發(fā)下,每次移位后各級寄存器狀態(tài)會發(fā)生變化。從任何一個寄存器輸出得到的一串序列,該序列稱為移位寄存器。其框圖如圖?1?所示為一個時鐘觸發(fā)下的時序電路。

圖1

圖中使用 n 個寄存器,通常將 a0 作為輸出信號產(chǎn)生 m 序列。從上圖也可以看出,一個完成的 n 級 m 序列是由一個相應(yīng)的線性反饋邏輯表達式,即為:

(其中, ‘⊕’代表異或運算或叫模 2 加運算,Cn ∈{0,1} )

由上式可知,只有當(dāng) Cn=1 時,對應(yīng)的多項式才有效。為了便于表示,通常將上式與本原多項式對應(yīng)。本原多項式的數(shù)學(xué)表達式如下:

僅當(dāng)該多項式為本原多項式時才能產(chǎn)生 m 序列,以下列出部分本原多項式表 1。

表1 2-10 階本原多項式

其中,n 階 m 序列具有如下特點:

a. 序列長度為 2n-1;

b.‘0’和‘1’個數(shù)相當(dāng),即‘1’的個數(shù)比‘0’的個數(shù)多且僅多 1 個。

本原多項式是由多位科學(xué)家及其科學(xué)工作者最終得來,關(guān)于它們的具體得來,這里不作多解釋。

本文設(shè)計采用的是 5 階 m 序列作為系統(tǒng)的偽隨機碼發(fā)生器,其對應(yīng)的硬件框圖如圖 2。由于級聯(lián)的寄存器初始狀態(tài)不能全為 0。

本設(shè)計中規(guī)定初始狀態(tài)為:a4 a3 a2 a1 a0 = 5’b10000。

a0 輸出的得到的 m 序列為:{0000101011101100011111001101001}。從左到右順序輸出。

圖2

根據(jù)以上 m 序列的拓撲結(jié)構(gòu)圖,我們就很容易使用 FPGA 的資源來設(shè)計5 階的 m 序列,只要 5 個觸發(fā)器和 1 個異或門就可以完成該設(shè)計。而 Verilog HDL 語言更容易完成設(shè)計。具體內(nèi)容,參考 coder 模塊。

三、漢明碼

數(shù)字信號在傳輸過程中常常因干擾而發(fā)生損壞。接收端接收到數(shù)據(jù)后可能錯誤的判決。乘性干擾引起的碼間串?dāng)_可以采用均衡的辦法糾正。而加性干擾的影響則需要其他辦法解決。對于加性干擾,本文考慮使用差錯控制措施。

差錯控制措施,即在數(shù)據(jù)中間添加必要的監(jiān)督位,達到可以對錯誤數(shù)據(jù)的監(jiān)督和糾錯能力。對于差錯控制措施,前輩科學(xué)家和科學(xué)工作者也設(shè)計出多種方法,各有各的優(yōu)劣。本設(shè)計使用的是漢明碼(7,4),其中 7 為碼組的總長度,4 為原始信息位數(shù),則監(jiān)督位為 3 位。故每發(fā)送 4 比特信息需要添加 3 比特的監(jiān)督位,監(jiān)督位是根據(jù)信息位既定約束關(guān)系得到。漢明碼是一種能糾錯 1 比特錯誤的特殊的線性分組碼。由于它的編譯碼簡單,在數(shù)據(jù)通信計算機存儲系統(tǒng)中廣泛應(yīng)用,如藍牙通信技術(shù)和硬盤陣列等。

本設(shè)計所使用的漢明碼的最小碼距為 3,可以糾正 1 為錯誤,檢測 2 位錯誤。但對 2 位錯誤碼并不能正確的糾錯。盡管發(fā)生 1 位錯的概率相對最高,但在一些比較高的應(yīng)用中漢明碼不能滿足要求。碼距是指兩個不同碼組間對應(yīng)位不同的個數(shù),例如 1000111 和 10001100 的碼距為 3。

對于以上介紹比較乏味,以下使用另一種角度來對(7,4)碼進行介紹漢明碼的原理與設(shè)計過程。

我們可以把添加糾錯碼作為一個系統(tǒng),即輸入 4 比特原始信息位(a6,a5,a4,a3)而輸出帶有 3 比特監(jiān)督位(a2,a1,a0)的碼組。

對于 3 個監(jiān)督位,有以下規(guī)則:

S1. 監(jiān)督位 a2 作為 a6、a5 和 a4 的偶校驗碼,即 a2^a6^a5^a4=0;

S2. 監(jiān)督位 a1 作為 a6、a5 和 a3 的偶校驗碼,即 a2^a6^a5^a3=0;

S3. 監(jiān)督位 a0 作為 a6、a4 和 a3 的偶校驗碼,即 a2^a6^a4^a3=0;(‘^’表示異或或者表示模 2 加)對應(yīng)以上 3 個監(jiān)督位的規(guī)則,可以列出其對應(yīng)的全部碼組,如表 2。

表 2

從上表中,不難看出,糾錯碼產(chǎn)生系統(tǒng)輸出由 a6a5a4a3a2a1a0 構(gòu)成,而每發(fā)送一個碼組,只發(fā)送 4 比特的原始信息。從上表中,也不能直觀的說明該編碼方式可以糾錯 1 位碼元,而不能糾錯 2位,而圖 3 正好可以解釋。

圖3

圖中 3 個大圓圈對應(yīng) 3 個監(jiān)督位的三個規(guī)則,可以這么理解,如下:

1. 如果接收到的信息只不符合規(guī)則“S1”,則對應(yīng)圖中 a2;

2. 如果接收到的信息只不符合規(guī)則“S2”,則對應(yīng)圖中 a1;

3. 如果接收到的信息只不符合規(guī)則“S3”,則對應(yīng)圖中 a0;

4. 如果接收到的信息不符合規(guī)則“S1”和“S2”,則對應(yīng)圖中 a2a1;

5. 如果接收到的信息不符合規(guī)則“S1”和“S3”,則對應(yīng)圖中 a2a0;

6. 如果接收到的信息不符合規(guī)則“S2”和“S3”,則對應(yīng)圖中 a1a0;

7. 如果接收到的信息不符合規(guī)則“S1”、“S2”和“S3”,則對應(yīng)圖中的a2a1a0。

從圖 3 中,可以給出一個結(jié)論,只要錯誤碼只有 1 位,系統(tǒng)就可以糾正錯誤;而如果錯誤碼達到 2 位,就無法糾正錯誤。

根據(jù)以上兩表對應(yīng)關(guān)系可以推出以下錯誤規(guī)則和誤碼位置關(guān)系的結(jié)論,列出如表 3 所示。

表 3

(注明: 對應(yīng)的 1 表示錯誤,例如 S1 S2 S3 等于 001,表示接收到的數(shù)據(jù)違反規(guī)則 S3)

從以上對漢明碼的原理,到設(shè)計使用(7,4)碼的設(shè)計,設(shè)計中的 3 個監(jiān)督位都可以使用異或操作完成。具體內(nèi)容將在后面介紹。

四、系統(tǒng)結(jié)構(gòu)

對于該系統(tǒng),我們最注重的是原始碼元漢明碼編碼、擴頻、信道編碼、頻解碼和糾錯碼系統(tǒng)。當(dāng)然,由于設(shè)計仿真需要模擬一些關(guān)于加性干擾,不得不在模擬發(fā)送過程中添加干擾源。加上測試平臺的模塊構(gòu)成了整個系統(tǒng)的通信方式,便于讀者理解。整個系統(tǒng)的拓撲結(jié)構(gòu)圖如圖 4 所示。

圖4

圖中包括整個設(shè)計的構(gòu)架,也是數(shù)字信號傳輸的基本模型。包括信源、漢明碼編碼、m 序列發(fā)生器、解擴器、m 序列同步器、漢明碼解碼器和信宿等。Testbench 平臺會把信源和信宿進行比對,輸出傳輸?shù)慕Y(jié)果,并且打印到屏幕上供查看。其中,發(fā)送端和接收端才可綜合,其它模塊均用于測試,不可綜合。噪聲發(fā)送器為模擬信道傳輸過程中的干擾。加法器表示加性干擾。各個模塊的代碼對應(yīng)如表 4 所示。

表 4 模塊與代碼文件對應(yīng)關(guān)系

還有一些相關(guān)的文件,將在中篇詳細說明。

作為一個底層模塊設(shè)計人員,以數(shù)據(jù)流作為主線是必須的。關(guān)于本人這個結(jié)論,并沒有真正的得到老一輩工程師的驗證。但在本篇中,就以數(shù)據(jù)流的方式作為設(shè)計主線。

系統(tǒng)的?verilog?實現(xiàn)

一、數(shù)據(jù)傳輸過程

從上一章中的拓撲結(jié)構(gòu)圖中可知數(shù)據(jù)流的過程,如圖 5 所示。

 

圖 5 mcu?

輸出給 coder 模塊原始信息,每 4 比特作為一組,所以,在 mcu 中,每一個字節(jié)拆分成 2 個 4 比特發(fā)送到 coder 模塊中。

在coder中對原始信號進行擴頻、信道編碼、最終輸出2比特的數(shù)據(jù)01和11(即 -1 和+1)給 add_noise,經(jīng)過 add_noise 加性干擾噪聲后,輸出 3 比特的數(shù)據(jù)給decoder 模塊,decoder 模塊經(jīng)過解擴后輸出給 correct 模塊糾錯,最終發(fā)送給 slaver模塊。

最終 top 模塊根據(jù)發(fā)送的原始數(shù)據(jù)和接收后的數(shù)據(jù)進行比對,輸出結(jié)果(打印到屏幕上)。這里只是大概的介紹了設(shè)計中數(shù)據(jù)流的過程。在以下各個模塊設(shè)計中還會具體提到。

二、MCU模塊

模塊 mcu 負責(zé)通信的信源部分,除了給 coder 發(fā)送數(shù)據(jù),也為 coder 模塊和add_noise 模塊提供時鐘、復(fù)位信號。該模塊對整個仿真有著相當(dāng)重要。因為它的設(shè)計關(guān)系到系統(tǒng)仿真的完整性。

mcu 模塊包含隨機數(shù)據(jù)的產(chǎn)生、存儲、發(fā)送。隨機數(shù)的產(chǎn)生采用系統(tǒng)函數(shù) random產(chǎn)生。而數(shù)據(jù)存儲有兩個位置,一個是輸出存儲到文件中,另一個是存儲到 memory中。存儲到文件中是為了提供仿真后數(shù)據(jù)的查看,而存放 memory 中為了數(shù)據(jù)的發(fā)送和之后數(shù)據(jù)的比對。

該模塊與下游模塊 coder 有一定的時序邏輯,它控制 coder 模塊的開始,由 mcu發(fā)送 send_ena 到 coder,隨后等待 coder 模塊反饋信號 insourse_ena。Insourse_ena信號有效,則發(fā)送數(shù)據(jù),否則停止發(fā)送數(shù)據(jù)。數(shù)據(jù)發(fā)送結(jié)束只要撤銷 send_ena 信號的有效性即可。

具體代碼如下:

//***************************************************************///模塊名: mcu//作 者: The last one//用 途: 包含發(fā)送部分全部內(nèi)容//版本說明://***************************************************************/`timescale 1us/1usmodule mcu(noised_data //輸出帶有噪聲信號)
parameter TestNumber = 400;parameter Period = 100;
/**********************發(fā)送數(shù)據(jù)端口信號定義*********************/  wire [1:0] un_noised_data;  output [2:0] noised_data;  reg clk1,clk31,rst_n;  reg send_ena;    wire insourse_ena;  integer indataFILE; //指向一個文件,用于存儲  integer i,j,k;    reg [7:0] indata_mem[TestNumber:1];  reg [7:0] indatabyte;  wire in_data;  assign in_data=indatabyte[7];  // 初始值  initial     begin      i = 0;      j = 1;      k = 1;    end      initial    begin      rst_n = 0;      send_ena = 0;      @(posedge clk31)      #(Period * 150)      rst_n = 1;      #(Period * 33)      send_ena = 1;    end        initial       begin        clk1 = 0;        #(Period*3)        forever #(Period * 31) clk1 = ~clk1;      end          initial       begin        clk31 = 0;        #(Period*20)        forever #(Period) clk31 = ~clk31;      end          initial    /********************************************    打開或者創(chuàng)建一個文件(名為 indataRandom.dat)    生成測試用的隨機字節(jié)寫入該文件中    把第一個數(shù)據(jù)賦給 indatabyte    最后關(guān)閉該文件,釋放 indataFILE    ********************************************/      begin        indataFILE = $fopen("./indataRandom.dat");        $display (" indataFILE=%0d ", indataFILE);        for(k = 1; k <= TestNumber; k = k+1)     ??????begin      ??????indata_mem[k]={$random}%256;?      ??????$fdisplay(indataFILE,"?%0h?",indata_mem[k]);?    ??????end        indatabyte <= indata_mem[1];        $fclose(indataFILE );       end      always@(posedge clk1)  /************************************************  當(dāng) coder 使能信號(insourse_ena)到來,每 1 個 clk1 時鐘把一個數(shù)據(jù)傳出  傳出的數(shù)據(jù)與 indataRandom.dat 文件的數(shù)據(jù)一樣  ************************************************/    begin       if(insourse_ena)      if ( j<=TestNumber )        begin         if(i<7)              begin                 indatabyte={indatabyte[6:0],1'b0};                i=i+1;              end          else if (i==7)              begin                 indatabyte=indata_mem[j+1];                j=j+1;                i=0;    ??????????end        end       else      j = 1;      else      ;    end     coder coder(              .clk1(clk1),              .clk31(clk31),              .rst_n(rst_n),              .send_ena(send_ena),              .in_data(in_data),              .out_data(un_noised_data),              .insourse_ena(insourse_ena)              );                add_noise noise(                .clk31(clk31),                .rst_n(rst_n),                .un_noised_data(un_noised_data),                .noised_data(noised_data)                );                endmodule

三、coder?模塊

模塊 coder 為原始數(shù)據(jù)的接收、對數(shù)據(jù)的漢明碼編碼、擴頻和信道編碼等操作。

模塊的通信時序如下:

1. 接收到模塊 mcu 原始數(shù)據(jù)到來之前,先發(fā)送一個同步頭,起止由 mcu 控制;

2. 每發(fā)送 128 個字節(jié)原始數(shù)據(jù)前,發(fā)送數(shù)據(jù) 0000 作為數(shù)據(jù)幀同步,用于檢測發(fā)送和接收兩端數(shù)據(jù)發(fā)送是否同步;

3. 對原始數(shù)據(jù)進行漢明碼編碼,監(jiān)督位為 3 位,全部放到數(shù)據(jù)位后;

4. 對編好的信息進行擴頻,1 比特擴頻到 31 比特;

5. 對擴頻后的信號進行信道編碼,即 1 用 01(+1)、0 用 11(-1)。

擴頻通信,原始數(shù)據(jù)的頻率必然比擴頻后的頻率小得多,本設(shè)計的 m 序列碼是 31 比特位為一個周期。所以,原始信息的頻率假設(shè)為 f 1,則擴頻頻率 f2 = 31x f1。因此,該模塊有兩個時鐘。

該模塊采用輸入使能信號(send_ena)和輸出反饋信號(insourse_ena)作為與上游模塊(mcu)的握手信號。當(dāng) send_ena 有效,同時 insourse_ena 有效時,mcu 才會發(fā)送真實的數(shù)據(jù)到輸入端口。而當(dāng) send_ena 信號有效的一段時間內(nèi),先發(fā)送同步頭和數(shù)據(jù)幀同步后,才使 insourse_ena 有效,發(fā)送原始數(shù)據(jù)。

發(fā)送端固定對應(yīng)的 m 序列為{0000101011101100011111001101001}。則每一個數(shù)據(jù)的發(fā)送都是按該序列發(fā)送,在接收端更容易同步(解調(diào)時更詳細解釋)。因此 m 序列的寄存器需一個標(biāo)示位(flag),使數(shù)據(jù)和隨機碼同步送。關(guān)

于該模塊的工作過程,可以參照下篇的仿真。

該模塊的參考具體代碼如下:

//************************************************************//模塊名: coder//作 者: The last one//工 程://用 途: 漢明碼編碼、擴頻、信道編碼//版本說明://************************************************************module coder(              input wire clk1,              input wire clk31,              input wire rst_n,              input wire send_ena, //發(fā)送信號使能              input wire in_data,              output reg insourse_ena, // 獲取數(shù)據(jù),用于與 mcu 握手              output wire [1:0] out_data // 輸入數(shù)據(jù)。              );                  parameter idle = 4'b0001,    body = 4'b0010;        reg in_data_buf;    reg out_data_flag;    reg check1,check2, check3; // 3 位監(jiān)督位    reg [4:0] m_coder; //m 系列碼組,最低位輸出作為 m 序列    reg flag;    reg [3:0] state1;    reg [7:0] data_number;    reg [3:0] state;    reg [3:0] state_m;    /***************************************************作為輸出使能模塊,并進行信道編碼***************************************************/    assign out_data = (send_ena && out_data_flag)?    (((in_data_buf ^ m_coder[0]) == 1'b1)? 2'b01 : 2'b11) : 2'b10;    // 該部分的 ll 信號只用于調(diào)制代碼時使用    reg ll;    always @(posedge clk1)      if(!rst_n)        ll <= 0;      else        ll <= in_data_buf;    /***********************************************狀態(tài)機,發(fā)送頭同步->數(shù)據(jù)幀同步->數(shù)據(jù) 每發(fā)送 128 個數(shù)據(jù)又跳轉(zhuǎn)到發(fā)送數(shù)據(jù)幀同步 Start***********************************************/    always @(posedge clk1)       begin        if(!rst_n)          sys_reset;        else if(send_ena)                case(state) // synthesis full_case                    4'h0 : head; //產(chǎn)生頭同步信號 11111111110                    4'h1 : data_frames; //數(shù)據(jù)幀同步信號 0000+000                    4'h2 : ready_data; //數(shù)據(jù)發(fā)送                endcase            else          sys_reset; //復(fù)位      end      /*************** 復(fù)位 Start****************/    task sys_reset;      begin          in_data_buf <= 1'b0;          insourse_ena <= 1'b0;          data_number <= 8'd0;          out_data_flag <= 1'b0;          flag <= 1'b0;          state <= 4'h0;          state1 <= 4'h0;          check1 <= 1'b0;          check2 <= 1'b0;          check3 <= 1'b0;      end    endtask    /***************************************** 發(fā)送數(shù)據(jù)幀同步信號 0000+000 Start*****************************************/    task head;        begin            case(state1) // synthesis full_case                0,1,2,3,4,5,6,7,8,9:              begin                  out_data_flag <= 1'b1;                  flag <= 1'b1;                  in_data_buf <= 1'b1;                  state1 <= state1 + 1'b1;              end                  10 : begin                          in_data_buf <= 1'b0;                          state <= 4'h1;                          state1 <= 4'h0;                        end            endcase        end    endtask    /***************************************** 發(fā)送數(shù)據(jù)幀同步信號 0000+000 Start*****************************************/    task data_frames;        begin            case(state1) // synthesis full_case                0,1,2,3,4,5:begin                                in_data_buf <= 1'b0;                                state1 <= state1 + 1'b1;                            end                                            6 : begin                        in_data_buf <= 1'b0;                        state <= 4'h2;                        state1 <= 4'h0;                        data_number <= 8'd0;                        insourse_ena <= 1'b1;                    end            endcase        end    endtask    /******************************************************** 發(fā)送真實數(shù)據(jù)模塊,每發(fā)送 4 位信息位和 3 位監(jiān)督位 Start********************************************************/    task ready_data;        begin            case(state1) // synthesis full_case            0 :begin                  insourse_ena <= 1'b1;                  in_data_buf <= in_data;                  check1 <= in_data;                  check2 <= in_data;                  check3 <= in_data;                  state1 <= state1 + 1'b1;???????????????end???????????????            1 :begin                    insourse_ena <= 1'b1;                    in_data_buf <= in_data;                    check1 <= check1 ^ in_data;                    check2 <= check2 ^ in_data;                    state1 <= state1 + 1'b1;???????????????end                        2 :begin                    insourse_ena <= 1'b1;                    in_data_buf <= in_data;                    check1 <= check1 ^ in_data;                    check3 <= check3 ^ in_data;                    state1 <= state1 + 1'b1;               end                        3 :begin                  in_data_buf <= in_data;                  check2 <= check2 ^ in_data;                  check3 <= check3 ^ in_data;                  state1 <= state1 + 1'b1;                  insourse_ena <= 1'b0; //暫停主機送來數(shù)據(jù),接下來發(fā)送監(jiān)督位???????????????end                        4 :begin                  in_data_buf <= check1;                  state1 <= state1 + 1'b1;????????????????end                        5 :begin            in_data_buf <= check2;            state1 <= state1 + 1'b1;            end                        6 :begin            in_data_buf <= check3;            state1 <= 4'h0;????????????if(data_number?==?8'd127)??????              begin              insourse_ena <= 1'b0;              data_number <= 8'd0;              state <= 4'h1;              end              else              begin              insourse_ena <= 1'b1;              data_number <= data_number + 1'b1;              end              end              endcase              end              endtask    /********************************************* m 系列產(chǎn)生 由主機發(fā)出使能信號 Start*********************************************/    always @(posedge clk31)         begin            if(!rst_n)                begin                state_m <= idle;                m_coder <= 5'b01000;                end            else                case(state_m) // synthesis full_case                    idle : if(flag)                              state_m <= body;???????????????????????????else                              state_m <= idle;                              body: begin                                        m_coder[4] <= m_coder[0] ^ m_coder[3];                                        m_coder[3:0] <= m_coder[4:1];????????????????????????????????????end                endcase        endendmodule

四、add_noise?模塊

該模塊代碼的作用是產(chǎn)生干擾,這里所說的干擾都為加性干擾,只要把無干擾數(shù)據(jù) 01(+1)和 11(-1)分別加上范圍在[-2,+2]的隨機數(shù)。

加干擾后,+1 將會變成 01±[-2,+2] = [-1,+3],-1 將會變成 11±[-2,+2] = [-3,+1]。并且兩個范圍都是均勻分布。

由于輸入數(shù)據(jù)為 2 個比特,必須擴展后加減法才是我們需要的。具體代碼如下:

/************************************************************///模塊名: mcu//作 者: The last one//用 途: 添加加性干擾//版本說明://************************************************************/module add_noise(                  clk31,                  rst_n,                  un_noised_data, //干擾數(shù)據(jù)輸入                  noised_data //添加干擾后輸出??????????????);??????????????    input clk31,    rst_n;    input [1:0] un_noised_data;    output [2:0] noised_data;    reg [2:0] noise;    /*****************************************************+1 = [-1,3]-1 = [-3,1]都是等概率的出現(xiàn)*****************************************************/    assign noised_data = {un_noised_data[1],un_noised_data} + noise;        always @(posedge clk31)      if(!rst_n)        noise <= 3'd0;      else        noise <= $random % 3; // noise = [-2,+2]    endmodule

模塊 mcu、模塊 coder、模塊 add_noise,使用 mcu 作為頂層模塊,激勵由 mcu產(chǎn)生、發(fā)送輸出為加加性噪聲后的信號。

五、decoder?模塊

decoder 是解擴模塊,包括查找同步頭、數(shù)據(jù)同步、解擴。

同步頭{1111_1111_110},數(shù)據(jù)幀同步{0000_000},必須接收到同步頭,且同步同步頭后和接收數(shù)據(jù)幀同步,之后才對數(shù)據(jù)解擴。

由于發(fā)送模塊和接收模塊有時間差,但可以確認的是,必須先接收,后再發(fā)送。發(fā)送端采用的是固定的 m 序列碼作為擴頻偽隨機碼,這樣做的利處就是接收端只要采用一樣的 m 序列作為解擴碼。

由于偽隨機序列具有很強的相關(guān)性。只要有 1 個時鐘錯誤,解擴結(jié)果相差會相當(dāng)?shù)拇蟆R揽克倪@個特性,可以把發(fā)送數(shù)據(jù)一一解擴。(具體解擴過程在仿真部分將更詳細說明)。

由于在模塊 add_noise 中添加了干擾,發(fā)送數(shù)據(jù)會有一定的誤差,所以,解擴過程需使用累加的方法進行。而累加的閥值這里固定在 28,由于累加過程會有減法運算,所以計算初值均為 100。

具體代碼如下:

//*******************************************************///模塊名: mcu//作 者: The last one//用 途: 解擴//版本說明://******************************************************/module decoder(                rst_n,                ena,                clk31x,                in_data,                out_data,                decode_data_flag??????????????);??????????????    input rst_n;    input ena;    input clk31x;    input [2:0] in_data;    output out_data;    output decode_data_flag;        reg out_data;    reg decode_data_flag;    reg [39:0] m_coder_buf;    reg [7:0] mm;    reg temp;    reg [7:0] temp_syn;    reg [3:0] state;    wire [2:0] psumi;    //已知的解調(diào)系列    wire [30:0] m =31'b1001011001111100011011101010000;    /************************************取絕對值正數(shù)是本身,負數(shù)則取反加 1************************************/    assign psumi =(in_data[2]==0)?{in_data[1],in_data[0]}:(~in_data+1);        parameter find_head = 4'b0001,               synchronize = 4'b0010,              //找到頭信號之后的同步解碼過程以找到 0 時為結(jié)束              find_head_end = 4'b0100,               //用于解調(diào)除 11111111110 以外的所有傳輸數(shù)據(jù)              main_body = 4'b1000;                   reg [7:0] sum1,              sum2,              sum3,              sum4,              sum5,              sum6,              sum7,              sum8,              sum9,              sum10,??????????????sum;???    reg [7:0] i,j;    /******************************************************產(chǎn)生一個循環(huán)的隨機碼,用于解擴******************************************************/    always @(posedge clk31x)         begin            if(!rst_n || (!ena))                m_coder_buf <= {m[8:0],m};            else                m_coder_buf<={m_coder_buf[9:1],m_coder_buf[0],m_coder_buf[30:1            ]};        end            always @(posedge clk31x)        if(!rst_n || (!ena))            begin                state <= find_head;                i <= 8'd0;                j <= 8'd0;                sum1 <= 8'd100;                sum2 <= 8'd100;                sum3 <= 8'd100;                sum4 <= 8'd100;                sum5 <= 8'd100;                sum6 <= 8'd100;                sum7 <= 8'd100;                sum8 <= 8'd100;                sum9 <= 8'd100;                sum10 <= 8'd100;                sum <= 8'd100;                mm <= 8'd0;                decode_data_flag <= 1'b0;                temp <= 1'bz;                out_data <= 1'bz;                temp_syn <= 8'b0000_0000;            end        else            case(state)????????????find_head:????/********************************************************尋找同步頭。*********************************************************/              begin                if(j != 8'd30)                  begin                  j <= j + 1'b1;                  if(in_data[2] == m_coder_buf[i])                      sum1 <= sum1 + psumi;                  else                      sum1 <= sum1 - psumi;                  if(in_data[2] == m_coder_buf[i+1])                      sum2 <= sum2 + psumi;                  else                      sum2 <= sum2 - psumi;                  if(in_data[2] == m_coder_buf[i+2])                      sum3 <= sum3 + psumi;                  else                      sum3 <= sum3 - psumi;                  if(in_data[2] == m_coder_buf[i+3])                      sum4 <= sum4 + psumi;                  else                      sum4 <= sum4 - psumi;                  if(in_data[2] == m_coder_buf[i+4])                      sum5 <= sum5 + psumi;                  else                      sum5 <= sum5 - psumi;                  if(in_data[2] == m_coder_buf[i+5])                      sum6 <= sum6 + psumi;                  else                      sum6 <= sum6 - psumi;                  if(in_data[2] == m_coder_buf[i+6])                      sum7 <= sum7 + psumi;                  else                      sum7 <= sum7 - psumi;                  if(in_data[2] == m_coder_buf[i+7])                      sum8 <= sum8 + psumi;                  else                      sum8 <= sum8 - psumi;                  if(in_data[2] == m_coder_buf[i+8])                      sum9 <= sum9 + psumi;                  else                      sum9 <= sum9 - psumi;                  if(in_data[2] == m_coder_buf[i+9])                      sum10 <= sum10 + psumi;                  else                      sum10 <= sum10 - psumi;                  if(sum1 >= 8'd128 || sum2 >= 8'd128 || sum3 >= 8'd128 ||                       sum4 >= 8'd128 || sum5 >= 8'd128 ||                       sum6 >= 8'd128 || sum7 >= 8'd128 || sum8 >= 8'd128 ||                       sum9 >= 8'd128 || sum10 >= 8'd128)                                      begin                      if(sum1 >= 8'd128) mm <= i;                      if(sum2 >= 8'd128) mm <= i+1;                      if(sum3 >= 8'd128) mm <= i+2;                      if(sum4 >= 8'd128) mm <= i+3;                      if(sum5 >= 8'd128) mm <= i+4;                      if(sum6 >= 8'd128) mm <= i+5;                      if(sum7 >= 8'd128) mm <= i+6;                      if(sum8 >= 8'd128) mm <= i+7;                      if(sum9 >= 8'd128) mm <= i+8;                      if(sum10 >= 8'd128) mm <= i+9;                      state <= synchronize;                    end                  end                else                  begin                    if(i < 30)                        i <= i + 8'd10;                    else                        i <= 8'd0;                        j <= 8'd0;                    if(in_data[2] == m_coder_buf[i])                        sum1 <= 8'd100 + psumi;                    else                        sum1 <= 8'd100 - psumi;                    if(in_data[2] == m_coder_buf[i+1])                        sum2 <= 8'd100 + psumi;                    else                        sum2 <= 8'd100 - psumi;                    if(in_data[2] == m_coder_buf[i+2])                        sum3 <= 8'd100 + psumi;                    else                        sum3 <= 8'd100 - psumi;                    if(in_data[2] == m_coder_buf[i+3])                        sum4 <= 8'd100 + psumi;                    else                        sum4 <= 8'd100 - psumi;                    if(in_data[2] == m_coder_buf[i+4])                        sum5 <= 8'd100 + psumi;                    else                        sum5 <= 8'd100 - psumi;                    if(in_data[2] == m_coder_buf[i+5])                        sum6 <= 8'd100 + psumi;                    else                        sum6 <= 8'd100 - psumi;                    if(in_data[2] == m_coder_buf[i+6])                        sum7 <= 8'd100 + psumi;                    else                        sum7 <= 8'd100 - psumi;                    if(in_data[2] == m_coder_buf[i+7])                        sum8 <= 8'd100 + psumi;                    else                        sum8 <= 8'd100 - psumi;                    if(in_data[2] == m_coder_buf[i+8])                        sum9 <= 8'd100 + psumi;                    else                        sum9 <= 8'd100 - psumi;                    if(in_data[2] == m_coder_buf[i+9])                        sum10 <= 8'd100 + psumi;                    else                        sum10 <= 8'd100 - psumi;                  end              end                    synchronize :/********************************************************* 同步同步頭************************************************/                begin                    if(mm < 8'd22)                       temp_syn<={m_coder_buf[mm+7],temp_syn[7:1]};                    else                       temp_syn<= {m_coder_buf[mm-22],temp_syn[7:1]};                    if(temp_syn == m[7:0])                         begin                              state <= find_head_end;                              j <= 8'd0;                            if(in_data[2] == m_coder_buf[mm])                              sum <= 8'd100 + psumi;                            else                              sum <= 8'd100 - psumi;                        end                end                            find_head_end :/************************************************找數(shù)據(jù)幀同步************************************************/                begin                    if(j != 8'd30)                     begin                        if(in_data[2] == m_coder_buf[mm])                            sum <= sum + psumi;                        else                            sum <= sum - psumi;                            j <= j + 1'b1;            ???????end                        else                            begin            ????????????????j?<=?8'd0;                            if(in_data[2] == m_coder_buf[mm])                                sum <= 8'd100 + psumi;                            else                                sum <= 8'd100 - psumi;                            if(sum >= 8'd100)                                begin                                temp <= 1'b1;                                end                            else                                begin                                    temp <= 1'b0;                                    decode_data_flag <= 1'b1;                                    state <= main_body;                                end                            end                end                    main_body :/************************************************** 解調(diào)數(shù)據(jù)****************************************************/                begin                 if(j != 8'd30)                     begin                        if(in_data[2] == m_coder_buf[mm])                        sum <= sum + psumi;                    else                        sum <= sum - psumi;                        j <= j + 1'b1;                    end                else                    begin                        j <= 8'd0;                        if(in_data[2] == m_coder_buf[mm])                            sum <= 8'd100 + psumi;                        else                            sum <= 8'd100 - psumi;                        if(sum >= 8'd100)                            out_data <= 1'b1;                        else                            out_data <= 1'b0;                    end                end            endcase    endmodule

該模塊只是對應(yīng)的解擴,并未涉及信息的檢錯和糾錯,檢錯將在 correct 模塊中進行。

六、correct?模塊

模塊 correct 將對解擴后的信息就行檢錯和糾錯。檢錯過程就相當(dāng)于漢明碼編碼的逆過程。但(7,4)漢明碼僅在 1 位錯誤的情況下可以檢出錯誤,如果多于 1 位錯誤,將無法糾錯過來(依據(jù) d>e+1;碼距 d=3)。

具體代碼如下:

//***********************************************************///模塊名: mcu//作 者: The last one//用 途: 檢錯、糾錯//版本說明://***********************************************************/module correct (                  clk1,                  rst_n,                  in_data, //輸入解調(diào)后的數(shù)據(jù)                  out_data, //輸出糾錯后的結(jié)果                  decode_data_flag,//來自 decode 模塊的信號,用以標(biāo)                  識信號已解調(diào)完畢(不包括同步判斷信號 11111111110)                  correct_data_flag,//輸出信號,表明已經(jīng)完成查錯和                  糾錯功能                   asyn_flag //輸出信號,高電平表示不同步              );                  parameter IDLE = 4'b0001,    PROCESS = 4'b0010,    FLAG_OUT = 4'b0011;    input clk1,rst_n;    input in_data;    input decode_data_flag;    output [3:0] out_data;    output correct_data_flag;    output asyn_flag;    reg [3:0] out_data;    reg correct_data_flag;    reg asyn_flag;    reg [6:0] in_data_buf,//輸入數(shù)據(jù)移位寄存器    data_buf; //輸入數(shù)據(jù)緩沖寄存器    reg flag; //讀滿標(biāo)示位    reg s1,s2,s3; //糾錯碼運算結(jié)果    reg [11:0] data_number;    reg [3:0] state1,state2;        always @(posedge clk1) //接收外來的數(shù)據(jù)        if(!rst_n || !decode_data_flag)          begin              state1 <= 4'h0;              flag <= 1'b0;          end        else          case(state1)/**********************************************接收解調(diào)出來的 7 位數(shù)據(jù)接收完 7 個數(shù)據(jù)后,給 flag 信號,進行數(shù)據(jù)處理. **********************************************/          0 : state1 <= 1;            1,2,3,4,5,6 :            begin                flag <= 1'b0;                in_data_buf <= {in_data_buf[5:0],in_data};                state1 <= state1 + 1'b1;            end                        7 : begin                  in_data_buf <= {in_data_buf[5:0],in_data};                  flag <= 1'b1;                  state1 <= 4'h1;                end                          endcase                    always @(posedge clk1) //把接收到的數(shù)據(jù)進行處理后,送出端口            if(!rst_n || !decode_data_flag)              begin                  s1 <= 1'b0;                  s2 <= 1'b0;                  s3 <= 1'b0;                  data_buf <= 7'hxx;                  state2 <= IDLE;                  data_number <= 12'd0;                  correct_data_flag <= 1'b0;                  asyn_flag <= 1'b0;              end            else              case(state2)                IDLE : begin // 等待 flag 到來                        correct_data_flag <= 1'b0;                        if(flag)                          begin                            state2 <= PROCESS;                            preprocessing; //預(yù)加工數(shù)據(jù)                          end                        else                           state2 <= IDLE;                      end                                    PROCESS: begin //糾錯處理                              correct_task;                              state2 <= FLAG_OUT;                           end                                             FLAG_OUT: begin                              state2 <= IDLE;                              if(data_number != 12'd1)                                correct_data_flag <= 1'b1;                            end                                              default : state2 <= 4'h0;              endcase                        task preprocessing;          begin/*******************************************************計算錯碼情況,但如果有兩個碼組錯誤將無法判定將 in_data_buf 賦給 data_buf 保存起來數(shù)據(jù)計滿 903 位(512 信息位,384 監(jiān)督位,4 位數(shù)據(jù)幀,3 位數(shù)據(jù)幀監(jiān)督位)data_number 賦 0 開始計數(shù)(0 -> 902)*******************************************************/              s1<=(in_data_buf[6]^in_data_buf[5]^in_data_buf[4]^in_data_buf[2]);              s2<=(in_data_buf[6]^in_data_buf[5]^in_data_buf[3]^in_data_buf[1]);              s3<=(in_data_buf[6]^in_data_buf[4]^in_data_buf[3]^in_data_buf[0]);              data_buf <= in_data_buf;          if(data_number < 902)            data_number <=data_number + 1'b1;          else            data_number <= 12'd0;?????????end?????????endtask?????????          task correct_task;            begin            case({s3,s2,s1})/***********************************************************數(shù)據(jù)位 監(jiān)督位-------------------------------- -------------------d6 d5 d4 d3 s1 s2 s3x x x s1x x x s2x x x s3 -----------------------------------------------------------如果有一位錯,必定是監(jiān)督位錯。如果是第一位數(shù)據(jù),判斷是否為數(shù)據(jù)幀(0000)若不是數(shù)據(jù)幀,則認為系統(tǒng)沒有同步數(shù)據(jù)幀,發(fā)送 syn_flag 高電平,以下類似.s1,s2,s3 如果錯誤有兩個以上,可以找到他們的相交區(qū)間s1,s2 錯誤,s3 正確 可以找到 不屬于 s3,而同時屬于 s1,s2 的數(shù)據(jù)為 d5s1,s3 錯誤,s2 正確 可以找到 不屬于 s2,而同時屬于 s1,s3 的數(shù)據(jù)為 d4s2,s3 錯誤,s1 正確 可以找到 不屬于 s1,而同時屬于 s2,s3 的數(shù)據(jù)為 d3s1,s2,s3 錯誤, 而同時屬于 s1,s2,s3 的數(shù)據(jù)為 d6*************************************************************/            3'b000,3'b001,3'b010,3'b100 :              begin                if(data_number == 12'd1)                if(data_buf[6:3] == 4'h0)                  asyn_flag <= 1'b0;                else                  asyn_flag <= 1'b1;                else if(data_number <= 902)                  out_data <= data_buf[6:3];                else                ;              end                          3'b011 :begin                    if(data_number == 12'd1)                    if(data_buf[6:3] == 4'b0100)                      asyn_flag <= 1'b0;                    else                      asyn_flag <= 1'b1;                    else if(data_number <= 902)                       out_data<={data_buf[6],~data_buf[5],data_buf[4:3]};                    else                    ;                    end                                  3'b110 :begin                      if(data_number == 12'd1)                      if(data_buf[6:3] == 4'b0001)                        asyn_flag <= 1'b0;                      else                        asyn_flag <= 1'b1;                      else if(data_number <= 902)                        out_data <= {data_buf[6:4],~data_buf[3]};                      else                      ;                      end                        3'b101 :begin                    if(data_number == 12'd1)                    if(data_buf[6:3] == 4'b0010)                      asyn_flag <= 1'b0;                    else                      asyn_flag <= 1'b1;                    else if(data_number <= 902)                      out_data<={data_buf[6:5],~data_buf[4],data_buf[3]};                    else                    ;                    end                                3'b111 :begin                    if(data_number == 12'd1)                    if(data_buf[6:3] == 4'b1000)                      asyn_flag <= 1'b0;                    else                      asyn_flag <= 1'b1;                    else if(data_number <= 902)                      out_data <= {~data_buf[6],data_buf[5:3]};                    else                    ;                    end            default : ;???????????endcase      end            endtask            endmodule

七、Correct_Decoder?模塊

模塊 Correct_Decoder 是模塊 decoder 和模塊 correct 的頂層模塊。

代碼如下:

//*******************************************************///模塊名: Correct_Decoder//作 者: The last one//用 途: 解擴和糾錯模塊的頂層模塊//版本說明://*******************************************************/module Correct_Decoder(                        rst_n,                        clk1,                        clk31x,                        ena_decoder,                        noised_data,                        pro_correct_data,                        correct_data_flag,                        asyn_flag                  );                      input rst_n,          clk1,          clk31x;              input ena_decoder; //使能 decoder 信號    input [2:0] noised_data;//從 add_noise 輸出的噪聲信號,等待解調(diào)    output [3:0] pro_correct_data; //處理后輸出的數(shù)據(jù)    output correct_data_flag; //錯誤碼標(biāo)示位    output asyn_flag; //系統(tǒng)數(shù)據(jù)幀同步信號    wire pro_decode_data;    wire decode_data_flag;        decoder decoder(                    .rst_n(rst_n),                    .ena(ena_decoder),                    .clk31x(clk31x),                    .in_data(noised_data),                    .out_data(pro_decode_data),                    .decode_data_flag(decode_data_flag)??????????????????);??????????????????    correct correct(                      .clk1(clk1),                      .rst_n(rst_n),                      .in_data(pro_decode_data),                      .out_data(pro_correct_data),                       .decode_data_flag(decode_data_flag),                       .correct_data_flag(correct_data_flag),                       .asyn_flag(asyn_flag)????????????????????);????????????????????Endmodule

八、slaver?模塊

模塊 slaver 充當(dāng)信宿。它接收來自于模塊 correct 糾錯后的數(shù)據(jù),對數(shù)據(jù)進行保存。以便查看結(jié)果。

模塊 slaver 作為接收端,它將給解擴和糾錯模塊提供時鐘信號,但其的起始必須必發(fā)送的起始快。并且它所產(chǎn)生的時鐘可以是隨機的開始,以 mcu 模塊產(chǎn)生的時鐘沒有相位上的關(guān)系。

其代碼如下:

//**********************************************************///模塊名: slaver//作 者: The last one//用 途: 包含發(fā)送部分全部內(nèi)容//版本說明://************************************************************/`timescale 1us/1usmodule slaver(              input [2:0] noised_data //接收帶有噪聲干擾信號?????????????);?????????????  parameter TestNumber = 400;  parameter Period = 100;  reg rst_nx,clk1x,clk31x,ena_decoder;  wire [3:0] pro_correct_data;  wire correct_data_flag;  wire asyn_flag;  integer i,j,h,k,l,zz;  reg flag;  reg [7:0] decoderout_mem[TestNumber:1]; //用于存儲發(fā)送的數(shù)據(jù)  reg [7:0] decoderout_buf;  integer outdataFILE;    initial     begin      i = 1;      j = 0;      h = 1;      k = 0;      l = 0;      zz = 0;      flag = 0;      ena_decoder <= 1'b0;      #(Period*3)      ena_decoder <= 1'b1;    end    initial   /***********************************  產(chǎn)生 clk1x 信號,延遲是隨機的.  ***********************************/    begin      clk1x = 0;      rst_nx = 0;      #(Period*({$random}%10)) //產(chǎn)生一個隨機的延遲開始      rst_nx = 1;      forever #(Period * 31) clk1x = ~clk1x;    end      initial     /***********************************    產(chǎn)生 clk31x 信號,延遲是隨機的.    ***********************************/      begin        clk31x = 0;        forever #(Period) clk31x = ~clk31x;      end          always @(posedge correct_data_flag)      begin        if(k == 902)            begin              k = 1;              h = 1;              j = 0;            end        else            k = k + 1;            if(zz == 0)                begin                  decoderout_buf[7:4] = pro_correct_data;                  if((h+j)%65 != 0 || flag == 1)                    begin                      zz = 1;                      flag = 0;                    end                  else if(flag == 0)                    begin                      zz = 0;                      flag = 1;                      j = j + 1;                    end                  end                  else                    begin                      decoderout_buf[3:0] = pro_correct_data;                      decoderout_mem[i] = decoderout_buf;                      i = i + 1;                      h = h + 1;                      zz = 0;              end    end    initial    begin      wait(i == TestNumber+1)       outdataFILE = $fopen("./decoderOut.dat");      $display (" outdataFILE=%0d ", outdataFILE);        for(l = 1; l <= TestNumber; l = l+1)          begin             $fdisplay(outdataFILE," %0h ",decoderout_mem[l]);          end      $fclose(outdataFILE );     end          always @(posedge clk1x)     if(asyn_flag)      begin        $display("Error The system doesn't synchronize any more");         $stop;      end    Correct_Decoder Correct_Decoder(                                  .rst_n(rst_nx),                                  .clk1(clk1x),                                  .clk31x(clk31x),                                  .ena_decoder(ena_decoder),                                  .noised_data(noised_data),                                  .pro_correct_data(pro_correct_data),                                  .correct_data_flag(correct_data_flag),                                  .asyn_flag(asyn_flag)???????????????????????????????);  enndmodule

九、Top?模塊

模塊 top 作為仿真平臺的頂層模塊,它包含 mcu 和 slaver 兩個模塊。并且對發(fā)送數(shù)據(jù)和接收數(shù)據(jù)進行對比。統(tǒng)計結(jié)果,并輸出(打印到屏幕)。

模塊 top 將給兩個模塊提供周期,仿真?zhèn)€數(shù)等參數(shù),以傳參的形式傳送。

它的代碼如下:

//**********************************************************///模塊名: slaver//作 者: The last one//用 途: 包含發(fā)送部分全部內(nèi)容//版本說明://************************************************************/`define PERIOD 100`define testnumber 500 //測試數(shù)據(jù)個數(shù)`timescale 1us/1usmodule top;  integer m,n;  wire [2:0] noised_data;
// 模塊整體工作流程,都是以任務(wù)形式//******** START *****************  initial  begin    sys_reset;    delay_system_end;    compare_data;    stop;  end
//******* END *****************//--------------------------------------------------------------------------------------------------------  task sys_reset; //復(fù)位    begin      m = 0; //記錄錯誤個數(shù)      n = 1;    end  endtask  //--------------------------------------------------------------------------------------------------------//--------------------------------------------------------------------------------------------------------  task delay_system_end; // 等待系統(tǒng)仿真結(jié)束    begin      wait(slaver.i == `testnumber+1)      $display("The system transmission endn");      $display("nn***********************************************");      $display(" Start to compare the data");    end  endtask
//--------------------------------------------------------------------------------------------------------//--------------------------------------------------------------------------------------------------------  task compare_data; // 比較發(fā)送數(shù)據(jù)和接收數(shù)據(jù),并統(tǒng)計結(jié)果  begin    $display(" NO. Result org rep");    $display(" ------------------------------------");  for(n=1;n <= `testnumber; n = n + 1)  begin    if(mcu.indata_mem[n] == slaver.decoderout_mem[n])      $display("%d Right %0h=      %0h",n,mcu.indata_mem[n],slaver.decoderout_mem[n]);    else      begin        $display("%d Wrong %0h!=        %0h",n,mcu.indata_mem[n],slaver.decoderout_mem[n]);        m = m + 1;      end  end  $display(" ------------------------------------");    if(m != 0)      begin        $display(" Wrong data number is %5d",m);        $display(" Right data number is %5d",`testnumber-m);      end    else      $display(" No wrong data!");      $display(" ------------------------------------");  end  endtask
//--------------------------------------------------------------------------------------------------------//--------------------------------------------------------------------------------------------------------  task stop; // 仿真停止    begin      $display(" Sim time is over");      $display(" ------------------------------------n");      $stop;    end  endtask
//--------------------------------------------------------------------------------------------------------mcu mcu(        .noised_data(noised_data)        );        slaver slaver(        .noised_data(noised_data)???????);???????  defparam slaver.Period = `PERIOD;  defparam mcu.Period = `PERIOD;  defparam mcu.TestNumber = `testnumber;  defparam slaver.TestNumber = `testnumber;
endmodule

仿真

一、模塊的建立及其仿真環(huán)境的生成

1.1、在計算機上,找一個沒有中文字符的目錄,新建以下幾個文件,如圖 6:

圖6

上圖為可以建立的文件,sim_wave.do 是仿真波形保存文件.tt.do。其代碼如下:

#建立 library 名為”work”vlib workvmap work work#編譯當(dāng)前目錄(./)中的 top.v、mcu.v …. vlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./top.vvlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./mcu.vvlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./slaver.vvlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./coder.vvlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./add_noise.vvlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./decoder.vvlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./correct.vvlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./Correct_Decoder.v#仿真 work 中的 top 模型vsim -novopt work.top

以上是輸入方式進行仿真,也可以直接使用圖形化的方式進行仿真。但沒有開始仿真,因為我們以下還要添加一條語句。但沒有響應(yīng)的文件。tt.bat 的代碼如下:

echopausevsim -do .tt.dopause

tt.bat 文件為批處理文件,僅為打開 modelsim、運行 tt.do 文件使用。也可以不使用該文件(以下不會詳細介紹)。

1.2、將對應(yīng)的代碼寫到相應(yīng)的文件中(sim_wave.do、tt.bat 文件可以不管)。

1.3、用 modelsim 的打開方式打開 top.v 文件(或者你先打開 modelsim,然后把目錄修改成以上所述的目錄也可)。運行的界面如圖 7(modelsim6.5d):

圖7

圖中的亂碼均為modelsim不兼容我所使用的notepad軟件編寫的中文字符,大俠均可不以理睬。

1.4、在 Transcript 中輸入”do tt.do”,運行當(dāng)前目錄下的 tt.do 文件。運行過程中,最后跳出如圖 8 的窗口。如果有錯誤,會在 Transcript 中用紅色字體說明(當(dāng)然,這里都是英文)。

圖8

在框圖 1 中為整個仿真平臺上的模型,可以點擊模型+展開。框圖 2 顯示當(dāng)前模型所含的項目。

1.5、添加波形,如圖 9、10、11,對模塊 coder 添加波形,并對波形進行分組。

圖9

圖10

圖11

對所有仿真模型添加波形,并且分組,如圖 12。

圖12

圖13

1.6、仿真開始?在 Transcript 中輸入”run -all” 等待結(jié)果。以上將生成仿真環(huán)境的全過程。下面會將對各個模塊進行說明。

二、模塊仿真

2.1、模塊 mcu?仿真

mcu 扮演一個信源產(chǎn)生模塊,其波形如圖 14。

圖14

在 send_ena 使能的情況下,當(dāng) insourse_ena 為高時,數(shù)據(jù)從 indatabyte 第 7 位端口輸出到 coder 模塊,圖中發(fā)送十六進制 24 的過程,僅在 insourse_ena 為高時發(fā)送。該模塊還產(chǎn)生兩個時鐘,兩個時鐘分別是 31 倍的頻率。clk1 和 clk31。

2.2、模塊?coder?仿真

模塊 coder 將對 mcu 傳送的數(shù)據(jù)進行編碼、擴頻。仿真波形如圖 15。

圖15

圖中的 in_data_buf 為發(fā)送碼,當(dāng)接收到 send_ena 后,先發(fā)送頭和數(shù)據(jù)幀,然后才發(fā)送數(shù)據(jù)如圖中從 133600us 開始發(fā)送數(shù)據(jù)”0010”(十六進制 2)后發(fā)送監(jiān)督碼的”101”,在 177000us 開始發(fā)送數(shù)據(jù)”0100”(十六進制 4)后發(fā)送監(jiān)督碼”110”。所有數(shù)據(jù)經(jīng)過信道編碼后,out_data 發(fā)送出去。

2.3、模塊?noise?仿真

添加干擾,經(jīng) coder 發(fā)送的 2bit 數(shù)據(jù)擴展到 3bit 數(shù)據(jù),并與噪聲進行加性。

仿真波形如圖 16。

圖16

圖中是對 1bit 數(shù)據(jù)進行擴頻后,其中 un_noised_data 為輸入數(shù)據(jù)(無噪聲)、經(jīng)過與 noise 數(shù)據(jù)相加,得到數(shù)據(jù) noised_data。這模塊就是充當(dāng)信道中的加性干擾源。

2.4、模塊?decoder?仿真

解擴是本系統(tǒng)的設(shè)計重點。它包含同步頭的同步和數(shù)據(jù)的接收等。

本設(shè)計采用一個循環(huán)偽隨機作為解擴碼。采用一個 31bit 的寄存器,初始化為級數(shù)為 5 的 m 序列,首尾循環(huán)。那么,在寄存器每一位上采數(shù),都可以得到一個偽隨機序列。分別得出 31 個 m 序列。而且靠近的寄存器位,采集的 m 序列只有一位的移位。因此,可以采用該方法,在發(fā)送端發(fā)送的數(shù)據(jù),不管為何時發(fā)送,在 31bit個寄存器中的 1 個寄存器中與之對應(yīng)。更通俗的說法,不管發(fā)送設(shè)備何時開始發(fā)送。都可以在 31bit 的寄存器中找到一個寄存器采到的 m 序列與之對應(yīng)。

由于在 31 比特的寄存器同時采數(shù)是比較耗費 FPGA 內(nèi)部資源,所以本設(shè)計采用寄存器的每 10 個 bit 位進行一一處理。如果前 10 個沒能找到對應(yīng)的 m 序列,則累加到后 10 個,以此類推,在 3 次的累加中,總能完全掃描完 31bit 位的寄存器。此時可以找到對應(yīng)的比特位。

由于發(fā)送設(shè)備的數(shù)據(jù)頭為 10 個”1”和 1 個”0”,而在 10 個”1”中的 1 是延伸的,沒法直接得到相鄰”1”的交界,而在得到合適的 m 序列位后,必須進行同步,同步的方法為采集最后一個”0”作為同步。

在接收完成數(shù)據(jù)頭后,進行數(shù)據(jù)幀同步。數(shù)據(jù)幀是 4bit 數(shù)據(jù)”0000”和 3bit 監(jiān)督位”000”。

接收完成數(shù)據(jù)幀之后才是數(shù)據(jù)的開始。由于數(shù)據(jù)比較大,累加基數(shù)這里是 100,閥值為 30,那么,當(dāng)接收到 130,說明接收到一個”1”。

仿真結(jié)果如下:

圖17

圖17 為接收的整體工作狀態(tài),sum1~sum10 分別采集 10 個寄存器比特位,當(dāng)有1 個接收超過 130,說明寄存器該為上的 m 序列可以接收到 1 個”1”,sum 是對數(shù)據(jù)幀和數(shù)據(jù)的解擴統(tǒng)計。

圖18

圖18 是一個完整數(shù)據(jù)解擴的過程,clk31 是采集時鐘,數(shù)據(jù)為 in_data_buf,從輸入到輸出,延遲一段時間后傳送到解擴模塊。psumi 為解擴的值,通過累加得到sum(in_data[2]判斷。為 1,則加;為 0,則減)。如果 sum 超過 130,說明發(fā)送數(shù)據(jù)為”1”,否則為”0”。(以上為數(shù)據(jù)”1”的例子)

通過解擴的數(shù)據(jù),送到 correct 模塊進行糾錯。

2.5、模塊?correct?仿真

模塊 correct 為糾錯模塊。它將解擴后的數(shù)據(jù)進行分析,即對漢明碼的反運算。該模塊的仿真過程省略。

2.6、模塊?Slaver?仿真

Slaver 是接收模塊端,它將解擴、糾錯后的數(shù)據(jù)進行存儲。仿真過程省略。

2.7、模塊?Top??仿真

Top 模塊應(yīng)該放第一塊講解,因為它是一個仿真平臺,它的子模塊包括 mcu 和slaver。它將兩個模塊的發(fā)送接收進行統(tǒng)計、并且進行計算、輸出,并對模塊參數(shù)設(shè)置。以下設(shè)置發(fā)送數(shù)據(jù)比特位為 500 的輸出結(jié)果(圖 19、圖 20):

圖19

圖20

以上是整個設(shè)計的仿真過程。

到此結(jié)束,直接擴頻通信也到此結(jié)束,各位大俠,有緣再見!

相關(guān)推薦

電子產(chǎn)業(yè)圖譜

任何技術(shù)的學(xué)習(xí)就好比一個江湖,對于每一位俠客都需要不斷的歷練,從初入江湖的小白到歸隱山林的隱世高人,需要不斷的自我感悟自己修煉,讓我們一起仗劍闖FPGA乃至更大的江湖。