【FPGA开发】SDRAM的读写VGA显示

这篇具有很好参考价值的文章主要介绍了【FPGA开发】SDRAM的读写VGA显示。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 原理及实验目的介绍

1.1 实验目的

        实验的目的旨在使用sdram作为中间媒介,在sdram上读写以在显示器上显示图像的目的。

1.2 实验原理

1.2.1 SDRAM型号介绍

这里我们使用的sdram为Hynix公司生产的型号为HY57V281620F的 芯片,其存储空间可以分为4Bank x 2M x16Bit,其中4Bank指的是该芯片有4个bank区间,2M指的是每个bank2M的存储地址,列地址A0~A8,行地址是 A0~A11,16bit指的是每个地址空间可以存16bit的数据,因此该芯片的总存储容量为128Mbit。

在本次实验中,因为我们不显示视频,我们的实验目的仅是为了显示一张640*480像素的图片,按照像素RGB的显示,每个像素的大小为8bits。所以我们所需要的大小为 640*480 bytes = 307200 bytes。SDRAM一个bank的空间大小为2^21 = 2097152 bytes 也就是32Mb,所以说一个bank就足够容纳这个图像的内容空间。因此我们在这次实验中,只需要对一个bank进行操作。

1.2.2 SDRAM的运行机制 :

SDRAM中使用的电容器来存储数据,但这些电容器不是永久性的存储介质。电容器中的电荷会逐渐泄漏,导致存储的信息消失。为了防止数据丢失,需要定期刷新操作(ref)。sdram和外界只有两个交流的接口

1.  cmd[17:0] : 用于控制sdram的一些操作过程指令

cmd组成 : {Cs_n,Ras_n,Cas_n,We_n,Ba,Addr}

【FPGA开发】SDRAM的读写VGA显示,fpga开发

具体代表的功能如下:

【FPGA开发】SDRAM的读写VGA显示,fpga开发

【FPGA开发】SDRAM的读写VGA显示,fpga开发

2.  dq[15:0] : 用于sdram数据读写的传输通道

2. 代码及功能框架设计:

2.1 功能框架 : 

我们在设计了五个模块分别是 : 

  • sdram 初始化模块 
  • switch sdram状态跳转仲裁模块
  • sdram刷新模块
  • write 写sdram的模块
  • read  读sdram的模块

2.2 代码框架

2.2.1 ini_module

对于sdram来说,在上电之后优先要进行的就是初始化的过程。在完成初始化之后,我们才可以进行后续的刷新和读写。具体的需要在这个过程中加入以下的几个步骤

【FPGA开发】SDRAM的读写VGA显示,fpga开发

比较重要的是对于模式寄存器的配置:(MRS)具体如下:

在本次实验中我们使用突发读和突发写,并且顺行突发,并且单次突发长度为16bits

【FPGA开发】SDRAM的读写VGA显示,fpga开发

具体代码如下:


// -----------------------------------------------------------------------------
// Copyright (c) 2014-2023 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File   : sdram_init.v
// Create : 2023-12-08 14:55:32
// Revise : 2023-12-15 15:07:25
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------


/*----------------------------------------------------------------------------------------------------
sdram初始化
cmd真值表		cs_n	ras_n	cas_n	we_n	ba[1:0]	addr[11]	addr[10],addr[9:0]
	NOP			0		1		1		1		X		X			X		X
	PRLL		0		0		1		0		X		x			1		X
	REF			0		0		0		1		X		X			X		X

MR模式寄存器		{ba[1:0],addr[11:0]}	=	{00,00,000,011,0,010}
		
-----------------------------------------------------------------------------------------------------*/

