PRBSをVerilogで作る

PRBSをVerilogで作る

 PRBSをVHDLとVerilogで設計します。CCITT 0.153やITU-T勧告 V.52などで使われているPRBS9を実際に設計してその手法を学びます。

 PRBSの生成多項式は次のように記されています。

G(y) = X9+X5+1

したがって、回路図で示すと次のようになります。

 この回路をそのまま作成しても面白くないので、PRBS3とPRBS15を切り替えられるように作ります。

PRBS3を規格として使う事はほとんどありませんし、例えば伝送路のBERを測る時に使うという事もありません。したがってPRBS3を解説している本もほとんど無いと思いますが、じつはPRBS3は自分で検証する際のデータとして非常に便利です。それは、PRBS3は7ビット周期になるので、データを見ただけで誤りを目視できるからです。たとえば、オシロスコープ等でデータを1ショットで捉えた時に、7ビット程度であれば、すぐに検証できます。これが128ビット等になると1ショットを取ったとしても巡回の始めを探すのに苦労します。なので、私はPRBSを使うときに、まずはPRBS3で大まかな動作を確認してから、PRBS9やPRBS15を使ってBER測定するようにしています。

//  TITLE:					"prbs.v"
//  MODULE NAME:			
//  PROJECT CODE:			
//  AUTHOR:					 (****@nakaharagiken.com)
//  CREATION DATE:			2020.8.7
//  SOURCE:            		
//  LICENSE:           		Copyright (c) 2020 nakaharagiken All rights reserved. 
//  DESCRIPTION:            PRBSジェネレータ
//  NOTES TO USER:			
//  SOURCE CONTROL:			
//  REVISION HISTORY:  	    v0.1	2020.8.7	First edition
//
//
//        1->2->3->4->5->6->7->8->9->10->11->12->13->14->15
//      -------------
//     |     |      |
//     ->0->+->1->2->												PRBS3 = x^3+x^2+1
//
//        1->2->3->4->5->6->7->8->9->10->11->12->13->14->15
//      ==============------------
//     |              |           |
//     ->0->1->2->3->4->5->6->7->8|									PRBS9 = x^9+x^5+1
//
//      ============================================-----
//     |                                            |   |
//     ->0->1->2->3->4->5->6->7->8->9->10->11->12->13->14			PRBS15 = x^15+x^14+1
//
`timescale 1ns/1ps

module prbs(
	clock,
	nreset,						// (i)	非同期リセット							~~reset~~|__normal__
	enable,						// (i)	シリアルクロック同期イネーブル			~~enable~~|__disable__
	mode,						// (i)	00=off, 01=PRBS3, 10=PRBS9,11=PRBS15
	prout						// (0)	出力
);

input			clock;
input			nreset;
input			enable;
input	[1:0]	mode;			// 00=off, 01=PRBS3, 10=PRBS9,11=PRBS15
output			prout;


//シフト・レジスタ
reg [14:0] 	shift_r;
reg			rprbsout;

reg	[2:0]	PRBS_S;

wire		wprbs3;
wire		wprbs9;
wire		wprbs15;

// 各モード別タップ位置
assign	wprbs3 = shift_r[2] ^ shift_r[1];
assign	wprbs9 = shift_r[8] ^ shift_r[4];
assign	wprbs15 = shift_r[14]	^ shift_r[13];

//モード選定
assign	wprbs = (mode == 2'b00) ?	1'b0:				// モード選択
				(mode == 2'b01) ?	wprbs3:
				(mode == 2'b10) ?	wprbs9:
									wprbs15;

///////////////////////////////////////////////////
// シフトレジスタの生成
///////////////////////////////////////////////////
always @ (posedge clock, negedge nreset) begin
	if (!nreset) begin
		shift_r <= 16'b1111_1111_1111_1111;				// 初期値
	end else begin
		if (mode == 2'b00) begin						// OFFの時にはシフトレジスタを止める
			shift_r <= 16'b1111_1111_1111_1111;			// 初期値
		end else begin
			if (enable == 1'b1) begin
				shift_r[14] <= shift_r[13];
				shift_r[13] <= shift_r[12];
				shift_r[12] <= shift_r[11];
				shift_r[11] <= shift_r[10];
				shift_r[10] <= shift_r[9];
				shift_r[9] <= shift_r[8];
				shift_r[8] <= shift_r[7];
				shift_r[7] <= shift_r[6];
				shift_r[6] <= shift_r[5];
				shift_r[5] <= shift_r[4];
				shift_r[4] <= shift_r[3];
				shift_r[3] <= shift_r[2];
				shift_r[2] <= shift_r[1];
				shift_r[1] <= shift_r[0];
				shift_r[0] <= wprbs;
			end else begin
				shift_r <= shift_r;
			end
		end
	end
end


/////////////////////////////////////////////////
// データ取り出しタップの選択
/////////////////////////////////////////////////
always @ (posedge clock, negedge nreset) begin
	if (!nreset) begin
		rprbsout <= 0;								// 初期値
	end else begin
		if (enable == 1'b1) begin					
			case (mode)
				2'b00	:	rprbsout <= 1'b0;			// OFF
				2'b01	:	rprbsout <= shift_r[2];		// PRBS3
				2'b10	:	rprbsout <= shift_r[8];		// PRBS9
				default	:	rprbsout <= shift_r[14];	// PRBS15
			endcase
		end
	end
end


assign prout = rprbsout;

endmodule


ソースコードは大きく2つに分かれています。PRBS用のシフトレジスタと出力信号の取り出しです。69行目からのブロックがPRBS用のシフトレジスタです。今回はPRBS15までを扱うので、15ビットのシフトレジスタになっています。shift_r[14:0] <= {shift_r[13:1],wprbs};のように書く事もできます。PRBSのモードを切り替えた時に、取り出すタップ位置を数えるのに、並べて書いた方が分かりやすいので、このように全ビットを記述しました。また、PRBSの初期値は71行目で電源投入時を設定していますが、モードを00にした時にも初期値で停止するようにしています。

 PRBSは冒頭で示したようなブロック図でXORを使って巡回しますが、G(y)に相当するのがソースコードのwprbsです。56行目を見てください。ここでPRBS3、PRBS9、PRBS15の各々のG(y)を求めています。そして、61行目で各モードを切り替えています。切り替え信号を作る時に、Verilogでは61行目のようにassignを使って切り替えると便利です。

 103行目からは出力信号の取り出し位置(タップ)を切り替えています。ここも61行目のようにassignを使って切り替えても良いのですが、下図のようにシフトレジスタの後に赤枠で示したように出力選択用にスイッチが入ります。

 このスイッチの遅延が生じますので、DFFを一つ追加してから出力するようにしています。最後に、テストベンチを記載しておきます。

//  TITLE:					"TB_prbs.v"
//  MODULE NAME:			TB_prbs
//  PROJECT CODE:			
//  AUTHOR:					 (****@nakaharagiken.com)
//  CREATION DATE:			2020.8.8
//  SOURCE:            		
//  LICENSE:           		Copyright (c) 2020 nakaharagiken All rights reserved. 
//  DESCRIPTION:            prbs.vのテストベンチ
//  NOTES TO USER:			
//  SOURCE CONTROL:			
//  REVISION HISTORY:  	    v0.1	2020.8.8	First edition
//
//

`timescale 1ns/1ps

