03-SDRAM:写操作(突发)

SDRAM 数据写操作(页突发)

  1. state:
  2. cnt_cmd: 指令执行时长
  3. cnt_cmd_reset:上一个计数器的清零信号 (依赖计满信号:trp_end、trfc_end、tmrd_end)
  4. trcd_end、twrite_end、trp_end: 自家延时结束的信号(依赖 状态)
  5. wr_ack: 写数据期间的状态,整个过程都拉高
  6. wr_end: 状态终止
  7. wr_sdram_en:

设计文件

// SDRAM 数据写操作(页突发)
// 1. state:		
// 2. cnt_cmd:		指令执行时长
// 3. cnt_cmd_reset:上一个计数器的清零信号			 (依赖计满信号:trp_end、trfc_end、tmrd_end)
// 4. trcd_end、twrite_end、trp_end: 自家延时结束的信号(依赖 状态)
// 5. wr_ack:		写数据期间的状态,整个过程都拉高
// 6. wr_end:		状态终止
// 7. wr_sdram_en:	

module sdram_write(
	input 				    clk,
	input 				    rst_n,
	input 				    init_end,
	input 	   	  			wr_en,
	input 		[23:0]		wr_addr,
	input 		[15:0]		wr_data,
	input 		[9:0]		wr_burst_len,
	
	output wire 			wr_ack,
	output wire 			wr_end,
	output reg 	[3:0] 		write_cmd,
	output reg 	[1:0] 		write_bank,
	output reg 	[12:0] 		write_addr,
	output reg 				wr_sdram_en, 
	output wire [15:0] 		wr_sdram_data
);

//==========================================parameter===========================================================
//状态机
localparam	WR_IDLE = 3'b000,				//写操作初始状态
			WR_ACT  = 3'b001,				//行激活状态
			WR_TRCD = 3'b011,				//行激活等待状态
			WR_WR 	= 3'b010,				//写操作状态
			WR_DATA = 3'b100,				//突发写操作状态,突发写入多个数据,直到突发终止
			WR_PRE 	= 3'b101,             	//预充电状态
			WR_TRP 	= 3'b111,             	//预充电状态等待状态
			WR_END 	= 3'b110;             	//结束状态
			
//等待时间参数定义	
localparam	TRP  = 3'd2	,					//预充电等待周期
			TRCD = 3'd2	;					//激活等待周期
			
//命令指令参数				
localparam 	NOP		  	= 4'b0111 ,			//空操作指令
			PRECHARGE 	= 4'b0010 ,			//预充电指令
			ACTIVE    	= 4'b0011 ,			//激活指令
			WRITE     	= 4'b0100 ,			//写指令
			BURST_STOP  = 4'b0110 ;			//突发终止指令
// 突发长度——从外部传入
//parameter MAX_WR = 4'd10;
//==========================================reg=================================================================			
reg [7:0]   state;
reg [7:0]   next_state;
reg [9:0]   cnt_cmd;//2、9

//==========================================wire=================================================================

wire trcd_end;
wire twrite_end;
wire trp_end;
wire cnt_cmd_reset;
//==========================================assign=================================================================

