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

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

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

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

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

今天給大俠帶來(lái)直接擴(kuò)頻通信,由于篇幅較長(zhǎng),話不多說(shuō),上貨。

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

理論基礎(chǔ)

一、擴(kuò)頻通信

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

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

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

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

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

二、M序列

2.1 偽隨機(jī)碼概述

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

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

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

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

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

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

2.2 偽隨機(jī)碼選型

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

2.3 m 序列產(chǎn)生

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

圖1

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

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

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

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

表1 2-10 階本原多項(xiàng)式

其中,n 階 m 序列具有如下特點(diǎn):

a. 序列長(zhǎng)度為 2n-1;

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

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

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

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

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

圖2

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

三、漢明碼

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

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

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

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

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

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

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

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

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

表 2

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

圖3

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

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

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

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

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

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

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

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

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

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

表 3

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

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

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

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

圖4

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

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

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

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

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

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

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

 

圖 5 mcu?

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

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

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

二、MCU模塊

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

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

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

具體代碼如下:

//***************************************************************///模塊名: mcu//作 者: The last one//用 途: 包含發(fā)送部分全部?jī)?nèi)容//版本說(shuō)明://***************************************************************/`timescale 1us/1usmodule mcu(noised_data //輸出帶有噪聲信號(hào))
parameter TestNumber = 400;parameter Period = 100;
/**********************發(fā)送數(shù)據(jù)端口信號(hào)定義*********************/  wire [1:0] un_noised_data;  output [2:0] noised_data;  reg clk1,clk31,rst_n;  reg send_ena;    wire insourse_ena;  integer indataFILE; //指向一個(gè)文件,用于存儲(chǔ)  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)建一個(gè)文件(名為 indataRandom.dat)    生成測(cè)試用的隨機(jī)字節(jié)寫入該文件中    把第一個(gè)數(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 使能信號(hào)(insourse_ena)到來(lái),每 1 個(gè) clk1 時(shí)鐘把一個(gè)數(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ù)的接收、對(duì)數(shù)據(jù)的漢明碼編碼、擴(kuò)頻和信道編碼等操作。

模塊的通信時(shí)序如下:

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

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

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

4. 對(duì)編好的信息進(jìn)行擴(kuò)頻,1 比特?cái)U(kuò)頻到 31 比特;

5. 對(duì)擴(kuò)頻后的信號(hào)進(jìn)行信道編碼,即 1 用 01(+1)、0 用 11(-1)。

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

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

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

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

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

//************************************************************//模塊名: coder//作 者: The last one//工 程://用 途: 漢明碼編碼、擴(kuò)頻、信道編碼//版本說(shuō)明://************************************************************module coder(              input wire clk1,              input wire clk31,              input wire rst_n,              input wire send_ena, //發(fā)送信號(hào)使能              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;    /***************************************************作為輸出使能模塊,并進(jìn)行信道編碼***************************************************/    assign out_data = (send_ena && out_data_flag)?    (((in_data_buf ^ m_coder[0]) == 1'b1)? 2'b01 : 2'b11) : 2'b10;    // 該部分的 ll 信號(hào)只用于調(diào)制代碼時(shí)使用    reg ll;    always @(posedge clk1)      if(!rst_n)        ll <= 0;      else        ll <= in_data_buf;    /***********************************************狀態(tài)機(jī),發(fā)送頭同步->數(shù)據(jù)幀同步->數(shù)據(jù) 每發(fā)送 128 個(gè)數(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)生頭同步信號(hào) 11111111110                    4'h1 : data_frames; //數(shù)據(jù)幀同步信號(hào) 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ù)幀同步信號(hào) 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ù)幀同步信號(hào) 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í)數(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; //暫停主機(jī)送來(lái)數(shù)據(jù),接下來(lái)發(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)生 由主機(jī)發(fā)出使能信號(hào) 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)生干擾,這里所說(shuō)的干擾都為加性干擾,只要把無(wú)干擾數(shù)據(jù) 01(+1)和 11(-1)分別加上范圍在[-2,+2]的隨機(jī)數(shù)。

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

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

