[FPGA] いくつかの基本的なモジュール コード

目次

VGAドライバーモジュール:

OV5640カメラ

最上位ファイル:

COMSモジュール:

カメラの cfg 構成:

IIc モジュール:

SDRAMモジュール:

TOPモジュール

FIFO制御モジュール:

2 つの FIFO を構築する

SDRAMコントロールモジュールTOP

SDRAMコマンド制御モジュール

SDRAMステータス制御モジュール

SDRAMデータ読み取りおよび書き込みモジュール

SDRAMパラメータファイル


カメラトップ:

module cam_vga(    
    input                 sys_clk     ,  //系统时钟
    input                 sys_rst_n   ,  //系统复位,低电平有效
    //摄像头接口
    input                 cam_pclk    ,  //cmos 数据像素时钟
    input                 cam_vsync   ,  //cmos 场同步信号
    input                 cam_href    ,  //cmos 行同步信号
    input        [7:0]    cam_data    ,  //cmos 数据  
	output                cam_rst_n   ,  //cmos 复位信号,低电平有效
    output                cam_pwdn    ,  //cmos 电源休眠模式选择信号
    output                cam_scl     ,  //cmos SCCB_SCL线
    inout                 cam_sda     ,  //cmos SCCB_SDA线
    //SDRAM接口
    output                sdram_clk   ,  //SDRAM 时钟
    output                sdram_cke   ,  //SDRAM 时钟有效
    output                sdram_cs_n  ,  //SDRAM 片选
    output                sdram_ras_n ,  //SDRAM 行有效
    output                sdram_cas_n ,  //SDRAM 列有效
    output                sdram_we_n  ,  //SDRAM 写有效
    output       [1:0]    sdram_ba    ,  //SDRAM Bank地址
    output       [1:0]    sdram_dqm   ,  //SDRAM 数据掩码
    output       [12:0]   sdram_addr  ,  //SDRAM 地址
    inout        [15:0]   sdram_data  ,  //SDRAM 数据    
    //VGA接口                          
    output                vga_hs      ,  //行同步信号
    output                vga_vs      ,  //场同步信号
    output        [15:0]  vga_rgb        //红绿蓝三原色输出 
	);

//parameter define
parameter  SLAVE_ADDR = 7'h3c         ;  //OV5640的器件地址7'h3c
parameter  BIT_CTRL   = 1'b1          ;  //OV5640的字节地址为16位  0:8位 1:16位
parameter  CLK_FREQ   = 26'd65_000_000;  //i2c_dri模块的驱动时钟频率 65MHz
parameter  I2C_FREQ   = 18'd250_000   ;  //I2C的SCL时钟频率,不超过400KHz
parameter  CMOS_H_PIXEL = 24'd1024    ;  //CMOS水平方向像素个数,用于设置SDRAM缓存大小
parameter  CMOS_V_PIXEL = 24'd768     ;  //CMOS垂直方向像素个数,用于设置SDRAM缓存大小
                                      
									  
wire   [15:0]         wr_data         ;  //sdram_ctrl模块写数据
wire   [15:0]         rd_data         ;  //sdram_ctrl模块读数据

//*****************************************************
//**                    main code
//*****************************************************

assign  rst_n = sys_rst_n & locked;
//系统初始化完成:SDRAM和摄像头都初始化完成
//避免了在SDRAM初始化过程中向里面写入数据
//assign  sys_init_done = sdram_init_done & cam_init_done;
//不对摄像头硬件复位,固定高电平
assign  cam_rst_n = 1'b1;
//电源休眠模式选择 0:正常模式 1:电源休眠模式
assign  cam_pwdn = 1'b0;

//锁相环
pll_clk u_pll_clk(
    .areset             (~sys_rst_n),
    .inclk0             (sys_clk),			
    .c0                 (clk_100m),			//100mhz时钟,SDRAM操作时钟
    .c1                 (clk_100m_shift),	//100mhz时钟,SDRAM相位偏移时钟
    .c2                 (clk_65m),			//65mhz时钟,提供给IIC驱动时钟和vga驱动时钟
    .locked             (locked)
    );

//摄像头模块
camera
	#(
	.SLAVE_ADDR			(SLAVE_ADDR),
	.BIT_CTRL  			(BIT_CTRL),
	
	.CLK_FREQ 			(CLK_FREQ) ,
    .I2C_FREQ			(I2C_FREQ),
	
	.CMOS_H_PIXEL      (CMOS_H_PIXEL),
	.CMOS_V_PIXEL      (CMOS_V_PIXEL)
    )
	cam(
	.clk_65m      	  	(clk_65m),
	.rst_n    		  	(rst_n),
		
	.cam_pclk        	(cam_pclk),  //cmos 数据像素时钟
	.cam_vsync       	(cam_vsync),  //cmos 场同步信号
	.cam_href        	(cam_href),  //cmos 行同步信号
	.cam_data        	(cam_data),  //cmos 数据   
	
	//与SDRAM的接口
	.sdram_init_done 	(sdram_init_done),
	.cmos_frame_valid	(wr_en),  //数据有效使能信号
	.cmos_frame_data 	(wr_data),   //有效数据        
	
	.cam_scl        	  	(cam_scl),      // I2C的SCL时钟信号
	.cam_sda              	(cam_sda)// I2C的SDA信号
);



