大俠好,歡迎來到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/1us
module 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
end
endmodule
四、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 s3
x x x s1
x x x s2
x 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ù)為 d5
s1,s3 錯誤,s2 正確 可以找到 不屬于 s2,而同時屬于 s1,s3 的數(shù)據(jù)為 d4
s2,s3 錯誤,s1 正確 可以找到 不屬于 s1,而同時屬于 s2,s3 的數(shù)據(jù)為 d3
s1,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/1us
module 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/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; //記錄錯誤個數(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 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
以上是輸入方式進行仿真,也可以直接使用圖形化的方式進行仿真。但沒有開始仿真,因為我們以下還要添加一條語句。但沒有響應(yīng)的文件。tt.bat 的代碼如下:
echo
pause
vsim -do .tt.do
pause
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é)束,各位大俠,有緣再見!