其实通过vga显示官方有一个ip核可以用,但是我不是主要为了实现vga显示,而是为了实现如何从ps端向pl端进行大量的数据传送,经过了间断性的不断代码测试,编写,我最终实现了。下面简单说下我是怎样实现的。
目的:
1.实现pl读取ddr内的数据将数据转换成vga的数据流显示到屏幕上,显示大小640*480
2.ps端向ddr内写入像素值,pl端能够同步转换并显示出来。
3.利用axi总线实现。
本次是使用的板子,zybo,板子上的芯片是zynq7010,pl端通信通过axi总线与ps端进行数据的交互,如果要进行简单的交互,可以通过利用axi从模式来进行交互,但是从模式有一个缺点,就是只能等待主端来读这个数据,没有办法来实现pl端主动去写。所以在这里我们就要用要axi的Master模式,从下面这个图片可以看到有7个axi总线可以去读写ddr,2个低速,和4个高速总线,我们这次使用高速总线。
1.配置zynq的外设:这个就不用讲了,网上一搜索一堆。
2.生成一个带axi master的例程ip核。
3. 对于生成的实例代码,其实是向0x xxxx_xxxx地址连续写入4KB的数据,然后在读出来,当然这个实例不满足我们的要求,我们显示的大小是640*480,60Hz,而且在编写代码时我偷了懒,每32bit才传一个像素值(颜色是16bit的),那么我一秒钟要写入的数据量就是 640*480*4*60KB=70.3125兆字节的数据,其实我可以优化到只占一半的带宽,但是我有点懒。
下面对实例进行改造,首先看如下几点:
一次事务传输的大小和长度,这些参数都可以在gui界面中配置,暂时不用管,要知道一次传输的长度最大只有256个字节。
// Base address of targeted slave parameter C_M_TARGET_SLAVE_BASE_ADDR = 32'h40000000, // Burst Length. Supports 1, 2, 4, 8, 16, 32, 64, 128, 256 burst lengths parameter integer C_M_AXI_BURST_LEN = 16,
要传输的事务次数,第二个参数被我们改过,原来是
C_MASTER_LENGTH-clogb2((C_M_AXI_BURST_LEN*C_M_AXI_DATA_WIDTH/8)-1);传输的次数就是 [2-1:0] 所占的大小的2次方,也就是4次。从clogb2这个函数就是取以2为底的对数。
// C_TRANSACTIONS_NUM is the width of the index counter for // number of write or read transaction. localparam integer C_TRANSACTIONS_NUM = clogb2(C_M_AXI_BURST_LEN-1); // Burst length for transactions, in C_M_AXI_DATA_WIDTHs. // Non-2^n lengths will eventually cause bursts across 4K address boundaries. // localparam integer C_MASTER_LENGTH = 12; // total number of burst transfers is master length divided by burst length and burst size localparam integer C_NO_BURSTS_REQ = 2;//C_MASTER_LENGTH-clogb2((C_M_AXI_BURST_LEN*C_M_AXI_DATA_WIDTH/8)-1);
这个例子的读写其实是一个3态的状态机实现的,从下面两个地方可以看出来,后面我把写的部分给干掉了,因为我只需要读数据:
代码有点长,粘过来了:
always @ ( posedge M_AXI_ACLK) begin if (M_AXI_ARESETN == 1'b0 ) begin // reset condition // All the signals are assigned default values under reset condition mst_exec_state <= IDLE; start_single_burst_write <= 1'b0; start_single_burst_read <= 1'b0; compare_done <= 1'b0; ERROR <= 1'b0; end else begin // state transition case (mst_exec_state) IDLE: // This state is responsible to wait for user defined C_M_START_COUNT // number of clock cycles. if ( init_txn_pulse == 1'b1) begin mst_exec_state <= INIT_READ; ERROR <= 1'b0; compare_done <= 1'b0; end else begin mst_exec_state <= IDLE; end // INIT_WRITE: // // This state is responsible to issue start_single_write pulse to // // initiate a write transaction. Write transactions will be // // issued until burst_write_active signal is asserted. // // write controller // if (writes_done) // begin // mst_exec_state <= INIT_READ;// // end // else // begin // mst_exec_state <= INIT_WRITE; // if (~axi_awvalid && ~start_single_burst_write && ~burst_write_active) // begin // start_single_burst_write <= 1'b1; // end // else // begin // start_single_burst_write <= 1'b0; //Negate to generate a pulse // end // end INIT_READ: // This state is responsible to issue start_single_read pulse to // initiate a read transaction. Read transactions will be // issued until burst_read_active signal is asserted. // read controller if (reads_done) begin mst_exec_state <= IDLE; end else begin mst_exec_state <= INIT_READ; if (~axi_arvalid && ~burst_read_active && ~start_single_burst_read) begin start_single_burst_read <= 1'b1; end else begin start_single_burst_read <= 1'b0; //Negate to generate a pulse end end INIT_COMPARE: // This state is responsible to issue the state of comparison // of written data with the read data. If no error flags are set, // compare_done signal will be asseted to indicate success. //if (~error_reg) begin ERROR <= error_reg; mst_exec_state <= IDLE; compare_done <= 1'b1; end default : begin mst_exec_state <= IDLE; end endcase end end //MASTER_EXECUTION_PROC
读地址的地方我也进行了修改,实例中的地址是读到4096个字节就自动复位了,而我们需要读到0x12C000才能复位,这里的复位信号用的是vga显示的场同步来复位的,刚我满足要求。
// Next address after ARREADY indicates previous address acceptance //指向要读的地址,可以通过改变这里来改变要读的地址 always @(posedge M_AXI_ACLK) begin // if (M_AXI_ARESETN == 0 || init_txn_pulse == 1'b1) if (M_AXI_ARESETN == 0 || vga_vs==1'b0) begin axi_araddr <= 'b0; end else if (M_AXI_ARREADY && axi_arvalid) begin axi_araddr <= axi_araddr + burst_size_bytes; end else axi_araddr <= axi_araddr; end
vga实例化的代码如下,增加了许多的数据传输的信号。
vga utt1_vga( clk_25,//25Mhz时钟 M_AXI_ARESETN,//复位 out_color,//输出的颜色 vga_hs,//行同步 vga_vs,//场同步 M_AXI_RDATA,//读的数据 M_AXI_ACLK,//读数据的时钟 rnext,//读取下一个数据 init_txn_pulse,//读取脉冲 repeat_one//重复或开始读取 );
vga的代码就不粘了,给大家个思路就好。
仿真得到如下波形,不要问我读缓存中的数据为啥是红色,因为我没有去写过数据,所以是全是X,这不重要,我开的缓存有点大,其实用不了这个大,懒的优化了:
可以看到,地址读取是连续的,取数据也没有冲突,仿真已经满足要求了。
在xilinx sdk中进行图片显示测试,代码如下:
/****************************************************************************** * * 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 "xparameters.h" #include "xgpio.h" #include "img.h" int *axi_addr=(int*)0x10000000; void set_point(int x,int y,long color){ if((x>=640||x<0)||(y>=480||y<0)){ return; } axi_addr[x+y*(640)]=color; } XGpio Gpio; /* The Instance of the GPIO Driver */ void delay(int n){ int i,j; for(i=0;i<n;i++){ for(j=0;j<2048;j++){ } } } int main() { int Status; init_platform(); cleanup_platform(); /* Initialize the GPIO driver */ Status = XGpio_Initialize(&Gpio, XPAR_GPIO_0_DEVICE_ID); if (Status != XST_SUCCESS) { return XST_FAILURE; } /* Set the direction for all signals as inputs except the LED output */ XGpio_SetDataDirection(&Gpio, 1, 0); XGpio_DiscreteWrite(&Gpio,1,0x1);//开始传送数据 delay(10); XGpio_DiscreteWrite(&Gpio,1,0x0); int i,j; for(i=0;i<640*480;i++) axi_addr[i]=0xffff; int x_y=0; // while(1){ for(i=0;i<480;i++){ for(j=0;j<640;j++){ unsigned short temp=((unsigned short*)gImage_img)[j+i*640]; temp=(temp>>11)|((temp&0x1f)<<11)|(temp&0x7e0); set_point((x_y%640)+j,i,temp); } } // x_y++; // } return 0; }点亮屏幕得到显示完美(其实我已经失败了很多次才完美的):
本来是张美女照片的,我们寝室的拿刀逼我让我换成风景照,没办法他们人多。
有问题,联系小号:549654313,大号:不告诉你