From 77d07a971440ba0713c109cee1973cfdc1ba69db Mon Sep 17 00:00:00 2001 From: Martin Braun Date: Fri, 27 Oct 2023 09:45:39 +0200 Subject: [PATCH] fpga/rfnoc: Add license checker block Original-commit: 5dad6c370f776981987afc2770d8a0ed0fb29575 --- .../blocks/rfnoc_block_license_check/Makefile | 49 ++++ .../rfnoc_block_license_check/Makefile.srcs | 11 + .../noc_shell_license_check.v | 161 ++++++++++++ .../rfnoc_block_license_check.v | 222 ++++++++++++++++ .../rfnoc_block_license_check_regs.vh | 64 +++++ .../rfnoc_block_license_check_tb.sv | 237 ++++++++++++++++++ lib/sec/license_check.sv | 36 ++- 7 files changed, 775 insertions(+), 5 deletions(-) create mode 100644 lib/rfnoc/blocks/rfnoc_block_license_check/Makefile create mode 100644 lib/rfnoc/blocks/rfnoc_block_license_check/Makefile.srcs create mode 100644 lib/rfnoc/blocks/rfnoc_block_license_check/noc_shell_license_check.v create mode 100644 lib/rfnoc/blocks/rfnoc_block_license_check/rfnoc_block_license_check.v create mode 100644 lib/rfnoc/blocks/rfnoc_block_license_check/rfnoc_block_license_check_regs.vh create mode 100644 lib/rfnoc/blocks/rfnoc_block_license_check/rfnoc_block_license_check_tb.sv diff --git a/lib/rfnoc/blocks/rfnoc_block_license_check/Makefile b/lib/rfnoc/blocks/rfnoc_block_license_check/Makefile new file mode 100644 index 0000000..c2f5b6c --- /dev/null +++ b/lib/rfnoc/blocks/rfnoc_block_license_check/Makefile @@ -0,0 +1,49 @@ +# +# 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 = ../../../../top +LIB_DIR = ../../../../lib +# Include viv_sim_preample 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. +include $(LIB_DIR)/rfnoc/core/Makefile.srcs +include $(LIB_DIR)/rfnoc/utils/Makefile.srcs +include $(LIB_DIR)/sec/Makefile.srcs +include $(LIB_DIR)/hwutils/Makefile.srcs +include Makefile.srcs + +DESIGN_SRCS += $(abspath \ +$(RFNOC_CORE_SRCS) \ +$(RFNOC_UTIL_SRCS) \ +$(RFNOC_BLOCK_LICENSE_CHECK_SRCS) \ +$(SEC_SRCS) \ +$(HWUTILS_SRCS) \ +) + +#------------------------------------------------- +# Testbench Specific +#------------------------------------------------- +SIM_TOP = rfnoc_block_license_check_tb glbl +SIM_SRCS = \ +$(abspath rfnoc_block_license_check_tb.sv) \ +$(VIVADO_PATH)/data/verilog/src/glbl.v \ + +#------------------------------------------------- +# 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/rfnoc/blocks/rfnoc_block_license_check/Makefile.srcs b/lib/rfnoc/blocks/rfnoc_block_license_check/Makefile.srcs new file mode 100644 index 0000000..9b79fd5 --- /dev/null +++ b/lib/rfnoc/blocks/rfnoc_block_license_check/Makefile.srcs @@ -0,0 +1,11 @@ +# +# Copyright 2023 Ettus Research, a National Instruments Brand +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +RFNOC_BLOCK_LICENSE_CHECK_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/rfnoc/blocks/rfnoc_block_license_check/, \ +noc_shell_license_check.v \ +rfnoc_block_license_check_regs.vh \ +rfnoc_block_license_check.v \ +)) diff --git a/lib/rfnoc/blocks/rfnoc_block_license_check/noc_shell_license_check.v b/lib/rfnoc/blocks/rfnoc_block_license_check/noc_shell_license_check.v new file mode 100644 index 0000000..fdd4c9e --- /dev/null +++ b/lib/rfnoc/blocks/rfnoc_block_license_check/noc_shell_license_check.v @@ -0,0 +1,161 @@ +// +// Copyright 2023 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: noc_shell_license_check +// +// Description: +// +// This is a tool-generated NoC-shell for the license_check block. +// See the RFNoC specification for more information about NoC shells. +// +// Parameters: +// +// THIS_PORTID : Control crossbar port to which this block is connected +// CHDR_W : AXIS-CHDR data bus width +// CTRL_CLK_IDX : The index of the control clock for this block. This is used +// to populate the backend interface, from where UHD can query +// the clock index and thus auto-deduct which clock is used. +// TB_CLK_IDX : The index of the timebase clock for this block. This is used +// to populate the backend interface, from where UHD can query +// the clock index and thus auto-deduct which clock is used. +// MTU : Maximum transmission unit (i.e., maximum packet size in +// CHDR words is 2**MTU). +// + +`default_nettype none + +module noc_shell_license_check #( + parameter [9:0] THIS_PORTID = 10'd0, + parameter CHDR_W = 64, + parameter [5:0] CTRL_CLK_IDX = 6'h3F, + parameter [5:0] TB_CLK_IDX = 6'h3F, + parameter [5:0] MTU = 10 +) ( + //--------------------- + // Framework Interface + //--------------------- + + // RFNoC Framework Clocks + input wire rfnoc_chdr_clk, + input wire rfnoc_ctrl_clk, + + // NoC Shell Generated Resets + output wire rfnoc_chdr_rst, + output wire rfnoc_ctrl_rst, + + // RFNoC Backend Interface + input wire [511:0] rfnoc_core_config, + output wire [511:0] rfnoc_core_status, + + // AXIS-Ctrl Control Input Port (from framework) + input wire [31:0] s_rfnoc_ctrl_tdata, + input wire s_rfnoc_ctrl_tlast, + input wire s_rfnoc_ctrl_tvalid, + output wire s_rfnoc_ctrl_tready, + // AXIS-Ctrl Control Output Port (to framework) + output wire [31:0] m_rfnoc_ctrl_tdata, + output wire m_rfnoc_ctrl_tlast, + output wire m_rfnoc_ctrl_tvalid, + input wire m_rfnoc_ctrl_tready, + + //--------------------- + // Client Interface + //--------------------- + + // CtrlPort Clock and Reset + output wire ctrlport_clk, + output wire ctrlport_rst, + // CtrlPort Master + output wire m_ctrlport_req_wr, + output wire m_ctrlport_req_rd, + output wire [19:0] m_ctrlport_req_addr, + output wire [31:0] m_ctrlport_req_data, + input wire m_ctrlport_resp_ack, + input wire [1:0] m_ctrlport_resp_status, + input wire [31:0] m_ctrlport_resp_data +); + + //--------------------------------------------------------------------------- + // Backend Interface + //--------------------------------------------------------------------------- + + backend_iface #( + .NOC_ID (32'h11C0CECC), + .NUM_DATA_I (0), + .NUM_DATA_O (0), + .CTRL_FIFOSIZE ($clog2(64)), + .MTU (MTU) + ) backend_iface_i ( + .rfnoc_chdr_clk (rfnoc_chdr_clk), + .rfnoc_chdr_rst (rfnoc_chdr_rst), + .rfnoc_ctrl_clk (rfnoc_ctrl_clk), + .rfnoc_ctrl_rst (rfnoc_ctrl_rst), + .data_i_flush_en (), + .data_i_flush_timeout (), + .data_i_flush_active (64'b0), + .data_i_flush_done (64'b1), + .data_o_flush_en (), + .data_o_flush_timeout (), + .data_o_flush_active (64'b0), + .data_o_flush_done (64'b1), + .rfnoc_core_config (rfnoc_core_config), + .rfnoc_core_status (rfnoc_core_status) + ); + + //--------------------------------------------------------------------------- + // Control Path + //--------------------------------------------------------------------------- + + assign ctrlport_clk = rfnoc_ctrl_clk; + assign ctrlport_rst = rfnoc_ctrl_rst; + + ctrlport_endpoint #( + .THIS_PORTID (THIS_PORTID), + .SYNC_CLKS (1), + .AXIS_CTRL_MST_EN (0), + .AXIS_CTRL_SLV_EN (1), + .SLAVE_FIFO_SIZE ($clog2(64)) + ) ctrlport_endpoint_i ( + .rfnoc_ctrl_clk (rfnoc_ctrl_clk), + .rfnoc_ctrl_rst (rfnoc_ctrl_rst), + .ctrlport_clk (ctrlport_clk), + .ctrlport_rst (ctrlport_rst), + .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata), + .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast), + .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid), + .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready), + .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata), + .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast), + .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid), + .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready), + .m_ctrlport_req_wr (m_ctrlport_req_wr), + .m_ctrlport_req_rd (m_ctrlport_req_rd), + .m_ctrlport_req_addr (m_ctrlport_req_addr), + .m_ctrlport_req_data (m_ctrlport_req_data), + .m_ctrlport_req_byte_en (), + .m_ctrlport_req_has_time (), + .m_ctrlport_req_time (), + .m_ctrlport_resp_ack (m_ctrlport_resp_ack), + .m_ctrlport_resp_status (m_ctrlport_resp_status), + .m_ctrlport_resp_data (m_ctrlport_resp_data), + .s_ctrlport_req_wr (1'b0), + .s_ctrlport_req_rd (1'b0), + .s_ctrlport_req_addr (20'b0), + .s_ctrlport_req_portid (10'b0), + .s_ctrlport_req_rem_epid (16'b0), + .s_ctrlport_req_rem_portid (10'b0), + .s_ctrlport_req_data (32'b0), + .s_ctrlport_req_byte_en (4'hF), + .s_ctrlport_req_has_time (1'b0), + .s_ctrlport_req_time (64'b0), + .s_ctrlport_resp_ack (), + .s_ctrlport_resp_status (), + .s_ctrlport_resp_data () + ); + +endmodule // noc_shell_license_check + + +`default_nettype wire diff --git a/lib/rfnoc/blocks/rfnoc_block_license_check/rfnoc_block_license_check.v b/lib/rfnoc/blocks/rfnoc_block_license_check/rfnoc_block_license_check.v new file mode 100644 index 0000000..b8fe498 --- /dev/null +++ b/lib/rfnoc/blocks/rfnoc_block_license_check/rfnoc_block_license_check.v @@ -0,0 +1,222 @@ +// +// Copyright 2023 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_license_check +// +// Description: +// +// This RFNoC block has no data ports. Its sole purpose is to take in a +// license key via ControlPort transactions, and verify its integrity by +// comparing it with a hash of the the device serial, a private key, and a +// feature ID. +// +// Parameters: +// +// THIS_PORTID : Control crossbar port to which this block is connected +// CHDR_W : AXIS-CHDR data bus width +// MTU : Maximum transmission unit (i.e., maximum packet size in +// CHDR words is 2**MTU). +// NUM_FEATURES: Number of features this block can unlock. The width of the +// output bus 'feature_enable' is of the same width as this +// number. Every bit in 'feature_enable' represents the locked/ +// unlocked state of one feature. +// FEATURE_IDS : A list of 32-bit feature IDs. There must be as many feature IDs +// as NUM_FEATURES (e.g., if NUM_FEATURES == 3, then there need +// to be 3 32-bit feature IDs in here). +// SERIAL_W : Width of the device serial. This is tyically 96 bits for +// RFSoC devices, and 64 bits for 7-series devices. Note that +// when using the image builder, this will be automatically +// populated. +// PKEY_W : Width of the private key. +// PRIVATE_KEY : The private key. Its length should match PKEY_W. +// + +`default_nettype none + +module rfnoc_block_license_check #( + parameter [9:0] THIS_PORTID = 10'd0, + parameter CHDR_W = 64, + parameter [5:0] MTU = 10, + parameter NUM_FEATURES = 1, + parameter FEATURE_IDS = {32'h0000}, + parameter SERIAL_W = 96, + parameter PKEY_W = 312, + parameter PRIVATE_KEY = 312'b0 +)( + // RFNoC Framework Clocks and Resets + input wire rfnoc_chdr_clk, + input wire rfnoc_ctrl_clk, + // RFNoC Backend Interface + input wire [511:0] rfnoc_core_config, + output wire [511:0] rfnoc_core_status, + // AXIS-Ctrl Input Port (from framework) + input wire [31:0] s_rfnoc_ctrl_tdata, + input wire s_rfnoc_ctrl_tlast, + input wire s_rfnoc_ctrl_tvalid, + output wire s_rfnoc_ctrl_tready, + // AXIS-Ctrl Output Port (to framework) + output wire [31:0] m_rfnoc_ctrl_tdata, + output wire m_rfnoc_ctrl_tlast, + output wire m_rfnoc_ctrl_tvalid, + input wire m_rfnoc_ctrl_tready, + + // The read-only device serial (device DNA) + input wire [SERIAL_W-1:0] serial, + + output wire [NUM_FEATURES-1:0] feature_enable +); + + `include "rfnoc_block_license_check_regs.vh" + + localparam COMPAT_MAJOR = 16'h0; + localparam COMPAT_MINOR = 16'h0; + + //--------------------------------------------------------------------------- + // Signal Declarations + //--------------------------------------------------------------------------- + + // Clocks and Resets + wire ctrlport_clk; + wire ctrlport_rst; + // CtrlPort Master + wire m_ctrlport_req_wr; + wire m_ctrlport_req_rd; + wire [19:0] m_ctrlport_req_addr; + wire [31:0] m_ctrlport_req_data; + wire m_ctrlport_resp_ack; + wire [1:0] m_ctrlport_resp_status; + wire [31:0] m_ctrlport_resp_data; + + //--------------------------------------------------------------------------- + // NoC Shell + //--------------------------------------------------------------------------- + + noc_shell_license_check #( + .CHDR_W (CHDR_W), + .THIS_PORTID (THIS_PORTID), + .MTU (MTU) + ) noc_shell_license_check_i ( + //--------------------- + // Framework Interface + //--------------------- + + // Clock Inputs + .rfnoc_chdr_clk (rfnoc_chdr_clk), + .rfnoc_ctrl_clk (rfnoc_ctrl_clk), + // Reset Outputs + .rfnoc_chdr_rst (), + .rfnoc_ctrl_rst (), + // RFNoC Backend Interface + .rfnoc_core_config (rfnoc_core_config), + .rfnoc_core_status (rfnoc_core_status), + // AXIS-Ctrl Input Port (from framework) + .s_rfnoc_ctrl_tdata (s_rfnoc_ctrl_tdata), + .s_rfnoc_ctrl_tlast (s_rfnoc_ctrl_tlast), + .s_rfnoc_ctrl_tvalid (s_rfnoc_ctrl_tvalid), + .s_rfnoc_ctrl_tready (s_rfnoc_ctrl_tready), + // AXIS-Ctrl Output Port (to framework) + .m_rfnoc_ctrl_tdata (m_rfnoc_ctrl_tdata), + .m_rfnoc_ctrl_tlast (m_rfnoc_ctrl_tlast), + .m_rfnoc_ctrl_tvalid (m_rfnoc_ctrl_tvalid), + .m_rfnoc_ctrl_tready (m_rfnoc_ctrl_tready), + + //--------------------- + // Client Interface + //--------------------- + + // CtrlPort Clock and Reset + .ctrlport_clk (ctrlport_clk), + .ctrlport_rst (ctrlport_rst), + // CtrlPort Master + .m_ctrlport_req_wr (m_ctrlport_req_wr), + .m_ctrlport_req_rd (m_ctrlport_req_rd), + .m_ctrlport_req_addr (m_ctrlport_req_addr), + .m_ctrlport_req_data (m_ctrlport_req_data), + .m_ctrlport_resp_ack (m_ctrlport_resp_ack), + .m_ctrlport_resp_status (m_ctrlport_resp_status), + .m_ctrlport_resp_data (m_ctrlport_resp_data) + + ); + + //--------------------------------------------------------------------------- + // User Logic + //--------------------------------------------------------------------------- + + localparam NUM_CTRLPORT_CLIENTS = 2; + + wire m_ctrlport_lc_req_wr, m_ctrlport_reg_req_wr; + wire m_ctrlport_lc_req_rd, m_ctrlport_reg_req_rd; + wire [19:0] m_ctrlport_lc_req_addr, m_ctrlport_reg_req_addr; + wire [31:0] m_ctrlport_lc_req_data, m_ctrlport_reg_req_data; + wire m_ctrlport_lc_resp_ack; + reg m_ctrlport_reg_resp_ack = 1'b0; + wire [ 1:0] m_ctrlport_lc_resp_status, m_ctrlport_reg_resp_status; + wire [31:0] m_ctrlport_lc_resp_data, m_ctrlport_reg_resp_data; + + ctrlport_splitter #( + .NUM_SLAVES(2) + ) ctrl_split_i ( + .ctrlport_clk(ctrlport_clk), + .ctrlport_rst(ctrlport_rst), + + .s_ctrlport_req_wr (m_ctrlport_req_wr), + .s_ctrlport_req_rd (m_ctrlport_req_rd), + .s_ctrlport_req_addr (m_ctrlport_req_addr), + .s_ctrlport_req_data (m_ctrlport_req_data), + .s_ctrlport_req_byte_en (4'hF), + .s_ctrlport_req_has_time (1'b0), + .s_ctrlport_req_time (64'h0), + .s_ctrlport_resp_ack (m_ctrlport_resp_ack), + .s_ctrlport_resp_status (m_ctrlport_resp_status), + .s_ctrlport_resp_data (m_ctrlport_resp_data), + + .m_ctrlport_req_wr ({m_ctrlport_lc_req_wr, m_ctrlport_reg_req_wr}), + .m_ctrlport_req_rd ({m_ctrlport_lc_req_rd, m_ctrlport_reg_req_rd}), + .m_ctrlport_req_addr ({m_ctrlport_lc_req_addr, m_ctrlport_reg_req_addr}), + .m_ctrlport_req_data ({m_ctrlport_lc_req_data, m_ctrlport_reg_req_data}), + .m_ctrlport_req_byte_en (/* not connected */), + .m_ctrlport_req_has_time (/* not connected */), + .m_ctrlport_req_time (/* not connected */), + .m_ctrlport_resp_ack ({m_ctrlport_lc_resp_ack, m_ctrlport_reg_resp_ack}), + .m_ctrlport_resp_status ({m_ctrlport_lc_resp_status, m_ctrlport_reg_resp_status}), + .m_ctrlport_resp_data ({m_ctrlport_lc_resp_data, m_ctrlport_reg_resp_data}) + ); + + license_check #( + .BASE_ADDR (REG_FEATURE_ID), + .PKEY_W (PKEY_W ), + .SERIAL_W (SERIAL_W ), + .NUM_FEATURES (NUM_FEATURES ), + .FEATURE_IDS (FEATURE_IDS ), + .PRIVATE_KEY (PRIVATE_KEY ) + ) license_check_i ( + .clk (ctrlport_clk), + .rst (ctrlport_rst), + + .serial (serial), + + .s_ctrlport_req_wr (m_ctrlport_lc_req_wr ), + .s_ctrlport_req_rd (m_ctrlport_lc_req_rd ), + .s_ctrlport_req_addr (m_ctrlport_lc_req_addr ), + .s_ctrlport_req_data (m_ctrlport_lc_req_data ), + .s_ctrlport_resp_ack (m_ctrlport_lc_resp_ack ), + .s_ctrlport_resp_status(m_ctrlport_lc_resp_status), + .s_ctrlport_resp_data (m_ctrlport_lc_resp_data ), + + .feature_enabled (feature_enable) + ); + + // Compat reg + always @(posedge ctrlport_clk) begin + m_ctrlport_reg_resp_ack <= m_ctrlport_reg_req_rd && m_ctrlport_reg_req_addr == REG_COMPAT; + end + assign m_ctrlport_reg_resp_data = {COMPAT_MAJOR, COMPAT_MINOR}; + assign m_ctrlport_reg_resp_status = 2'b00; + + +endmodule // rfnoc_block_license_check + + +`default_nettype wire diff --git a/lib/rfnoc/blocks/rfnoc_block_license_check/rfnoc_block_license_check_regs.vh b/lib/rfnoc/blocks/rfnoc_block_license_check/rfnoc_block_license_check_regs.vh new file mode 100644 index 0000000..22e6e38 --- /dev/null +++ b/lib/rfnoc/blocks/rfnoc_block_license_check/rfnoc_block_license_check_regs.vh @@ -0,0 +1,64 @@ +// +// Copyright 2023 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_license_check_regs (Header) +// +// Description: +// +// This is a header file that contains the register descriptions for the +// RFNoC License checker block. +// + +//----------------------------------------------------------------------------- +// Register Space +//----------------------------------------------------------------------------- + +localparam BASE_ADDR = 'h0; + + +//----------------------------------------------------------------------------- +// Replay Register Descriptions +//----------------------------------------------------------------------------- + +// REG_COMPAT (R) +// +// Compatibility version. This read-only register is used by software to +// determine if this block's version is compatible with the running software. A +// major version change indicates the software for the previous major version +// is no longer compatible. A minor version change means the previous version +// is compatible, but some new features may be unavailable. +// +// [31:16] Major version +// [15: 0] Minor version +// +localparam REG_COMPAT = 'h00; +// +localparam REG_MAJOR_POS = 16; +localparam REG_MAJOR_LEN = 16; +// +localparam REG_MINOR_POS = 0; +localparam REG_MINOR_LEN = 16; + +// REG_FEATURE_ID (W) +// +// Returns information about the size of the attached memory. The address size +// allows software to determine what buffer size and base address values are +// valid. +// +// [31:16] : Memory Data Word Size. Returns the bit width of the RAM word size. +// [15: 0] : Memory Address Size. Returns the bit width of the RAM byte +// address. That is, the memory is 2**VALUE bytes in size. +// +localparam REG_FEATURE_ID = 'h04; + +// REG_USER_KEY (W) +// +// User key register. This is where the hash part of a license key is written to, +// one 32-bit word at a time. +// +localparam REG_USER_KEY = 'h08; + + +localparam REG_FEAT_ENB_RB = 'h0C; diff --git a/lib/rfnoc/blocks/rfnoc_block_license_check/rfnoc_block_license_check_tb.sv b/lib/rfnoc/blocks/rfnoc_block_license_check/rfnoc_block_license_check_tb.sv new file mode 100644 index 0000000..26b6218 --- /dev/null +++ b/lib/rfnoc/blocks/rfnoc_block_license_check/rfnoc_block_license_check_tb.sv @@ -0,0 +1,237 @@ +// +// Copyright 2023 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: rfnoc_block_license_check_tb +// +// Description: Testbench for the license_check RFNoC block. +// + +`default_nettype none + + +module rfnoc_block_license_check_tb; + + `include "test_exec.svh" + + import PkgTestExec::*; + import PkgChdrUtils::*; + import PkgRfnocBlockCtrlBfm::*; + import PkgRfnocItemUtils::*; + + //--------------------------------------------------------------------------- + // Testbench Configuration + //--------------------------------------------------------------------------- + + localparam [31:0] NOC_ID = 32'h11C0CECC; + localparam [ 9:0] THIS_PORTID = 10'h123; + localparam int CHDR_W = 64; // CHDR size in bits + localparam int MTU = 10; // Log2 of max transmission unit in CHDR words + localparam int NUM_FEATURES = 2; + localparam [(32*NUM_FEATURES)-1:0] FEATURE_IDS = {32'hF00D, 32'hC0DE}; + localparam int NUM_PORTS_I = 0; + localparam int NUM_PORTS_O = 0; + localparam int ITEM_W = 32; // Sample size in bits + localparam int SPP = 64; // Samples per packet + localparam int PKT_SIZE_BYTES = SPP * (ITEM_W/8); + localparam real CHDR_CLK_PER = 5.0; // 200 MHz + localparam real CTRL_CLK_PER = 8.0; // 125 MHz + localparam int SERIAL_W = 96; + localparam int PKEY_W = 312; + localparam [SERIAL_W-1:0] SERIAL = 96'h012345678ABCDEF00C0DE0BB; + localparam [PKEY_W-1:0] PRIVATE_KEY = 312'h5EC; + + localparam COMPAT_ADDR = 0; + localparam FID_ADDR = 4; + localparam KEY_ADDR = 8; + localparam FID_RB_ADDR = 12; + localparam LIST_RB_ADDR = 16; + localparam int FEATURE0 = 32'hC0DE; + localparam int FEATURE1 = 32'hF00D; + + localparam hash0_0 = 32'h80f122aa; + localparam hash0_1 = 32'hd504a1ec; + localparam hash0_2 = 32'h7ae8f37b; + localparam hash0_3 = 32'h4eee414c; + localparam hash0_4 = 32'hf1cf32ec; + localparam hash0_5 = 32'hc543ee2e; + localparam hash0_6 = 32'h1d4e1d4a; + localparam hash0_7 = 32'he59eb41b; + localparam hash1_0 = 32'hefd81f78; + localparam hash1_1 = 32'ha5fdee0a; + localparam hash1_2 = 32'h16ed039c; + localparam hash1_3 = 32'hfa51db76; + localparam hash1_4 = 32'h9a8da088; + localparam hash1_5 = 32'h328f8ed7; + localparam hash1_6 = 32'h3d6af85b; + localparam hash1_7 = 32'ha1ce57d4; + + + //--------------------------------------------------------------------------- + // Clocks and Resets + //--------------------------------------------------------------------------- + + bit rfnoc_chdr_clk; + bit rfnoc_ctrl_clk; + + sim_clock_gen #(.PERIOD(CHDR_CLK_PER)) + rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst()); + sim_clock_gen #(.PERIOD(CHDR_CLK_PER)) + rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst()); + + //--------------------------------------------------------------------------- + // Bus Functional Models + //--------------------------------------------------------------------------- + + // Backend Interface + RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk); + + // AXIS-Ctrl Interface + AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0); + AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0); + + // Block Controller BFM + RfnocBlockCtrlBfmCtrlOnly blk_ctrl = new(backend, m_ctrl, s_ctrl); + + //--------------------------------------------------------------------------- + // Device Under Test (DUT) + //--------------------------------------------------------------------------- + + logic [NUM_FEATURES-1:0] feature_enable; + + rfnoc_block_license_check #( + .THIS_PORTID (THIS_PORTID), + .CHDR_W (CHDR_W), + .MTU (MTU), + .NUM_FEATURES (NUM_FEATURES), + .FEATURE_IDS (FEATURE_IDS), + .SERIAL_W (SERIAL_W), + .PRIVATE_KEY (312'h5EC) + ) dut ( + .rfnoc_chdr_clk (rfnoc_chdr_clk), + .rfnoc_ctrl_clk (rfnoc_ctrl_clk), + .rfnoc_core_config (backend.cfg), + .rfnoc_core_status (backend.sts), + .s_rfnoc_ctrl_tdata (m_ctrl.tdata), + .s_rfnoc_ctrl_tlast (m_ctrl.tlast), + .s_rfnoc_ctrl_tvalid (m_ctrl.tvalid), + .s_rfnoc_ctrl_tready (m_ctrl.tready), + .m_rfnoc_ctrl_tdata (s_ctrl.tdata), + .m_rfnoc_ctrl_tlast (s_ctrl.tlast), + .m_rfnoc_ctrl_tvalid (s_ctrl.tvalid), + .m_rfnoc_ctrl_tready (s_ctrl.tready), + + .serial (SERIAL), + .feature_enable (feature_enable) + ); + + //--------------------------------------------------------------------------- + // Main Test Process + //--------------------------------------------------------------------------- + + initial begin : tb_main + + // Initialize the test exec object for this testbench + test.start_tb("rfnoc_block_license_check_tb"); + + // Start the BFMs running + blk_ctrl.run(); + + //-------------------------------- + // Reset + //-------------------------------- + + test.start_test("Reset block", 10us); + blk_ctrl.reset_ctrl(); + test.end_test(); + + //-------------------------------- + // Verify Block Info + //-------------------------------- + + test.start_test("Verify Block Info", 2us); + `ASSERT_ERROR(blk_ctrl.get_noc_id() == NOC_ID, "Incorrect NOC_ID Value"); + `ASSERT_ERROR(blk_ctrl.get_num_data_i() == NUM_PORTS_I, "Incorrect NUM_DATA_I Value"); + `ASSERT_ERROR(blk_ctrl.get_num_data_o() == NUM_PORTS_O, "Incorrect NUM_DATA_O Value"); + `ASSERT_ERROR(blk_ctrl.get_mtu() == MTU, "Incorrect MTU Value"); + begin + logic [31:0] rb_val; + blk_ctrl.reg_read(COMPAT_ADDR, rb_val); + `ASSERT_ERROR(rb_val == 32'h00000000, + $sformatf("Compat readback incorrect! (0x%X)", rb_val)); + end + test.end_test(); + + //-------------------------------- + // Test Sequences + //-------------------------------- + + begin + logic [31:0] rb_val; + test.start_test("Unlock feature0", 100us); + blk_ctrl.reg_write(FID_ADDR, FEATURE0); + blk_ctrl.reg_write(KEY_ADDR, hash0_0); + blk_ctrl.reg_write(KEY_ADDR, hash0_1); + blk_ctrl.reg_write(KEY_ADDR, hash0_2); + blk_ctrl.reg_write(KEY_ADDR, hash0_3); + blk_ctrl.reg_write(KEY_ADDR, hash0_4); + blk_ctrl.reg_write(KEY_ADDR, hash0_5); + blk_ctrl.reg_write(KEY_ADDR, hash0_6); + blk_ctrl.reg_write(KEY_ADDR, hash0_7); + `ASSERT_ERROR(feature_enable[0], "Feature 0 was not unlocked!"); + blk_ctrl.reg_read(FID_RB_ADDR, rb_val); + `ASSERT_ERROR(rb_val == 32'h80000000, + $sformatf("Feature enable readback incorrect! (0x%X)", rb_val)); + test.end_test(); + end + + + begin + logic [31:0] rb_val; + test.start_test("Unlock feature1", 100us); + blk_ctrl.reg_write(FID_ADDR, FEATURE1); + blk_ctrl.reg_write(KEY_ADDR, hash1_0); + blk_ctrl.reg_write(KEY_ADDR, hash1_1); + blk_ctrl.reg_write(KEY_ADDR, hash1_2); + blk_ctrl.reg_write(KEY_ADDR, hash1_3); + blk_ctrl.reg_write(KEY_ADDR, hash1_4); + blk_ctrl.reg_write(KEY_ADDR, hash1_5); + blk_ctrl.reg_write(KEY_ADDR, hash1_6); + blk_ctrl.reg_write(KEY_ADDR, hash1_7); + `ASSERT_ERROR(feature_enable[1], "Feature 1 was not unlocked!"); + blk_ctrl.reg_read(FID_RB_ADDR, rb_val); + `ASSERT_ERROR(rb_val == 32'h80000001, + $sformatf("Feature enable readback incorrect! (0x%X)", rb_val)); + test.end_test(); + end + + begin + logic [31:0] rb_val; + test.start_test("Reading back feature IDs", 100us); + blk_ctrl.reg_read(LIST_RB_ADDR, rb_val); + `ASSERT_ERROR(rb_val == 32'hC0DE, + $sformatf("Feature ID readback incorrect! (0x%X)", rb_val)); + blk_ctrl.reg_read(LIST_RB_ADDR, rb_val); + `ASSERT_ERROR(rb_val == 32'hF00D, + $sformatf("Feature ID readback incorrect! (0x%X)", rb_val)); + blk_ctrl.reg_read(LIST_RB_ADDR, rb_val); + `ASSERT_ERROR(rb_val == 32'hC0DE, + $sformatf("Feature ID readback incorrect! (0x%X)", rb_val)); + blk_ctrl.reg_read(FID_RB_ADDR, rb_val); + `ASSERT_ERROR(rb_val == 32'h80000001, + $sformatf("Feature enable readback incorrect! (0x%X)", rb_val)); + test.end_test(); + end + + //-------------------------------- + // Finish Up + //-------------------------------- + + // Display final statistics and results + test.end_tb(); + end : tb_main + +endmodule : rfnoc_block_license_check_tb + +`default_nettype wire diff --git a/lib/sec/license_check.sv b/lib/sec/license_check.sv index 7623804..fbb7aae 100644 --- a/lib/sec/license_check.sv +++ b/lib/sec/license_check.sv @@ -43,6 +43,12 @@ // ID that was written. Bit 31 tells us if the feature // was unlocked. The rest tells us which feature index // it is. +// - RB_FID_ADDR (BASE_ADDR+12): Readback, will return a feature flag this +// module was compiled with. On the next read, +// it will return the next feature ID. To +// learn all feature IDs that this module knows, +// keep reading this register until values +// repeat. // // Parameters: // @@ -64,8 +70,7 @@ module license_check #( parameter SERIAL_W = 96, parameter NUM_FEATURES = 1, parameter [(32*NUM_FEATURES)-1:0] FEATURE_IDS = {32'hC0DE}, - parameter [PKEY_W-1:0] PRIVATE_KEY = 0, - parameter DEVICE_TYPE = "ULTRASCALE" + parameter [PKEY_W-1:0] PRIVATE_KEY = 0 )( input wire clk, input wire rst, @@ -113,6 +118,7 @@ module license_check #( localparam FID_ADDR = BASE_ADDR; localparam KEY_ADDR = BASE_ADDR + 4; localparam RB_ADDR = BASE_ADDR + 8; + localparam RB_FID_ADDR = BASE_ADDR + 12; // States typedef enum logic [2:0] { @@ -123,6 +129,11 @@ module license_check #( ST_CTRLPORT_ERROR } state_t; + typedef enum logic { + ST_RB_ENABLED, + ST_RB_FIDS + } rb_state_t; + // Registers & Wires state_t state = ST_IDLE; reg key_valid = 1'b0; // Tracks if current hash matches @@ -130,6 +141,9 @@ module license_check #( reg [2:0] word_cnt = 3'd7; // Track which word we are currently comparing // The index of the feature ID we're hashing/comparing reg [FIDXREG_W-1:0] feature_idx = NO_FID; + // The index of the last feature ID we returned on RB_FID_ADDR + reg [FIDXREG_W-1:0] rb_feature_idx = (NUM_FEATURES - 1); + reg rb_last_req = ST_RB_ENABLED; wire fid_wr_req = (s_ctrlport_req_wr && (s_ctrlport_req_addr == FID_ADDR)); wire key_wr_req = (s_ctrlport_req_wr && (s_ctrlport_req_addr == KEY_ADDR)); @@ -172,6 +186,8 @@ module license_check #( // On reset, all features are disabled. Otherwise, they always stay on. feature_enabled <= {NUM_FEATURES{1'b0}}; feature_idx <= NO_FID; + // Reset the readback feature index + rb_feature_idx <= (NUM_FEATURES - 1); end else case (state) ST_IDLE : begin //// Idle state: We're waiting on input via CtrlPort. @@ -225,6 +241,14 @@ module license_check #( // Handle readback end else if (s_ctrlport_req_rd && (s_ctrlport_req_addr == RB_ADDR)) begin state <= ST_ACK; + rb_last_req <= ST_RB_ENABLED; + + end else if (s_ctrlport_req_rd && (s_ctrlport_req_addr == RB_FID_ADDR)) begin + state <= ST_ACK; + rb_last_req <= ST_RB_FIDS; + rb_feature_idx <= rb_feature_idx == (NUM_FEATURES-1) + ? {FIDXREG_W{1'b0}} + : (rb_feature_idx + 1); // Any other read/write request is ignored end else begin @@ -257,9 +281,11 @@ module license_check #( assign s_ctrlport_resp_status = (state == ST_CTRLPORT_ERROR) ? CTRL_STS_CMDERR : CTRL_STS_OKAY; - assign s_ctrlport_resp_data = (feature_idx == NO_FID) - ? 32'b0 - : {feature_enabled[feature_idx], {(31-FIDXREG_W){1'b0}}, feature_idx}; + assign s_ctrlport_resp_data = (rb_last_req == ST_RB_ENABLED) ? + ((feature_idx == NO_FID) + ? 32'b0 + : {feature_enabled[feature_idx], {(31-FIDXREG_W){1'b0}}, feature_idx}) + : (FEATURE_IDS[(32*rb_feature_idx) +: 32]); endmodule