文章目录
前言
在上一篇有关hdb3 的编码和译码的介绍中,简单介绍了hdb3的编码规则,以及使用MATLAB进行了仿真实验。感兴趣的朋友可以去看一下。HDB3 的编码与译码 ①(MATLAB 实现)2021-9-11。仿真实验过后,就要在FPGA中实现,通过这次学习也是对FPGA的相关知识有了进一步的了解。下面也是介绍了相关部分的设计,主要的参考资料是:HDB3码编码器及解码器verilog代码编程及实现这个课件给出了详细的设计流程,编码设计主要参考了这个课件的介绍。
本篇文章也是介绍了编码的实现过程。译码过程在下一篇文章中介绍。
一、实现HDB3编码步骤
梳理好完善逻辑就是成功的一半,要实现的HDB3的编码不可操之过急。上述课件中,给出了详细的过程以及相应的代码。
在来哦哦饥饿这个编程最初,我一直也不是很理解hdb3编码中的V还有B要用什么代替,以及最后的正负极性,要如何处理?这些问题都在课件中给出了相应的解释。
跟做MATLAB的仿真一样,在编码之中一共有四中符号:1,0,B,V。那就用一个两位的寄存器来分别表示就可以了。
0:00
1:01
B:10
V:11
这就是开始的“原材料”准备。下面开始分模块。
主要的流程有三个:
第一步:插入符号V。
第二步:插入符号B。
第三步整体统一改变极性。并且将代表各符号的值统一成0,+1和-1。
分好了模块,就可以一个模块一个模块的进行设计。
1. 插入V模块
第一个模块是插V模块。在这个模块中,要将输入进来的单极性信号转变为两位的信号,同时在固定格式下插上V。那么固定格式是什么格式那?在HDB3的编码规则中,不允许有超过三个0连续,如果有就将第四个0转变成V那么这第四个零就是要插V的位置了。
首先先来完成逻辑框图。
整体的逻辑就是进行判断分类,再分类。
module add_v
(
Clk,Rstn_,
DataIn,DataOut
);
input Clk;
input Rstn_;
input DataIn;
output [1:0] DataOut;
reg [1:0] DataOut;
reg [1:0] count; //连“0”计数器 最多就会有4个 0
always @ (posedge Clk or negedge Rstn_)
if (!Rstn_)
begin
count <= 2'd0;
DataOut <= 2'b00;
end
else
begin
if (DataIn == 1'b1) //如果收到的是 1
begin
count <= 2'd0;
DataOut <= 2'b01; //先不考虑极性
end
else
begin
count <= count + 1'b1;
if (count == 2'd3) // 连 0 达到 4个 输出 “V” 11
begin
count <= 2'd0;
DataOut <= 2'b11;
end
else
DataOut <= 2'b00; //连 0 未达到 4个 输出 00
end
end
endmodule
简单看一下仿真结果:
可以看到在仿真结果图中,开始端的01前面有一小段00,那是因为在时钟的上升沿手到达之前,编码的值就是00。从而可以的出一个结论,在实际使用hdb3编码模块的时候应当加一个使能位,使用的顺序应当是先给好数据,随后快速地使能模块,再进行编码,这样保证整体的编码质量。
2. 插入B模块
插入B模块可以说是整个编码的核心了,还是一样捋一下逻辑。什么时候才会插入B那?当两个V之间的1的个数为偶数个时,就需要将后面那个V前面的三个0中的第一个0转变成B。
这其中有几个很重要的点:
第一个点就是要检测V,同时转变的是V之后的第四个值,所以要设计一个四位的移位寄存器。这样才能保证,既能看到前面又能变后面。
第二个点就是偶数这个问题要怎么处理,如何判断1的个数是奇数还是偶数那?这里的解决方案非常牛逼,我们是不知道两个V之间是会有多少个1的,有可能很多,也有可能很少,所以不可能将每一个1都计上去,这里就利用了寄存器会溢出的特性。
定义一个一位的寄存器,有一个奇数个1,寄存器的值就是1。有偶数个1,寄存器的值就是0;
/**
时间:2021年9月9日
文件名:add_B.v
所属项目:hdb3 编解码
顶层模块:hdb3.v
模块名称及其描述:hdb3 编码过程中实现插入 “B” 的操作
修改记录:无
*/
module add_B
(
Clk,Rstn_,
DataIn_B,DataOut_B
);
input Clk;
input Rstn_;
input [1:0] DataIn_B;
output [1:0] DataOut_B;
/****************V和1的计数器设计************************/
reg count_1; //对 “1” 的计数器
reg [1:0] count_V; //对 “V” 的计数器
always @ (posedge Clk or negedge Rstn_)
if (!Rstn_)
begin
count_1 <= 1'b0;
count_V <= 1'b0;
end
else
begin
if(data[0] == 2'b11) //输入进来的是 V 将1的计数器清零,同时将V的计数器+1
begin
count_1 <= 1'b0;
count_V = count_V + 1'b1;
end
else if(data[0] == 2'b01) //输入进来的是 1 //1的计数器加1
begin
count_1 <= count_1 + 1'b1;
if(count_V == 2'd2)//当这个V当过后边的V时,重新将它变成前面的V
count_V <= 1'b1;
end
else //输入进来的是 0
begin
if(count_V == 2'd2)//当这个V当过后边的V时,重新将它变成前面的V
count_V <= 1'b1;
end
end
/****************四位的移位寄存器************************/
reg [1:0] data[3:0];
reg [1:0] i;
always @ (posedge Clk or negedge Rstn_)
if (!Rstn_)
begin
// for (i=0; i<3; i=i+1)
// data[i] <= 2'b0;
end
else
begin
data[3] <= data[2];
data[2] <= data[1];
data[1] <= data[0];
data[0] <= DataIn_B;
end
/***************数据输出端口的设计*************************/
//count_1==0 && count_V==1 表示 1的个数是偶数个
//data[0]==2'b11 表示偶数个的个数是在两个 V 之间数出来的
assign DataOut_B = (count_1==0)&&(count_V==1)&&(data[0]==2'b11)? 2'b10:data[3];
endmodule
前文给出的资料中,PPT中的例程代码是有一个小小的bug在这里做了订正。他的bug是,如果源码的开始是110000…也就是开始就会有偶数个1。那么他也会在第一个V的前面加上B,这是不合理的。这里给出了修正。
现在来分析一下每个计数器的工作逻辑。
可以看到只有在两个V之间1的个数为偶数个的时候才会插入一个B。
看一下仿真的结果:
由于四位移位寄存器的存在,所以输出会滞后四个钟。同时还要注意的是,第一个B应该是0,也是PPT中的bug。
3. 整理极性
通过前两部的整理,信号源码已经整理成,00,01,10,11了,也就是0,1,B,V。这里开始整理极性,同时在整理完极性之后的输出应该整理为0,+1,-1。这里用00来代表0,用01来代表+1,用10来代表-1;
极性的变化规则是:
1.1和 B看成一组,正负交替变化,一般第一个1的极性是负号。
2.V的极性看成一组,正负交替变化,第一个V的极性和前一个非0符号的极性相同。
这里对B模块输入进来的数据进行判断整理即可。还是简单的画一个逻辑图。
极性的整体统一还有另一个功能,就是产生脉冲,当然输出的数据两位也可以当做正负脉冲来使用,这里还是使用了两个端口来产生正负脉冲。
整体的代码:
module polar
(
Clk,Rstn_,
DataOut_B,PolarOut,
BP,BN
);
input Clk;
input Rstn_;
input [1:0] DataOut_B;
output [1:0] PolarOut;
//设计正脉冲和负脉冲
output BP; // 正脉冲
output BN; // 负脉冲
reg [1:0] PolarOut;
reg BP;
reg BN;
/****************极性翻转************************/
reg even = 0;
always @ (posedge Clk or negedge Rstn_)
if (!Rstn_)
begin
end
else
begin
if( (DataOut_B == 2'b01) || (DataOut_B == 2'b10) ) //输入为 1 或者 B 两者翻转
begin
if(even == 1) //将初始位设计成 -1
PolarOut <= 2'b01;
else
PolarOut <= 2'b10;
even <= ~even; //将标识位翻转
end
else if(DataOut_B == 2'b11) //输入进来的是 V
begin
if(even == 1)
PolarOut <= 2'b10;
else
PolarOut <= 2'b01;
end
else //输入为 00
PolarOut <= 2'b00;
end
/****************极性翻转************************/
always @ (posedge Clk or negedge Rstn_)
if (!Rstn_)
begin
end
else
begin
if(PolarOut == 2'b01) //输入为 +1
begin
BP <= 1'b1;
BN <= 1'b0;
end
else if(PolarOut == 2'b10) //输入为 -1
begin
BP <= 1'b0;
BN <= 1'b1;
end
else
begin
BP <= 1'b0;
BN <= 1'b0;
end
end
endmodule
其中主要注意的点就是1和V的极性,他们各自是交替变化,但同时第一个V的极性又和前一个1的极性有关系(这里的1包含B),这里就是用一个寄存器even来控制整体的极性。来看一下仿真结果。
和自己手算的结果比较一下,可以得出相应的结果。
二、 总结
通过对hdb3的编码,通过这个小课题使得自己认识到,基本还是比较差,另外也产生另一个问题,就是不会使用相应的仿真工具也就是ModelSim,之前使用的一直是逻辑分析仪,没有使用过这种,数据量的仿真,都是跑跑时序,现阶段也是总结出来最适合我的开发方法,如果要调试时序类的程序,跑跑时序什么的有条件就可以用逻辑分析仪,要是处理数据编码什么的 程序还是需要到仿真环境ModelSim。积累吧,积累多了自然就会了。