So far on this journal of looking at the verification of the Versal AIE and new capabilities of Vitis for verification, everything has been in simulation.
We have looked at how we can use MATLAB or Python for Vitis functional simulation, followed by examining how we can simulate the AIE designs using XSIM.
In this project we are going to examine how we are able to run our Vitis Sub System designs on actual hardware using AMD Vitis Hardware In The Loop capabilities.
AMD Vitis Hardware In The Loop Overview.To use the Vitis HIL capabilities the Vitis design must first be captures as a Vitis Sub System (VSS).
Once we are satisfied the VSS performs as desired using Vitis Functional Simulation and XSim in Vivado. We are in a position to test the design on hardware.
This testing on hardware enables us to verify the VSS performs as expected on the hardware prior to integration with the rest of the wider design.
Starting with the VSS the HIL flow, takes the VSS and creates an design around it which supports HIL. Once the design is completed the HIL server is then created which is packaged with the design on the SD Card.
On the target board once the board is powered and booted from the SD Card, the host system is able to interact with the target board using Ethernet.
The host system can leverage Python or Matlab to communicate with the design over the Ethernet link.
This allows developers to be able to send down test vectors to the target board, perform the computation on the hardware, and receive the results back from the hardware over Ethernet. This allows the developers to verify the behaviour on hardware.
Currently this capability is supported for the VCK190 target evaluation kit and designs can consist of AI engine, HLS or HDL modules or any combination provided they are within a Vitis Sub System.
Creating a VSSIn AMD Vitis, a VSS (Vitis Sub System) is created by packaging reusable PL kernels and/or AI Engine graphs into a platform-independent component using the v++ linker. The flow starts by compiling PL kernels into .xo files (from HLS or RTL) and, if required, building AI Engine graphs into a libadf.
These components are then described in a VSS configuration file that defines kernel instances, subsystem metadata, and interconnections.
vss=amd.com:<vss_library_name>:<vss_component_name>:<version_number>:<list_of_instances of PL kernels and/or AI Engine graphs>
[connectivity]
nk=producer:2:p0,p1
nk=consumer:2:c0,c1
vss=amd.com:myLib:MyVSSComponent:1.0:p0,p1,c0,c1,ai_engine_0
stream_connect=p0.outs:c0:ins
stream_connect=p1:outs:ai_engine_0.si_0
stream_connect=ai_engine_0.so_0:c1:insThe VSS is generated by running the linker in VSS mode for a specific target device part, producing a .vss archive that can be reused, simulated, or hierarchically integrated into higher-level Vitis designs, provided the same device part is used.
For example, a simple PL-only VSS can be created with a configuration file that instantiates a kernel and defines the subsystem, and then built using a command such as v++ --link --mode vss --part xcvc1902-vsva2197-2MP-e-S --config vss.cfg my_kernel.xo, which generates a reusable my_vss.vss subsystem ready to be imported into another Vitis project.
Another method of creating a Vitis Sub System is to use Vitis Model Composer export to VSS feature.
For this project we will be using a VSS provided by AMD as part of the Vitis HIL demonstration. A compressed archive of these VSS is attached to the project.
Preparationthe first thing we need to do is ensure we have Vitis 2025.2 installed on the development machine. If you do not have it, you can download it from here.
With Vitis installed the next thing we need is the SDK and RootFS, we can download these from the AMD website also.
Once these are downloaded the first step is to create the sdk, this can be done by extracting the Versal Common Image and running the script sdk.sh
To be able to run the build we then need to set a couple of variables the first SDK_LOCAL points to the directory of the SDK just created.
The second SYSROOT_VERSAL points to the location of the kernel image, and RootFS this was also provided as part of the common image download.
We are now ready to be able to create the SD Card image of the target VSS. For this we are going to target a channeliser example.
Creating the HIL Server SD Card ImageThis will be a command line build, so the first thing we need to do is source Vitis 2025.2 environment set up from the terminal we used above.
Once this is completed we are going to run the command
hil_gen -vss ./chn_vss/vss/DUT/ -p /media/data_hdd/tools/2025.2/Vitis/base_platforms/xilinx_vck190_base_202520_1/xilinx_vck190_base_202520_1.xpfm -sdk $SDK_LOCAL -rootfs $SYSROOT_VERSALThis will start the build process.
Once the build process starts you will see there a HIL_DUT folder created which is the working directory for the build.
After a short while the build process will be completed
Under the build working directory you will be able to see the output SD Card information and a SD Card Image
The SD Card image and the hil_interface_spec.json which is referenced by the host application code are linked by a unique hash code so the correct pair of files is used, preventing mix up from previous builds
The contents of the SD Card
The next step is to copy this to a SD card, insert it into the VK190 and boot the board.
Hardware in the LoopTo get started with HIL the first step is to insert the SD Card into the VCK190 and booting the board.
Over the serial port, log into the embedded Linux running on the VCK190 using the user name petalinux.
If the board is connected to a network, the board should have an IP address assigned by the DHCP server, this step should be skipped.
But if the board’s ethernet cable is connected to a PC, some steps are necessary to make the board network accessible.”
ifconfig end0 10.81.109.156Under then channeliser example there are two python files under the python folder. Open the host file and edit the IP address for the VCK 190
On the VCK190, run the following commands
sudo su
cd /run/media/mmcblk0p1To start the server run the command
./hil_server.shThis will start the hardware in the loop server waiting for the client to connect to the host.
On the host run the hil_host.py file this will start the HIL example running. You will see the HIL processing the data as expected.
On the VCK190 target you will see the client connect and the application begin being exercised under control of the client.
There are two scripts used for this application, lets take a look at key elements of them.
The wb_src file provides the stimulus engine that really brings the Hardware-in-the-Loop setup to life. This module generates a repeatable and fully controllable wideband complex waveform that looks and behaves like a real signal environment. It allows me to enable or disable up to 16 individual sub-channels, selectively apply QAM modulation with raised-cosine pulse shaping, and even inject constant (DC-like) channels for quick sanity checks. Crucially for HIL operation, the FIR filter state is preserved between calls, so the signal remains continuous across processing blocks—exactly what the hardware would see in a real streaming system.
# Copyright 1986-2022 Xilinx, Inc. All Rights Reserved.
# Copyright 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved.
# This file contains confidential and proprietary information of Xilinx, Inc. and is protected under U.S. and international copyright and other intellectual property laws.
# DISCLAIMER
# This disclaimer is not a license and does not grant any rights to the materials distributed herewith. Except as otherwise provided in a valid license issued to you by Xilinx, and to the maximum extent permitted by applicable law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and (2) Xilinx shall not be liable (whether in contract or tort, including negligence, or under any other theory of liability) for any loss or damage of any kind or nature related to, arising under or in connection with these materials, including for any direct, or any indirect, special, incidental, or consequential loss or damage (including loss of data, profits, goodwill, or any type of loss or damage suffered as a result of any action brought by a third party) even if such damage or loss was reasonably foreseeable or Xilinx had been advised of the possibility of the same.
# CRITICAL APPLICATIONS
# Xilinx products are not designed or intended to be fail-safe, or for use in any application requiring fail-safe performance, such as life-support or safety devices or systems, Class III medical devices, nuclear facilities, applications related to the deployment of airbags, or any other applications that could lead to death, personal injury, or severe property or environmental damage (individually and collectively, "Critical Applications"). Customer assumes the sole risk and liability of any use of Xilinx products in Critical Applications, subject only to applicable laws and regulations governing limitations on product liability.
# THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS PART OF THIS FILE AT ALL TIMES.
import numpy as np
from scipy.signal import fir_filter_design, lfilter
class wb_src:
def __init__(self,Nsmp,upsmp,chan_en,qam_en,swp_en,swp_rate):
self.Nsmp = Nsmp
self.upsmp = upsmp
self.chan_en = chan_en
self.qam_en = qam_en
self.swp_en = swp_en
self.swp_rate = swp_rate
coef = rcosdesign(0.22,4,self.upsmp)
self.qam_coef = self.upsmp * coef / np.sum(coef)
self.fir_state = np.zeros((len(self.qam_coef)-1,16),dtype=np.complex64)
self.prv_carrier_ph = np.random.randint(1,self.Nsmp+1,size=16)
self.prv_sweep_ph = np.zeros(16,dtype=int)
self.frq_nrm = np.array([0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1])
self.nvar = 0.05
self.gain = 1/6
def get_samp(self):
# array for individual samples
wbsmp = np.zeros((self.upsmp*self.Nsmp,16),dtype=complex)
# generate baseband QAM samples
for i in range(16):
if self.chan_en[i] and self.qam_en[i]:
iq = 2*np.random.randint(0,4,size=(self.Nsmp,2))-3
qsym = (iq[:,0] + 1j*iq[:,1]) / np.sqrt(10)
up_len = len(qsym) * self.upsmp
qsym_us = np.zeros(up_len, dtype=np.complex64)
qsym_us[::self.upsmp] = qsym
qsamp, self.fir_state[:,i] = lfilter(self.qam_coef,1,qsym_us,zi=self.fir_state[:,i])
wbsmp[:,i] = qsamp
# Constant channels
I = np.where((np.array(self.chan_en) == 1) & (np.array(self.qam_en) == 0))[0]
if I.size > 0:
wbsmp[:, I] = np.ones((self.upsmp*self.Nsmp, 1), dtype=np.complex64)
# Carrier modulation
I = np.where(np.array(self.chan_en) == 1)[0]
J = np.where((np.array(self.chan_en) == 1) & (np.array(self.swp_en) == 1))[0]
if I.size > 0:
cr_cntr = (np.tile(np.arange(1, self.upsmp * self.Nsmp + 1).reshape(-1, 1), (1, len(I))) + self.prv_carrier_ph[I]) % (2**32)
self.prv_carrier_ph[I] = cr_cntr[-1, :]
cr_cntr = cr_cntr * self.frq_nrm[I] / 16
if J.size > 0:
swp_cntr = (np.tile(np.arange(1, self.upsmp * self.Nsmp + 1).reshape(-1, 1), (1, len(J))) + self.prv_sweep_ph[J]) % (2**32)
self.prv_sweep_ph[J] = swp_cntr[-1, :]
swp_cntr = (swp_cntr**2) * np.array(self.swp_rate)[J] * 1e-8
ia = [np.where(I == j)[0][0] for j in J]
cr_cntr[:, ia] += swp_cntr
carr_smp = np.exp(1j * 2 * np.pi * cr_cntr)
wbsmp[:, I] *= carr_smp
# add noise and set signal level
noise = self.nvar * (np.random.randn(self.upsmp*self.Nsmp) + 1j * np.random.randn(self.upsmp*self.Nsmp))
smp = self.gain * (np.sum(wbsmp, axis=1) + noise)
return smp
def rcosdesign(alpha, span, sps):
# Raised cosine filter design
N = span * sps + 1
t = np.arange(-span/2, span/2 + 1/sps, 1/sps)
h = np.zeros_like(t)
for i in range(len(t)):
if t[i] == 0.0:
h[i] = 1.0
elif abs(t[i]) == 1 / (2 * alpha):
h[i] = (np.pi / 4) * np.sinc(1 / (2 * alpha))
else:
numerator = np.sin(np.pi * t[i]) * np.cos(np.pi * alpha * t[i])
denominator = (np.pi * t[i]) * (1 - (2 * alpha * t[i]) ** 2)
h[i] = numerator / denominator
h /= np.sum(h)
return hThe HIL_HOST.py controls the testing on hardware using the design as a callable hardware accelerator sitting on the network. From the Python host, we instantiate the HIL client using an interface specification JSON and simply point it at the board’s IP address and port.
Inside the main processing loop, the HIL flow looks very much like a streaming system. Complex samples are generated using wb_src and quantized into a fixed-point varray, and passed to the FPGA using hil_chnlzr.run().
One important real-world detail is that the amount of data returned by the hardware isn’t always uniform across outputs or across calls. To deal with this, we explicitly buffer each output stream in a FIFO on the host and only release frames once all channels have data available.
# Copyright 1986-2022 Xilinx, Inc. All Rights Reserved.
# Copyright 2022-2025 Advanced Micro Devices, Inc. All Rights Reserved.
# This file contains confidential and proprietary information of Xilinx, Inc. and is protected under U.S. and international copyright and other intellectual property laws.
# DISCLAIMER
# This disclaimer is not a license and does not grant any rights to the materials distributed herewith. Except as otherwise provided in a valid license issued to you by Xilinx, and to the maximum extent permitted by applicable law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and (2) Xilinx shall not be liable (whether in contract or tort, including negligence, or under any other theory of liability) for any loss or damage of any kind or nature related to, arising under or in connection with these materials, including for any direct, or any indirect, special, incidental, or consequential loss or damage (including loss of data, profits, goodwill, or any type of loss or damage suffered as a result of any action brought by a third party) even if such damage or loss was reasonably foreseeable or Xilinx had been advised of the possibility of the same.
# CRITICAL APPLICATIONS
# Xilinx products are not designed or intended to be fail-safe, or for use in any application requiring fail-safe performance, such as life-support or safety devices or systems, Class III medical devices, nuclear facilities, applications related to the deployment of airbags, or any other applications that could lead to death, personal injury, or severe property or environmental damage (individually and collectively, "Critical Applications"). Customer assumes the sole risk and liability of any use of Xilinx products in Critical Applications, subject only to applicable laws and regulations governing limitations on product liability.
# THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS PART OF THIS FILE AT ALL TIMES.
import numpy as np
import matplotlib.pyplot as plt
from hil import hil
import varray as va
from wb_src import wb_src
import time
# Configure host code
n_iter = 1024 # specifies how many times HIL run function is called
N = 16384 # sets block size for HIL processing
tictocsum = 0.0 # total HIL processing time
totalsamp = 0 # total output samples
stats_en = False # delays accumulating stats until output is available
fft_sz = 1024 # for spectrum display
overlap = fft_sz // 2 # for spectrum display
step = fft_sz -overlap # for spectrum display
# Instantiate HIL object
interface_spec = "../../hil_DUT/hil_interface_spec.json"
hil_server = "xx.xx.xx.xxx:8888" # modify for your IP
hil_chnlzr = hil(interface_spec=interface_spec)
hil_chnlzr.setConnection(ip_addr=hil_server)
# Configure HIL object - adjust frame size and timeout to optimize HIL for your network
chan_frame_size = 4 * N
timeout = 0.01
for i in range(7):
hil_chnlzr.setInputFrameSize(i, chan_frame_size)
for i in range(8):
hil_chnlzr.setOutputFrameSize(i, chan_frame_size)
hil_chnlzr.setTimeout(timeout)
hil_chnlzr.initialize()
# Create a FIFO to align HIL output
ssro = 8
fifo_max = 5 * chan_frame_size
fifo_buffer = np.zeros((fifo_max, ssro), dtype=complex)
fifo_ptr = np.zeros(ssro, dtype=int)
fifo_size = np.zeros(ssro, dtype=int)
# Configure wideband signal source
Fs = 8.75e9
upsmp = 28
chan_en = [0,1,1,0,0,1,1,0,0,0,0,0,0,0,0,0] # enable individual sub-channels
qam_en = [0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0] # modulate selected sub-channels
swp_en = [0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # enable carrier sweep on sub-channels
swp_rate = [0,0.02,0,0,0,0,0,0,0,0,0,0,0,0,0,0] # set carrier sweep rate: range [-1.0, +1.0]
wbs = wb_src(N,upsmp,chan_en,qam_en,swp_en,swp_rate)
# Setup spectrum display for wideband input
fig_src, ax_src = plt.subplots()
fig_src.patch.set_facecolor('black')
ax_src.set_facecolor('black')
fft_freqs = np.fft.fftshift(np.fft.fftfreq(fft_sz, 1/Fs))
line, = ax_src.plot(fft_freqs/1e9,np.zeros(fft_sz),color='cyan')
for spine in ax_src.spines.values():
spine.set_color('white')
ax_src.set_ylim(-20, 50)
ax_src.set_xlim(-Fs/2e9, Fs/2e9)
ax_src.set_title("Wideband Signal Input", color='white')
ax_src.set_xlabel("Frequency (GHz)", color='white')
ax_src.set_ylabel("Power (dB)", color='white')
ax_src.tick_params(colors='white')
ax_src.grid(True, color='gray')
# Setup spectrum display for channelizer outputs
disp_slct = [1, 2, 5, 6]
fig, axes = plt.subplots(2, 2, figsize=(8,8))
fig.subplots_adjust(hspace=0.4,wspace=0.4)
for ax in axes.flat:
ax.set_facecolor('black')
for spine in ax.spines.values():
spine.set_color('white')
ax.set_ylim(-40, 50)
ax.set_xlim(-(8/7)*Fs/(16*2e6), (8/7)*Fs/(16*2e6))
ax.set_xlabel("Frequency (MHz)", color='white')
ax.set_ylabel("Power (dB)", color='white')
ax.tick_params(colors='white')
ax.grid(True, color='gray')
fig.patch.set_facecolor('black')
fft_freqo = np.fft.fftshift(np.fft.fftfreq(fft_sz, 16/((8/7)*Fs)))
line0, = axes[0,0].plot(fft_freqo/1e6,np.zeros(fft_sz),color='yellow')
line1, = axes[0,1].plot(fft_freqo/1e6,np.zeros(fft_sz),color='yellow')
line2, = axes[1,0].plot(fft_freqo/1e6,np.zeros(fft_sz),color='yellow')
line3, = axes[1,1].plot(fft_freqo/1e6,np.zeros(fft_sz),color='yellow')
axes[0,0].set_title(f"Channel {disp_slct[0]}", color='white')
axes[0,1].set_title(f"Channel {disp_slct[1]}", color='white')
axes[1,0].set_title(f"Channel {disp_slct[2]}", color='white')
axes[1,1].set_title(f"Channel {disp_slct[3]}", color='white')
plt.ion()
plt.show()
# Main processing loop
for _ in range(n_iter):
# generate wideband samples
smp = wbs.get_samp()
# update input spectrum plot
segments = [smp[i:i+fft_sz] for i in range(0, len(smp)-fft_sz+1, step)]
pwrspec = []
for seg in segments:
seg_windowed = seg * np.hanning(fft_sz)
fft_result = np.fft.fft(seg_windowed, n=fft_sz, axis=0)
pwrspec.append(np.abs(fft_result)**2)
avg_pwrspec = np.mean(pwrspec, axis=0)
line.set_ydata(np.fft.fftshift(10*np.log10(avg_pwrspec)))
fig_src.canvas.draw()
fig_src.canvas.flush_events()
# break the wideband signal data into SSR streams
Nwbs = smp.size
chnlzr_in = np.zeros((Nwbs//7,7), dtype = np.complex64)
for col in range(7):
src_idx = np.column_stack((np.arange(13-col,Nwbs,14), np.arange(6-col,Nwbs,14))).T.reshape(-1,order='F')
chnlzr_in[:,col] = (2**15) * (smp[src_idx])
# create a Vitis array to send to HIL
send_data = va.array(chnlzr_in, dtype=va.cint16)
# process data with HIL
tic = time.time()
return_data = hil_chnlzr.run(send_data)
if stats_en:
tictocsum += time.time() - tic
# Put new samples into FIFO
for i in range(ssro):
nsmp = return_data[i].shape[0]
if nsmp > 0:
mod_idx = (fifo_ptr[i] + fifo_size[i] + np.arange(nsmp)) % fifo_max
fifo_buffer[mod_idx,i] = np.array(return_data[i], dtype=complex)
fifo_size[i] += nsmp
# Determine number of output samples to read from fifo.
# More data than specified by chan_frame_size may be available for
# reading, but a fixed amount is read so that output displays function
# more like real spectrum analyzers. This also adds latency between
# input and output displays.
if np.min(fifo_size) == 0:
nout = 0
else:
nout = chan_frame_size
if stats_en:
totalsamp += ssro * nout
print(f"HIL processing throughput = {np.round(totalsamp / tictocsum)} samples per second.")
elif nout != 0:
stats_en = True
# Extract data from FIFO for display
if nout != 0:
chnlzr_ssr_out = np.zeros((nout,ssro), dtype=complex)
for i in range(ssro):
mod_idx = (fifo_ptr[i] + np.arange(nout)) % fifo_max
chnlzr_ssr_out[:,i] = fifo_buffer[mod_idx,i]
fifo_ptr[i] = (fifo_ptr[i] + nout) % fifo_max
fifo_size[i] -= nout
# Extract individual channel streams from channelizer SSR output
ssr_combined = (2**-15) * (chnlzr_ssr_out.flatten(order='F'))
Nall = ssr_combined.size
chnlzr_out = np.zeros((Nall // 16, 16), dtype = complex)
for i in range(4):
src_idx = np.column_stack((np.arange(i,Nall//8,4), np.arange(Nall//8+i,Nall//4,4))).T.reshape(-1,order='F')
chnlzr_out[:,i] = ssr_combined[src_idx]
for i in range(4):
src_idx = np.column_stack((np.arange(Nall//4+i,3*Nall//8,4), np.arange(3*Nall//8+i,Nall//2,4))).T.reshape(-1,order='F')
chnlzr_out[:,i+4] = ssr_combined[src_idx]
for i in range(4):
src_idx = np.column_stack((np.arange(Nall//2+i,5*Nall//8,4), np.arange(5*Nall//8+i,3*Nall//4,4))).T.reshape(-1,order='F')
chnlzr_out[:,i+8] = ssr_combined[src_idx]
for i in range(4):
src_idx = np.column_stack((np.arange(3*Nall//4+i,7*Nall//8,4), np.arange(7*Nall//8+i,Nall,4))).T.reshape(-1,order='F')
chnlzr_out[:,i+12] = ssr_combined[src_idx]
else:
chnlzr_out = np.array([], dtype=complex)
# Display spectrum of output channels
if chnlzr_out.size != 0:
segments = [chnlzr_out[i:i+fft_sz,disp_slct[0]] for i in range(0, chnlzr_out.shape[0]-fft_sz+1, step)]
pwrspec = []
for seg in segments:
seg_windowed = seg * np.hanning(fft_sz)
fft_result = np.fft.fft(seg_windowed, n=fft_sz, axis=0)
pwrspec.append(np.abs(fft_result)**2)
avg_pwrspec = np.mean(pwrspec, axis=0)
line0.set_ydata(np.fft.fftshift(10*np.log10(avg_pwrspec)))
segments = [chnlzr_out[i:i+fft_sz,disp_slct[1]] for i in range(0, chnlzr_out.shape[0]-fft_sz+1, step)]
pwrspec = []
for seg in segments:
seg_windowed = seg * np.hanning(fft_sz)
fft_result = np.fft.fft(seg_windowed, n=fft_sz, axis=0)
pwrspec.append(np.abs(fft_result)**2)
avg_pwrspec = np.mean(pwrspec, axis=0)
line1.set_ydata(np.fft.fftshift(10*np.log10(avg_pwrspec)))
segments = [chnlzr_out[i:i+fft_sz,disp_slct[2]] for i in range(0, chnlzr_out.shape[0]-fft_sz+1, step)]
pwrspec = []
for seg in segments:
seg_windowed = seg * np.hanning(fft_sz)
fft_result = np.fft.fft(seg_windowed, n=fft_sz, axis=0)
pwrspec.append(np.abs(fft_result)**2)
avg_pwrspec = np.mean(pwrspec, axis=0)
line2.set_ydata(np.fft.fftshift(10*np.log10(avg_pwrspec)))
segments = [chnlzr_out[i:i+fft_sz,disp_slct[3]] for i in range(0, chnlzr_out.shape[0]-fft_sz+1, step)]
pwrspec = []
for seg in segments:
seg_windowed = seg * np.hanning(fft_sz)
fft_result = np.fft.fft(seg_windowed, n=fft_sz, axis=0)
pwrspec.append(np.abs(fft_result)**2)
avg_pwrspec = np.mean(pwrspec, axis=0)
line3.set_ydata(np.fft.fftshift(10*np.log10(avg_pwrspec)))
fig.canvas.draw()
fig.canvas.flush_events()Wrap UpThis project has completed the trilogy demonstrating how we can verify versal AIE, HLS and RTL kernel sub systems using hardware in the loop. This is the final aspect of verification which starts with a functional simulation, progresses to RTL Simulation in Vivado with the wider design and finally hardware in the loop verification.
To join the early access lounge for hardware-in-the-loop verification, please reach out to your FAE.








Comments