Inhaltsverzeichnis
SDRAM-Datenlese- und -schreibmodul
camera_top:
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-Treibermodul:
erklären:
3 rein——— 5 raus
eingeben:
1,50 Mio. Uhr
2. Zurücksetzen
3. Ein Pixel des eingehenden 16-Bit-Pixelbildes
Ausgang:
1. Leitungssynchronisation direkt an der VGA-Schnittstelle angeschlossen
2. Feldsynchronisation direkt angeschlossen an die VGA-Schnittstelle
3.RGB16-Bit-Pixelausgang direkt mit der VGA-Schnittstelle verbunden
4. Pixel-Abszisse. Dies ist im Allgemeinen der Ort, an dem das Pixel verarbeitet werden soll, die Bildverarbeitung
5. Vertikale Pixelkoordinate Dies ist im Allgemeinen der Ort, an dem das Pixel verarbeitet werden soll, die Bildverarbeitung
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-Kamera
Datei der obersten Ebene:
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-Modul:
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
Kamera-CFG-Konfiguration:
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-Modul:
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-Modul:
Insgesamt 9 Dateien: drei oberste Schichten, zwei FIFOs, drei Statussteuerbefehle und ein Parameter
TOP-Modul
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-Steuermodul:
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
Erstellen Sie zwei FIFOs
16 Bit, 1024 Byte
SDRAM-Steuermodul 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-Befehlssteuermodul
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-Statuskontrollmodul
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-Datenlese- und -schreibmodul
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-Parameterdatei
// 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