assign trcd_end   	 = ((state == WR_TRCD)  && (cnt_cmd == TRCD    -1))?1'd1:1'd0;
assign twrite_end 	 = ((state == WR_DATA) && (cnt_cmd == wr_burst_len -1))?1'd1:1'd0;
assign trp_end    	 = ((state == WR_TRP)  && (cnt_cmd == TRP   -1))?1'd1:1'd0;
assign cnt_cmd_reset = ((state == WR_IDLE)||(state == WR_WR) || (state == WR_END) || trcd_end || twrite_end || trp_end)?1'd1:1'd0;
assign wr_ack		 = ((state == WR_WR)  || (state == WR_DATA) && (cnt_cmd <= wr_burst_len -2'd2))?1'd1:1'd0;//在数据低8位末拉低,计数器要在一个完整时钟周期才有效,因此这里是在低8位
assign wr_end		 = ((state == WR_END)) ? 1'd1 : 1'd0;
assign wr_sdram_data = (wr_sdram_en) ? wr_data : 16'd0;
//==========================================always=================================================================

always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		wr_sdram_en <= 1'd0;
	end
	else 
		wr_sdram_en <= wr_ack;
end

always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		cnt_cmd <= 10'd0;
	end
	else if(cnt_cmd_reset)begin
		cnt_cmd <= 10'd0;
	end
	else
		cnt_cmd <= cnt_cmd + 10'd1;
end


//==========================================状态机=================================================================

// 第一段
always@(posedge clk or negedge rst_n)begin
	if(!rst_n )begin
		state <= WR_IDLE;
	end
	else 
		state <= next_state;
end
//第二段
always@(*)begin
	case(state)
		WR_IDLE:
			if(init_end && wr_en)
				next_state = WR_ACT;
			else 
				next_state = WR_IDLE;
		WR_ACT :next_state = WR_TRCD;
		WR_TRCD :
			if(trcd_end)
				next_state = WR_WR;
			else
				next_state = WR_TRCD;
		WR_WR  :next_state = WR_DATA;
	    WR_DATA:
			if(twrite_end)
				next_state = WR_PRE;
			else
				next_state = WR_DATA;
	    WR_PRE :next_state = WR_TRP;
		WR_TRP :
			if(trp_end)
				next_state = WR_END;
			else
				next_state = WR_TRP;
		WR_END: next_state = WR_IDLE;
	default:	next_state = WR_IDLE;
	endcase
end

// 第三段
	always@(posedge clk or negedge rst_n)begin
		if(!rst_n )begin
			write_cmd	 <= NOP;
			write_bank	 <= 2'b11;
			write_addr	 <= 13'h1fff;
			
		end
		else 
			case(state)
				WR_IDLE : begin
					write_cmd	 <= NOP;
					write_bank	 <= 2'b11;
					write_addr	 <= 13'h1fff;
				end
				WR_ACT  : begin
					write_cmd	 <= ACTIVE;
					write_bank	 <= wr_addr[23:22];//wr_addr有3个地址,bank+13位行地址+9位列地址
					write_addr	 <= wr_addr[21:9];
				end
				WR_TRCD : begin
					write_cmd	 <= NOP;
					write_bank	 <= 2'b11;
					write_addr	 <= 13'h1fff;
				end
				WR_WR   : begin
					write_cmd	 <= WRITE;
					write_bank	 <= wr_addr[23:22];//wr_addr有3个地址,bank+13位行地址+9位列地址
					write_addr	 <= {{4{1'b0}},wr_addr[8:0]};
				end
				WR_DATA : begin
					write_bank	 <= 2'b11;
					write_addr	 <= 13'h1fff;
					if(twrite_end)
						write_cmd	 <= BURST_STOP;//写完数据,就停止,然后再预充电
					else
						write_cmd	 <= NOP;
				end
				WR_PRE  : begin
					write_cmd	 <= PRECHARGE;
					write_bank	 <= wr_addr[23:22];
					write_addr	 <= 13'h0400;//拉高A10,对所有bank预充电
				end
				WR_TRP:	begin
					write_cmd	 <= NOP;
					write_bank	 <= 2'b11;
					write_addr	 <= 13'h1fff;
				end
				WR_END: begin
					write_cmd	 <= NOP;
					write_bank	 <= 2'b11;
					write_addr	 <= 13'h1fff;
				end 
			default:begin
			write_cmd	 <= NOP;
			write_bank	 <= 2'b11;
			write_addr	 <= 13'h1fff;
			
		end
			endcase
	end
endmodule 

测试文件


`timescale  1ns/1ns

// Author        : EmbedFire
// Create Date   : 2019/08/25
// Module Name   : tb_sdram_init
// Project Name  : uart_sdram
// Target Devices: Altera EP4CE10F17C8N
// Tool Versions : Quartus 13.0
// Description   : SDRAM初始化模块仿真
// 
// Revision      : V1.0
// Additional Comments:
// 
// 实验平台: 野火_征途Pro_FPGA开发板
// 公司    : http://www.embedfire.com
// 论坛    : http://www.firebbs.cn
// 淘宝    : https://fire-stm32.taobao.com


module  tb_sdram_write();

//********************************************************************//
//****************** Internal Signal and Defparam ********************//
//********************************************************************//

//wire define
//clk_gen
wire            clk_50m         ;   //PLL输出50M时钟
wire            clk_100m        ;   //PLL输出100M时钟
wire            clk_100m_shift  ;   //PLL输出100M时钟,相位偏移-30deg
wire            locked          ;   //PLL时钟锁定信号
wire            rst_n           ;   //复位信号,低有效
//sdram_init
wire    [3:0]   init_cmd        ;   //初始化阶段指令
wire    [1:0]   init_ba         ;   //初始化阶段L-Bank地址
wire    [12:0]  init_addr       ;   //初始化阶段地址总线
wire            init_end        ;   //初始化完成信号

// wire            auto_ref_req ;
// wire   [3:0]    auto_ref_cmd ;
// wire   [1:0]    auto_ref_bank;
// wire   [12:0]   auto_ref_addr;
// wire            auto_ref_end ;
//sdram
wire    [3:0]   sdram_cmd       ;   //SDRAM操作指令
wire    [1:0]   sdram_bank		;   //SDRAM L-Bank地址
wire    [12:0]  sdram_addr      ;   //SDRAM地址总线
wire    [15:0]  sdram_dq;
wire        wr_ack;
wire        wr_end;
wire        wr_sdram_en;
wire [3:0]  write_cmd;
wire [1:0]  write_bank;
wire [12:0] write_addr;
wire [15:0] wr_sdram_data;

//reg define
reg             clk_c1         ;   //系统时钟
reg             sys_rst_n       ;   //复位信号
// reg             auto_ref_en;

//defparam
//重定义仿真模型中的相关参数
defparam sdram_model_plus_inst.addr_bits = 13;          //地址位宽
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; //L-Bank容量

//********************************************************************//
//**************************** Clk And Rst ***************************//
//********************************************************************//

//时钟、复位信号
initial
  begin
    clk_c1     =   1'b1  ;
    sys_rst_n   <=  1'b0  ;
    #200
    sys_rst_n   <=  1'b1  ;
  end

always  #10 clk_c1 = ~clk_c1;

//rst_n:复位信号
assign  rst_n = sys_rst_n & locked;



// //atref_en:自动刷新使能
// always@(posedge clk_100m or negedge rst_n)begin
//     if(rst_n == 1'b0)
//         auto_ref_en <=  1'b0;
//     else if((init_end == 1'b1) && (auto_ref_req == 1'b1))
//         auto_ref_en <=  1'b1;
//     else if(auto_ref_end == 1'b1)
//         auto_ref_en <=  1'b0;
// end 

reg wr_en;
//wr_en:写数据使能
always@(posedge clk_100m or negedge rst_n)
    if(rst_n == 1'b0)
        wr_en   <=  1'b0;
    else    if(wr_end == 1'b1)
        wr_en   <=  1'b0;
    else    if(init_end == 1'b1)
        wr_en   <=  1'b1;
    else
        wr_en   <=  wr_en;

reg [15:0] wr_data_in;
//wr_data_in:写数据
always@(posedge clk_100m or negedge rst_n)
    if(rst_n == 1'b0)
        wr_data_in  <=  16'd0;
    else    if(wr_data_in == 16'd10)
        wr_data_in  <=  16'd0;
    else    if(wr_ack == 1'b1)
        wr_data_in  <=  wr_data_in + 1'b1;
    else
        wr_data_in  <=  wr_data_in;

//sdram_cmd,sdram_ba,sdram_addr
assign  sdram_cmd  = (init_end == 1'b1) ? write_cmd  : init_cmd;
assign  sdram_bank = (init_end == 1'b1) ? write_bank : init_ba;
assign  sdram_addr = (init_end == 1'b1) ? write_addr : init_addr;
 
//wr_sdram_data
assign  sdram_dq = (wr_sdram_en == 1'b1) ? wr_sdram_data : 16'hz;
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//

//------------- clk_gen_inst -------------
clk_gen clk_gen_inst (
    .inclk0     (clk_c1        ),
    .areset     (~sys_rst_n     ),
    .c0         (clk_50m        ),
    .c1         (clk_100m       ),
    .c2         (clk_100m_shift ),

    .locked     (locked         )
);

//------------- sdram_init_inst -------------
sdram_initial  sdram_initial_inst(

    .clk        (clk_100m   ),
    .rst_n      (rst_n      ),

    .init_cmd   (init_cmd   ),
    .init_bank  (init_ba    ),
    .init_addr  (init_addr  ),
    .init_end   (init_end   )

);
// sdram_auto_ref  sdram_auto_ref_inst(

//     .clk          (clk_100m   ),
//     .rst_n        (rst_n      ),
//     .init_end     (init_end   ),
//     .auto_ref_en  (auto_ref_en),

//     .auto_ref_req   (auto_ref_req   ),
//     .auto_ref_cmd   (auto_ref_cmd   ),
//     .auto_ref_bank  (auto_ref_bank  ),
//     .auto_ref_addr  (auto_ref_addr  ),
//     .auto_ref_end   (auto_ref_end   )

// );



sdram_write sdram_write_inst(
	.clk(clk_100m),
	.rst_n(rst_n),
	.init_end(init_end),
	.wr_en(wr_en),
	.wr_addr(24'h000_000 ),
	.wr_data(wr_data_in),
	.wr_burst_len(10'd10 ),
	
	.wr_ack(wr_ack),
	.wr_end(wr_end),
	.write_cmd(write_cmd),
	.write_bank(write_bank),
	.write_addr(write_addr),
	.wr_sdram_en(wr_sdram_en), 
	.wr_sdram_data(wr_sdram_data)
);


//-------------sdram_model_plus_inst-------------
sdram_model_plus    sdram_model_plus_inst(
    .Dq     (sdram_dq        ),
    .Addr   (sdram_addr      ),
    .Ba     (sdram_bank     ),
    .Clk    (clk_100m_shift ),
    .Cke    (1'b1           ),
    .Cs_n   (sdram_cmd[3]    ),
    .Ras_n  (sdram_cmd[2]    ),
    .Cas_n  (sdram_cmd[1]    ),
    .We_n   (sdram_cmd[0]    ),
    .Dqm    (2'b0           ),
    .Debug  (1'b1           )

);


//------------------------------------------------
//--    状态机名称查看器
//------------------------------------------------
reg [79:0]	name_state_cur;				//每字符8位宽,这里取最多10个字符80位宽
 
always @(*) begin
    case(sdram_write_inst.state)
        3'b000:		name_state_cur = "WR_IDLE";
        3'b001:		name_state_cur = "WR_ACT ";
        3'b011:		name_state_cur = "WR_TRCD ";
        3'b010:		name_state_cur = "WR_WR  "; 
        3'b100:     name_state_cur = "WR_DATA  "; 
        3'b101:     name_state_cur = "WR_PRE  ";
		3'b110:		name_state_cur = "WR_END"; 
		3'b111:		name_state_cur = "WR_TRP "; 
        default:	name_state_cur = "WR_IDLE";
    endcase
end

endmodule

打印信息

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值