zoukankan      html  css  js  c++  java
  • PPM解码器

    PPM即Pulse Position Modulation(脉冲位置调制),利用脉冲的相对位置来传递信息的一种调制方式。在这种调制方式中,数据能够高速的传递。本文就来详细介绍一下PPM解码器。

    1、PPM的功能描述

    输入信号

    • clk,时钟周期为0.59us
    • rst,异步复位信号,低电平有效
    • din,输入的PPM编码后的数据

    输出信号

    • [7:0] dout,PPM解码后的8位数据
    • d_en,输出数据有效标志,高电平有效,持续一个时钟周期
    • f_en,帧头检测有效标志,高电平有效,持续一个时钟周期

    PPM数据编码格式.png

    PPM数据帧格式.png

    2、PPM的功能分析

    计数器用来控制时序,移位寄存器用来暂存数据,状态机用来进行状态转换。

    2.1计数器

    时钟的周期是0.59us,而输入的每一位数据宽度为9.44us=0.59us※16,解码2bit的数据需要的时间为75.52us=0.59us※128,解码一个完整的8位数据,需要302.08us=75.52us※4。基于以上分析,我们可以设置3个计数器来控制数据的采样。

    • count0,0~15,每16个时钟周期采一位din信号。
    • count1,0~7,解码2bit需要采到8位din信号。
    • count2,0~3,完成一个完整的8位信号,需要解码2bit数据4次。

    计数器count0.png
    计数器count1.png
    计数器count2.png

    2.2移位寄存器

    我们要对输入数据的8位数据进行判读,就要求我们对数据进行暂存。这里我们采用移位寄存器对输入数据进行暂存。与此同时,输出的8bit数据是2bit数据输出累加到8bit,所以我们也需要移位寄存器对输出数据进行暂存。

    • [7:0] reg1,对输入的数据进行移位操作,{reg1[6:0],din}
    • [7:0] reg2,对输出的数据进行暂存,等待8bit移满,就进行数据的输出,{2'b11,reg2[7:2]}{2'b00,reg2[7:2]}{2'b10,reg2[7:2]}{2'b01,reg2[7:2]}

    移位寄存器reg1.png
    移位寄存器reg2.png

    2.3状态机

    在传送数据的时候,主要有两个状态。要么是收到帧头解码数据,要么是没有收到帧头不进行解码。

    • S0,表示没有收到帧头,处于未工作状态。
    • S1,表示收到帧头,开始进行解码。

    状态转移图.png

    :以上电路图和状态转移图的判断条件有所简化。

    具体代码如下:

      module PPM(
           clk,
           rst,
           din,
           dout,
           d_en,
           f_en);
           
       input clk,rst;
       input din;
       output [7:0] dout;
       output d_en,f_en;
       reg [7:0] dout;
       reg d_en,f_en;
    
       reg [3:0] count0;
       reg [2:0] count1;
       reg [2:0] count2;
    
       reg [7:0] reg1;
       reg [7:0] reg2;
       reg cs,ns;
    
      parameter  SOF=8'b01111011,
                 EOF=4'b1101,
                d_00=8'b10111111,
                d_01=8'b11101111,
                d_10=8'b11111011,
                d_11=8'b11111110,
                   S0=1'b0,
                   S1=1'b1;
          
        always@(posedge clk or negedge rst)
           begin
             if(!rst)
                cs<=S0;
             else 
              cs<=ns;
           end
    
           always@(cs or count0 or count1 or reg1)
             begin
              case(cs)
                S0:begin
                 if((count0==15)&&(reg1==SOF))
                   begin
                   ns=S1;
                  f_en=1'b1;
                  end
                 else
                    begin
                    ns=S0;
                   f_en=1'b0;
                    end
                 end
         S1:begin
        if((count0==15)&&(count1==3)&&(reg1[3:0]==EOF))
        begin
          ns=S0;
          f_en=1'b0;
        end
      else
        begin
          ns=S1;
          f_en=1'b0;
        end
      end
      default:begin
                 ns=ns;
                f_en=1'b0;
               end
      endcase          
    end
    
          always@(posedge clk or negedge rst)
           begin
            if(!rst)
              begin
            count0<=0;
            reg1<=8'b00000000;
            end
         else
            begin 
              if(count0==15)
                begin
               reg1<={reg1[6:0],din};
              count0<=0;
             end
         else
         count0<=count0+1;   
     end
    end
    
    always@(posedge clk or negedge rst)
       begin
        if(!rst)
           count1<=0;
       else
       begin 
    if(cs==S1)
      if(count0==15)
         if(count1==7)
           count1<=0;
         else
           count1<=count1+1;
      else
        count1<=count1;
    else
      count1<=0;
     end
    end
        
    always@(posedge clk or negedge rst)
    begin
      if(!rst)
      count2<=0;
    else
    begin
      if((count0==15)&&(cs==S1)&&(count1==7))
        if(count2==3)
          count2<=0;
        else
          count2<=count2+1;
    end
    end
    
     always@(posedge clk or negedge rst)
     begin
     if(!rst)
         d_en<=0;
       else
         begin
           if((count0==15)&&(count1==7)&&(cs==S1)&&(count2==3))  
             d_en<=1;
           else
             d_en<=0;
          end
      end
    
     always@(posedge clk or negedge rst)
     begin
    if(!rst)
      reg2<=8'b00000000;
    else
    begin 
      if(cs==S1)
       if((count0==15)&&(count1==7)&&(count2<=3))     
              begin
              case(reg1)
              d_00:reg2<={2'b00,reg2[7:2]};
              d_01:reg2<={2'b01,reg2[7:2]};
              d_10:reg2<={2'b10,reg2[7:2]};  
              d_11:reg2<={2'b11,reg2[7:2]};
              default:reg2<=reg2;
              endcase  
              end
         else reg2<=reg2;
         else reg2<=0;     
      end
    end        
    
    always@(posedge clk or negedge rst)
      begin
      if(!rst)
    dout<=0;
      else 
      begin
       if((d_en)&&(cs==S1))
    dout<=reg2;
      else if (cs==S0)
    dout<=0;
     end
    end    
    
     endmodule 
    

    对以上代码做如下说明
    用拼接符号{}实现了移位寄存器,在使用拼接符号时一定要指定每一个元素的位宽。在位拼接表达式中不允许存在没有指明位数的信号。
    仿真波形图.png

    3、testbench的编写

    我们下面举一个例子来说明用文件读入的方法对存储器赋值。
    先定义一个有256个地址的字节存贮器
    reg [7:0] mem[40:0]; 地址为0~40,一个地址上存放着8bit的数据
    利用文件读入的方法对men赋值

    • initial $readmemb("mem.txt",mem);
      以二进制的方式读取mem.txt中的数据到mem中。
    • initial $readmemh("mem.txt",mem,16);
      以十六进制的方式读取mem.txt中的数据到mem[16]-mem[40]。
    • initial $readmemh("mem.data",mem,23,1);
      以十六进制的方式读取mem.txt中的数据到mem[23]-mem[1]。

    对读入文件做几点说明

    • 不同地址的数据以空格键或者回车键结束,从mem[0]开始读入数据。

    • 对一个地址的数据读入是从高位开始的,即从mem[0][7]开始读入mem.txt中的第一个数据,第一位地址的第7个元素为mem[1][7]=0 。

    • 对读入文件的命名规则,比如.txt文件名为mem,那么读入文件名应该为“mem.txt”。

    • 在Verilog中支持的文件路径格式为C:/Users/XQ/Desktop/mem.txt,而不是传统Windows底下的C:UsersXQDesktopmem.txt

    • 必须在run xx ns以后才能对存储器进行复制,初始的存储器的值都为xx。
      下面是一个名为mem的txt文件:

         00000000 //无效输入
         01111011 //帧头
         10111111 //"00"
         11101111 //"01"
         11111011 //"10"
         11111110 //"11"输出数据e4
         11111011 //"10"
         11111110 //"11"
         10111111 //"00"
         11101111 //"01"输出数据4e
         11010000 //帧尾
         00001100
         01111011 //帧头
         11111011 //"10"
         11111110 //"11"
         10111111 //"00"
         11101111 //"01"输出数据4e
         11111110 //"11"
         11101111 //"01"
         11111011 //"10"
         11111110 //"11"输出数据e7
         10111111 //"00"
         11101111 //"01"
         11111110 //"11"
         11101111 //"01"输出数据74
         11010010 //帧尾
         11010010
         01111011 //帧头
         10111111 //"00"
         11101111 //"01"
         11111011 //"10"
         11111110 //"11"输出数据e4
         10111111 //"00"
         11101111 //"01"
         11111011 //"10"
         11111110 //"11"输出数据e4
         11111011 //"10"
         11111110 //"11"
         10111111 //"00"
         11101111 //"01"输出数据4e
         11010000 //帧尾
      

    tb文件代码如下:

      `timescale 1ns/1ns
        module PPM_top;
    
         reg clk,rst;
         reg din;
         wire [7:0]dout;
         wire d_en,f_en;
    
         reg [7:0] mem[40:0];  
        integer i,j;
    
         initial
           begin
               $readmemb("C:/Users/XQ/Desktop/mem.txt",mem);
              for(i=0;i<41;i=i+1)
                 for(j=7;j>=0;j=j-1)
                   begin
                    #9440 
                     din=mem[i][j];
                      $display("mem[%0d][%0d]=%b",i,j,mem[i][j]);
                 end
               end
    
      always
         #295 clk=~clk;
    
       initial
         begin
          clk=0;
          rst=1;
          #200
         rst=0;
         #259
         rst=1;   
        end
    
      PPM ut1(
               .clk(clk),
               .rst(rst),
               .din(din),
               .d_en(d_en),
               .f_en(f_en),
               .dout(dout)
               );
                     
    endmodule
    

    :reg类型的数据默认为是无符号的数,若reg [7:0] j那么在j=j-1中就不会出现负数,而是0-1=8'b11111111=255;若integer [7:0] j那么就会出现0-1=-1,正好符合我们的本意。采用integer配合FOR语句,行数比较少,但是integer不能综合,只能用来仿真。

  • 相关阅读:
    更新glibc,删除libc库后,命令行都不能使用了
    进程和线程、协程的区别
    PMP项目管理--资源管理
    清除缓存 echo 1/2/3 > /proc/sys/vm/drop_caches
    gdb malloc方法
    随时更新---能力集
    输出gdb调试信息到文件中
    主动生成core文件 gcore +pid
    PMP项目管理--风险管理
    linux后台程序
  • 原文地址:https://www.cnblogs.com/xuqing125/p/8888081.html
Copyright © 2011-2022 走看看