//SDRAM 控制器顶层模块,封装成FIFO接口
//SDRAM 控制器地址组成: {bank_addr[1:0],row_addr[12:0],col_addr[8:0]}
sdram_top u_sdram_top(
    .ref_clk            (clk_100m),                   //sdram 控制器参考时钟
    .out_clk            (clk_100m_shift),             //用于输出的相位偏移时钟
    .rst_n              (rst_n),                      //系统复位
                                                        
    //用户写端口                                        
    .wr_clk             (cam_pclk),                   //写端口FIFO: 写时钟
    .wr_en              (wr_en),                      //写端口FIFO: 写使能
    .wr_data            (wr_data),                    //写端口FIFO: 写数据
    .wr_min_addr        (24'd0),                      //写SDRAM的起始地址
    .wr_max_addr        (CMOS_H_PIXEL*CMOS_V_PIXEL),  //写SDRAM的结束地址
    .wr_len             (10'd512),                    //写SDRAM时的数据突发长度
    .wr_load            (~rst_n),                     //写端口复位: 复位写地址,清空写FIFO
                                                        
    //用户读端口                                        
    .rd_clk             (clk_65m),                    //读端口FIFO: 读时钟
    .rd_en              (rd_en),                      //读端口FIFO: 读使能
    .rd_data            (rd_data),                    //读端口FIFO: 读数据
    .rd_min_addr        (24'd0),                      //读SDRAM的起始地址
    .rd_max_addr        (CMOS_H_PIXEL*CMOS_V_PIXEL),  //读SDRAM的结束地址
    .rd_len             (10'd512),                    //从SDRAM中读数据时的突发长度
    .rd_load            (~rst_n),                     //读端口复位: 复位读地址,清空读FIFO
                                                
    //用户控制端口                                
    .sdram_read_valid   (1'b1),                       //SDRAM 读使能
    .sdram_pingpang_en  (1'b1),                       //SDRAM 乒乓操作使能
    .sdram_init_done    (sdram_init_done),            //SDRAM 初始化完成标志
                                                
    //SDRAM 芯片接口                                
    .sdram_clk          (sdram_clk),                  //SDRAM 芯片时钟
    .sdram_cke          (sdram_cke),                  //SDRAM 时钟有效
    .sdram_cs_n         (sdram_cs_n),                 //SDRAM 片选
    .sdram_ras_n        (sdram_ras_n),                //SDRAM 行有效
    .sdram_cas_n        (sdram_cas_n),                //SDRAM 列有效
    .sdram_we_n         (sdram_we_n),                 //SDRAM 写有效
    .sdram_ba           (sdram_ba),                   //SDRAM Bank地址
    .sdram_addr         (sdram_addr),                 //SDRAM 行/列地址
    .sdram_data         (sdram_data),                 //SDRAM 数据
    .sdram_dqm          (sdram_dqm)                   //SDRAM 数据掩码
    );


//VGA驱动模块
vga_driver u_vga_driver(
    .vga_clk            (clk_65m),    
    .sys_rst_n          (rst_n),    
    
    .vga_hs             (vga_hs),       
    .vga_vs             (vga_vs),       
    .vga_rgb            (vga_rgb),      
        
    .pixel_data         (rd_data), 
    .data_req           (rd_en),                      //请求像素点颜色数据输入
    .pixel_xpos         (), 
    .pixel_ypos         ()
    ); 
   
endmodule

VGAドライバーモジュール:

説明:

3イン————5アウト

入力:

150万クロック

2.リセット

3. 受信した 16 ビット ピクセル画像の 1 ピクセル

出力:

1. VGAインターフェースに直接接続されたライン同期

2. VGA インターフェースに直接接続されたフィールド同期

3.VGAインターフェースに直接接続されたRGB16ビットピクセル出力

4. ピクセル横座標 これは通常、ピクセルが画像処理されるべき場所です。

5. ピクセル垂直座標 これは通常、ピクセルが画像処理されるべき場所です。

module vga_driver(
    input           vga_clk,      //VGA驱动时钟
    input           sys_rst_n,    //复位信号
    //VGA接口                          
    output          vga_hs,       //行同步信号
    output          vga_vs,       //场同步信号
    output  [15:0]  vga_rgb,      //红绿蓝三原色输出
    
    input   [15:0]  pixel_data,   //像素点数据
    output          data_req  ,   //请求像素点颜色数据输入 
    output  [ 9:0]  pixel_xpos,   //像素点横坐标
    output  [ 9:0]  pixel_ypos    //像素点纵坐标    
    );                             
                                                        
//parameter define  
parameter  H_SYNC   =  10'd96;    //行同步
parameter  H_BACK   =  10'd48;    //行显示后沿
parameter  H_DISP   =  10'd640;   //行有效数据
parameter  H_FRONT  =  10'd16;    //行显示前沿
parameter  H_TOTAL  =  10'd800;   //行扫描周期

parameter  V_SYNC   =  10'd2;     //场同步
parameter  V_BACK   =  10'd33;    //场显示后沿
parameter  V_DISP   =  10'd480;   //场有效数据
parameter  V_FRONT  =  10'd10;    //场显示前沿
parameter  V_TOTAL  =  10'd525;   //场扫描周期
          
//reg define                                     
reg  [9:0] cnt_h;
reg  [9:0] cnt_v;

//wire define
wire       vga_en;
wire       data_req; 

//*****************************************************
//**                    main code
//*****************************************************
//VGA行场同步信号
assign vga_hs  = (cnt_h <= H_SYNC - 1'b1) ? 1'b0 : 1'b1;
assign vga_vs  = (cnt_v <= V_SYNC - 1'b1) ? 1'b0 : 1'b1;

//使能RGB565数据输出
assign vga_en  = (((cnt_h >= H_SYNC+H_BACK) && (cnt_h < H_SYNC+H_BACK+H_DISP))
                 &&((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                 ?  1'b1 : 1'b0;
                 
//RGB565数据输出                 
assign vga_rgb = vga_en ? pixel_data : 16'd0;

//请求像素点颜色数据输入                
assign data_req = (((cnt_h >= H_SYNC+H_BACK-1'b1) && (cnt_h < H_SYNC+H_BACK+H_DISP-1'b1))
                  && ((cnt_v >= V_SYNC+V_BACK) && (cnt_v < V_SYNC+V_BACK+V_DISP)))
                  ?  1'b1 : 1'b0;

//像素点坐标                
assign pixel_xpos = data_req ? (cnt_h - (H_SYNC + H_BACK - 1'b1)) : 10'd0;
assign pixel_ypos = data_req ? (cnt_v - (V_SYNC + V_BACK - 1'b1)) : 10'd0;

//行计数器对像素时钟计数
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt_h <= 10'd0;                                  
    else begin
        if(cnt_h < H_TOTAL - 1'b1)                                               
            cnt_h <= cnt_h + 1'b1;                               
        else 
            cnt_h <= 10'd0;  
    end
end

//场计数器对行计数
always @(posedge vga_clk or negedge sys_rst_n) begin         
    if (!sys_rst_n)
        cnt_v <= 10'd0;                                  
    else if(cnt_h == H_TOTAL - 1'b1) begin
        if(cnt_v < V_TOTAL - 1'b1)                                               
            cnt_v <= cnt_v + 1'b1;                               
        else 
            cnt_v <= 10'd0;  
    end
end
endmodule 

OV5640カメラ

最上位ファイル:

module camera_top(
	input                 clk_65m      	  ,
	input                 rst_n    		  ,  
	
	//与SDRAM的接口
	input                 sdram_init_done ,
	output                cmos_frame_valid,  //数据有效使能信号
	output       [15:0]   cmos_frame_data ,   //有效数据        
	
	// 硬件输入
	input                 cam_pclk        ,  //cmos 数据像素时钟
	input                 cam_vsync       ,  //cmos 场同步信号
	input                 cam_href        ,  //cmos 行同步信号
	input        [7:0]    cam_data        ,  //cmos 数据 
	// 硬件输出
	output            	  cam_scl        	  ,      // I2C的SCL时钟信号
	inout                 cam_sda              // I2C的SDA信号
);

//parameter define
parameter  SLAVE_ADDR = 7'h3c         ;  //OV5640的器件地址7'h3c
parameter  BIT_CTRL   = 1'b1          ;  //OV5640的字节地址为16位  0:8位 1:16位
parameter  CLK_FREQ   = 26'd65_000_000;  //i2c_dri模块的驱动时钟频率 65MHz
parameter  I2C_FREQ   = 18'd250_000   ;  //I2C的SCL时钟频率,不超过400KHz
parameter  CMOS_H_PIXEL = 24'd1024    ;  //CMOS水平方向像素个数,用于设置SDRAM缓存大小
parameter  CMOS_V_PIXEL = 24'd768     ;  //CMOS垂直方向像素个数,用于设置SDRAM缓存大小

wire    [23:0]  i2c_data ;

assign  sys_init_done = sdram_init_done & cam_init_done;

//I2C配置模块
i2c_ov5640_rgb565_cfg 
   #(
     .CMOS_H_PIXEL      (CMOS_H_PIXEL),
     .CMOS_V_PIXEL      (CMOS_V_PIXEL)
    )   
   u_i2c_cfg(   
    .clk                (i2c_dri_clk),
    .rst_n              (rst_n),
    .i2c_done           (i2c_done),
    .i2c_exec           (i2c_exec),
    .i2c_data           (i2c_data),
    .init_done          (cam_init_done)
    );
	 
//I2C驱动模块
i2c_dri 
   #(
    .SLAVE_ADDR         (SLAVE_ADDR),       //参数传递
    .CLK_FREQ           (CLK_FREQ  ),              
    .I2C_FREQ           (I2C_FREQ  )                
    )   
   u_i2c_dri(   
    .clk                (clk_65m   ),
    .rst_n              (rst_n     ),   
        
    .i2c_exec           (i2c_exec  ),   
    .bit_ctrl           (BIT_CTRL  ),   
    .i2c_rh_wl          (1'b0),             //固定为0,只用到了IIC驱动的写操作   
    .i2c_addr           (i2c_data[23:8]),   
    .i2c_data_w         (i2c_data[7:0]),   
    .i2c_data_r         (),   
    .i2c_done           (i2c_done  ),   
    .scl                (cam_scl   ),   
    .sda                (cam_sda   ),   
        
    .dri_clk            (i2c_dri_clk)       //I2C操作时钟
);


//CMOS图像数据采集模块
cmos_capture_data u_cmos_capture_data(  //系统初始化完成之后再开始采集数据 
    .rst_n              (rst_n & sys_init_done), 		//只有当iic配置+SDRAM配置完成,才行
        
    .cam_pclk           (cam_pclk),
    .cam_vsync          (cam_vsync),
    .cam_href           (cam_href),
    .cam_data           (cam_data),
        
        
    .cmos_frame_vsync   (),
    .cmos_frame_href    (),
    .cmos_frame_valid   (cmos_frame_valid),       //数据有效使能信号
    .cmos_frame_data    (cmos_frame_data)      //有效数据 
    );

	 
endmodule

COMSモジュール:

module cmos_capture_data(
    input                 rst_n           ,  //复位信号
    //摄像头接口
    input                 cam_pclk        ,  //cmos 数据像素时钟
    input                 cam_vsync       ,  //cmos 场同步信号
    input                 cam_href        ,  //cmos 行同步信号
    input        [7:0]    cam_data        ,  //cmos 数据                             
    //用户接口
    output                cmos_frame_vsync,  //帧有效信号    
    output                cmos_frame_href ,  //行有效信号
    output                cmos_frame_valid,  //数据有效使能信号
    output       [15:0]   cmos_frame_data    //有效数据        
    );

//寄存器全部配置完成后,先等待10帧数据
//待寄存器配置生效后再开始采集图像
parameter  WAIT_FRAME = 4'd10  ;             //寄存器数据稳定等待的帧个数            

//reg define
reg             cam_vsync_d0   ;
reg             cam_vsync_d1   ;
reg             cam_href_d0    ;
reg             cam_href_d1    ;
reg    [3:0]    cmos_ps_cnt    ;             //等待帧数稳定计数器
reg             frame_val_flag ;             //帧有效的标志

reg    [7:0]    cam_data_d0    ;             
reg    [15:0]   cmos_data_t    ;             //用于8位转16位的临时寄存器
reg             byte_flag      ;             
reg             byte_flag_d0   ;

//wire define
wire            pos_vsync      ;

//*****************************************************
//**                    main code
//*****************************************************

//采输入场同步信号的上升沿
assign pos_vsync = (~cam_vsync_d1) & cam_vsync_d0;  

//输出帧有效信号
assign  cmos_frame_vsync = frame_val_flag  ?  cam_vsync_d1  :  1'b0; 
//输出行有效信号
assign  cmos_frame_href  = frame_val_flag  ?  cam_href_d1   :  1'b0; 
//输出数据使能有效信号
assign  cmos_frame_valid = frame_val_flag  ?  byte_flag_d0  :  1'b0; 
//输出数据
assign  cmos_frame_data  = frame_val_flag  ?  cmos_data_t   :  1'b0; 

//采输入场同步信号的上升沿
always @(posedge cam_pclk or negedge rst_n) begin
    if(!rst_n) begin
        cam_vsync_d0 <= 1'b0;
        cam_vsync_d1 <= 1'b0;
        cam_href_d0 <= 1'b0;
        cam_href_d1 <= 1'b0;
    end
    else begin
        cam_vsync_d0 <= cam_vsync;
        cam_vsync_d1 <= cam_vsync_d0;
        cam_href_d0 <= cam_href;
        cam_href_d1 <= cam_href_d0;
    end
end

//对帧数进行计数
always @(posedge cam_pclk or negedge rst_n) begin
    if(!rst_n)
        cmos_ps_cnt <= 4'd0;
    else if(pos_vsync && (cmos_ps_cnt < WAIT_FRAME))
        cmos_ps_cnt <= cmos_ps_cnt + 4'd1;
end

//帧有效标志
always @(posedge cam_pclk or negedge rst_n) begin
    if(!rst_n)
        frame_val_flag <= 1'b0;
    else if((cmos_ps_cnt == WAIT_FRAME) && pos_vsync)
        frame_val_flag <= 1'b1;
    else;    
end            

//8位数据转16位RGB565数据        
always @(posedge cam_pclk or negedge rst_n) begin
    if(!rst_n) begin
        cmos_data_t <= 16'd0;
        cam_data_d0 <= 8'd0;
        byte_flag <= 1'b0;
    end
    else if(cam_href) begin
        byte_flag <= ~byte_flag;
        cam_data_d0 <= cam_data;
        if(byte_flag)
            cmos_data_t <= {cam_data_d0,cam_data};
        else;   
    end
    else begin
        byte_flag <= 1'b0;
        cam_data_d0 <= 8'b0;
    end    
end        

//产生输出数据有效信号(cmos_frame_valid)
always @(posedge cam_pclk or negedge rst_n) begin
    if(!rst_n)
        byte_flag_d0 <= 1'b0;
    else
        byte_flag_d0 <= byte_flag;    
end          

endmodule 

カメラの cfg 構成:

module i2c_ov5640_rgb565_cfg
  #(
    parameter CMOS_H_PIXEL = 24'd1024,//CMOS水平方向像素个数
    parameter CMOS_V_PIXEL = 24'd768  //CMOS垂直方向像素个数
    )
   (  
    input                clk      ,   //时钟信号
    input                rst_n    ,   //复位信号,低电平有效
    
    input                i2c_done ,   //I2C寄存器配置完成信号
    output  reg          i2c_exec ,   //I2C触发执行信号
    output  reg  [23:0]  i2c_data ,   //I2C要配置的地址与数据(高16位地址,低8位数据)
    output  reg          init_done    //初始化完成信号
    );

//parameter define
localparam  REG_NUM = 8'd248  ;       //总共需要配置的寄存器个数
localparam  TOTAL_H_PIXEL = CMOS_H_PIXEL + 13'd1216;  //水平总像素大小
localparam  TOTAL_V_PIXEL = CMOS_V_PIXEL + 13'd504;   //垂直总像素大小

//reg define
reg   [14:0]   start_init_cnt;        //等待延时计数器
reg    [7:0]   init_reg_cnt  ;        //寄存器配置个数计数器

//*****************************************************
//**                    main code
//*****************************************************

//cam_scl配置成250khz,输入的clk为1Mhz,周期为1us,20000*1us = 20ms
//OV5640上电到开始配置IIC至少等待20ms
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        start_init_cnt <= 15'd0;
    else if(start_init_cnt < 15'd20000)
        start_init_cnt <= start_init_cnt + 1'b1;                    
end

//寄存器配置个数计数    
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        init_reg_cnt <= 8'd0;
    else if(i2c_exec)   
        init_reg_cnt <= init_reg_cnt + 8'b1;
end

//i2c触发执行信号   
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        i2c_exec <= 1'b0;
    else if(start_init_cnt == 15'd19999)
        i2c_exec <= 1'b1;
    else if(i2c_done && (init_reg_cnt < REG_NUM))
        i2c_exec <= 1'b1;
    else
        i2c_exec <= 1'b0;
end 

//初始化完成信号
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        init_done <= 1'b0;
    else if((init_reg_cnt == REG_NUM) && i2c_done)  
        init_done <= 1'b1;  
end

//配置寄存器地址与数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        i2c_data <= 24'd0;
    else begin
        case(init_reg_cnt)
            //先对寄存器进行软件复位,使寄存器恢复初始值
            8'd0  : i2c_data <= {16'h3008,8'h82}; //Bit[7]:复位 Bit[6]:电源休眠
            8'd1  : i2c_data <= {16'h3008,8'h02}; //正常工作模式
            8'd2  : i2c_data <= {16'h3103,8'h02}; //Bit[1]:1 PLL Clock
            //引脚输入/输出控制 FREX/VSYNC/HREF/PCLK/D[9:6]
            8'd3  : i2c_data <= {8'h30,8'h17,8'hff};
            //引脚输入/输出控制 D[5:0]/GPIO1/GPIO0 
            8'd4  : i2c_data <= {16'h3018,8'hff};
            8'd5  : i2c_data <= {16'h3037,8'h13}; //PLL分频控制
            8'd6  : i2c_data <= {16'h3108,8'h01}; //系统根分频器
            8'd7  : i2c_data <= {16'h3630,8'h36};
            8'd8  : i2c_data <= {16'h3631,8'h0e};
            8'd9  : i2c_data <= {16'h3632,8'he2};
            8'd10 : i2c_data <= {16'h3633,8'h12};
            8'd11 : i2c_data <= {16'h3621,8'he0};
            8'd12 : i2c_data <= {16'h3704,8'ha0};
            8'd13 : i2c_data <= {16'h3703,8'h5a};
            8'd14 : i2c_data <= {16'h3715,8'h78};
            8'd15 : i2c_data <= {16'h3717,8'h01};
            8'd16 : i2c_data <= {16'h370b,8'h60};
            8'd17 : i2c_data <= {16'h3705,8'h1a};
            8'd18 : i2c_data <= {16'h3905,8'h02};
            8'd19 : i2c_data <= {16'h3906,8'h10};
            8'd20 : i2c_data <= {16'h3901,8'h0a};
            8'd21 : i2c_data <= {16'h3731,8'h12};
            8'd22 : i2c_data <= {16'h3600,8'h08}; //VCM控制,用于自动聚焦
            8'd23 : i2c_data <= {16'h3601,8'h33}; //VCM控制,用于自动聚焦
            8'd24 : i2c_data <= {16'h302d,8'h60}; //系统控制
            8'd25 : i2c_data <= {16'h3620,8'h52};
            8'd26 : i2c_data <= {16'h371b,8'h20};
            8'd27 : i2c_data <= {16'h471c,8'h50};
            8'd28 : i2c_data <= {16'h3a13,8'h43}; //AEC(自动曝光控制)
            8'd29 : i2c_data <= {16'h3a18,8'h00}; //AEC 增益上限
            8'd30 : i2c_data <= {16'h3a19,8'hf8}; //AEC 增益上限
            8'd31 : i2c_data <= {16'h3635,8'h13};
            8'd32 : i2c_data <= {16'h3636,8'h03};
            8'd33 : i2c_data <= {16'h3634,8'h40};
            8'd34 : i2c_data <= {16'h3622,8'h01};
            8'd35 : i2c_data <= {16'h3c01,8'h34};
            8'd36 : i2c_data <= {16'h3c04,8'h28};
            8'd37 : i2c_data <= {16'h3c05,8'h98};
            8'd38 : i2c_data <= {16'h3c06,8'h00}; //light meter 1 阈值[15:8]
            8'd39 : i2c_data <= {16'h3c07,8'h08}; //light meter 1 阈值[7:0]
            8'd40 : i2c_data <= {16'h3c08,8'h00}; //light meter 2 阈值[15:8]
            8'd41 : i2c_data <= {16'h3c09,8'h1c}; //light meter 2 阈值[7:0]
            8'd42 : i2c_data <= {16'h3c0a,8'h9c}; //sample number[15:8]
            8'd43 : i2c_data <= {16'h3c0b,8'h40}; //sample number[7:0]
            8'd44 : i2c_data <= {16'h3810,8'h00}; //Timing Hoffset[11:8]
            8'd45 : i2c_data <= {16'h3811,8'h10}; //Timing Hoffset[7:0]
            8'd46 : i2c_data <= {16'h3812,8'h00}; //Timing Voffset[10:8]
            8'd47 : i2c_data <= {16'h3708,8'h64};
            8'd48 : i2c_data <= {16'h4001,8'h02}; //BLC(黑电平校准)补偿起始行号
            8'd49 : i2c_data <= {16'h4005,8'h1a}; //BLC(黑电平校准)补偿始终更新
            8'd50 : i2c_data <= {16'h3000,8'h00}; //系统块复位控制
            8'd51 : i2c_data <= {16'h3004,8'hff}; //时钟使能控制
            8'd52 : i2c_data <= {16'h4300,8'h61}; //格式控制 RGB565
            8'd53 : i2c_data <= {16'h501f,8'h01}; //ISP RGB
            8'd54 : i2c_data <= {16'h440e,8'h00};
            8'd55 : i2c_data <= {16'h5000,8'ha7}; //ISP控制
            8'd56 : i2c_data <= {16'h3a0f,8'h30}; //AEC控制;stable range in high
            8'd57 : i2c_data <= {16'h3a10,8'h28}; //AEC控制;stable range in low
            8'd58 : i2c_data <= {16'h3a1b,8'h30}; //AEC控制;stable range out high
            8'd59 : i2c_data <= {16'h3a1e,8'h26}; //AEC控制;stable range out low
            8'd60 : i2c_data <= {16'h3a11,8'h60}; //AEC控制; fast zone high
            8'd61 : i2c_data <= {16'h3a1f,8'h14}; //AEC控制; fast zone low
            //LENC(镜头校正)控制 16'h5800~16'h583d
            8'd62 : i2c_data <= {16'h5800,8'h23}; 
            8'd63 : i2c_data <= {16'h5801,8'h14};
            8'd64 : i2c_data <= {16'h5802,8'h0f};
            8'd65 : i2c_data <= {16'h5803,8'h0f};
            8'd66 : i2c_data <= {16'h5804,8'h12};
            8'd67 : i2c_data <= {16'h5805,8'h26};
            8'd68 : i2c_data <= {16'h5806,8'h0c};
            8'd69 : i2c_data <= {16'h5807,8'h08};
            8'd70 : i2c_data <= {16'h5808,8'h05};
            8'd71 : i2c_data <= {16'h5809,8'h05};
            8'd72 : i2c_data <= {16'h580a,8'h08};
            8'd73 : i2c_data <= {16'h580b,8'h0d};
            8'd74 : i2c_data <= {16'h580c,8'h08};
            8'd75 : i2c_data <= {16'h580d,8'h03};
            8'd76 : i2c_data <= {16'h580e,8'h00};
            8'd77 : i2c_data <= {16'h580f,8'h00};
            8'd78 : i2c_data <= {16'h5810,8'h03};
            8'd79 : i2c_data <= {16'h5811,8'h09};
            8'd80 : i2c_data <= {16'h5812,8'h07};
            8'd81 : i2c_data <= {16'h5813,8'h03};
            8'd82 : i2c_data <= {16'h5814,8'h00};
            8'd83 : i2c_data <= {16'h5815,8'h01};
            8'd84 : i2c_data <= {16'h5816,8'h03};
            8'd85 : i2c_data <= {16'h5817,8'h08};
            8'd86 : i2c_data <= {16'h5818,8'h0d};
            8'd87 : i2c_data <= {16'h5819,8'h08};
            8'd88 : i2c_data <= {16'h581a,8'h05};
            8'd89 : i2c_data <= {16'h581b,8'h06};
            8'd90 : i2c_data <= {16'h581c,8'h08};
            8'd91 : i2c_data <= {16'h581d,8'h0e};
            8'd92 : i2c_data <= {16'h581e,8'h29};
            8'd93 : i2c_data <= {16'h581f,8'h17};
            8'd94 : i2c_data <= {16'h5820,8'h11};
            8'd95 : i2c_data <= {16'h5821,8'h11};
            8'd96 : i2c_data <= {16'h5822,8'h15};
            8'd97 : i2c_data <= {16'h5823,8'h28};
            8'd98 : i2c_data <= {16'h5824,8'h46};
            8'd99 : i2c_data <= {16'h5825,8'h26};
            8'd100: i2c_data <= {16'h5826,8'h08};
            8'd101: i2c_data <= {16'h5827,8'h26};
            8'd102: i2c_data <= {16'h5828,8'h64};
            8'd103: i2c_data <= {16'h5829,8'h26};
            8'd104: i2c_data <= {16'h582a,8'h24};
            8'd105: i2c_data <= {16'h582b,8'h22};
            8'd106: i2c_data <= {16'h582c,8'h24};
            8'd107: i2c_data <= {16'h582d,8'h24};
            8'd108: i2c_data <= {16'h582e,8'h06};
            8'd109: i2c_data <= {16'h582f,8'h22};
            8'd110: i2c_data <= {16'h5830,8'h40};
            8'd111: i2c_data <= {16'h5831,8'h42};
            8'd112: i2c_data <= {16'h5832,8'h24};
            8'd113: i2c_data <= {16'h5833,8'h26};
            8'd114: i2c_data <= {16'h5834,8'h24};
            8'd115: i2c_data <= {16'h5835,8'h22};
            8'd116: i2c_data <= {16'h5836,8'h22};
            8'd117: i2c_data <= {16'h5837,8'h26};
            8'd118: i2c_data <= {16'h5838,8'h44};
            8'd119: i2c_data <= {16'h5839,8'h24};
            8'd120: i2c_data <= {16'h583a,8'h26};
            8'd121: i2c_data <= {16'h583b,8'h28};
            8'd122: i2c_data <= {16'h583c,8'h42};
            8'd123: i2c_data <= {16'h583d,8'hce};
            //AWB(自动白平衡控制) 16'h5180~16'h519e
            8'd124: i2c_data <= {16'h5180,8'hff};
            8'd125: i2c_data <= {16'h5181,8'hf2};
            8'd126: i2c_data <= {16'h5182,8'h00};
            8'd127: i2c_data <= {16'h5183,8'h14};
            8'd128: i2c_data <= {16'h5184,8'h25};
            8'd129: i2c_data <= {16'h5185,8'h24};
            8'd130: i2c_data <= {16'h5186,8'h09};
            8'd131: i2c_data <= {16'h5187,8'h09};
            8'd132: i2c_data <= {16'h5188,8'h09};
            8'd133: i2c_data <= {16'h5189,8'h75};
            8'd134: i2c_data <= {16'h518a,8'h54};
            8'd135: i2c_data <= {16'h518b,8'he0};
            8'd136: i2c_data <= {16'h518c,8'hb2};
            8'd137: i2c_data <= {16'h518d,8'h42};
            8'd138: i2c_data <= {16'h518e,8'h3d};
            8'd139: i2c_data <= {16'h518f,8'h56};
            8'd140: i2c_data <= {16'h5190,8'h46};
            8'd141: i2c_data <= {16'h5191,8'hf8};
            8'd142: i2c_data <= {16'h5192,8'h04};
            8'd143: i2c_data <= {16'h5193,8'h70};
            8'd144: i2c_data <= {16'h5194,8'hf0};
            8'd145: i2c_data <= {16'h5195,8'hf0};
            8'd146: i2c_data <= {16'h5196,8'h03};
            8'd147: i2c_data <= {16'h5197,8'h01};
            8'd148: i2c_data <= {16'h5198,8'h04};
            8'd149: i2c_data <= {16'h5199,8'h12};
            8'd150: i2c_data <= {16'h519a,8'h04};
            8'd151: i2c_data <= {16'h519b,8'h00};
            8'd152: i2c_data <= {16'h519c,8'h06};
            8'd153: i2c_data <= {16'h519d,8'h82};
            8'd154: i2c_data <= {16'h519e,8'h38};
            //Gamma(伽马)控制 16'h5480~16'h5490
            8'd155: i2c_data <= {16'h5480,8'h01}; 
            8'd156: i2c_data <= {16'h5481,8'h08};
            8'd157: i2c_data <= {16'h5482,8'h14};
            8'd158: i2c_data <= {16'h5483,8'h28};
            8'd159: i2c_data <= {16'h5484,8'h51};
            8'd160: i2c_data <= {16'h5485,8'h65};
            8'd161: i2c_data <= {16'h5486,8'h71};
            8'd162: i2c_data <= {16'h5487,8'h7d};
            8'd163: i2c_data <= {16'h5488,8'h87};
            8'd164: i2c_data <= {16'h5489,8'h91};
            8'd165: i2c_data <= {16'h548a,8'h9a};
            8'd166: i2c_data <= {16'h548b,8'haa};
            8'd167: i2c_data <= {16'h548c,8'hb8};
            8'd168: i2c_data <= {16'h548d,8'hcd};
            8'd169: i2c_data <= {16'h548e,8'hdd};
            8'd170: i2c_data <= {16'h548f,8'hea};
            8'd171: i2c_data <= {16'h5490,8'h1d};
            //CMX(彩色矩阵控制) 16'h5381~16'h538b
            8'd172: i2c_data <= {16'h5381,8'h1e};
            8'd173: i2c_data <= {16'h5382,8'h5b};
            8'd174: i2c_data <= {16'h5383,8'h08};
            8'd175: i2c_data <= {16'h5384,8'h0a};
            8'd176: i2c_data <= {16'h5385,8'h7e};
            8'd177: i2c_data <= {16'h5386,8'h88};
            8'd178: i2c_data <= {16'h5387,8'h7c};
            8'd179: i2c_data <= {16'h5388,8'h6c};
            8'd180: i2c_data <= {16'h5389,8'h10};
            8'd181: i2c_data <= {16'h538a,8'h01};
            8'd182: i2c_data <= {16'h538b,8'h98};
            //SDE(特殊数码效果)控制 16'h5580~16'h558b
            8'd183: i2c_data <= {16'h5580,8'h06};
            8'd184: i2c_data <= {16'h5583,8'h40};
            8'd185: i2c_data <= {16'h5584,8'h10};
            8'd186: i2c_data <= {16'h5589,8'h10};
            8'd187: i2c_data <= {16'h558a,8'h00};
            8'd188: i2c_data <= {16'h558b,8'hf8};
            8'd189: i2c_data <= {16'h501d,8'h40}; //ISP MISC
            //CIP(颜色插值)控制 (16'h5300~16'h530c)
            8'd190: i2c_data <= {16'h5300,8'h08};
            8'd191: i2c_data <= {16'h5301,8'h30};
            8'd192: i2c_data <= {16'h5302,8'h10};
            8'd193: i2c_data <= {16'h5303,8'h00};
            8'd194: i2c_data <= {16'h5304,8'h08};
            8'd195: i2c_data <= {16'h5305,8'h30};
            8'd196: i2c_data <= {16'h5306,8'h08};
            8'd197: i2c_data <= {16'h5307,8'h16};
            8'd198: i2c_data <= {16'h5309,8'h08};
            8'd199: i2c_data <= {16'h530a,8'h30};
            8'd200: i2c_data <= {16'h530b,8'h04};
            8'd201: i2c_data <= {16'h530c,8'h06};
            8'd202: i2c_data <= {16'h5025,8'h00};
            //系统时钟分频 Bit[7:4]:系统时钟分频 input clock =24Mhz, PCLK = 48Mhz
            8'd203: i2c_data <= {16'h3035,8'h11}; 
            8'd204: i2c_data <= {16'h3036,8'h3c}; //PLL倍频
            8'd205: i2c_data <= {16'h3c07,8'h08};
            //时序控制 16'h3800~16'h3821
            8'd206: i2c_data <= {16'h3820,8'h46};
            8'd207: i2c_data <= {16'h3821,8'h01};
            8'd208: i2c_data <= {16'h3814,8'h31};
            8'd209: i2c_data <= {16'h3815,8'h31};
            8'd210: i2c_data <= {16'h3800,8'h00};
            8'd211: i2c_data <= {16'h3801,8'h00};
            8'd212: i2c_data <= {16'h3802,8'h00};
            8'd213: i2c_data <= {16'h3803,8'h04};
            8'd214: i2c_data <= {16'h3804,8'h0a};
            8'd215: i2c_data <= {16'h3805,8'h3f};
            8'd216: i2c_data <= {16'h3806,8'h07};
            8'd217: i2c_data <= {16'h3807,8'h9b};

            //设置输出像素个数
            //DVP 输出水平像素点数高4位
            8'd218: i2c_data <= {16'h3808,{4'd0,CMOS_H_PIXEL[11:8]}};
            //DVP 输出水平像素点数低8位
            8'd219: i2c_data <= {16'h3809,CMOS_H_PIXEL[7:0]};
            //DVP 输出垂直像素点数高3位
            8'd220: i2c_data <= {16'h380a,{5'd0,CMOS_V_PIXEL[10:8]}};
            //DVP 输出垂直像素点数低8位
            8'd221: i2c_data <= {16'h380b,CMOS_V_PIXEL[7:0]};
            //水平总像素大小高5位
            8'd222: i2c_data <= {16'h380c,{3'd0,TOTAL_H_PIXEL[12:8]}};
            //水平总像素大小低8位 
            8'd223: i2c_data <= {16'h380d,TOTAL_H_PIXEL[7:0]};
            //垂直总像素大小高5位 
            8'd224: i2c_data <= {16'h380e,{3'd0,TOTAL_V_PIXEL[12:8]}};
            //垂直总像素大小低8位     
            8'd225: i2c_data <= {16'h380f,TOTAL_V_PIXEL[7:0]};

            8'd226: i2c_data <= {16'h3813,8'h06};
            8'd227: i2c_data <= {16'h3618,8'h00};
            8'd228: i2c_data <= {16'h3612,8'h29};
            8'd229: i2c_data <= {16'h3709,8'h52};
            8'd230: i2c_data <= {16'h370c,8'h03};
            8'd231: i2c_data <= {16'h3a02,8'h17}; //60Hz max exposure
            8'd232: i2c_data <= {16'h3a03,8'h10}; //60Hz max exposure
            8'd233: i2c_data <= {16'h3a14,8'h17}; //50Hz max exposure
            8'd234: i2c_data <= {16'h3a15,8'h10}; //50Hz max exposure
            8'd235: i2c_data <= {16'h4004,8'h02}; //BLC(背光) 2 lines
            8'd236: i2c_data <= {16'h4713,8'h03}; //JPEG mode 3
            8'd237: i2c_data <= {16'h4407,8'h04}; //量化标度
            8'd238: i2c_data <= {16'h460c,8'h22};     
            8'd239: i2c_data <= {16'h4837,8'h22}; //DVP CLK divider
            8'd240: i2c_data <= {16'h3824,8'h02}; //DVP CLK divider
            8'd241: i2c_data <= {16'h5001,8'ha3}; //ISP 控制
            8'd242: i2c_data <= {16'h3b07,8'h0a}; //帧曝光模式  
            //彩条测试使能 
            8'd243: i2c_data <= {16'h503d,8'h00}; //8'h00:正常模式 8'h80:彩条显示
            //测试闪光灯功能
            8'd244: i2c_data <= {16'h3016,8'h02};
            8'd245: i2c_data <= {16'h301c,8'h02};
            8'd246: i2c_data <= {16'h3019,8'h02}; //打开闪光灯
            8'd247: i2c_data <= {16'h3019,8'h00}; //关闭闪光灯
            //只读存储器,防止在case中没有列举的情况,之前的寄存器被重复改写
            default : i2c_data <= {16'h300a,8'h00}; //器件ID高8位
        endcase
    end
end

endmodule

IIc モジュール:

module i2c_dri
    #(// slave address(器件地址),放此处方便参数传递
      parameter   SLAVE_ADDR =  7'b1010000   ,
      parameter   CLK_FREQ   = 26'd50_000_000,   // i2c_dri模块的驱动时钟频率(CLK_FREQ)
      parameter   I2C_FREQ   = 18'd250_000       // I2C的SCL时钟频率
     )(
          //global clock
          input                clk        ,      // i2c_dri模块的驱动时钟(CLK_FREQ)
          input                rst_n      ,      // 复位信号

          //i2c interface
          input                i2c_exec   ,      // I2C触发执行信号
          input                bit_ctrl   ,      // 字地址位控制(16b/8b)
          input                i2c_rh_wl  ,      // I2C读写控制信号
          input        [15:0]  i2c_addr   ,      // I2C器件内地址
          input        [ 7:0]  i2c_data_w ,      // I2C要写的数据
          output  reg  [ 7:0]  i2c_data_r ,      // I2C读出的数据
          output  reg          i2c_done   ,      // I2C一次操作完成
          output  reg          scl        ,      // I2C的SCL时钟信号
          inout                sda        ,      // I2C的SDA信号

          //user interface
          output  reg          dri_clk           // 驱动I2C操作的驱动时钟
     );

//localparam define
localparam  st_idle     = 8'b0000_0001;          // 空闲状态
localparam  st_sladdr   = 8'b0000_0010;          // 发送器件地址(slave address)
localparam  st_addr16   = 8'b0000_0100;          // 发送16位字地址
localparam  st_addr8    = 8'b0000_1000;          // 发送8位字地址
localparam  st_data_wr  = 8'b0001_0000;          // 写数据(8 bit)
localparam  st_addr_rd  = 8'b0010_0000;          // 发送器件地址读
localparam  st_data_rd  = 8'b0100_0000;          // 读数据(8 bit)
localparam  st_stop     = 8'b1000_0000;          // 结束I2C操作

//reg define
reg            sda_dir     ;                     // I2C数据(SDA)方向控制
reg            sda_out     ;                     // SDA输出信号
reg            st_done     ;                     // 状态结束
reg            wr_flag     ;                     // 写标志
reg    [ 6:0]  cnt         ;                     // 计数
reg    [ 7:0]  cur_state   ;                     // 状态机当前状态
reg    [ 7:0]  next_state  ;                     // 状态机下一状态
reg    [15:0]  addr_t      ;                     // 地址
reg    [ 7:0]  data_r      ;                     // 读取的数据
reg    [ 7:0]  data_wr_t   ;                     // I2C需写的数据的临时寄存
reg    [ 9:0]  clk_cnt     ;                     // 分频时钟计数

//wire define
wire          sda_in      ;                      // SDA输入信号
wire   [8:0]  clk_divide  ;                      // 模块驱动时钟的分频系数

//*****************************************************
//**                    main code
//*****************************************************

//SDA控制
assign  sda     = sda_dir ?  sda_out : 1'bz;     // SDA数据输出或高阻
assign  sda_in  = sda ;                          // SDA数据输入
assign  clk_divide = (CLK_FREQ/I2C_FREQ) >> 3;   // 模块驱动时钟的分频系数

//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        dri_clk <=  1'b1;
        clk_cnt <= 10'd0;
    end
    else if(clk_cnt == clk_divide - 1'd1) begin
        clk_cnt <= 10'd0;
        dri_clk <= ~dri_clk;
    end
    else
        clk_cnt <= clk_cnt + 1'b1;
end

//(三段式状态机)同步时序描述状态转移
always @(posedge dri_clk or negedge rst_n) begin
    if(!rst_n)
        cur_state <= st_idle;
    else
        cur_state <= next_state;
end

//组合逻辑判断状态转移条件
always @( * ) begin
//    next_state = st_idle;
    case(cur_state)
        st_idle: begin                           // 空闲状态
           if(i2c_exec) begin
               next_state = st_sladdr;
           end
           else
               next_state = st_idle;
        end
        st_sladdr: begin
            if(st_done) begin
                if(bit_ctrl)                     // 判断是16位还是8位字地址
                   next_state = st_addr16;
                else
                   next_state = st_addr8 ;
            end
            else
                next_state = st_sladdr;
        end
        st_addr16: begin                         // 写16位字地址
            if(st_done) begin
                next_state = st_addr8;
            end
            else begin
                next_state = st_addr16;
            end
        end
        st_addr8: begin                          // 8位字地址
            if(st_done) begin
                if(wr_flag==1'b0)                // 读写判断
                    next_state = st_data_wr;
                else
                    next_state = st_addr_rd;
            end
            else begin
                next_state = st_addr8;
            end
        end
        st_data_wr: begin                        // 写数据(8 bit)
            if(st_done)
                next_state = st_stop;
            else
                next_state = st_data_wr;
        end
        st_addr_rd: begin                        // 写地址以进行读数据
            if(st_done) begin
                next_state = st_data_rd;
            end
            else begin
                next_state = st_addr_rd;
            end
        end
        st_data_rd: begin                        // 读取数据(8 bit)
            if(st_done)
                next_state = st_stop;
            else
                next_state = st_data_rd;
        end
        st_stop: begin                           // 结束I2C操作
            if(st_done)
                next_state = st_idle;
            else
                next_state = st_stop ;
        end
        default: next_state= st_idle;
    endcase
end

//时序电路描述状态输出
always @(posedge dri_clk or negedge rst_n) begin
    //复位初始化
    if(!rst_n) begin
        scl        <= 1'b1;
        sda_out    <= 1'b1;
        sda_dir    <= 1'b1;
        i2c_done   <= 1'b0;
        cnt        <= 1'b0;
        st_done    <= 1'b0;
        data_r     <= 1'b0;
        i2c_data_r <= 1'b0;
        wr_flag    <= 1'b0;
        addr_t     <= 1'b0;
        data_wr_t  <= 1'b0;
    end
    else begin
        st_done <= 1'b0 ;
        cnt     <= cnt +1'b1 ;
        case(cur_state)
             st_idle: begin                            // 空闲状态
                scl     <= 1'b1;
                sda_out <= 1'b1;
                sda_dir <= 1'b1;
                i2c_done<= 1'b0;
                cnt     <= 7'b0;
                if(i2c_exec) begin
                    wr_flag   <= i2c_rh_wl ;
                    addr_t    <= i2c_addr  ;
                    data_wr_t <= i2c_data_w;
                end
            end
            st_sladdr: begin                           // 写地址(器件地址和字地址)
                case(cnt)
                    7'd1 : sda_out <= 1'b0;            // 开始I2C
                    7'd3 : scl <= 1'b0;
                    7'd4 : sda_out <= SLAVE_ADDR[6];   // 传送器件地址
                    7'd5 : scl <= 1'b1;
                    7'd7 : scl <= 1'b0;
                    7'd8 : sda_out <= SLAVE_ADDR[5];
                    7'd9 : scl <= 1'b1;
                    7'd11: scl <= 1'b0;
                    7'd12: sda_out <= SLAVE_ADDR[4];
                    7'd13: scl <= 1'b1;
                    7'd15: scl <= 1'b0;
                    7'd16: sda_out <= SLAVE_ADDR[3];
                    7'd17: scl <= 1'b1;
                    7'd19: scl <= 1'b0;
                    7'd20: sda_out <= SLAVE_ADDR[2];
                    7'd21: scl <= 1'b1;
                    7'd23: scl <= 1'b0;
                    7'd24: sda_out <= SLAVE_ADDR[1];
                    7'd25: scl <= 1'b1;
                    7'd27: scl <= 1'b0;
                    7'd28: sda_out <= SLAVE_ADDR[0];
                    7'd29: scl <= 1'b1;
                    7'd31: scl <= 1'b0;
                    7'd32: sda_out <= 1'b0;            // 0:写
                    7'd33: scl <= 1'b1;
                    7'd35: scl <= 1'b0;
                    7'd36: begin
                        sda_dir <= 1'b0;               // 从机应答
                        sda_out <= 1'b1;
                    end
                    7'd37: scl     <= 1'b1;
                    7'd38: st_done <= 1'b1;
                    7'd39: begin
                        scl <= 1'b0;
                        cnt <= 1'b0;
                    end
                    default :  ;
                endcase
            end
            st_addr16: begin
                case(cnt)
                    7'd0 : begin
                        sda_dir <= 1'b1 ;
                        sda_out <= addr_t[15];         // 传送字地址
                    end
                    7'd1 : scl <= 1'b1;
                    7'd3 : scl <= 1'b0;
                    7'd4 : sda_out <= addr_t[14];
                    7'd5 : scl <= 1'b1;
                    7'd7 : scl <= 1'b0;
                    7'd8 : sda_out <= addr_t[13];
                    7'd9 : scl <= 1'b1;
                    7'd11: scl <= 1'b0;
                    7'd12: sda_out <= addr_t[12];
                    7'd13: scl <= 1'b1;
                    7'd15: scl <= 1'b0;
                    7'd16: sda_out <= addr_t[11];
                    7'd17: scl <= 1'b1;
                    7'd19: scl <= 1'b0;
                    7'd20: sda_out <= addr_t[10];
                    7'd21: scl <= 1'b1;
                    7'd23: scl <= 1'b0;
                    7'd24: sda_out <= addr_t[9];
                    7'd25: scl <= 1'b1;
                    7'd27: scl <= 1'b0;
                    7'd28: sda_out <= addr_t[8];
                    7'd29: scl <= 1'b1;
                    7'd31: scl <= 1'b0;
                    7'd32: begin
                        sda_dir <= 1'b0;               // 从机应答
                        sda_out <= 1'b1;
                    end
                    7'd33: scl     <= 1'b1;
                    7'd34: st_done <= 1'b1;
                    7'd35: begin
                        scl <= 1'b0;
                        cnt <= 1'b0;
                    end
                    default :  ;
                endcase
            end
            st_addr8: begin
                case(cnt)
                    7'd0: begin
                       sda_dir <= 1'b1 ;
                       sda_out <= addr_t[7];           // 字地址
                    end
                    7'd1 : scl <= 1'b1;
                    7'd3 : scl <= 1'b0;
                    7'd4 : sda_out <= addr_t[6];
                    7'd5 : scl <= 1'b1;
                    7'd7 : scl <= 1'b0;
                    7'd8 : sda_out <= addr_t[5];
                    7'd9 : scl <= 1'b1;
                    7'd11: scl <= 1'b0;
                    7'd12: sda_out <= addr_t[4];
                    7'd13: scl <= 1'b1;
                    7'd15: scl <= 1'b0;
                    7'd16: sda_out <= addr_t[3];
                    7'd17: scl <= 1'b1;
                    7'd19: scl <= 1'b0;
                    7'd20: sda_out <= addr_t[2];
                    7'd21: scl <= 1'b1;
                    7'd23: scl <= 1'b0;
                    7'd24: sda_out <= addr_t[1];
                    7'd25: scl <= 1'b1;
                    7'd27: scl <= 1'b0;
                    7'd28: sda_out <= addr_t[0];
                    7'd29: scl <= 1'b1;
                    7'd31: scl <= 1'b0;
                    7'd32: begin
                        sda_dir <= 1'b0;               // 从机应答
                        sda_out <= 1'b1;
                    end
                    7'd33: scl     <= 1'b1;
                    7'd34: st_done <= 1'b1;
                    7'd35: begin
                        scl <= 1'b0;
                        cnt <= 1'b0;
                    end
                    default :  ;
                endcase
            end
            st_data_wr: begin                          // 写数据(8 bit)
                case(cnt)
                    7'd0: begin
                        sda_out <= data_wr_t[7];       // I2C写8位数据
                        sda_dir <= 1'b1;
                    end
                    7'd1 : scl <= 1'b1;
                    7'd3 : scl <= 1'b0;
                    7'd4 : sda_out <= data_wr_t[6];
                    7'd5 : scl <= 1'b1;
                    7'd7 : scl <= 1'b0;
                    7'd8 : sda_out <= data_wr_t[5];
                    7'd9 : scl <= 1'b1;
                    7'd11: scl <= 1'b0;
                    7'd12: sda_out <= data_wr_t[4];
                    7'd13: scl <= 1'b1;
                    7'd15: scl <= 1'b0;
                    7'd16: sda_out <= data_wr_t[3];
                    7'd17: scl <= 1'b1;
                    7'd19: scl <= 1'b0;
                    7'd20: sda_out <= data_wr_t[2];
                    7'd21: scl <= 1'b1;
                    7'd23: scl <= 1'b0;
                    7'd24: sda_out <= data_wr_t[1];
                    7'd25: scl <= 1'b1;
                    7'd27: scl <= 1'b0;
                    7'd28: sda_out <= data_wr_t[0];
                    7'd29: scl <= 1'b1;
                    7'd31: scl <= 1'b0;
                    7'd32: begin
                        sda_dir <= 1'b0;               // 从机应答
                        sda_out <= 1'b1;
                    end
                    7'd33: scl <= 1'b1;
                    7'd34: st_done <= 1'b1;
                    7'd35: begin
                        scl  <= 1'b0;
                        cnt  <= 1'b0;
                    end
                    default  :  ;
                endcase
            end
            st_addr_rd: begin                          // 写地址以进行读数据
                case(cnt)
                    7'd0 : begin
                        sda_dir <= 1'b1;
                        sda_out <= 1'b1;
                    end
                    7'd1 : scl <= 1'b1;
                    7'd2 : sda_out <= 1'b0;            // 重新开始
                    7'd3 : scl <= 1'b0;
                    7'd4 : sda_out <= SLAVE_ADDR[6];   // 传送器件地址
                    7'd5 : scl <= 1'b1;
                    7'd7 : scl <= 1'b0;
                    7'd8 : sda_out <= SLAVE_ADDR[5];
                    7'd9 : scl <= 1'b1;
                    7'd11: scl <= 1'b0;
                    7'd12: sda_out <= SLAVE_ADDR[4];
                    7'd13: scl <= 1'b1;
                    7'd15: scl <= 1'b0;
                    7'd16: sda_out <= SLAVE_ADDR[3];
                    7'd17: scl <= 1'b1;
                    7'd19: scl <= 1'b0;
                    7'd20: sda_out <= SLAVE_ADDR[2];
                    7'd21: scl <= 1'b1;
                    7'd23: scl <= 1'b0;
                    7'd24: sda_out <= SLAVE_ADDR[1];
                    7'd25: scl <= 1'b1;
                    7'd27: scl <= 1'b0;
                    7'd28: sda_out <= SLAVE_ADDR[0];
                    7'd29: scl <= 1'b1;
                    7'd31: scl <= 1'b0;
                    7'd32: sda_out <= 1'b1;            // 1:读
                    7'd33: scl <= 1'b1;
                    7'd35: scl <= 1'b0;
                    7'd36: begin
                        sda_dir <= 1'b0;               // 从机应答
                        sda_out <= 1'b1;
                    end
                    7'd37: scl     <= 1'b1;
                    7'd38: st_done <= 1'b1;
                    7'd39: begin
                        scl <= 1'b0;
                        cnt <= 1'b0;
                    end
                    default : ;
                endcase
            end
            st_data_rd: begin                          // 读取数据(8 bit)
                case(cnt)
                    7'd0: sda_dir <= 1'b0;
                    7'd1: begin
                        data_r[7] <= sda_in;
                        scl       <= 1'b1;
                    end
                    7'd3: scl  <= 1'b0;
                    7'd5: begin
                        data_r[6] <= sda_in ;
                        scl       <= 1'b1   ;
                    end
                    7'd7: scl  <= 1'b0;
                    7'd9: begin
                        data_r[5] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd11: scl  <= 1'b0;
                    7'd13: begin
                        data_r[4] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd15: scl  <= 1'b0;
                    7'd17: begin
                        data_r[3] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd19: scl  <= 1'b0;
                    7'd21: begin
                        data_r[2] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd23: scl  <= 1'b0;
                    7'd25: begin
                        data_r[1] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd27: scl  <= 1'b0;
                    7'd29: begin
                        data_r[0] <= sda_in;
                        scl       <= 1'b1  ;
                    end
                    7'd31: scl  <= 1'b0;
                    7'd32: begin
                        sda_dir <= 1'b1;              // 非应答
                        sda_out <= 1'b1;
                    end
                    7'd33: scl     <= 1'b1;
                    7'd34: st_done <= 1'b1;
                    7'd35: begin
                        scl <= 1'b0;
                        cnt <= 1'b0;
                        i2c_data_r <= data_r;
                    end
                    default  :  ;
                endcase
            end
            st_stop: begin                            // 结束I2C操作
                case(cnt)
                    7'd0: begin
                        sda_dir <= 1'b1;              // 结束I2C
                        sda_out <= 1'b0;
                    end
                    7'd1 : scl     <= 1'b1;
                    7'd3 : sda_out <= 1'b1;
                    7'd15: st_done <= 1'b1;
                    7'd16: begin
                        cnt      <= 1'b0;
                        i2c_done <= 1'b1;             // 向上层模块传递I2C结束信号
                    end
                    default  : ;
                endcase
            end
        endcase
    end
end

endmodule

SDRAMモジュール:

合計 9 ファイル: 3 つのトップレイヤー、2 つの FIFO、3 つのステータス制御コマンド、および 1 つのパラメーター

TOPモジュール

module  sdram_top(
    input         ref_clk,                  //sdram 控制器参考时钟
    input         out_clk,                  //用于输出的相位偏移时钟
    input         rst_n,                    //系统复位
    
    //用户写端口         
    input         wr_clk,                   //写端口FIFO: 写时钟
    input         wr_en,                    //写端口FIFO: 写使能
    input  [15:0] wr_data,                  //写端口FIFO: 写数据
    input  [23:0] wr_min_addr,              //写SDRAM的起始地址
    input  [23:0] wr_max_addr,              //写SDRAM的结束地址
    input  [ 9:0] wr_len,                   //写SDRAM时的数据突发长度
    input         wr_load,                  //写端口复位: 复位写地址,清空写FIFO
    
    //用户读端口
    input         rd_clk,                   //读端口FIFO: 读时钟
    input         rd_en,                    //读端口FIFO: 读使能
    output [15:0] rd_data,                  //读端口FIFO: 读数据
    input  [23:0] rd_min_addr,              //读SDRAM的起始地址
    input  [23:0] rd_max_addr,              //读SDRAM的结束地址
    input  [ 9:0] rd_len,                   //从SDRAM中读数据时的突发长度
    input         rd_load,                  //读端口复位: 复位读地址,清空读FIFO
    
    //用户控制端口  
    input         sdram_read_valid,         //SDRAM 读使能
    input         sdram_pingpang_en,        //SDRAM 乒乓操作使能
    output        sdram_init_done,          //SDRAM 初始化完成标志
    
    //SDRAM 芯片接口
    output        sdram_clk,                //SDRAM 芯片时钟
    output        sdram_cke,                //SDRAM 时钟有效
    output        sdram_cs_n,               //SDRAM 片选
    output        sdram_ras_n,              //SDRAM 行有效
    output        sdram_cas_n,              //SDRAM 列有效
    output        sdram_we_n,               //SDRAM 写有效
    output [ 1:0] sdram_ba,                 //SDRAM Bank地址
    output [12:0] sdram_addr,               //SDRAM 行/列地址
    inout  [15:0] sdram_data,               //SDRAM 数据
    output [ 1:0] sdram_dqm                 //SDRAM 数据掩码
    );

//wire define
wire        sdram_wr_req;                   //sdram 写请求
wire        sdram_wr_ack;                   //sdram 写响应
wire [23:0] sdram_wr_addr;                  //sdram 写地址
wire [15:0] sdram_din;                      //写入sdram中的数据

wire        sdram_rd_req;                   //sdram 读请求
wire        sdram_rd_ack;                   //sdram 读响应
wire [23:0] sdram_rd_addr;                   //sdram 读地址
wire [15:0] sdram_dout;                     //从sdram中读出的数据

//*****************************************************
//**                    main code
//***************************************************** 
assign  sdram_clk = out_clk;                //将相位偏移时钟输出给sdram芯片
assign  sdram_dqm = 2'b00;                  //读写过程中均不屏蔽数据线
            
//SDRAM 读写端口FIFO控制模块
sdram_fifo_ctrl u_sdram_fifo_ctrl(
    .clk_ref            (ref_clk),          //SDRAM控制器时钟
    .rst_n              (rst_n),            //系统复位

    //用户写端口
    .clk_write          (wr_clk),           //写端口FIFO: 写时钟
    .wrf_wrreq          (wr_en),            //写端口FIFO: 写请求
    .wrf_din            (wr_data),          //写端口FIFO: 写数据  
    .wr_min_addr        (wr_min_addr),      //写SDRAM的起始地址
    .wr_max_addr        (wr_max_addr),      //写SDRAM的结束地址
    .wr_length          (wr_len),           //写SDRAM时的数据突发长度
    .wr_load            (wr_load),          //写端口复位: 复位写地址,清空写FIFO    
    
    //用户读端口                                                 
    .clk_read           (rd_clk),           //读端口FIFO: 读时钟
    .rdf_rdreq          (rd_en),            //读端口FIFO: 读请求
    .rdf_dout           (rd_data),          //读端口FIFO: 读数据
    .rd_min_addr        (rd_min_addr),      //读SDRAM的起始地址
    .rd_max_addr        (rd_max_addr),      //读SDRAM的结束地址
    .rd_length          (rd_len),           //从SDRAM中读数据时的突发长度
    .rd_load            (rd_load),          //读端口复位: 复位读地址,清空读FIFO
   
    //用户控制端口    
    .sdram_read_valid   (sdram_read_valid), //sdram 读使能
    .sdram_init_done    (sdram_init_done),  //sdram 初始化完成标志
    .sdram_pingpang_en  (sdram_pingpang_en),//sdram 乒乓操作使能
    
    //SDRAM 控制器写端口
    .sdram_wr_req       (sdram_wr_req),     //sdram 写请求
    .sdram_wr_ack       (sdram_wr_ack),     //sdram 写响应
    .sdram_wr_addr      (sdram_wr_addr),    //sdram 写地址
    .sdram_din          (sdram_din),        //写入sdram中的数据
    
    //SDRAM 控制器读端口
    .sdram_rd_req       (sdram_rd_req),     //sdram 读请求
    .sdram_rd_ack       (sdram_rd_ack),     //sdram 读响应
    .sdram_rd_addr      (sdram_rd_addr),    //sdram 读地址
    .sdram_dout         (sdram_dout)        //从sdram中读出的数据
    );

//SDRAM控制器
sdram_controller u_sdram_controller(
    .clk                (ref_clk),          //sdram 控制器时钟
    .rst_n              (rst_n),            //系统复位
    
    //SDRAM 控制器写端口  
    .sdram_wr_req       (sdram_wr_req),     //sdram 写请求
    .sdram_wr_ack       (sdram_wr_ack),     //sdram 写响应
    .sdram_wr_addr      (sdram_wr_addr),    //sdram 写地址
    .sdram_wr_burst     (wr_len),           //写sdram时数据突发长度
    .sdram_din          (sdram_din),        //写入sdram中的数据
    
    //SDRAM 控制器读端口
    .sdram_rd_req       (sdram_rd_req),     //sdram 读请求
    .sdram_rd_ack       (sdram_rd_ack),     //sdram 读响应
    .sdram_rd_addr      (sdram_rd_addr),    //sdram 读地址
    .sdram_rd_burst     (rd_len),           //读sdram时数据突发长度
    .sdram_dout         (sdram_dout),       //从sdram中读出的数据
    
    .sdram_init_done    (sdram_init_done),  //sdram 初始化完成标志

    //SDRAM 芯片接口
    .sdram_cke          (sdram_cke),        //SDRAM 时钟有效
    .sdram_cs_n         (sdram_cs_n),       //SDRAM 片选
    .sdram_ras_n        (sdram_ras_n),      //SDRAM 行有效 
    .sdram_cas_n        (sdram_cas_n),      //SDRAM 列有效
    .sdram_we_n         (sdram_we_n),       //SDRAM 写有效
    .sdram_ba           (sdram_ba),         //SDRAM Bank地址
    .sdram_addr         (sdram_addr),       //SDRAM 行/列地址
    .sdram_data         (sdram_data)        //SDRAM 数据  
    );
    
endmodule 

FIFO制御モジュール:

module sdram_fifo_ctrl(
    input             clk_ref,           //SDRAM控制器时钟
    input             rst_n,             //系统复位 
                                         
    //用户写端口                         
    input             clk_write,         //写端口FIFO: 写时钟 
    input             wrf_wrreq,         //写端口FIFO: 写请求 
    input      [15:0] wrf_din,           //写端口FIFO: 写数据 
    input      [23:0] wr_min_addr,       //写SDRAM的起始地址
    input      [23:0] wr_max_addr,       //写SDRAM的结束地址
    input      [ 9:0] wr_length,         //写SDRAM时的数据突发长度 
    input             wr_load,           //写端口复位: 复位写地址,清空写FIFO 
                                         
    //用户读端口                         
    input             clk_read,          //读端口FIFO: 读时钟
    input             rdf_rdreq,         //读端口FIFO: 读请求 
    output     [15:0] rdf_dout,          //读端口FIFO: 读数据
    input      [23:0] rd_min_addr,       //读SDRAM的起始地址
    input      [23:0] rd_max_addr,       //读SDRAM的结束地址
    input      [ 9:0] rd_length,         //从SDRAM中读数据时的突发长度 
    input             rd_load,           //读端口复位: 复位读地址,清空读FIFO
                                         
    //用户控制端口                         
    input             sdram_read_valid,  //SDRAM 读使能
    input             sdram_init_done,   //SDRAM 初始化完成标志
    input             sdram_pingpang_en, //SDRAM 乒乓操作使能
                                         
    //SDRAM 控制器写端口                 
    output reg        sdram_wr_req,      //sdram 写请求
    input             sdram_wr_ack,      //sdram 写响应
    output reg [23:0] sdram_wr_addr,     //sdram 写地址
    output     [15:0] sdram_din,         //写入SDRAM中的数据 
                                         
    //SDRAM 控制器读端口                 
    output reg        sdram_rd_req,      //sdram 读请求
    input             sdram_rd_ack,      //sdram 读响应
    output reg [23:0] sdram_rd_addr,     //sdram 读地址 
    input      [15:0] sdram_dout         //从SDRAM中读出的数据 
    );

//reg define
reg        wr_ack_r1;                    //sdram写响应寄存器      
reg        wr_ack_r2;                    
reg        rd_ack_r1;                    //sdram读响应寄存器      
reg        rd_ack_r2;                    
reg        wr_load_r1;                   //写端口复位寄存器      
reg        wr_load_r2;                   
reg        rd_load_r1;                   //读端口复位寄存器      
reg        rd_load_r2;                   
reg        read_valid_r1;                //sdram读使能寄存器      
reg        read_valid_r2;                
reg        sw_bank_en;                   //切换BANK使能信号
reg        rw_bank_flag;                 //读写bank的标志
                                         
//wire define                            
wire       write_done_flag;              //sdram_wr_ack 下降沿标志位      
wire       read_done_flag;               //sdram_rd_ack 下降沿标志位      
wire       wr_load_flag;                 //wr_load      上升沿标志位      
wire       rd_load_flag;                 //rd_load      上升沿标志位      
wire [9:0] wrf_use;                      //写端口FIFO中的数据量
wire [9:0] rdf_use;                      //读端口FIFO中的数据量

//*****************************************************
//**                    main code
//***************************************************** 

//检测下降沿
assign write_done_flag = wr_ack_r2   & ~wr_ack_r1;  
assign read_done_flag  = rd_ack_r2   & ~rd_ack_r1;

//检测上升沿
assign wr_load_flag    = ~wr_load_r2 & wr_load_r1;
assign rd_load_flag    = ~rd_load_r2 & rd_load_r1;

//寄存sdram写响应信号,用于捕获sdram_wr_ack下降沿
always @(posedge clk_ref or negedge rst_n) begin
    if(!rst_n) begin
        wr_ack_r1 <= 1'b0;
        wr_ack_r2 <= 1'b0;
    end
    else begin
        wr_ack_r1 <= sdram_wr_ack;
        wr_ack_r2 <= wr_ack_r1;     
    end
end 

//寄存sdram读响应信号,用于捕获sdram_rd_ack下降沿
always @(posedge clk_ref or negedge rst_n) begin
    if(!rst_n) begin
        rd_ack_r1 <= 1'b0;
        rd_ack_r2 <= 1'b0;
    end
    else begin
        rd_ack_r1 <= sdram_rd_ack;
        rd_ack_r2 <= rd_ack_r1;
    end
end 

//同步写端口复位信号,用于捕获wr_load上升沿
always @(posedge clk_ref or negedge rst_n) begin
    if(!rst_n) begin
        wr_load_r1 <= 1'b0;
        wr_load_r2 <= 1'b0;
    end
    else begin
        wr_load_r1 <= wr_load;
        wr_load_r2 <= wr_load_r1;
    end
end

//同步读端口复位信号,同时用于捕获rd_load上升沿
always @(posedge clk_ref or negedge rst_n) begin
    if(!rst_n) begin
        rd_load_r1 <= 1'b0;
        rd_load_r2 <= 1'b0;
    end
    else begin
        rd_load_r1 <= rd_load;
        rd_load_r2 <= rd_load_r1;
    end
end

//同步sdram读使能信号
always @(posedge clk_ref or negedge rst_n) begin
    if(!rst_n) begin
        read_valid_r1 <= 1'b0;
        read_valid_r2 <= 1'b0;
    end
    else begin
        read_valid_r1 <= sdram_read_valid;
        read_valid_r2 <= read_valid_r1;
    end
end

//sdram写地址产生模块
always @(posedge clk_ref or negedge rst_n) begin
    if (!rst_n) begin
        sdram_wr_addr <= 24'd0;
        sw_bank_en <= 1'b0;
        rw_bank_flag <= 1'b0;
    end
    else if(wr_load_flag) begin              //检测到写端口复位信号时,写地址复位
        sdram_wr_addr <= wr_min_addr;   
        sw_bank_en <= 1'b0;
        rw_bank_flag <= 1'b0;
    end
    else if(write_done_flag) begin           //若突发写SDRAM结束,更改写地址
                                             //若未到达写SDRAM的结束地址,则写地址累加
        if(sdram_pingpang_en) begin          //SDRAM 读写乒乓使能
            if(sdram_wr_addr[22:0] < wr_max_addr - wr_length)
                sdram_wr_addr <= sdram_wr_addr + wr_length;
            else begin                       //切换BANK
                rw_bank_flag <= ~rw_bank_flag;   
                sw_bank_en <= 1'b1;          //拉高切换BANK使能信号
            end            
        end       
                                             //若突发写SDRAM结束,更改写地址
        else if(sdram_wr_addr < wr_max_addr - wr_length)
            sdram_wr_addr <= sdram_wr_addr + wr_length;
        else                                 //到达写SDRAM的结束地址,回到写起始地址
            sdram_wr_addr <= wr_min_addr;
    end
    else if(sw_bank_en) begin                //到达写SDRAM的结束地址,回到写起始地址
        sw_bank_en <= 1'b0;
        if(rw_bank_flag == 1'b0)             //切换BANK
            sdram_wr_addr <= {1'b0,wr_min_addr[22:0]};
        else
            sdram_wr_addr <= {1'b1,wr_min_addr[22:0]};     
    end
end

//sdram读地址产生模块
always @(posedge clk_ref or negedge rst_n) begin
    if(!rst_n) begin
        sdram_rd_addr <= 24'd0;
    end 
    else if(rd_load_flag)                    //检测到读端口复位信号时,读地址复位
        sdram_rd_addr <= rd_min_addr;
    else if(read_done_flag) begin            //突发读SDRAM结束,更改读地址
                                             //若未到达读SDRAM的结束地址,则读地址累加                 
        if(sdram_pingpang_en) begin          //SDRAM 读写乒乓使能  
            if(sdram_rd_addr[22:0] < rd_max_addr - rd_length)
                sdram_rd_addr <= sdram_rd_addr + rd_length;
            else begin                       //到达读SDRAM的结束地址,回到读起始地址
                                             //读取没有在写数据的bank地址
                if(rw_bank_flag == 1'b0)     //根据rw_bank_flag的值切换读BANK地址
                    sdram_rd_addr <= {1'b1,rd_min_addr[22:0]};
                else
                    sdram_rd_addr <= {1'b0,rd_min_addr[22:0]};    
            end    
        end
                                             //若突发写SDRAM结束,更改写地址
        else if(sdram_rd_addr < rd_max_addr - rd_length)  
            sdram_rd_addr <= sdram_rd_addr + rd_length;
        else                                 //到达写SDRAM的结束地址,回到写起始地址
            sdram_rd_addr <= rd_min_addr;
    end
end

//sdram 读写请求信号产生模块
always@(posedge clk_ref or negedge rst_n) begin
    if(!rst_n) begin
        sdram_wr_req <= 0;
        sdram_rd_req <= 0;
    end
    else if(sdram_init_done) begin       //SDRAM初始化完成后才能响应读写请求
                                         //优先执行写操作,防止写入SDRAM中的数据丢失
        if(wrf_use >= wr_length) begin   //若写端口FIFO中的数据量达到了写突发长度
            sdram_wr_req <= 1;           //发出写sdarm请求
            sdram_rd_req <= 0;           
        end
        else if((rdf_use < rd_length)    //若读端口FIFO中的数据量小于读突发长度,
                 && read_valid_r2) begin //同时sdram读使能信号为高
            sdram_wr_req <= 0;           
            sdram_rd_req <= 1;           //发出读sdarm请求
        end
        else begin
            sdram_wr_req <= 0;
            sdram_rd_req <= 0;
        end
    end
    else begin
        sdram_wr_req <= 0;
        sdram_rd_req <= 0;
    end
end

//例化写端口FIFO
wrfifo  u_wrfifo(
    //用户接口
    .wrclk      (clk_write),             //写时钟
    .wrreq      (wrf_wrreq),             //写请求
    .data       (wrf_din),               //写数据
    
    //sdram接口
    .rdclk      (clk_ref),               //读时钟
    .rdreq      (sdram_wr_ack),          //读请求
    .q          (sdram_din),             //读数据

    .rdusedw    (wrf_use),               //FIFO中的数据量
    .aclr       (~rst_n | wr_load_flag)  //异步清零信号
    );  

//例化读端口FIFO
rdfifo  u_rdfifo(
    //sdram接口
    .wrclk      (clk_ref),               //写时钟
    .wrreq      (sdram_rd_ack),          //写请求
    .data       (sdram_dout),            //写数据
    
    //用户接口
    .rdclk      (clk_read),              //读时钟
    .rdreq      (rdf_rdreq),             //读请求
    .q          (rdf_dout),              //读数据

    .wrusedw    (rdf_use),               //FIFO中的数据量
    .aclr       (~rst_n | rd_load_flag)  //异步清零信号   
    );
    
endmodule 

2 つの FIFO を構築する

16ビット、1024バイト

SDRAMコントロールモジュールTOP

module sdram_controller(
    input         clk,              //SDRAM控制器时钟,100MHz
    input         rst_n,            //系统复位信号,低电平有效
    
    //SDRAM 控制器写端口  
    input         sdram_wr_req,     //写SDRAM请求信号
    output        sdram_wr_ack,     //写SDRAM响应信号
    input  [23:0] sdram_wr_addr,    //SDRAM写操作的地址
    input  [ 9:0] sdram_wr_burst,   //写sdram时数据突发长度
    input  [15:0] sdram_din,        //写入SDRAM的数据
    
    //SDRAM 控制器读端口  
    input         sdram_rd_req,     //读SDRAM请求信号
    output        sdram_rd_ack,     //读SDRAM响应信号
    input  [23:0] sdram_rd_addr,    //SDRAM写操作的地址
    input  [ 9:0] sdram_rd_burst,   //读sdram时数据突发长度
    output [15:0] sdram_dout,       //从SDRAM读出的数据
    
    output        sdram_init_done,  //SDRAM 初始化完成标志
                                     
    // FPGA与SDRAM硬件接口
    output        sdram_cke,        // SDRAM 时钟有效信号
    output        sdram_cs_n,       // SDRAM 片选信号
    output        sdram_ras_n,      // SDRAM 行地址选通脉冲
    output        sdram_cas_n,      // SDRAM 列地址选通脉冲
    output        sdram_we_n,       // SDRAM 写允许位
    output [ 1:0] sdram_ba,         // SDRAM L-Bank地址线
    output [12:0] sdram_addr,       // SDRAM 地址总线
    inout  [15:0] sdram_data        // SDRAM 数据总线
    );

//wire define
wire [4:0] init_state;              // SDRAM初始化状态
wire [3:0] work_state;              // SDRAM工作状态
wire [9:0] cnt_clk;                 // 延时计数器
wire       sdram_rd_wr;             // SDRAM读/写控制信号,低电平为写,高电平为读

//*****************************************************
//**                    main code
//*****************************************************     

// SDRAM 状态控制模块                
sdram_ctrl u_sdram_ctrl(            
    .clk                (clk),                      
    .rst_n              (rst_n),

    .sdram_wr_req       (sdram_wr_req), 
    .sdram_rd_req       (sdram_rd_req),
    .sdram_wr_ack       (sdram_wr_ack),
    .sdram_rd_ack       (sdram_rd_ack),                     
    .sdram_wr_burst     (sdram_wr_burst),
    .sdram_rd_burst     (sdram_rd_burst),
    .sdram_init_done    (sdram_init_done),
    
    .init_state         (init_state),
    .work_state         (work_state),
    .cnt_clk            (cnt_clk),
    .sdram_rd_wr        (sdram_rd_wr)
    );

// SDRAM 命令控制模块
sdram_cmd u_sdram_cmd(              
    .clk                (clk),
    .rst_n              (rst_n),

    .sys_wraddr         (sdram_wr_addr),            
    .sys_rdaddr         (sdram_rd_addr),
    .sdram_wr_burst     (sdram_wr_burst),
    .sdram_rd_burst     (sdram_rd_burst),
    
    .init_state         (init_state),   
    .work_state         (work_state),
    .cnt_clk            (cnt_clk),
    .sdram_rd_wr        (sdram_rd_wr),
    
    .sdram_cke          (sdram_cke),        
    .sdram_cs_n         (sdram_cs_n),   
    .sdram_ras_n        (sdram_ras_n),  
    .sdram_cas_n        (sdram_cas_n),  
    .sdram_we_n         (sdram_we_n),   
    .sdram_ba           (sdram_ba),         
    .sdram_addr         (sdram_addr)
    );

// SDRAM 数据读写模块
sdram_data u_sdram_data(        
    .clk                (clk),
    .rst_n              (rst_n),
    
    .sdram_data_in      (sdram_din),
    .sdram_data_out     (sdram_dout),
    .work_state         (work_state),
    .cnt_clk            (cnt_clk),
    
    .sdram_data         (sdram_data)
    );

endmodule 

SDRAMコマンド制御モジュール

module sdram_cmd(
    input             clk,              //系统时钟
    input             rst_n,            //低电平复位信号

    input      [23:0] sys_wraddr,       //写SDRAM时地址
    input      [23:0] sys_rdaddr,       //读SDRAM时地址
    input      [ 9:0] sdram_wr_burst,   //突发写SDRAM字节数
    input      [ 9:0] sdram_rd_burst,   //突发读SDRAM字节数
    
    input      [ 4:0] init_state,       //SDRAM初始化状态
    input      [ 3:0] work_state,       //SDRAM工作状态
    input      [ 9:0] cnt_clk,          //延时计数器 
    input             sdram_rd_wr,      //SDRAM读/写控制信号,低电平为写
    
    output            sdram_cke,        //SDRAM时钟有效信号
    output            sdram_cs_n,       //SDRAM片选信号
    output            sdram_ras_n,      //SDRAM行地址选通脉冲
    output            sdram_cas_n,      //SDRAM列地址选通脉冲
    output            sdram_we_n,       //SDRAM写允许位
    output reg [ 1:0] sdram_ba,         //SDRAM的L-Bank地址线
    output reg [12:0] sdram_addr        //SDRAM地址总线
    );

`include "sdram_para.v"                 //包含SDRAM参数定义模块

//reg define
reg  [ 4:0] sdram_cmd_r;                //SDRAM操作指令

//wire define
wire [23:0] sys_addr;                   //SDRAM读写地址 

//*****************************************************
//**                    main code
//***************************************************** 

//SDRAM 控制信号线赋值
assign {sdram_cke,sdram_cs_n,sdram_ras_n,sdram_cas_n,sdram_we_n} = sdram_cmd_r;

//SDRAM 读/写地址总线控制
assign sys_addr = sdram_rd_wr ? sys_rdaddr : sys_wraddr;
    
//SDRAM 操作指令控制
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) begin
            sdram_cmd_r <= `CMD_INIT;
            sdram_ba    <= 2'b11;
            sdram_addr  <= 13'h1fff;
    end
    else
        case(init_state)
                                        //初始化过程中,以下状态不执行任何指令
            `I_NOP,`I_TRP,`I_TRF,`I_TRSC: begin
                    sdram_cmd_r <= `CMD_NOP;
                    sdram_ba    <= 2'b11;
                    sdram_addr  <= 13'h1fff;    
                end
            `I_PRE: begin               //预充电指令
                    sdram_cmd_r <= `CMD_PRGE;
                    sdram_ba    <= 2'b11;
                    sdram_addr  <= 13'h1fff;
                end 
            `I_AR: begin
                                        //自动刷新指令
                    sdram_cmd_r <= `CMD_A_REF;
                    sdram_ba    <= 2'b11;
                    sdram_addr  <= 13'h1fff;                        
                end                 
            `I_MRS: begin               //模式寄存器设置指令
                    sdram_cmd_r <= `CMD_LMR;
                    sdram_ba    <= 2'b00;
                    sdram_addr  <= {    //利用地址线设置模式寄存器,可根据实际需要进行修改
                        3'b000,         //预留
                        1'b0,           //读写方式 A9=0,突发读&突发写
                        2'b00,          //默认,{A8,A7}=00
                        3'b011,         //CAS潜伏期设置,这里设置为3,{A6,A5,A4}=011
                        1'b0,           //突发传输方式,这里设置为顺序,A3=0
                        3'b111          //突发长度,这里设置为页突发,{A2,A1,A0}=011
                    };
                end 
            `I_DONE:                    //SDRAM初始化完成
                    case(work_state)    //以下工作状态不执行任何指令
                        `W_IDLE,`W_TRCD,`W_CL,`W_TWR,`W_TRP,`W_TRFC: begin
                                sdram_cmd_r <= `CMD_NOP;
                                sdram_ba    <= 2'b11;
                                sdram_addr  <= 13'h1fff;
                            end
                        `W_ACTIVE: begin//行有效指令
                                sdram_cmd_r <= `CMD_ACTIVE;
                                sdram_ba    <= sys_addr[23:22];
                                sdram_addr  <= sys_addr[21:9];
                            end
                        `W_READ: begin  //读操作指令
                                sdram_cmd_r <= `CMD_READ;
                                sdram_ba    <= sys_addr[23:22];
                                sdram_addr  <= {4'b0000,sys_addr[8:0]};
                            end
                        `W_RD: begin    //突发传输终止指令
                                if(`end_rdburst) 
                                    sdram_cmd_r <= `CMD_B_STOP;
                                else begin
                                    sdram_cmd_r <= `CMD_NOP;
                                    sdram_ba    <= 2'b11;
                                    sdram_addr  <= 13'h1fff;
                                end
                            end                             
                        `W_WRITE: begin //写操作指令
                                sdram_cmd_r <= `CMD_WRITE;
                                sdram_ba    <= sys_addr[23:22];
                                sdram_addr  <= {4'b0000,sys_addr[8:0]};
                            end     
                        `W_WD: begin    //突发传输终止指令
                                if(`end_wrburst) 
                                    sdram_cmd_r <= `CMD_B_STOP;
                                else begin
                                    sdram_cmd_r <= `CMD_NOP;
                                    sdram_ba    <= 2'b11;
                                    sdram_addr  <= 13'h1fff;
                                end
                            end
                        `W_PRE:begin    //预充电指令
                                sdram_cmd_r <= `CMD_PRGE;
                                sdram_ba    <= sys_addr[23:22];
                                sdram_addr  <= 13'h0400;
                            end             
                        `W_AR: begin    //自动刷新指令
                                sdram_cmd_r <= `CMD_A_REF;
                                sdram_ba    <= 2'b11;
                                sdram_addr  <= 13'h1fff;
                            end
                        default: begin
                                sdram_cmd_r <= `CMD_NOP;
                                sdram_ba    <= 2'b11;
                                sdram_addr  <= 13'h1fff;
                            end
                    endcase
            default: begin
                    sdram_cmd_r <= `CMD_NOP;
                    sdram_ba    <= 2'b11;
                    sdram_addr  <= 13'h1fff;
                end
        endcase
end

endmodule 

SDRAMステータス制御モジュール

module sdram_ctrl(
    input            clk,			    //系统时钟
    input            rst_n,			    //复位信号,低电平有效
    
    input            sdram_wr_req,	    //写SDRAM请求信号
    input            sdram_rd_req,	    //读SDRAM请求信号
    output           sdram_wr_ack,	    //写SDRAM响应信号
    output           sdram_rd_ack,	    //读SDRAM响应信号
    input      [9:0] sdram_wr_burst,	//突发写SDRAM字节数(1-512个)
    input      [9:0] sdram_rd_burst,	//突发读SDRAM字节数(1-256个)	
    output           sdram_init_done,   //SDRAM系统初始化完毕信号

    output reg [4:0] init_state,	    //SDRAM初始化状态
    output reg [3:0] work_state,	    //SDRAM工作状态
    output reg [9:0] cnt_clk,	        //时钟计数器
    output reg       sdram_rd_wr 		//SDRAM读/写控制信号,低电平为写,高电平为读
    );

`include "sdram_para.v"		            //包含SDRAM参数定义模块
                                        
//parameter define                      
parameter  TRP_CLK	  = 10'd4;	        //预充电有效周期
parameter  TRC_CLK	  = 10'd6;	        //自动刷新周期
parameter  TRSC_CLK	  = 10'd6;	        //模式寄存器设置时钟周期
parameter  TRCD_CLK	  = 10'd2;	        //行选通周期
parameter  TCL_CLK	  = 10'd3;	        //列潜伏期
parameter  TWR_CLK	  = 10'd2;	        //写入校正
                                        
//reg define                            
reg [14:0] cnt_200us;                   //SDRAM 上电稳定期200us计数器
reg [10:0] cnt_refresh;	                //刷新计数寄存器
reg        sdram_ref_req;		        //SDRAM 自动刷新请求信号
reg        cnt_rst_n;		            //延时计数器复位信号,低有效	
reg [ 3:0] init_ar_cnt;                 //初始化过程自动刷新计数器
                                        
//wire define                           
wire       done_200us;		            //上电后200us输入稳定期结束标志位
wire       sdram_ref_ack;		        //SDRAM自动刷新请求应答信号	

//*****************************************************
//**                    main code
//***************************************************** 

//SDRAM上电后200us稳定期结束后,将标志信号拉高
assign done_200us = (cnt_200us == 15'd20_000);

//SDRAM初始化完成标志 
assign sdram_init_done = (init_state == `I_DONE);

//SDRAM 自动刷新应答信号
assign sdram_ref_ack = (work_state == `W_AR);

//写SDRAM响应信号
assign sdram_wr_ack = ((work_state == `W_TRCD) & ~sdram_rd_wr) | 
					  ( work_state == `W_WRITE)|
					  ((work_state == `W_WD) & (cnt_clk < sdram_wr_burst - 2'd2));
                      
//读SDRAM响应信号
assign sdram_rd_ack = (work_state == `W_RD) & 
					  (cnt_clk >= 10'd1) & (cnt_clk < sdram_rd_burst + 2'd1);
                      
//上电后计时200us,等待SDRAM状态稳定
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) 
        cnt_200us <= 15'd0;
	else if(cnt_200us < 15'd20_000) 
        cnt_200us <= cnt_200us + 1'b1;
    else
        cnt_200us <= cnt_200us;
end
 
//刷新计数器循环计数7812ns (60ms内完成全部8192行刷新操作)
always @ (posedge clk or negedge rst_n)
	if(!rst_n) 
        cnt_refresh <= 11'd0;
	else if(cnt_refresh < 11'd781)      // 64ms/8192 =7812ns
        cnt_refresh <= cnt_refresh + 1'b1;	
	else 
        cnt_refresh <= 11'd0;	

//SDRAM 刷新请求
always @ (posedge clk or negedge rst_n)
	if(!rst_n) 
        sdram_ref_req <= 1'b0;
	else if(cnt_refresh == 11'd780) 
        sdram_ref_req <= 1'b1;	        //刷新计数器计时达7812ns时产生刷新请求
	else if(sdram_ref_ack) 
        sdram_ref_req <= 1'b0;		    //收到刷新请求响应信号后取消刷新请求 

//延时计数器对时钟计数
always @ (posedge clk or negedge rst_n) 
	if(!rst_n) 
        cnt_clk <= 10'd0;
	else if(!cnt_rst_n)                 //在cnt_rst_n为低电平时延时计数器清零
        cnt_clk <= 10'd0;
	else 
        cnt_clk <= cnt_clk + 1'b1;
        
//初始化过程中对自动刷新操作计数
always @ (posedge clk or negedge rst_n) 
	if(!rst_n) 
        init_ar_cnt <= 4'd0;
	else if(init_state == `I_NOP) 
        init_ar_cnt <= 4'd0;
	else if(init_state == `I_AR)
        init_ar_cnt <= init_ar_cnt + 1'b1;
    else
        init_ar_cnt <= init_ar_cnt;
	
//SDRAM的初始化状态机
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) 
        init_state <= `I_NOP;
	else 
		case (init_state)
                                        //上电复位后200us结束则进入下一状态
            `I_NOP:  init_state <= done_200us  ? `I_PRE : `I_NOP;
                                        //预充电状态
			`I_PRE:  init_state <= `I_TRP;
                                        //预充电等待,TRP_CLK个时钟周期
			`I_TRP:  init_state <= (`end_trp)  ? `I_AR  : `I_TRP;
                                        //自动刷新
			`I_AR :  init_state <= `I_TRF;	
                                        //等待自动刷新结束,TRC_CLK个时钟周期
			`I_TRF:  init_state <= (`end_trfc) ? 
                                        //连续8次自动刷新操作
                                   ((init_ar_cnt == 4'd8) ? `I_MRS : `I_AR) : `I_TRF;
                                        //模式寄存器设置
			`I_MRS:	 init_state <= `I_TRSC;	
                                        //等待模式寄存器设置完成,TRSC_CLK个时钟周期
			`I_TRSC: init_state <= (`end_trsc) ? `I_DONE : `I_TRSC;
                                        //SDRAM的初始化设置完成标志
			`I_DONE: init_state <= `I_DONE;
			default: init_state <= `I_NOP;
		endcase
end

//SDRAM的工作状态机,工作包括读、写以及自动刷新操作
always @ (posedge clk or negedge rst_n) begin
	if(!rst_n) 
		work_state <= `W_IDLE;          //空闲状态
	else
		case(work_state)
                                        //定时自动刷新请求,跳转到自动刷新状态
            `W_IDLE: if(sdram_ref_req & sdram_init_done) begin
						 work_state <= `W_AR; 		
					     sdram_rd_wr <= 1'b1;
				     end 		        
                                        //写SDRAM请求,跳转到行有效状态
					 else if(sdram_wr_req & sdram_init_done) begin
						 work_state <= `W_ACTIVE;
						 sdram_rd_wr <= 1'b0;	
					 end                
                                        //读SDRAM请求,跳转到行有效状态
					 else if(sdram_rd_req && sdram_init_done) begin
						 work_state <= `W_ACTIVE;
						 sdram_rd_wr <= 1'b1;	
					 end                
                                        //无操作请求,保持空闲状态
					 else begin 
						 work_state <= `W_IDLE;
						 sdram_rd_wr <= 1'b1;
					 end
                     
            `W_ACTIVE:                  //行有效,跳转到行有效等待状态
                         work_state <= `W_TRCD;
            `W_TRCD: if(`end_trcd)      //行有效等待结束,判断当前是读还是写
						 if(sdram_rd_wr)//读:进入读操作状态
                             work_state <= `W_READ;
						 else           //写:进入写操作状态
                             work_state <= `W_WRITE;
					 else 
                         work_state <= `W_TRCD;
                         
            `W_READ:	                //读操作,跳转到潜伏期
                         work_state <= `W_CL;	
            `W_CL:		                //潜伏期:等待潜伏期结束,跳转到读数据状态
                         work_state <= (`end_tcl) ? `W_RD:`W_CL;	                                        
            `W_RD:		                //读数据:等待读数据结束,跳转到预充电状态
                         work_state <= (`end_tread) ? `W_PRE:`W_RD;
                         
            `W_WRITE:	                //写操作:跳转到写数据状态
                         work_state <= `W_WD;
            `W_WD:		                //写数据:等待写数据结束,跳转到写回周期状态
                         work_state <= (`end_twrite) ? `W_TWR:`W_WD;                         
            `W_TWR:	                    //写回周期:写回周期结束,跳转到预充电状态
                         work_state <= (`end_twr) ? `W_PRE:`W_TWR;
                         
            `W_PRE:		                //预充电:跳转到预充电等待状态
                         work_state <= `W_TRP;
            `W_TRP:	                //预充电等待:预充电等待结束,进入空闲状态
                         work_state <= (`end_trp) ? `W_IDLE:`W_TRP;
                         
            `W_AR:		                //自动刷新操作,跳转到自动刷新等待
                         work_state <= `W_TRFC;             
            `W_TRFC:	                //自动刷新等待:自动刷新等待结束,进入空闲状态
                         work_state <= (`end_trfc) ? `W_IDLE:`W_TRFC;
            default: 	 work_state <= `W_IDLE;
		endcase
end

//计数器控制逻辑
always @ (*) begin
	case (init_state)
        `I_NOP:	 cnt_rst_n <= 1'b0;     //延时计数器清零(cnt_rst_n低电平复位)
                                        
        `I_PRE:	 cnt_rst_n <= 1'b1;     //预充电:延时计数器启动(cnt_rst_n高电平启动)
                                        //等待预充电延时计数结束后,清零计数器
        `I_TRP:	 cnt_rst_n <= (`end_trp) ? 1'b0 : 1'b1;
                                        //自动刷新:延时计数器启动
        `I_AR:
                 cnt_rst_n <= 1'b1;
                                        //等待自动刷新延时计数结束后,清零计数器
        `I_TRF:
                 cnt_rst_n <= (`end_trfc) ? 1'b0 : 1'b1;	
                                        
        `I_MRS:  cnt_rst_n <= 1'b1;	    //模式寄存器设置:延时计数器启动
                                        //等待模式寄存器设置延时计数结束后,清零计数器
        `I_TRSC: cnt_rst_n <= (`end_trsc) ? 1'b0:1'b1;
                                        
        `I_DONE: begin                  //初始化完成后,判断工作状态
		    case (work_state)
				`W_IDLE:	cnt_rst_n <= 1'b0;
                                        //行有效:延时计数器启动
				`W_ACTIVE: 	cnt_rst_n <= 1'b1;
                                        //行有效延时计数结束后,清零计数器
				`W_TRCD:	cnt_rst_n <= (`end_trcd)   ? 1'b0 : 1'b1;
                                        //潜伏期延时计数结束后,清零计数器
				`W_CL:		cnt_rst_n <= (`end_tcl)    ? 1'b0 : 1'b1;
                                        //读数据延时计数结束后,清零计数器
				`W_RD:		cnt_rst_n <= (`end_tread)  ? 1'b0 : 1'b1;
                                        //写数据延时计数结束后,清零计数器
				`W_WD:		cnt_rst_n <= (`end_twrite) ? 1'b0 : 1'b1;
                                        //写回周期延时计数结束后,清零计数器
				`W_TWR:	    cnt_rst_n <= (`end_twr)    ? 1'b0 : 1'b1;
                                        //预充电等待延时计数结束后,清零计数器
				`W_TRP:	cnt_rst_n <= (`end_trp) ? 1'b0 : 1'b1;
                                        //自动刷新等待延时计数结束后,清零计数器
				`W_TRFC:	cnt_rst_n <= (`end_trfc)   ? 1'b0 : 1'b1;
				default:    cnt_rst_n <= 1'b0;
		    endcase
        end
		default: cnt_rst_n <= 1'b0;
	endcase
end
 
endmodule 

SDRAMデータ読み取りおよび書き込みモジュール

module sdram_data(
    input             clk,              //系统时钟
    input             rst_n,            //低电平复位信号

    input   [15:0]    sdram_data_in,    //写入SDRAM中的数据
    output  [15:0]    sdram_data_out,   //从SDRAM中读取的数据
    input   [ 3:0]    work_state,       //SDRAM工作状态寄存器
    input   [ 9:0]    cnt_clk,          //时钟计数
    
    inout   [15:0]    sdram_data        //SDRAM数据总线
    );

`include "sdram_para.v"                 //包含SDRAM参数定义模块

//reg define
reg        sdram_out_en;                //SDRAM数据总线输出使能
reg [15:0] sdram_din_r;                 //寄存写入SDRAM中的数据
reg [15:0] sdram_dout_r;                //寄存从SDRAM中读取的数据

//*****************************************************
//**                    main code
//***************************************************** 

//SDRAM 双向数据线作为输入时保持高阻态
assign sdram_data = sdram_out_en ? sdram_din_r : 16'hzzzz;

//输出SDRAM中读取的数据
assign sdram_data_out = sdram_dout_r;

//SDRAM 数据总线输出使能
always @ (posedge clk or negedge rst_n) begin 
    if(!rst_n) 
       sdram_out_en <= 1'b0;
   else if((work_state == `W_WRITE) | (work_state == `W_WD)) 
       sdram_out_en <= 1'b1;            //向SDRAM中写数据时,输出使能拉高
   else 
       sdram_out_en <= 1'b0;
end

//将待写入数据送到SDRAM数据总线上
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) 
        sdram_din_r <= 16'd0;
    else if((work_state == `W_WRITE) | (work_state == `W_WD))
        sdram_din_r <= sdram_data_in;   //寄存写入SDRAM中的数据
end

//读数据时,寄存SDRAM数据线上的数据
always @ (posedge clk or negedge rst_n) begin
    if(!rst_n) 
        sdram_dout_r <= 16'd0;
    else if(work_state == `W_RD) 
        sdram_dout_r <= sdram_data;     //寄存从SDRAM中读取的数据
end

endmodule 

SDRAMパラメータファイル


// SDRAM 初始化过程各个状态
`define     I_NOP           5'd0                            //等待上电200us稳定期结束
`define     I_PRE           5'd1                            //预充电状态
`define     I_TRP           5'd2                            //等待预充电完成       tRP
`define     I_AR            5'd3                            //自动刷新            
`define     I_TRF           5'd4                            //等待自动刷新结束    tRC
`define     I_MRS           5'd5                            //模式寄存器设置
`define     I_TRSC          5'd6                            //等待模式寄存器设置完成 tRSC
`define     I_DONE          5'd7                            //初始化完成

// SDRAM 工作过程各个状态
`define     W_IDLE          4'd0                            //空闲
`define     W_ACTIVE        4'd1                            //行有效
`define     W_TRCD          4'd2                            //行有效等待
`define     W_READ          4'd3                            //读操作
`define     W_CL            4'd4                            //潜伏期
`define     W_RD            4'd5                            //读数据
`define     W_WRITE         4'd6                            //写操作
`define     W_WD            4'd7                            //写数据
`define     W_TWR           4'd8                            //写回
`define     W_PRE           4'd9                            //预充电
`define     W_TRP           4'd10                           //预充电等待
`define     W_AR            4'd11                           //自动刷新
`define     W_TRFC          4'd12                           //自动刷新等待
  
//延时参数
`define     end_trp         cnt_clk == TRP_CLK              //预充电有效周期结束
`define     end_trfc        cnt_clk == TRC_CLK              //自动刷新周期结束
`define     end_trsc        cnt_clk == TRSC_CLK             //模式寄存器设置时钟周期结束
`define     end_trcd        cnt_clk == TRCD_CLK-1           //行选通周期结束
`define     end_tcl         cnt_clk == TCL_CLK-1            //潜伏期结束
`define     end_rdburst     cnt_clk == sdram_rd_burst-4     //读突发终止
`define     end_tread       cnt_clk == sdram_rd_burst+2     //突发读结束     
`define     end_wrburst     cnt_clk == sdram_wr_burst-1     //写突发终止
`define     end_twrite      cnt_clk == sdram_wr_burst-1     //突发写结束
`define     end_twr         cnt_clk == TWR_CLK              //写回周期结束

//SDRAM控制信号命令
`define     CMD_INIT        5'b01111                        // INITIATE
`define     CMD_NOP         5'b10111                        // NOP COMMAND
`define     CMD_ACTIVE      5'b10011                        // ACTIVE COMMAND
`define     CMD_READ        5'b10101                        // READ COMMADN
`define     CMD_WRITE       5'b10100                        // WRITE COMMAND
`define     CMD_B_STOP      5'b10110                        // BURST STOP
`define     CMD_PRGE        5'b10010                        // PRECHARGE
`define     CMD_A_REF       5'b10001                        // AOTO REFRESH
`define     CMD_LMR         5'b10000                        // LODE MODE REGISTER

おすすめ

転載: blog.csdn.net/qq_42792802/article/details/127114781
おすすめ