1. AVALON总线分类

在QSYS下,一个简单的组件包含了许多接口,它们实现了不同的功能,大致有下面几种:

Avalon-MM

Avalon-ST

Avalon Conduit

Avalon-TC

Avalon Interrupt

Avalon Clock.

这些标准是开源的,不需要license就可以开发使用。下图是一个总线应用的示意图:

NIOS2随笔——自定义IP(DPRAM)_第1张图片


2. AVALON MM总线

这里以AVALON MM为例,设计一个用户自定义的DPRAM组件,并用DMA方式将DPRAM PORTB中的数据搬移到内存SDRAM/DDR2中。下面接口定义是AVALON MM SLAVE总线的信号,具体时序在Avalon Interface Specifications中有详细描述。

//avalon MM slave
input avs_clk,
input avs_reset_n,
input avs_chipselect_n,
input [7:0] avs_address,
input [3:0] avs_byteenable,
input avs_write,
input [31 :0] avs_writedata,
input avs_read,
output avs_readdatavalid,
output [31 :0] avs_readdata


3. Qsys中添加自定义组件

1) 新建组件

NIOS2随笔——自定义IP(DPRAM)_第2张图片

2)添加文件

NIOS2随笔——自定义IP(DPRAM)_第3张图片

3) 信号设置

NIOS2随笔——自定义IP(DPRAM)_第4张图片

4) 接口时序


4. FPGA逻辑代码

下面是用户自定义的DPRAM的Verilog代码:

//file name:		vid_dpram.v
//author:			shugen.yin
//date:				2016.8.15
//function:			user dpram for Qsys
//log:	

module vid_dpram(
	//user port
	input inclock,
	input [7:0] data,
	input data_valid,
	input vid_vsync,
	
	//avalon MM slave
	input avs_clk,
	input avs_reset_n,
	input avs_chipselect_n,
	input [7:0] avs_address,
	input [3:0] avs_byteenable,
	input avs_write,
	input [31 :0] avs_writedata,
	input avs_read,
	output avs_readdatavalid,
	output [31 :0] avs_readdata
);

parameter VID_WIDTH = 640;


reg avs_readdatavalid_r0;
reg avs_readdatavalid_r1;

reg [9:0] wraddress_0;
reg [9:0] wraddress_1;

reg rdaddress_high_bit;
reg wraddress_high_bit;

wire [10:0] wraddress;

wire [8:0] rdaddress;
assign rdaddress = (avs_chipselect_n==1'b0)?{1'b0,avs_address[7:0]}:0;
	

wire rden;
assign rden = avs_read & ~avs_chipselect_n;	
	
dpram_01	dpram_01_inst (
	.data ( data ),
	.wrclock ( inclock ),
	.rdclock ( avs_clk ),
	.rden	(rden),
	.rdaddress ( rdaddress ),		//input	[8:0]  rdaddress;
	.wraddress ( wraddress ),		//input	[10:0]  wraddress;
	.wren (1'b0 ),
	.q ( avs_readdata )
	);


endmodule

dpram的PORT B端用HEX文件初始化(511~0循环递减):


5. NIOS2 源代码

#include 
#include 
#include 
#include 
#include "system.h"
#include "alt_types.h"
#include "altera_avalon_dma_regs.h"
#include "sys/alt_dma.h"
#include 


#define DAT_LEN 640

unsigned int buffer0[DAT_LEN/4];
unsigned int *point=VID_DPRAM_0_BASE;

static void DMA_Init(void); //初始化DMA
unsigned int dma_end_flag = 0;

alt_dma_txchan tx;
alt_dma_rxchan rx;

void dma_done()
{
	dma_end_flag++;
}

static void DMA_Init(void)
{
	tx = alt_dma_txchan_open("/dev/dma_0");

	if(tx != NULL)
	{
		printf("DMA transition start\n");
	}

	alt_dma_txchan_ioctl(tx,ALT_DMA_SET_MODE_32,NULL);

	//point是源地址、传输数据块长度是DAT_LEN
	alt_dma_txchan_send(tx, point, DAT_LEN, NULL, NULL);

	rx = alt_dma_rxchan_open("/dev/dma_0");

	alt_dma_rxchan_ioctl(rx,ALT_DMA_SET_MODE_32,NULL);

	//buffer0是目标地址、传输数据块长度是DAT_LEN、dma_done()是DMA完成后被调用的回调函数
	alt_dma_rxchan_prepare(rx, buffer0 , DAT_LEN, dma_done, NULL);
}


int main()
{
	int i;

	for(i=0;i


6. 运行结果