SDRAM小项目——读模块

发布时间:2024年01月15日

?根据文档信息,读模块和写模块过程相似,所以直接在写模块上修改信号名称。

与写模块不同的是,读模块的数据输出在读命令发出后几个周期才开始输出。

读模块代码:

module sdram_read(
		input					sclk		,
		input					srst		,
		//communicate with top
		input					rd_en		,
		output	wire			rd_req		,
		output	reg				flag_rd_end	,
		//
		input					ref_req		,
		input					rd_trig		,
		//write interface
		output	reg	[3:0]		rd_cmd		,
		output	reg	[11:0]		rd_addr		,
		output	wire	[1:0]		bank_addr
		//output  reg		[15:0]	w_data
);

//==========================================================
//=======	define parameter and internal signal	========
//==========================================================
//define state
localparam		s_idle  =  5'b00001;
localparam		s_req	=	5'b00010;
localparam		s_act	=	5'b00100;
localparam		s_rd	= 	5'b01000;
localparam		s_pre	= 	5'b10000;

//
localparam		cmd_nop = 4'b0111;
localparam		cmd_pre = 4'b0010;
localparam		cmd_ref = 4'b0001;
localparam		cmd_act = 4'b0011;
localparam		cmd_rd = 4'b0101;


reg				flag_rd;
reg[4:0]		state;

reg 						flag_act_end;
reg 						flag_pre_end;
reg							sd_row_end;
reg[1:0]					burst_cnt;
reg[1:0]					burst_cnt_t;
reg							rd_data_end;

reg[3:0]					act_cnt;
reg[3:0]					break_cnt;
reg[6:0]					col_cnt;

reg[11:0]					row_addr;
wire[8:0]					col_addr;


