直接擴頻通訊(中)Verilog 實現
今天給大俠帶來直接擴頻通訊,由於篇幅較長,分三篇。今天帶來中篇,也是第二篇,系統的verilog實現。話不多說,上貨。
導讀
本篇適用於有一定通訊基礎的大俠,本篇使用的理論不僅僅是擴頻通訊。為了便於學習,本章為整體工程的設計,將按照自己設計思路介紹整個設計的程。硬體電路設計和C語言程式設計有著本實上的區別。各位大俠可依據自己的需要進行閱讀,參考學習。
第二篇內容摘要:本篇介紹系統的 verilog 實現。根據個人的設計經驗,在硬體設計中,以整個系統設計來看,以控制流作為主線,以單個模組設計來看,以資料流作為主線;而C語言程式設計均以控制流作為主線。
作為一個底層模組設計人員,以資料流作為主線是必須的。關於本人這個結論,並沒有真正的得到老一輩工程師的驗證。但在本篇中,就以資料流的方式作為設計主線。
系統的verilog實現
一、資料傳輸過程
從上一章中的拓撲結構圖中可知資料流的過程,如圖 5 所示。
圖 5 mcu
輸出給 coder 模組原始資訊,每 4 位元作為一組,所以,在 mcu 中,每一個位元組拆分成 2 個 4 位元傳送到 coder 模組中。
在coder中對原始訊號進行擴頻、通道編碼、最終輸出2位元的資料01和11(即 -1 和+1)給 add_noise,經過 add_noise 加性干擾噪聲後,輸出 3 位元的資料給decoder 模組,decoder 模組經過解擴後輸出給 correct 模組糾錯,最終傳送給 slaver模組。
最終 top 模組根據傳送的原始資料和接收後的資料進行比對,輸出結果(列印到螢幕上)。這裡只是大概的介紹了設計中資料流的過程。在以下各個模組設計中還會具體提到。
二、MCU模組
模組 mcu 負責通訊的信源部分,除了給 coder 傳送資料,也為 coder 模組和add_noise 模組提供時鐘、復位訊號。該模組對整個模擬有著相當重要。因為它的設計關係到系統模擬的完整性。
mcu 模組包含隨機資料的產生、儲存、傳送。隨機數的產生採用系統函式 random產生。而資料儲存有兩個位置,一個是輸出儲存到檔案中,另一個是儲存到 memory中。儲存到檔案中是為了提供模擬後資料的檢視,而存放 memory 中為了資料的傳送和之後資料的比對。
該模組與下游模組 coder 有一定的時序邏輯,它控制 coder 模組的開始,由 mcu傳送 send_ena 到 coder,隨後等待 coder 模組反饋訊號 insourse_ena。Insourse_ena訊號有效,則傳送資料,否則停止傳送資料。資料傳送結束只要撤銷 send_ena 訊號的有效性即可。
具體程式碼如下:
//***************************************************************/
//模組名: mcu
//作 者: The last one
//用 途: 包含傳送部分全部內容
//版本說明:
//***************************************************************/
`timescale 1us/1us
module mcu(
noised_data //輸出帶有噪聲訊號
)
parameter TestNumber = 400;
parameter Period = 100;
/**********************傳送資料埠訊號定義*********************/
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
/********************************************
開啟或者建立一個檔案(名為 indataRandom.dat)
生成測試用的隨機位元組寫入該檔案中
把第一個資料賦給 indatabyte
最後關閉該檔案,釋放 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)
/************************************************
當 coder 使能訊號(insourse_ena)到來,每 1 個 clk1 時鐘把一個數據傳出
傳出的資料與 indataRandom.dat 檔案的資料一樣
************************************************/
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 為原始資料的接收、對資料的漢明碼編碼、擴頻和通道編碼等操作。
模組的通訊時序如下:
1. 接收到模組 mcu 原始資料到來之前,先發送一個同步頭,起止由 mcu 控制;
2. 每傳送 128 個位元組原始資料前,傳送資料 0000 作為資料幀同步,用於檢測傳送和接收兩端資料傳送是否同步;
3. 對原始資料進行漢明碼編碼,監督位為 3 位,全部放到資料位後;
4. 對編好的資訊進行擴頻,1 位元擴頻到 31 位元;
5. 對擴頻後的訊號進行通道編碼,即 1 用 01(+1)、0 用 11(-1)。
擴頻通訊,原始資料的頻率必然比擴頻後的頻率小得多,本設計的 m 序列碼是 31 位元位為一個週期。所以,原始資訊的頻率假設為 f 1,則擴頻頻率 f2 = 31x f1。因此,該模組有兩個時鐘。
該模組採用輸入使能訊號(send_ena)和輸出反饋訊號(insourse_ena)作為與上游模組(mcu)的握手訊號。當 send_ena 有效,同時 insourse_ena 有效時,mcu 才會傳送真實的資料到輸入埠。而當 send_ena 訊號有效的一段時間內,先發送同步頭和資料幀同步後,才使 insourse_ena 有效,傳送原始資料。
傳送端固定對應的 m 序列為{0000101011101100011111001101001}。則每一個數據的傳送都是按該序列傳送,在接收端更容易同步(解調時更詳細解釋)。因此 m 序列的暫存器需一個標示位(flag),使資料和隨機碼同步送。關
於該模組的工作過程,可以參照下篇的模擬。
該模組的參考具體程式碼如下:
//************************************************************
//************************************************************
module coder(
input wire clk1,
input wire clk31,
input wire rst_n,
input wire send_ena, //傳送訊號使能
input wire in_data,
output reg insourse_ena, // 獲取資料,用於與 mcu 握手
output wire [1:0] out_data // 輸入資料。
);
parameter idle = 4'b0001,
body = 4'b0010;
reg in_data_buf;
reg out_data_flag;
reg check1,check2, check3; // 3 位監督位
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)?
reg ll;
always @(posedge clk1)
if(!rst_n)
ll <= 0;
else
ll <= in_data_buf;
/***********************************************
主狀態機,傳送頭同步->資料幀同步->資料
***********************************************/
always @(posedge clk1)
begin
if(!rst_n)
sys_reset;
else if(send_ena)
endcase
else
end
/***************
****************/
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
/*****************************************
*****************************************/
task head;
begin
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
/*****************************************
*****************************************/
task data_frames;
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
/********************************************************
********************************************************/
task ready_data;
begin
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; //暫停主機送來資料,接下來發送監督位
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;
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 系列產生 由主機發出使能訊號 Start
*********************************************/
always @(posedge clk31)
begin
if(!rst_n)
begin
state_m <= idle;
m_coder <= 5'b01000;
end
else
idle : if(flag)
state_m <= body;
else
state_m <= idle;
body: begin
end
endcase
end
endmodule
四、add_noise模組
該模組程式碼的作用是產生干擾,這裡所說的干擾都為加性干擾,只要把無干擾資料 01(+1)和 11(-1)分別加上範圍在[-2,+2]的隨機數。
加干擾後,+1 將會變成 01±[-2,+2] = [-1,+3],-1 將會變成 11±[-2,+2] = [-3,+1]。並且兩個範圍都是均勻分佈。
由於輸入資料為 2 個位元,必須擴充套件後加減法才是我們需要的。具體程式碼如下:
/************************************************************/
//模組名: mcu
//作 者: The last one
//用 途: 新增加性干擾
//版本說明:
//************************************************************/
module add_noise(
clk31,
rst_n,
un_noised_data, //干擾資料輸入
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]
都是等概率的出現
*****************************************************/
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產生、傳送輸出為加加性噪聲後的訊號。
五、decoder模組
decoder 是解擴模組,包括查詢同步頭、資料同步、解擴。
同步頭{1111_1111_110},資料幀同步{0000_000},必須接收到同步頭,且同步同步頭後和接收資料幀同步,之後才對資料解擴。
由於傳送模組和接收模組有時間差,但可以確認的是,必須先接收,後再發送。傳送端採用的是固定的 m 序列碼作為擴頻偽隨機碼,這樣做的利處就是接收端只要採用一樣的 m 序列作為解擴碼。
由於偽隨機序列具有很強的相關性。只要有 1 個時鐘錯誤,解擴結果相差會相當的大。依靠它的這個特性,可以把傳送資料一一解擴。(具體解擴過程在模擬部分將更詳細說明)。
由於在模組 add_noise 中添加了干擾,傳送資料會有一定的誤差,所以,解擴過程需使用累加的方法進行。而累加的閥值這裡固定在 28,由於累加過程會有減法運算,所以計算初值均為 100。
具體程式碼如下:
//*******************************************************/
//******************************************************/
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;
//已知的解調系列
wire [30:0] m =31'b1001011001111100011011101010000;
/************************************
取絕對值
************************************/
assign psumi =(in_data[2]==0)?{in_data[1],in_data[0]}:(~in_data+1);
parameter find_head = 4'b0001,
synchronize = 4'b0010,
find_head_end = 4'b0100,
main_body = 4'b1000;
reg [7:0] sum1,
sum2,
sum3,
sum4,
sum5,
sum6,
sum7,
sum8,
sum9,
sum10,
sum;
reg [7:0] i,j;
/******************************************************
產生一個迴圈的隨機碼,用於解擴
******************************************************/
always @(posedge clk31x)
begin
m_coder_buf <= {m[8:0],m};
else
]};
end
always @(posedge clk31x)
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)
/********************************************************
尋找同步頭。
*********************************************************/
begin
begin
j <= j + 1'b1;
sum1 <= sum1 + psumi;
else
sum1 <= sum1 - psumi;
sum2 <= sum2 + psumi;
else
sum2 <= sum2 - psumi;
sum3 <= sum3 + psumi;
else
sum3 <= sum3 - psumi;
sum4 <= sum4 + psumi;
else
sum4 <= sum4 - psumi;
sum5 <= sum5 + psumi;
else
sum5 <= sum5 - psumi;
sum6 <= sum6 + psumi;
else
sum6 <= sum6 - psumi;
sum7 <= sum7 + psumi;
else
sum7 <= sum7 - psumi;
sum8 <= sum8 + psumi;
else
sum8 <= sum8 - psumi;
sum9 <= sum9 + psumi;
else
sum9 <= sum9 - psumi;
sum10 <= sum10 + psumi;
else
sum10 <= sum10 - psumi;
sum4 >= 8'd128 || sum5 >= 8'd128 ||
sum6 >= 8'd128 || sum7 >= 8'd128 || sum8 >= 8'd128 ||
sum9 >= 8'd128 || sum10 >= 8'd128)
begin
state <= synchronize;
end
end
else
begin
i <= i + 8'd10;
else
i <= 8'd0;
j <= 8'd0;
sum1 <= 8'd100 + psumi;
else
sum1 <= 8'd100 - psumi;
sum2 <= 8'd100 + psumi;
else
sum2 <= 8'd100 - psumi;
sum3 <= 8'd100 + psumi;
else
sum3 <= 8'd100 - psumi;
sum4 <= 8'd100 + psumi;
else
sum4 <= 8'd100 - psumi;
sum5 <= 8'd100 + psumi;
else
sum5 <= 8'd100 - psumi;
sum6 <= 8'd100 + psumi;
else
sum6 <= 8'd100 - psumi;
sum7 <= 8'd100 + psumi;
else
sum7 <= 8'd100 - psumi;
sum8 <= 8'd100 + psumi;
else
sum8 <= 8'd100 - psumi;
sum9 <= 8'd100 + psumi;
else
sum9 <= 8'd100 - psumi;
sum10 <= 8'd100 + psumi;
else
sum10 <= 8'd100 - psumi;
end
end
synchronize :
/*********************************************************
同步同步頭
************************************************/
begin
else
begin
state <= find_head_end;
j <= 8'd0;
sum <= 8'd100 + psumi;
else
sum <= 8'd100 - psumi;
end
end
find_head_end :
/************************************************
找資料幀同步
************************************************/
begin
begin
sum <= sum + psumi;
else
sum <= sum - psumi;
j <= j + 1'b1;
end
else
begin
sum <= 8'd100 + psumi;
else
sum <= 8'd100 - psumi;
begin
temp <= 1'b1;
end
else
begin
temp <= 1'b0;
decode_data_flag <= 1'b1;
state <= main_body;
end
end
end
main_body :
/**************************************************
解調資料
****************************************************/
begin
begin
sum <= sum + psumi;
else
sum <= sum - psumi;
j <= j + 1'b1;
end
else
begin
j <= 8'd0;
sum <= 8'd100 + psumi;
else
sum <= 8'd100 - psumi;
out_data <= 1'b1;
else
out_data <= 1'b0;
end
end
endcase
endmodule
該模組只是對應的解擴,並未涉及資訊的檢錯和糾錯,檢錯將在 correct 模組中進行。
correct
模組 correct 將對解擴後的資訊就行檢錯和糾錯。檢錯過程就相當於漢明碼編碼的逆過程。但(7,4)漢明碼僅在 1 位錯誤的情況下可以檢出錯誤,如果多於 1 位錯誤,將無法糾錯過來(依據 d>e+1;碼距 d=3)。
具體程式碼如下:
//***********************************************************/
//模組名: mcu
//作 者: The last one
//用 途: 檢錯、糾錯
//版本說明:
//***********************************************************/
module correct (
clk1,
rst_n,
in_data, //輸入解調後的資料
out_data, //輸出糾錯後的結果
decode_data_flag,//來自 decode 模組的訊號,用以標
識訊號已解調完畢(不包括同步判斷訊號 11111111110)
correct_data_flag,//輸出訊號,表明已經完成查錯和
糾錯功能
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,//輸入資料移位暫存器
data_buf; //輸入資料緩衝暫存器
reg flag; //讀滿標示位
reg s1,s2,s3; //糾錯碼運算結果
reg [11:0] data_number;
reg [3:0] state1,state2;
always @(posedge clk1) //接收外來的資料
if(!rst_n || !decode_data_flag)
begin
state1 <= 4'h0;
flag <= 1'b0;
end
else
case(state1)
/**********************************************
接收解調出來的 7 位資料
接收完 7 個數據後,給 flag 訊號,進行資料處理.
**********************************************/
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) //把接收到的資料進行處理後,送出埠
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; //預加工資料
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 儲存起來
資料計滿 903 位(512 資訊位,384 監督位,4 位資料幀,3 位資料幀監督位)
data_number 賦 0 開始計數(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})
/***********************************************************
資料位 監督位
-------------------------------- -------------------
d6 d5 d4 d3 s1 s2 s3
x x x s1
x x x s2
x x x s3
-----------------------------------------------------------
如果有一位錯,必定是監督位錯。
如果是第一位資料,判斷是否為資料幀(0000)
若不是資料幀,則認為系統沒有同步資料幀,
傳送 syn_flag 高電平,以下類似.
s1,s2,s3 如果錯誤有兩個以上,可以找到他們的相交區間
s1,s2 錯誤,s3 正確 可以找到 不屬於 s3,而同時屬於 s1,s2 的資料為 d5
s1,s3 錯誤,s2 正確 可以找到 不屬於 s2,而同時屬於 s1,s3 的資料為 d4
s2,s3 錯誤,s1 正確 可以找到 不屬於 s1,而同時屬於 s2,s3 的資料為 d3
s1,s2,s3 錯誤, 而同時屬於 s1,s2,s3 的資料為 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 輸出的噪聲訊號,等待解調
output [3:0] pro_correct_data; //處理後輸出的資料
output correct_data_flag; //錯誤碼標示位
output asyn_flag; //系統資料幀同步訊號
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 充當信宿。它接收來自於模組 correct 糾錯後的資料,對資料進行儲存。以便檢視結果。
模組 slaver 作為接收端,它將給解擴和糾錯模組提供時鐘訊號,但其的起始必須必傳送的起始快。並且它所產生的時鐘可以是隨機的開始,以 mcu 模組產生的時鐘沒有相位上的關係。
其程式碼如下:
//**********************************************************/
//模組名: slaver
//作 者: The last one
//用 途: 包含傳送部分全部內容
//版本說明:
//************************************************************/
`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]; //用於儲存傳送的資料
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
/***********************************
產生 clk1x 訊號,延遲是隨機的.
***********************************/
begin
clk1x = 0;
rst_nx = 0;
#(Period*({$random}%10)) //產生一個隨機的延遲開始
rst_nx = 1;
forever #(Period * 31) clk1x = ~clk1x;
end
initial
/***********************************
產生 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 兩個模組。並且對傳送資料和接收資料進行對比。統計結果,並輸出(列印到螢幕)。
模組 top 將給兩個模組提供週期,仿真個數等引數,以傳參的形式傳送。
它的程式碼如下:
//**********************************************************/
//模組名: slaver
//作 者: The last one
//用 途: 包含傳送部分全部內容
//版本說明:
//************************************************************/
`define PERIOD 100
`define testnumber 500 //測試資料個數
`timescale 1us/1us
module top;
integer m,n;
wire [2:0] noised_data;
// 模組整體工作流程,都是以任務形式
//******** START *****************
initial
begin
sys_reset;
delay_system_end;
compare_data;
stop;
end
//******* END *****************
//--------------------------------------------------------------------------------------------------------
task sys_reset; //復位
begin
m = 0; //記錄錯誤個數
n = 1;
end
endtask
//--------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------
task delay_system_end; // 等待系統模擬結束
begin
wait(slaver.i == `testnumber+1)
$display("The system transmission end\n");
$display("\n\n***********************************************");
$display(" Start to compare the data");
end
endtask
//--------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------
task compare_data; // 比較傳送資料和接收資料,並統計結果
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
本篇對每一個模組的功能和程式碼都做了說明,但還是不夠詳細。大俠更需要的是實際的實現。在下一章中會對整個系統工程進行模擬,並更詳細的介紹各個模組的功能。
本篇到此結束,明天帶來最後一篇,關於模擬相關內容。
END
後續會持續更新,帶來Vivado、 ISE、Quartus II 、candence等安裝相關設計教程,學習資源、專案資源、好文推薦等,希望大俠持續關注。大俠們,江湖偌大,繼續闖蕩,願一切安好,有緣再見!
往期推薦