千兆以太网的双向图像传输和显示系统(3)

  1. 实现目标:上位机通过串口发送图片到FPGA中,FPGA接收图片并保存在DDR2中,保存在 DDR2中的图片分为两路,一路经过VGA时序输出到RGB触摸屏上,另一路按照以太网帧格式进 行编码, 将以太网帧发送到PC端后,可用上位机软件实时显示视频图像。
  2. 主要工作:1.FPGA串口图像接收。2.DDR2读写。3.千兆以太网发送。4.VGA视频图像显示。
  3. 使用工具:Quartus ii、Modelsim、Wireshark、小梅哥UDP摄像头、串口传图工具。
    三、DDR2控制器设计
    1.DDR2 IP核调用
    根据自己板子上的DDR2型号进行配置,我这里使用的是小梅哥AC6012开发板,所以配置参数按照小梅哥教程配置。
    在这里插入图片描述
    突发长度为8
    在这里插入图片描述
    其他部分保持不变,最终Finish完成。
    DDR2输入输出接口还需要两个FIFO
    WR_FIFO
    在这里插入图片描述
    RD_FIFO
    在这里插入图片描述
    DDR2控制器设计如下:

module DDR2_Control_4Port
(
	//	HOST Side
	REF_CLK,
	RESET_N,
		  
	GLOBAL_RES,
	
	//	FIFO Write Side 1
	WR1_DATA,
	WR1,
	WR1_ADDR,
	WR1_MAX_ADDR,
	WR1_LENGTH,
	WR1_LOAD,
	WR1_CLK,
	WR1_FULL,
	WR1_USE,
	

	//	FIFO Read Side 1
	RD1_DATA,
	RD1,
	RD1_ADDR,
	RD1_MAX_ADDR,
	RD1_LENGTH,
	RD1_LOAD,	
	RD1_CLK,



	//	DDR Side
	mem_odt,
	mem_cs_n,
	mem_cke,
	mem_addr,
	mem_ba,
	mem_ras_n,
	mem_cas_n,
	mem_we_n,
	mem_dm,
	mem_clk,
	mem_clk_n,
	mem_dq,
	mem_dqs,
	
	local_rdata
	
);

`define  ASIZE           	24      	// total address width of the DDR2			
`define	DSIEZE_IN			16			//	data width of the CMOS 
`define	DSIEZE_DDR2			32			//	data width of the DDR2 IP 
`define	RATIO					2			// the data width ratio of `DSIEZE_DDR2/`DSIEZE_IN
//	HOST Side
input                           	REF_CLK;                	//System Clock
input                           	RESET_N;                	//System Reset

