扰码可以对原始的用户数据进行扰乱,得到随机化的用户数据。发送电路在发送数据前先对数据进行随机扰乱,接收电路使用相同的扰乱算法重新恢复出原始的数据。
什么是扰码与解扰
扰码器使用LFSR实现,用来产生伪随机比特序列,它和串行输入的数据进行异或,从而实现对输入数据的随机化。正如我们在LFSR部分讨论过的,伪随机序列也是周期重复的,其周期长度取决于LFSR中触发器的级数和所选择的多项式。接收电路本地有一个和发送电路中相同的伪随机序列产生器,它产生的数据与接收数据进行异或,可以恢复出发端原始的串行数据。
这里用到了一个逻辑运算表达式:如果A^B=C,那么C^B=A,此处A为原始数据,B为扰码器的输出,C为扰码后的数据。如图6.4所示。
扰码的作用
扰码有以下两个作用:
(1)扰码可以使重复的数据图案的频谱被展宽。例如,在数据流中重复出现序列10101010,这会导致高频离散频谱的出现从而产生较强的EMI(电磁干扰)。当进行扰码后,该数据被随机化,EMI噪声会大大减弱。
(2)扰码的另一用处是减少并行线路中的串扰。扰码可以使功率谱分布更为平滑和均匀,从而降低高频串扰。
串行扰码器
结合LFSR及其给出的多项式可以方便地设计串行扰码器。对于串行扰码器,一个时钟周期只有1比特的用户数据到达,每个时钟上升沿之后输出一位经过扰码后的数据,同时LFSR内部触发器的值被更新。然而,很多时候在一个时钟周期内到达多个比特的数据,此时我们需要设计并行扰码器,它可以在一个时钟周期内输入和输出多位数据。
并行扰码器
对并行数据加扰,遵循和串行加扰同样的算法。以每个时钟周期到达8位并行数据为例,LFSR伪随机序列产生器需要在每个时钟周期内产生8位随机数,同时扰码器在每个时钟周期内产生8位扰码后的随机数据。我们可以假定有一个8倍于当前并行数据工作时钟的虚拟时钟,在8个虚拟时钟周期之后,LFSR伪随机序列产生器可以产生8位数据(注意,LFSR伪随机序列产生器的输出与当前输入数据是无关的,与寄存器的当前状态有关),这8位数据与输入的8位原始数据进行异或,就可以得到并行扰码的最终结果。在后面的部分中,我们将对PCIe专用扰码器的实现进行讨论。并行扰码技术同样适用于16比特或32比特的并行数据。
扰码电路设计要点
我们讨论了如何对串行数据及并行数据进行加扰处理,然而,为了实现扰码电路,还需要注意以下三个要点。
(1)扰码器初始化
发送电路和接收电路必须可以独立地对扰码器和解扰器进行初始化,否则二者就不能实现同步,从而接收电路也无法恢复出原始数据。PCIe中使用了一个名为COM的字符,发送电路和接收电路都可以识別该字符,并在收到该字符后将电路中的扰码器置为预先约定的相同的初始始值。这些COM字符被周期性地发送,使得收发双方能够同步或者对LFSR进行周期性的初始化。
(2)扰码器暂停
正常工作时,LFSR内部触发器的值在每个时钟周期都会进行更新,然而,LFSR应该可以被暂停更新。例如,在PCIe中,数据流中会添加或删除SKIP字符,并且SKIP字符的数量在中间处理过程中还可能发生变化。无论是发送电路还是接收电路,SKIP字符都是不需要进行扰码和解扰处理的,因此扰码电路和解扰电路应该可以在这些字符出现时进入“暂停”状态,“跳过”对它们的处理。
(3)扰码器去使能
扰码器还应该可以工作在LFSR内部寄存器不断更新,但不产生有效输出的状态。例如,在PCIe中,训练字符(TS1/TS2)未被加扰,但LFSR内部仍能不断更新。
PCIe扰码电路
PCIe扰码器(如图6.5所示)是一个16位LFSR多项式如下:
- ,它有16个LFSR触发器,图6.5是其具体电路;
- COM字符将LFSR初始化为16’hFFFF;
- SKP字符可以令其工作暂停。
PCIe扰码器工作波形如图6.6所示。
Verilog RTL-PCIe扰码器
// 每时钟周期处理8比特并行数据
module scrambler_8bits
(clk,
rstb,
data_in,
k_in,
disab_scram,
data_out,
k_out);
//****************************************************************
input clk;
input rstb;
input [7:0]data_in; // input data to be scrambled
input k_in; // when 1, the input is a control character.
// when 0, the data is regular data
input disab_scram; // when 1 scrambling is disabled ,
output [7:0] data_out; // scrambled data output
output k_out; // when 1 the output is a control character.
//****************************************************************
localparam LFSR INIT = 16‘hFFFF;
reg [15:0] lfsr, lfsr_nxt;
wire [15:0] lfsr_int;
wire initialize_scrambler, pause_scrambler;
reg [7:0] data_out, data_out_nxt;
wire [7:0] data_out_int;
/* First find the equations for the LFSR flops. Since there are 8 bits of data coming input, the LFSR flops value moves 8 times (as there is an imaginary clock running 8 times faster). Find the intermediate value. Refer to PCIe sepe for the following algorithm */
assign lfsr_int[0] = lfsr[8];
assign lfsr_int[l] = lfsr[9];
assign Ifsr_int[2] = lfsr[10];
assign lfsr_int[3] = lfsr[8] ^ lfsr[ll];
assign lfsr_int[4] = lfsr[8] ^ lfsr[9] ^ lfsr[12];
assign lfsr_int[51 = lfsr[8] ^ lfsr[9] ^ lfer[10] ^ lfsr[13];
assign lfsr_int[6] = lfsr|9] ^ lfsr[10] ^ lfsr[l1] ^ lfsr[14];
assign lfsr_int[7] = lfsr[10]^ lfsr[ll] ^ lfsr[12] ^ lfsr[15];
assign lfsr_inr[8] = lfsr[0] ^ lfsr[l1] ^ lfsr[12] ^ lfsr[13];
assign lfsr_int[9] = lfsr[l] ^ lfsr[12] ^ lfsr[13] ^ lfsr|14];
assign lfsr_int[10] = lfsr|2] ^ lfsr|13] ^ lfsr[14] ^ lfsr[15];
assign lfsr_int[l1] = lfsr[3] ^ lfsr[l4] ^ lfsr[15];
assign lfsr_int[l2] = lfsr[4] ^ lfsr[15];
assign lfsr_int[13] = lfsr[5];
assign lfsr_int[14] = lfsr[6];
assign lfsr_int[15] = lfsr[7];
// now use the special handles to define lfsr_nxt[15:0]
//****************************************************************
assign initialize_scrambler = (data_in == 8'hBC) && (k_in == 1); //COM char
assign pause_scrambler = (data_in == 8'h1C) && (k_in == 1) // SKP char
always @(*)
begin
lfsr_nxt = lfsr;
if (disab_scram | pause_scrambler )
lfsr_nxt = lfsr;
else if (initialize_scrambler)
lfsr_nxt = LFSR_INIT;
else
lfsr_nxt = lfsr_int;
end
// flop inference
always @(posedge clk or negedge rstb)
begin
if(!rstb)
lfsr <= LFSR_INIT;
else
lfsr <= lfsr_nxt;
end
// Now we need to perform the XOR operation with the input data_in to derive
// scrambled data First derive data_outjnt[7:0]
//****************************************************************
assign data_out_int[0] = data_in[0] ^ lfsr[l5];
assign data_out_int[1] = data_in[1] ^ lfsr[l4];
assign data_out_int[2] = data_in[2] ^ lfsr[13];
assign data_out_int[3] = data_in[3] ^ lfsr[12];
assign data_out_int[4] = data_in[4] ^ lfsr[l1];
assign data_out_int[5] = data_in[5] ^ lfsr[l0];
assign data_out_int[6| = data_in[6] ^ lfsr[9];
assign data_out_int[7] = data_in[7] ^ lfsr[8];
always @(*)
begin
data_out_nxt = data_out_int;
if (disab_scram || k_in) // scrambling disabled or input control character
data_out_nxt = data_in;
else
data_out_nxt = data_out_int;
end
// flop inference
//****************************************************************
always @(posedge clk or negedge rstb)
begin
if(!rstb)
data_out <= 'd0;
else
data_out <= data_out_nxt;
end
endmodule
原出处:FPGA 的逻辑
作者:碎碎思
相关文章推荐
更多FPGA技术干货请关注FPGA 的逻辑技术专栏。