大俠好,歡迎來(lái)到FPGA技術(shù)江湖,江湖偌大,相見(jiàn)即是緣分。大俠可以關(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 序列一般使用寫(xiě)入雙口 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è)異或門(mén)就可以完成該設(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ù)和硬盤(pán)陣列等。
本設(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 模塊的開(kāi)始,由 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/1us
module 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
/********************************************
打開(kāi)或者創(chuàng)建一個(gè)文件(名為 indataRandom.dat)
生成測(cè)試用的隨機(jī)字節(jié)寫(xiě)入該文件中
把第一個(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
end
endmodule
四、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 開(kāi)始計(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 s3
x x x s1
x x x s2
x 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ù)為 d5
s1,s3 錯(cuò)誤,s2 正確 可以找到 不屬于 s2,而同時(shí)屬于 s1,s3 的數(shù)據(jù)為 d4
s2,s3 錯(cuò)誤,s1 正確 可以找到 不屬于 s1,而同時(shí)屬于 s2,s3 的數(shù)據(jù)為 d3
s1,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ī)的開(kāi)始,以 mcu 模塊產(chǎn)生的時(shí)鐘沒(méi)有相位上的關(guān)系。
其代碼如下:
//**********************************************************/
//模塊名: slaver
//作 者: The last one
//用 途: 包含發(fā)送部分全部?jī)?nèi)容
//版本說(shuō)明:
//************************************************************/
`timescale 1us/1us
module 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ī)的延遲開(kāi)始
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/1us
module 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 work
vmap work work
#編譯當(dāng)前目錄(./)中的 top.v、mcu.v ….
vlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./top.v
vlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./mcu.v
vlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./slaver.v
vlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./coder.v
vlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./add_noise.v
vlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./decoder.v
vlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./correct.v
vlog -work work -L mtiAvm -L mtiOvm -L mtiUPF ./Correct_Decoder.v
#仿真 work 中的 top 模型
vsim -novopt work.top
以上是輸入方式進(jìn)行仿真,也可以直接使用圖形化的方式進(jìn)行仿真。但沒(méi)有開(kāi)始仿真,因?yàn)槲覀円韵逻€要添加一條語(yǔ)句。但沒(méi)有響應(yīng)的文件。tt.bat 的代碼如下:
echo
pause
vsim -do .tt.do
pause
tt.bat 文件為批處理文件,僅為打開(kāi) modelsim、運(yùn)行 tt.do 文件使用。也可以不使用該文件(以下不會(huì)詳細(xì)介紹)。
1.2、將對(duì)應(yīng)的代碼寫(xiě)到相應(yīng)的文件中(sim_wave.do、tt.bat 文件可以不管)。
1.3、用 modelsim 的打開(kāi)方式打開(kāi) top.v 文件(或者你先打開(kāi) modelsim,然后把目錄修改成以上所述的目錄也可)。運(yùn)行的界面如圖 7(modelsim6.5d):
圖7
圖中的亂碼均為modelsim不兼容我所使用的notepad軟件編寫(xiě)的中文字符,大俠均可不以理睬。
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)擊模型+展開(kāi)??驁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、仿真開(kāi)始?在 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 開(kāi)始發(fā)送數(shù)據(jù)”0010”(十六進(jìn)制 2)后發(fā)送監(jiān)督碼的”101”,在 177000us 開(kāi)始發(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í)開(kāi)始發(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ù)的開(kāi)始。由于數(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é)束,各位大俠,有緣再見(jiàn)!