PPM即Pulse Position Modulation(脈沖位置調(diào)制),利用脈沖的相對位置來傳遞信息的一種調(diào)制方式。在這種調(diào)制方式中,數(shù)據(jù)能夠高速的傳遞。本文就來詳細(xì)介紹一下PPM解碼器。
1、PPM的功能描述
輸入信號
- clk,時鐘周期為0.59us
- rst,異步復(fù)位信號,低電平有效
- din,輸入的PPM編碼后的數(shù)據(jù)
輸出信號
- [7:0] dout,PPM解碼后的8位數(shù)據(jù)
- d_en,輸出數(shù)據(jù)有效標(biāo)志,高電平有效,持續(xù)一個時鐘周期
- f_en,幀頭檢測有效標(biāo)志,高電平有效,持續(xù)一個時鐘周期


2、PPM的功能分析
計數(shù)器用來控制時序,移位寄存器用來暫存數(shù)據(jù),狀態(tài)機(jī)用來進(jìn)行狀態(tài)轉(zhuǎn)換。
2.1計數(shù)器
時鐘的周期是0.59us,而輸入的每一位數(shù)據(jù)寬度為9.44us=0.59us※16,解碼2bit的數(shù)據(jù)需要的時間為75.52us=0.59us※128,解碼一個完整的8位數(shù)據(jù),需要302.08us=75.52us※4?;谝陨戏治?,我們可以設(shè)置3個計數(shù)器來控制數(shù)據(jù)的采樣。
- count0,0~15,每16個時鐘周期采一位din信號。
- count1,0~7,解碼2bit需要采到8位din信號。
- count2,0~3,完成一個完整的8位信號,需要解碼2bit數(shù)據(jù)4次。



2.2移位寄存器
我們要對輸入數(shù)據(jù)的8位數(shù)據(jù)進(jìn)行判讀,就要求我們對數(shù)據(jù)進(jìn)行暫存。這里我們采用移位寄存器對輸入數(shù)據(jù)進(jìn)行暫存。與此同時,輸出的8bit數(shù)據(jù)是2bit數(shù)據(jù)輸出累加到8bit,所以我們也需要移位寄存器對輸出數(shù)據(jù)進(jìn)行暫存。
- [7:0] reg1,對輸入的數(shù)據(jù)進(jìn)行移位操作,{reg1[6:0],din}。
- [7:0] reg2,對輸出的數(shù)據(jù)進(jìn)行暫存,等待8bit移滿,就進(jìn)行數(shù)據(jù)的輸出,{2'b11,reg2[7:2]},{2'b00,reg2[7:2]},{2'b10,reg2[7:2]},{2'b01,reg2[7:2]}。


2.3狀態(tài)機(jī)
在傳送數(shù)據(jù)的時候,主要有兩個狀態(tài)。要么是收到幀頭解碼數(shù)據(jù),要么是沒有收到幀頭不進(jìn)行解碼。
- S0,表示沒有收到幀頭,處于未工作狀態(tài)。
- S1,表示收到幀頭,開始進(jìn)行解碼。

注:以上電路圖和狀態(tài)轉(zhuǎn)移圖的判斷條件有所簡化。
具體代碼如下:
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
對以上代碼做如下說明:
用拼接符號{}實(shí)現(xiàn)了移位寄存器,在使用拼接符號時一定要指定每一個元素的位寬。在位拼接表達(dá)式中不允許存在沒有指明位數(shù)的信號。

3、testbench的編寫
我們下面舉一個例子來說明用文件讀入的方法對存儲器賦值。
先定義一個有256個地址的字節(jié)存貯器
reg [7:0] mem[40:0]; 地址為0~40,一個地址上存放著8bit的數(shù)據(jù)
利用文件讀入的方法對men賦值
-
initial $readmemb("mem.txt",mem);
以二進(jìn)制的方式讀取mem.txt中的數(shù)據(jù)到mem中。 -
initial $readmemh("mem.txt",mem,16);
以十六進(jìn)制的方式讀取mem.txt中的數(shù)據(jù)到mem[16]-mem[40]。 -
initial $readmemh("mem.data",mem,23,1);
以十六進(jìn)制的方式讀取mem.txt中的數(shù)據(jù)到mem[23]-mem[1]。
對讀入文件做幾點(diǎn)說明
不同地址的數(shù)據(jù)以空格鍵或者回車鍵結(jié)束,從mem[0]開始讀入數(shù)據(jù)。
對一個地址的數(shù)據(jù)讀入是從高位開始的,即從mem[0][7]開始讀入mem.txt中的第一個數(shù)據(jù),第一位地址的第7個元素為mem[1][7]=0 。
對讀入文件的命名規(guī)則,比如.txt文件名為mem,那么讀入文件名應(yīng)該為“mem.txt”。
在Verilog中支持的文件路徑格式為
C:/Users/XQ/Desktop/mem.txt,而不是傳統(tǒng)Windows底下的C:\Users\XQ\Desktop\mem.txt。-
必須在run xx ns以后才能對存儲器進(jìn)行復(fù)制,初始的存儲器的值都為xx。
下面是一個名為mem的txt文件:00000000 //無效輸入 01111011 //幀頭 10111111 //"00" 11101111 //"01" 11111011 //"10" 11111110 //"11"輸出數(shù)據(jù)e4 11111011 //"10" 11111110 //"11" 10111111 //"00" 11101111 //"01"輸出數(shù)據(jù)4e 11010000 //幀尾 00001100 01111011 //幀頭 11111011 //"10" 11111110 //"11" 10111111 //"00" 11101111 //"01"輸出數(shù)據(jù)4e 11111110 //"11" 11101111 //"01" 11111011 //"10" 11111110 //"11"輸出數(shù)據(jù)e7 10111111 //"00" 11101111 //"01" 11111110 //"11" 11101111 //"01"輸出數(shù)據(jù)74 11010010 //幀尾 11010010 01111011 //幀頭 10111111 //"00" 11101111 //"01" 11111011 //"10" 11111110 //"11"輸出數(shù)據(jù)e4 10111111 //"00" 11101111 //"01" 11111011 //"10" 11111110 //"11"輸出數(shù)據(jù)e4 11111011 //"10" 11111110 //"11" 10111111 //"00" 11101111 //"01"輸出數(shù)據(jù)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類型的數(shù)據(jù)默認(rèn)為是無符號的數(shù),若reg [7:0] j那么在j=j-1中就不會出現(xiàn)負(fù)數(shù),而是0-1=8'b11111111=255;若integer [7:0] j那么就會出現(xiàn)0-1=-1,正好符合我們的本意。采用integer配合FOR語句,行數(shù)比較少,但是integer不能綜合,只能用來仿真。