OSERDES minitest without the need for hardware loopbacks.

Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
This commit is contained in:
Maciej Kurc 2019-09-11 13:34:07 +02:00
parent a7a3431ceb
commit b31345010c
12 changed files with 735 additions and 0 deletions

View File

@ -0,0 +1,38 @@
SYNTH ?= vivado
YOSYS = $(XRAY_DIR)/third_party/yosys/yosys
PART = xc7a35tcsg324-1
clean:
@find . -name "build-par.*" | xargs rm -rf
@find . -name "build-syn.*" | xargs rm -rf
@rm -f *.edif
@rm -f *.bit
@rm -f *.bin
@rm -f *.log
@rm -f *.dcp
help:
@echo "Usage: make all [SYNTH=<vivado/yosys>]"
.PHONY: clean help
$(YOSYS):
cd $(XRAY_DIR)/third_party/yosys && make config-gcc && make -j$(shell nproc)
ifeq ($(SYNTH), yosys)
%.edif: %.v $(YOSYS)
$(YOSYS) -p "read_verilog $< ; synth_xilinx -flatten -nosrl; write_edif -pvector bra -attrprop $@" -l $@.log
else ifeq ($(SYNTH), vivado)
%.edif: %.v tcl/syn.tcl
mkdir -p build-syn.$(basename $@)
cd build-syn.$(basename $@) && env PROJECT_NAME=$(basename $@) $(XRAY_VIVADO) -mode batch -source ../tcl/syn.tcl -nojournal -log ../$@.log
rm -rf *.backup.log
endif
%.bit: %.edif tcl/par.tcl
mkdir -p build-par.$(basename $@)
cd build-par.$(basename $@) && env PROJECT_NAME=$(basename $@) $(XRAY_VIVADO) -mode batch -source ../tcl/par.tcl -nojournal -log ../$@.log
rm -rf *.backup.log

View File

@ -0,0 +1,46 @@
# Minitest for OSERDES
## Description
This test allows to verify that OSERDES is working on hardware. Tested modes are:
- SDR 2:1
- SDR 3:1
- SDR 4:1
- SDR 5:1
- SDR 6:1
- SDR 7:1
- SDR 8:1
- DDR 4:1
- DDR 6:1
- DDR 8:1
No chaining of two OSERDES bels.
A pseudo random sequence of words is generated by a LFSR. The sequence is then serialized by the OSERDES and the output bitstream goes to the output pin. The pin is using 3-state buffer which is constantly on. This allows to read serialized data from the same pin without the need of hardware pin loopback connection.
Simultaneously to the OSERDES operation, the word sequence is serialized internally by the FPGA logic. Both bitstreams are then compared and an error indication signal is generated. In order to mitigate for the OSERDES latency, the reference bitstream is delayed by a number of clock cycles which is adaptively changed.
LEDs indicate whether data is being received corectly. When a LED is lit then
there is correct reception:
- LED0 - SDR 2:1
- LED1 - SDR 3:1
- LED2 - SDR 4:1
- LED3 - SDR 5:1
- LED4 - SDR 6:1
- LED5 - SDR 7:1
- LED6 - SDR 8:1
- LED7 - DDR 4:1
- LED8 - DDR 6:1
- LED9 - DDR 8:1
- LED10 - Blinking continuously
The switch SW0 is used as reset.
## Building
To build the project run the following command and the bit file will be generated.
```
make basys3_oserdes_rates.bit
```

View File

