diff --git a/lib/hwutils/Makefile.srcs b/lib/hwutils/Makefile.srcs new file mode 100644 index 0000000..1ae5ef2 --- /dev/null +++ b/lib/hwutils/Makefile.srcs @@ -0,0 +1,13 @@ +# +# Copyright 2023 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +################################################## +# Hardware Utilities Sources +################################################## +HWUTILS_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/hwutils/, \ +device_dna.v \ +device_dna_ctrlport.v \ +)) diff --git a/lib/hwutils/device_dna.v b/lib/hwutils/device_dna.v new file mode 100644 index 0000000..474f8af --- /dev/null +++ b/lib/hwutils/device_dna.v @@ -0,0 +1,117 @@ +// +// Copyright 2023 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +// Module: device_dna +// +// Description: +// +// Read back the PL DNA. +// When reset, this module will shift the PL DNA into an output register. +// +// Parameters: +// +// DNA_WIDTH: The width of the DNA register. UltraScale(+) devices have a 96-bit +// DNA, 7-series have a 57-bit DNA. If DNA_WIDTH is smaller than +// that, only LSBs will be output. If it's larger, then the DNA +// will be zero-padded. +// DEVICE_TYPE: Either ULTRASCALE or 7SERIES. +// +// Signals: +// +// dna: This register will hold the full DNA value. +// valid: This is low while the DNA value register is being populated. Only +// when high is the value in `dna' valid. +// + +`default_nettype none + +module device_dna #( + // Number of bits of DNA to output + parameter DNA_WIDTH = 96, + // For future use: Different FPGA types have different primitives for reading DNA + parameter DEVICE_TYPE = "ULTRASCALE" +)( + input wire clk, + input wire rst, + + // The device DNA + output reg [DNA_WIDTH-1:0] dna, + // This output is low while the dna register is being populated + output wire valid +); + + localparam ST_RESET = 2'd0; + localparam ST_READ = 2'd1; + localparam ST_SHIFT = 2'd2; + localparam ST_DONE = 2'd3; + + localparam MAX_SHIFT = (DEVICE_TYPE == "7SERIES") ? 57 : DNA_WIDTH; + + reg [1:0] state = ST_RESET; + reg [$clog2(DNA_WIDTH)-1:0] bit_cnt = 0; + + wire read; + wire shift; + wire dout0; + + + always @(posedge clk) begin + if (rst) begin + dna <= {DNA_WIDTH{1'b0}}; + state <= ST_READ; + end else if (state == ST_READ) begin + state <= ST_SHIFT; + bit_cnt <= MAX_SHIFT-1; + end else if (state == ST_SHIFT) begin + // Ultrascale shifts the DNA out LSB first, 7-series shifts the DNA out + // MSB first (cf. UG-470 and UG-570 for 7-series and Ultrascale). + if (DEVICE_TYPE == "ULTRASCALE") begin + dna <= { dout0, dna[DNA_WIDTH-1:1] }; + end else if (DEVICE_TYPE == "7SERIES") begin + dna <= { dna[DNA_WIDTH-2:0], dout0 }; + end + state <= (bit_cnt == 0) ? ST_DONE : ST_SHIFT; + bit_cnt <= bit_cnt - 1; + end else begin + // Nothing in ST_DONE + end + end + + assign read = state == ST_READ; + assign shift = state == ST_SHIFT; + assign valid = state == ST_DONE; + + if (DEVICE_TYPE == "ULTRASCALE") begin : gen_ultrascale_dna + + DNA_PORTE2 #( + .SIM_DNA_VALUE(96'h12F1110_C0D111A0_11C0FFEE) + ) dna_inst ( + .CLK (clk), + .DIN (1'b0), + .READ (read), + .SHIFT(shift), + .DOUT (dout0) + ); + + end else if (DEVICE_TYPE == "7SERIES") begin : gen_7series_dna + + DNA_PORT #( + .SIM_DNA_VALUE(57'h0D111A0_C0DE00FF) + ) dna_inst ( + .CLK (clk), + .DIN (1'b0), + .READ (read), + .SHIFT(shift), + .DOUT (dout0) + ); + + end else begin : gen_assert + ERROR_invalid_device_type(); + end + +endmodule + +`default_nettype wire diff --git a/lib/hwutils/device_dna_ctrlport.v b/lib/hwutils/device_dna_ctrlport.v new file mode 100644 index 0000000..340cf8e --- /dev/null +++ b/lib/hwutils/device_dna_ctrlport.v @@ -0,0 +1,97 @@ + +// +// Copyright 2023 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// + +// Module: device_dna_ctrlport +// +// Description: +// +// Read back the PL DNA via CtrlPort transactions, 32-bit at a time. +// +// This module will return the PL DNA via a 32-bit ctrlport transaction. This +// means the DNA value is split up into multiple registers. For example, if a +// 96 bit DNA width is selected, there will be three consecutive registers +// holding the DNA value. +// +// Note the DNA value width is chip-dependent. For example, Ultrascale+ devices +// like the RFSoC have a 96-bit DNA value. Everything above the 96 bits would be +// zero-padded. +// +// After resetting, it takes some clock cycles to load the DNA value. During +// this time, transactions will return an error code. +// +// Parameters: +// +// BASE_ADDR: Readback address for the 32 LSBs of the device DNA. The next +// 32 bits will be addressable at BASE_ADDR+4, and so on. +// DNA_WIDTH: The width of the DNA register. UltraScale(+) devices have a 96-bit +// DNA, 7-series have a 57-bit DNA. If DNA_WIDTH is smaller than +// that, only LSBs will be output. If it's larger, then the DNA +// will be zero-padded. +// DEVICE_TYPE: Either ULTRASCALE or 7SERIES. +// + +`default_nettype none + +module device_dna_ctrlport #( + parameter BASE_ADDR = 0, + // Number of bits of DNA to output + parameter DNA_WIDTH = 96, + // For future use: Different FPGA types have different primitives for reading DNA + parameter DEVICE_TYPE = "ULTRASCALE" +)( + input wire ctrlport_clk, + input wire reset, + + input wire s_ctrlport_req_rd, + input wire [19:0] s_ctrlport_req_addr, + output wire s_ctrlport_resp_ack, + output wire [ 1:0] s_ctrlport_resp_status, + output wire [31:0] s_ctrlport_resp_data +); + + `include "../rfnoc/core/ctrlport.vh" + + if (DEVICE_TYPE != "ULTRASCALE" && DEVICE_TYPE != "7SERIES") begin : gen_assertion + ERROR_only_ultrascale_and_7series_supported(); + end + + wire [DNA_WIDTH-1:0] device_dna_value; + wire device_dna_valid; + wire [1:0] reg_ro_status; + + device_dna #( + .DNA_WIDTH(DNA_WIDTH), + .DEVICE_TYPE(DEVICE_TYPE) + ) device_dna_i ( + .clk (ctrlport_clk), + .rst (reset), + .dna (device_dna_value), + .valid(device_dna_valid) + ); + + ctrlport_reg_ro #( + .ADDR (BASE_ADDR), + .WIDTH (DNA_WIDTH) + // Don't need to assert COHERENT, because device_dna_value won't change unless + // device_dna_valid is also deasserted + ) dna_ctrlport_reg_ro_i ( + .ctrlport_clk (ctrlport_clk ), + .s_ctrlport_req_rd (s_ctrlport_req_rd ), + .s_ctrlport_req_addr (s_ctrlport_req_addr ), + .s_ctrlport_resp_ack (s_ctrlport_resp_ack ), + .s_ctrlport_resp_status (reg_ro_status ), + .s_ctrlport_resp_data (s_ctrlport_resp_data), + .value_in (device_dna_value ) + ); + + // If we don't have a valid timestamp yet, we finish transaction, + // but with an error code + assign s_ctrlport_resp_status = device_dna_valid ? reg_ro_status : CTRL_STS_CMDERR; + +endmodule + +`default_nettype wire diff --git a/lib/sim/hwutils/dna/Makefile b/lib/sim/hwutils/dna/Makefile new file mode 100644 index 0000000..5467bc7 --- /dev/null +++ b/lib/sim/hwutils/dna/Makefile @@ -0,0 +1,42 @@ +# +# Copyright 2023 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +#------------------------------------------------- +# Top-of-Makefile +#------------------------------------------------- +# Define BASE_DIR to point to the "top" dir +BASE_DIR = $(abspath ../../../../top) +# Include viv_sim_preamble after defining BASE_DIR +include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak + +#------------------------------------------------- +# Design Specific +#------------------------------------------------- +# Include makefiles and sources for the DUT and its dependencies + +DESIGN_SRCS += \ +$(abspath ../../../hwutils/device_dna.v) \ +$(abspath ../../../hwutils/device_dna_ctrlport.v) \ +$(abspath ../../../rfnoc/utils/ctrlport_reg_ro.v) \ +$(VIVADO_PATH)/data/verilog/src/glbl.v \ + +MODELSIM_ARGS += glbl + +#------------------------------------------------- +# Testbench Specific +#------------------------------------------------- +SIM_TOP = device_dna_tb + +SIM_SRCS = \ +$(abspath device_dna_tb.sv) \ + +#------------------------------------------------- +# Bottom-of-Makefile +#------------------------------------------------- +# Include all simulator specific makefiles here +# Each should define a unique target to simulate +# e.g. xsim, vsim, etc and a common "clean" target +include $(BASE_DIR)/../tools/make/viv_simulator.mak diff --git a/lib/sim/hwutils/dna/device_dna_tb.sv b/lib/sim/hwutils/dna/device_dna_tb.sv new file mode 100644 index 0000000..bc84cee --- /dev/null +++ b/lib/sim/hwutils/dna/device_dna_tb.sv @@ -0,0 +1,206 @@ +// +// Copyright 2023 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Description: +// +// Testbench for device_dna and device_dna_ctrlport. +// + +`default_nettype none + + +module device_dna_tb (); + + // Include macros and time declarations for use with PkgTestExec + `include "test_exec.svh" + import PkgTestExec::*; + + localparam real CLK_PERIOD = 10.0; // ns + + //--------------------------------------------------------------------------- + // Clocks and Resets + //--------------------------------------------------------------------------- + + bit clk; + bit rst; + bit rst_dut = 0; + + sim_clock_gen #(.PERIOD(CLK_PERIOD)) + clk_gen (.clk(clk), .rst(rst)); + + //--------------------------------------------------------------------------- + // Device Under Test (DUT) + //--------------------------------------------------------------------------- + + logic i_ctrlport_req_rd = 1'b0; + logic [19:0] i_ctrlport_req_addr = 20'h0; + logic o_ctrlport_resp_ack; + logic [1:0] o_ctrlport_resp_status; + logic [31:0] o_ctrlport_resp_data; + + logic o_ctrlport_resp_ack_7s; + logic [1:0] o_ctrlport_resp_status_7s; + logic [31:0] o_ctrlport_resp_data_7s; + + + device_dna_ctrlport #( + .BASE_ADDR(0), + .DNA_WIDTH(128) + ) device_dna_dut ( + .ctrlport_clk (clk), + .reset (rst | rst_dut), + + .s_ctrlport_req_rd (i_ctrlport_req_rd ), + .s_ctrlport_req_addr (i_ctrlport_req_addr ), + .s_ctrlport_resp_ack (o_ctrlport_resp_ack ), + .s_ctrlport_resp_status(o_ctrlport_resp_status), + .s_ctrlport_resp_data (o_ctrlport_resp_data ) + ); + + device_dna_ctrlport #( + .BASE_ADDR(32), + .DNA_WIDTH(57), + .DEVICE_TYPE("7SERIES") + ) device_dna_dut_7s ( + .ctrlport_clk (clk), + .reset (rst | rst_dut), + + .s_ctrlport_req_rd (i_ctrlport_req_rd ), + .s_ctrlport_req_addr (i_ctrlport_req_addr ), + .s_ctrlport_resp_ack (o_ctrlport_resp_ack_7s ), + .s_ctrlport_resp_status(o_ctrlport_resp_status_7s), + .s_ctrlport_resp_data (o_ctrlport_resp_data_7s ) + ); + + //-------------------------------- + // Task + //-------------------------------- + task automatic check_dna( + int address, + int expected_dna = 1 + ); + i_ctrlport_req_addr <= address; + i_ctrlport_req_rd <= 1; + @(posedge clk); + i_ctrlport_req_rd <= 0; + while (!o_ctrlport_resp_ack) @(posedge clk); + test.assert_error( + o_ctrlport_resp_ack, "CtrlPort response ACK not asserted!"); + test.assert_error( + !o_ctrlport_resp_status, + $sformatf("CtrlPort response status not zero (%d)!", o_ctrlport_resp_status)); + test.assert_error( + o_ctrlport_resp_data == expected_dna, + $sformatf("Incorrect DNA value: %x! Expected %x.", o_ctrlport_resp_data, expected_dna)); + @(posedge clk); + endtask; + + task automatic check_dna_7s( + int address, + int expected_dna = 1 + ); + i_ctrlport_req_addr <= address; + i_ctrlport_req_rd <= 1; + @(posedge clk); + i_ctrlport_req_rd <= 0; + while (!o_ctrlport_resp_ack_7s) @(posedge clk); + test.assert_error( + o_ctrlport_resp_ack_7s, "CtrlPort response ACK not asserted!"); + test.assert_error( + !o_ctrlport_resp_status_7s, + $sformatf("CtrlPort response status not zero (%d)!", o_ctrlport_resp_status_7s)); + test.assert_error( + o_ctrlport_resp_data_7s == expected_dna, + $sformatf("Incorrect DNA value: %x! Expected %x.", o_ctrlport_resp_data_7s, expected_dna)); + @(posedge clk); + endtask; + + //--------------------------------------------------------------------------- + // Main Test Process + //--------------------------------------------------------------------------- + + initial begin : tb_main + string tb_name; + + tb_name = $sformatf("device_dna_ctrlport"); + test.start_tb(tb_name, 1ms); + + //------------------------------------------------------- + // Reset clock generator and wait for reset to complete + //------------------------------------------------------- + + test.start_test("Reset", 100us); + clk_gen.reset(); + if (rst) @rst; + test.end_test(); + + //-------------------------------- + // Test Sequences + //-------------------------------- + + // Reset DNA module and verify that we cannot read back serial immediately after + test.start_test("Reset DUT only", 2us); + rst_dut <= 1; // Assert reset for one clock cycle + @(posedge clk); + rst_dut <= 0; + // Now we wait for some amount of clock cycles that is smaller than the time + // it takes to fully load the DNA value (at least 96 cycles on any UltraScale + // device) + repeat (12) @(posedge clk); + // Request a read and wait for ACK + i_ctrlport_req_rd <= 1; + @(posedge clk); + i_ctrlport_req_rd <= 0; + while (!o_ctrlport_resp_ack) @(posedge clk); + test.assert_error( + o_ctrlport_resp_status == 2'b01, + $sformatf("Response status should be 1, but is %d", o_ctrlport_resp_status)); + // Oh no! We call reset *again*, in the middle of a read. + rst_dut <= 1; // Assert reset for one clock cycle + @(posedge clk); + rst_dut <= 0; + // Now wait for status to clear + while (o_ctrlport_resp_status) @(posedge clk); + test.end_test(); + + // Read back the DNA value + test.start_test("Reading DNA values", 1ms); + check_dna(0, 32'h11C0FFEE); + check_dna(4, 32'hC0D111A0); + check_dna(8, 32'h012F1110); + @(posedge clk); + test.end_test(); + + // One more reset and read + test.start_test("Reading DNA values after 2nd reset", 1ms); + rst_dut <= 1; // Assert reset for one clock cycle + @(posedge clk); + rst_dut <= 0; + @(posedge clk); + // Now wait for status to clear + while (o_ctrlport_resp_status) @(posedge clk); + check_dna(0, 32'h11C0FFEE); + test.end_test(); + + // Read back the DNA value (7-series) + test.start_test("Reading DNA values (7-series)", 1ms); + check_dna_7s(32, 32'hC0DE00FF); + check_dna_7s(36, 32'h00D111A0); + @(posedge clk); + test.end_test(); + + + + //-------------------------------- + // Finish Up + //-------------------------------- + + test.end_tb(); + + end : tb_main + +endmodule : device_dna_tb + +`default_nettype wire