The ITU-T O.150 standard defines several methods of generating pseudorandom binary sequence meeting maximum sequences of zeros or ones. These are used for testing digital transmission equipment. I found a messy Xilinx implementation (v1.0) in XAPP1240 and I felt there was a more general and succinct implementation with no need for a checker, so I've reinvented it. Subsequently, I found a significantly cleaned up v1.1, but felt opportunities had been missed and an explanation would be helpful. It is essentially a linear feedback shift register, but producing multiple bits of PRBS per clock cycle in a similar way to the Polynomial divisions used for CRC checking.
PRBS Generator

The figure above shows an external LFSR, that is where the logic to implement the polynomial taps is outside the shift register path. A generic polynomial can be applied either as an input port or more usually as a compile time generic. When using the latter, constant propagation will minimise away all the AND gates. Depending on the polynomial tap bit, each AND gate will become either a wire ('1') or a disconnection ('0'), and the XOR gate tree will be be pruned accordingly.

In order to calculate multiple output bits per clock cycle, we must step the shift register multiple times per clock cycle, which means the results of the logic are not always taken from the flip-flop outputs, but from the flop inputs, i.e. from the unregistered nets. To achieve this in VHDL a loop is used to update a variable holding the accumulated LFSR state so far, and peeling off one bit per iteration to pass to the data_out port as it also forms the new LSB of the LFSR register.
library ieee;
use ieee.std_logic_1164.all;
entity prbs_generator is
generic (
inv_pattern_g : boolean := false;
-- NB. Vector direction 'to' or 'downto' does not matter.
-- 9876543210
poly_g : std_ulogic_vector := "100010000";
data_width_g : positive := 4
);
port (
clk : in std_logic; -- System clock
reset : in std_logic; -- Sync reset active high
enable : in std_logic; -- Enable pattern generation
data_out : out std_logic_vector(data_width_g-1 downto 0) -- Generated PRBS pattern
);
end entity;
architecture rtl of prbs_generator is
signal reg : std_logic_vector(poly_g'length-1 downto 0) := (others => '1');
begin
process (clk)
variable reg_v : std_logic_vector(reg'range);
begin
if rising_edge(clk) then
if reset = '1' then
reg <= (others => '1');
data_out <= (others => '1');
elsif enable = '1' then
reg_v := reg;
for i in data_out'reverse_range loop
reg_v := reg_v(poly_g'length-2 downto 0) & (xor(reg_v and poly_g));
data_out(i) <= not reg_v(0) when inv_pattern_g else reg_v(0);
end loop;
reg <= reg_v;
end if;
end if;
end process;
end architecture;
Note that the pictures above are drawn in reverse to the code because circuit diagrams have flip-flops transfering D to Q in the left to right direction. The example code is written so that the polynomial is specified with the highest power of 2 (xn MSB) on the left and the lowest (x0 = 1, LSB) on the right. Hence the above diagrams need flipping horizontally when comparing against the VHDL code.
ITU-T Polynomials
Polynomial length | Polynomial tap | Invert pattern? | Number of stages | Bit sequence length | Max 0 sequence |
---|---|---|---|---|---|
7 | 6 | false | 7 | 127 | 6 |
9 | 5 | false | 9 | 511 | 8 |
11 | 9 | false | 11 | 2047 | 10 |
15 | 14 | true | 15 | 32767 | 15 |
20 | 3 | false | 20 | 1048575 | 19 |
23 | 18 | true | 23 | 8388607 | 23 |
29 | 27 | true | 29 | 536870911 | 29 |
31 | 28 | true | 31 | 2147483647 | 31 |
The next issue I was keen to solve was the application of the ITU-T standard polynomials. I felt the Xilinx implementation left the setting of multiple generics to the user when they could be set far more simply by indicating whish row of the above table should be used.
The itu_prbs_generator component instantiates the more general LFSR-based PRBS generator, but customises it with one of the ITU-T polynomials. Just select which row number from the table below, or array constant index in itu_t_o150_c, to use for the itu_prbs_generator. The table row is passed to a function to create the expanded polynomial vector which is used for the poly_g generic in the code above. The need for inversion of the output is handled in a similar way so that it is correct for the required polynomial. This saves misconfiguring the parameters. Or you can re-use the more general prbs_generator with your own desired polynomial for a totally different purpose. This separates two concerns, the re-usability apsect of code and the customisation of it to a given standard.
PRBS Checker
The XAPP1240 code also includes a PRBS checker for the receiver to verify transmission bit errors. I find this redundant and unecessary.

As the code is written by Xilinx, the transmitted data sequence is presented to data_in. As this should be the same sequence of values as created by the LFSR logic, they feed this into the shift register instead of the feedback. Then Xilinx create a new version of the expected value from the shift register such that data_out is the same as data_in, then compare the values (with XOR).
This is all works just fine. However it seems unecessary work because you do not need a second component for the checker at all. In fact they roll the generator and checker into a single component that does both when instantiated and setup correctly. Again, extra complication which could see incorrect implementation. But my main concern about the checker they provide is the "error extension" caused by feeding a bad bit from data_in into the shift register. The error will affect the generated data_out until it is shifted out the other end because the whole register feeds the combinatorial logic creating data_out. It also means the comparison of received data to locally generated data will not reflect the bit position of any transmissions error in a multi-bit stream.

Better surely to recreate a local copy of the expected pattern and simply compare? Now any differences directly shows the bit offset of the transmission error in a multi-bit data stream. Only the generator needs to be coded, and the checker variant either not coded or not bundled into the same PRBS component (with either a checker flag in the generic or on a port). Simpler and less easy to setup wrong.
Conclusions
I believe I have provided a simpler implementation of the VHDL for the PRBS generation component, and removed complications for the checking at the receiver.