其实通过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,大号:不告诉你