/************************************************************///模塊名: mcu//作 者: The last one//用 途: 添加加性干擾//版本說(shuō)明://************************************************************/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 作為頂層模塊,激勵(lì)由 mcu產(chǎn)生、發(fā)送輸出為加加性噪聲后的信號(hào)。

五、decoder?模塊

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

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

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

由于偽隨機(jī)序列具有很強(qiáng)的相關(guān)性。只要有 1 個(gè)時(shí)鐘錯(cuò)誤,解擴(kuò)結(jié)果相差會(huì)相當(dāng)?shù)拇?。依靠它的這個(gè)特性,可以把發(fā)送數(shù)據(jù)一一解擴(kuò)。(具體解擴(kuò)過(guò)程在仿真部分將更詳細(xì)說(shuō)明)。

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

具體代碼如下:

//*******************************************************///模塊名: mcu//作 者: The last one//用 途: 解擴(kuò)//版本說(shuō)明://******************************************************/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;    /************************************取絕對(duì)值正數(shù)是本身,負(fù)數(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,              //找到頭信號(hào)之后的同步解碼過(guò)程以找到 0 時(shí)為結(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)生一個(gè)循環(huán)的隨機(jī)碼,用于解擴(kuò)******************************************************/    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

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

六、correct?模塊

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

具體代碼如下:

//***********************************************************///模塊名: mcu//作 者: The last one//用 途: 檢錯(cuò)、糾錯(cuò)//版本說(shuō)明://***********************************************************/module correct (                  clk1,                  rst_n,                  in_data, //輸入解調(diào)后的數(shù)據(jù)                  out_data, //輸出糾錯(cuò)后的結(jié)果                  decode_data_flag,//來(lái)自 decode 模塊的信號(hào),用以標(biāo)                  識(shí)信號(hào)已解調(diào)完畢(不包括同步判斷信號(hào) 11111111110)                  correct_data_flag,//輸出信號(hào),表明已經(jīng)完成查錯(cuò)和                  糾錯(cuò)功能                   asyn_flag //輸出信號(hào),高電平表示不同步              );                  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; //糾錯(cuò)碼運(yùn)算結(jié)果    reg [11:0] data_number;    reg [3:0] state1,state2;        always @(posedge clk1) //接收外來(lái)的數(shù)據(jù)        if(!rst_n || !decode_data_flag)          begin              state1 <= 4'h0;              flag <= 1'b0;          end        else          case(state1)/**********************************************接收解調(diào)出來(lái)的 7 位數(shù)據(jù)接收完 7 個(gè)數(shù)據(jù)后,給 flag 信號(hào),進(jìn)行數(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ù)進(jìn)行處理后,送出端口            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 到來(lái)                        correct_data_flag <= 1'b0;                        if(flag)                          begin                            state2 <= PROCESS;                            preprocessing; //預(yù)加工數(shù)據(jù)                          end                        else                           state2 <= IDLE;                      end                                    PROCESS: begin //糾錯(cuò)處理                              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/*******************************************************計(jì)算錯(cuò)碼情況,但如果有兩個(gè)碼組錯(cuò)誤將無(wú)法判定將 in_data_buf 賦給 data_buf 保存起來(lái)數(shù)據(jù)計(jì)滿 903 位(512 信息位,384 監(jiān)督位,4 位數(shù)據(jù)幀,3 位數(shù)據(jù)幀監(jiān)督位)data_number 賦 0 開始計(jì)數(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 -----------------------------------------------------------如果有一位錯(cuò),必定是監(jiān)督位錯(cuò)。如果是第一位數(shù)據(jù),判斷是否為數(shù)據(jù)幀(0000)若不是數(shù)據(jù)幀,則認(rèn)為系統(tǒng)沒(méi)有同步數(shù)據(jù)幀,發(fā)送 syn_flag 高電平,以下類似.s1,s2,s3 如果錯(cuò)誤有兩個(gè)以上,可以找到他們的相交區(qū)間s1,s2 錯(cuò)誤,s3 正確 可以找到 不屬于 s3,而同時(shí)屬于 s1,s2 的數(shù)據(jù)為 d5s1,s3 錯(cuò)誤,s2 正確 可以找到 不屬于 s2,而同時(shí)屬于 s1,s3 的數(shù)據(jù)為 d4s2,s3 錯(cuò)誤,s1 正確 可以找到 不屬于 s1,而同時(shí)屬于 s2,s3 的數(shù)據(jù)為 d3s1,s2,s3 錯(cuò)誤, 而同時(shí)屬于 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//用 途: 解擴(kuò)和糾錯(cuò)模塊的頂層模塊//版本說(shuō)明://*******************************************************/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 信號(hào)    input [2:0] noised_data;//從 add_noise 輸出的噪聲信號(hào),等待解調(diào)    output [3:0] pro_correct_data; //處理后輸出的數(shù)據(jù)    output correct_data_flag; //錯(cuò)誤碼標(biāo)示位    output asyn_flag; //系統(tǒng)數(shù)據(jù)幀同步信號(hào)    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)信宿。它接收來(lái)自于模塊 correct 糾錯(cuò)后的數(shù)據(jù),對(duì)數(shù)據(jù)進(jìn)行保存。以便查看結(jié)果。

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

