ZYNQ 图像处理之千兆网传(一)【寄存器级操作】

 

 

 最近在做图像处理和加速,芯片是xilinx 的 zynq xc7z020。需要用到千兆网,由于网上很多都是直接移植lwip,但是个人感觉用着不爽(个人感觉),不能实现随心所欲的控制,代码量又多,所以自己研究了一下,直接操作寄存器,实现千兆网通信。以下是个人的理解及操作,仅供大家参考。

要实现千兆网,在xc7z020需要做到以下几点:

目录

要实现千兆网,在xc7z020需要做到以下几点:

一 配置网络寄存器

二 配置PYH 芯片

三 配置中断寄存器(如果要用中断的话)

四 创建Descriptors

以下效果图,可以达到970多Mpbs的速度


一 配置网络寄存器

在配置网络寄存器之前,先对控制器进行复位操作,代码如下

//Initialize the Controller
void Initialize_Controller()
{
	//1. Clear the Network Control register. Write 0x0 to gem.net_ctrl register.
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_NWCTRL_OFFSET,0x00000000);

	//2. Clear the Statistics registers. Write a 1 to gem.net_ctrl[clear_stat_regs].
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_NWCTRL_OFFSET,XEMACPS_NWCTRL_STATCLR_MASK);

	//3. Clear the Status registers. Write a 1 to the Status registers. gem.rx_status = 0x0F andgem.tx_status = 0xFF.
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_RXSR_OFFSET,0x0f);
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_TXSR_OFFSET,0xff);

	//4. Disable all interrupts. Write 0x7FF_FEFF to the gem.intr_dis register.
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_IDR_OFFSET,0x7FFFEFF);

	//5. Clear the buffer queues. Write 0x0 to the gem.rx_qbar and gem.tx_qbar registers.
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR  + XEMACPS_RXQBASE_OFFSET,0x00000000);
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_TXQBASE_OFFSET,0x00000000);

}

接下来 寄存器配置

//Configure the Controller
void Configure_Controller()
{
	unsigned int cfg_value;
	unsigned int Divisor  = 0x07;//<< XEMACPS_NWCFG_MDC_SHIFT_MASK
//1. Program the Network Configuration register (gem.net_cfg)
	cfg_value = XEMACPS_NWCFG_FDEN_MASK
			| XEMACPS_NWCFG_1000_MASK
			| XEMACPS_NWCFG_LENERRDSCRD_MASK
			| XEMACPS_NWCFG_RXCHKSUMEN_MASK
			| XEMACPS_NWCFG_PAUSEEN_MASK
			| XEMACPS_NWCFG_FCSREM_MASK
			| XEMACPS_NWCFG_UCASTHASHEN_MASK
			| (Divisor << XEMACPS_NWCFG_MDC_SHIFT_MASK);


	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_NWCFG_OFFSET,cfg_value);
	xil_printf("cfg_value = %x \r\n",cfg_value);

//2. Set the MAC address
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR+XEMACPS_LADDR1L_OFFSET,LOCAL_MAC0);
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR+XEMACPS_LADDR1H_OFFSET,LOCAL_MAC1);

	xil_printf("LADDR1L = %x \r\n",Xil_In32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_LADDR1L_OFFSET));
	xil_printf("LADDR1H = %x \r\n",Xil_In32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_LADDR1H_OFFSET));

//3. Program the DMA Configuration register
	cfg_value = XEMACPS_DMACR_RXBUF_MASK
			| XEMACPS_DMACR_RXSIZE_MASK
			| XEMACPS_DMACR_TXSIZE_MASK
			| XEMACPS_DMACR_DISC_WHEN_NO_AHB
			| XEMACPS_DMACR_TCPCKSUM_MASK
			| XEMACPS_DMACR_ENDIAN_MASK
			| XEMACPS_DMACR_INCR16_AHB_BURST;


	xil_printf("cfg_value_dma = %x \r\n",cfg_value);
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_DMACR_OFFSET,cfg_value);

//4. Program the Network Control Register (gem.net_ctrl)
	cfg_value = Xil_In32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_NWCTRL_OFFSET);
	cfg_value = cfg_value
			| XEMACPS_NWCTRL_MDEN_MASK;
			//+ XEMACPS_NWCTRL_TXEN_MASK
			//+ XEMACPS_NWCTRL_RXEN_MASK;
   Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_NWCTRL_OFFSET,cfg_value);


}

中断寄存器配置