module TB_prbs;

parameter SYSCLK_PERIOD = 100;		// 10MHz

reg			i_clock;
reg			i_nreset;				// (i)	非同期リセット							~~reset~~|__normal__
reg			i_enable;				// (i)	シリアルクロック同期イネーブル			~~enable~~|__disable__
reg	[1:0]	i_mode;					// (i)	00=off, 01=PRBS3, 10=PRBS9,11=PRBS15
wire		o_prout;				// (0)	出力

//////////////////////////////////////////////////////////////////////
// Clock Driver
//////////////////////////////////////////////////////////////////////
always @(i_clock)
    #(SYSCLK_PERIOD / 2.0) i_clock <= !i_clock;


//////////////////////////////////////////////////////////////////////
// Main
//////////////////////////////////////////////////////////////////////
initial
begin

	i_clock <= 0;
	i_nreset <= 0;									//	初期値
	i_enable <= 0;
	i_mode <= 2'b00;

	repeat(50) @(posedge i_clock);
	i_nreset <= 1;
	repeat(100) @(posedge i_clock);

	i_enable <= 1'b1;
	i_mode <= 2'b00;
	repeat(100) @(posedge i_clock);
	i_enable <= 1'b0;
	repeat(5) @(posedge i_clock);
	i_enable <= 1'b1;
	repeat(5) @(posedge i_clock);
	i_enable <= 1'b0;
	repeat(5) @(posedge i_clock);
	i_enable <= 1'b1;
	repeat(100) @(posedge i_clock);

	i_enable <= 1'b0;
	i_mode <= 2'b00;
	repeat(5) @(posedge i_clock);

	i_mode <= 2'b01;
	i_enable <= 1'b1;
	repeat(100) @(posedge i_clock);

	i_enable <= 1'b0;
	repeat(5) @(posedge i_clock);
	i_enable <= 1'b1;
	repeat(5) @(posedge i_clock);
	i_enable <= 1'b0;
	repeat(5) @(posedge i_clock);
	i_enable <= 1'b1;
	repeat(100) @(posedge i_clock);

	i_enable <= 1'b0;
	i_mode <= 2'b00;
	repeat(5) @(posedge i_clock);

	i_mode <= 2'b10;
	i_enable <= 1'b1;
	repeat(100) @(posedge i_clock);

	i_enable <= 1'b0;
	repeat(5) @(posedge i_clock);
	i_enable <= 1'b1;
	repeat(5) @(posedge i_clock);
	i_enable <= 1'b0;
	repeat(5) @(posedge i_clock);
	i_enable <= 1'b1;
	repeat(100) @(posedge i_clock);

	i_mode <= 2'b11;
	i_enable <= 1'b1;
	repeat(100) @(posedge i_clock);

	i_enable <= 1'b0;
	repeat(5) @(posedge i_clock);
	i_enable <= 1'b1;
	repeat(5) @(posedge i_clock);
	i_enable <= 1'b0;
	repeat(5) @(posedge i_clock);
	i_enable <= 1'b1;
	repeat(100) @(posedge i_clock);


end


prbs inst_prbs(
	.clock		(i_clock),
	.nreset		(i_nreset),						// (i)	非同期リセット							~~reset~~|__normal__
	.enable		(i_enable),						// (i)	シリアルクロック同期イネーブル			~~enable~~|__disable__
	.mode		(i_mode),						// (i)	00=off, 01=PRBS3, 10=PRBS9,11=PRBS15
	.prout		(o_prout)						// (0)	出力
);

endmodule