As stated in the title, this time we aim to achieve seamless video processing transition based on our previously established design foundation.
This post also requires reading the previous sections first to make implementation easier.
VivadoThe Block Design is primarily based on the previous one. Since the earlier Video Test Pattern Generator (TPG) was only used as a bypass, we can replace it with an AXI4-stream broadcaster.
The AXI4-stream broadcaster is an IP core that duplicates an input AXI4 data stream to multiple output channels, allowing simultaneous distribution of data. This enables parallel processing in applications such as video processing and communication systems.
However, note that when outputting from the AXI4-stream broadcaster, it must be connected to an AXI4-stream Data FIFO; otherwise, no data will be output. This is mainly because the subsequent IP requires waiting for all interfaces to assert tvalid, which is not the case on the S00 interface. For more details, please refer to the official Xilinx explanation.
For the AXI4-stream Data FIFO reset, we choose to use GPIO in Vitis to customize when to reset.
Thus, we now have two identical HDMI input sources. One input is processed through the Sobel Filter while the other is directly passed through as a bypass. Each of these signals is handled by its own VDMA for buffering and synchronization.
From the outputs of the individual VDMAs, we can connect an AXI4-stream switch or an AXI4-stream combiner to achieve source switching. Using the AXI4-stream switch will completely switch to a different source, which causes a brief blackout during the transition. In contrast, the AXI4-stream combiner aligns different signals via the tuser signal and then combines them (e.g., merging 24-bit tdata with 24-bit tdata into 48-bit tdata). Since the signals are combined into a single source—selecting either tdata[47:24] or tdata[23:0] for output—the transition occurs without a blackout.
Here are some of the key signals in the AXI4-Stream interface:
- TDATA: Carries the primary data payload.
- TVALID: Indicates that the data on TDATA is valid and ready for transfer.
- TREADY: Signifies that the downstream component is ready to receive data.
- TKEEP: Marks which byte lanes of TDATA are valid, useful for data streams that are not full width.
- TLAST: Denotes the last data word of a packet or frame.
- TUSER: Provides additional user-defined sideband information, often used for alignment or metadata purposes.
Thus, you can see the relationships between these signals as shown in the waveform below.
As mentioned earlier, since the confirmed output is either tdata[47:24] or tdata[23:0], we can write a simple RTL Code IP to determine which one to use.
First, define the inputs and outputs.
module AXI4_Switch(
input [47:0] Combine_data,
input Combine_valid,
input [1:0] Combine_user,
input Combine_last,
output reg Combine_ready,
input switch,
output reg [23:0] video_data,
output reg video_valid,
output reg video_user,
output reg video_last,
input video_ready
);
- To align the tuser signals from different sources, the AXI4-Stream Combiner combines them into a 2-bit signal. Only when both bits are set to 1 can we determine the output data reliably.
- The switch signal is controlled by a physical switch on the PYNQ-Z2 board, implemented via GPIO.
Thus, the current mode is determined only when the switch mode is confirmed and the tuser signals from the different sources are aligned.
reg update_mode = 0;
always@(*) begin
if(switch == 1 && (Combine_user[0] & Combine_user[1]) == 1) update_mode = 1;
else if(switch == 0 && (Combine_user[0] & Combine_user[1]) == 1) update_mode = 0;
end
always@(*) begin
video_valid = Combine_valid;
video_user = (Combine_user[0] & Combine_user[1]);
video_last = Combine_last;
Combine_ready = video_ready;
if(switch == 1) begin
if(update_mode == 1) video_data = Combine_data[47:24];
end
else if(switch == 0) begin
if(update_mode == 0) video_data = Combine_data[23:0];
end
end
The written RTL code can be directly dragged into the Block Design to form an IP.
Finally, connect the AXI4-Stream Subset Converter to form a proper AXI4-Stream, then attach the AXI4-Stream Data FIFO to ensure that signals such as tvalid, tuser, and tready are output correctly.
Regarding the relationship between the physical switch on the PYNQ-Z2 and the control of the signal sources via GPIO, as shown in the diagram below, the value from the physical switch is first read from GPIO channel 1. Then, this value is output through GPIO channel 2 to the Custom IP.
After adding the physical switch pin configuration to the XDC, the entire Block Design is complete.
set_property -dict { PACKAGE_PIN M20 IOSTANDARD LVCMOS33 } [get_ports { GPIO_0_tri_i }]; #IO_L7N_T1_AD2N_35 Sch=sw[0]
VitisTo build a new Platform and Application, or update the XSA on an existing Platform, you can refer to the previous post and update the code as follows:
/******************************************************************************
*
* Copyright (C) 2009 - 2014 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* XILINX BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*
* helloworld.c: simple test application
*
* This application configures UART 16550 to baud rate 9600.
* PS7 UART (Zynq) is not initialized by this application, since
* bootrom/bsp configures it to baud rate 115200
*
* ------------------------------------------------
* | UART TYPE BAUD RATE |
* ------------------------------------------------
* uartns550 9600
* uartlite Configurable only in HW design
* ps7_uart 115200 (configured by bootrom/bsp)
*/
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xgpio.h"
#include "xhls_sobel_axi_stream_top.h"
XHls_sobel_axi_stream_top example_ptr;
XGpio input;
XGpio FIFO_Reset;
#define width 1920
#define height 1080
void ResetVDMA()
{
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x00, 0x00000004);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x30, 0x00000004);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0x00, 0x00000004);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0x30, 0x00000004);
}
void RunVDMAwithRegister(){
/////////////////////////// VDMA 0 /////////////////////////
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x30, 0x8B);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xAC, XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x0000000);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xB0, XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x2000000);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xB4, XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x4000000);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xA8, width*3);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xA4, width*3);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0xA0, height);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x00, 0x8B);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x5C, XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x0000000);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x60, XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x2000000);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x64, XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x4000000);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x58, width*3);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x54, width*3);
Xil_Out32(XPAR_AXI_VDMA_0_BASEADDR + 0x50, height);
/////////////////////////////////////////////////////////////////////////////
/////////////////////////// VDMA 1 /////////////////////////
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0x30, 0x8B);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0xAC, XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x6000000);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0xB0, XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x8000000);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0xB4, XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0xA000000);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0xA8, width*3);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0xA4, width*3);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0xA0, height);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0x00, 0x8B);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0x5C, XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x6000000);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0x60, XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0x8000000);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0x64, XPAR_PS7_DDR_0_S_AXI_BASEADDR + 0xA000000);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0x58, width*3);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0x54, width*3);
Xil_Out32(XPAR_AXI_VDMA_1_BASEADDR + 0x50, height);
/////////////////////////////////////////////////////////////////////////////
}
int main()
{
init_platform();
int Status;
XGpio_Initialize(&input, XPAR_AXI_GPIO_0_DEVICE_ID);
XGpio_Initialize(&FIFO_Reset, XPAR_AXI_GPIO_1_DEVICE_ID);
XGpio_DiscreteWrite(&FIFO_Reset, 1, 0);
usleep(10000);
XGpio_DiscreteWrite(&FIFO_Reset, 1, 1);
ResetVDMA();
// Initialize module
Status = XHls_sobel_axi_stream_top_Initialize(&example_ptr, XPAR_HLS_SOBEL_AXI_STREAM_0_DEVICE_ID);
if (Status != XST_SUCCESS) {
xil_printf("Example Initialization Failed\r\n");
return XST_FAILURE;
}
XHls_sobel_axi_stream_top_Set_rows(&example_ptr, height);
XHls_sobel_axi_stream_top_Set_cols(&example_ptr, width);
RunVDMAwithRegister();
for(;;){
XHls_sobel_axi_stream_top_Start(&example_ptr);
XHls_sobel_axi_stream_top_EnableAutoRestart(&example_ptr);
XGpio_DiscreteWrite(&input, 2, XGpio_DiscreteRead(&input, 1));
}
print("Successfully ran application");
cleanup_platform();
return 0;
}
One thing to note is that you must include the following two lines inside the for loop; otherwise, only the very first frame will be output.
XHls_sobel_axi_stream_top_Start(&example_ptr);
XHls_sobel_axi_stream_top_EnableAutoRestart(&example_ptr);
ResultThank you very much for taking the time to read this post. I sincerely welcome any suggestions or feedback you may have and look forward to engaging in thoughtful discussion with you!
Comments
Please log in or sign up to comment.