1. 程式人生 > 實用技巧 >直接擴頻通訊(中)Verilog 實現

直接擴頻通訊(中)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/1usmodule 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),使資料和隨機碼同步送。關

於該模組的工作過程,可以參照下篇的模擬。

該模組的參考具體程式碼如下:

//************************************************************//模組名: 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        endendmodule




四、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。

具體程式碼如下:

//*******************************************************///模組名: 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            endcaseendmodule

該模組只是對應的解擴,並未涉及資訊的檢錯和糾錯,檢錯將在 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;endendtask          task correct_task;            begin            case({s3,s2,s1})/***********************************************************資料位 監督位-------------------------------- -------------------d6 d5 d4 d3 s1 s2 s3x x x s1x x x s2x x x s3 -----------------------------------------------------------如果有一位錯,必定是監督位錯。如果是第一位資料,判斷是否為資料幀(0000)若不是資料幀,則認為系統沒有同步資料幀,傳送 syn_flag 高電平,以下類似.s1,s2,s3 如果錯誤有兩個以上,可以找到他們的相交區間s1,s2 錯誤,s3 正確 可以找到 不屬於 s3,而同時屬於 s1,s2 的資料為 d5s1,s3 錯誤,s2 正確 可以找到 不屬於 s2,而同時屬於 s1,s3 的資料為 d4s2,s3 錯誤,s1 正確 可以找到 不屬於 s1,而同時屬於 s2,s3 的資料為 d3s1,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            endtaskendmodule




七、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/1usmodule 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/1usmodule 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等安裝相關設計教程,學習資源、專案資源、好文推薦等,希望大俠持續關注。大俠們,江湖偌大,繼續闖蕩,願一切安好,有緣再見!

往期推薦