fpga: rfnoc: Add Signal Generator RFNoC block
Original-commit: 1e94f85b8bafc3f9acab7ef35d2675fa7e61f6f4
This commit is contained in:
committed by
Aaron Rossetto
parent
3e1e0be3bc
commit
2eb71626fb
@@ -102,6 +102,7 @@ ddc.v \
|
||||
duc.v \
|
||||
cic_decimate.v \
|
||||
cic_interpolate.v \
|
||||
sine_tone.v \
|
||||
axi_fir_filter.v \
|
||||
fir_filter_slice.v \
|
||||
axi_fir_filter_dec.v \
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#
|
||||
# Copyright 2020 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. Note:
|
||||
# UHD_FPGA_DIR must be passed into this Makefile.
|
||||
BASE_DIR = ../../../../top
|
||||
# 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 $(BASE_DIR)/../lib/rfnoc/core/Makefile.srcs
|
||||
include $(BASE_DIR)/../lib/rfnoc/utils/Makefile.srcs
|
||||
include $(LIB_IP_DIR)/cordic_rotator/Makefile.inc
|
||||
include Makefile.srcs
|
||||
|
||||
DESIGN_SRCS += $(abspath \
|
||||
$(RFNOC_CORE_SRCS) \
|
||||
$(RFNOC_UTIL_SRCS) \
|
||||
$(RFNOC_OOT_SRCS) \
|
||||
$(LIB_IP_CORDIC_ROTATOR_SRCS) \
|
||||
)
|
||||
|
||||
#-------------------------------------------------
|
||||
# Testbench Specific
|
||||
#-------------------------------------------------
|
||||
SIM_TOP = rfnoc_block_siggen_all_tb glbl
|
||||
SIM_SRCS = \
|
||||
$(abspath $(IP_BUILD_DIR)/cordic_rotator/sim/cordic_rotator.vhd) \
|
||||
$(VIVADO_PATH)/data/verilog/src/glbl.v \
|
||||
$(abspath rfnoc_block_siggen_tb.sv) \
|
||||
$(abspath rfnoc_block_siggen_all_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,24 @@
|
||||
#
|
||||
# Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
#
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
#
|
||||
|
||||
##################################################
|
||||
# RFNoC Block Sources
|
||||
##################################################
|
||||
# Here, list all the files that are necessary to synthesize this block. Don't
|
||||
# include testbenches!
|
||||
# Make sure that the source files are nicely detectable by a regex. Best to put
|
||||
# one on each line.
|
||||
# The first argument to addprefix is the current path to this Makefile, so the
|
||||
# path list is always absolute, regardless of from where we're including or
|
||||
# calling this file. RFNOC_OOT_SRCS needs to be a simply expanded variable
|
||||
# (not a recursively expanded variable), and we take care of that in the build
|
||||
# infrastructure.
|
||||
RFNOC_OOT_SRCS += $(addprefix $(dir $(abspath $(lastword $(MAKEFILE_LIST)))), \
|
||||
noc_shell_siggen.v \
|
||||
rfnoc_siggen_core.v \
|
||||
rfnoc_block_siggen_regs.vh \
|
||||
rfnoc_block_siggen.v \
|
||||
)
|
||||
@@ -0,0 +1,263 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: noc_shell_siggen
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// This is a tool-generated NoC-shell for the siggen 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
|
||||
// MTU : Maximum transmission unit (i.e., maximum packet size in
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module noc_shell_siggen #(
|
||||
parameter [9:0] THIS_PORTID = 10'd0,
|
||||
parameter CHDR_W = 64,
|
||||
parameter [5:0] MTU = 10,
|
||||
parameter NUM_PORTS = 1
|
||||
) (
|
||||
//---------------------
|
||||
// Framework Interface
|
||||
//---------------------
|
||||
|
||||
// RFNoC Framework Clocks
|
||||
input wire rfnoc_chdr_clk,
|
||||
input wire rfnoc_ctrl_clk,
|
||||
input wire ce_clk,
|
||||
|
||||
// NoC Shell Generated Resets
|
||||
output wire rfnoc_chdr_rst,
|
||||
output wire rfnoc_ctrl_rst,
|
||||
output wire ce_rst,
|
||||
|
||||
// RFNoC Backend Interface
|
||||
input wire [511:0] rfnoc_core_config,
|
||||
output wire [511:0] rfnoc_core_status,
|
||||
|
||||
// AXIS-CHDR Input Ports (from framework)
|
||||
input wire [(1)*CHDR_W-1:0] s_rfnoc_chdr_tdata,
|
||||
input wire [(1)-1:0] s_rfnoc_chdr_tlast,
|
||||
input wire [(1)-1:0] s_rfnoc_chdr_tvalid,
|
||||
output wire [(1)-1:0] s_rfnoc_chdr_tready,
|
||||
// AXIS-CHDR Output Ports (to framework)
|
||||
output wire [(0+NUM_PORTS)*CHDR_W-1:0] m_rfnoc_chdr_tdata,
|
||||
output wire [(0+NUM_PORTS)-1:0] m_rfnoc_chdr_tlast,
|
||||
output wire [(0+NUM_PORTS)-1:0] m_rfnoc_chdr_tvalid,
|
||||
input wire [(0+NUM_PORTS)-1:0] m_rfnoc_chdr_tready,
|
||||
|
||||
// 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 [31:0] m_ctrlport_resp_data,
|
||||
|
||||
// AXI-Stream Data Clock and Reset
|
||||
output wire axis_data_clk,
|
||||
output wire axis_data_rst,
|
||||
// Data Stream to User Logic: out
|
||||
input wire [NUM_PORTS*32*1-1:0] s_out_axis_tdata,
|
||||
input wire [NUM_PORTS*1-1:0] s_out_axis_tkeep,
|
||||
input wire [NUM_PORTS-1:0] s_out_axis_tlast,
|
||||
input wire [NUM_PORTS-1:0] s_out_axis_tvalid,
|
||||
output wire [NUM_PORTS-1:0] s_out_axis_tready,
|
||||
input wire [NUM_PORTS*64-1:0] s_out_axis_ttimestamp,
|
||||
input wire [NUM_PORTS-1:0] s_out_axis_thas_time,
|
||||
input wire [NUM_PORTS*16-1:0] s_out_axis_tlength,
|
||||
input wire [NUM_PORTS-1:0] s_out_axis_teov,
|
||||
input wire [NUM_PORTS-1:0] s_out_axis_teob
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Backend Interface
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
wire data_i_flush_en;
|
||||
wire [31:0] data_i_flush_timeout;
|
||||
wire [63:0] data_i_flush_active;
|
||||
wire [63:0] data_i_flush_done;
|
||||
wire data_o_flush_en;
|
||||
wire [31:0] data_o_flush_timeout;
|
||||
wire [63:0] data_o_flush_active;
|
||||
wire [63:0] data_o_flush_done;
|
||||
|
||||
backend_iface #(
|
||||
.NOC_ID (32'h51663110),
|
||||
.NUM_DATA_I (1),
|
||||
.NUM_DATA_O (0+NUM_PORTS),
|
||||
.CTRL_FIFOSIZE ($clog2(32)),
|
||||
.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),
|
||||
.rfnoc_core_config (rfnoc_core_config),
|
||||
.rfnoc_core_status (rfnoc_core_status),
|
||||
.data_i_flush_en (data_i_flush_en),
|
||||
.data_i_flush_timeout (data_i_flush_timeout),
|
||||
.data_i_flush_active (data_i_flush_active),
|
||||
.data_i_flush_done (data_i_flush_done),
|
||||
.data_o_flush_en (data_o_flush_en),
|
||||
.data_o_flush_timeout (data_o_flush_timeout),
|
||||
.data_o_flush_active (data_o_flush_active),
|
||||
.data_o_flush_done (data_o_flush_done)
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Reset Generation
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
wire ce_rst_pulse;
|
||||
|
||||
pulse_synchronizer #(.MODE ("POSEDGE")) pulse_synchronizer_ce (
|
||||
.clk_a(rfnoc_chdr_clk), .rst_a(1'b0), .pulse_a (rfnoc_chdr_rst), .busy_a (),
|
||||
.clk_b(ce_clk), .pulse_b (ce_rst_pulse)
|
||||
);
|
||||
|
||||
pulse_stretch_min #(.LENGTH(32)) pulse_stretch_min_ce (
|
||||
.clk(ce_clk), .rst(1'b0),
|
||||
.pulse_in(ce_rst_pulse), .pulse_out(ce_rst)
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Control Path
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
assign ctrlport_clk = ce_clk;
|
||||
assign ctrlport_rst = ce_rst;
|
||||
|
||||
ctrlport_endpoint #(
|
||||
.THIS_PORTID (THIS_PORTID),
|
||||
.SYNC_CLKS (0),
|
||||
.AXIS_CTRL_MST_EN (0),
|
||||
.AXIS_CTRL_SLV_EN (1),
|
||||
.SLAVE_FIFO_SIZE ($clog2(32))
|
||||
) 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 (2'b0),
|
||||
.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 ()
|
||||
);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Data Path
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
genvar i;
|
||||
|
||||
assign axis_data_clk = ce_clk;
|
||||
assign axis_data_rst = ce_rst;
|
||||
|
||||
//---------------------
|
||||
// Input Data Paths
|
||||
//---------------------
|
||||
|
||||
// No input data paths for this block
|
||||
assign s_rfnoc_chdr_tready[0] = 1'b1;
|
||||
assign data_i_flush_done[0] = 1'b1;
|
||||
|
||||
//---------------------
|
||||
// Output Data Paths
|
||||
//---------------------
|
||||
|
||||
for (i = 0; i < NUM_PORTS; i = i + 1) begin: gen_output_out
|
||||
axis_data_to_chdr #(
|
||||
.CHDR_W (CHDR_W),
|
||||
.ITEM_W (32),
|
||||
.NIPC (1),
|
||||
.SYNC_CLKS (0),
|
||||
.INFO_FIFO_SIZE ($clog2(32)),
|
||||
.PYLD_FIFO_SIZE ($clog2(32)),
|
||||
.MTU (MTU),
|
||||
.SIDEBAND_AT_END (0)
|
||||
) axis_data_to_chdr_out_out (
|
||||
.axis_chdr_clk (rfnoc_chdr_clk),
|
||||
.axis_chdr_rst (rfnoc_chdr_rst),
|
||||
.axis_data_clk (axis_data_clk),
|
||||
.axis_data_rst (axis_data_rst),
|
||||
.m_axis_chdr_tdata (m_rfnoc_chdr_tdata[(0+i)*CHDR_W+:CHDR_W]),
|
||||
.m_axis_chdr_tlast (m_rfnoc_chdr_tlast[0+i]),
|
||||
.m_axis_chdr_tvalid (m_rfnoc_chdr_tvalid[0+i]),
|
||||
.m_axis_chdr_tready (m_rfnoc_chdr_tready[0+i]),
|
||||
.s_axis_tdata (s_out_axis_tdata[(32*1)*i+:(32*1)]),
|
||||
.s_axis_tkeep (s_out_axis_tkeep[1*i+:1]),
|
||||
.s_axis_tlast (s_out_axis_tlast[i]),
|
||||
.s_axis_tvalid (s_out_axis_tvalid[i]),
|
||||
.s_axis_tready (s_out_axis_tready[i]),
|
||||
.s_axis_ttimestamp (s_out_axis_ttimestamp[64*i+:64]),
|
||||
.s_axis_thas_time (s_out_axis_thas_time[i]),
|
||||
.s_axis_tlength (s_out_axis_tlength[16*i+:16]),
|
||||
.s_axis_teov (s_out_axis_teov[i]),
|
||||
.s_axis_teob (s_out_axis_teob[i]),
|
||||
.flush_en (data_o_flush_en),
|
||||
.flush_timeout (data_o_flush_timeout),
|
||||
.flush_active (data_o_flush_active[0+i]),
|
||||
.flush_done (data_o_flush_done[0+i])
|
||||
);
|
||||
end
|
||||
|
||||
endmodule // noc_shell_siggen
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,242 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: rfnoc_block_siggen
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Signal generator RFNoC block. This block outputs packets of one of three
|
||||
// output types based on the REG_WAVEFORM register setting. Supported modes
|
||||
// include constant, sinusoidal, and noise/random. The output is also run
|
||||
// through a gain stage that is configurable using the REG_GAIN register.
|
||||
// See the register descriptions in rfnoc_block_siggen_regs.vh for details.
|
||||
//
|
||||
// The sine output is based on the Xilinx CORDIC IP, configured for the
|
||||
// rotate function, with scaled radians as the units. See the CORDIC user
|
||||
// guide (PG105) and register descriptions for details.
|
||||
//
|
||||
// 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_PORTS : Number of siggen cores to instantiate.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module rfnoc_block_siggen #(
|
||||
parameter [9:0] THIS_PORTID = 10 'd0,
|
||||
parameter CHDR_W = 64,
|
||||
parameter [5:0] MTU = 10,
|
||||
parameter NUM_PORTS = 1
|
||||
) (
|
||||
// RFNoC Framework Clocks and Resets
|
||||
input wire rfnoc_chdr_clk,
|
||||
input wire rfnoc_ctrl_clk,
|
||||
input wire ce_clk,
|
||||
// RFNoC Backend Interface
|
||||
input wire [ 511:0] rfnoc_core_config,
|
||||
output wire [ 511:0] rfnoc_core_status,
|
||||
// AXIS-CHDR Input Ports (from framework)
|
||||
input wire [ CHDR_W-1:0] s_rfnoc_chdr_tdata,
|
||||
input wire s_rfnoc_chdr_tlast,
|
||||
input wire s_rfnoc_chdr_tvalid,
|
||||
output wire s_rfnoc_chdr_tready,
|
||||
// AXIS-CHDR Output Ports (to framework)
|
||||
output wire [NUM_PORTS*CHDR_W-1:0] m_rfnoc_chdr_tdata,
|
||||
output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tlast,
|
||||
output wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tvalid,
|
||||
input wire [ NUM_PORTS-1:0] m_rfnoc_chdr_tready,
|
||||
// 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
|
||||
);
|
||||
|
||||
`include "rfnoc_block_siggen_regs.vh"
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Signal Declarations
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// 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 [31:0] m_ctrlport_resp_data;
|
||||
// Data Stream to User Logic: out
|
||||
wire [NUM_PORTS*32*1-1:0] s_out_axis_tdata;
|
||||
wire [ NUM_PORTS-1:0] s_out_axis_tlast;
|
||||
wire [ NUM_PORTS-1:0] s_out_axis_tvalid;
|
||||
wire [ NUM_PORTS-1:0] s_out_axis_tready;
|
||||
wire [ NUM_PORTS*16-1:0] s_out_axis_tlength;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// NoC Shell
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
wire ce_rst;
|
||||
|
||||
noc_shell_siggen #(
|
||||
.CHDR_W (CHDR_W),
|
||||
.THIS_PORTID (THIS_PORTID),
|
||||
.MTU (MTU),
|
||||
.NUM_PORTS (NUM_PORTS)
|
||||
) noc_shell_siggen_i (
|
||||
//---------------------
|
||||
// Framework Interface
|
||||
//---------------------
|
||||
|
||||
// Clock Inputs
|
||||
.rfnoc_chdr_clk (rfnoc_chdr_clk),
|
||||
.rfnoc_ctrl_clk (rfnoc_ctrl_clk),
|
||||
.ce_clk (ce_clk),
|
||||
// Reset Outputs
|
||||
.rfnoc_chdr_rst (),
|
||||
.rfnoc_ctrl_rst (),
|
||||
.ce_rst (ce_rst),
|
||||
// RFNoC Backend Interface
|
||||
.rfnoc_core_config (rfnoc_core_config),
|
||||
.rfnoc_core_status (rfnoc_core_status),
|
||||
// CHDR Input Ports (from framework)
|
||||
.s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
|
||||
.s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
|
||||
.s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
|
||||
.s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
|
||||
// CHDR Output Ports (to framework)
|
||||
.m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
|
||||
.m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
|
||||
.m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
|
||||
.m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
|
||||
// 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_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_data (m_ctrlport_resp_data),
|
||||
|
||||
// AXI-Stream Clock and Reset
|
||||
.axis_data_clk (),
|
||||
.axis_data_rst (),
|
||||
// Data Stream from User Logic: out
|
||||
.s_out_axis_tdata (s_out_axis_tdata),
|
||||
.s_out_axis_tkeep ({NUM_PORTS{1'b1}}),
|
||||
.s_out_axis_tlast (s_out_axis_tlast),
|
||||
.s_out_axis_tvalid (s_out_axis_tvalid),
|
||||
.s_out_axis_tready (s_out_axis_tready),
|
||||
.s_out_axis_ttimestamp ({NUM_PORTS{64'b0}}),
|
||||
.s_out_axis_thas_time ({NUM_PORTS{1'b0}}),
|
||||
.s_out_axis_tlength (s_out_axis_tlength),
|
||||
.s_out_axis_teov ({NUM_PORTS{1'b0}}),
|
||||
.s_out_axis_teob ({NUM_PORTS{1'b0}})
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// CtrlPort Splitter
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Create a CtrlPort bus for each port instance
|
||||
|
||||
wire [ 1*NUM_PORTS-1:0] ctrlport_req_wr;
|
||||
wire [ 1*NUM_PORTS-1:0] ctrlport_req_rd;
|
||||
wire [20*NUM_PORTS-1:0] ctrlport_req_addr;
|
||||
wire [32*NUM_PORTS-1:0] ctrlport_req_data;
|
||||
wire [ 1*NUM_PORTS-1:0] ctrlport_resp_ack;
|
||||
wire [32*NUM_PORTS-1:0] ctrlport_resp_data;
|
||||
|
||||
ctrlport_decoder #(
|
||||
.NUM_SLAVES (NUM_PORTS),
|
||||
.BASE_ADDR (0),
|
||||
.SLAVE_ADDR_W (SIGGEN_ADDR_W)
|
||||
) ctrlport_decoder_i (
|
||||
.ctrlport_clk (ce_clk),
|
||||
.ctrlport_rst (ce_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'b0),
|
||||
.s_ctrlport_resp_ack (m_ctrlport_resp_ack),
|
||||
.s_ctrlport_resp_status (),
|
||||
.s_ctrlport_resp_data (m_ctrlport_resp_data),
|
||||
.m_ctrlport_req_wr (ctrlport_req_wr),
|
||||
.m_ctrlport_req_rd (ctrlport_req_rd),
|
||||
.m_ctrlport_req_addr (ctrlport_req_addr),
|
||||
.m_ctrlport_req_data (ctrlport_req_data),
|
||||
.m_ctrlport_req_byte_en (),
|
||||
.m_ctrlport_req_has_time (),
|
||||
.m_ctrlport_req_time (),
|
||||
.m_ctrlport_resp_ack (ctrlport_resp_ack),
|
||||
.m_ctrlport_resp_status ({NUM_PORTS{2'b0}}),
|
||||
.m_ctrlport_resp_data (ctrlport_resp_data)
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Port Instances
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
genvar port;
|
||||
generate
|
||||
for (port = 0; port < NUM_PORTS; port = port+1) begin : gen_ports
|
||||
|
||||
rfnoc_siggen_core rfnoc_siggen_core_i (
|
||||
.clk (ce_clk),
|
||||
.rst (ce_rst),
|
||||
.s_ctrlport_req_wr (ctrlport_req_wr [port* 1 +: 1]),
|
||||
.s_ctrlport_req_rd (ctrlport_req_rd [port* 1 +: 1]),
|
||||
.s_ctrlport_req_addr (ctrlport_req_addr [port*20 +: 20]),
|
||||
.s_ctrlport_req_data (ctrlport_req_data [port*32 +: 32]),
|
||||
.s_ctrlport_resp_ack (ctrlport_resp_ack [port* 1 +: 1]),
|
||||
.s_ctrlport_resp_data (ctrlport_resp_data [port*32 +: 32]),
|
||||
.m_tdata (s_out_axis_tdata [port*32 +: 32]),
|
||||
.m_tlast (s_out_axis_tlast [port* 1 +: 1]),
|
||||
.m_tvalid (s_out_axis_tvalid [port* 1 +: 1]),
|
||||
.m_tready (s_out_axis_tready [port* 1 +: 1]),
|
||||
.m_tlength (s_out_axis_tlength [port*16 +: 16])
|
||||
);
|
||||
|
||||
end
|
||||
endgenerate
|
||||
|
||||
endmodule // rfnoc_block_siggen
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: rfnoc_block_logpwr_all_tb
|
||||
//
|
||||
// Description: Top-level testbench for the Signal Generator RFNoC block. This
|
||||
// instantiates rfnoc_block_siggen_tb with different parameters to test
|
||||
// multiple configurations.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module rfnoc_block_siggen_all_tb;
|
||||
|
||||
// Test multiple CHDR widths
|
||||
rfnoc_block_siggen_tb #(.CHDR_W(64), .NUM_PORTS(1)) test_siggen_0();
|
||||
rfnoc_block_siggen_tb #(.CHDR_W(64), .NUM_PORTS(2)) test_siggen_1();
|
||||
rfnoc_block_siggen_tb #(.CHDR_W(64), .NUM_PORTS(3)) test_siggen_2();
|
||||
rfnoc_block_siggen_tb #(.CHDR_W(128), .NUM_PORTS(2)) test_siggen_3();
|
||||
rfnoc_block_siggen_tb #(.CHDR_W(256), .NUM_PORTS(1)) test_siggen_4();
|
||||
|
||||
endmodule : rfnoc_block_siggen_all_tb
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,134 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: rfnoc_block_siggen_regs (Header)
|
||||
//
|
||||
// Description: RFNoC Signal Generator block register descriptions
|
||||
//
|
||||
|
||||
|
||||
// Address space size, per signal generator core. That is, each signal
|
||||
// generator core's address space is separated in the CtrlPort address space
|
||||
// by 2^SIGGEN_ADDR_W bytes.
|
||||
localparam SIGGEN_ADDR_W = 5;
|
||||
|
||||
|
||||
|
||||
// REG_ENABLE (R/W)
|
||||
//
|
||||
// Starts or stops the waveform output. Write a 1 to enable waveform output, 0
|
||||
// to disable waveform output. Starting and stopping occurs on packet
|
||||
// boundaries.
|
||||
//
|
||||
// [31:1] Reserved
|
||||
// [0] Enable bit
|
||||
//
|
||||
localparam REG_ENABLE = 'h00;
|
||||
//
|
||||
localparam REG_ENABLE_LEN = 1;
|
||||
|
||||
|
||||
// REG_SPP (R/W)
|
||||
//
|
||||
// The number of samples per packet to output for the selected waveform. This
|
||||
// is read at the start of each new packet.
|
||||
//
|
||||
localparam REG_SPP = 'h04;
|
||||
//
|
||||
localparam REG_SPP_LEN = 14;
|
||||
|
||||
|
||||
// REG_WAVEFORM (R/W)
|
||||
//
|
||||
// Selects the type of waveform to output. The possible values are:
|
||||
//
|
||||
// 0 : (WAVE_CONST) Constant data
|
||||
// 1 : (WAVE_SINE) Sine wave
|
||||
// 2 : (WAVE_NOISE) Noise / random data
|
||||
//
|
||||
localparam REG_WAVEFORM = 'h08;
|
||||
//
|
||||
localparam REG_WAVEFORM_LEN = 2;
|
||||
//
|
||||
localparam WAVE_CONST = 2'h0;
|
||||
localparam WAVE_SINE = 2'h1;
|
||||
localparam WAVE_NOISE = 2'h2;
|
||||
|
||||
|
||||
// REG_GAIN (R/W)
|
||||
//
|
||||
// Sets the gain for the output. This is a 16-bit signed fixed point value
|
||||
// with 15 fractional bits. The gain is applied to both the real and imaginary
|
||||
// parts of each output sample. This gain is applied to all waveform output
|
||||
// types.
|
||||
//
|
||||
localparam REG_GAIN = 'h0C;
|
||||
//
|
||||
localparam REG_GAIN_LEN = 16;
|
||||
|
||||
|
||||
// REG_CONSTANT (R/W)
|
||||
//
|
||||
// Sets the value for the sample to output for the constant waveform. Both the
|
||||
// real and imaginary components are treated as 16-bit signed fixed point
|
||||
// values with 15 fractional bits.
|
||||
//
|
||||
// [31:16] Real/I component
|
||||
// [15: 0] Imaginary/Q component
|
||||
//
|
||||
localparam REG_CONSTANT = 'h10;
|
||||
//
|
||||
localparam REG_CONSTANT_LEN = 32;
|
||||
|
||||
|
||||
// REG_PHASE_INC (R/W)
|
||||
//
|
||||
// Sets the phase increment, in "scaled radians", for the sine waveform
|
||||
// generator. This is the amount by which REG_CARTESIAN is rotated each clock
|
||||
// cycle. In other words, it controls the rate of rotation, or the frequency,
|
||||
// of the sine wave. The range of the phase value is -1.0 to +1.0. In scaled
|
||||
// radians, the value range -1 to +1 corresponds to -Pi to Pi in radians.
|
||||
//
|
||||
// In other words, the normalized frequency (in cycles/sample) of the
|
||||
// sinusoidal output is equal to 0.5*REG_PHASE_INC.
|
||||
//
|
||||
// [31:16] : Reserved
|
||||
// [15: 0] : Signed fixed-point phase value with 3 integer bits and 13
|
||||
// fractional bits.
|
||||
//
|
||||
localparam REG_PHASE_INC = 'h14;
|
||||
//
|
||||
localparam REG_PHASE_INC_LEN = 16;
|
||||
|
||||
|
||||
// REG_CARTESIAN (R/W)
|
||||
//
|
||||
// Sets the (X,Y) Cartesian coordinate that will be rotated to generate the
|
||||
// sine output. The rate of rotation is controlled by REG_PHASE_INC. Note that
|
||||
// this input vector is also scaled by a "CORDIC scale factor" that equals
|
||||
// about 1.16444 (the product of sqrt(1 + 2^(-2i)) for i = 1 to n, where n =
|
||||
// 14, the number of fractional bits used by the CORDIC IP).
|
||||
//
|
||||
// Both the X and Y coordinates are signed fixed-point values with 15
|
||||
// fractional bits.
|
||||
//
|
||||
// For example, supposed you wanted a sinusoidal output with an amplitude of
|
||||
// about 0.9. In that case, you could set the Y coordinate to 0 and the X
|
||||
// coordinate to 0.9/1.16444 = 0.7729. In fixed-point, that's 0.7729 * 2^15 =
|
||||
// 0x62EE.
|
||||
//
|
||||
// NOTE: The Xilinx CORDIC IP describes the input and output as 16-bit signed
|
||||
// fixed point with 2 integer and 14 fractional bits, which is accurate.
|
||||
// However, since we treat the output as sc16 (15 fractional bits), we need to
|
||||
// double the value of the CARTESIAN inputs to get the output we want for sc16.
|
||||
// This is mathematically inequivalent to simply saying the CARTESIAN inputs
|
||||
// have 15 fractional bits instead of 14.
|
||||
//
|
||||
// [31:16] : Y (Imaginary) component
|
||||
// [15: 0] : X (Real) component
|
||||
//
|
||||
localparam REG_CARTESIAN = 'h18;
|
||||
//
|
||||
localparam REG_CARTESIAN_LEN = 32;
|
||||
@@ -0,0 +1,719 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: rfnoc_block_siggen_tb
|
||||
//
|
||||
// Description: Testbench for the siggen RFNoC block.
|
||||
//
|
||||
|
||||
`default_nettype none
|
||||
|
||||
|
||||
module rfnoc_block_siggen_tb #(
|
||||
parameter CHDR_W = 64,
|
||||
parameter NUM_PORTS = 1
|
||||
);
|
||||
|
||||
`include "test_exec.svh"
|
||||
|
||||
import PkgTestExec::*;
|
||||
import PkgChdrUtils::*;
|
||||
import PkgRfnocBlockCtrlBfm::*;
|
||||
import PkgRfnocItemUtils::*;
|
||||
|
||||
`include "rfnoc_block_siggen_regs.vh"
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Testbench Configuration
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
localparam [31:0] NOC_ID = 32'h51663110;
|
||||
localparam [ 9:0] THIS_PORTID = 10'h123;
|
||||
localparam int MTU = 10; // Log2 of max transmission unit in CHDR words
|
||||
localparam int NUM_PORTS_I = 1;
|
||||
localparam int NUM_PORTS_O = 0+NUM_PORTS;
|
||||
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 int STALL_PROB = 25; // Default BFM stall probability
|
||||
localparam real CHDR_CLK_PER = 5.0; // 200 MHz
|
||||
localparam real CTRL_CLK_PER = 8.0; // 125 MHz
|
||||
localparam real CE_CLK_PER = 4.0; // 250 MHz
|
||||
|
||||
localparam real PI = 2*$acos(0);
|
||||
|
||||
// Number of fractional bits used for fixed point values of the different
|
||||
// settings (derived from the DUT).
|
||||
localparam int GAIN_FRAC = 15;
|
||||
localparam int CONST_FRAC = 15;
|
||||
localparam int PHASE_FRAC = 13;
|
||||
localparam int CART_FRAC = 14;
|
||||
|
||||
// Maximum real (floating point) values allowed for the different fixed
|
||||
// point formats (for range checking). All of the fixed point values are
|
||||
// signed 16-bit.
|
||||
localparam real MAX_GAIN_R = (2.0**15-1) / (2.0**GAIN_FRAC);
|
||||
localparam real MIN_GAIN_R = -(2.0**15) / (2.0**GAIN_FRAC);
|
||||
localparam real MAX_CONST_R = (2.0**15-1) / (2.0**CONST_FRAC);
|
||||
localparam real MIN_CONST_R = -(2.0**15) / (2.0**CONST_FRAC);
|
||||
localparam real MAX_CART_R = (2.0**15-1) / (2.0**CART_FRAC);
|
||||
localparam real MIN_CART_R = -(2.0**15) / (2.0**CART_FRAC);
|
||||
// Note that the CORDIC only supports phase values from -1.0 to +1.0.
|
||||
localparam real MAX_PHASE_R = +1.0;
|
||||
localparam real MIN_PHASE_R = -1.0;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Clocks and Resets
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bit rfnoc_chdr_clk;
|
||||
bit rfnoc_ctrl_clk;
|
||||
bit ce_clk;
|
||||
|
||||
sim_clock_gen #(.PERIOD(CHDR_CLK_PER), .AUTOSTART(0))
|
||||
rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst());
|
||||
sim_clock_gen #(.PERIOD(CTRL_CLK_PER), .AUTOSTART(0))
|
||||
rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst());
|
||||
sim_clock_gen #(.PERIOD(CE_CLK_PER), .AUTOSTART(0))
|
||||
ce_clk_gen (.clk(ce_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);
|
||||
|
||||
// AXIS-CHDR Interfaces
|
||||
AxiStreamIf #(CHDR_W) m_chdr [NUM_PORTS_I] (rfnoc_chdr_clk, 1'b0);
|
||||
AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS_O] (rfnoc_chdr_clk, 1'b0);
|
||||
|
||||
// Block Controller BFM
|
||||
RfnocBlockCtrlBfm #(CHDR_W, ITEM_W) blk_ctrl = new(backend, m_ctrl, s_ctrl);
|
||||
|
||||
// CHDR word and item/sample data types
|
||||
typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t;
|
||||
typedef ChdrData #(CHDR_W, ITEM_W)::item_t item_t;
|
||||
|
||||
// Connect block controller to BFMs
|
||||
for (genvar i = 0; i < NUM_PORTS_I; i++) begin : gen_bfm_input_connections
|
||||
initial begin
|
||||
blk_ctrl.connect_master_data_port(i, m_chdr[i], PKT_SIZE_BYTES);
|
||||
blk_ctrl.set_master_stall_prob(i, STALL_PROB);
|
||||
end
|
||||
end
|
||||
for (genvar i = 0; i < NUM_PORTS_O; i++) begin : gen_bfm_output_connections
|
||||
initial begin
|
||||
blk_ctrl.connect_slave_data_port(i, s_chdr[i]);
|
||||
blk_ctrl.set_slave_stall_prob(i, STALL_PROB);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Device Under Test (DUT)
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// DUT Slave (Input) Port Signals
|
||||
logic [CHDR_W*NUM_PORTS_I-1:0] s_rfnoc_chdr_tdata;
|
||||
logic [ NUM_PORTS_I-1:0] s_rfnoc_chdr_tlast;
|
||||
logic [ NUM_PORTS_I-1:0] s_rfnoc_chdr_tvalid;
|
||||
logic [ NUM_PORTS_I-1:0] s_rfnoc_chdr_tready;
|
||||
|
||||
// DUT Master (Output) Port Signals
|
||||
logic [CHDR_W*NUM_PORTS_O-1:0] m_rfnoc_chdr_tdata;
|
||||
logic [ NUM_PORTS_O-1:0] m_rfnoc_chdr_tlast;
|
||||
logic [ NUM_PORTS_O-1:0] m_rfnoc_chdr_tvalid;
|
||||
logic [ NUM_PORTS_O-1:0] m_rfnoc_chdr_tready;
|
||||
|
||||
// Map the array of BFMs to a flat vector for the DUT connections
|
||||
for (genvar i = 0; i < NUM_PORTS_I; i++) begin : gen_dut_input_connections
|
||||
// Connect BFM master to DUT slave port
|
||||
assign s_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W] = m_chdr[i].tdata;
|
||||
assign s_rfnoc_chdr_tlast[i] = m_chdr[i].tlast;
|
||||
assign s_rfnoc_chdr_tvalid[i] = m_chdr[i].tvalid;
|
||||
assign m_chdr[i].tready = s_rfnoc_chdr_tready[i];
|
||||
end
|
||||
for (genvar i = 0; i < NUM_PORTS_O; i++) begin : gen_dut_output_connections
|
||||
// Connect BFM slave to DUT master port
|
||||
assign s_chdr[i].tdata = m_rfnoc_chdr_tdata[CHDR_W*i+:CHDR_W];
|
||||
assign s_chdr[i].tlast = m_rfnoc_chdr_tlast[i];
|
||||
assign s_chdr[i].tvalid = m_rfnoc_chdr_tvalid[i];
|
||||
assign m_rfnoc_chdr_tready[i] = s_chdr[i].tready;
|
||||
end
|
||||
|
||||
rfnoc_block_siggen #(
|
||||
.THIS_PORTID (THIS_PORTID),
|
||||
.CHDR_W (CHDR_W),
|
||||
.MTU (MTU),
|
||||
.NUM_PORTS (NUM_PORTS)
|
||||
) dut (
|
||||
.rfnoc_chdr_clk (rfnoc_chdr_clk),
|
||||
.rfnoc_ctrl_clk (rfnoc_ctrl_clk),
|
||||
.ce_clk (ce_clk),
|
||||
.rfnoc_core_config (backend.cfg),
|
||||
.rfnoc_core_status (backend.sts),
|
||||
.s_rfnoc_chdr_tdata (s_rfnoc_chdr_tdata),
|
||||
.s_rfnoc_chdr_tlast (s_rfnoc_chdr_tlast),
|
||||
.s_rfnoc_chdr_tvalid (s_rfnoc_chdr_tvalid),
|
||||
.s_rfnoc_chdr_tready (s_rfnoc_chdr_tready),
|
||||
.m_rfnoc_chdr_tdata (m_rfnoc_chdr_tdata),
|
||||
.m_rfnoc_chdr_tlast (m_rfnoc_chdr_tlast),
|
||||
.m_rfnoc_chdr_tvalid (m_rfnoc_chdr_tvalid),
|
||||
.m_rfnoc_chdr_tready (m_rfnoc_chdr_tready),
|
||||
.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)
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Helper Tasks
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Write a 32-bit register
|
||||
task automatic write_reg(int port, bit [19:0] addr, bit [31:0] value);
|
||||
blk_ctrl.reg_write(port * (2**SIGGEN_ADDR_W) + addr, value);
|
||||
endtask : write_reg
|
||||
|
||||
// Read a 32-bit register
|
||||
task automatic read_reg(int port, bit [19:0] addr, output logic [31:0] value);
|
||||
blk_ctrl.reg_read(port * (2**SIGGEN_ADDR_W) + addr, value);
|
||||
endtask : read_reg
|
||||
|
||||
|
||||
// Check if two samples are within a given distance from each other (i.e.,
|
||||
// check if the Cartesian distance is < threshold).
|
||||
function bit samples_are_close(
|
||||
logic [31:0] samp_a, samp_b,
|
||||
real threshold = 3.0
|
||||
);
|
||||
real ax, ay, bx, by;
|
||||
real distance;
|
||||
|
||||
// Treat the samples and signed 16-bit numbers (not fixed point)
|
||||
ax = signed'(samp_a[31:16]);
|
||||
ay = signed'(samp_a[15: 0]);
|
||||
bx = signed'(samp_b[31:16]);
|
||||
by = signed'(samp_b[15: 0]);
|
||||
|
||||
distance = $sqrt( (ax-bx)*(ax-bx) + (ay-by)*(ay-by) );
|
||||
|
||||
return distance <= threshold;
|
||||
endfunction : samples_are_close
|
||||
|
||||
|
||||
// Convert real to signed 16-bit fixed point with "frac" fractional bits
|
||||
function automatic logic [15:0] real_to_fixed(real value, int frac = 15);
|
||||
// Convert to fixed point value
|
||||
value = value * 2.0**frac;
|
||||
|
||||
// Round
|
||||
value = $floor(value + 0.5);
|
||||
|
||||
// Saturate
|
||||
if (value > 16'sh7FFF) value = 16'sh7FFF;
|
||||
if (value < 16'sh8000) value = 16'sh8000;
|
||||
return int'(value);
|
||||
endfunction : real_to_fixed
|
||||
|
||||
|
||||
// Convert signed 16-bit fixed point to real, where the fixed point has
|
||||
// "frac" fractional bits.
|
||||
function automatic real fixed_to_real(
|
||||
logic signed [15:0] value,
|
||||
int frac = 15
|
||||
);
|
||||
return real'(value) / (2.0 ** frac);
|
||||
endfunction : fixed_to_real
|
||||
|
||||
|
||||
// Compute the next sine value we expect based on the previous
|
||||
function automatic logic [31:0] next_sine_value(
|
||||
logic [31:0] sample,
|
||||
logic [15:0] phase_inc
|
||||
);
|
||||
real x, y, phase, new_x, new_y;
|
||||
x = fixed_to_real(sample[31:16], CART_FRAC);
|
||||
y = fixed_to_real(sample[15: 0], CART_FRAC);
|
||||
phase = fixed_to_real(phase_inc, PHASE_FRAC) * PI;
|
||||
|
||||
// Compute the rotated coordinates
|
||||
new_x = x*$cos(phase) + y*$sin(phase);
|
||||
new_y = -x*$sin(phase) + y*$cos(phase);
|
||||
|
||||
return { real_to_fixed(new_x, CART_FRAC), real_to_fixed(new_y, CART_FRAC) };
|
||||
endfunction : next_sine_value
|
||||
|
||||
|
||||
// Apply a gain to an input value, then round and clip the same way the DUT
|
||||
// does.
|
||||
function automatic logic [15:0] apply_gain(
|
||||
logic signed [15:0] gain,
|
||||
logic signed [15:0] value
|
||||
);
|
||||
logic signed [31:0] result;
|
||||
bit round;
|
||||
|
||||
// Apply gain
|
||||
result = gain * value;
|
||||
|
||||
// Now we "round and clip". The round and clip block is configured with
|
||||
// 32-bit input, 16-bit output, and one "clip_bit". This means it takes
|
||||
// the upper 17-bits of the result, rounded, then converts that to a
|
||||
// 16-bit result, saturated.
|
||||
|
||||
// Round the value in the upper 17 bits to nearest (biased towards +inf,
|
||||
// but don't allow overflow).
|
||||
if (result[31:15] != 17'h0FFFF) begin
|
||||
round = result[14];
|
||||
end else begin
|
||||
round = 0;
|
||||
end
|
||||
result = result >>> 15; // Arithmetic right shift
|
||||
result = result + round; // Round the result
|
||||
|
||||
// Saturate to 16-bit number
|
||||
if (result < 16'sh8000) begin
|
||||
result = 16'sh8000;
|
||||
end else if (result > 16'sh7FFF) begin
|
||||
result = 16'sh7FFF;
|
||||
end
|
||||
|
||||
return result[15:0];
|
||||
endfunction : apply_gain
|
||||
|
||||
|
||||
// Flush (drop) any queued up packets on the output
|
||||
task automatic flush_output(int port, timeout = 100);
|
||||
item_t items[$];
|
||||
|
||||
forever begin
|
||||
fork
|
||||
begin : wait_for_data_fork
|
||||
// Wait for tvalid to rise for up to "timeout" clock cycles
|
||||
if (m_rfnoc_chdr_tvalid[port])
|
||||
wait(!m_rfnoc_chdr_tvalid[port]);
|
||||
wait(m_rfnoc_chdr_tvalid[port]);
|
||||
end
|
||||
begin : wait_for_timeout_fork
|
||||
#(CHDR_CLK_PER*timeout);
|
||||
end
|
||||
join_any
|
||||
|
||||
// Check if we timed out or if new data arrived
|
||||
if (!m_rfnoc_chdr_tvalid[port]) break;
|
||||
end
|
||||
|
||||
// Dump all the packets that were received
|
||||
while (blk_ctrl.num_received(port)) begin
|
||||
blk_ctrl.recv_items(port, items);
|
||||
end
|
||||
endtask : flush_output
|
||||
|
||||
|
||||
// Test a read/write register for correct functionality
|
||||
//
|
||||
// port : Replay block port to use
|
||||
// addr : Register byte address
|
||||
// mask : Mask of the bits we expect to be writable
|
||||
// initial_value : Value we expect to read initially
|
||||
//
|
||||
task automatic test_read_write_reg(
|
||||
int port,
|
||||
bit [19:0] addr,
|
||||
bit [31:0] mask = 32'hFFFFFFFF,
|
||||
logic [31:0] initial_value = '0
|
||||
);
|
||||
string err_msg;
|
||||
logic [31:0] value;
|
||||
logic [31:0] expected;
|
||||
|
||||
err_msg = $sformatf("Register 0x%X failed read/write test: ", addr);
|
||||
|
||||
// Check initial value
|
||||
expected = initial_value;
|
||||
read_reg(port, addr, value);
|
||||
`ASSERT_ERROR(value === expected, {err_msg, "initial value"});
|
||||
|
||||
// Write maximum value
|
||||
expected = (initial_value & ~mask) | mask;
|
||||
write_reg(port, addr, '1);
|
||||
read_reg(port, addr, value);
|
||||
`ASSERT_ERROR(value === expected, {err_msg, "write max value"});
|
||||
|
||||
// Test writing 0
|
||||
expected = (initial_value & ~mask);
|
||||
write_reg(port, addr, '0);
|
||||
read_reg(port, addr, value);
|
||||
`ASSERT_ERROR(value === expected, {err_msg, "write zero"});
|
||||
|
||||
// Restore original value
|
||||
write_reg(port, addr, initial_value);
|
||||
endtask : test_read_write_reg
|
||||
|
||||
|
||||
// Run the block using the indicated settings and verify the output.
|
||||
task automatic run_waveform(
|
||||
int port,
|
||||
logic signed [15:0] gain = 16'h7FFF, // 0.99997
|
||||
logic [2:0] mode = WAVE_CONST,
|
||||
int num_packets = 1,
|
||||
int spp = SPP,
|
||||
logic signed [15:0] const_re = 16'h7FFF, // 0.99997
|
||||
logic signed [15:0] const_im = 16'h7FFF, // 0.99997
|
||||
logic signed [15:0] phase_inc = real_to_fixed(0.5, 13), //real_to_fixed(2.0/16, 13), // 2*pi/16 radians
|
||||
logic signed [15:0] cart_x = real_to_fixed(1.0, 14),
|
||||
logic signed [15:0] cart_y = real_to_fixed(0.0, 14)
|
||||
);
|
||||
write_reg(port, REG_SPP, spp);
|
||||
write_reg(port, REG_WAVEFORM, mode);
|
||||
write_reg(port, REG_GAIN, gain);
|
||||
if (mode == WAVE_CONST) begin
|
||||
write_reg(port, REG_CONSTANT, {const_re, const_im});
|
||||
end else if (mode == WAVE_SINE) begin
|
||||
write_reg(port, REG_PHASE_INC, phase_inc);
|
||||
write_reg(port, REG_CARTESIAN, {cart_y, cart_x});
|
||||
end
|
||||
write_reg(port, REG_ENABLE, 1);
|
||||
|
||||
for (int packet_count = 0; packet_count < num_packets; packet_count++) begin
|
||||
item_t items[$];
|
||||
item_t expected_const, expected_sine, actual;
|
||||
|
||||
// Receive the next packet
|
||||
blk_ctrl.recv_items(port, items);
|
||||
|
||||
// Verify the length
|
||||
`ASSERT_ERROR(
|
||||
items.size() == spp,
|
||||
"Packet length didn't match configured SPP"
|
||||
);
|
||||
|
||||
// Verify the payload
|
||||
foreach (items[i]) begin
|
||||
actual = items[i];
|
||||
|
||||
// Determine the expected constant output
|
||||
expected_const[31:16] = apply_gain(gain, const_re);
|
||||
expected_const[15: 0] = apply_gain(gain, const_im);
|
||||
|
||||
// Determine the expected sine output
|
||||
if (i == 0) begin
|
||||
// We have no basis for comparison on the first sample, so don't
|
||||
// check it. It will be used to compute the next output.
|
||||
expected_sine = actual;
|
||||
end else begin
|
||||
expected_sine = next_sine_value(items[i-1], phase_inc);
|
||||
end
|
||||
|
||||
// Check the output
|
||||
if (mode == WAVE_CONST) begin
|
||||
// For the constant, we expect the output to match exactly
|
||||
`ASSERT_ERROR(
|
||||
actual == expected_const,
|
||||
$sformatf("Incorrect constant sample on packet %0d. Expected 0x%X, received 0x%X.",
|
||||
packet_count, expected_const, actual)
|
||||
);
|
||||
end else if (mode == WAVE_SINE) begin
|
||||
// For sine, it's hard to reproduce the rounding behavior of the IP
|
||||
// exactly, so we just check if we're close to the expected answer.
|
||||
`ASSERT_ERROR(
|
||||
samples_are_close(actual, expected_sine),
|
||||
$sformatf("Incorrect sine sample on packet %0d. Expected 0x%X, received 0x%X.",
|
||||
packet_count, expected_sine, actual)
|
||||
);
|
||||
end else if (mode == WAVE_NOISE) begin
|
||||
if (i != 0) begin
|
||||
// For noise, it's hard to even estimate the output, so make sure
|
||||
// it's changing.
|
||||
`ASSERT_ERROR(items[i] !== items[i-1],
|
||||
$sformatf("Noise output didn't update on packet %0d.Received 0x%X.",
|
||||
packet_count, actual)
|
||||
);
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
// Disable the output and flush any output
|
||||
write_reg(port, REG_ENABLE, 0);
|
||||
flush_output(port);
|
||||
endtask : run_waveform
|
||||
|
||||
|
||||
// Run the block using the "constant" waveform mode using the indicated
|
||||
// settings and verify the output.
|
||||
task automatic run_const(
|
||||
int port,
|
||||
int num_packets = 50,
|
||||
int spp = SPP,
|
||||
real gain,
|
||||
real re,
|
||||
real im
|
||||
);
|
||||
logic signed [15:0] fgain, fre, fim; // Fixed-point versions
|
||||
|
||||
// Check the ranges
|
||||
`ASSERT_FATAL(gain <= MAX_GAIN_R || gain >= MIN_GAIN_R, "Gain out of range");
|
||||
`ASSERT_FATAL(re <= MAX_CONST_R || re >= MIN_CONST_R, "Real out of range");
|
||||
`ASSERT_FATAL(im <= MAX_CONST_R || im >= MIN_CONST_R, "Imag out of range");
|
||||
|
||||
// Convert arguments to fixed point
|
||||
fgain = real_to_fixed(gain, GAIN_FRAC);
|
||||
fre = real_to_fixed(re, CONST_FRAC);
|
||||
fim = real_to_fixed(im, CONST_FRAC);
|
||||
|
||||
// Test the waveform
|
||||
run_waveform(
|
||||
.port(port),
|
||||
.gain(fgain),
|
||||
.mode(WAVE_CONST),
|
||||
.num_packets(num_packets),
|
||||
.spp(spp),
|
||||
.const_re(fre),
|
||||
.const_im(fim)
|
||||
);
|
||||
endtask : run_const
|
||||
|
||||
|
||||
// Run the block using the "sine" waveform mode using the indicated settings
|
||||
// and verify the output.
|
||||
task automatic run_sine(
|
||||
int port,
|
||||
int num_packets = 50,
|
||||
int spp = SPP,
|
||||
real gain,
|
||||
real x,
|
||||
real y,
|
||||
real phase
|
||||
);
|
||||
logic signed [15:0] fgain, fx, fy, fphase; // Fixed-point versions
|
||||
|
||||
// Check the ranges
|
||||
`ASSERT_FATAL(gain <= MAX_GAIN_R || gain >= MIN_GAIN_R, "Gain out of range");
|
||||
`ASSERT_FATAL(x <= MAX_CART_R || x >= MIN_CART_R, "X out of range");
|
||||
`ASSERT_FATAL(y <= MAX_CART_R || y >= MIN_CART_R, "Y out of range");
|
||||
`ASSERT_FATAL(phase <= MAX_PHASE_R || phase >= MIN_PHASE_R, "Phase out of range");
|
||||
|
||||
// Convert arguments to fixed point.
|
||||
fgain = real_to_fixed(gain, GAIN_FRAC);
|
||||
fx = real_to_fixed(x, CART_FRAC);
|
||||
fy = real_to_fixed(y, CART_FRAC);
|
||||
fphase = real_to_fixed(phase, PHASE_FRAC);
|
||||
|
||||
// Test the waveform
|
||||
run_waveform(
|
||||
.port(port),
|
||||
.gain(fgain),
|
||||
.mode(WAVE_SINE),
|
||||
.num_packets(num_packets),
|
||||
.spp(spp),
|
||||
.cart_x(fx),
|
||||
.cart_y(fy),
|
||||
.phase_inc(fphase)
|
||||
);
|
||||
endtask : run_sine
|
||||
|
||||
|
||||
// Run the block using the "noise" waveform mode using the indicated
|
||||
// settings and verify the output.
|
||||
task automatic run_noise(
|
||||
int port,
|
||||
int num_packets = 50,
|
||||
int spp = SPP,
|
||||
real gain
|
||||
);
|
||||
logic signed [15:0] fgain; // Fixed-point versions
|
||||
|
||||
// Check the ranges
|
||||
`ASSERT_FATAL(gain <= MAX_GAIN_R || gain >= MIN_GAIN_R, "Gain out of range");
|
||||
|
||||
// Convert arguments to fixed point
|
||||
fgain = real_to_fixed(gain, GAIN_FRAC);
|
||||
|
||||
// Test the waveform
|
||||
run_waveform(
|
||||
.port(port),
|
||||
.gain(fgain),
|
||||
.mode(WAVE_NOISE),
|
||||
.num_packets(num_packets),
|
||||
.spp(spp)
|
||||
);
|
||||
endtask : run_noise
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Test Procedures
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Test the min and max allowed values on all registers
|
||||
task automatic test_registers(int port);
|
||||
test.start_test($sformatf("Test registers (port %0d)", port), 1ms);
|
||||
// REG_ENABLE and REG_WAVEFORM will be tested during the other tests
|
||||
test_read_write_reg(port, REG_SPP, {REG_SPP_LEN{1'b1}}, 32'd16);
|
||||
test_read_write_reg(port, REG_GAIN, {REG_GAIN_LEN{1'b1}}, 32'h7FFF);
|
||||
test_read_write_reg(port, REG_CONSTANT, {REG_CONSTANT_LEN{1'b1}}, 32'h0);
|
||||
test_read_write_reg(port, REG_PHASE_INC, {REG_PHASE_INC_LEN{1'b1}}, {REG_PHASE_INC_LEN{1'bX}});
|
||||
test_read_write_reg(port, REG_CARTESIAN, {REG_CARTESIAN_LEN{1'b1}}, {REG_CARTESIAN_LEN{1'bX}});
|
||||
test.end_test();
|
||||
endtask : test_registers
|
||||
|
||||
|
||||
// Run through all the waveform modes to make sure they work as expected
|
||||
task automatic test_waveforms(int port);
|
||||
test.start_test($sformatf("Test waveforms (port %0d)", port), 1ms);
|
||||
run_const(.port(port), .gain(0.5), .re(0.25), .im(0.5));
|
||||
run_sine(.port(port), .gain(0.75), .x(0.25), .y(0.5), .phase(2.0/64));
|
||||
run_noise(.port(port), .gain(0.999));
|
||||
test.end_test();
|
||||
endtask : test_waveforms
|
||||
|
||||
|
||||
// Use the constant waveform to test the gain. The gain logic is shared by
|
||||
// all modes, but using "const" waveform makes it easy to control the values
|
||||
// we're testing.
|
||||
task automatic test_gain(int port);
|
||||
logic signed [15:0] min_val;
|
||||
logic signed [15:0] max_val;
|
||||
|
||||
test.start_test($sformatf("Test gain (port %0d)", port), 1ms);
|
||||
|
||||
max_val = 16'sh7FFF;
|
||||
min_val = 16'sh8000;
|
||||
|
||||
// Test max gain with min and max sample values
|
||||
run_waveform(.port(port), .mode(WAVE_CONST), .gain(max_val),
|
||||
.const_re(max_val), .const_im(min_val));
|
||||
// Test min gain with max and min sample values
|
||||
run_waveform(.port(port), .mode(WAVE_CONST), .gain(min_val),
|
||||
.const_re(min_val), .const_im(max_val));
|
||||
// Test zero
|
||||
run_waveform(.port(port), .mode(WAVE_CONST), .gain(0),
|
||||
.const_re(max_val), .const_im(min_val));
|
||||
// Test 0.5 * 0.5 = 0.25 and 0.25 * 0.5 = 0.125
|
||||
run_waveform(
|
||||
.port(port),
|
||||
.mode(WAVE_CONST),
|
||||
.const_re(real_to_fixed(0.5, CONST_FRAC)),
|
||||
.const_im(real_to_fixed(0.25, CONST_FRAC)),
|
||||
.gain(real_to_fixed(0.5, GAIN_FRAC))
|
||||
);
|
||||
test.end_test();
|
||||
endtask : test_gain
|
||||
|
||||
|
||||
// Test the phase setting for the sine waveform
|
||||
task automatic test_phase(int port);
|
||||
test.start_test($sformatf("Test phase (port %0d)", port), 1ms);
|
||||
// Test typical phase
|
||||
run_sine(.port(port), .gain(0.5), .x(1.0), .y(0.0), .phase(2.0/16), .num_packets(2));
|
||||
// Test max phase
|
||||
run_sine(.port(port), .gain(0.5), .x(1.0), .y(0.0), .phase(MAX_PHASE_R), .num_packets(2));
|
||||
// Test min phase
|
||||
run_sine(.port(port), .gain(0.5), .x(1.0), .y(0.0), .phase(MIN_PHASE_R), .num_packets(2));
|
||||
test.end_test();
|
||||
endtask : test_phase
|
||||
|
||||
|
||||
// Use constant waveform to test min and max packet lengths
|
||||
task automatic test_packet_length(int port);
|
||||
test.start_test($sformatf("Test packet length (port %0d)", port), 1ms);
|
||||
run_waveform(.port(port), .spp(2));
|
||||
run_waveform(.port(port), .spp(SPP));
|
||||
run_waveform(.port(port), .spp((2**MTU-1) * (CHDR_W / ITEM_W))); // Test MTU size
|
||||
test.end_test();
|
||||
endtask : test_packet_length
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Main Test Process
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
initial begin : tb_main
|
||||
int port;
|
||||
|
||||
// Initialize the test exec object for this testbench
|
||||
test.start_tb(
|
||||
$sformatf("rfnoc_block_siggen_tb (CHDR_W = %0d, NUM_PORTS = %0d)",
|
||||
CHDR_W, NUM_PORTS));
|
||||
|
||||
// Don't start the clocks until after start_tb() returns. This ensures that
|
||||
// the clocks aren't toggling while other instances of this testbench are
|
||||
// running, which speeds up simulation time.
|
||||
rfnoc_chdr_clk_gen.start();
|
||||
rfnoc_ctrl_clk_gen.start();
|
||||
ce_clk_gen.start();
|
||||
|
||||
// Start the BFMs running
|
||||
blk_ctrl.run();
|
||||
|
||||
//--------------------------------
|
||||
// Reset
|
||||
//--------------------------------
|
||||
|
||||
test.start_test("Flush block then reset it", 10us);
|
||||
blk_ctrl.flush_and_reset();
|
||||
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");
|
||||
test.end_test();
|
||||
|
||||
//--------------------------------
|
||||
// Test Sequences
|
||||
//--------------------------------
|
||||
|
||||
// Run basic test all ports
|
||||
for(port = 0; port < NUM_PORTS; port++) begin
|
||||
test_registers(port);
|
||||
test_waveforms(port);
|
||||
end
|
||||
|
||||
// Run remaining tests on single port
|
||||
port = 0;
|
||||
test_gain(port);
|
||||
test_packet_length(port);
|
||||
test_phase(port);
|
||||
|
||||
//--------------------------------
|
||||
// Finish Up
|
||||
//--------------------------------
|
||||
|
||||
// Display final statistics and results, but don't call $finish, since we
|
||||
// don't want to kill other instances of this testbench that may be
|
||||
// running.
|
||||
test.end_tb(0);
|
||||
|
||||
// Kill the clocks to end this instance of the testbench
|
||||
rfnoc_chdr_clk_gen.kill();
|
||||
rfnoc_ctrl_clk_gen.kill();
|
||||
ce_clk_gen.kill();
|
||||
end : tb_main
|
||||
|
||||
endmodule : rfnoc_block_siggen_tb
|
||||
|
||||
|
||||
`default_nettype wire
|
||||
@@ -0,0 +1,284 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: rfnoc_siggen_core
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// This module contains the registers and core logic for a single RFNoC
|
||||
// Signal Generator module instance.
|
||||
//
|
||||
|
||||
|
||||
module rfnoc_siggen_core (
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
// CtrlPort Slave
|
||||
input wire s_ctrlport_req_wr,
|
||||
input wire s_ctrlport_req_rd,
|
||||
input wire [19:0] s_ctrlport_req_addr,
|
||||
input wire [31:0] s_ctrlport_req_data,
|
||||
output reg s_ctrlport_resp_ack,
|
||||
output reg [31:0] s_ctrlport_resp_data,
|
||||
|
||||
// Output data stream
|
||||
output wire [31:0] m_tdata,
|
||||
output wire m_tlast,
|
||||
output wire m_tvalid,
|
||||
input wire m_tready,
|
||||
output wire [15:0] m_tlength
|
||||
);
|
||||
|
||||
`include "rfnoc_block_siggen_regs.vh"
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Registers
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Define maximum fixed point value for the gain, equal to about 0.9999
|
||||
localparam MAX_GAIN = {REG_GAIN_LEN-1{1'b1}};
|
||||
|
||||
reg [ REG_ENABLE_LEN-1:0] reg_enable = 0;
|
||||
reg [ REG_SPP_LEN-1:0] reg_spp = 16;
|
||||
reg [ REG_WAVEFORM_LEN-1:0] reg_waveform = WAVE_CONST;
|
||||
reg [ REG_GAIN_LEN-1:0] reg_gain = MAX_GAIN;
|
||||
reg [ REG_CONSTANT_LEN-1:0] reg_constant = 0;
|
||||
reg [REG_PHASE_INC_LEN-1:0] reg_phase_inc;
|
||||
reg [REG_CARTESIAN_LEN-1:0] reg_cartesian;
|
||||
|
||||
reg reg_phase_inc_stb;
|
||||
reg reg_cartesian_stb;
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
reg_enable <= 0;
|
||||
reg_spp <= 16;
|
||||
reg_waveform <= WAVE_CONST;
|
||||
reg_gain <= MAX_GAIN;
|
||||
reg_constant <= 0;
|
||||
reg_phase_inc <= 'bX;
|
||||
reg_cartesian <= 'bX;
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
s_ctrlport_resp_data <= 'bX;
|
||||
reg_phase_inc_stb <= 1'b0;
|
||||
reg_cartesian_stb <= 1'b0;
|
||||
end else begin
|
||||
|
||||
// Default assignments
|
||||
s_ctrlport_resp_ack <= 1'b0;
|
||||
s_ctrlport_resp_data <= 0;
|
||||
reg_phase_inc_stb <= 1'b0;
|
||||
reg_cartesian_stb <= 1'b0;
|
||||
|
||||
// Handle register writes
|
||||
if (s_ctrlport_req_wr) begin
|
||||
s_ctrlport_resp_ack <= 1;
|
||||
case (s_ctrlport_req_addr)
|
||||
REG_ENABLE : reg_enable <= s_ctrlport_req_data[REG_ENABLE_LEN-1:0];
|
||||
REG_SPP : reg_spp <= s_ctrlport_req_data[REG_SPP_LEN-1:0];
|
||||
REG_WAVEFORM : reg_waveform <= s_ctrlport_req_data[REG_WAVEFORM_LEN-1:0];
|
||||
REG_GAIN : reg_gain <= s_ctrlport_req_data[REG_GAIN_LEN-1:0];
|
||||
REG_CONSTANT : reg_constant <= s_ctrlport_req_data[REG_CONSTANT_LEN-1:0];
|
||||
REG_PHASE_INC : begin
|
||||
reg_phase_inc <= s_ctrlport_req_data[REG_PHASE_INC_LEN-1:0];
|
||||
reg_phase_inc_stb <= 1'b1;
|
||||
end
|
||||
REG_CARTESIAN : begin
|
||||
reg_cartesian <= s_ctrlport_req_data[REG_CARTESIAN_LEN-1:0];
|
||||
reg_cartesian_stb <= 1'b1;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
// Handle register reads
|
||||
if (s_ctrlport_req_rd) begin
|
||||
s_ctrlport_resp_ack <= 1;
|
||||
case (s_ctrlport_req_addr)
|
||||
REG_ENABLE : s_ctrlport_resp_data[REG_ENABLE_LEN-1:0] <= reg_enable;
|
||||
REG_SPP : s_ctrlport_resp_data[REG_SPP_LEN-1:0] <= reg_spp;
|
||||
REG_WAVEFORM : s_ctrlport_resp_data[REG_WAVEFORM_LEN-1:0] <= reg_waveform;
|
||||
REG_GAIN : s_ctrlport_resp_data[REG_GAIN_LEN-1:0] <= reg_gain;
|
||||
REG_CONSTANT : s_ctrlport_resp_data[REG_CONSTANT_LEN-1:0] <= reg_constant;
|
||||
REG_PHASE_INC : s_ctrlport_resp_data[REG_PHASE_INC_LEN-1:0] <= reg_phase_inc;
|
||||
REG_CARTESIAN : s_ctrlport_resp_data[REG_CARTESIAN_LEN-1:0] <= reg_cartesian;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Waveform Generation
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
wire [31:0] axis_sine_tdata;
|
||||
wire axis_sine_tvalid;
|
||||
wire axis_sine_tready;
|
||||
wire [31:0] axis_const_tdata;
|
||||
wire axis_const_tvalid;
|
||||
wire axis_const_tready;
|
||||
wire [31:0] axis_noise_tdata;
|
||||
wire axis_noise_tvalid;
|
||||
wire axis_noise_tready;
|
||||
|
||||
//------------------------------------
|
||||
// Sine waveform generation
|
||||
//------------------------------------
|
||||
|
||||
// Convert the registers writes to settings bus transactions. Only one
|
||||
// register strobe will assert at a time.
|
||||
wire sine_set_stb = reg_cartesian_stb | reg_phase_inc_stb;
|
||||
wire [31:0] sine_set_data = reg_cartesian_stb ? reg_cartesian : reg_phase_inc;
|
||||
wire [ 7:0] sine_set_addr = reg_cartesian_stb;
|
||||
|
||||
sine_tone #(
|
||||
.WIDTH (32),
|
||||
.SR_PHASE_INC_ADDR (0),
|
||||
.SR_CARTESIAN_ADDR (1)
|
||||
) sine_tone_i (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (1'b0),
|
||||
.enable (1'b1),
|
||||
.set_stb (sine_set_stb),
|
||||
.set_data (sine_set_data),
|
||||
.set_addr (sine_set_addr),
|
||||
.o_tdata (axis_sine_tdata),
|
||||
.o_tlast (),
|
||||
.o_tvalid (axis_sine_tvalid),
|
||||
.o_tready (axis_sine_tready)
|
||||
);
|
||||
|
||||
//------------------------------------
|
||||
// Constant waveform generation
|
||||
//------------------------------------
|
||||
|
||||
assign axis_const_tdata = reg_constant;
|
||||
assign axis_const_tvalid = 1'b1;
|
||||
|
||||
//------------------------------------
|
||||
// Noise waveform generation
|
||||
//------------------------------------
|
||||
|
||||
assign axis_noise_tvalid = 1'b1;
|
||||
|
||||
// Random number generator
|
||||
rng rng_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.out (axis_noise_tdata)
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Waveform Selection
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
wire [31:0] axis_mux_tdata;
|
||||
wire axis_mux_tvalid;
|
||||
wire axis_mux_tready;
|
||||
|
||||
axi_mux_select #(
|
||||
.WIDTH (32),
|
||||
.SIZE (3),
|
||||
.SWITCH_ON_LAST (0)
|
||||
) axi_mux_select_i (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.clear (1'b0),
|
||||
.select (reg_waveform),
|
||||
.i_tdata ({axis_noise_tdata, axis_sine_tdata, axis_const_tdata}),
|
||||
.i_tlast ({3'd0}), // Length controlled by SPP register
|
||||
.i_tvalid ({axis_noise_tvalid, axis_sine_tvalid, axis_const_tvalid}),
|
||||
.i_tready ({axis_noise_tready, axis_sine_tready, axis_const_tready}),
|
||||
.o_tdata (axis_mux_tdata),
|
||||
.o_tlast (),
|
||||
.o_tvalid (axis_mux_tvalid),
|
||||
.o_tready (axis_mux_tready)
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Gain
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
wire [63:0] axis_gain_tdata;
|
||||
wire axis_gain_tvalid;
|
||||
wire axis_gain_tready;
|
||||
wire [31:0] axis_round_tdata;
|
||||
wire axis_round_tvalid;
|
||||
wire axis_round_tready;
|
||||
|
||||
mult_rc #(
|
||||
.WIDTH_REAL (16),
|
||||
.WIDTH_CPLX (16),
|
||||
.WIDTH_P (32),
|
||||
.DROP_TOP_P (5),
|
||||
.LATENCY (4)
|
||||
) mult_rc_i (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.real_tdata (reg_gain),
|
||||
.real_tlast (1'b0),
|
||||
.real_tvalid (1'b1),
|
||||
.real_tready (),
|
||||
.cplx_tdata (axis_mux_tdata),
|
||||
.cplx_tlast (1'b0),
|
||||
.cplx_tvalid (axis_mux_tvalid),
|
||||
.cplx_tready (axis_mux_tready),
|
||||
.p_tdata (axis_gain_tdata),
|
||||
.p_tlast (),
|
||||
.p_tvalid (axis_gain_tvalid),
|
||||
.p_tready (axis_gain_tready)
|
||||
);
|
||||
|
||||
axi_round_and_clip_complex #(
|
||||
.WIDTH_IN (32),
|
||||
.WIDTH_OUT (16),
|
||||
.CLIP_BITS (1)
|
||||
) axi_round_and_clip_complex_i (
|
||||
.clk (clk),
|
||||
.reset (rst),
|
||||
.i_tdata (axis_gain_tdata),
|
||||
.i_tlast (1'b0),
|
||||
.i_tvalid (axis_gain_tvalid),
|
||||
.i_tready (axis_gain_tready),
|
||||
.o_tdata (axis_round_tdata),
|
||||
.o_tlast (),
|
||||
.o_tvalid (axis_round_tvalid),
|
||||
.o_tready (axis_round_tready)
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Packet Length Control
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
wire [REG_SPP_LEN-1:0] m_tlength_samples;
|
||||
|
||||
assign m_tlength = { m_tlength_samples, 2'b0 }; // 4 bytes per sample
|
||||
|
||||
axis_packetize #(
|
||||
.DATA_W (32),
|
||||
.SIZE_W (REG_SPP_LEN),
|
||||
.FLUSH (1)
|
||||
) axis_packetize_i (
|
||||
.clk (clk),
|
||||
.rst (rst),
|
||||
.gate (~reg_enable),
|
||||
.size (reg_spp),
|
||||
.i_tdata (axis_round_tdata),
|
||||
.i_tvalid (axis_round_tvalid),
|
||||
.i_tready (axis_round_tready),
|
||||
.o_tdata (m_tdata),
|
||||
.o_tlast (m_tlast),
|
||||
.o_tvalid (m_tvalid),
|
||||
.o_tready (m_tready),
|
||||
.o_tuser (m_tlength_samples)
|
||||
);
|
||||
|
||||
endmodule
|
||||
@@ -0,0 +1,159 @@
|
||||
//
|
||||
// Copyright 2020 Ettus Research, a National Instruments Brand
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
//
|
||||
// Module: sine_tone
|
||||
//
|
||||
// Description:
|
||||
//
|
||||
// Sine tone generator. This block uses the Xilinx CORDIC IP configured to
|
||||
// perform the rotate function in units of scaled radians. See the CORDIC IP
|
||||
// Product Guide (PG105) for details.
|
||||
//
|
||||
// The SR_PHASE_INC register controls the phase increment, in scaled
|
||||
// radians, for the sine waveform generator. It is a 16-bit signed
|
||||
// fixed-point phase value with 3 integer bits and 13 fractional bits. This
|
||||
// is the amount by which REG_CARTESIAN is rotated each clock cycle. In
|
||||
// other words, it controls the rate of rotation, or the frequency, of the
|
||||
// sine wave. In scaled radians, the phase value range -1 to +1 corresponds
|
||||
// to -Pi to Pi in radians.
|
||||
//
|
||||
// The SR_CARTESIAN register sets the sets the (X,Y) Cartesian coordinate
|
||||
// that will be rotated to generate the sine output. Both X and Y are 16-bit
|
||||
// signed fixed-point values with 2 integer bits and 14 fractional bits. Y
|
||||
// is in the upper 16-bits and X is in the lower 16-bits.
|
||||
//
|
||||
// In addition to rotation, the SR_CARTESIAN input vector is also scaled by
|
||||
// a "CORDIC scale factor" that equals about 1.1644 (that is, the product of
|
||||
// sqrt(1 + 2^(-2i)) for i = 1 to n, where n = 14, the number of fractional
|
||||
// bits).
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// SR_PHASE_INC_ADDR : The address to use for SR_PHASE_INC.
|
||||
// SR_CARTESIAN_ADDR : The address to use for SR_CARTESIAN.
|
||||
//
|
||||
|
||||
|
||||
module sine_tone #(
|
||||
parameter WIDTH = 32,
|
||||
parameter SR_PHASE_INC_ADDR = 129,
|
||||
parameter SR_CARTESIAN_ADDR = 130
|
||||
) (
|
||||
input clk,
|
||||
input reset,
|
||||
input clear,
|
||||
input enable,
|
||||
|
||||
// Settings bus
|
||||
input set_stb,
|
||||
input [WIDTH-1:0] set_data,
|
||||
input [ 7:0] set_addr,
|
||||
|
||||
// Output sinusoid
|
||||
output [WIDTH-1:0] o_tdata,
|
||||
output o_tlast,
|
||||
output o_tvalid,
|
||||
input o_tready
|
||||
);
|
||||
|
||||
wire [15:0] phase_in_tdata;
|
||||
wire phase_in_tlast;
|
||||
wire phase_in_tvalid;
|
||||
wire phase_in_tready;
|
||||
|
||||
wire [15:0] phase_out_tdata;
|
||||
wire phase_out_tlast;
|
||||
wire phase_out_tvalid;
|
||||
wire phase_out_tready;
|
||||
|
||||
wire [WIDTH-1:0] cartesian_tdata;
|
||||
wire cartesian_tlast;
|
||||
wire cartesian_tvalid;
|
||||
wire cartesian_tready;
|
||||
|
||||
wire [WIDTH-1:0] sine_out_tdata;
|
||||
wire sine_out_tlast;
|
||||
wire sine_out_tvalid;
|
||||
wire sine_out_tready;
|
||||
|
||||
// AXI settings bus for phase values
|
||||
axi_setting_reg #(
|
||||
.ADDR (SR_PHASE_INC_ADDR),
|
||||
.AWIDTH (8),
|
||||
.WIDTH (16),
|
||||
.STROBE_LAST (1),
|
||||
.REPEATS (1)
|
||||
) set_phase_acc (
|
||||
.clk (clk),
|
||||
.reset (reset),
|
||||
.error_stb (),
|
||||
.set_stb (set_stb),
|
||||
.set_addr (set_addr),
|
||||
.set_data (set_data),
|
||||
.o_tdata (phase_in_tdata),
|
||||
.o_tlast (phase_in_tlast),
|
||||
.o_tvalid (phase_in_tvalid),
|
||||
.o_tready (phase_in_tready & enable)
|
||||
);
|
||||
|
||||
// AXI settings bus for Cartesian values
|
||||
axi_setting_reg #(
|
||||
.ADDR (SR_CARTESIAN_ADDR),
|
||||
.AWIDTH (8),
|
||||
.WIDTH (32),
|
||||
.REPEATS (1)
|
||||
) set_axis_cartesian (
|
||||
.clk (clk),
|
||||
.reset (reset),
|
||||
.error_stb (),
|
||||
.set_stb (set_stb),
|
||||
.set_addr (set_addr),
|
||||
.set_data (set_data),
|
||||
.o_tdata (cartesian_tdata),
|
||||
.o_tlast (),
|
||||
.o_tvalid (cartesian_tvalid),
|
||||
.o_tready (cartesian_tready & enable)
|
||||
);
|
||||
|
||||
assign cartesian_tlast = 1;
|
||||
|
||||
// Phase accumulator
|
||||
phase_accum phase_acc (
|
||||
.clk (clk),
|
||||
.reset (reset),
|
||||
.clear (clear),
|
||||
.i_tdata (phase_in_tdata),
|
||||
.i_tlast (phase_in_tlast),
|
||||
.i_tvalid (1'b1),
|
||||
.i_tready (phase_in_tready),
|
||||
.o_tdata (phase_out_tdata),
|
||||
.o_tlast (phase_out_tlast),
|
||||
.o_tvalid (phase_out_tvalid),
|
||||
.o_tready (phase_out_tready & enable)
|
||||
);
|
||||
|
||||
// CORDIC
|
||||
cordic_rotator cordic_inst (
|
||||
.aclk (clk),
|
||||
.aresetn (~(reset|clear)),
|
||||
.s_axis_phase_tdata (phase_out_tdata),
|
||||
.s_axis_phase_tvalid (phase_out_tvalid & cartesian_tvalid & enable),
|
||||
.s_axis_phase_tready (phase_out_tready),
|
||||
.s_axis_cartesian_tdata (cartesian_tdata),
|
||||
.s_axis_cartesian_tlast (cartesian_tlast),
|
||||
.s_axis_cartesian_tvalid (phase_out_tvalid & cartesian_tvalid & enable),
|
||||
.s_axis_cartesian_tready (cartesian_tready),
|
||||
.m_axis_dout_tdata (sine_out_tdata),
|
||||
.m_axis_dout_tlast (sine_out_tlast),
|
||||
.m_axis_dout_tvalid (sine_out_tvalid),
|
||||
.m_axis_dout_tready (sine_out_tready & enable)
|
||||
);
|
||||
|
||||
assign o_tdata = sine_out_tdata;
|
||||
assign o_tlast = sine_out_tlast;
|
||||
assign o_tvalid = sine_out_tvalid;
|
||||
assign sine_out_tready = o_tready;
|
||||
|
||||
endmodule // sine_tone
|
||||
Reference in New Issue
Block a user