fpga: Add module for reading Xilinx device DNA
- This provides a CtrlPort access to reading back the device DNA - Available for US+ devices (i.e., RFSoC) and 7-series. Original-commit: 43856f90da01cebc1e4b327f4435966ccd1df602
This commit is contained in:
@@ -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 \
|
||||
))
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user