void Configure_Interrupts()
{
	//Enable interrupts
	/* Enable TX and RX interrupts */

	u32 mask = XEMACPS_IXR_TX_ERR_MASK
			|XEMACPS_IXR_RX_ERR_MASK
			|XEMACPS_IXR_FRAMERX_MASK
			|XEMACPS_IXR_TXCOMPL_MASK;

	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_IER_OFFSET,mask);


}

 

二 配置PYH 芯片

PYH 芯片配置,需要根据芯片的手册,我用的是KSZ9031RNX-EVA,因为代码比较多,这里的代码就不贴了,可以根据你使用的芯片型号,在网上找对应的配置程序。

 

三 配置中断寄存器(如果需要用中断的话)

中断对于嵌入式来说,是一个比较复杂的梗,首先要开启系统级中断(PS中断系统),其次要使能个体中断(eth0中断),这里把代码贴出来,如果用需要用到的朋友可以作为简单的参考。

系统级中断控制代码(PS中断系统)

//定义中断向量表结构体,Handler为函数,Data为函数Handler的参数
typedef struct {
    Xil_ExceptionHandler Handler;
    void *Data;
} XExc_VectorTableEntry;
//申明中断向量表
extern XExc_VectorTableEntry XExc_VectorTable[];
// 复位中断
void IntAllDisabled(void)
{
	/* Remove current CPU from interrupt target register */

	Xil_Out32(ICDICER0, 0xffffffff);//不使能中断
	Xil_Out32(ICDICER1, 0xffffffff);//不使能中断
	Xil_Out32(ICDICER2, 0xffffffff);//不使能中断
	//disables the distributor
	Xil_Out32(ICDDCR,0x00);

}
//分发中断
void DistributorInit(void)
{
	u32 reg_addr;
	reg_addr = ICDICFR0;
	u32	default_priority = 0xa0a0a0a0;

	//清除触发模式
	for(char i=0;i<6;i++)
	{
		reg_addr += i*4;
		Xil_Out32(reg_addr,0x00000000);

	}



	// 设置default 优先级
	reg_addr = ICDIPR0;
	for(char i=0;i<24;i++)
	{
		reg_addr += i*4;
		Xil_Out32(reg_addr,default_priority);

	}

	// enables the distributor
	Xil_Out32(ICDDCR,0x01);

}

// CPU 中断设置
void CPU_Init(void)
{
    //中断优先级都是A0,优先级高于F0,CPU可接受这些中断
    Xil_Out32(ICCPMR,0xF0);
    //处理器能接收IRQ,使能中断信号连接到处理器
    Xil_Out32(ICCICR,0x07);
}

个体中断使能(eth0中断)

void Ethernet0_Int_Init(void)
{



    Xil_Out32(ICDICER1, 0x400000);//不使能 #54


    Xil_Out32(ICDICFR3, 0x1000);//电平触发
    Xil_Out32(ICDIPR13, 0xA00000);//优先级A0
    Xil_Out32(ICDIPTR13,0x010000);//处理器为CPU1

    Xil_Out32(ICDISER1, 0x400000);//使能 #54

}

初始化中断的代码

 void Int_init(void)
{
	 u32 data = 30;
    Xil_ExceptionInit();
    XExc_VectorTable[5].Handler =(Xil_ExceptionHandler)InterruptHandler_IRQ;// 中断处理函数
    XExc_VectorTable[5].Data = data;

    //initialize
    IntAllDisabled();
    DistributorInit();
    CPU_Init();
    Ethernet0_Int_Init();


    xil_printf("begin\r\n");
    Xil_ExceptionEnable();

}

 

四 创建Descriptors

要实现千兆网的收发需要一个叫 Buffer Descriptors List 的东西,在这里暂且把它叫做“缓存描述表”,收发各需要一个。这里只单独讨论发的时候,其实“缓存描述表”可以理解为结构体(本人是按结构体来申请和定义),这个结构体里面包含多个Buffer Descriptors(简称BD),每个BD包含两个word(word0 和 word1, 一个word = 4bytes)。word0 里存放了一个地址,指向需要发送数据的起始地址。word1是一些状态位。对于更详细的描述,可以看 《ug585-Zynq-7000-TRM(Technical Reference Manual).pdf》手册p495的 Rx Buffer描述。

以下是代码

// Descriptor strcut
struct DESC_DEF
{
	u32 word0;
	u32 word1;
};


// Descriptor List strcut
struct DESC_LIST_DEF
{
	struct DESC_DEF Descriptor[32];
};


struct DESC_LIST_DEF Tx_List __attribute__ ((aligned(64)));

