// // CBG Squanderer // (C) 2002 D J Greaves, University of Cambridge. // // Please use this source material as you wish provided author acknowledgment is preserved. // No warranty of correctness is implied and no liabilty for patent infringement (e.g. frame slip alignmnet) losses or other consequences is accepted. // Simple serialiser and deserialiser (SERDES) code in Verilog RTL. // Includes many classical paradigms: // Centre of eye detection using oversampling // NRZI encoding for polarity insensitivity // Frame alignment using an embedded pattern // Self-synchronous scrambling // Top-level for the transmitter unit. module MYSER_TX #(parameter pwidth = 32) ( input clk, input reset, output serout, input scrambler_enable, output baudclk, input [pwidth-1:0] parin ); wire parload; wire rz_pre_scramble, rz_post_scramble; reg [1:0] osr; assign baudclk = osr[1]; NRZI_ENC nrzi_enc( .clk(clk), .ce(bitstrobe), .reset(reset), .dout(serout), .din(rz_post_scramble)); SCRAMBLER_15 the_scrambler( .clk(clk), .ce(bitstrobe), .scrambler_enable(scrambler_enable), .reset(reset), .dout(rz_post_scramble), .din(rz_pre_scramble)); SERDIV #(pwidth+5) the_serdiv (.clk(clk), .ce(bitstrobe), .slip(0), .reset(reset), .parload(parload)); always @(posedge clk) if (reset) begin osr <= 0; end else osr <= osr + 1; assign bitstrobe = osr == 3; `ifdef GENDUMMYDATA // False traffic generator. reg [5:0] dummydata; always @(posedge clk) if (reset) begin dummydata <= 48; end else begin if (bitstrobe && parload) dummydata <= dummydata + 1; end wire [pwidth-1:0] pardata = { dummydata, parin[0] }; `else // !`ifdef GENDUMMYDATA wire [pwidth-1:0] pardata = parin; `endif SERSHIFTOUT #(pwidth) the_shiftout (.clk(clk), .ce(bitstrobe), .reset(reset), .serout(rz_pre_scramble), .din(pardata), .parload(parload)); endmodule // // // module MYSER_RX( input clk, input reset, input serin, input scrambler_enable, output perror, output in_synch, output [pwidth-1:0] parout ); parameter pwidth = 32; wire parload; wire rz_pre_scramble, rz_post_scramble; wire bitstrobe; wire serin_clean; RX_EYE_CENTERISER_OSF4 centeriser( .clk(clk), .reset(reset), .serout(serin_clean), .bitstrobe(bitstrobe), .serin(serin)); NRZI_DEC nrzi_enc( .clk(clk), .ce(bitstrobe), .reset(reset), .dout(rz_pre_scramble), .din(serin_clean)); DESCRAMBLER_15 the_scrambler( .clk(clk), .ce(bitstrobe), .scrambler_enable(scrambler_enable), .reset(reset), .din(rz_pre_scramble), .dout(rz_post_scramble)); wire slip; SERDIV #(pwidth+5) the_serdiv (.clk(clk), .ce(bitstrobe), .slip(slip), .reset(reset), .parload(parload)); wire faw_spotter; SERSHIFTIN #(pwidth) the_shiftin (.clk(clk), .ce(bitstrobe), .reset(reset), .serin(rz_post_scramble), .dout(parout), .par_error(perror), .faw_spotter(faw_spotter), .parload(parload)); // Synchro control. reg [1:0] hysteresis; reg [1:0] mode; always @(posedge clk) if (reset) begin hysteresis <= 0; mode <= 0; end else if (parload && bitstrobe) begin if (faw_spotter && hysteresis != 3) hysteresis <= hysteresis + 1; if (!faw_spotter && hysteresis != 0) hysteresis <= hysteresis - 1; if (mode == 0) begin if (faw_spotter) mode <= 1; end if (mode == 1) begin if (faw_spotter) mode <= 2; else mode <= 0; end if (mode == 2) begin if (hysteresis == 0) mode <= 0; end end assign slip = (mode == 0 && !faw_spotter); assign in_synch = (mode == 2); endmodule // Dual modulus divider. module SERDIV #(parameter ratio=32+5) (input clk, input ce, input reset, input slip, output reg parload); reg [7:0] counter; // Max pwidth is 255 then. always @(posedge clk) if (reset) begin counter <= 0; parload <= 0; end else if (ce) begin parload <= !counter; counter <= (counter == ratio-1) ? 0: (slip && counter == 4) ? counter + 2: counter+1; end endmodule module SCRAMBLER_15 // Self-synchronous PRBS scramber x^15+x^14+1 (input clk, input ce, input reset, input din, input scrambler_enable, output reg dout); reg [14:0] polyreg; wire sd = polyreg[14] ^ polyreg [13] ^ din; always @(posedge clk) if (reset) begin polyreg <= 0; end else if (ce) begin polyreg <= { polyreg [13:0], sd }; dout <= (scrambler_enable) ? sd: din; end endmodule // SCRAMBLER_15 module DESCRAMBLER_15 // Self-synchronous PRBS descramber scramber x^15+x^14+1 (input clk, input ce, input scrambler_enable, input reset, input din, output reg dout); reg [14:0] polyreg; wire taps = polyreg[14] ^ polyreg [13]; always @(posedge clk) if (reset) begin polyreg <= 0; end else if (ce) begin polyreg <= { polyreg [13:0], din }; dout <= (scrambler_enable) ? din ^ taps: din; end endmodule // SCRAMBLER_15 // Parallel to serial convertor. module SERSHIFTOUT #(parameter pwidth=32) (input clk, input ce, input reset, input [pwidth-1:0] din, input parload, output serout); reg [pwidth-1+5:0] shifter; wire [3:0] faw = 5; wire parity = (^(din)); always @(posedge clk) if (reset) begin shifter <= 0; end else if (ce) begin if (parload) shifter <= { din, parity, faw }; else shifter <= shifter >> 1; // Send lsb first end assign serout = shifter[0]; endmodule // SERIALISER // Serial to parallel convertor. module SERSHIFTIN #(parameter pwidth=32) (input clk, input ce, input reset, output [pwidth-1:0] dout, input parload, input serin, output faw_spotter, output reg par_error); reg [pwidth-1+5:0] shifter, broadside; assign faw_spotter = (broadside [3:0] == 4'd5); wire parity_received = broadside[4]; wire parity_measured = (^(dout)); assign dout = broadside >> 5; reg parload_r1; always @(posedge clk) if (reset) begin shifter <= 0; parload_r1 <= 0; par_error <= 0; end else if (ce) begin parload_r1 <= parload; shifter <= { serin, shifter[pwidth-1+5:1] }; // Rx lsb first if (parload) begin broadside <= shifter; end if (parload_r1) par_error = (parity_received != parity_measured); end assign serout = shifter[0]; endmodule // DESERIALISER // NRZI encoder - make a transition on a one, so that the channel is polarity insensitive. module NRZI_ENC(input clk, input ce, input reset, input din, output reg dout); always @(posedge clk) if (reset) begin dout <= 0; end else begin if (din && ce) dout <= !dout; end endmodule // NRZI_ENC // NRZI decoder - detect a transition as a one, so that the channel is polarity insensitive. module NRZI_DEC(input clk, input ce, input reset, input din, output reg dout); reg prev; always @(posedge clk) if (reset) begin prev <= 0; dout <= 0; end else if (ce) begin prev <= din; dout <= din ^ prev; end endmodule // NRZI_DEC // Unit to find best oversampling position. // (But no a DPLL in this version or full digital clock recovery) module RX_EYE_CENTERISER_OSF4 ( input clk, // 4 times oversampling clock input reset, input serin, output reg serout, output bitstrobe ); reg [1:0] osr_div; reg d0, d1; // Decision flops - metastability (not in theory, but for all practical design lifetimes). reg [4:0] shifter4; always @(posedge clk) if (reset) begin d0 <= 0; d1 <= 0; shifter4 <= 0; osr_div <= 0; end else begin osr_div <= osr_div+1; d0 <= serin; d1 <= d0; shifter4 <= { shifter4[4:0], d1 }; end wire[4:0] shifter4_flipped = (shifter4[4]) ? shifter4 : ~shifter4; // Make msb 1. wire vote2 = (shifter4_flipped == 5'b11110); wire vote3 = (shifter4_flipped == 5'b11100); wire vote0 = (shifter4_flipped == 5'b11000); wire vote1 = (shifter4_flipped == 5'b10000); reg [1:0] vote; reg [7:0] running_average; always @(posedge clk) if (reset) begin vote <= 0; running_average <= 0; serout <= 0; end else if (bitstrobe) begin vote <= (vote0)? 0: (vote1)? 1: (vote2)? 2: (vote3)? 3: vote; // Running average of the votes in fixed point 2.6 format. running_average <= ({3'd0, running_average} * 11'd7 + ({9'd0, vote} << 6) + 11'd4) / 11'd8; serout <= (running_average >= 128+64+32 || running_average < 32) ? shifter4[3]: (running_average >= 128+32) ? shifter4[2]: (running_average >= 64+32) ? shifter4[1]: shifter4[0]; end assign bitstrobe = (osr_div == 3); endmodule // EOF - DJ Greaves