output									GLOBAL_RES;
//	FIFO Write Side 1
input [`DSIEZE_IN-1:0]         	WR1_DATA;               	//Data input
input										WR1;								//Write Request
input	[`ASIZE-1:0]					WR1_ADDR;						//Write start address
input	[`ASIZE-1:0]					WR1_MAX_ADDR;					//Write max address
input	[`ASIZE-1:0]					WR1_LENGTH;						//Write length
input										WR1_LOAD;				      //Write register load & fifo clear
input										WR1_CLK;				         //Write fifo clock
output									WR1_FULL;						//Write fifo full
output[`ASIZE-1:0]					WR1_USE;							//Write fifo usedw

//	FIFO Read Side 1
output[`DSIEZE_IN-1:0]        	RD1_DATA;               	//Data output
input										RD1;								//Read Request
input	[`ASIZE-1:0]					RD1_ADDR;						//Read start address
input	[`ASIZE-1:0]					RD1_MAX_ADDR;					//Read max address
input	[`ASIZE-1:0]					RD1_LENGTH;						//Read length
input										RD1_LOAD;						//Read register load & fifo clear
input										RD1_CLK;							//Read fifo clock


output[0:0]								mem_odt;
output[0:0]								mem_cs_n;
output[0:0]								mem_cke;
output[12:0]							mem_addr;
output[2:0]								mem_ba;
output									mem_ras_n;
output									mem_cas_n;
output									mem_we_n;
output[1:0]								mem_dm;
inout	[0:0]								mem_clk;
inout	[0:0]								mem_clk_n;
inout	[15:0]							mem_dq;
inout	[1:0]								mem_dqs;

//	Internal Registers/Wires
//	Controller
reg	[`ASIZE-1:0]					mADDR;							//Internal address
reg	[`ASIZE-1:0]					mLENGTH;							//Internal length
reg	[`ASIZE-1:0]					rWR1_ADDR;						//Register write address				
reg	[`ASIZE-1:0]					rWR1_MAX_ADDR;					//Register max write address				
reg	[`ASIZE-1:0]					rWR1_LENGTH;					//Register write length
reg	[`ASIZE-1:0]					rRD1_ADDR;						//Register read address
reg	[`ASIZE-1:0]					rRD1_MAX_ADDR;					//Register max read address
reg	[`ASIZE-1:0]					rRD1_LENGTH;					//Register read length
reg	[1:0]								WR_MASK;							//Write port active mask
reg	[1:0]								RD_MASK;							//Read port active mask
reg										mWR_DONE;						//Flag write done, 1 pulse SDR_CLK
reg										mRD_DONE;						//Flag read done, 1 pulse SDR_CLK
reg										mWR;								//Internal WR edge capture
reg										mRD;								//Internal RD edge capture

wire  [`DSIEZE_DDR2-1:0]     	 	WR_DATA1;                	//Controller Data input 1
wire  [`DSIEZE_DDR2-1:0]      	WR_DATA2;                	//Controller Data input 2

//	FIFO Control
wire	[`ASIZE-1:0]					write_side_fifo_rusedw1;
wire	[`ASIZE-1:0]					read_side_fifo_wusedw1;
wire	[`ASIZE-1:0]					write_side_fifo_rusedw2;
wire	[`ASIZE-1:0]					read_side_fifo_wusedw2;
//	DRAM Internal Control

wire                            	CLK;

wire	[3:0]								local_size;	
wire										local_ready;
wire	[3:0]								local_be;
reg	[`DSIEZE_DDR2-1:0]			local_wdata;
output wire	[`DSIEZE_DDR2-1:0]			local_rdata;
reg	[`ASIZE:0]						local_address;
reg										local_read_req;
reg										local_write_req;
reg										local_burstbegin;
wire										local_init_done;
wire										local_rdata_valid;

