(转载请写明出处,谢谢!)
一:前言
这2天在帮一同学弄用cpld实现hdb3的编码和解码,要用vhdl,可惜我只是会一点verilog,所以我开始就用verilog来写,写出来后看点vhdl的语法就可以很快用vhdl搞定了。这里我还是选取自己比较熟悉的verilog来讲解。
Hdb3即3阶高密度双极码。Hdb3码因为其无直流分量、低频分量少,连0个数不超过3、便于时钟的提取和恢复,适合对基带信号直接传输。应用还是比较广泛的,所以ITU-T规定,对于2M,8M,32M的数字接口均采用HDB3接口,可见其作用不可小觑。
关于Hdb3码的编码解码大家可以去网上查下。
二:编码
编码过程分为3个步骤。
1:加V;
废话不说,先看代码。
module AddV(Clk,Data_In,Data_OutV);
input Clk,Data_In;
output reg [1:0]Data_OutV;
reg[1:0]Count0;
always@(posedge Clk)
begin
if(Data_In==1'b1)
begin
Data_OutV<=2'b01;
Count0<=2'b0;
end
else if(Data_In==1'b0)
begin
Count0<=Count0+2'b1;
if(Count0==2'b11)//because it is parall,so it is 3,not 4
begin
Count0<=2'b0;
Data_OutV<=2'b11;
end
else Data_OutV<=2'b00;
end
end
endmodule
模块有2个输入,即系统时钟和需要传输的基带数字信号(只有0和1),1个输出,即经过加V后的数字信号(00代表0,01代表1,11代表V).
具体方法是判断输入Data_In的电平,如果为1,那么输出Data_OutV就为01,如果输入为0,则用Count0计数其0出现的次数,如果Count0小于3个,则输出为00,否则输出为11。这里为什么用小于3个呢,而不是4个呢?因为hdl语言是并行执行的。看相关代码:
begin
Count0<=Count0+2'b1;
if(Count0==2'b11)//because it is parall,so it is 3,not 4
begin
Count0<=2'b0;
Data_OutV<=2'b11;
end
end
Count0加1是和Count0是否等于3是并行执行的,也就是说当第一个0到来时,虽然Count0加1了,但是用if判断时其还是0,以此类推。
2:加B;
module AddB(Clk,Data_In,Data_OutB);
input Clk;
input[1:0] Data_In;
output reg[1:0] Data_OutB;
reg[1:0] Buffer[4];
reg FirstV;
reg Count_Even;//Count_Even only can be assigned as 0 or 1;so 0 is stand for even,1 // is stand for odd
always@(posedge Clk)
Data_OutB=(FirstV==1'b1&&Count_Even==1'b0&&Buffer[0]==2'b11)?2'b10:Buffer[3];
always@(posedge Clk) //take in turn of coming into the register Buffer;
begin
Buffer[0]<=Data_In;
Buffer[1]<=Buffer[0];
Buffer[2]<=Buffer[1];
Buffer[3]<=Buffer[2];
end
always@(posedge Clk)
begin
if(2'b01==Buffer[3]) //here we judge the high bit of Buffer,because Data_OutB
Count_Even<=Count_Even+1'b1; //is usually assigned as Buffer[3];
else if(2'b11==Buffer[0]) //here we judge the low bit of Buffer,because if Buffer[0]
Count_Even<=1'b0; //is 2'b11.then Buffer[1],Buffer[2],Buffer[3] is // 2'b00,so we
end //should not conut Buffer[3];
always@(posedge Clk)
begin
if(2'b11==Buffer[0]) //Judeg if it is the first coming of 'v',if it is ,hten //FisrtV is 1;
if(1'b0==FirstV)
FirstV<=1'b1;
end
endmodule
加B模块也是2个输入,其中1个输入为加V的输出,另一个是时钟;一个输出,即加B后的数字信号输出。(00代表0,01代表1,10代表B,11代表V)
加B模块在加V模块的基础上主要是完成把某些地方的00变成10,什么地方呢?首先,我们知道,V前面肯定有3个0,我们就是要把V最前面的那个0变成B,但并不是所有的这样的0都需要变,还必须满足在两个V之间非0的个数(也就只剩下1了)为偶数的情况下我们才需要把这样的0变成B,两者缺一不可。
怎样判断非0个数的奇偶呢?我们这里采用的是1个位宽的变量Count_Even,因为只有1个位宽,所有非0即1。当然因为其初始化为0,遇到两个V之间的1时Count_Even就加1,此时Count_Even=1,再碰到1个时又加1,这时Count_Even加1就回到了0啦。因此,Count_Even=0时代表两V之间有偶数个1,Count_Even为1时代表两V之间有奇数个1。这方法比较简单,不必用变量累加,然后对2取模判断奇偶!
因为我们用到了判断2个V之间的1的个数,所以我们还需要1个标志FirstV表示我们检测到了第1个V,只有当我们检测到了第1个V后我们才开始统计1的个数。
难点在加B,因为当检测到2个V之间1的个数为偶数时,我们需要把后面V的最前面那个0变成B,所以我们的设计必须有记忆功能,要记住至少4个将要发送的数据。这里我们采用4个长度的数组Buffer作为记忆存储。并且数据Data_OutB是从Buffer[3]中输出的,因此上电后系统输出有一下延时。下面解释下这里为什么采用长度为4的数组来记忆呢?因为我们判断1是在FirstV=1的前提下,且是通过Buffer[3]判断的,所以当第二个V(判V是通过Buffer[0])来临时,Buffer[1] 、Buffer[2]、 Buffer[3]肯定全为0,因为V前面有3个0,而我们就是想把Buffer[3]改为B即可。
3:单双极性变化;
module Polar(Clk,Data_In,Hdb3);
input Clk;
input[1:0]Data_In;
output reg[1:0]Hdb3;
reg FlagP;
always@(posedge Clk)
begin
if(2'b01==Data_In||2'b10==Data_In)
begin
FlagP<=FlagP+1'b1; //FlagP stands for if we should make the code //number negetive
if(1'b1==FlagP) //if FlagP is 1,we should output -1
Hdb3<=2'b10;
else Hdb3<=2'b01;
end
else if(2'b11==Data_In) //if it is 'v',we should make sure it is the same //polar of the pre 'b' or '1'
begin
if(1'b1==FlagP) //if FlagP is 1,we should output +1
Hdb3<=2'b01;
else Hdb3<=2'b10;
end
else if(2'b00==Data_In) //here stay the same
Hdb3<=2'b00;
end
endmodule
单双极性变换很简单,输入为加B后的输出,单极性;输出为双极性,有正有负数,当然+V、-V、+B、-V也是用+1和-1表示了,因为都是数字信号。(+1,+B,+V用01表示,0用00表示,-V,-B、-1用10表示)。
因为除V外,1和B都是正负交错输出的(B也可以看做1),V与前面的1或B同极性。所以为了达到交错的目的,用了FlagP来判断B和1的奇偶数,类似前面的Count_Even。
利用FlagP来输出正负1和B的输出,显然,也可以利用FlagP来输出正负V的,只是规则恰好相反。
三:解码
解码分为2个部分,去V和B部分;单双极性转换部分。
1:去V和B;
module AbandonVB(Clk,Hdb3,Data_OutP);
input Clk;
input[1:0]Hdb3;
output reg[1:0] Data_OutP;
reg[1:0]Buffer[5];
reg[1:0]Count_0;
always@(posedge Clk)
begin
Buffer[4]=Buffer[3];
Buffer[3]=Buffer[2];
Buffer[2]=Buffer[1];
Buffer[1]=Buffer[0];
Buffer[0]=Hdb3;
begin
if(2'b0==Buffer[0])
Count_0=Count_0+2'b1;
else if(2'b10==Count_0&&(2'b00==(Buffer[0]^Buffer[3])))
begin
Buffer[0]=2'b00;
Buffer[3]=2'b00;
Count_0=2'b0;
end
else if(2'b11==Count_0&&(2'b00==(Buffer[0]^Buffer[4])))
begin
Buffer[0]=2'b0;
Count_0=2'b0;
end
else// if(2'b01==Buffer[0]||2'b10==Buffer[0]||2'b11==Buffer[0])
Count_0=2'b0;
end
Data_OutP=Buffer[4];
end
endmodule
去V和B,我是根据解码规则:当有3个连续0时判断这3个0前后数字符号是否相同,如果符号相同,则3个0后面的那个肯定是V,这时只需将V变成0即可;当有2个连续的0时,如果2个0前后的数字符号一致,这2个0前面那个肯定是B,后面那个肯定是V,所以将此时的V和B都变成0即可。这里判断符号相同采用异或,异或结果为0则符号相同。V和B的存在仅以上2种情况,不可能出现1个连续0前后有V和B的。当然这里也采用了Count_0来计数连续0的个数。
2:双单极性转换
module Polar(Clk,Data_In,True_Code);
input Clk;
input[1:0]Data_In;
output reg True_Code;
always@(posedge Clk)
begin
if(2'b01==Data_In||2'b10==Data_In)
True_Code<=1'b1;
else if(2'b00==Data_In)
True_Code<=1'b0;
end
endmodule
这个最简单了,不管是正负1都输出1,是0就输出0就可以了。因为Hdb3码的源码肯定只有位宽为1的0和1.
四:测试
分为2个版本,verilog和VHDL,每个版本都包含编码器(encode)和解码器(decode)。
其中编码的测试数字信号代码为:
原码:1 0 0 1 1 0 0 0 0 1 0 1 1 0 1 0 0 0 0 1 1 1 0
加V: 01 00 00 01 01 00 00 00 11 01 00 01 01 00 01 00 00 00 11 11 11 11 00
(以上为加V后的代码。此处的00代表0,01代表1,11代表V)
加B: 01 00 00 01 01 00 00 00 11 01 00 01 01 00 01 10 00 00 11 11 11 11 00:
(以上为加B后的代码。此处的00代表0,01代表1,10代表B,11代表V)
Hdb3:01 00 00 10 01 00 00 00 01 10 00 01 10 00 01 10 00 00 10 01 10 01 00
(以上为双单极性变换后的代码。此处的00代表0,01代表+1,10代表-1,
即: +1 0 0 -1 1 0 0 0 1 -1 0 1 -1 0 1 -1 0 0 -1 1 -1 1 0)
其顶层模块如下:
仿真波形图如下:
其中解码的测试数字信号代码为:
接收码:01 00 10 10 01 00 00 00 01 10 00 01 10 00 01 10 00 00 10 01 10 01 00
(和编码规则定义的相同,此处的00代表0,01代表+1,10代表-1,
即:+1 0 0 -1 1 0 0 0 1 -1 0 1 -1 0 1 -1 0 0 -1 1 -1 1 0)
去VB后:01 00 00 10 01 00 00 00 00 10 00 01 10 00 01 00 00 00 00 01 10 01 00
(去掉V和B的编码,此处00代表0,01代表1,10代表0)
解码后:1 0 0 1 1 0 0 0 0 1 0 1 1 0 1 0 0 0 0 1 1 1 0
其顶层模块如下:
仿真波形图如下:
五:附录
VHDL版本编码
1.加V:
library ieee;
use ieee.std_logic_1164.all;
entity AddV is
port(Clk:in std_logic;Data_In:in std_logic;Data_OutV:out std_logic_vector(1 downto 0));
end AddV;
architecture AddV of Addv is
--signal Count0:std_logic_vector(0 to 1);
signal Count0:integer range 0 to 3;
begin
process(Clk)
begin
-- wait until Clk='1' and Clk'event;
if( Clk='1' and Clk'event)then
if(Data_In='1')then
Data_OutV<="01";
Count0<=0;
elsif(Data_In='0')then
Count0<=Count0+1;
if(Count0=3)then
Count0<=0;
Data_OutV<="11";
else Data_OutV<="00";
end if;
end if;
end if;
end process;
end AddV;
2.加B:
library ieee;
use ieee.std_logic_1164.all;
entity AddB is
port(Clk:in std_logic;Data_In:in std_logic_vector(1 downto 0);
Data_OutB:out std_logic_vector(1 downto 0));
end AddB;
architecture AddB of AddB is
type array_4 is array(3 downto 0) of std_logic_vector(1 downto 0);
signal FirstV:std_logic;
signal Buf:array_4;
signal Count_Even:integer range 0 to 1;
begin
ShuChu:process(Clk)
begin
if( Clk='1' and Clk'event)then
if(FirstV='1' and Count_Even=0 and Buf(0)="11")then
Data_OutB<="10";
else Data_OutB<=Buf(3);
end if;
-- Data_OutB<=(FirstV='1'&&Count_Even=0&&Buf[0]="11")?"10":Buf(3);
end if;
end process ShuChu;
Buff:process(Clk)
begin
if( Clk='1' and Clk'event)then
Buf(0)<=Data_In;
Buf(1)<=Buf(0);
Buf(2)<=Buf(1);
Buf(3)<=Buf(2);
end if;
end process Buff;
Count:process(Clk)
begin
if( Clk='1' and Clk'event)then
if("01"=Buf(3))then --here we judge the high bit of --Buffer,because Data_OutB
Count_Even<=Count_Even+1; --is usually assigned as --Buffer[3];
elsif("11"=Buf(0))then --here we judge the low bit of Buffer,because if Buffer[0]
Count_Even<=0; --is 2'b11.then --Buffer[1],Buffer[2],Buffer[3] is 2'b00,so we
end if; --should not conut Buffer[3];
end if;
end process Count;
JudgeV:process(Clk)
begin
if( Clk='1' and Clk'event)then
if("11"=Buf(0)) then --Judeg if it is the first coming of 'v',if it --is ,hten FisrtV is 1;
if('0'=FirstV)then
FirstV<='1';
end if;
end if;
end if;
end process JudgeV;
end Addb;
3.单双极性转换:
library ieee;
use ieee.std_logic_1164.all;
entity Polar is
port(Clk:in std_logic;Data_In:std_logic_vector(1 downto 0);
Hdb3:out std_logic_vector(1 downto 0));
end Polar;
architecture Polar of Polar is
signal FlagP:integer range 0 to 1;
begin
process(Clk)
begin
if(Clk='1' and Clk'event)then
if("01"=Data_In or "10"=Data_In)then
FlagP<=FlagP+1;
if(1=FlagP)then
Hdb3<="10";
else Hdb3<="01";
end if;
elsif("11"=Data_In) then
if(1=FlagP)then
Hdb3<="01";
else Hdb3<="10";
end if;
elsif("00"=Data_In)then
Hdb3<="00";
end if;
end if;
end process;
end Polar;
VHDL版本解码
1.去V和B:
library ieee;
use ieee.std_logic_1164.all;
entity AbandonVB is
port(Clk:in std_logic;Hdb3:in std_logic_vector(1 downto 0);
Data_OutP:out std_logic_vector(1 downto 0));
end AbandonVB;
architecture AbandonVB of AbandonVB is
type array_5 is array(4 downto 0) of std_logic_vector(1 downto 0);
shared variable Buf:array_5;
shared variable Count_0:integer range 0 to 3;
begin
process(Clk)
begin
if(Clk='1' and Clk'event)then
Buf(4):=Buf(3);
Buf(3):=Buf(2);
Buf(2):=Buf(1);
Buf(1):=Buf(0);
Buf(0):=Hdb3;
if("00"=Buf(0))then
Count_0:=Count_0+1;
elsif(2=Count_0 and ("00"=(Buf(0) xor Buf(3))))then
Buf(3):="00";
Buf(0):="00";
Count_0:=0;
elsif(3=Count_0 and ("00"=(Buf(0) xor Buf(4))))then
Buf(0):="00";
Count_0:=0;
else Count_0:=0;
end if;
Data_OutP<=Buf(4);
end if;
end process;
end AbandonVB;
2.双单极性转换:
library ieee;
use ieee.std_logic_1164.all;
entity Polar is
port(Clk:in std_logic;Data_In:in std_logic_vector(1 downto 0);
True_Code:out std_logic);
end Polar;
architecture Polar of Polar is
begin
process(Clk)
begin
if(Clk='1' and Clk'event)then
if("10"=Data_In or "01"=Data_In)then
True_Code<='1';
elsif("00"=Data_In)then
True_Code<='0';
end if;
end if;
end process;
end Polar;
六:总结
通过fpga仿真实现hdb3编码解码,感受到了verilog和VHDL的使用不同,VHDL语法比较严格,难弄,个人还是喜欢潇洒的verilog风格些。
用户377235 2013-6-15 15:32
用户377235 2013-6-2 18:35
用户377235 2012-11-4 09:47
不过,正好课设是这个题目,学习了
xucun915_925777961 2011-6-1 16:29