今天给大侠带来直接扩频通信,由于篇幅较长,分三篇。今天带来中篇,也是第二篇,系统的 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 ");
$display(" ***********************************************");
$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(" ------------------------------------ ");
$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等安装相关设计教程,学习资源、项目资源、好文推荐等,希望大侠持续关注。大侠们,江湖偌大,继续闯荡,愿一切安好,有缘再见!
往期推荐