基于FPGA的直接扩频通信系统设计(中)Verilog 实现

2021-05-21 18:09:48 浏览数 (1)

基于FPGA的直接扩频通信系统设计(中)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 信号的有效性即可。

具体代码如下:

代码语言:javascript复制
//***************************************************************/
//模块名: 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=
 ", indataFILE);
        for(k = 1; k <= TestNumber; k = k 1) 
          begin
            indata_mem[k]={$random}%6; 
            $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),使数据和随机码同步送。关

于该模块的工作过程,可以参照下篇的仿真。

该模块的参考具体代码如下:

代码语言:javascript复制
//************************************************************
//模块名: coder
//作 者: The last one
//工 程:
//用 途: 汉明码编码、扩频、信道编码
//版本说明:
//************************************************************
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)?
    (((in_data_buf ^ m_coder[0]) == 1'b1)? 2'b01 : 2'b11) : 2'b10;
    // 该部分的 ll 信号只用于调制代码时使用
    reg ll;
    always @(posedge clk1)
      if(!rst_n)
        ll <= 0;
      else
        ll <= in_data_buf;
    
/***********************************************
 主状态机,发送头同步->数据帧同步->数据
 每发送 128 个数据又跳转到发送数据帧同步 Start
***********************************************/
    always @(posedge clk1) 
      begin
        if(!rst_n)
          sys_reset;
        else if(send_ena)
                case(state) // synthesis full_case
                    4'h0 : head; //产生头同步信号 11111111110
                    4'h1 : data_frames; //数据帧同步信号 0000 000
                    4'h2 : ready_data; //数据发送
                endcase
            else
          sys_reset; //复位
      end
      
/***************
 复位 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
    
/*****************************************
 发送数据帧同步信号 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
    
/*****************************************
 发送数据帧同步信号 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
    
/********************************************************
 发送真实数据模块,每发送 4 位信息位和 3 位监督位 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; //暂停主机送来数据,接下来发送监督位
               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 系列产生 由主机发出使能信号 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 模块

该模块代码的作用是产生干扰,这里所说的干扰都为加性干扰,只要把无干扰数据 01( 1)和 11(-1)分别加上范围在[-2, 2]的随机数。

加干扰后, 1 将会变成 01±[-2, 2] = [-1, 3],-1 将会变成 11±[-2, 2] = [-3, 1]。并且两个范围都是均匀分布。

由于输入数据为 2 个比特,必须扩展后加减法才是我们需要的。具体代码如下:

代码语言:javascript复制
/************************************************************/
//模块名: 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。

具体代码如下:

代码语言:javascript复制
//*******************************************************/
//模块名: 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;
    
//已知的解调系列
    wire [30:0] m =31'b1001011001111100011011101010000;
    
/************************************
取绝对值
正数是本身,负数则取反加 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 时为结束
              find_head_end = 4'b0100, 
              //用于解调除 11111111110 以外的所有传输数据
              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
            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 :
/************************************************
找数据帧同步
************************************************/
                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 :
/**************************************************
 解调数据
****************************************************/
                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

该模块只是对应的解扩,并未涉及信息的检错和纠错,检错将在 correct 模块中进行。

六、correct 模块

模块 correct 将对解扩后的信息就行检错和纠错。检错过程就相当于汉明码编码的逆过程。但(7,4)汉明码仅在 1 位错误的情况下可以检出错误,如果多于 1 位错误,将无法纠错过来(依据 d>e 1;码距 d=3)。

具体代码如下:

代码语言:javascript复制
//***********************************************************/
//模块名: 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 模块产生的时钟没有相位上的关系。

其代码如下:

代码语言:javascript复制
//**********************************************************/
//模块名: 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})) //产生一个随机的延迟开始
      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)e != 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=
 ", 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 将给两个模块提供周期,仿真个数等参数,以传参的形式传送。

它的代码如下:

代码语言:javascript复制
//**********************************************************/
//模块名: 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 endn");
      $display("nn***********************************************");
      $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 ]",m);
        $display(" Right data number is ]",`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

本篇对每一个模块的功能和代码都做了说明,但还是不够详细。大侠更需要的是实际的实现。在下一章中会对整个系统工程进行仿真,并更详细的介绍各个模块的功能。

本篇到此结束,明天带来最后一篇,关于仿真相关内容。

0 人点赞