diff --git a/fpga/common/rtl/tdma_ber.v b/fpga/common/rtl/tdma_ber.v new file mode 100644 index 000000000..c7a1053b5 --- /dev/null +++ b/fpga/common/rtl/tdma_ber.v @@ -0,0 +1,447 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * TDMA BER module + */ +module tdma_ber # +( + parameter COUNT = 1, + parameter INDEX_WIDTH = 6, + parameter SLICE_WIDTH = 5, + parameter AXIL_DATA_WIDTH = 32, + parameter AXIL_ADDR_WIDTH = INDEX_WIDTH+4+1+$clog2(COUNT), + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8), + parameter SCHEDULE_START_S = 48'h0, + parameter SCHEDULE_START_NS = 30'h0, + parameter SCHEDULE_PERIOD_S = 48'd0, + parameter SCHEDULE_PERIOD_NS = 30'd1000000, + parameter TIMESLOT_PERIOD_S = 48'd0, + parameter TIMESLOT_PERIOD_NS = 30'd100000, + parameter ACTIVE_PERIOD_S = 48'd0, + parameter ACTIVE_PERIOD_NS = 30'd100000 +) +( + input wire clk, + input wire rst, + + /* + * PHY connections + */ + input wire [COUNT-1:0] phy_tx_clk, + input wire [COUNT-1:0] phy_rx_clk, + input wire [COUNT*7-1:0] phy_rx_error_count, + output wire [COUNT-1:0] phy_tx_prbs31_enable, + output wire [COUNT-1:0] phy_rx_prbs31_enable, + + /* + * AXI-Lite slave interface + */ + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [AXIL_DATA_WIDTH-1:0] s_axil_wdata, + input wire [AXIL_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * PTP clock + */ + input wire [95:0] ptp_ts_96, + input wire ptp_ts_step +); + +// check configuration +initial begin + if (AXIL_ADDR_WIDTH < INDEX_WIDTH+4+1+$clog2(COUNT)) begin + $error("Error: AXI address width too narrow"); + $finish; + end + + if (AXIL_DATA_WIDTH != 32) begin + $error("Error: AXI data width must be 32"); + $finish; + end + + if (AXIL_STRB_WIDTH * 8 != AXIL_DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end +end + +wire [AXIL_ADDR_WIDTH-1:0] axil_csr_awaddr; +wire [2:0] axil_csr_awprot; +wire axil_csr_awvalid; +wire axil_csr_awready; +wire [AXIL_DATA_WIDTH-1:0] axil_csr_wdata; +wire [AXIL_STRB_WIDTH-1:0] axil_csr_wstrb; +wire axil_csr_wvalid; +wire axil_csr_wready; +wire [1:0] axil_csr_bresp; +wire axil_csr_bvalid; +wire axil_csr_bready; +wire [AXIL_ADDR_WIDTH-1:0] axil_csr_araddr; +wire [2:0] axil_csr_arprot; +wire axil_csr_arvalid; +wire axil_csr_arready; +wire [AXIL_DATA_WIDTH-1:0] axil_csr_rdata; +wire [1:0] axil_csr_rresp; +wire axil_csr_rvalid; +wire axil_csr_rready; + +// control registers +reg axil_csr_awready_reg = 1'b0; +reg axil_csr_wready_reg = 1'b0; +reg axil_csr_bvalid_reg = 1'b0; +reg axil_csr_arready_reg = 1'b0; +reg [AXIL_DATA_WIDTH-1:0] axil_csr_rdata_reg = {AXIL_DATA_WIDTH{1'b0}}; +reg axil_csr_rvalid_reg = 1'b0; + +reg tdma_enable_reg = 1'b0; +wire tdma_locked; +wire tdma_error; + +reg [79:0] set_tdma_schedule_start_reg = 0; +reg set_tdma_schedule_start_valid_reg = 0; +reg [79:0] set_tdma_schedule_period_reg = 0; +reg set_tdma_schedule_period_valid_reg = 0; +reg [79:0] set_tdma_timeslot_period_reg = 0; +reg set_tdma_timeslot_period_valid_reg = 0; +reg [79:0] set_tdma_active_period_reg = 0; +reg set_tdma_active_period_valid_reg = 0; + +wire tdma_schedule_start; +wire [INDEX_WIDTH-1:0] tdma_timeslot_index; +wire tdma_timeslot_start; +wire tdma_timeslot_end; +wire tdma_timeslot_active; + +assign axil_csr_awready = axil_csr_awready_reg; +assign axil_csr_wready = axil_csr_wready_reg; +assign axil_csr_bresp = 2'b00; +assign axil_csr_bvalid = axil_csr_bvalid_reg; +assign axil_csr_arready = axil_csr_arready_reg; +assign axil_csr_rdata = axil_csr_rdata_reg; +assign axil_csr_rresp = 2'b00; +assign axil_csr_rvalid = axil_csr_rvalid_reg; + +always @(posedge clk) begin + axil_csr_awready_reg <= 1'b0; + axil_csr_wready_reg <= 1'b0; + axil_csr_bvalid_reg <= axil_csr_bvalid_reg && !axil_csr_bready; + axil_csr_arready_reg <= 1'b0; + axil_csr_rvalid_reg <= axil_csr_rvalid_reg && !axil_csr_rready; + + set_tdma_schedule_start_valid_reg <= 1'b0; + set_tdma_schedule_period_valid_reg <= 1'b0; + set_tdma_timeslot_period_valid_reg <= 1'b0; + set_tdma_active_period_valid_reg <= 1'b0; + + if (axil_csr_awvalid && axil_csr_wvalid && !axil_csr_bvalid) begin + // write operation + axil_csr_awready_reg <= 1'b1; + axil_csr_wready_reg <= 1'b1; + axil_csr_bvalid_reg <= 1'b1; + + case (axil_csr_awaddr & ({AXIL_ADDR_WIDTH{1'b1}} << 2)) + 16'h0100: begin + // TDMA control + tdma_enable_reg <= axil_csr_wdata[0]; + end + 16'h0114: set_tdma_schedule_start_reg[29:0] <= axil_csr_wdata; // TDMA schedule start ns + 16'h0118: set_tdma_schedule_start_reg[63:32] <= axil_csr_wdata; // TDMA schedule start sec l + 16'h011C: begin + // TDMA schedule start sec h + set_tdma_schedule_start_reg[79:64] <= axil_csr_wdata; + set_tdma_schedule_start_valid_reg <= 1'b1; + end + 16'h0124: set_tdma_schedule_period_reg[29:0] <= axil_csr_wdata; // TDMA schedule period ns + 16'h0128: set_tdma_schedule_period_reg[63:32] <= axil_csr_wdata; // TDMA schedule period sec l + 16'h012C: begin + // TDMA schedule period sec h + set_tdma_schedule_period_reg[79:64] <= axil_csr_wdata; + set_tdma_schedule_period_valid_reg <= 1'b1; + end + 16'h0134: set_tdma_timeslot_period_reg[29:0] <= axil_csr_wdata; // TDMA timeslot period ns + 16'h0138: set_tdma_timeslot_period_reg[63:32] <= axil_csr_wdata; // TDMA timeslot period sec l + 16'h013C: begin + // TDMA timeslot period sec h + set_tdma_timeslot_period_reg[79:64] <= axil_csr_wdata; + set_tdma_timeslot_period_valid_reg <= 1'b1; + end + 16'h0144: set_tdma_active_period_reg[29:0] <= axil_csr_wdata; // TDMA active period ns + 16'h0148: set_tdma_active_period_reg[63:32] <= axil_csr_wdata; // TDMA active period sec l + 16'h014C: begin + // TDMA active period sec h + set_tdma_active_period_reg[79:64] <= axil_csr_wdata; + set_tdma_active_period_valid_reg <= 1'b1; + end + endcase + end + + if (axil_csr_arvalid && !axil_csr_rvalid) begin + // read operation + axil_csr_arready_reg <= 1'b1; + axil_csr_rvalid_reg <= 1'b1; + axil_csr_rdata_reg <= {AXIL_DATA_WIDTH{1'b0}}; + + case (axil_csr_araddr & ({AXIL_ADDR_WIDTH{1'b1}} << 2)) + 16'h0000: axil_csr_rdata_reg <= 0; + 16'h0010: axil_csr_rdata_reg <= COUNT; + 16'h0014: axil_csr_rdata_reg <= INDEX_WIDTH; + 16'h0018: axil_csr_rdata_reg <= SLICE_WIDTH; + 16'h0100: begin + // TDMA control + axil_csr_rdata_reg[0] <= tdma_enable_reg; + end + 16'h0104: begin + // TDMA status + axil_csr_rdata_reg[0] <= tdma_locked; + axil_csr_rdata_reg[1] <= tdma_error; + end + 16'h0114: axil_csr_rdata_reg <= set_tdma_schedule_start_reg[29:0]; // TDMA schedule start ns + 16'h0118: axil_csr_rdata_reg <= set_tdma_schedule_start_reg[63:32]; // TDMA schedule start sec l + 16'h011C: axil_csr_rdata_reg <= set_tdma_schedule_start_reg[79:64]; // TDMA schedule start sec h + 16'h0124: axil_csr_rdata_reg <= set_tdma_schedule_period_reg[29:0]; // TDMA schedule period ns + 16'h0128: axil_csr_rdata_reg <= set_tdma_schedule_period_reg[63:32]; // TDMA schedule period sec l + 16'h012C: axil_csr_rdata_reg <= set_tdma_schedule_period_reg[79:64]; // TDMA schedule period sec h + 16'h0134: axil_csr_rdata_reg <= set_tdma_timeslot_period_reg[29:0]; // TDMA timeslot period ns + 16'h0138: axil_csr_rdata_reg <= set_tdma_timeslot_period_reg[63:32]; // TDMA timeslot period sec l + 16'h013C: axil_csr_rdata_reg <= set_tdma_timeslot_period_reg[79:64]; // TDMA timeslot period sec h + 16'h0144: axil_csr_rdata_reg <= set_tdma_active_period_reg[29:0]; // TDMA active period ns + 16'h0148: axil_csr_rdata_reg <= set_tdma_active_period_reg[63:32]; // TDMA active period sec l + 16'h014C: axil_csr_rdata_reg <= set_tdma_active_period_reg[79:64]; // TDMA active period sec h + endcase + end + + if (rst) begin + axil_csr_awready_reg <= 1'b0; + axil_csr_wready_reg <= 1'b0; + axil_csr_bvalid_reg <= 1'b0; + axil_csr_arready_reg <= 1'b0; + axil_csr_rvalid_reg <= 1'b0; + + tdma_enable_reg <= 1'b0; + end +end + +tdma_scheduler #( + .INDEX_WIDTH(INDEX_WIDTH), + .SCHEDULE_START_S(SCHEDULE_START_S), + .SCHEDULE_START_NS(SCHEDULE_START_NS), + .SCHEDULE_PERIOD_S(SCHEDULE_PERIOD_S), + .SCHEDULE_PERIOD_NS(SCHEDULE_PERIOD_NS), + .TIMESLOT_PERIOD_S(TIMESLOT_PERIOD_S), + .TIMESLOT_PERIOD_NS(TIMESLOT_PERIOD_NS), + .ACTIVE_PERIOD_S(ACTIVE_PERIOD_S), + .ACTIVE_PERIOD_NS(ACTIVE_PERIOD_NS) +) +tdma_scheduler_inst ( + .clk(clk), + .rst(rst), + .input_ts_96(ptp_ts_96), + .input_ts_step(ptp_ts_step), + .enable(tdma_enable_reg), + .input_schedule_start(set_tdma_schedule_start_reg), + .input_schedule_start_valid(set_tdma_schedule_start_valid_reg), + .input_schedule_period(set_tdma_schedule_period_reg), + .input_schedule_period_valid(set_tdma_schedule_period_valid_reg), + .input_timeslot_period(set_tdma_timeslot_period_reg), + .input_timeslot_period_valid(set_tdma_timeslot_period_valid_reg), + .input_active_period(set_tdma_active_period_reg), + .input_active_period_valid(set_tdma_active_period_valid_reg), + .locked(tdma_locked), + .error(tdma_error), + .schedule_start(tdma_schedule_start), + .timeslot_index(tdma_timeslot_index), + .timeslot_start(tdma_timeslot_start), + .timeslot_end(tdma_timeslot_end), + .timeslot_active(tdma_timeslot_active) +); + +wire [COUNT*AXIL_ADDR_WIDTH-1:0] axil_ch_awaddr; +wire [COUNT*3-1:0] axil_ch_awprot; +wire [COUNT-1:0] axil_ch_awvalid; +wire [COUNT-1:0] axil_ch_awready; +wire [COUNT*AXIL_DATA_WIDTH-1:0] axil_ch_wdata; +wire [COUNT*AXIL_STRB_WIDTH-1:0] axil_ch_wstrb; +wire [COUNT-1:0] axil_ch_wvalid; +wire [COUNT-1:0] axil_ch_wready; +wire [COUNT*2-1:0] axil_ch_bresp; +wire [COUNT-1:0] axil_ch_bvalid; +wire [COUNT-1:0] axil_ch_bready; +wire [COUNT*AXIL_ADDR_WIDTH-1:0] axil_ch_araddr; +wire [COUNT*3-1:0] axil_ch_arprot; +wire [COUNT-1:0] axil_ch_arvalid; +wire [COUNT-1:0] axil_ch_arready; +wire [COUNT*AXIL_DATA_WIDTH-1:0] axil_ch_rdata; +wire [COUNT*2-1:0] axil_ch_rresp; +wire [COUNT-1:0] axil_ch_rvalid; +wire [COUNT-1:0] axil_ch_rready; + +parameter CH_ADDR_WIDTH = INDEX_WIDTH+4; +parameter CH_BASE_ADDR_WIDTH = (COUNT+1)*AXIL_ADDR_WIDTH; +parameter CH_BASE_ADDR = calcBaseAddrs(CH_ADDR_WIDTH); + +function [CH_BASE_ADDR_WIDTH-1:0] calcBaseAddrs(input [31:0] width); + integer i; + begin + calcBaseAddrs = {CH_BASE_ADDR_WIDTH{1'b0}}; + for (i = 0; i < COUNT+1; i = i + 1) begin + calcBaseAddrs[i * AXIL_ADDR_WIDTH +: AXIL_ADDR_WIDTH] = i * (2**width); + end + end +endfunction + +function [31:0] w_32(input [31:0] val); + w_32 = val; +endfunction + +axil_interconnect #( + .DATA_WIDTH(AXIL_DATA_WIDTH), + .ADDR_WIDTH(AXIL_ADDR_WIDTH), + .S_COUNT(1), + .M_COUNT(COUNT+1), + .M_BASE_ADDR(CH_BASE_ADDR), + .M_ADDR_WIDTH({COUNT+1{w_32(CH_ADDR_WIDTH)}}), + .M_CONNECT_READ({COUNT+1{1'b1}}), + .M_CONNECT_WRITE({COUNT+1{1'b1}}) +) +axil_csr_interconnect_inst ( + .clk(clk), + .rst(rst), + .s_axil_awaddr(s_axil_awaddr), + .s_axil_awprot(s_axil_awprot), + .s_axil_awvalid(s_axil_awvalid), + .s_axil_awready(s_axil_awready), + .s_axil_wdata(s_axil_wdata), + .s_axil_wstrb(s_axil_wstrb), + .s_axil_wvalid(s_axil_wvalid), + .s_axil_wready(s_axil_wready), + .s_axil_bresp(s_axil_bresp), + .s_axil_bvalid(s_axil_bvalid), + .s_axil_bready(s_axil_bready), + .s_axil_araddr(s_axil_araddr), + .s_axil_arprot(s_axil_arprot), + .s_axil_arvalid(s_axil_arvalid), + .s_axil_arready(s_axil_arready), + .s_axil_rdata(s_axil_rdata), + .s_axil_rresp(s_axil_rresp), + .s_axil_rvalid(s_axil_rvalid), + .s_axil_rready(s_axil_rready), + .m_axil_awaddr( {axil_ch_awaddr, axil_csr_awaddr}), + .m_axil_awprot( {axil_ch_awprot, axil_csr_awprot}), + .m_axil_awvalid( {axil_ch_awvalid, axil_csr_awvalid}), + .m_axil_awready( {axil_ch_awready, axil_csr_awready}), + .m_axil_wdata( {axil_ch_wdata, axil_csr_wdata}), + .m_axil_wstrb( {axil_ch_wstrb, axil_csr_wstrb}), + .m_axil_wvalid( {axil_ch_wvalid, axil_csr_wvalid}), + .m_axil_wready( {axil_ch_wready, axil_csr_wready}), + .m_axil_bresp( {axil_ch_bresp, axil_csr_bresp}), + .m_axil_bvalid( {axil_ch_bvalid, axil_csr_bvalid}), + .m_axil_bready( {axil_ch_bready, axil_csr_bready}), + .m_axil_araddr( {axil_ch_araddr, axil_csr_araddr}), + .m_axil_arprot( {axil_ch_arprot, axil_csr_arprot}), + .m_axil_arvalid( {axil_ch_arvalid, axil_csr_arvalid}), + .m_axil_arready( {axil_ch_arready, axil_csr_arready}), + .m_axil_rdata( {axil_ch_rdata, axil_csr_rdata}), + .m_axil_rresp( {axil_ch_rresp, axil_csr_rresp}), + .m_axil_rvalid( {axil_ch_rvalid, axil_csr_rvalid}), + .m_axil_rready( {axil_ch_rready, axil_csr_rready}) +); + +generate + genvar n; + + for (n = 0; n < COUNT; n = n + 1) begin + + tdma_ber_ch #( + .INDEX_WIDTH(INDEX_WIDTH), + .SLICE_WIDTH(SLICE_WIDTH), + .AXIL_DATA_WIDTH(AXIL_DATA_WIDTH), + .AXIL_ADDR_WIDTH(CH_ADDR_WIDTH), + .AXIL_STRB_WIDTH(AXIL_STRB_WIDTH) + ) + tdma_ber_ch_inst ( + .clk(clk), + .rst(rst), + .phy_tx_clk(phy_tx_clk[n]), + .phy_rx_clk(phy_rx_clk[n]), + .phy_rx_error_count(phy_rx_error_count[n*7 +: 7]), + .phy_tx_prbs31_enable(phy_tx_prbs31_enable[n]), + .phy_rx_prbs31_enable(phy_rx_prbs31_enable[n]), + .s_axil_awaddr(axil_ch_awaddr[n*AXIL_ADDR_WIDTH +: AXIL_ADDR_WIDTH]), + .s_axil_awprot(axil_ch_awprot[n*3 +: 3]), + .s_axil_awvalid(axil_ch_awvalid[n]), + .s_axil_awready(axil_ch_awready[n]), + .s_axil_wdata(axil_ch_wdata[n*AXIL_DATA_WIDTH +: AXIL_DATA_WIDTH]), + .s_axil_wstrb(axil_ch_wstrb[n*AXIL_STRB_WIDTH +: AXIL_STRB_WIDTH]), + .s_axil_wvalid(axil_ch_wvalid[n]), + .s_axil_wready(axil_ch_wready[n]), + .s_axil_bresp(axil_ch_bresp[n*2 +: 2]), + .s_axil_bvalid(axil_ch_bvalid[n]), + .s_axil_bready(axil_ch_bready[n]), + .s_axil_araddr(axil_ch_araddr[n*AXIL_ADDR_WIDTH +: AXIL_ADDR_WIDTH]), + .s_axil_arprot(axil_ch_arprot[n*3 +: 3]), + .s_axil_arvalid(axil_ch_arvalid[n]), + .s_axil_arready(axil_ch_arready[n]), + .s_axil_rdata(axil_ch_rdata[n*AXIL_DATA_WIDTH +: AXIL_DATA_WIDTH]), + .s_axil_rresp(axil_ch_rresp[n*2 +: 2]), + .s_axil_rvalid(axil_ch_rvalid[n]), + .s_axil_rready(axil_ch_rready[n]), + .tdma_timeslot_index(tdma_timeslot_index), + .tdma_timeslot_start(tdma_timeslot_start), + .tdma_timeslot_active(tdma_timeslot_active) + ); + + end +endgenerate + +endmodule diff --git a/fpga/common/rtl/tdma_ber_ch.v b/fpga/common/rtl/tdma_ber_ch.v new file mode 100644 index 000000000..a7ec42985 --- /dev/null +++ b/fpga/common/rtl/tdma_ber_ch.v @@ -0,0 +1,585 @@ +/* + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +*/ + +// Language: Verilog 2001 + +`timescale 1ns / 1ps + +/* + * TDMA BER module + */ +module tdma_ber_ch # +( + parameter INDEX_WIDTH = 6, + parameter SLICE_WIDTH = 5, + parameter AXIL_DATA_WIDTH = 32, + parameter AXIL_ADDR_WIDTH = INDEX_WIDTH+4, + parameter AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8) +) +( + input wire clk, + input wire rst, + + /* + * PHY connections + */ + input wire phy_tx_clk, + input wire phy_rx_clk, + input wire [6:0] phy_rx_error_count, + output wire phy_tx_prbs31_enable, + output wire phy_rx_prbs31_enable, + + /* + * AXI-Lite slave interface + */ + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_awaddr, + input wire [2:0] s_axil_awprot, + input wire s_axil_awvalid, + output wire s_axil_awready, + input wire [AXIL_DATA_WIDTH-1:0] s_axil_wdata, + input wire [AXIL_STRB_WIDTH-1:0] s_axil_wstrb, + input wire s_axil_wvalid, + output wire s_axil_wready, + output wire [1:0] s_axil_bresp, + output wire s_axil_bvalid, + input wire s_axil_bready, + input wire [AXIL_ADDR_WIDTH-1:0] s_axil_araddr, + input wire [2:0] s_axil_arprot, + input wire s_axil_arvalid, + output wire s_axil_arready, + output wire [AXIL_DATA_WIDTH-1:0] s_axil_rdata, + output wire [1:0] s_axil_rresp, + output wire s_axil_rvalid, + input wire s_axil_rready, + + /* + * TDMA schedule + */ + input wire [INDEX_WIDTH-1:0] tdma_timeslot_index, + input wire tdma_timeslot_start, + input wire tdma_timeslot_active +); + +parameter VALID_ADDR_WIDTH = AXIL_ADDR_WIDTH - $clog2(AXIL_STRB_WIDTH); +parameter WORD_WIDTH = AXIL_STRB_WIDTH; +parameter WORD_SIZE = AXIL_DATA_WIDTH/WORD_WIDTH; + +// check configuration +initial begin + if (AXIL_ADDR_WIDTH < INDEX_WIDTH+4) begin + $error("Error: AXI address width too narrow"); + $finish; + end + + if (AXIL_DATA_WIDTH != 32) begin + $error("Error: AXI data width must be 32"); + $finish; + end + + if (AXIL_STRB_WIDTH * 8 != AXIL_DATA_WIDTH) begin + $error("Error: Interface requires byte (8-bit) granularity"); + $finish; + end +end + +reg tx_prbs31_enable_reg = 1'b0, tx_prbs31_enable_next; +reg rx_prbs31_enable_reg = 1'b0, rx_prbs31_enable_next; + +// PHY TX BER interface +reg phy_tx_prbs31_enable_reg = 1'b0; + +always @(posedge phy_tx_clk) begin + phy_tx_prbs31_enable_reg <= tx_prbs31_enable_reg; +end + +assign phy_tx_prbs31_enable = phy_tx_prbs31_enable_reg; + +// PHY RX BER interface +reg phy_rx_prbs31_enable_reg = 1'b0; + +// accumulate errors, dump every 16 cycles +reg [10:0] phy_rx_error_count_reg = 0; +reg [10:0] phy_rx_error_count_acc_reg = 0; +reg [3:0] phy_rx_count_reg = 4'd0; +reg phy_rx_flag_reg = 1'b0; + +always @(posedge phy_rx_clk) begin + phy_rx_prbs31_enable_reg <= rx_prbs31_enable_reg; + + phy_rx_count_reg <= phy_rx_count_reg + 1; + + if (phy_rx_count_reg == 0) begin + phy_rx_error_count_reg <= phy_rx_error_count_acc_reg; + phy_rx_error_count_acc_reg <= phy_rx_error_count; + phy_rx_flag_reg <= !phy_rx_flag_reg; + end else begin + phy_rx_error_count_acc_reg <= phy_rx_error_count_acc_reg + phy_rx_error_count; + end +end + +assign phy_rx_prbs31_enable = phy_rx_prbs31_enable_reg; + +// synchronize dumped counts to control clock domain +reg rx_flag_sync_reg_1 = 1'b0; +reg rx_flag_sync_reg_2 = 1'b0; +reg rx_flag_sync_reg_3 = 1'b0; + +always @(posedge clk) begin + rx_flag_sync_reg_1 <= phy_rx_flag_reg; + rx_flag_sync_reg_2 <= rx_flag_sync_reg_1; + rx_flag_sync_reg_3 <= rx_flag_sync_reg_2; +end + +reg [31:0] cycle_count_reg = 32'd0, cycle_count_next; +reg [31:0] update_count_reg = 32'd0, update_count_next; +reg [31:0] rx_error_count_reg = 32'd0, rx_error_count_next; + +reg [31:0] atomic_cycle_count_reg = 32'd0, atomic_cycle_count_next; +reg [31:0] atomic_update_count_reg = 32'd0, atomic_update_count_next; +reg [31:0] atomic_rx_error_count_reg = 32'd0, atomic_rx_error_count_next; + +reg accumulate_enable_reg = 1'b0, accumulate_enable_next; +reg slice_enable_reg = 1'b0, slice_enable_next; + +reg [31:0] slice_time_reg = 0, slice_time_next; +reg [31:0] slice_offset_reg = 0, slice_offset_next; + +reg [SLICE_WIDTH-1:0] slice_select_reg = 0, slice_select_next; + +reg error_count_mem_read_reg = 0; +reg [31:0] error_count_mem_read_data_reg = 0; +reg update_count_mem_read_reg = 0; +reg [31:0] update_count_mem_read_data_reg = 0; + +reg [31:0] error_count_mem[(2**(INDEX_WIDTH+SLICE_WIDTH))-1:0]; +reg [31:0] update_count_mem[(2**(INDEX_WIDTH+SLICE_WIDTH))-1:0]; + +integer i; + +initial begin + for (i = 0; i < 2**(INDEX_WIDTH+SLICE_WIDTH); i = i + 1) begin + error_count_mem[i] = 0; + update_count_mem[i] = 0; + end +end + +reg [10:0] phy_rx_error_count_sync_reg = 0; +reg phy_rx_error_count_sync_valid_reg = 1'b0; + +always @(posedge clk) begin + phy_rx_error_count_sync_valid_reg <= 1'b0; + if (rx_flag_sync_reg_2 ^ rx_flag_sync_reg_3) begin + phy_rx_error_count_sync_reg <= phy_rx_error_count_reg; + phy_rx_error_count_sync_valid_reg <= 1'b1; + end +end + +reg slice_running_reg = 1'b0; +reg slice_active_reg = 1'b0; +reg [31:0] slice_count_reg = 0; +reg [SLICE_WIDTH-1:0] cur_slice_reg = 0; + +reg [1:0] accumulate_state_reg = 0; +reg [31:0] rx_ts_update_count_read_reg = 0; +reg [31:0] rx_ts_error_count_read_reg = 0; +reg [31:0] rx_ts_update_count_reg = 0; +reg [31:0] rx_ts_error_count_reg = 0; +reg [INDEX_WIDTH+SLICE_WIDTH-1:0] index_reg = 0; + +always @(posedge clk) begin + if (tdma_timeslot_start) begin + slice_running_reg <= 1'b1; + if (slice_offset_reg) begin + slice_active_reg <= 1'b0; + slice_count_reg <= slice_offset_reg; + end else begin + slice_active_reg <= 1'b1; + slice_count_reg <= slice_time_reg; + end + cur_slice_reg <= 0; + end else if (slice_count_reg > 0) begin + slice_count_reg <= slice_count_reg - 1; + end else begin + slice_count_reg <= slice_time_reg; + slice_active_reg <= slice_running_reg; + if (slice_active_reg && slice_running_reg) begin + cur_slice_reg <= cur_slice_reg + 1; + slice_running_reg <= cur_slice_reg < {SLICE_WIDTH{1'b1}}; + slice_active_reg <= cur_slice_reg < {SLICE_WIDTH{1'b1}}; + end + end + + case (accumulate_state_reg) + 2'd0: begin + if (accumulate_enable_reg && tdma_timeslot_active && phy_rx_error_count_sync_valid_reg) begin + if (slice_enable_reg) begin + index_reg <= (tdma_timeslot_index << SLICE_WIDTH) + cur_slice_reg; + if (slice_active_reg) begin + accumulate_state_reg <= 2'd1; + end + end else begin + index_reg <= (tdma_timeslot_index << SLICE_WIDTH); + accumulate_state_reg <= 2'd1; + end + end + end + 2'd1: begin + rx_ts_update_count_read_reg <= update_count_mem[index_reg]; + rx_ts_error_count_read_reg <= error_count_mem[index_reg]; + + accumulate_state_reg <= 2'd2; + end + 2'd2: begin + rx_ts_error_count_reg <= rx_ts_error_count_read_reg + phy_rx_error_count_sync_reg; + rx_ts_update_count_reg <= rx_ts_update_count_read_reg + 1; + + // if ((rx_ts_error_count_reg + inc_reg) >> 32) begin + // rx_ts_error_count_reg <= 32'hffffffff; + // end else begin + // rx_ts_error_count_reg <= rx_ts_error_count_reg + inc_reg; + // end + + // if ((rx_ts_update_count_reg + 1) >> 32) begin + // rx_ts_update_count_reg <= 32'hffffffff; + // end else begin + // rx_ts_update_count_reg <= rx_ts_update_count_reg + 1; + // end + + accumulate_state_reg <= 2'd3; + end + 2'd3: begin + update_count_mem[index_reg] <= rx_ts_update_count_reg; + error_count_mem[index_reg] <= rx_ts_error_count_reg; + + accumulate_state_reg <= 2'd0; + end + default: begin + accumulate_state_reg <= 2'd0; + end + endcase +end + +// control registers +reg read_eligible; +reg write_eligible; + +reg mem_wr_en; +reg mem_rd_en; + +reg last_read_reg = 1'b0, last_read_next; + +reg s_axil_awready_reg = 1'b0, s_axil_awready_next; +reg s_axil_wready_reg = 1'b0, s_axil_wready_next; +reg s_axil_bvalid_reg = 1'b0, s_axil_bvalid_next; +reg s_axil_arready_reg = 1'b0, s_axil_arready_next; +reg [AXIL_DATA_WIDTH-1:0] s_axil_rdata_reg = {AXIL_DATA_WIDTH{1'b0}}, s_axil_rdata_next; +reg s_axil_rvalid_reg = 1'b0, s_axil_rvalid_next; + +assign s_axil_awready = s_axil_awready_reg; +assign s_axil_wready = s_axil_wready_reg; +assign s_axil_bresp = 2'b00; +assign s_axil_bvalid = s_axil_bvalid_reg; +assign s_axil_arready = s_axil_arready_reg; +assign s_axil_rdata = error_count_mem_read_reg ? error_count_mem_read_data_reg : (update_count_mem_read_reg ? update_count_mem_read_data_reg : s_axil_rdata_reg); +assign s_axil_rresp = 2'b00; +assign s_axil_rvalid = s_axil_rvalid_reg; + +wire [INDEX_WIDTH+SLICE_WIDTH-1:0] axil_ram_addr = ((mem_rd_en ? s_axil_araddr[INDEX_WIDTH+1+2-1:1+2] : s_axil_awaddr[INDEX_WIDTH+1+2-1:1+2]) << SLICE_WIDTH) | slice_select_reg; + +always @* begin + mem_wr_en = 1'b0; + mem_rd_en = 1'b0; + + last_read_next = last_read_reg; + + s_axil_awready_next = 1'b0; + s_axil_wready_next = 1'b0; + s_axil_bvalid_next = s_axil_bvalid_reg && !s_axil_bready; + + s_axil_arready_next = 1'b0; + s_axil_rdata_next = s_axil_rdata_reg; + s_axil_rvalid_next = s_axil_rvalid_reg && !s_axil_rready; + + cycle_count_next = cycle_count_reg + 1; + + update_count_next = update_count_reg; + rx_error_count_next = rx_error_count_reg; + if (phy_rx_error_count_sync_valid_reg) begin + update_count_next = update_count_reg + 1; + rx_error_count_next = phy_rx_error_count_sync_reg + rx_error_count_reg; + end + + atomic_cycle_count_next = atomic_cycle_count_reg + 1; + if (atomic_cycle_count_reg[31] && ~atomic_cycle_count_next[31]) begin + atomic_cycle_count_next = 32'hffffffff; + end + + atomic_update_count_next = atomic_update_count_reg; + atomic_rx_error_count_next = atomic_rx_error_count_reg; + if (phy_rx_error_count_sync_valid_reg) begin + atomic_update_count_next = atomic_update_count_reg + 1; + atomic_rx_error_count_next = phy_rx_error_count_sync_reg + atomic_rx_error_count_reg; + + // saturate + if (atomic_update_count_reg[31] && ~atomic_update_count_next[31]) begin + atomic_update_count_next = 32'hffffffff; + end + if (atomic_rx_error_count_reg[31] && ~atomic_rx_error_count_next[31]) begin + atomic_rx_error_count_next = 32'hffffffff; + end + end + + accumulate_enable_next = accumulate_enable_reg; + slice_enable_next = slice_enable_reg; + + slice_time_next = slice_time_reg; + slice_offset_next = slice_offset_reg; + + slice_select_next = slice_select_reg; + + tx_prbs31_enable_next = tx_prbs31_enable_reg; + rx_prbs31_enable_next = rx_prbs31_enable_reg; + + write_eligible = s_axil_awvalid && s_axil_wvalid && (!s_axil_bvalid || s_axil_bready) && (!s_axil_awready && !s_axil_wready); + read_eligible = s_axil_arvalid && (!s_axil_rvalid || s_axil_rready) && (!s_axil_arready); + + if (write_eligible && (!read_eligible || last_read_reg)) begin + last_read_next = 1'b0; + + s_axil_awready_next = 1'b1; + s_axil_wready_next = 1'b1; + s_axil_bvalid_next = 1'b1; + + if (s_axil_awaddr[INDEX_WIDTH+2+1+1-1]) begin + mem_wr_en = 1'b1; + end else begin + case (s_axil_awaddr & ({AXIL_ADDR_WIDTH{1'b1}} << 2)) + 16'h0000: begin + // control + tx_prbs31_enable_next = s_axil_wdata[0]; + rx_prbs31_enable_next = s_axil_wdata[1]; + end + 16'h0004: begin + // cycle count + cycle_count_next = 1; + end + 16'h0008: begin + // update count + update_count_next = 0; + end + 16'h000C: begin + // error count + rx_error_count_next = 0; + end + 16'h0014: begin + // cycle count + atomic_cycle_count_next = 1; + end + 16'h0018: begin + // update count + atomic_update_count_next = 0; + end + 16'h001C: begin + // error count + atomic_rx_error_count_next = 0; + end + 16'h0020: begin + // control + accumulate_enable_next = s_axil_wdata[0]; + slice_enable_next = s_axil_wdata[1]; + end + 16'h0024: begin + // slice time + slice_time_next = s_axil_wdata; + end + 16'h0028: begin + // slice offset + slice_offset_next = s_axil_wdata; + end + 16'h0030: begin + // slice select + slice_select_next = s_axil_wdata; + end + endcase + end + end else if (read_eligible) begin + last_read_next = 1'b1; + + s_axil_arready_next = 1'b1; + s_axil_rvalid_next = 1'b1; + s_axil_rdata_next = {AXIL_DATA_WIDTH{1'b0}}; + + if (s_axil_araddr[INDEX_WIDTH+2+1+1-1]) begin + mem_rd_en = 1'b1; + end else begin + case (s_axil_araddr & ({AXIL_ADDR_WIDTH{1'b1}} << 2)) + 16'h0000: begin + // control + s_axil_rdata_next[0] = tx_prbs31_enable_reg; + s_axil_rdata_next[1] = rx_prbs31_enable_reg; + end + 16'h0004: begin + // cycle count + s_axil_rdata_next = cycle_count_reg; + end + 16'h0008: begin + // update count + s_axil_rdata_next = update_count_reg; + end + 16'h000C: begin + // error count + s_axil_rdata_next = rx_error_count_reg; + end + 16'h0014: begin + // cycle count + s_axil_rdata_next = atomic_cycle_count_reg; + atomic_cycle_count_next = 1; + end + 16'h0018: begin + // update count + s_axil_rdata_next = atomic_update_count_reg; + atomic_update_count_next = 0; + end + 16'h001C: begin + // error count + s_axil_rdata_next = atomic_rx_error_count_reg; + atomic_rx_error_count_next = 0; + end + 16'h0020: begin + // control + s_axil_rdata_next[0] = accumulate_enable_reg; + s_axil_rdata_next[1] = slice_enable_reg; + end + 16'h0024: begin + // slice time + s_axil_rdata_next = slice_time_reg; + end + 16'h0028: begin + // slice offset + s_axil_rdata_next = slice_offset_reg; + end + 16'h0030: begin + // slice select + s_axil_rdata_next = slice_select_reg; + end + endcase + end + end +end + +always @(posedge clk) begin + if (rst) begin + last_read_reg <= 1'b0; + + s_axil_awready_reg <= 1'b0; + s_axil_wready_reg <= 1'b0; + s_axil_bvalid_reg <= 1'b0; + + s_axil_arready_reg <= 1'b0; + s_axil_rvalid_reg <= 1'b0; + + cycle_count_reg <= 32'd0; + update_count_reg <= 32'd0; + rx_error_count_reg <= 32'd0; + + atomic_cycle_count_reg <= 32'd0; + atomic_update_count_reg <= 32'd0; + atomic_rx_error_count_reg <= 32'd0; + + accumulate_enable_reg <= 1'b0; + slice_enable_reg <= 1'b0; + + slice_time_reg <= 32'd0; + slice_offset_reg <= 32'd0; + + slice_select_reg <= 0; + + tx_prbs31_enable_reg <= 1'b0; + rx_prbs31_enable_reg <= 1'b0; + end else begin + last_read_reg <= last_read_next; + + s_axil_awready_reg <= s_axil_awready_next; + s_axil_wready_reg <= s_axil_wready_next; + s_axil_bvalid_reg <= s_axil_bvalid_next; + + s_axil_arready_reg <= s_axil_arready_next; + s_axil_rvalid_reg <= s_axil_rvalid_next; + + cycle_count_reg <= cycle_count_next; + update_count_reg <= update_count_next; + rx_error_count_reg <= rx_error_count_next; + + atomic_cycle_count_reg <= atomic_cycle_count_next; + atomic_update_count_reg <= atomic_update_count_next; + atomic_rx_error_count_reg <= atomic_rx_error_count_next; + + accumulate_enable_reg <= accumulate_enable_next; + slice_enable_reg <= slice_enable_next; + + slice_time_reg <= slice_time_next; + slice_offset_reg <= slice_offset_next; + + slice_select_reg <= slice_select_next; + + tx_prbs31_enable_reg <= tx_prbs31_enable_next; + rx_prbs31_enable_reg <= rx_prbs31_enable_next; + end + + s_axil_rdata_reg <= s_axil_rdata_next; + + error_count_mem_read_reg <= 1'b0; + update_count_mem_read_reg <= 1'b0; + + if (mem_rd_en) begin + if (s_axil_araddr[2]) begin + error_count_mem_read_data_reg <= error_count_mem[axil_ram_addr]; + error_count_mem_read_reg <= 1'b1; + end else begin + update_count_mem_read_data_reg <= update_count_mem[axil_ram_addr]; + update_count_mem_read_reg <= 1'b1; + end + end else begin + for (i = 0; i < WORD_WIDTH; i = i + 1) begin + if (mem_wr_en && s_axil_wstrb[i]) begin + if (s_axil_awaddr[2]) begin + error_count_mem[axil_ram_addr][WORD_SIZE*i +: WORD_SIZE] <= s_axil_wdata[WORD_SIZE*i +: WORD_SIZE]; + end else begin + update_count_mem[axil_ram_addr][WORD_SIZE*i +: WORD_SIZE] <= s_axil_wdata[WORD_SIZE*i +: WORD_SIZE]; + end + end + end + end +end + +endmodule diff --git a/fpga/common/syn/tdma_ber_ch.tcl b/fpga/common/syn/tdma_ber_ch.tcl new file mode 100644 index 000000000..de9fab97f --- /dev/null +++ b/fpga/common/syn/tdma_ber_ch.tcl @@ -0,0 +1,59 @@ +# Copyright 2019, The Regents of the University of California. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +# OF SUCH DAMAGE. +# +# The views and conclusions contained in the software and documentation are those +# of the authors and should not be interpreted as representing official policies, +# either expressed or implied, of The Regents of the University of California. + +# TDMA BER channel module + +foreach inst [get_cells -hier -filter {(ORIG_REF_NAME == tdma_ber_ch || REF_NAME == tdma_ber_ch)}] { + puts "Inserting timing constraints for tdma_ber_ch instance $inst" + + # get clock periods + set clk [get_clocks -of_objects [get_pins $inst/tx_prbs31_enable_reg_reg/C]] + set tx_clk [get_clocks -of_objects [get_pins $inst/phy_tx_prbs31_enable_reg_reg/C]] + set rx_clk [get_clocks -of_objects [get_pins $inst/phy_rx_prbs31_enable_reg_reg/C]] + + set clk_period [get_property -min PERIOD $clk] + set tx_clk_period [get_property -min PERIOD $tx_clk] + set rx_clk_period [get_property -min PERIOD $rx_clk] + + set min_clk_period [expr $tx_clk_period < $write_clk_period ? $tx_clk_period : $write_clk_period] + + # control synchronization + set_property ASYNC_REG TRUE [get_cells -hier -regexp ".*/phy_(rx|tx)_prbs31_enable_reg_reg" -filter "PARENT == $inst"] + + set_max_delay -from [get_cells "$inst/tx_prbs31_enable_reg_reg"] -to [get_cells "$inst/phy_tx_prbs31_enable_reg_reg"] -datapath_only $clk_period + set_max_delay -from [get_cells "$inst/rx_prbs31_enable_reg_reg"] -to [get_cells "$inst/phy_rx_prbs31_enable_reg_reg"] -datapath_only $clk_period + + # data synchronization + set_property ASYNC_REG TRUE [get_cells -hier -regexp ".*/rx_flag_sync_reg_\[123\]_reg" -filter "PARENT == $inst"] + + set_max_delay -from [get_cells "$inst/phy_rx_flag_reg_reg"] -to [get_cells $inst/rx_flag_sync_reg_1_reg] -datapath_only $rx_clk_period + + set_max_delay -from [get_cells "$inst/phy_rx_error_count_reg_reg[*]"] -to [get_cells $inst/phy_rx_error_count_sync_reg_reg[*]] -datapath_only $rx_clk_period + set_bus_skew -from [get_cells "$inst/phy_rx_error_count_reg_reg[*]"] -to [get_cells $inst/phy_rx_error_count_sync_reg_reg[*]] $clk_period +} diff --git a/fpga/common/tb/test_tdma_ber.py b/fpga/common/tb/test_tdma_ber.py new file mode 100755 index 000000000..afb621a9e --- /dev/null +++ b/fpga/common/tb/test_tdma_ber.py @@ -0,0 +1,233 @@ +#!/usr/bin/env python +""" + +Copyright 2019, The Regents of the University of California. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE REGENTS OF THE UNIVERSITY OF CALIFORNIA ''AS +IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE REGENTS OF THE UNIVERSITY OF CALIFORNIA OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY +OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of The Regents of the University of California. + +""" + +from myhdl import * +import os +import struct + +import axil +import ptp + +module = 'tdma_ber' +testbench = 'test_%s' % module + +srcs = [] + +srcs.append("../rtl/%s.v" % module) +srcs.append("../rtl/tdma_ber_ch.v") +srcs.append("../rtl/tdma_scheduler.v") +srcs.append("../lib/axi/rtl/axil_interconnect.v") +srcs.append("../lib/axi/rtl/arbiter.v") +srcs.append("../lib/axi/rtl/priority_encoder.v") +srcs.append("%s.v" % testbench) + +src = ' '.join(srcs) + +build_cmd = "iverilog -o %s.vvp %s" % (testbench, src) + +def bench(): + + # Parameters + COUNT = 2 + INDEX_WIDTH = 6 + SLICE_WIDTH = 5 + AXIL_DATA_WIDTH = 32 + AXIL_ADDR_WIDTH = INDEX_WIDTH+4+1+(COUNT-1).bit_length() + AXIL_STRB_WIDTH = (AXIL_DATA_WIDTH/8) + SCHEDULE_START_S = 0x0 + SCHEDULE_START_NS = 0x0 + SCHEDULE_PERIOD_S = 0 + SCHEDULE_PERIOD_NS = 1000000 + TIMESLOT_PERIOD_S = 0 + TIMESLOT_PERIOD_NS = 100000 + ACTIVE_PERIOD_S = 0 + ACTIVE_PERIOD_NS = 100000 + + # Inputs + clk = Signal(bool(0)) + rst = Signal(bool(0)) + current_test = Signal(intbv(0)[8:]) + + phy_tx_clk = Signal(intbv(0)[COUNT:]) + phy_rx_clk = Signal(intbv(0)[COUNT:]) + phy_rx_error_count = Signal(intbv(0)[COUNT*7:]) + s_axil_awaddr = Signal(intbv(0)[AXIL_ADDR_WIDTH:]) + s_axil_awprot = Signal(intbv(0)[3:]) + s_axil_awvalid = Signal(bool(0)) + s_axil_wdata = Signal(intbv(0)[AXIL_DATA_WIDTH:]) + s_axil_wstrb = Signal(intbv(0)[AXIL_STRB_WIDTH:]) + s_axil_wvalid = Signal(bool(0)) + s_axil_bready = Signal(bool(0)) + s_axil_araddr = Signal(intbv(0)[AXIL_ADDR_WIDTH:]) + s_axil_arprot = Signal(intbv(0)[3:]) + s_axil_arvalid = Signal(bool(0)) + s_axil_rready = Signal(bool(0)) + ptp_ts_96 = Signal(intbv(0)[96:]) + ptp_ts_step = Signal(bool(0)) + + # Outputs + phy_tx_prbs31_enable = Signal(intbv(0)[COUNT:]) + phy_rx_prbs31_enable = Signal(intbv(0)[COUNT:]) + s_axil_awready = Signal(bool(0)) + s_axil_wready = Signal(bool(0)) + s_axil_bresp = Signal(intbv(0)[2:]) + s_axil_bvalid = Signal(bool(0)) + s_axil_arready = Signal(bool(0)) + s_axil_rdata = Signal(intbv(0)[AXIL_DATA_WIDTH:]) + s_axil_rresp = Signal(intbv(0)[2:]) + s_axil_rvalid = Signal(bool(0)) + + # AXI4-Lite master + axil_master_inst = axil.AXILiteMaster() + axil_master_pause = Signal(bool(False)) + + axil_master_logic = axil_master_inst.create_logic( + clk, + rst, + m_axil_awaddr=s_axil_awaddr, + m_axil_awprot=s_axil_awprot, + m_axil_awvalid=s_axil_awvalid, + m_axil_awready=s_axil_awready, + m_axil_wdata=s_axil_wdata, + m_axil_wstrb=s_axil_wstrb, + m_axil_wvalid=s_axil_wvalid, + m_axil_wready=s_axil_wready, + m_axil_bresp=s_axil_bresp, + m_axil_bvalid=s_axil_bvalid, + m_axil_bready=s_axil_bready, + m_axil_araddr=s_axil_araddr, + m_axil_arprot=s_axil_arprot, + m_axil_arvalid=s_axil_arvalid, + m_axil_arready=s_axil_arready, + m_axil_rdata=s_axil_rdata, + m_axil_rresp=s_axil_rresp, + m_axil_rvalid=s_axil_rvalid, + m_axil_rready=s_axil_rready, + pause=axil_master_pause, + name='master' + ) + + # PTP clock + ptp_clock = ptp.PtpClock() + + ptp_logic = ptp_clock.create_logic( + clk, + rst, + ts_96=ptp_ts_96 + ) + + # DUT + if os.system(build_cmd): + raise Exception("Error running build command") + + dut = Cosimulation( + "vvp -m myhdl %s.vvp -lxt2" % testbench, + clk=clk, + rst=rst, + current_test=current_test, + phy_tx_clk=phy_tx_clk, + phy_rx_clk=phy_rx_clk, + phy_rx_error_count=phy_rx_error_count, + phy_tx_prbs31_enable=phy_tx_prbs31_enable, + phy_rx_prbs31_enable=phy_rx_prbs31_enable, + s_axil_awaddr=s_axil_awaddr, + s_axil_awprot=s_axil_awprot, + s_axil_awvalid=s_axil_awvalid, + s_axil_awready=s_axil_awready, + s_axil_wdata=s_axil_wdata, + s_axil_wstrb=s_axil_wstrb, + s_axil_wvalid=s_axil_wvalid, + s_axil_wready=s_axil_wready, + s_axil_bresp=s_axil_bresp, + s_axil_bvalid=s_axil_bvalid, + s_axil_bready=s_axil_bready, + s_axil_araddr=s_axil_araddr, + s_axil_arprot=s_axil_arprot, + s_axil_arvalid=s_axil_arvalid, + s_axil_arready=s_axil_arready, + s_axil_rdata=s_axil_rdata, + s_axil_rresp=s_axil_rresp, + s_axil_rvalid=s_axil_rvalid, + s_axil_rready=s_axil_rready, + ptp_ts_96=ptp_ts_96, + ptp_ts_step=ptp_ts_step + ) + + @always(delay(4)) + def clkgen(): + clk.next = not clk + + @always(delay(3)) + def clkgen2(): + phy_tx_clk.next = ~phy_tx_clk + phy_rx_clk.next = ~phy_rx_clk + + @instance + def check(): + yield delay(100) + yield clk.posedge + rst.next = 1 + yield clk.posedge + rst.next = 0 + yield clk.posedge + yield delay(100) + yield clk.posedge + + # testbench stimulus + + yield clk.posedge + print("test 1: Test pulse out") + current_test.next = 1 + + axil_master_inst.init_write(0x0110, struct.pack('