module	sdram_ini(
			input	wire		i_clk,	//50MHZ
			input	wire		i_rst,
			
			output	reg			o_ini_end_flag,
			output	reg[17:0]	o_ini_cmd
			);
	
	parameter	TRP		=	2,
				TRC		=	4,
				TMRD	=	2;

	parameter	DEY_200US		=	9999,
				PALL_CNT		=	10000,
				REF_ST1			=	10001+TRP,
				REF_ST2			=	10002+TRP+TRC,
				MRD_CNT			=	10003+TRP+TRC+TRC,
				END_INI_CNT		=	10003+TRP+TRC+TRC+TMRD;
			
	parameter	NOP		=	18'h1c000,//18'b01_1100_0000_0000_0000
				PALL	=	18'h08400,//18'b00_1000_0100_0000_0000
				REF		=	18'h04000,//18'b00_0100_0000_0000_0000
				MR		=	18'h00032;//18'b00_0000_0000_0011_0010
			
	
	reg[15:0]	ini_cnt;


	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			ini_cnt <= 'd0;
		end
		else if (ini_cnt == END_INI_CNT)begin
			ini_cnt <= 'd0;
		end
		else if(o_ini_end_flag == 1'b0) begin
			ini_cnt <= ini_cnt + 'd1;
		end
		else begin
			ini_cnt <= ini_cnt;
		end
	end


	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			o_ini_end_flag <= 1'b0;
		end
		else if (ini_cnt >= END_INI_CNT)begin
			o_ini_end_flag <= 1'b1;
		end
		else begin
			o_ini_end_flag <= o_ini_end_flag;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			o_ini_cmd <= NOP;
		end
		else if (ini_cnt <= DEY_200US)begin
			o_ini_cmd <= NOP;
		end
		else if(ini_cnt == PALL_CNT) begin
			o_ini_cmd <= PALL;
		end
		else if(ini_cnt > PALL_CNT && ini_cnt < REF_ST1) begin
			o_ini_cmd <= NOP;
		end
		else if(ini_cnt == REF_ST1) begin
			o_ini_cmd <= REF;
		end
		else if(ini_cnt > REF_ST1 && ini_cnt < REF_ST2) begin
			o_ini_cmd <= NOP;
		end
		else if(ini_cnt == REF_ST2) begin
			o_ini_cmd <= REF;
		end
		else if(ini_cnt > REF_ST2 && ini_cnt < MRD_CNT) begin
			o_ini_cmd <= NOP;
		end
		else if(ini_cnt == MRD_CNT)begin
			o_ini_cmd <= MR;
		end
		else if(ini_cnt > MRD_CNT)begin
			o_ini_cmd <= NOP;
		end
	end


	
endmodule		

2.2.2 sd_main_ctrl 

这个模块是sdram的控制模块,我们在这个模块中,控制sdram的状态机跳转。具体的状态机跳转如下:

【FPGA开发】SDRAM的读写VGA显示,fpga开发

对于这个sdram芯片我们需要每15.625us进行一次刷新,所以我们在读写的过程中如果发生刷新的请求,我们必须中断我们的读写过程来进行刷新的操作。我们需要注意的是我们跳转回仲裁状态机的过程中,有两个key_reg,一个是data_end,一个ref_break_end。data_end,表示数据已经传输完成了,ref_break_end表示中断了进行刷新。

2.2.3 sdram_ref

SDRAM 是动态 RAM 必须要通过刷新才能保证给每一个存储单元充电, 并保证存储单元中的数据不丢失。由于此 SDRAM 芯片要求刷新周期是 ,4096 次刷新 /64ms. 如果按照等间隔去执行刷新的话 64ms/4096=15.625us,我们的时钟频率是50mhz,每个周期的长度为20ns,所以说我们每隔780个时钟周期,我们就需要进行一次刷新。我们用一个计数器周期性的发出刷新请求。

代码如下:


// -----------------------------------------------------------------------------
// Copyright (c) 2014-2024 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File   : sdram_main_ctrl.v
// Create : 2023-12-13 10:04:05
// Revise : 2024-01-04 18:25:28
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------

module sdram_main_ctrl(
		input  wire 				i_clk,
		input  wire 				i_vclk,
		input  wire 				i_rst,
		output wire [17:0] 			o_cmd,
		output wire [15:0] 			io_dq,
		output wire [7:0]		    o_vga,
		output wire 			    o_hsync,
		output wire 			    o_vsync,
		input  wire 				i_wfifo_en,
		input  wire [7:0]			i_wfifo_data

	);

	parameter IDLE  = 6'b000001;
	parameter INI   = 6'b000010;
	parameter SW    = 6'b000100;
	parameter REF   = 6'b001000;
	parameter READ  = 6'b010000;
	parameter WRITE = 6'b100000;

	reg [5:0] state;

	//INI
	wire 		ini_flag;
	wire 		ini_end;
	//REF
	wire 		ref_req;
	wire 		ref_en;
	wire 		ref_end;
	//READ
	wire 		read_data_end;
	wire 		read_ref_break_end;
	wire 		r_en;
	wire        r_req;
	reg [15:0]  r_dq;
	wire 		vga_start;
	wire 		vga_rd_en;
	wire [7:0]	vga_rd_data;
	//WRITE
	wire  		write_data_end;
	wire 		write_ref_break_end;
	wire 		w_req;
	wire [15:0] w_dq;
	// wire 		wfifo_en;
	// wire [7:0]	wfifo_data;
	wire 		w_en;				//写使能状态跳转
	wire 		oe;				//output enable



	wire [17:0] ref_cmd,ini_cmd,w_cmd,r_cmd;

	assign ini_flag = (state == INI) ? 1'b1 : 1'b0;          //此处和课程不一样
	assign ref_en 	= (ref_req && state == SW); 
	assign w_en     = (w_req == 1'b1) && (ref_req == 1'b0) && (state == SW);
	assign r_en     = (w_req == 1'b0  && ref_req == 1'b0 && r_req == 1'b1 && state == SW);

	assign o_cmd[17:14] = ref_cmd[17:14] & ini_cmd[17:14] & w_cmd[17:14] & r_cmd[17:14];
	assign o_cmd[13:0]  = ref_cmd[13:0]  | ini_cmd[13:0]  | w_cmd[13:0] | r_cmd[13:0]; 
	assign oe           = (state == WRITE) ? 1'b1 : 1'b0;
	assign o_dq         = oe ? w_dq : 16'dz;

	always @(posedge i_clk) begin
		r_dq <= io_dq;
	end


	always @(posedge i_clk or posedge i_rst) begin
		if (i_rst == 1'b1) begin
			state <= IDLE;
		end
		else begin case(state)
				IDLE : begin
						state <= INI;
				end
				INI : begin 
					if(ini_end == 1'b1) begin
						state <= SW;
					end	
				end
				SW : begin 
					if(ref_en == 1'b1) begin
						state <= REF;
					end
					else if(w_req == 1'b1 && ref_req == 1'b0) begin
						state <= WRITE;
					end
					else if(r_req == 1'b1 && ref_req == 1'b0 && w_req == 1'b0) begin
						state <= READ;
					end 
				end
				REF : begin
					if(ref_end == 1'b1) begin
						state <= SW;
					end
				end 
				WRITE : begin
					if(write_data_end == 1'b1 || write_ref_break_end == 1'b1) begin
						state <= SW;
					end
				end
				READ : begin
					if(read_data_end == 1'b1 || read_ref_break_end == 1'b1) begin
						state <= SW;
					end
				end
			endcase
		end
	end


	sdram_ini inst_sdram_ini (
			.i_clk           (i_clk),
			.i_rst           (i_rst),
			.o_ini_end_flag  (ini_end),
			.o_ini_cmd       (ini_cmd)
		);


	sdram_ref  inst_sdram_ref (
			.i_clk     (i_clk),
			.i_rst     (i_rst),
			.i_ini_end (ini_end),
			.i_ref_en  (ref_en),
			.o_ref_end (ref_end),
			.o_ref_req (ref_req),
			.o_ref_cmd (ref_cmd)
		);

	sdram_wr inst_sdram_wr (
			.i_clk                 (i_clk),
			.i_rst                 (i_rst),
			.o_w_req               (w_req),
			.i_wfifo_en            (wfifo_en),
			.i_wfifo_data          (wfifo_data),
			.i_w_en                (w_en),
			.i_ref_req             (ref_req),
			.o_write_ref_break_end (write_ref_break_end),
			.o_write_data_end      (write_data_end),
			.o_w_cmd               (w_cmd),
			.o_w_dq                (w_dq)
		);


	sdram_rd inst_sdram_rd (
			.i_clk                (i_clk),
			.i_vclk               (i_vclk),
			.i_rst                (i_rst),
			.o_r_req              (r_req),
			.i_r_en               (r_en),
			.i_ref_req            (ref_req),
			.o_read_ref_break_end (read_ref_break_end),
			.o_read_data_end      (read_data_end),
			.o_r_cmd              (r_cmd),
			.i_r_dq               (r_dq),
			.o_vga_start          (vga_start),
			.i_vga_rd_en          (vga_rd_en),
			.o_vga_rd_data        (vga_rd_data)
		);


	//vga显示,显示行和列 ,还有对应各自的像素的数值8bits
	VGA_TIMING  inst_VGA_TIMING (
			.sclk       (i_vclk),
			.rst_n      (vga_start),
			.po_vga     (o_vga),
			.po_de      (po_de),
			.po_v_sync  (o_vsync),
			.po_h_sync  (o_hsync),
			.rgb_pixel  (vga_rd_data),
			.rd_fifo_en (vga_rd_en)
		);



endmodule

2.2.4 sdram_wr

sdram在写的过程比较重要需要考虑的是如何应对ref的刷新进程,并且能在刷新结束之后,继续自己的之前的写入过程。具体我们要实现的流程是我们通过串口把要写的数据写入我们设定好的fifo中,之后写入sdram的时候再从这个fifo读取数据。

状态机如下:

中间涉及一个比较重要的地方就是,本次实验我们只写入同一个bank,所以bank的地址都为0,我们具体要选择写入的列由WRITE中cmd的后几位决定,我们具体要写入的行由ACTIVE状态中,我们激活的行来决定。

【FPGA开发】SDRAM的读写VGA显示,fpga开发

代码如下:

// -----------------------------------------------------------------------------
// Copyright (c) 2014-2024 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File   : sdram_wr.v
// Create : 2023-12-27 17:12:11
// Revise : 2024-01-02 17:32:39
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------

/*----------------------------------------------------------------------------------------------------
sdram初始化
cmd真值表		cs_n	ras_n	cas_n	we_n	ba[1:0]	addr[11]	addr[10],addr[9:0]
	NOP			0		1		1		1		X		X			X		X
	PRLL		0		0		1		0		X		x			1		X
	REF			0		0		0		1		X		X			X		X
	WR          0       1       0       0       V       V           V       V
	ACT         0       0       1       1       V       V           V       V        

MR模式寄存器		{ba[1:0],addr[11:0]}	=	{00,00,000,011,0,010}
-----------------------------------------------------------------------------------------------------*/


module sdram_wr (
		input	wire 		i_clk,
		input 	wire 		i_rst,
		output  wire 		o_w_req,
		input   wire 		i_wfifo_en,
		input   wire [7:0]  i_wfifo_data,
		input   wire 		i_w_en,
		input   wire 		i_ref_req,
		output  wire 		o_write_ref_break_end,
		output  wire 		o_write_data_end,
		output  wire [17:0]	o_w_cmd,
		output  wire [15:0] o_w_dq
	);

	//sdram一行的大小位512*16 bits 所以当fifo的大小位1024*8bits的时候就可以启动fifo的读取

	parameter IDLE    = 5'b0_0001;
	parameter WREQ    = 5'b0_0010;
	parameter ACTIVE  = 5'b0_0100;
	parameter WRITE   = 5'b0_1000;
	parameter PREC    = 5'b1_0000;


	parameter WR    = 18'b01_0000_0000_0000_0000;
	parameter ACT   = 18'b01_0000_0000_0000_0000;
	parameter NOP   = 18'h1c000;
	parameter PALL	= 18'h08400;//18'b00_1000_0100_0000_0000


	reg [4:0] 	state;
	reg 		sd_ram_start;
	reg [2:0]	act_cnt;	
	reg 		act_end;
	wire[1:0]   bank_addr;
	reg	[11:0]	sd_row_cnt;
	reg 		sd_row_end;
	reg [1:0]	burst_cnt;
	reg [7:0]	burst_col_cnt;
	reg			img_end;
	reg			rd_fifo_en;
	wire[15:0]	rd_fifo_data;
	wire[9 :0]	rd_data_count;
	reg 		sd_row_end_flag;
	reg [3:0] 	pre_cnt;
	reg 		pre_end;
	reg 		ref_break;
	reg [17:0]  cmd;
	wire    	ref_break_end;


	wire 		full,empty;


	//assign 
	assign o_w_dq       		 = rd_fifo_data;
	assign o_write_ref_break_end = (state == PREC) & ref_break & pre_end;
	assign o_write_data_end      = (state == PREC) & sd_row_end_flag & pre_end;
	assign o_w_cmd               = cmd;
	assign bank_addr             = 2'b00;

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			state <= IDLE; 
		end
		else case (state) 
			IDLE : begin
				if(sd_ram_start == 1'b1) 
					state <= WREQ;
			end		
			WREQ : begin
				if(i_w_en == 1'b1) 
					state <= ACTIVE;
			end
			ACTIVE : begin
				if(act_end == 1'b1) 
					state <= WRITE;
			end
			WRITE: begin
				if(sd_row_end == 1'b1 || (burst_cnt == 'd3 && i_ref_req == 1'b1))
					state <= PREC;
			end 
			PREC: begin
				if(pre_end == 1'b1 && sd_row_end_flag == 1'b1)
					state <= IDLE;
				else if(ref_break == 1'b1 && pre_end == 1'b1)
				 	state <= WREQ;
			end
			default : state <= IDLE;
		endcase

	end
	

	//IDLE
	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			sd_ram_start <= 1'b0;
		end
		else if(state != IDLE) begin
			sd_ram_start <= 1'b0;
		end
		else if (state == IDLE && rd_data_count >= 'd510) begin
			sd_ram_start <= 1'b1;
		end
	end


	//WREQ
	assign o_w_req = (state == WREQ);



	//ACTIVE
	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			act_cnt <= 'd0;
		end
		else if(state == ACTIVE && act_cnt == 'd3) begin
			act_cnt <= 'd0;
		end
		else if (state == ACTIVE) begin
			act_cnt <= act_cnt + 'd1;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			act_end <= 'd0;
		end
		else if (act_cnt == 'd2) begin
			act_end <= 1'b1;
		end
		else begin
			act_end <= 1'b0;
		end
	end


	//WRITE
	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			burst_cnt <= 'd0;
		end
		else if (state == WRITE) begin
			burst_cnt <= burst_cnt + 'd1;
		end
		else begin
			burst_cnt <= 'd0;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			burst_col_cnt <= 'd0;
		end
		else if (sd_row_end == 1'b1) begin
			burst_col_cnt <= 'd0;
		end
		else if (state == WRITE && burst_cnt == 'd3) begin
			burst_col_cnt <= burst_col_cnt + 'd1;
		end
	end


	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			sd_row_end <= 1'b0;
		end
		else if (burst_col_cnt == 'd127 && burst_cnt == 'd2 && state == WRITE) begin
			sd_row_end <= 1'b1;
		end
		else begin
			sd_row_end <= 1'b0;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			sd_row_cnt <= 'd0;
		end
		else if (sd_row_cnt == 'd299 && sd_row_end == 1'b1) begin
			sd_row_cnt <= 'd0;
		end
		else if (sd_row_end == 1'b1)begin
			sd_row_cnt <= sd_row_cnt + 'd1;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			img_end <= 1'b0;
		end
		else if (sd_row_cnt == 'd299 && burst_cnt == 'd2) begin
			img_end <= 1'b1;
		end
		else begin
			img_end <= 1'b0;
		end
	end


	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			rd_fifo_en <= 1'b0;
		end
		else if (state == WRITE) begin
			rd_fifo_en <= 1'b1;
		end
		else begin
			rd_fifo_en <= 1'b0;
		end
	end


	//PRECHARGE
	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			sd_row_end_flag <= 1'b0;
		end
		else if (state == PREC && pre_cnt == 'd8) begin
			sd_row_end_flag <= 1'b0;
		end
		else if (state == WRITE && sd_row_end == 1'b1) begin
			sd_row_end_flag <= 1'b1;
		end
		else begin
			sd_row_end_flag <= sd_row_end_flag;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			pre_cnt <= 'd0;
		end
		else if (state == PREC && pre_cnt == 'd8) begin
			pre_cnt <= 'd0;
		end
		else if (state == PREC) begin
			pre_cnt <= pre_cnt + 'd1;
		end
		else begin
			pre_cnt <= pre_cnt;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			pre_end <= 1'b0;
		end
		else if (state == PREC && pre_cnt == 'd7) begin
			pre_end <= 1'b1;
		end
		else begin
			pre_end <= 1'b0;
		end
	end

	//PREC & REF
	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			ref_break <= 1'b0;
		end
		else if (state == PREC && pre_cnt == 'd8) begin
			ref_break <= 1'b0;
		end
		else if (burst_cnt == 'd3 && i_ref_req == 1'b1 && sd_row_end != 1'b1 && state == WRITE) begin
			ref_break <= 1'b1;
		end
		else begin
			ref_break <= ref_break;
		end
	end


	//ALL
	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			cmd <= NOP;
		end
		else if (state == ACTIVE && act_cnt == 'd0) begin
			cmd <= {ACT[17:14],bank_addr,sd_row_cnt};
		end
		else if(state == WRITE && burst_cnt == 'd0) begin
			cmd <= {WR[17:14],bank_addr,{3'b000,burst_col_cnt,2'b00}};
		end
		else if(state == PREC && sd_row_end == 1'b1) begin
			cmd <= PALL;
		end
		else begin
			cmd <= NOP;
		end
	end


	asfifo_w2048x8_r1024x16 wrbuffer (
	  .wr_clk(i_clk),                // input wire wr_clk
	  .rd_clk(i_clk),                // input wire rd_clk
	  .din(i_wfifo_data),            // input wire [7 : 0] din
	  .wr_en(i_wfifo_en),            // input wire wr_en
	  .rd_en(rd_fifo_en),            // input wire rd_en
	  .dout(rd_fifo_data),           // output wire [15 : 0] dout
	  .full(full),                   // output wire full
	  .empty(empty),                 // output wire empty
	  .rd_data_count(rd_data_count)  // output wire [9 : 0] rd_data_count
	);

endmodule

2.2.5 sdram_rd

sdram的读取是整个状态机优先级最低的一个阶段,我们要读取的时候要保证,我们这个过程中没有进行刷新的写入的操作,读取的流程如下,我们从sdram读取数据之后,我们保存数据到我们的fifo中,因为此次实验,我们要做的事VGA的图像显示,所以说,我们在这个阶段就用vga模块读取这个fifo里面的数据,最后显示出来。

状态机如下:

这个状态机和写状态机十分相像,但是一些触发条件有一些细化的区别,我们读取的行列也和写一样,ACTIVE来激活我们要读取的行,然后用READ阶段的cmd来读取我们所需要的列。

【FPGA开发】SDRAM的读写VGA显示,fpga开发

代码如下:


// -----------------------------------------------------------------------------
// Copyright (c) 2014-2024 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File   : sdram_rd.v
// Create : 2024-01-03 11:41:34
// Revise : 2024-01-04 18:13:02
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------


/*----------------------------------------------------------------------------------------------------
sdram初始化
cmd真值表		cs_n	ras_n	cas_n	we_n	ba[1:0]	addr[11]	addr[10],addr[9:0]
	NOP			0		1		1		1		X		X			X		X
	PRLL		0		0		1		0		X		x			1		X
	REF			0		0		0		1		X		X			X		X
	WR          0       1       0       0       V       V           V       V
	RD          0       1       0       1       V       V           V       V
	ACT         0       0       1       1       V       V           V       V        

MR模式寄存器		{ba[1:0],addr[11:0]}	=	{00,00,000,011,0,010}
-----------------------------------------------------------------------------------------------------*/


module sdram_rd (
		input	wire 		i_clk,
		input   wire 		i_vclk, //25mHz vga CLK 查vga相关的文档可以知道
		input 	wire 		i_rst,
		output  wire 		o_r_req,
		input   wire 		i_r_en,
		input   wire 		i_ref_req,
		output  wire 		o_read_ref_break_end,
		output  wire 		o_read_data_end,
		output  wire [17:0]	o_r_cmd,
		input   wire [15:0] i_r_dq,
		output 	wire 		o_vga_start,
		input   wire		i_vga_rd_en,
		output  wire [7:0]  o_vga_rd_data
	);
 
	//sdram一行的大小位512*16 bits 所以当fifo的大小位1024*8bits的时候就可以启动fifo的读取

	parameter IDLE    = 5'b0_0001;
	parameter RREQ    = 5'b0_0010;
	parameter ACTIVE  = 5'b0_0100;
	parameter READ    = 5'b0_1000;
	parameter PREC    = 5'b1_0000;


	parameter WR    = 18'b01_0000_0000_0000_0000;
	parameter RD    = 18'b01_0100_0000_0000_0000;
	parameter ACT   = 18'b01_0000_0000_0000_0000;
	parameter NOP   = 18'h1c000;
	parameter PALL	= 18'h08400;//18'b00_1000_0100_0000_0000


	reg [4:0] 	state;
	reg 		sd_read_start;
	reg [2:0]	act_cnt;	
	reg 		act_end;
	wire[1:0]   bank_addr;
	reg	[11:0]	sd_row_cnt;
	reg 		sd_row_end;
	reg [1:0]	burst_cnt;
	reg [7:0]	burst_col_cnt;
	reg			img_end;
	reg			rd_fifo_en;
	wire[15:0]	rd_fifo_data;
	wire[10:0]	rd_data_count;
	wire[10:0]  wr_data_count;
	reg 		sd_row_end_flag;
	reg [3:0] 	pre_cnt;
	reg 		pre_end;
	reg 		ref_break;
	reg [17:0]  cmd;
	wire    	ref_break_end;
	wire 		wfifo_en;
	wire [7:0]  wfifo_data;
	reg  [4:0]	read_state_dly;
	reg 		vga_fifo_ready;
	wire 		full,empty;


	//assign 
	assign o_r_dq       		 = rd_fifo_data;
	assign o_read_ref_break_end  = (state == PREC) & ref_break & pre_end;
	assign o_read_data_end       = (state == PREC) & sd_row_end_flag & pre_end;
	assign o_r_cmd               = cmd;
	assign bank_addr             = 2'b00;

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			state <= IDLE; 
		end
		else begin case (state) 
			IDLE : begin
				if(sd_read_start == 1'b1) 
					state <= RREQ;
			end		
			RREQ : begin
				if(i_r_en == 1'b1) 
					state <= ACTIVE;
			end
			ACTIVE : begin
				if(act_end == 1'b1) 
					state <= READ;
			end
			READ: begin
				if(sd_row_end == 1'b1 || (burst_cnt == 'd3 && i_ref_req == 1'b1))
					state <= PREC;
			end 
			PREC: begin
				if(pre_end == 1'b1 && sd_row_end_flag == 1'b1)
					state <= IDLE;
				else if(ref_break == 1'b1 && pre_end == 1'b1)
				 	state <= RREQ;
			end
			default : state <= IDLE;
		endcase
	end

	end
	

	//IDLE
	//当fifo中的大小小于512的时候 才需要继续写入 这是一个阈值的问题
	//当fifo的有效数据太多的时候,我们应该优先考虑的是把fifo中的数据发送给VGA
	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			sd_read_start <= 1'b0;
		end
		else if(state != IDLE) begin
			sd_read_start <= 1'b0;
		end
		else if (state == IDLE && wr_data_count <'d512) begin
			sd_read_start <= 1'b1;
		end
	end


	//RREQ
	assign o_r_req = (state == RREQ);



	//ACTIVE
	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			act_cnt <= 'd0;
		end
		else if(state == ACTIVE && act_cnt == 'd3) begin
			act_cnt <= 'd0;
		end
		else if (state == ACTIVE) begin
			act_cnt <= act_cnt + 'd1;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			act_end <= 'd0;
		end
		else if (act_cnt == 'd2) begin
			act_end <= 1'b1;
		end
		else begin
			act_end <= 1'b0;
		end
	end


	//READ
	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			burst_cnt <= 'd0;
		end
		else if (state == READ) begin
			burst_cnt <= burst_cnt + 'd1;
		end
		else begin
			burst_cnt <= 'd0;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			burst_col_cnt <= 'd0;
		end
		else if (sd_row_end == 1'b1) begin
			burst_col_cnt <= 'd0;
		end
		else if (state == READ && burst_cnt == 'd3) begin
			burst_col_cnt <= burst_col_cnt + 'd1;
		end
	end


	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			sd_row_end <= 1'b0;
		end
		else if (burst_col_cnt == 'd127 && burst_cnt == 'd2 && state == READ) begin
			sd_row_end <= 1'b1;
		end
		else begin
			sd_row_end <= 1'b0;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			sd_row_cnt <= 'd0;
		end
		else if (sd_row_cnt == 'd299 && sd_row_end == 1'b1) begin
			sd_row_cnt <= 'd0;
		end
		else if (sd_row_end == 1'b1)begin
			sd_row_cnt <= sd_row_cnt + 'd1;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			img_end <= 1'b0;
		end
		else if (sd_row_cnt == 'd299 && burst_cnt == 'd2) begin
			img_end <= 1'b1;
		end
		else begin
			img_end <= 1'b0;
		end
	end


	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			rd_fifo_en <= 1'b0;
		end
		else if (state == READ) begin
			rd_fifo_en <= 1'b1;
		end
		else begin
			rd_fifo_en <= 1'b0;
		end
	end


	//PRECHARGE
	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			sd_row_end_flag <= 1'b0;
		end
		else if (state == PREC && pre_cnt == 'd8) begin
			sd_row_end_flag <= 1'b0;
		end
		else if (state == READ && sd_row_end == 1'b1) begin
			sd_row_end_flag <= 1'b1;
		end
		else begin
			sd_row_end_flag <= sd_row_end_flag;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			pre_cnt <= 'd0;
		end
		else if (state == PREC && pre_cnt == 'd8) begin
			pre_cnt <= 'd0;
		end
		else if (state == PREC) begin
			pre_cnt <= pre_cnt + 'd1;
		end
		else begin
			pre_cnt <= pre_cnt;
		end
	end

	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			pre_end <= 1'b0;
		end
		else if (state == PREC && pre_cnt == 'd7) begin
			pre_end <= 1'b1;
		end
		else begin
			pre_end <= 1'b0;
		end
	end

	//PREC & REF
	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			ref_break <= 1'b0;
		end
		else if (state == PREC && pre_cnt == 'd8) begin
			ref_break <= 1'b0;
		end
		else if (burst_cnt == 'd3 && i_ref_req == 1'b1 && sd_row_end != 1'b1 && state == READ) begin
			ref_break <= 1'b1;
		end
		else begin
			ref_break <= ref_break;
		end
	end


	//ALL
	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			cmd <= NOP;
		end
		else if (state == ACTIVE && act_cnt == 'd0) begin
			cmd <= {ACT[17:14],bank_addr,sd_row_cnt};
		end
		else if(state == READ && burst_cnt == 'd0) begin
			cmd <= {RD[17:14],bank_addr,{3'b000,burst_col_cnt,2'b00}};
		end
		else if(state == PREC && sd_row_end == 1'b1) begin
			cmd <= PALL;
		end
		else begin
			cmd <= NOP;
		end
	end


	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			read_state_dly <= 4'b0;
		end
		else begin
			read_state_dly <= {read_state_dly[3:0],state[3]};
		end
	end



	always @(posedge i_clk) begin
		if (i_rst == 1'b1) begin
			vga_fifo_ready <= 1'b0;
		end
		else if(rd_data_count > 648)begin
			vga_fifo_ready <= 1'b1;
		end
	end


//VGAVGAVGA
assign wfifo_data  = i_r_dq;
assign wfifo_en    = read_state_dly[4];
assign o_vga_start = vga_fifo_ready;


//vga必须等fifo缓冲了一定数据之后才能读数据

asfifo_w2048x16r4096x8 rdbuffer (
	  .wr_clk(clk), // input wr_clk
	  .rd_clk(i_vclk), // input rd_clk      -> 此处是vga读取fifo的数据来显示
	  .din(wfifo_data), // input [15 : 0] din
	  .wr_en(wfifo_en), // input wr_en
	  .rd_en(i_vga_rd_en), // input rd_en
	  .dout(o_vga_rd_data), // output [7 : 0] dout
	  .full(full), // output full
	  .empty(empty), // output empty
	  .rd_data_count(rd_data_count), // output [10 : 0] rd_data_count
	  .wr_data_count(wr_data_count) // output [10 : 0] wr_data_count
);
endmodule






3. 顶层功能及框架描述

3.1 顶层功能描述:

本次实验中,我们要显示的是通过串口写入sdram中,然后再通过读取sdram显示到显示器上。我们通过matlab解析一个图片的RGB内容,rgb的单像素内容为8bits,我们用串口发送这些数据,到fifo中。等到能够写满bank一行的时候,我们开始写操作。但在没开始写操作的时候,我们的读操作已经开始读取sdram了,这个时候读出来的就是白条,等到我们把sdram写完的时候,这些白条的位置就会被相应的数据覆盖。

下面看一下顶层的代码:

顶层的代码之留下了和sdram和串口,以及VGA相应行列RGB数据的相应接口


// -----------------------------------------------------------------------------
// Copyright (c) 2014-2024 All rights reserved
// -----------------------------------------------------------------------------
// Author : XIBO WU (Gatsby) wuxibo2023@163.com
// File   : top_sdram.v
// Create : 2023-12-08 17:08:26
// Revise : 2024-01-04 18:26:14
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------


module top_sdram(
	input					i_clk,
	input					i_rst_n,
	input					i_rx,   
	inout      [15 : 0] 	io_Dq,
    output     [11 : 0] 	o_Addr,
    output     [1 : 0] 		o_Ba,
    output     				o_sdclk,
    output              	o_Cke,
    output 					o_Cs_n,
    output 					o_Ras_n,
    output					o_Cas_n,
    output 					o_We_n,
	output		[7:0]		o_vga,
    output				    o_hsync,
    output					o_vsync,
    output     [1: 0] 		o_Dqm       
	);

	wire 	   rst;
	wire 	   sclk;
	wire 	   vclk;
	wire [7:0] po_data;
	wire 	   po_flag;
	assign o_Dqm = 2'b00;
	assign rst = ~i_rst_n;
	assign o_Cke = 1'b1;
	assign o_sdclk = i_clk;//50 Mhz




	clk_gen inst_clk_gen (
	    // Clock out ports
	    .clk_50mhz(sclk),     // output clk_50mhz
	    .clk_25mhz(vclk),     // output clk_25mhz
	   // Clock in ports
	    .clk_in1(i_clk)
	);      // input clk_in1

	sdram_main_ctrl inst_sdram_main_ctrl (
		.i_clk (sclk),
		.i_vclk(vclk),
		.i_rst (rst),
		.o_cmd ({o_Cs_n,o_Ras_n,o_Cas_n,o_We_n,o_Ba,o_Addr}),
		.o_dq  (io_Dq),
		.o_vga (o_vga),
		.o_hsync(o_hsync),
		.o_vsync(o_vsync),
		.i_wfifo_en (po_flag),
		.i_wfifo_data (po_data)
	);


	uart_rx  inst_uart_rx (
		.sclk    (sclk),
		.rst_n   (i_rst_n),
		.rx      (rx),
		.po_data (po_data),
		.po_flag (po_flag)
	);



endmodule 

4. 结语

这里面还有VGA_TIMING模块还有uart_rx模块,在这里我们就不做展示了,后面的时候,后面在其他的文章里,会专门写串口和vga的部分的代码,这些都是可以复用的模块,后面会进行使用。

我们这次做的实验到这里就结束了文章来源地址https://www.toymoban.com/news/detail-797676.html

到了这里,关于【FPGA开发】SDRAM的读写VGA显示的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 记一次基于FPGA的VGA显示四操作数计算器工程的开发流程——(1)从顶层设计说起

    记一次基于FPGA的VGA显示四操作数计算器工程的开发流程——(1)从顶层设计说起

    首先值得说明的是,在这个项目几乎完成之际,笔者才愈发体会到了硬件思维和软件思维的云泥之别。不幸的是,在此项目的实现过程中,绝大部分代码的思维仍然是软件思维,因此该项目主要模块的设计部分可能并不能体现硬件操作的独到之处,不符合硬件工程师的基本设

    2024年02月04日
    浏览(15)
  • FPGA学习——VGA显示

    FPGA学习——VGA显示

    VGA(Video Graphics Array)是IBM在1987年随PS/2机⼀起推出的⼀种视频,具有分辨率⾼、显⽰速率快、颜⾊丰富等优点,在彩 ⾊显⽰器领域得到了⼴泛的应⽤。不⽀持热插拔,不⽀持⾳频传输。对于⼀些嵌⼊式VGA显⽰系统,可以在不使⽤VGA显⽰卡和计算机的 情况下,实现VGA图像的显⽰

    2024年02月04日
    浏览(14)
  • 基于FPGA的VGA图像显示

    基于FPGA的VGA图像显示

    引言:本文我们介绍利用FPGA实现VGA图像显示,主要介绍VGA硬件接口、VGA接口时序原理以及FPGA代码实现VGA接口时序、仿真等内容。 VGA(Video Graphics Array)视频图形阵列是IBM于1987年提出的一个使用模拟信号的电脑显示标准。VGA接口即电脑采用VGA标准输出数据的专用接口。VGA接口

    2024年02月06日
    浏览(11)
  • FPGA—VGA 显示器显示彩条(附代码)

    FPGA—VGA 显示器显示彩条(附代码)

    目录 1. 理论 2. 实操 2.1 顶层设计 2.1.1 模块框图 2.1.2 代码编写  2.1.3 仿真验证 2.2 时钟生成模块 2.3 VGA时序控制模块 2.3.1 模块框图 2.3.2 波形图绘制 2.3.3 代码编写 2.3.4 仿真验证 2.4 图像数据生成模块 2.4.1 模块框图 2.4.2 波形图绘制 2.4.3 代码编写 3.总结 VGA简介        图像显示

    2024年02月09日
    浏览(15)
  • 【FPGA】通俗理解从VGA显示到HDMI显示

    【FPGA】通俗理解从VGA显示到HDMI显示

    注:大部分参考内容来自“征途Pro《FPGA Verilog开发实战指南——基于Altera EP4CE10》2021.7.10(上)”  贴个下载地址: 野火FPGA-Altera-EP4CE10征途开发板_核心板 — 野火产品资料下载中心 文档 hdmi显示器驱动设计与验证 — [野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Pro开

    2024年02月09日
    浏览(12)
  • FPGA——基于VGA协议显示彩条、图片、字符

    FPGA——基于VGA协议显示彩条、图片、字符

    本篇博客主要是深入了解VGA协议,理解不同显示模式下的VGA控制时序参数(行频、场频、水平/垂直同步时钟周期、显示后沿/前沿等概念和计算方式)。并通过Verilog编程,实现以下VGA显示:1、屏幕上显示彩色条纹;2、显示自定义的汉字字符;3、输出一幅彩色图像。 VGA(Vi

    2024年02月04日
    浏览(13)
  • FPGA_简单工程_VGA显示驱动器

    FPGA_简单工程_VGA显示驱动器

    一 理论 使用640*480@60显示模式,将数字信号转换位模拟信号,经由VGA进行显示。 使用3GM723,3路高清视频编码芯片。 3GM7123编码芯片:                                该芯片的主要功能是将RGB888的颜色数据转换成模拟的电压信号,然后进入到VGA接口的3个RGB接口。例如RGB8

    2024年02月20日
    浏览(8)
  • FPGA_工程_基于Rom的VGA图像显示

    FPGA_工程_基于Rom的VGA图像显示

    一 工程框图 框图中,CLK_in,Vga_ctrl,Vga_pic模块已有,只需要对顶层模块进行修改,并将rom ip例化添加到Vga_pic模块的.v文件中,对Vga_pic的.v文件进行一定修改。 二 理论补充 显示图像的方法:                           使用matlab将图像格式转化为,.mif数据文件,再使用.m

    2024年02月20日
    浏览(12)
  • 【FPGA】VGA显示文字、彩条、图片——基于DE2-115

    【FPGA】VGA显示文字、彩条、图片——基于DE2-115

    **VGA(Video Graphics Array)**视频图形阵列是IBM于1987年提出的一个使用模拟信号的电脑显示标准。VGA接口即电脑采用VGA标准输出数据的专用接口。VGA接口共有15针,分成3排,每排5个孔,显卡上应用最为广泛的接口类型,绝大多数显卡都带有此种接口。它传输红、绿、蓝模拟信号

    2024年02月02日
    浏览(11)
  • 【FPGA实验】基于DE2-115平台的VGA显示

    【FPGA实验】基于DE2-115平台的VGA显示

    VGA(Video Graphics Array)视频图形阵列是IBM于1987年提出的一个使用模拟信号的电脑显示标准。VGA接口即电脑采用VGA标准输出数据的专用接口。VGA接口共有15针,分成3排,每排5个孔,显卡上应用最为广泛的接口类型,绝大多数显卡都带有此种接口。它传输红、绿、蓝模拟信号以及

    2024年02月03日
    浏览(12)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包