@ -0,0 +1,61 @@
create_clock -period 10.000 -name clk [get_ports clk]
set_property PACKAGE_PIN W5 [get_ports clk]
set_property PACKAGE_PIN B18 [get_ports rx]
set_property PACKAGE_PIN A18 [get_ports tx]
set_property PACKAGE_PIN V17 [get_ports sw[ 0]]
set_property PACKAGE_PIN V16 [get_ports sw[ 1]]
set_property PACKAGE_PIN W16 [get_ports sw[ 2]]
set_property PACKAGE_PIN W17 [get_ports sw[ 3]]
set_property PACKAGE_PIN W15 [get_ports sw[ 4]]
set_property PACKAGE_PIN V15 [get_ports sw[ 5]]
set_property PACKAGE_PIN W14 [get_ports sw[ 6]]
set_property PACKAGE_PIN W13 [get_ports sw[ 7]]
set_property PACKAGE_PIN V2 [get_ports sw[ 8]]
set_property PACKAGE_PIN T3 [get_ports sw[ 9]]
set_property PACKAGE_PIN T2 [get_ports sw[10]]
set_property PACKAGE_PIN R3 [get_ports sw[11]]
set_property PACKAGE_PIN W2 [get_ports sw[12]]
set_property PACKAGE_PIN U1 [get_ports sw[13]]
set_property PACKAGE_PIN T1 [get_ports sw[14]]
set_property PACKAGE_PIN R2 [get_ports sw[15]]
set_property PACKAGE_PIN U16 [get_ports led[ 0]]
set_property PACKAGE_PIN E19 [get_ports led[ 1]]
set_property PACKAGE_PIN U19 [get_ports led[ 2]]
set_property PACKAGE_PIN V19 [get_ports led[ 3]]
set_property PACKAGE_PIN W18 [get_ports led[ 4]]
set_property PACKAGE_PIN U15 [get_ports led[ 5]]
set_property PACKAGE_PIN U14 [get_ports led[ 6]]
set_property PACKAGE_PIN V14 [get_ports led[ 7]]
set_property PACKAGE_PIN V13 [get_ports led[ 8]]
set_property PACKAGE_PIN V3 [get_ports led[ 9]]
set_property PACKAGE_PIN W3 [get_ports led[10]]
set_property PACKAGE_PIN U3 [get_ports led[11]]
set_property PACKAGE_PIN P3 [get_ports led[12]]
set_property PACKAGE_PIN N3 [get_ports led[13]]
set_property PACKAGE_PIN P1 [get_ports led[14]]
set_property PACKAGE_PIN L1 [get_ports led[15]]
# JA (X1Y1)
set_property PACKAGE_PIN J1 [get_ports io[0]]
set_property PACKAGE_PIN L2 [get_ports io[1]]
set_property PACKAGE_PIN J2 [get_ports io[2]]
set_property PACKAGE_PIN G2 [get_ports io[3]]
# JB (X0Y2)
set_property PACKAGE_PIN A14 [get_ports io[4]]
set_property PACKAGE_PIN A16 [get_ports io[5]]
set_property PACKAGE_PIN B15 [get_ports io[6]]
# JC (X0Y0)
set_property PACKAGE_PIN K17 [get_ports io[7]]
set_property PACKAGE_PIN M18 [get_ports io[8]]
set_property PACKAGE_PIN N17 [get_ports io[9]]
foreach port [get_ports] {
set_property IOSTANDARD LVCMOS33 $port
set_property SLEW FAST $port
}

View File

@ -0,0 +1,134 @@
`include "src/lfsr.v"
`include "src/oserdes_test.v"
`include "src/comparator.v"
`default_nettype none
// ============================================================================
module top
(
input wire clk,
input wire rx,
output wire tx,
input wire [15:0] sw,
output wire [15:0] led,
inout wire [9:0] io
);
// ============================================================================
// Clock & reset
reg [3:0] rst_sr;
initial rst_sr <= 4'hF;
always @(posedge clk)
if (sw[0])
rst_sr <= 4'hF;
else
rst_sr <= rst_sr >> 1;
wire CLK = clk;
wire RST = rst_sr[0];
// ============================================================================
// Clocks for OSERDES
wire CLK1_X0Y0;
wire CLK2_X0Y0;
wire CLK1_X0Y2;
wire CLK2_X0Y2;
wire CLK1_X1Y1;
wire CLK2_X1Y1;
reg clk_r;
always @(posedge CLK)
if (RST) clk_r <= 1'b0;
else clk_r <= !clk_r;
BUFMR mr_buf_x1y1_1 (.I(CLK), .O(CLK1_X1Y1));
BUFMR mr_buf_x1y1_2 (.I(clk_r), .O(CLK2_X1Y1));
BUFMR mr_buf_x0y2_1 (.I(CLK), .O(CLK1_X0Y2));
BUFMR mr_buf_x0y2_2 (.I(clk_r), .O(CLK2_X0Y2));
BUFMR mr_buf_x0y0_1 (.I(CLK), .O(CLK1_X0Y0));
BUFMR mr_buf_x0y0_2 (.I(clk_r), .O(CLK2_X0Y0));
// ============================================================================
// Test uints
wire [9:0] error;
genvar i;
generate for (i=0; i<10; i=i+1) begin
localparam DATA_WIDTH = (i == 0) ? 2 :
(i == 1) ? 3 :
(i == 2) ? 4 :
(i == 3) ? 5 :
(i == 4) ? 6 :
(i == 5) ? 7 :
(i == 6) ? 8 :
(i == 7) ? 4 :
(i == 8) ? 6 :
/*(i == 9) ?*/ 8;
localparam DATA_RATE = (i < 7) ? "SDR" : "DDR";
wire CLK1 = (i >= 0 && i < 4) ? CLK1_X1Y1 :
(i >= 4 && i < 7) ? CLK1_X0Y2 :
CLK1_X0Y0;
wire CLK2 = (i >= 0 && i < 4) ? CLK2_X1Y1 :
(i >= 4 && i < 7) ? CLK2_X0Y2 :
CLK2_X0Y0;
oserdes_test #
(
.DATA_WIDTH (DATA_WIDTH),
.DATA_RATE (DATA_RATE)
)
oserdes_test
(
.CLK (CLK),
.CLK1 (CLK1),
.CLK2 (CLK2),
.RST (RST),
.IO_DAT (io[i]),
.O_ERROR (error[i])
);
end endgenerate
// ============================================================================
// IOs
reg [24:0] heartbeat_cnt;
always @(posedge CLK)
heartbeat_cnt <= heartbeat_cnt + 1;
assign led[ 0] = !error[0];
assign led[ 1] = !error[1];
assign led[ 2] = !error[2];
assign led[ 3] = !error[3];
assign led[ 4] = !error[4];
assign led[ 5] = !error[5];
assign led[ 6] = !error[6];
assign led[ 7] = !error[7];
assign led[ 8] = !error[8];
assign led[ 9] = !error[9];
assign led[10] = heartbeat_cnt[23];
assign led[11] = 1'b0;
assign led[12] = 1'b0;
assign led[13] = 1'b0;
assign led[14] = 1'b0;
assign led[15] = 1'b0;
endmodule