void BdBaseAddr(struct DESC_LIST_DEF *p_List)
{


	p_List->Descriptor[0].word0 = (u32)p_header->eth_remote_mac;// 以太网数据报头
	p_List->Descriptor[1].word0 = eth0_ctrl.frame_addr[0];// 需要发送的数据的首地址
	//write gem.tx_qbar register
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_TXQBASE_OFFSET,(u32)Tx_List.Descriptor);
	Xil_DCacheFlushRange((UINTPTR)(u32)p_header->eth_remote_mac, sizeof(struct HEAD_DEF));


}


void BdSetLength(struct DESC_LIST_DEF *p_List)
{


	p_List->Descriptor[0].word1 = sizeof(struct HEAD_DEF);//p_header->ip_IHL*5 + 14 + 8;
	p_List->Descriptor[1].word1 = PAYLOAD_BYTES;


}


void BdClearTxUsed(struct DESC_LIST_DEF *p_List)
{

	p_List->Descriptor[0].word1 &=(~XEMACPS_TXBUF_USED_MASK);
	p_List->Descriptor[1].word1 &=(~XEMACPS_TXBUF_USED_MASK);

}



void BdSetLast(struct DESC_LIST_DEF *p_List)
{
	p_List->Descriptor[1].word1 = p_List->Descriptor[1].word1 | XEMACPS_TXBUF_LAST_MASK |XEMACPS_TXBUF_WRAP_MASK;
}

 

 

到这里,初始化阶段几乎算是完成了,只是以太网数据报头这些需要自己去组装,这里涉及到以太网的知识,知识点比较多就不一一表述了,大家可以网上查

 

整体的初始化代码

void eth0_init()
{
	Initialize_Controller();
	Configure_Controller();
	init_phy();

	Configure_Interrupts();
	BdBaseAddr(&Tx_List);
	BdSetLength(&Tx_List);
	BdSetLast(&Tx_List);


}

 

接下来是发送代码

void Enable_Controller()
{

	u32 value;

	//1. Enable the Transmitter. Write a 1 to gem.net_ctrl[tx_en]
	//2. Enable the Receiver. Write a 1 to gem.net_ctrl[rx_en].

	value = Xil_In32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_NWCTRL_OFFSET);

	value = value | XEMACPS_NWCTRL_TXEN_MASK | XEMACPS_NWCTRL_RXEN_MASK;
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_NWCTRL_OFFSET,value);
}

void start_trasnission()
{
	u32 value;

	value = Xil_In32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_NWCTRL_OFFSET);
	Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_NWCTRL_OFFSET,value |XEMACPS_NWCTRL_STARTTX_MASK);

}


// 发送一帧图像

void Trans_Frames()
{


	u32 txsr;
	//u32 *p_dptr;


	txsr  = Xil_In32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_TXSR_OFFSET);
	txsr &= XEMACPS_TXSR_TXCOMPL_MASK;
	if(eth0_ctrl.flg | txsr)
	{
        
		eth0_ctrl.flg  = 0;

		// clear trans compelet bit
		Xil_Out32(XEMACPS_GEM0_BAYSE_ADDR + XEMACPS_TXSR_OFFSET,(txsr & XEMACPS_TXSR_TXCOMPL_MASK));
		BdClearTxUsed(&Tx_List);

		u32 value = sizeof(struct DESC_LIST_DEF);
		Xil_DCacheFlushRange((UINTPTR)Tx_List.Descriptor, value);

		Enable_Controller();
		start_trasnission();



	}


}

由于篇幅,里面还有很多知识点没有写,比如以太网数据结构、网络字节序、crc验证等,有需要的朋友可以到网上查询。将来也许我会把这些知识点作为一个系列写出来,不过这是以后的事了。

今天这个只是实现从相机发送数据到PC端,至于从PC端发到相机,留在下一节分享。

由于个人不太爱写注释(很坏很坏的习惯),以上代码注释比较少,可能阅读起来不是很爽,不过好在代码量不是很多,如果此代码有用得上的朋友,若有疑问的地方可以留言,我会尽量回复。

以下效果图,可以达到970多Mpbs的速度

ZYNQ 图像处理之千兆网传(一)【寄存器级操作】_第1张图片

 

因能力所限,难免有理解不到位之处,欢迎大家批评指正

更多分享,请关注微信公众号:FPGA历险记

ZYNQ 图像处理之千兆网传(一)【寄存器级操作】_第2张图片

 

你可能感兴趣的:(ZYNQ图像处理)