assign	local_be		= 4'hF;//写入字节使能,每一位对应一个字节
assign	local_size 	= 4'h8;//用户接口的突发长度,指定每次用户发起一次写入请求会写入多个数据
assign	GLOBAL_RES 	= local_init_done;
//DDR2 IP核
DDR2		I_DDR2
(
	.pll_ref_clk			(REF_CLK),
	.phy_clk					(CLK),
	
	.soft_reset_n			(1'b1),
	.global_reset_n		(RESET_N),

	.local_address			(local_address),	  //数据写入的DDR2存储器的地址{CS,ROW,BA,COL}
	.local_write_req		(local_write_req),  //写数据请求信号为1则local_wdata端口上的数据允许被写入ddr2控制器
	.local_wdata			(local_wdata),	     //写入ddr2中的数据
	.local_read_req		(local_read_req),   //读请求信号
	.local_rdata			(local_rdata),	     //ddr2中读出的数据
	.local_burstbegin		(local_burstbegin), //用户接口的突发启动信号,该信号每一个高脉冲会启动一次突发传输,每次传输长度为local_size指定的长度
 
	.local_be				(local_be),			  //写入字节使能,每一位对应一个字节
	.local_size				(local_size),       //用户接口的突发长度,指定每次用户发起一次写入请求会写入多个数据,最大不超过IP核配置中的最大长度
	.local_ready			(local_ready),      //ddr控制器就绪标志信号,该信号有效则表明当前数据被成功写入了ddr2控制器中,为0则表示ddr控制器无法接受当前操作,用户需要保持当前地址、数据和控制信号不变直到ready信号再次拉高
	.local_rdata_valid	(local_rdata_valid),//读取数据有效标志信号,当该信号有效时,表明local_rdata上的数据是读到的有效内容
	.local_init_done		(local_init_done),  //ddr2控制器初始化完成标志信号

	.mem_odt					(mem_odt),          //DDR2片上终结信号
	.mem_cs_n				(mem_cs_n),			  //DDR2片选信号
	.mem_cke					(mem_cke),			  //DDR2时钟使能信号
	.mem_addr				(mem_addr),			  //DDR2地址总线
	.mem_ba					(mem_ba),			  //DDR2BANK信号
	.mem_ras_n				(mem_ras_n),		  //DDR2行地址选择信号
	.mem_cas_n				(mem_cas_n),		  //DDR2列地址选择信号
	.mem_we_n				(mem_we_n),			  //DDR2写使能信号
	.mem_dm					(mem_dm),		     //DDR2数据掩膜信号
	.mem_clk					(mem_clk),			  //DDR2时钟信号
	.mem_clk_n				(mem_clk_n),		  //DDR2时钟反相信号
	.mem_dq					(mem_dq),			  //DDR2数据总线
	.mem_dqs					(mem_dqs)			  //DDR2数据源同步信号
);

reg		WR_FIFO_EN;
//写FIFO 16转32位,show-ahead模式
WR_FIFO 	WR_FIFO_1
(
	.data						(WR1_DATA),
	.wrreq					(WR1),
	.wrclk					(WR1_CLK),
	.aclr						((!local_init_done) || WR1_LOAD),
	.rdreq					(WR_FIFO_EN & WR_MASK[0]),
	.rdclk					(CLK),
	.q							(WR_DATA1),
	.wrfull					(WR1_FULL),
	.wrusedw					(WR1_USE),
	.rdusedw					(write_side_fifo_rusedw1)
);


//读FIFO 32转16位,show-ahead模式
RD_FIFO 	RD_FIFO_1
(
	.data						(local_rdata),
	.wrreq					(local_rdata_valid & RD_MASK[0]),
	.wrclk					(CLK),
	.aclr						((!local_init_done) || RD1_LOAD),
	.rdreq					(RD1),
	.rdclk					(RD1_CLK),
	.q							(RD1_DATA),
	.wrusedw					(read_side_fifo_wusedw1)
);
				

//读写控制器
reg	[9:0]		Write_NUM;
reg	[9:0]		Read_NUM;			

reg	[3:0]		WR_Step;
reg	[3:0]		RD_Step;

reg	[7:0]		WR_Time;
reg	[7:0]		RD_Time;
	
always@(posedge CLK or negedge local_init_done)
begin
	if(!local_init_done)
	begin
		mWR_DONE				<=	0;
		mRD_DONE				<=	0;
		local_write_req	<= 0;
		local_read_req 	<= 0;	
		local_burstbegin 	<= 0;
		local_address		<= 0;
		local_wdata			<= 0;
		Write_NUM 			<= 0;
		Read_NUM	 			<= 0;
		WR_FIFO_EN			<= 0;	
		WR_Step				<= 0;
		RD_Step				<= 0;	
	end
	else
	begin
		case(WR_MASK)
			2'b01	:
						local_wdata <= WR_DATA1;
			2'b10	:
						local_wdata <= WR_DATA2;
			default:
					local_wdata <= 0;
		endcase
	
		if(mWR)//写判断
		begin
			if(local_ready)//ddr控制器就绪标志信号,只有该标志为1才能写入
			begin
				case(WR_Step)
					4'd0: //先初始化
						begin
							local_write_req <= 0; local_burstbegin <= 0; Write_NUM <= 0; WR_Time <= 0; WR_FIFO_EN <= 0; WR_Step  <= 4'd1;	
						end
					4'd1://写FIFO模块的读侧使能
						begin
							WR_FIFO_EN <= 1; WR_Step  <= 4'd2;	
						end										
					4'd2://发送写请求,突发开始,写地址赋值 	
						begin
							local_write_req <= 1; local_burstbegin <= 1; local_address<= mADDR + WR_Time * local_size; WR_FIFO_EN <= 1;
								
							WR_Step <= 4'd3;
						end
					4'd3://写数据:每次突发连续写8次,一共写WR_Time次
						begin	
							local_burstbegin <= 1'b0;
							
							if(Write_NUM >= local_size - 2)
								WR_FIFO_EN <= 0;
							else
								WR_FIFO_EN <= 1;
							
							if(Write_NUM < local_size - 1)
							begin
								Write_NUM <= Write_NUM + 1'b1; WR_Step <= 4'd3;								
							end
							else
							begin
								Write_NUM <= 0; local_write_req <= 0;
								
								if(WR_Time < mLENGTH/local_size -1)      //判断写到第几个批次的突发写周期
								begin
									WR_Time <= WR_Time + 8'd1; WR_Step <= 4'd1; 
								end
								else
								begin
									mWR_DONE <= 1; WR_Step <= 4'd0;      //发送写完成标志,写状态回到初始态
								end			
							end	
						end		
					default:
						WR_Step <= 4'd0;	
				endcase	
			end
			else
			begin
				local_write_req <= 0; WR_FIFO_EN <= 0;
			end
		end
		else	
		begin
			mWR_DONE <= 0; local_write_req <= 0; local_burstbegin <= 0; Write_NUM <= 0; WR_Time <= 0; WR_FIFO_EN <= 0; WR_Step <= 4'd0; 
		end

		if(mRD)//读判断
		begin
			case(RD_Step)
				4'd0: //初始化
					begin
						local_burstbegin <= 0; local_read_req <= 0; Read_NUM	<= 0; RD_Time <= 0; RD_Step <= 4'd1;						
					end
				4'd1: //发送读请求,突发开始,读地址赋值 	
					begin
						local_read_req <= 1; local_burstbegin <= 1'b1; Read_NUM <= 0; local_address	<= mADDR + RD_Time * local_size ; RD_Step <= 4'd2;	
					end	
				4'd2:
					begin	
						if(local_ready)//ddr控制器就绪标志信号,只有该标志为1才能读
						begin
							local_burstbegin <= 1'b0; local_read_req <= 0;	
								
							if(local_rdata_valid)//读取数据有效标志信号,当该信号有效时,表明local_rdata上的数据是读到的有效内容。读数据:每次突发连续读8次,一共写RD_Time次
							begin
									if(Read_NUM < local_size -1)
									begin
										Read_NUM <= Read_NUM + 1'b1; RD_Step <= 4'd2;	
									end	
									else
									begin
										Read_NUM <= 0;
										
										if(RD_Time < mLENGTH/local_size -1)
										begin
											RD_Time <= RD_Time + 8'd1; RD_Step <= 4'd1;
										end	
										else
										begin
											RD_Time	<= 0; mRD_DONE <= 1; RD_Step <= 4'd0;	
										end															
									end
							end		
						end
						else
							RD_Step <= 4'd2;	
					end
				default:
					RD_Step <= 4'd0;	
			endcase				
		end
		else
		begin
			mRD_DONE <= 0; local_burstbegin <= 0; local_read_req <= 0; Read_NUM	<= 0; RD_Time <= 0; RD_Step <= 4'd0;	
		end
	end
end
	
//	Internal Address & Length Control
always@(posedge CLK or negedge local_init_done)
begin
	if(!local_init_done)//赋初值
	begin
		rWR1_ADDR		<=	WR1_ADDR/`RATIO;
		rWR1_MAX_ADDR	<=	WR1_MAX_ADDR;
		rRD1_ADDR		<=	RD1_ADDR/`RATIO;
		rRD1_MAX_ADDR	<=	RD1_MAX_ADDR;
		rWR1_LENGTH		<=	WR1_LENGTH;
		rRD1_LENGTH		<=	RD1_LENGTH;
	end
	else
	begin
		//	Write Side 1
		if(WR1_LOAD)//加载写入ddr2的地址和写入的长度
		begin
			rWR1_ADDR	<=	WR1_ADDR/`RATIO;
			rWR1_LENGTH	<=	WR1_LENGTH;
		end
		else if(mWR_DONE&WR_MASK[0])//每次写完rWR1_LENGTH长度后更新写地址+rWR1_LENGTH
		begin
			if(rWR1_ADDR<rWR1_MAX_ADDR/`RATIO-rWR1_LENGTH)
			rWR1_ADDR	<=	rWR1_ADDR + rWR1_LENGTH;
			else
			rWR1_ADDR	<=	WR1_ADDR/`RATIO;//写完一张图片以后写地址归零
		end
		//	Read Side 1
		if(RD1_LOAD)//加载读ddr2的地址和读的长度
		begin
			rRD1_ADDR	<=	RD1_ADDR/`RATIO;
			rRD1_LENGTH	<=	RD1_LENGTH;
		end
		else if(mRD_DONE&RD_MASK[0])//每次读完rRD1_LENGTH长度后更新写地址+rRD1_LENGTH
		begin
			if(rRD1_ADDR<rRD1_MAX_ADDR/`RATIO-rRD1_LENGTH)
			rRD1_ADDR	<=	rRD1_ADDR+rRD1_LENGTH;
			else
			rRD1_ADDR	<=	RD1_ADDR/`RATIO;
		end
	end
end

//****************Global controller**************************//
/*
	

*/
always@(posedge CLK or negedge local_init_done)
begin
	if(!local_init_done)
	begin
		mWR		<=	0;
		mRD		<=	0;
		mADDR		<=	0;
		mLENGTH	<=	0;
		WR_MASK	<=	2'b00;
		RD_MASK	<=	2'b00;
	end
	else
	begin
		if( (mWR==0) && (mRD==0) &&
			(WR_MASK==2'b0)	&&	(RD_MASK==2'b0) &&
			(WR1_LOAD==1'b0)	&&	(RD1_LOAD==1'b0) 
			 )
		begin
		
		//	Write Side 1  当写FIFO中的数据超过rWR1_LENGTH时,写FIFO向DDR2中写入数据
			 if( (write_side_fifo_rusedw1 >= rWR1_LENGTH) && (rWR1_LENGTH!=0) )
			begin
				mADDR		<=	rWR1_ADDR;
				mLENGTH	<=	rWR1_LENGTH;
				WR_MASK	<=	2'b01;
				RD_MASK	<=	2'b00;
				mWR		<=	1;
				mRD		<=	0;
			end
		//	Read Side 1  当读FIFO中的数据少于rRD1_LENGTH时,读FIFO读取DDR2中的数据
			else if( (read_side_fifo_wusedw1 < rRD1_LENGTH) )
			begin
				mADDR	<=	rRD1_ADDR;
				mLENGTH	<=	rRD1_LENGTH;
				WR_MASK	<=	2'b00;
				RD_MASK	<=	2'b01;
				mWR		<=	0;
				mRD		<=	1;				
			end
		end
		if(mWR_DONE)
		begin
			WR_MASK	<=	0;
			mWR		<=	0;
		end
		if(mRD_DONE)
		begin
			RD_MASK	<=	0;
			mRD		<=	0;
		end
	end
end

endmodule

参考资料:(1)小梅哥DDR2简明教程V1.1
(2)Altera DDR2控制器学习笔记
(3)咸鱼FPGA博客

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值