View File

@ -0,0 +1,18 @@
#!/bin/bash
set -e
# Check args
if [ "$#" -ne 1 ]; then
echo "Usage: run_vivado.sh <testbench file>"
exit -1
fi
# Check if testbench exists
if [ ! -f $1 ]; then
echo "Testbench $1 not found!"
exit -1
fi
# Run Vivado
TESTBENCH_TITLE=$(basename $1 .v) ${XRAY_VIVADO} -mode batch -source sim.tcl -nojournal -verbose -log vivado.log

View File

@ -0,0 +1,20 @@
create_project -force -part xc7a35ticsg324-1L $::env(TESTBENCH_TITLE) $::env(TESTBENCH_TITLE)
read_verilog $::env(TESTBENCH_TITLE).v
set_property top tb [get_filesets sim_1]
synth_design -top tb -verbose
set_property xsim.simulate.log_all_signals true [get_filesets sim_1]
set_property xsim.simulate.runtime 0 [get_filesets sim_1]
launch_simulation -verbose
restart
open_vcd ../../../../waveforms.vcd
run -all
flush_vcd
close_vcd

View File

@ -0,0 +1,71 @@
`include "../src/oserdes_test.v"
`include "../src/lfsr.v"
`include "../src/comparator.v"
`default_nettype none
`timescale 1ns / 1ps
// ============================================================================
module tb;
// ============================================================================
reg CLK;
initial CLK <= 1'b0;
always #0.5 CLK <= !CLK;
reg [3:0] rst_sr;
initial rst_sr <= 4'hF;
always @(posedge CLK) rst_sr <= rst_sr >> 1;
wire RST;
assign RST = rst_sr[0];
// ============================================================================
initial begin
$dumpfile("waveforms.vcd");
$dumpvars;
end
integer cycle_cnt;
initial cycle_cnt <= 0;
always @(posedge CLK)
if (!RST) cycle_cnt <= cycle_cnt + 1;
always @(posedge CLK)
if (!RST && cycle_cnt >= 10000)
$finish;
// ============================================================================
reg clk_r;
always @(posedge CLK)
if (RST) clk_r <= 1'b0;
else clk_r <= !clk_r;
wire CLK1 = CLK;
wire CLK2 = clk_r;
// ============================================================================
wire s_dat;
oserdes_test #
(
.DATA_WIDTH (8),
.DATA_RATE ("SDR"),
.ERROR_HOLD (4)
)
trx_path
(
.CLK (CLK),
.CLK1 (CLK1),
.CLK2 (CLK2),
.RST (RST),
.IO_DAT (s_dat)
);
endmodule

View File

@ -0,0 +1,93 @@
// This module compares two bitstreams and automatically determines their
// offset. This is done by iteratively changing bit delay for I_DAT_REF
// every time the number of errors exceeds ERROR_COUNT. The output O_ERROR
// signal is high for at least ERROR_HOLD cycles.
`default_nettype none
// ============================================================================
module comparator #
(
parameter ERROR_COUNT = 8,
parameter ERROR_HOLD = 2500000
)
(
input wire CLK,
input wire RST,
input wire I_DAT_REF,
input wire I_DAT_IOB,
output wire O_ERROR
);
// ============================================================================
// Data latch
reg [2:0] i_dat_ref_sr;
reg [2:0] i_dat_iob_sr;
always @(posedge CLK)
i_dat_ref_sr <= (i_dat_ref_sr << 1) | I_DAT_REF;
always @(posedge CLK)
i_dat_iob_sr <= (i_dat_iob_sr << 1) | I_DAT_IOB;
wire i_dat_ref = i_dat_ref_sr[2];
wire i_dat_iob = i_dat_iob_sr[2];
// ============================================================================
// Shift register for reference data, shift strobe generator.
reg [31:0] sreg;
reg [ 4:0] sreg_sel;
wire sreg_dat;
reg sreg_sh;
always @(posedge CLK)
sreg <= (sreg << 1) | i_dat_ref;
always @(posedge CLK)
if (RST)
sreg_sel <= 0;
else if(sreg_sh)
sreg_sel <= sreg_sel + 1;
assign sreg_dat = sreg[sreg_sel];
// ============================================================================
// Comparator and error counter
wire cmp_err;
reg [31:0] err_cnt;
assign cmp_err = sreg_dat ^ i_dat_iob;
always @(posedge CLK)
if (RST)
err_cnt <= 0;
else if(sreg_sh)
err_cnt <= 0;
else if(cmp_err)
err_cnt <= err_cnt + 1;
always @(posedge CLK)
if (RST)
sreg_sh <= 0;
else if(~sreg_sh && (err_cnt == ERROR_COUNT))
sreg_sh <= 1;
else
sreg_sh <= 0;
// ============================================================================
// Output generator
reg [24:0] o_cnt;
always @(posedge CLK)
if (RST)
o_cnt <= -1;
else if (cmp_err)
o_cnt <= ERROR_HOLD - 2;
else if (~o_cnt[24])
o_cnt <= o_cnt - 1;
assign O_ERROR = !o_cnt[24];
endmodule

