This is an old revision of the document!
This wiki page is a follow up documentation for the ADI article titled “A Simple Baseband Processor for RF Transceivers”. The article covers the theory and implementation details of a simple BBP using the ZC706 + AD-FMCOMMS3 rapid prototyping platform. This page details the build process of the BBP design and how to run it on the ZC706 + AD-FMCOMMS3 hardware.
Please note that Analog Devices does NOT support this design. The recommended procedure for BBP design is to model it in Simulink and subsequently implement the design using HDL coder. The radio communication demonstration detailed here may be used as a frame work and implementation flow for such designs. The core of the axi_xcomm2ip is intentionally obfuscated to discourage any use or modification of the IP.
In addition to the BBP design, this document also covers the git repository use, adding AXI interfaces and modifying projects. There fore, it is important that the reader is familiar with the ADI HDL and Linux frame work and the tools needed to build them. Also note that the instructions exclusively use a Linux environment, make system and command line to do things. Please do not seek support or ask questions outside the scope of this document.
The ADI reference design repositories are hosted on GitHub. In this section, we show how to setup these repositories in your environment and use your own repository structure and version control along with it. The goal is to leave the ADI repository AS IT IS and do all the customizations outside of it.
[~]> mkdir rfbbp [~]> cd rfbbp [~/rfbbp]> git clone git@github.com:analogdevicesinc/hdl.git [~/rfbbp]> cd hdl [~/rfbbp/hdl]> git checkout hdl_2016_r1 [~/rfbbp/hdl]> git fetch [~/rfbbp/hdl]> git rebase origin/hdl_2016_r1 [~/rfbbp/hdl]> cd .. [~/rfbbp]> [~/rfbbp]> git clone git@github.com:analogdevicesinc/linux.git [~/rfbbp]> cd linux [~/rfbbp/hdl]> git checkout 2016_R1 [~/rfbbp/hdl]> git fetch [~/rfbbp/hdl]> git rebase origin/2016_R1 [~/rfbbp/hdl]> cd .. [~/rfbbp]>
After the repositories are cloned, the only thing to do is rebase the repository whenever you wish to update it. There is no need to change any files or scripts inside these repositories to use them.
The BBP library is just a directory that needs to contain some specific files. You may place this anywhere in your workspace. In this case, the library is created outside the ADI hdl repository but inside the rfbbp directory.
[~/rfbbp]> mkdir -p ip/axi_xcomm2ip [~/rfbbp]> touch ip/axi_xcomm2ip/Makefile [~/rfbbp]> touch ip/axi_xcomm2ip/axi_xcomm2ip.v [~/rfbbp]> touch ip/axi_xcomm2ip/axi_xcomm2ip_constr.xdc [~/rfbbp]> touch ip/axi_xcomm2ip/axi_xcomm2ip_ip.tcl
The IP library is “ip” and the IP is named “axi_xcomm2ip”. The minimum set of files an IP needs is the Makefile, a top HDL file, the constraints (if any) and a TCL file to build the IP.
It is beyond the scope of this document to cover the full HDL code base of the BBP. Instead we focus on some key factors below.
As mentioned in the article, the IP is an AXI IP core, making it a peripheral that interfaces to the processor so that it can be accessed via software. The ADI reference design repository features a lot of such AXI cores. A quick look reveals that the common module “up_axi” interfaces to the AXI bus. So the first part of the IP core is to instantiate this module. The internal signals mentioned below are then used to infer register or memory space accessible to the processor within the core.
wire up_clk; wire up_rstn; wire up_wreq_s; wire [13:0] up_waddr_s; wire [31:0] up_wdata_s; wire up_wack_s; wire up_rreq_s; wire [13:0] up_raddr_s; wire [31:0] up_rdata_s; wire up_rack_s; assign up_clk = s_axi_aclk; assign up_rstn = s_axi_aresetn; up_axi i_up_axi ( .up_rstn (up_rstn), .up_clk (up_clk), .up_axi_awvalid (s_axi_awvalid), .up_axi_awaddr (s_axi_awaddr), .up_axi_awready (s_axi_awready), .up_axi_wvalid (s_axi_wvalid), .up_axi_wdata (s_axi_wdata), .up_axi_wstrb (s_axi_wstrb), .up_axi_wready (s_axi_wready), .up_axi_bvalid (s_axi_bvalid), .up_axi_bresp (s_axi_bresp), .up_axi_bready (s_axi_bready), .up_axi_arvalid (s_axi_arvalid), .up_axi_araddr (s_axi_araddr), .up_axi_arready (s_axi_arready), .up_axi_rvalid (s_axi_rvalid), .up_axi_rresp (s_axi_rresp), .up_axi_rdata (s_axi_rdata), .up_axi_rready (s_axi_rready), .up_wreq (up_wreq_s), .up_waddr (up_waddr_s), .up_wdata (up_wdata_s), .up_wack (up_wack_s), .up_rreq (up_rreq_s), .up_raddr (up_raddr_s), .up_rdata (up_rdata_s), .up_rack (up_rack_s));
The BBP is implemented as an “insert-able” core. It is placed right in front of the AXI_AD9361 IP core. It is also intended to run at the AD9361 interface clock. So we use the same clock and reset signals as that of the AXI_AD9361 IP.
input clk, input rst,
In the receive direction it needs to interface to the ADC data ports of the AXI_AD9361 core. This makes it essentially an offline data processing core.
input adc_valid_i0, input [15:0] adc_data_i0, input adc_valid_q0, input [15:0] adc_data_q0, input adc_valid_i1, input [15:0] adc_data_i1, input adc_valid_q1, input [15:0] adc_data_q1,
Similarly, in the transmit direction it needs to interface to the DAC data ports of the AXI_AD9361 core. However, this breaks the default data path of the ADI design in which the DAC data is sourced from the DMA core. So the BBP IP core needs the DMA signals in order to maintain the default data path unless programmed by the software otherwise.
input dac_valid_i0, input [15:0] dma_data_i0, output [15:0] dac_data_i0, input dac_valid_q0, input [15:0] dma_data_q0, output [15:0] dac_data_q0, input dac_valid_i1, input [15:0] dma_data_i1, output [15:0] dac_data_i1, input dac_valid_q1, input [15:0] dma_data_q1, output [15:0] dac_data_q1, input dma_dovf, input dma_dunf, output dac_dovf, output dac_dunf,
Then infer a simple data interface multiplexer.
assign dac_data_i0 = dac_data_i0_int; assign dac_data_q0 = dac_data_q0_int; assign dac_data_i1 = dac_data_i1_int; assign dac_data_q1 = dac_data_q1_int; assign dac_dovf = dac_dovf_int; assign dac_dunf = dac_dunf_int; always @(posedge rst or posedge clk) begin if (rst == 1'b1) begin dac_enable_m1 <= 1'd0; dac_enable <= 1'd0; dac_data_i0_int <= 'd0; dac_data_q0_int <= 'd0; dac_data_i1_int <= 'd0; dac_data_q1_int <= 'd0; dac_dovf_int <= 'd0; dac_dunf_int <= 'd0; end else begin dac_enable_m1 <= up_dac_enable; dac_enable <= dac_enable_m1; if (dac_enable == 1'b1) begin dac_data_i0_int <= dac_data_i; dac_data_q0_int <= dac_data_q; dac_data_i1_int <= 16'd0; dac_data_q1_int <= 16'd0; dac_dovf_int <= 1'd0; dac_dunf_int <= 1'd0; end else begin dac_data_i0_int <= dma_data_i0; dac_data_q0_int <= dma_data_q0; dac_data_i1_int <= dma_data_i1; dac_data_q1_int <= dma_data_q1; dac_dovf_int <= dma_dovf; dac_dunf_int <= dma_dunf; end end end
The rest of the IP core may now generate its own transmit data for the DAC and process the received data from the ADC as it seems fit. This part, though critical, is beyond the scope of this document and is intentionally left out of our discussion.
In order to use this IP core all we need now is a definition of the register space.
The TCL file is quite easy to create and simply makes use of the ADI repository TCL procedures like any other IP core. The only thing to note is the use of the environment variable “AD_HDL_DIR”. This variable is used to point to the ADI HDL repository and use the IP cores, projects and scripts inside of it.
set ad_hdl_dir $::env(ADI_HDL_DIR) set ad_phdl_dir $::env(ADI_HDL_DIR) source $ad_hdl_dir/library/scripts/adi_ip.tcl adi_ip_create axi_xcomm2ip adi_ip_files axi_xcomm2ip [list \ "$ad_hdl_dir/library/common/ad_rst.v" \ "$ad_hdl_dir/library/common/ad_mem.v" \ "$ad_hdl_dir/library/common/up_axi.v" \ "axi_xcomm2ip_core.v" \ "axi_xcomm2ip.v" ] adi_ip_properties axi_xcomm2ip adi_ip_constraints axi_xcomm2ip [list \ "axi_xcomm2ip_constr.xdc" ] ipx::save_core [ipx::current_core]
The Makefile is also a straight copy of the ADI IP Makefile. However note the export of the environment variable. This variable is set based on the workspace setup we have. This is all that is needed to use the ADI HDL frame work outside of the HDL repository.
export ADI_HDL_DIR := ../../../hdl export ADI_PHDL_DIR := ../../../hdl M_DEPS += $(ADI_HDL_DIR)/library/scripts/adi_env.tcl M_DEPS += $(ADI_HDL_DIR)/library/scripts/adi_ip.tcl M_DEPS += $(ADI_HDL_DIR)/library/common/ad_mem.v M_DEPS += $(ADI_HDL_DIR)/library/common/ad_rst.v M_DEPS += $(ADI_HDL_DIR)/library/common/up_axi.v M_DEPS += axi_xcomm2ip_constr.xdc M_DEPS := axi_xcomm2ip_ip.tcl M_DEPS += axi_xcomm2ip.v M_VIVADO := vivado -mode batch -source M_FLIST := *.cache M_FLIST += *.data M_FLIST += *.xpr M_FLIST += *.log M_FLIST += component.xml M_FLIST += *.jou M_FLIST += xgui M_FLIST += .Xil .PHONY: all clean clean-all all: axi_xcomm2ip.xpr clean:clean-all clean-all: rm -rf $(M_FLIST) axi_xcomm2ip.xpr: $(M_DEPS) rm -rf $(M_FLIST) $(M_VIVADO) axi_xcomm2ip_ip.tcl >> axi_xcomm2ip_ip.log 2>&1