其代碼如下:

//**********************************************************///模塊名: slaver//作 者: The last one//用 途: 包含發(fā)送部分全部?jī)?nèi)容//版本說(shuō)明://************************************************************/`timescale 1us/1usmodule slaver(              input [2:0] noised_data //接收帶有噪聲干擾信號(hào)?????????????);?????????????  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]; //用于存儲(chǔ)發(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 信號(hào),延遲是隨機(jī)的.  ***********************************/    begin      clk1x = 0;      rst_nx = 0;      #(Period*({$random}%10)) //產(chǎn)生一個(gè)隨機(jī)的延遲開始      rst_nx = 1;      forever #(Period * 31) clk1x = ~clk1x;    end      initial     /***********************************    產(chǎn)生 clk31x 信號(hào),延遲是隨機(jī)的.    ***********************************/      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 作為仿真平臺(tái)的頂層模塊,它包含 mcu 和 slaver 兩個(gè)模塊。并且對(duì)發(fā)送數(shù)據(jù)和接收數(shù)據(jù)進(jìn)行對(duì)比。統(tǒng)計(jì)結(jié)果,并輸出(打印到屏幕)。

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

它的代碼如下:

//**********************************************************///模塊名: slaver//作 者: The last one//用 途: 包含發(fā)送部分全部?jī)?nèi)容//版本說(shuō)明://************************************************************/`define PERIOD 100`define testnumber 500 //測(cè)試數(shù)據(jù)個(gè)數(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; //記錄錯(cuò)誤個(gè)數(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)計(jì)結(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、在計(jì)算機(jī)上,找一個(gè)沒(méi)有中文字符的目錄,新建以下幾個(gè)文件,如圖 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

以上是輸入方式進(jìn)行仿真,也可以直接使用圖形化的方式進(jìn)行仿真。但沒(méi)有開始仿真,因?yàn)槲覀円韵逻€要添加一條語(yǔ)句。但沒(méi)有響應(yīng)的文件。tt.bat 的代碼如下:

echopausevsim -do .tt.dopause

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

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

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

圖7

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

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

圖8

在框圖 1 中為整個(gè)仿真平臺(tái)上的模型,可以點(diǎn)擊模型+展開??驁D 2 顯示當(dāng)前模型所含的項(xiàng)目。

1.5、添加波形,如圖 9、10、11,對(duì)模塊 coder 添加波形,并對(duì)波形進(jìn)行分組。

圖9

圖10

圖11

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

圖12

圖13

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

二、模塊仿真

2.1、模塊 mcu?仿真

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

圖14

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

2.2、模塊?coder?仿真

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

圖15

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

2.3、模塊?noise?仿真

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

仿真波形如圖 16。

圖16

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

2.4、模塊?decoder?仿真

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

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

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

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

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

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

仿真結(jié)果如下:

圖17

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

圖18

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

通過(guò)解擴(kuò)的數(shù)據(jù),送到 correct 模塊進(jìn)行糾錯(cuò)。

2.5、模塊?correct?仿真

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

2.6、模塊?Slaver?仿真

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

2.7、模塊?Top??仿真

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

圖19

圖20

以上是整個(gè)設(shè)計(jì)的仿真過(guò)程。

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

相關(guān)推薦

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

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