View File

@ -0,0 +1,30 @@
`default_nettype none
// ============================================================================
module lfsr #
(
parameter WIDTH = 16, // LFSR width
parameter [WIDTH-1:0] POLY = 16'hD008, // Polynomial
parameter [WIDTH-1:0] SEED = 1 // Initial value
)
(
input wire CLK,
input wire CE,
input wire RST,
output reg [WIDTH-1:0] O
);
wire feedback = ^(O & POLY);
always @(posedge CLK) begin
if(RST) begin
O <= SEED;
end else if(CE) begin
O <= {O[WIDTH-2:0], feedback};
end
end
endmodule

View File

@ -0,0 +1,196 @@
`default_nettype none
// ============================================================================
module oserdes_test #
(
parameter DATA_WIDTH = 8,
parameter DATA_RATE = "SDR",
parameter ERROR_HOLD = 2500000
)
(
// "Hi speed" clock and reset
input wire CLK,
input wire RST,
// OSERDES clocks
input wire CLK1,
input wire CLK2,
// Data pin
inout wire IO_DAT,
// Error indicator
output wire O_ERROR
);
// ============================================================================
// Generate CLK2 and CLKDIV for OSERDES using BUFRs
localparam CLKDIV_DIVIDE =
(DATA_RATE == "SDR" && DATA_WIDTH == 2) ? "2" :
(DATA_RATE == "SDR" && DATA_WIDTH == 3) ? "3" :
(DATA_RATE == "SDR" && DATA_WIDTH == 4) ? "4" :
(DATA_RATE == "SDR" && DATA_WIDTH == 5) ? "5" :
(DATA_RATE == "SDR" && DATA_WIDTH == 6) ? "6" :
(DATA_RATE == "SDR" && DATA_WIDTH == 7) ? "7" :
(DATA_RATE == "SDR" && DATA_WIDTH == 8) ? "8" :
(DATA_RATE == "DDR" && DATA_WIDTH == 4) ? "4" :
(DATA_RATE == "DDR" && DATA_WIDTH == 6) ? "6" :
(DATA_RATE == "DDR" && DATA_WIDTH == 8) ? "8" : "BYPASS";
wire CLKX;
wire CLKDIV;
BUFIO io_buf (.I((DATA_RATE == "DDR") ? CLK2 : CLK1), .O(CLKX));
BUFR #
(
.BUFR_DIVIDE (CLKDIV_DIVIDE)
)
bufr_clkdiv
(
.I (CLK1),
.O (CLKDIV),
.CLR (RST),
.CE (1'd1)
);
// The clock enable signal for the "hi speed" clock domain.
reg clkdiv_r;
wire ce;
always @(posedge CLK)
clkdiv_r <= CLKDIV;
assign ce = clkdiv_r && !CLKDIV;
// ============================================================================
// Data source
reg lfsr_stb;
wire [7:0] lfsr_dat;
lfsr lfsr
(
.CLK (CLK),
.RST (RST),
.CE (ce),
.O (lfsr_dat)
);
always @(posedge CLK)
if (RST)
lfsr_stb <= 1'b0;
else
lfsr_stb <= ce;
// Synchronize generated data wordst to the CLKDIV
reg [7:0] ser_dat;
always @(posedge CLKDIV)
ser_dat <= lfsr_dat;
// ============================================================================
// OSERDES
// OSERDES reset generator (required for it to work properly!)
reg [3:0] ser_rst_sr;
initial ser_rst_sr <= 4'hF;
always @(posedge CLKDIV or posedge RST)
if (RST) ser_rst_sr <= 4'hF;
else ser_rst_sr <= ser_rst_sr >> 1;
wire ser_rst = ser_rst_sr[0];
// OSERDES
wire ser_oq;
wire ser_tq;
OSERDESE2 #
(
.DATA_RATE_OQ (DATA_RATE),
.DATA_WIDTH (DATA_WIDTH),
.DATA_RATE_TQ ((DATA_RATE == "DDR" && DATA_WIDTH == 4) ? "DDR" : "SDR"),
.TRISTATE_WIDTH ((DATA_RATE == "DDR" && DATA_WIDTH == 4) ? 4 : 1)
)
oserdes
(
.CLK (CLKX),
.CLKDIV (CLKDIV),
.RST (ser_rst),
.OCE (1'b1),
.D1 (ser_dat[0]),
.D2 (ser_dat[1]),
.D3 (ser_dat[2]),
.D4 (ser_dat[3]),
.D5 (ser_dat[4]),
.D6 (ser_dat[5]),
.D7 (ser_dat[6]),
.D8 (ser_dat[7]),
.OQ (ser_oq),
.TCE (1'b1),
.T1 (1'b0), // All 0 to keep OBUFT always on.
.T2 (1'b0),
.T3 (1'b0),
.T4 (1'b0),
.TQ (ser_tq)
);
// ============================================================================
// IOB
wire iob_i;
OBUFT obuf
(
.I (ser_oq),
.T (ser_tq),
.O (IO_DAT)
);
IBUF ibuf
(
.I (IO_DAT),
.O (iob_i)
);
// ============================================================================
// Reference data serializer
reg [7:0] ref_sr;
wire ref_o;
always @(posedge CLK)
if (RST)
ref_sr <= 0;
else if (ce)
ref_sr <= lfsr_dat;
else
ref_sr <= ref_sr >> 1;
assign ref_o = ref_sr[0];
// ============================================================================
// Data comparator
comparator #
(
.ERROR_COUNT (16),
.ERROR_HOLD (ERROR_HOLD)
)
comparator
(
.CLK (CLK),
.RST (RST),
.I_DAT_REF (ref_o),
.I_DAT_IOB (iob_i),
.O_ERROR (O_ERROR)
);
endmodule

View File

@ -0,0 +1,17 @@
create_project -force -name $env(PROJECT_NAME) -part xc7a35tcpg236-1
read_edif ../$env(PROJECT_NAME).edif
link_design -part xc7a35tcpg236-1
source ../basys3.xdc
set_property SEVERITY {Warning} [get_drc_checks UCIO-1]
set_property SEVERITY {Warning} [get_drc_checks NSTD-1]
set_property SEVERITY {Warning} [get_drc_checks REQP-1936]
place_design
route_design
write_checkpoint -force ../$env(PROJECT_NAME).dcp
write_bitstream -force ../$env(PROJECT_NAME).bit

View File

@ -0,0 +1,11 @@
create_project -force -name $env(PROJECT_NAME) -part xc7a35tcpg236-1
read_verilog ../$env(PROJECT_NAME).v
synth_design -top top
report_timing_summary -file top_timing_synth.rpt
report_utilization -hierarchical -file top_utilization_hierarchical_synth.rpt
report_utilization -file top_utilization_synth.rpt
write_edif -force ../$env(PROJECT_NAME).edif