//==========================================================
//====================	main	code	====================
//==========================================================
//flag_rd
always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			flag_rd <= 1'b0;
		else if(rd_trig == 1'b1  &&flag_rd == 1'b0)
			flag_rd <= 1'b1;
		else if(rd_data_end == 1'b1)
			flag_rd <= 1'b0;
end

// rd_cmd
always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			rd_cmd <= cmd_nop;
		else case(state)
			s_act:
				if(act_cnt == 'd0)
					rd_cmd <= cmd_act;
				else
					rd_cmd <= cmd_nop;
			s_rd:
				if(burst_cnt == 'd0)
					rd_cmd <= cmd_rd;
				else
					rd_cmd <= cmd_nop;
			s_pre:
				if(break_cnt == 'd0)
					rd_cmd <= cmd_pre;
				else
					rd_cmd <= cmd_nop;
			default:rd_cmd <= cmd_nop;
		endcase
end

//burst_cnt
always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			burst_cnt  <=  'd0;
		else if(state == s_rd)
			burst_cnt <= burst_cnt + 1'b1;
		else
			burst_cnt <= 'd0;
end

//burst_cnt
always@(posedge sclk )begin
		burst_cnt_t <= burst_cnt;
end


//state
always@(posedge sclk  or negedge srst)begin
		if(srst == 1'b0)
			state  <= s_idle;
		else case(state)
			s_idle:
				if(rd_trig == 1'b1)
					state  <=  s_req;
				else
					state  <=  s_idle;
			s_req:
				if(rd_en  ==  1'b1)
					state  <=  s_act ;
				else
					state  <=  s_req;
			s_act:
				if(flag_act_end  ==  1'b1)
					state  <=  s_rd;
				else
					state  <=  s_act;
			s_rd:
				if(rd_data_end  ==  1'b1)
					state  <=  s_pre;
				else if(ref_req == 1'b1  && burst_cnt_t == 'd3 &&flag_rd  == 1'b1)
					state  <=  s_pre;
				else if(sd_row_end == 1'b1  &&  flag_rd == 1'b1)
					state  <=  s_pre;
			s_pre:
				if(ref_req == 1'b1  && flag_rd  ==  1'b1)
					state  <=  s_req;
				else if(flag_pre_end  == 1'b1  &&flag_rd == 1'b1)//sd_row_end==1
					state  <=  s_act;
				else if(rd_data_end == 1'b1)
					state  <= s_idle;
		default:state  <=  s_idle;
		endcase
end

//flag_act_end
always@(posedge sclk or negedge srst)begin
		if(srst  ==  1'b0)
			flag_act_end  <=  1'b0;
		else if (act_cnt  ==  'd3)				//  3?
			flag_act_end  <=  1'b1;
		else
			flag_act_end  <=  1'b0;
end

//flag_pre_end
always@(posedge sclk or negedge srst)begin
		if(srst  ==  1'b0)
			flag_pre_end  <=  1'b0;
		else if (break_cnt  ==  'd3)
			flag_pre_end  <=  1'b1;
		else
			flag_pre_end  <=  1'b0;
end

//break_cnt
always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			break_cnt  <=  'd0;
		else if(state  ==  s_pre)
			break_cnt <= break_cnt +1'b1;
		else
			break_cnt <= 'd0;
end

//act_cnt
always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			act_cnt <=  'd0;
		else if(state  ==  s_act)
			act_cnt <= act_cnt +1'b1;
		else
			act_cnt <= 'd0;
end

always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			rd_data_end <=  'd0;
		else if(row_addr == 'd1 && col_addr == 'd511)
			rd_data_end  <= 1'b1;
		else
			rd_data_end <= 1'b0;
end

always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
				col_cnt <= 'd0;
		else if(col_cnt  ==  'd511)
				col_cnt <= 'd0;
		else if(burst_cnt_t  == 'd3)
				col_cnt <= col_cnt + 1'b1;
end

always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
				row_addr <= 'd0;
		else if(sd_row_end == 1'b1)
				row_addr <= row_addr +'b1;
end

//rd_addr
always@(*)begin
		case(state)
			s_act:
				if(act_cnt == 'd0)
					rd_addr <= row_addr;
			s_rd:
				rd_addr <= {3'b000,col_addr};
			s_pre:
				if(break_cnt == 'd0)
					rd_addr <= {12'b0100_0000_0000};
		endcase
end

always@(posedge sclk or negedge srst )begin
		if(srst == 1'b0)
			sd_row_end <= 1'b0;
		else if(col_addr == 'd510)
			sd_row_end <= 1'b1;
		else	
			sd_row_end <= 1'b0;
end

//flag_rd_end  有无意义?
always@(posedge sclk or negedge srst )begin
		if(srst == 1'b0)
			flag_rd_end <= 1'b0;
		else if((state == s_pre && ref_req == 1'b1)
				|| (state == s_pre && rd_data_end == 1'b1))
			flag_rd_end <= 1'b1;
		else	
			flag_rd_end <= 1'b0;
end

assign bank_addr = 2'b00;
assign col_addr = {col_cnt,burst_cnt_t};
assign rd_req = state[1];


//产生测试数据
/* always@(*)begin
	case(burst_cnt_t)
		0: w_data <= 'd3;
		1: w_data <= 'd5;
		2: w_data <= 'd7;
		3: w_data <= 'd9;
	endcase
end
 */

endmodule

顶层模块例化读模块:

module	sdram_top(
		input			sclk,
		input			srst,
		//sdram		interface
		output	wire	sdram_clk,
		output	wire	sdram_cke,
		output	wire	sdram_cs_n,
		output	wire	sdram_cas_n,
		output	wire	sdram_ras_n,
		output	wire	sdram_we_n,
		output	wire	[1:0]	sdram_bank,
		output	reg		[11:0]	sdram_addr,
		output	wire	[1:0]	sdram_dqm,
		inout			[15:0]	sdram_dq,
		//
		input 			w_trig,
		input			rd_trig
);

//==========================================================
//=======	define parameter and internal signal	========
//==========================================================

//define state machine
reg		[4:0]	state;
localparam		idle		=		5'b00001;
localparam		arbit		= 		5'b00010;
localparam		aref		= 		5'b00100;
localparam		write		=		5'b01000;
localparam		read		=       5'b10000;

//init module
reg[3:0]		sd_cmd;
wire 			flag_init_end;
wire	[3:0]	init_cmd;
wire	[11:0]	init_addr;

//refresh module
reg				ref_en;
wire			ref_req;
wire			flag_ref_end;
wire 	[3:0]	cmd_reg;
wire	[11:0]	ref_addr;

//write module
 reg				w_en	;	
 wire               w_req	;	
 wire               flag_w_end;
 wire[3:0]           w_cmd		;
 wire[11:0]           w_addr		;
 wire[1:0]           w_bank_addr;
 wire[15:0]			w_data;
 
 //read module
reg					rd_en		  ;
wire					rd_req		  ;
wire					flag_rd_end   ;
wire	[3:0]			rd_cmd		   ;
wire	[11:0]			rd_addr        ;
wire	[1:0]			rd_bank_addr	   ;
 
 

//==========================================================
//====================	main	code	====================
//==========================================================
always@(posedge sclk or negedge srst) begin
	if(srst 	==		1'b0)
		state	<=		idle;
	else
	case(state)
		idle:
				if(flag_init_end	==	1'b1)
					state	<=	arbit;
				else
					state	<=	idle;
		arbit:
				if(ref_en	==		1'b1)
					state	<=	aref;
				else if(w_en == 1'b1)
					state <= write;
				else if(rd_en == 1'b1)
					state <= read;
				else
					state	<=	arbit;
		aref:
				if(flag_ref_end	==	1'b1)
					state	<=	arbit;
				else
					state	<=	aref;
		write:
				if(flag_w_end == 1'b1)
					state <= arbit;
				else
					state <= write;
		read:
				if(flag_rd_end == 1'b1)
					state <= arbit;
				else
					state <= read;
		default:	
					state	<=	idle;
	endcase
end

always@(*)
begin
	case(state)
		idle:
		begin
			sd_cmd <= init_cmd;
			sdram_addr <= init_addr;
		end
		aref:
		begin
			sd_cmd <= cmd_reg;
			sdram_addr <= ref_addr;
		end
		write:
		begin
			sd_cmd <= w_cmd;
			sdram_addr <= w_addr;
		end
		read:
		begin
			sd_cmd <= rd_cmd;
			sdram_addr <= rd_addr; 
		end
		default:begin
			sd_cmd <= 4'b0111;//nop
			sdram_addr <= 'd0;
		end
	endcase	
end


//ref_en
always@(posedge sclk or  negedge srst)begin
		if(srst == 1'b0)
			ref_en <= 1'b0;
		else if(state == arbit	&& ref_req == 1'b1)
			ref_en <= 1'b1;
		else	
			ref_en <= 1'b0;
end

//w_en
always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			w_en <= 1'b0;
		else if(state == arbit && ref_en == 1'b0 && w_req == 1'b1)
			w_en <= 1'b1;
		else
			w_en <= 1'b0;
end

//rd_en
always@(posedge sclk or negedge srst)begin
		if(srst == 1'b0)
			rd_en <= 1'b0;
		else if(state == arbit && ref_en == 1'b0 && w_req == 1'b0 && rd_req == 1'b1)
			rd_en <= 1'b1;
		else
			rd_en <= 1'b0;
end



assign	sdram_cke	=	1'b1;
//assign	sdram_addr	=	(state == idle) ?init_addr:ref_addr;
//assign	{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = (state == idle) ?init_cmd:cmd_reg;
assign	{sdram_cs_n, sdram_ras_n, sdram_cas_n, sdram_we_n} = sd_cmd;
assign 	sdram_dqm	=	2'b00;
assign	sdram_clk	=	~sclk;	//内部时钟上升沿采集命令,命令又是由系统时钟上升沿产生的??(为了保证采样时刻处在数据中间时刻)
assign	sdram_dq = (state == write)?w_data:{16{1'bz}};
assign  sdram_bank = (state == write)?w_bank_addr:rd_bank_addr;


sdram_init	sdram_init_inst(
	.sclk				(sclk)	,
	.srst				(srst)	,
	.cmd_reg			(init_cmd)	,	
	.sdram_addr			(init_addr)	,	
	.flag_init_end	    		(flag_init_end)
);	


sdram_aref		sdram_aref(
		.sclk						(sclk),
		.srst						(srst),
		//commmunicate with arbit
		.ref_en							(ref_en),
		.ref_req						(ref_req),
		.flag_ref_end					(flag_ref_end),
		//others                       
		.flag_init_end					(flag_init_end),
		.cmd_reg						(cmd_reg),
		.ref_addr						(ref_addr)
);

sdram_wirte		sdram_wirte_inst(
		.sclk							(sclk),
		.srst							(srst),
		//communicate with top          
		.w_en							(w_en		),
		.w_req							(w_req		),
		.flag_w_end						(flag_w_end),
		//                              
		.ref_req						(ref_req),
		.w_trig							(w_trig	),
		//write interface               
		.w_cmd							(w_cmd		),
		.w_addr							(w_addr		),
		.bank_addr                      (w_bank_addr  ),
		.w_data							(w_data)
);

sdram_read		sdram_read_inst(
		.sclk		(sclk),
		.srst		(srst),
		//communicate with top
		.rd_en			(rd_en		),
		.rd_req			(rd_req		),
		.flag_rd_end	(flag_rd_end),
		//
		.ref_req		(ref_req),
		.rd_trig		(rd_trig),
		//write interface
		.rd_cmd					(rd_cmd		),
		.rd_addr				(rd_addr	),
		.bank_addr				(rd_bank_addr)
);


endmodule

测试代码中添加读触发信号,并进行初始化

`timescale	1ns/1ns

module	tb_sdram_top;

	reg			sclk;
	reg			srst;
//----------------------------------------
	wire	sdram_clk;
	wire	sdram_cke;
	wire	sdram_cs_n;
	wire	sdram_cas_n;
	wire	sdram_ras_n;
	wire	sdram_we_n;
	wire	[1:0]	sdram_bank;
	wire	[11:0]	sdram_addr;
	wire	[1:0]	sdram_dqm;
    wire	[15:0]	sdram_dq;
	
	reg				w_trig;
	reg 			rd_trig;
//----------------------------------------

initial begin	
	w_trig <= 0;
	rd_trig<= 0;
	#205000
	w_trig <= 'b1;
	#20
	w_trig <= 'b0;
	#226500
	rd_trig <= 1'b1;
	#20
	rd_trig <= 1'b0;
end

initial begin
			sclk = 1;
			srst <= 0;
			#100
			srst <=1;
end

always	#10		sclk = ~sclk;

defparam sdram_model_plus_inst.addr_bits =    12;
defparam sdram_model_plus_inst.data_bits =    16;
defparam sdram_model_plus_inst.col_bits  =    9;
defparam sdram_model_plus_inst.mem_sizes =    2*1024*1024;//1 M 


sdram_top		sdram_top_inst(
			.sclk							(sclk		),
			.srst							(srst		),
			.sdram_clk						(sdram_clk	),
			.sdram_cke						(sdram_cke	),
			.sdram_cs_n						(sdram_cs_n	),
			.sdram_cas_n						(sdram_cas_n),
			.sdram_ras_n						(sdram_ras_n),
			.sdram_we_n						(sdram_we_n	),
			.sdram_bank						(sdram_bank	),
			.sdram_addr						(sdram_addr	),
			.sdram_dqm						(sdram_dqm	),
			.sdram_dq						(sdram_dq	),
			.w_trig							(w_trig),
			.rd_trig						(rd_trig)
);


sdram_model_plus sdram_model_plus_inst(
 .Dq				(sdram_dq)	,
 .Addr				(sdram_addr), 
 .Ba				(sdram_bank), 
 .Clk				(sdram_clk), 
 .Cke				(sdram_cke), 
 .Cs_n				(sdram_cs_n), 
 .Ras_n				(sdram_ras_n), 
 .Cas_n				(sdram_cas_n), 
 .We_n				(sdram_we_n), 
 .Dqm				(sdram_dqm),
 .Debug				(1'b1)
 );
 
 
 
endmodule

在modelsim中用脚本文件仿真:

##create work library
vlib work

vlog		"./tb_sdram_top.v"
vlog		"../src/sdram_model_plus.v"
vlog		"../src/sdram_top1.5.v"
vlog		"../src/sdram_write.v"
vlog		"../src/sdram_init.v"
vlog		"../src/sdram_ref.v"
vlog		"../src/sdram_read.v"

vsim	-voptargs=+acc work.tb_sdram_top

# Set the window types
view wave
view structure
view signals

add wave -divider {sdram_top1.5}
add wave tb_sdram_top/sdram_top_inst/*
add wave -divider {sdram_write}
add wave tb_sdram_top/sdram_top_inst/sdram_wirte_inst/*
add wave -divider {sdram_read}
add wave tb_sdram_top/sdram_top_inst/sdram_read_inst/*
add wave -divider {tb_sdram_top}
add wave tb_sdram_top/sdram_model_plus_inst/*
.main clear

run 205us

到此,简易的SDRAM控制器已经完成了,通过SDRAM控制器的学习,大致了解了如何阅读文档信息;根据文档信息设计时序逻辑;学会了一些脚本语言的使用。后续还需要优化SDRAM控制器的设计。

文章来源:https://blog.csdn.net/weixin_49574391/article/details/135606308
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。