此项目一共分为摄像头配置模块,图像采集模块,异步FIFO控制模块,SDRAM控制模块,SDRAM端口模块,VGA显示模块。
摄像头配置模块
直接采用IIC接口对摄像头进行配置:模块分化:IIC端口模块,IIC控制模块,和LUT查找表模块;配置图像像素输出为1280*720
摄像头配置参数
//涉嫌头参数配置-LUT模块
module lut_da(
input clk ,
input rst_n ,
input redy , //反馈信号,1可以发数据,0 不能读写
output reg data_en ,
output [24:0] data_out ,
output config_done //1代表配置完成
);
parameter RW_CTRL=1'b0; //1代表写模式+读模式 ;0代表只写
localparam WAIT_TIME =1000_000,//上电等待时间
MAX =253-2 ,
WAIT =3'b001,
WRITE =3'b010,
IDLE =3'b100;
reg [19:0] cnt ;
wire add_cnt ;
wire end_cnt ;
reg [24:0] lut_data;
reg cnt_flag;
reg [2:0] state;
reg [2:0] state_next;
wire wait_write ;
wire write_idle ;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
state<=WAIT;
end
else begin
state<=state_next;
end
end
always @(*)begin
case(state)
WAIT :begin
if(wait_write) begin
state_next=WRITE;
end
else begin
state_next=WAIT;
end
end
WRITE:begin
if(write_idle) begin
state_next=IDLE;
end
else begin
state_next=WRITE;
end
end
IDLE :begin
state_next=IDLE;
end
default:state=WAIT;
endcase
end
assign wait_write=state==WAIT && end_cnt;
assign write_idle=state==WRITE&& end_cnt;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt <= 0;
end
else if(add_cnt)begin
if(end_cnt)begin
cnt <= 0;
end
else begin
cnt <= cnt + 1'b1;
end
end
end
assign add_cnt = state==WAIT|| (state==WRITE&&cnt_flag);
assign end_cnt = add_cnt && cnt==( (state==WAIT) ? (WAIT_TIME-1 ): (MAX+1) ) ;
//data_en
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_en <= 0;
end
else if( data_en==1 && state==WRITE ) begin
data_en <= 0;
end
else if( state==WRITE && redy==1 )begin //redy==1模块空闲中,可以读写操作
data_en<=1;
end
end
assign config_done= state==IDLE;
assign data_out= (state==WRITE) ? lut_data : 1'b0;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_flag <= 0;
end
else if(data_en)begin
cnt_flag<=1'b1;
end
else begin
cnt_flag<=0;
end
end
//lut_data
always@(*)begin
case( cnt ) //2个字节地址,一个字节数据
//15fps VGA YUV output
// 24MHz input clock, 24MHz PCLK
0 : lut_data = {RW_CTRL,24'h3103_11}; // system clock from pad, bit[1]
1 : lut_data = {RW_CTRL,24'h3008_82}; // software reset, bit[7]
2 : lut_data = {RW_CTRL,24'h3008_42}; // software power down, bit[6]
3 : lut_data = {RW_CTRL,24'h3103_03}; // system clock from PLL, bit[1]
4 : lut_data = {RW_CTRL,24'h3017_ff}; // FREX, Vsync, HREF, PCLK, D[9:6] output enable
5 : lut_data = {RW_CTRL,24'h3018_ff}; // D[5:0], GPIO[1:0] output enable
6 : lut_data = {RW_CTRL,24'h3034_1a}; // MIPI 10-bit
7 : lut_data = {RW_CTRL,24'h3037_13}; // PLL root divider, bit[4], PLL pre-divider, bit[3:0]
8 : lut_data = {RW_CTRL,24'h3108_01}; // PCLK root divider, bit[5:4], SCLK2x root divider, bit[3:2]
9 : lut_data = {RW_CTRL,24'h3630_36};// SCLK root divider, bit[1:0]
10 : lut_data = {RW_CTRL,24'h3631_0e};
11 : lut_data = {RW_CTRL,24'h3632_e2};
12 : lut_data = {RW_CTRL,24'h3633_12};
13 : lut_data = {RW_CTRL,24'h3621_e0};
14 : lut_data = {RW_CTRL,24'h3704_a0};
15 : lut_data = {RW_CTRL,24'h3703_5a};
16 : lut_data = {RW_CTRL,24'h3715_78};
17 : lut_data = {RW_CTRL,24'h3717_01};
18 : lut_data = {RW_CTRL,24'h370b_60};
19 : lut_data = {RW_CTRL,24'h3705_1a};
20 : lut_data = {RW_CTRL,24'h3905_02};
21 : lut_data = {RW_CTRL,24'h3906_10};
22 : lut_data = {RW_CTRL,24'h3901_0a};
23 : lut_data = {RW_CTRL,24'h3731_12};
24 : lut_data = {RW_CTRL,24'h3600_08}; // VCM control
25 : lut_data = {RW_CTRL,24'h3601_33}; // VCM control
26 : lut_data = {RW_CTRL,24'h302d_60}; // system control
27 : lut_data = {RW_CTRL,24'h3620_52};
28 : lut_data = {RW_CTRL,24'h371b_20};
29 : lut_data = {RW_CTRL,24'h471c_50};
30 : lut_data = {RW_CTRL,24'h3a13_43}; // pre-gain = 1.047x
31 : lut_data = {RW_CTRL,24'h3a18_00}; // gain ceiling
32 : lut_data = {RW_CTRL,24'h3a19_f8}; // gain ceiling = 15.5x
33 : lut_data = {RW_CTRL,24'h3635_13};
34 : lut_data = {RW_CTRL,24'h3636_03};
35 : lut_data = {RW_CTRL,24'h3634_40};
36 : lut_data = {RW_CTRL,24'h3622_01};
// 50/60Hz detection 50/60Hz 灯光条纹过滤
37 : lut_data = {RW_CTRL,24'h3c01_34}; // Band auto, bit[7]
38 : lut_data = {RW_CTRL,24'h3c04_28}; // threshold low sum
39 : lut_data = {RW_CTRL,24'h3c05_98}; // threshold high sum
40 : lut_data = {RW_CTRL,24'h3c06_00}; // light meter 1 threshold[15:8]
41 : lut_data = {RW_CTRL,24'h3c07_08}; // light meter 1 threshold[7:0]
42 : lut_data = {RW_CTRL,24'h3c08_00}; // light meter 2 threshold[15:8]
43 : lut_data = {RW_CTRL,24'h3c09_1c}; // light meter 2 threshold[7:0]
44 : lut_data = {RW_CTRL,24'h3c0a_9c}; // sample number[15:8]
45 : lut_data = {RW_CTRL,24'h3c0b_40}; // sample number[7:0]
46 : lut_data = {RW_CTRL,24'h3810_00}; // Timing Hoffset[11:8]
47 : lut_data = {RW_CTRL,24'h3811_10}; // Timing Hoffset[7:0]
48 : lut_data = {RW_CTRL,24'h3812_00}; // Timing Voffset[10:8]
49 : lut_data = {RW_CTRL,24'h3708_64};
50 : lut_data = {RW_CTRL,24'h4001_02}; // BLC start from line 2
51 : lut_data = {RW_CTRL,24'h4005_1a}; // BLC always update
52 : lut_data = {RW_CTRL,24'h3000_00}; // enable blocks
53 : lut_data = {RW_CTRL,24'h3004_ff}; // enable clocks
54 : lut_data = {RW_CTRL,24'h300e_58}; // MIPI power down, DVP enable
55 : lut_data = {RW_CTRL,24'h302e_00};
56 : lut_data = {RW_CTRL,24'h4300_61}; // RGB,
57 : lut_data = {RW_CTRL,24'h501f_01}; // ISP RGB
58 : lut_data = {RW_CTRL,24'h440e_00};
59 : lut_data = {RW_CTRL,24'h5000_a7}; // Lenc on, raw gamma on, BPC on, WPC on, CIP on
// AEC target 自动曝光控制
60 : lut_data = {RW_CTRL,24'h3a0f_30}; // stable range in high
61 : lut_data = {RW_CTRL,24'h3a10_28}; // stable range in low
62 : lut_data = {RW_CTRL,24'h3a1b_30}; // stable range out high
63 : lut_data = {RW_CTRL,24'h3a1e_26}; // stable range out low
64 : lut_data = {RW_CTRL,24'h3a11_60}; // fast zone high
65 : lut_data = {RW_CTRL,24'h3a1f_14}; // fast zone low
// Lens correction for ? 镜头补偿
66 : lut_data = {RW_CTRL,24'h5800_23};
67 : lut_data = {RW_CTRL,24'h5801_14};
68 : lut_data = {RW_CTRL,24'h5802_0f};
69 : lut_data = {RW_CTRL,24'h5803_0f};
70 : lut_data = {RW_CTRL,24'h5804_12};
71 : lut_data = {RW_CTRL,24'h5805_26};
72 : lut_data = {RW_CTRL,24'h5806_0c};
73 : lut_data = {RW_CTRL,24'h5807_08};
74 : lut_data = {RW_CTRL,24'h5808_05};
75 : lut_data = {RW_CTRL,24'h5809_05};
76 : lut_data = {RW_CTRL,24'h580a_08};
77 : lut_data = {RW_CTRL,24'h580b_0d};
78 : lut_data = {RW_CTRL,24'h580c_08};
79 : lut_data = {RW_CTRL,24'h580d_03};
80 : lut_data = {RW_CTRL,24'h580e_00};
81 : lut_data = {RW_CTRL,24'h580f_00};
82 : lut_data = {RW_CTRL,24'h5810_03};
83 : lut_data = {RW_CTRL,24'h5811_09};
84 : lut_data = {RW_CTRL,24'h5812_07};
85 : lut_data = {RW_CTRL,24'h5813_03};
86 : lut_data = {RW_CTRL,24'h5814_00};
87 : lut_data = {RW_CTRL,24'h5815_01};
88 : lut_data = {RW_CTRL,24'h5816_03};
89 : lut_data = {RW_CTRL,24'h5817_08};
90 : lut_data = {RW_CTRL,24'h5818_0d};
91 : lut_data = {RW_CTRL,24'h5819_08};
92 : lut_data = {RW_CTRL,24'h581a_05};
93 : lut_data = {RW_CTRL,24'h581b_06};
94 : lut_data = {RW_CTRL,24'h581c_08};
95 : lut_data = {RW_CTRL,24'h581d_0e};
96 : lut_data = {RW_CTRL,24'h581e_29};
97 : lut_data = {RW_CTRL,24'h581f_17};
98 : lut_data = {RW_CTRL,24'h5820_11};
99 : lut_data = {RW_CTRL,24'h5821_11};
100: lut_data = {RW_CTRL,24'h5822_15};
101: lut_data = {RW_CTRL,24'h5823_28};
102: lut_data = {RW_CTRL,24'h5824_46};
103: lut_data = {RW_CTRL,24'h5825_26};
104: lut_data = {RW_CTRL,24'h5826_08};
105: lut_data = {RW_CTRL,24'h5827_26};
106: lut_data = {RW_CTRL,24'h5828_64};
107: lut_data = {RW_CTRL,24'h5829_26};
108: lut_data = {RW_CTRL,24'h582a_24};
109: lut_data = {RW_CTRL,24'h582b_22};
110: lut_data = {RW_CTRL,24'h582c_24};
111: lut_data = {RW_CTRL,24'h582d_24};
112: lut_data = {RW_CTRL,24'h582e_06};
113: lut_data = {RW_CTRL,24'h582f_22};
114: lut_data = {RW_CTRL,24'h5830_40};
115: lut_data = {RW_CTRL,24'h5831_42};
116: lut_data = {RW_CTRL,24'h5832_24};
117: lut_data = {RW_CTRL,24'h5833_26};
118: lut_data = {RW_CTRL,24'h5834_24};
119: lut_data = {RW_CTRL,24'h5835_22};
120: lut_data = {RW_CTRL,24'h5836_22};
121: lut_data = {RW_CTRL,24'h5837_26};
122: lut_data = {RW_CTRL,24'h5838_44};
123: lut_data = {RW_CTRL,24'h5839_24};
124: lut_data = {RW_CTRL,24'h583a_26};
125: lut_data = {RW_CTRL,24'h583b_28};
126: lut_data = {RW_CTRL,24'h583c_42};
127: lut_data = {RW_CTRL,24'h583d_ce}; // lenc BR offset
// AWB 自动白平衡
128: lut_data = {RW_CTRL,24'h5180_ff}; // AWB B block
129: lut_data = {RW_CTRL,24'h5181_f2}; // AWB control
130: lut_data = {RW_CTRL,24'h5182_00}; // [7:4] max local counter, [3:0] max fast counter
131: lut_data = {RW_CTRL,24'h5183_14}; // AWB advanced
132: lut_data = {RW_CTRL,24'h5184_25};
133: lut_data = {RW_CTRL,24'h5185_24};
134: lut_data = {RW_CTRL,24'h5186_09};
135: lut_data = {RW_CTRL,24'h5187_09};
136: lut_data = {RW_CTRL,24'h5188_09};
137: lut_data = {RW_CTRL,24'h5189_75};
138: lut_data = {RW_CTRL,24'h518a_54};
139: lut_data = {RW_CTRL,24'h518b_e0};
140: lut_data = {RW_CTRL,24'h518c_b2};
141: lut_data = {RW_CTRL,24'h518d_42};
142: lut_data = {RW_CTRL,24'h518e_3d};
143: lut_data = {RW_CTRL,24'h518f_56};
144: lut_data = {RW_CTRL,24'h5190_46};
145: lut_data = {RW_CTRL,24'h5191_f8}; // AWB top limit
146: lut_data = {RW_CTRL,24'h5192_04}; // AWB bottom limit
147: lut_data = {RW_CTRL,24'h5193_70}; // red limit
148: lut_data = {RW_CTRL,24'h5194_f0}; // green limit
149: lut_data = {RW_CTRL,24'h5195_f0}; // blue limit
150: lut_data = {RW_CTRL,24'h5196_03}; // AWB control
151: lut_data = {RW_CTRL,24'h5197_01}; // local limit
152: lut_data = {RW_CTRL,24'h5198_04};
153: lut_data = {RW_CTRL,24'h5199_12};
154: lut_data = {RW_CTRL,24'h519a_04};
155: lut_data = {RW_CTRL,24'h519b_00};
156: lut_data = {RW_CTRL,24'h519c_06};
157: lut_data = {RW_CTRL,24'h519d_82};
158: lut_data = {RW_CTRL,24'h519e_38}; // AWB control
// Gamma 伽玛曲线
159: lut_data = {RW_CTRL,24'h5480_01}; // Gamma bias plus on, bit[0]
160: lut_data = {RW_CTRL,24'h5481_08};
161: lut_data = {RW_CTRL,24'h5482_14};
162: lut_data = {RW_CTRL,24'h5483_28};
163: lut_data = {RW_CTRL,24'h5484_51};
164: lut_data = {RW_CTRL,24'h5485_65};
165: lut_data = {RW_CTRL,24'h5486_71};
166: lut_data = {RW_CTRL,24'h5487_7d};
167: lut_data = {RW_CTRL,24'h5488_87};
168: lut_data = {RW_CTRL,24'h5489_91};
169: lut_data = {RW_CTRL,24'h548a_9a};
170: lut_data = {RW_CTRL,24'h548b_aa};
171: lut_data = {RW_CTRL,24'h548c_b8};
172: lut_data = {RW_CTRL,24'h548d_cd};
173: lut_data = {RW_CTRL,24'h548e_dd};
174: lut_data = {RW_CTRL,24'h548f_ea};
175: lut_data = {RW_CTRL,24'h5490_1d};
// color matrix 色彩矩阵
176: lut_data = {RW_CTRL,24'h5381_1e}; // CMX1 for Y
177: lut_data = {RW_CTRL,24'h5382_5b}; // CMX2 for Y
178: lut_data = {RW_CTRL,24'h5383_08}; // CMX3 for Y
179: lut_data = {RW_CTRL,24'h5384_0a}; // CMX4 for U
180: lut_data = {RW_CTRL,24'h5385_7e}; // CMX5 for U
181: lut_data = {RW_CTRL,24'h5386_88}; // CMX6 for U
182: lut_data = {RW_CTRL,24'h5387_7c}; // CMX7 for V
183: lut_data = {RW_CTRL,24'h5388_6c}; // CMX8 for V
184: lut_data = {RW_CTRL,24'h5389_10}; // CMX9 for V
185: lut_data = {RW_CTRL,24'h538a_01}; // sign[9]
186: lut_data = {RW_CTRL,24'h538b_98}; // sign[8:1]
// UV adjust UV 色彩饱和度调整
187: lut_data = {RW_CTRL,24'h5580_06}; // saturation on, bit[1]
188: lut_data = {RW_CTRL,24'h5583_40};
189: lut_data = {RW_CTRL,24'h5584_10};
190: lut_data = {RW_CTRL,24'h5589_10};
191: lut_data = {RW_CTRL,24'h558a_00};
192: lut_data = {RW_CTRL,24'h558b_f8};
193: lut_data = {RW_CTRL,24'h501d_40}; // enable manual offset of contrast
// CIP 锐化和降噪
194: lut_data = {RW_CTRL,24'h5300_08}; // CIP sharpen MT threshold 1
195: lut_data = {RW_CTRL,24'h5301_30}; // CIP sharpen MT threshold 2
196: lut_data = {RW_CTRL,24'h5302_10}; // CIP sharpen MT offset 1
197: lut_data = {RW_CTRL,24'h5303_00}; // CIP sharpen MT offset 2
198: lut_data = {RW_CTRL,24'h5304_08}; // CIP DNS threshold 1
199: lut_data = {RW_CTRL,24'h5305_30}; // CIP DNS threshold 2
200: lut_data = {RW_CTRL,24'h5306_08}; // CIP DNS offset 1
201: lut_data = {RW_CTRL,24'h5307_16}; // CIP DNS offset 2
202: lut_data = {RW_CTRL,24'h5309_08}; // CIP sharpen TH threshold 1
203: lut_data = {RW_CTRL,24'h530a_30}; // CIP sharpen TH threshold 2
204: lut_data = {RW_CTRL,24'h530b_04}; // CIP sharpen TH offset 1
205: lut_data = {RW_CTRL,24'h530c_06}; // CIP sharpen TH offset 2
206: lut_data = {RW_CTRL,24'h5025_00};
207: lut_data = {RW_CTRL,24'h3008_02}; // wake up from standby, bit[6]
// input clock 24Mhz, PCLK 84Mhz
208: lut_data = {RW_CTRL,24'h3035_21}; // PLL
209: lut_data = {RW_CTRL,24'h3036_69}; // PLL
210: lut_data = {RW_CTRL,24'h3c07_07}; // lightmeter 1 threshold[7:0]
211: lut_data = {RW_CTRL,24'h3820_47}; // flip
212: lut_data = {RW_CTRL,24'h3821_01}; // no mirror
213: lut_data = {RW_CTRL,24'h3814_31}; // timing X inc
214: lut_data = {RW_CTRL,24'h3815_31}; // timing Y inc
215: lut_data = {RW_CTRL,24'h3800_00}; // HS
216: lut_data = {RW_CTRL,24'h3801_00}; // HS
217: lut_data = {RW_CTRL,24'h3802_00}; // VS
218: lut_data = {RW_CTRL,24'h3803_fa}; // VS
219: lut_data = {RW_CTRL,24'h3804_0a}; // HW : lut_data = HE}
220: lut_data = {RW_CTRL,24'h3805_3f}; // HW : lut_data = HE}
221: lut_data = {RW_CTRL,24'h3806_06}; // VH : lut_data = VE}
222: lut_data = {RW_CTRL,24'h3807_a9}; // VH : lut_data = VE}
223: lut_data = {RW_CTRL,24'h3808_05}; // DVPHO 1280
224: lut_data = {RW_CTRL,24'h3809_00}; // DVPHO
225: lut_data = {RW_CTRL,24'h380a_02}; // DVPVO 720
226: lut_data = {RW_CTRL,24'h380b_d0}; // DVPVO
227: lut_data = {RW_CTRL,24'h380c_07}; // HTS
228: lut_data = {RW_CTRL,24'h380d_64}; // HTS
229: lut_data = {RW_CTRL,24'h380e_02}; // VTS
230: lut_data = {RW_CTRL,24'h380f_e4}; // VTS
231: lut_data = {RW_CTRL,24'h3813_04}; // timing V offset
232: lut_data = {RW_CTRL,24'h3618_00};
233: lut_data = {RW_CTRL,24'h3612_29};
234: lut_data = {RW_CTRL,24'h3709_52};
235: lut_data = {RW_CTRL,24'h370c_03};
236: lut_data = {RW_CTRL,24'h3a02_02}; // 60Hz max exposure
237: lut_data = {RW_CTRL,24'h3a03_e0}; // 60Hz max exposure
238: lut_data = {RW_CTRL,24'h3a14_02}; // 50Hz max exposure
239: lut_data = {RW_CTRL,24'h3a15_e0}; // 50Hz max exposure
240: lut_data = {RW_CTRL,24'h4004_02}; // BLC line number
241: lut_data = {RW_CTRL,24'h3002_1c}; // reset JFIFO, SFIFO, JPG
242: lut_data = {RW_CTRL,24'h3006_c3}; // disable clock of JPEG2x, JPEG
243: lut_data = {RW_CTRL,24'h4713_03}; // JPEG mode 3
244: lut_data = {RW_CTRL,24'h4407_04}; // Quantization scale
245: lut_data = {RW_CTRL,24'h460b_37};
246: lut_data = {RW_CTRL,24'h460c_20};
247: lut_data = {RW_CTRL,24'h4837_16}; // MIPI global timing
248: lut_data = {RW_CTRL,24'h3824_04}; // PCLK manual divider
249: lut_data = {RW_CTRL,24'h5001_83}; // SDE on, CMX on, AWB on
250: lut_data = {RW_CTRL,24'h3503_00}; // AEC/AGC on
251: lut_data = {RW_CTRL,24'h4740_20}; // VS 1
//252: lut_data = {RW_CTRL,24'h503d_80}; // color bar 选择彩条输出
//253: lut_data = {RW_CTRL,24'h4741_00}; //
default : lut_data = 0;
endcase
end
endmodule
IIC端口模块
//IIC端口模块
`include "param.v"
module i2c_itfc(
input clk ,
input rst_n ,
input [4:0] cmd ,
input [7:0] wr_din ,
input sda_in ,
output reg sda_en ,
output reg sda_out ,
output reg scl ,
output reg [7:0]rd_out ,
output reg rd_out_vld ,
output reg done0 ,
output reg ack
);
//高位先发msb
parameter PAN=150,
DOW_TIME=75,
WR_TIME =15, //发送数据不管
RD_TIME =90;//采集数据
localparam IDLE = 7'b000_0001,
START = 7'b000_0010,
WRITE = 7'b000_0100,
ACTACK = 7'b000_1000,
READ = 7'b001_0000,
SENDACK = 7'b010_0000,
STOP = 7'b100_0000;
reg [6:0] state;
reg [6:0] state_next;
wire idle_start ;
wire start_write ;
wire write_actack ;
wire actack_start ;
wire actack_write ;
wire actack_read ;
wire read_sendack ;
wire sendack_read ;
wire sendack_stop ;
wire stop_idle ;
wire actack_stop ;
reg [15:0] max;
reg [15:0] cnt_bit;
wire add_cnt_bit;
wire end_cnt_bit;
reg [3:0] cnt_byte;
wire add_cnt_byte;
wire end_cnt_byte;
reg [7:0] data_flag ; //接收数据暂存
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
state<=IDLE;
end
else begin
state<=state_next;
end
end
always@(*) begin
case(state)
IDLE : begin
if(idle_start) begin
state_next=START;
end
else begin
state_next<=state;
end
end
START: begin
if(start_write) begin
state_next=WRITE;
end
else begin
state_next<=state;
end
end
WRITE: begin
if(write_actack) begin
state_next=ACTACK;
end
else begin
state_next<=state;
end
end
ACTACK: begin
if(actack_start) begin
state_next=START;
end
else if(actack_write) begin
state_next=WRITE;
end
else if(actack_read) begin
state_next=READ;
end
else if(actack_stop) begin
state_next=STOP;
end
else begin
state_next<=state;
end
end
READ : begin
if(read_sendack) begin
state_next=SENDACK;
end
else begin
state_next<=state;
end
end
SENDACK: begin
if(sendack_stop) begin
state_next=STOP;
end
else if(sendack_read) begin
state_next=READ;
end
else begin
state_next<=state;
end
end
STOP : begin
if(stop_idle) begin
state_next=IDLE;
end
else begin
state_next<=state;
end
end
default:state_next<=IDLE;
endcase
end
assign idle_start =state==IDLE && (cmd==`CMD_EN ) ; //开始工作
assign start_write =state==START && (end_cnt_byte ) ;
assign write_actack=state==WRITE && (end_cnt_byte ) ;
assign actack_start=state==ACTACK && (cmd==`CMD_RDST &&end_cnt_byte ) ; //读数据的时候发起始位1次
assign actack_write=state==ACTACK && (cmd==`CMD_WR &&end_cnt_byte ) ; //写数据
assign actack_read =state==ACTACK && (cmd==`CMD_RD &&end_cnt_byte ) ; //接收数据
assign actack_stop =state==ACTACK && ((cmd==`CMD_NO &&end_cnt_byte)||ack==1) ; //写完数据接发停止位 或者ack无应答
assign read_sendack=state==READ && ( end_cnt_byte ) ;
assign sendack_read=state==SENDACK&& ( end_cnt_byte ) ;
assign sendack_stop=state==SENDACK&& (cmd==`CMD_NO &&end_cnt_byte ) ; //发停止位
assign stop_idle =state==STOP && (end_cnt_byte ) ;
//一个字节周期
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_bit<=0;
end
else if(end_cnt_bit) begin
cnt_bit<=0;
end
else if(add_cnt_bit) begin
cnt_bit<=cnt_bit+1'b1;
end
end
assign add_cnt_bit=state!=IDLE;
assign end_cnt_bit=add_cnt_bit && cnt_bit==PAN-1;
//cnt_byte多少个字节周期
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_byte<=0;
end
else if(end_cnt_byte) begin
cnt_byte<=0;
end
else if(add_cnt_byte) begin
cnt_byte<=cnt_byte+1'b1;
end
end
assign add_cnt_byte=state!=IDLE && end_cnt_bit;
assign end_cnt_byte=add_cnt_byte && cnt_byte==max-1; //cnt_byte==MAX-1之后等下个周期add_cnt_byte到了才为零
///max
always@( * ) begin
if(!rst_n) begin
max=0;
end
else begin
case(state)
START : max=1;
WRITE : max=8;
ACTACK : max=1;
READ : max=8;
SENDACK: max=1;
STOP : max=1;
default:max=0;
endcase
end
end
//scl
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
scl<=1;
end
else if( cnt_bit==DOW_TIME-1 ) begin //
scl<=1;
end
else if( start_write|| ( (state==WRITE||state==READ||state==ACTACK||state==SENDACK)&&cnt_bit==PAN-1&&actack_stop==0 ) ) begin
scl<=0; //数据传输状态scl先低电平再高电平
end
else if(state==START ||state==STOP) begin //起始状态,stop状态高电平
scl<=1;
end
end
//ack
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
ack<=0;
end
else if(state==ACTACK && cnt_bit==RD_TIME) begin
ack<=sda_in; //发数据时,反馈ack是否应答
end
else if( stop_idle ) begin //一次数据发送结束,反馈一个信号
ack<=1;
end
else begin
ack<=0;
end
end
//sda_en
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
sda_en<=1;
end
else if(actack_read||write_actack||sendack_read) begin //接收应答信号 和 读数据时候 为低电平
sda_en<=0;
end
else if( read_sendack ||actack_start||actack_write||actack_stop) begin
sda_en<=1;
end
end
//sda_out
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
sda_out<=1; //空闲为1
end
else if(state==START&& cnt_bit==DOW_TIME ) begin //起始状态,sda由高拉低
sda_out<=0;
end
else if(state==WRITE && cnt_bit==WR_TIME ) begin //写数据状态,scl低电平写数据
sda_out<=wr_din[7-cnt_byte]; //高字节先发
end
else if(read_sendack&&cmd==`CMD_NO) begin //发送应答信号状态, NO_ACK
sda_out<=1;
end
else if( read_sendack ) begin //发送应答信号状态,ACK
sda_out<=0;
end
else if( actack_stop|| sendack_stop ) begin //停止状态先拉低
sda_out<=0;
end
else if( state==STOP && cnt_bit==DOW_TIME ) begin //停止状态又由低拉高
sda_out<=1;
end
end
//sda_in data_flag
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
data_flag<=0;
end
else if(state==READ && cnt_bit==RD_TIME-1 ) begin
data_flag[8-cnt_byte]<=sda_in;
end
end
//done
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
done0<=0;
end
else if( ((state==ACTACK|| state==READ )&&cnt_bit==PAN-4 && cnt_byte==max-1)||stop_idle) begin
//done信号提前3个周期给出去,或者状态结束(反馈到控制模块)
done0<=1;
end
else begin
done0<=0;
end
end
//rd_out rd_out_vld
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
rd_out<=0;
rd_out_vld<=0;
end
else if( state==READ &&cnt_bit==PAN-1 ) begin
rd_out<=data_flag;
rd_out_vld<=1;
end
else begin
rd_out_vld<=0;
end
end
endmodule
IIC控制模块
//IIC控制模块
`include "param.v"
module sio_drive(
input clk ,
input rst_n ,
output reg redy , //写数据进来要求。1代表空闲中,0代表工作中
input data_en , //lut数据传输开始使能
input [24:0] data_l , //lut数据模块
output [7:0] dout , //读出的数据
output dout_vld , //读出有效数据
output reg no_ack , //无应答
//端口模块
output reg [4:0] cmd ,
output reg [7:0] data_i2c , //传给端口模块数据
input [7:0] i2c_din , //端口模块传回数据
input i2c_din_vld ,
input done , //上次数据传输完成标志
input ack //端口模块
);
//
parameter WR_NUM=3'd4, //读操作一共周期数
RD_NUM=3'd5, //写操作一共周期数
WR_ID =8'h78, //摄像头写ID
RD_ID =8'h79; //摄像头读ID
reg [2:0] cnt_byte ;
wire add_cnt_byte ;
wire end_cnt_byte ;
reg [2:0] MAX ;
wire rw_ctrl ;
reg [23:0] data_lut ; //数据暂存
reg wr_rd ;
reg [1:0] per ;
assign rw_ctrl = data_en ? data_l[24]:1'b0; //是写+读 或者只写
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
data_lut<=0;
end
else if(data_en)begin
data_lut<=data_l[23:0] ;
end
end
//wr_rd per
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
per<=0;
wr_rd<=0; //0代表读,1代表写
end
else if( rw_ctrl==1 && data_en ) begin //写模式+读模式
per<=2;
wr_rd<=1;
end
else if(rw_ctrl==0 && data_en) begin //只写模式
per<=1;
wr_rd<=1;
end
else if(cnt_byte==MAX && ack==1) begin //ack只有一个周期
per<=per-1'b1;
wr_rd<=0; //读数据
end
end
//redy
always@(* ) begin
if( per==0 ) begin //per==1 代表空闲状态
redy=1'b1 ;
end
else begin
redy=0 ;
end
end
//no_ack
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
no_ack<=0;
end
else if(cnt_byte>0&&cnt_byte
图像采集模块
//图像采集模块
`include "param.v"
module ov5640_capture(
input clk ,
input rst_n ,
input href ,//行同步
input vsync ,//帧同步
input config_done , //配置模块完成
input [7:0] din ,
//输出
output reg [15:0] dout , //信号与数据和数据有效 在时钟周期内是同步的,
output reg dout_vld , //信号与数据和数据有效 在时钟周期内是同步的,
output reg sop , //信号与数据和数据有效 在时钟周期内是同步的,包头
output reg eop , //信号与数据和数据有效 在时钟周期内是同步的,包尾
output reg [23:0] cnt_dout_vld
);
reg [11:0] cnt_col; //行计数器 2560
wire add_cnt_col;
wire end_cnt_col;
reg [9:0] cnt_row; //场计数器 720
wire add_cnt_row;
wire end_cnt_row;
reg vsync_r; //一帧的起点
reg href_flag;
reg dout_lvd_en;
//帧起点同步
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
vsync_r<=0;
end
else if( vsync && config_done) begin //摄像头配置完成
vsync_r<=1'b1 ;
end
else if(dout_vld) begin
vsync_r<=0;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
href_flag <= 0;
end
else if(dout_lvd_en) begin
href_flag<=0;
end
else if( href )begin
href_flag<=1'b1;
end
else begin
href_flag<=0;
end
end
//行计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_col <= 0;
end
else if(add_cnt_col)begin
if(end_cnt_col)begin
cnt_col <= 0;
end
else begin
cnt_col <= cnt_col + 1'b1;
end
end
end
assign add_cnt_col = href_flag ;
assign end_cnt_col = add_cnt_col && cnt_col ==`COL_MAX-1 ;//1280
//场计数
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_row <= 0;
end
else if(add_cnt_row)begin
if(end_cnt_row)begin
cnt_row <= 0;
end
else begin
cnt_row <= cnt_row + 1'b1;
end
end
end
assign add_cnt_row = end_cnt_col;
assign end_cnt_row = add_cnt_row && cnt_row ==`ROW_MAX-1; //720
always @(posedge clk or negedge rst_n)begin
if(!rst_n) begin
dout<=0;
end
else begin
dout[15-cnt_col[0]*8 -:8] <= din;
end
end
//dout_vld 数据晚一个周期
always @(posedge clk or negedge rst_n)begin
if(!rst_n) begin
dout_vld<=0;
end
else if(add_cnt_col && cnt_col[0]==1)begin
dout_vld<=1'b1;
end
else begin
dout_vld<=0;
end
end
//sop
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sop <= 0;
end
else if( add_cnt_col && cnt_col[0]==1&& vsync_r )begin
sop<=1'b1;
end
else begin
sop<=0;
end
end
always @(*)begin
if(end_cnt_row)begin
eop=1'b1;
end
else begin
eop=1'b0;
end
end
//可修改
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout_lvd_en <= 0;
end
else if(eop)begin
dout_lvd_en<=1'b1;
end
else if( vsync )begin
dout_lvd_en<=1'b0;
end
end
//仿真测试用
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_dout_vld<=0;
end
else if(eop) begin
cnt_dout_vld<=0;
end
else if( add_cnt_col && dout_vld==1'b1) begin
cnt_dout_vld<=cnt_dout_vld+1'b1;
end
end
endmodule
异步FIFO控制模块
此模块采用FIFO18位宽:数据+sop+eop判断
//异步FIFO处理跨时域问题,判断是否丢帧问题
`include "param.v"
//wrfifo 18位
//rdfifo 16位
module fifo_ctrl (
input clk , //100m
input rst_n ,
input clk_in , //84m
input clk_out , //75m
//采集模块数据
input [15:0] din_pixel ,
input din_pixel_vld ,
input sop ,
input eop ,
//vga模块
input req_vga ,
output reg[15:0] data_vga ,
output reg data_vga_vld ,
//wrfifo 读出给sdram数据
output [11:0] wrfifo_rdusedw ,
output [17:0] wrfifo_q ,
input wrfifo_rdreq ,
//rdfifo sdram写进数据
input [15:0] rdfifo_data ,
input rdfifo_wrreq ,
output reg sop_vld , //一帧数据丢失,不能读wrfifo数据
output [10:0] rdfifo_wrusedw ,
output reg[23:0] cnt_num ,
output reg[23:0] cnt_vld
);
//wrfifo
reg [17:0] wrfifo_data ;
//reg eop_vld ;
reg fail ;
reg wrfifo_wrreq ;
wire wrfifo_wrfull ;
//rdfifo
wire rdfifo_rdempty ;
wire [15:0] rdfifo_q ;
wire rdfifo_rdreq ;
reg rdreq_flag ;
wire [11:0] wrfifo_wusedw ;
reg sop_flag ;
wire [17:0] wrfifo_qout ;//wrFIFO输出
wrfifo u_wrfifo(
.aclr (~rst_n ) ,
.data (wrfifo_data ) ,
.rdclk (clk ) ,
.rdreq (rdreq_flag ) ,
.wrclk (clk_in ) ,
.wrreq (wrfifo_wrreq ) ,
.q (wrfifo_qout ) ,
.rdusedw (wrfifo_rdusedw ) ,
.wrfull (wrfifo_wrfull ) ,
.wrusedw (wrfifo_wusedw )
);
//*****************************************WRFIFO*******************************************//
always @(posedge clk_in or negedge rst_n)begin
if(!rst_n)begin
wrfifo_data <= 0;
end
else begin
wrfifo_data<={sop,eop,din_pixel} ;
end
end
//sop_vld为1代表这一帧数据丢失
always @(posedge clk or negedge rst_n)begin //写时钟下
if(!rst_n)begin
sop_vld<=1'b0;
end
else if( wrfifo_rdusedw>4050 )begin
sop_vld <= 1'b1;
end
else if( wrfifo_qout[17] /* sop */) begin //写数据时钟
sop_vld<=1'b0;
end
end
always @( * )begin
if(sop_vld )begin
rdreq_flag=1'b1;
end
else begin
rdreq_flag = wrfifo_rdreq;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_num <= 0;
end
else if(wrfifo_qout[16]) begin
cnt_num<=0;
end
else if(sop_vld==0 &&rdreq_flag )begin
cnt_num<=cnt_num+1'b1;
end
end
assign wrfifo_q=(sop_vld==0)?wrfifo_qout:0;
always @(posedge clk_in or negedge rst_n)begin
if(!rst_n)begin
sop_flag <= 0;
end
else if(sop==1'b1)begin
sop_flag<=1'b1;
end
else if(eop)begin
sop_flag <= 0;
end
end
always @(posedge clk_in or negedge rst_n)begin
if(!rst_n)begin
wrfifo_wrreq <= 0;
end
else if(wrfifo_wrfull==1'b0)begin
wrfifo_wrreq<=(sop_flag&& din_pixel_vld )||sop ;
end
end
//tb测试用
always @(posedge clk_in or negedge rst_n)begin
if(!rst_n)begin
cnt_vld <= 0;
end
else if(sop_flag==0) begin
cnt_vld<=0;
end
else if(wrfifo_wrreq)begin
cnt_vld<=cnt_vld+1'b1 ;
end
end
//************************rdfifo****************************************//
rdfifo u_rdfifo(
.aclr (~rst_n ) ,
.data (rdfifo_data ) ,
.rdclk (clk_out ) ,
.rdreq (rdfifo_rdreq ) ,
.wrclk (clk ) ,
.wrreq (rdfifo_wrreq ) ,
.q (rdfifo_q ) ,
.rdempty (rdfifo_rdempty ) ,
.wrusedw (rdfifo_wrusedw )
);
//data_vga
//data_vga_vld
always@(posedge clk_out or negedge rst_n) begin
if(!rst_n) begin
data_vga<=0;
data_vga_vld<=0;
end
else begin
data_vga<=rdfifo_q;
data_vga_vld<=rdfifo_rdreq;
end
end
assign rdfifo_rdreq= rdfifo_rdempty==1'b0 ? req_vga : 1'b0 ;
endmodule
SDRAM控制模块
SDRAM控制模块
`include "param.v"
module sdram_ctrl (
input clk ,
input rst_n ,
//wrfifo
input [1:0] wrfifo_seop ,
input [11:0] wrfifo_rdusedw ,
input sop_vld ,
//rdfifo
input [10:0] rdfifo_wrusedw ,
output reg [15:0] dout_fifo ,
output reg dout_fifo_vld ,
//接收端口模块数据
input [2:0] done ,
input [15:0] din_ctrl ,
input din_ctrl_vld ,
//传给端口模块
output reg [23:0] addr_iftc ,
output reg wr_en_out ,
output reg rd_en_out ,
output reg sop ,
output reg eop
);
//状态参数
localparam IDLE =5'b0000_1,
ITFCEN=5'b0001_0,
ADDR =5'b0010_0,
WRITE =5'b0100_0,
READ =5'b1000_0;
reg [4:0] state;
reg [4:0] state_next;
wire idle_itfcen;
wire itfcen_addr;
wire idle_addr ;
wire addr_write;
wire addr_read ;
wire write_idle;
wire read_idle ;
//信号参数
wire [15:0] din ;
reg wr_en ;
reg rd_en ;
reg [21:0] addr_wr;
wire add_addr_wr;
wire end_addr_wr;
reg [21:0] addr_rd;
wire add_addr_rd;
wire end_addr_rd;
reg flag ;
reg [1:0] wr_bank ;
reg [1:0] rd_bank ;
reg seop ;
reg bank_turn ;//bank翻转记录
//打拍
reg sop_flag;
wire nedge;
wire pedge;
reg sop_vld_r0;
reg sop_vld_r1;
//********************************sop+打拍***************************************8/
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sop<=0;
eop<=0;
end
else if(state==WRITE||write_idle) begin
eop<=wrfifo_seop[0];
sop<=0;
end
else if(addr_write ) begin
sop<=wrfifo_seop[1];
eop<=0;
end
else begin
sop<=0;
eop<=0;
end
end
//**************************状态机**********************************************/
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state <= IDLE;
end
else begin
state <= state_next;
end
end
always @(*)begin
case(state)
IDLE :begin
if(idle_itfcen) begin
state_next=ITFCEN;
end
else begin
state_next=state;
end
end
ITFCEN :begin
if(itfcen_addr) begin
state_next=ADDR;
end
else begin
state_next=state;
end
end
ADDR: begin
if(addr_write) begin
state_next=WRITE;
end
else if(addr_read) begin
state_next=READ;
end
else begin
state_next=state;
end
end
WRITE:begin
if(write_idle) begin
state_next=IDLE;
end
else begin
state_next=state;
end
end
READ :begin
if(read_idle) begin
state_next=IDLE;
end
else begin
state_next=state;
end
end
default:state_next=IDLE;
endcase
end
assign idle_itfcen =state==IDLE && (wr_en ||rd_en ); //en必须只有一个周期
assign itfcen_addr =state==ITFCEN &&(rd_en_out||wr_en_out) ;//仲裁后,判断读写
assign addr_write=state==ADDR && (flag==1'b1 ); //提示下一周期给数据
assign addr_read =state==ADDR && (flag==1'b0 );//读,给一次地址就行
assign write_idle=state==WRITE && (done[2]==1'b0 );//提示下一周期停止给数据
assign read_idle =state==READ && (done[2]==1'b0 );//done[2]代表一次突发完成
//****************************地址*****************************************/
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
addr_wr <=1'b0;
end
else if(add_addr_wr)begin
if(end_addr_wr)begin
addr_wr <=1'b0;
end
else begin
addr_wr <= addr_wr + `BUSRT_MAX;
end
end
end
assign add_addr_wr = write_idle; //突发完后再加一次
assign end_addr_wr = add_addr_wr && addr_wr ==`FPS-`BUSRT_MAX ; //一帧像素-突发长度1800-1
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
addr_rd <=1'b0;
end
else if(add_addr_rd)begin
if(end_addr_rd)begin
addr_rd <=1'b0;
end
else begin
addr_rd <= addr_rd + `BUSRT_MAX;
end
end
end
assign add_addr_rd = read_idle;
assign end_addr_rd = add_addr_rd && addr_rd == `FPS-`BUSRT_MAX ;
//*******************************************seop***************************************//
///
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
seop <= 1'b1;
end
else if( end_addr_wr )begin
seop <=1'b0;
end
else if(bank_turn==0 ) begin //seop突发写标志,1才能写。0 不能写
seop <= 1'b1;
end
end
// rd_bank, wr_bank, bank_turn状态 写模式翻转记录
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_bank <= 2'b00;
rd_bank <=2'b11;
bank_turn<=1'b0; //状态翻转记录 0代表已经翻转,可以写。1代表还未翻转等待翻转中
end
else if( seop==0 && end_addr_rd )begin
wr_bank <=~wr_bank;
rd_bank <=~rd_bank;
bank_turn<=1'b0;
end
else if( seop==1'b1 && wr_en ) begin
bank_turn<=1'b1; //翻转后
end
end
//bank_flag 第一个bank写完标志
//*****************************突发信号******************************/
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sop_vld_r0 <=1'b0;
sop_vld_r1 <=1'b0;
end
else begin //
sop_vld_r0 <=sop_vld;
sop_vld_r1 <=sop_vld_r0;
end
end
assign pedge=sop_vld_r0&& ~sop_vld_r1;
assign nedge=~sop_vld_r0&& sop_vld_r1;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sop_flag <=1'b0;
end
else if(pedge) begin
sop_flag<=1'b1;
end
else if(nedge) begin
sop_flag<=1'b0;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_en <=1'b0;
end
else if(idle_itfcen) begin
wr_en<=1'b0;
end
else if( (wrfifo_rdusedw >=`BUSRT_MAX-1) && seop==1'b1 && state==IDLE && sop_flag==0 )begin
wr_en<=1'b1; //代表一次突发写
end
end
//rd_en//代表一次突发读
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_en <=1'b0;
end
else if(idle_itfcen) begin
rd_en <=1'b0;
end
else if(/*rd_flag && */rdfifo_wrusedw<`FIFO_L && state==IDLE )begin //读数据,rdfifo数据量小于300,且端口模块空闲中
rd_en<=1'b1;
end
else if(rdfifo_wrusedw>`FIFO_H) begin //读数据,rdfifo数据量大于1600,
rd_en<=1'b0;
end
end
//读写仲裁标志
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <=1'b0;
end
else if(wr_en&&rd_en &&state==IDLE) begin // 同时接到读请求、写请求.
flag<=~flag;
end
else if(wr_en)begin //突发一次写,
flag<=1'b1;
end
else if(rd_en)begin //突发一次读
flag<=1'b0;
end
end
//wr_en_out 传给端口模块的读写使能信号
//rd_en_out
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_en_out <=0 ;
rd_en_out <=0 ;
end
else if( (wr_en_out||rd_en_out)&&state==ITFCEN) begin
wr_en_out <=0 ;
rd_en_out <=0 ;
end
else if( state==ITFCEN)begin
wr_en_out <=flag ;
rd_en_out <=~flag ;
end
else begin
wr_en_out <=0 ;
rd_en_out <=0 ;
end
end
//**************************输出*********************************************/
//addr_iftc
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
addr_iftc <= 0;
end
else if(flag==1'b0)begin
addr_iftc <= {rd_bank,addr_rd};
end
else begin
addr_iftc <= {wr_bank,addr_wr};
end
end
//输出 时序
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout_fifo <= 0;
dout_fifo_vld<=0;
end
else begin
dout_fifo <= din_ctrl;
dout_fifo_vld<=din_ctrl_vld;
end
end
endmodule
SDRAM端口模块
SDRAM端口模块
`include "param.v"
module sdram_itfc(
input clk ,
input rst_n ,
input wr_en , //写使能 .同时地址数据有效
input rd_en , //读使能
input [23:0] addr , //12个地址加2个bank地址
input [15:0] dout_sdram ,
output reg dout_sdram_req ,
output sclk ,
output reg cke ,
output cs_n , //除时钟外,使能型号
output ras_n , //
output cas_n , //
output we_n , //
output reg[1:0] ba_addr , //bank地址
output reg[12:0] sdram_addr , //输进sdram地址
//DQM mask
output reg[1:0] dqm_out ,
inout [15:0] dq ,
//接控制模块
output reg[2:0] done , //反馈信号。地址反馈 及一个数据写完了反馈
output reg[15:0] data_ctrl ,
output reg data_ctrl_vld
);
localparam WAIT =8'b0000_0001, //上电等待20us
PRECH=8'b0000_0010,//预充电
AREF =8'b0000_0100,//刷新
MRS =8'b0000_1000,//模式设置
IDLE =8'b0001_0000,//
ACTI =8'b0010_0000,//bank+row激活 bank,block都是内部划分块名
WRITE=8'b0100_0000,//bank+列激活+写
READ =8'b1000_0000;//bank+列激活+读
reg [3:0] cmd;
reg [7:0] state;
reg [7:0] state_next;
wire wait_prech ;
wire prech_aref ;
wire prech_idle ;
wire aref_mrs ;
wire aref_idle ;
wire mrs_idle ;
wire idle_aref ;
wire idle_acti ;
wire acti_write ;
wire acti_read ;
wire write_prech;
wire read_prech ;
reg [15:0] cnt0;
wire add_cnt0;
wire end_cnt0;
reg [9:0] cnt_ref;
reg ref_flag;
reg [15:0] max; //每个状态cnt0计数最大值
reg flag; //PRECH标志信号
reg wrrd_flag;
reg data_vld_r0; //CL为2的条件下
reg data_vld_r1;
reg data_vld_r2; //列延时为3的情况下选
wire [1:0] bank;
wire [12:0] row ;
wire [8:0] col ;
reg work;
wire [15:0] dq_in ;
reg [15:0] dq_out ;
reg dq_en ;
assign dq_in=dq;
assign dq=dq_en?dq_out:16'bz;
assign bank=addr[23:22];
assign row =addr[21:9] ;
assign col =addr[8:0] ;
always@(posedge clk or negedge rst_n ) begin
if(!rst_n) begin
state<=WAIT;
end
else begin
state<=state_next;
end
end
always@(*) begin
case(state)
WAIT :begin
if(wait_prech) begin
state_next=PRECH;
end
else begin
state_next=state;
end
end
PRECH :begin
if( prech_aref) begin
state_next=AREF;
end
else if(prech_idle) begin
state_next=IDLE;
end
else begin
state_next=state;
end
end
AREF :begin
if(aref_mrs) begin
state_next=MRS;
end
else if(aref_idle) begin
state_next=IDLE;
end
else begin
state_next=state;
end
end
MRS :begin
if(mrs_idle) begin
state_next=IDLE;
end
else begin
state_next=state;
end
end
IDLE :begin
if(idle_aref) begin//优先
state_next=AREF;
end
else if(idle_acti) begin
state_next=ACTI;
end
else begin
state_next=state;
end
end
ACTI :begin
if(acti_write) begin
state_next=WRITE;
end
else if(acti_read) begin
state_next=READ;
end
else begin
state_next=state;
end
end
WRITE :begin
if(write_prech) begin
state_next=PRECH;
end
else begin
state_next=state;
end
end
READ :begin
if(read_prech) begin
state_next=PRECH;
end
else begin
state_next=state;
end
end
default:state_next=IDLE;
endcase
end
assign wait_prech = state== WAIT && ( end_cnt0 );
assign prech_aref = state== PRECH && ( flag==0 && end_cnt0 );
assign prech_idle = state== PRECH && ( flag==1 && end_cnt0 );
assign aref_mrs = state== AREF && ( flag==0 && end_cnt0 );
assign aref_idle = state== AREF && ( flag==1 && end_cnt0 );
assign mrs_idle = state== MRS && ( end_cnt0 );
assign idle_aref = state== IDLE && ( ref_flag==1 );
assign idle_acti = state== IDLE && ( work &&ref_flag==0 ); //ref_flag==1,done就不能发出信号,en就不能为1
assign acti_write = state== ACTI && ( wrrd_flag==0 && end_cnt0 );
assign acti_read = state== ACTI && ( wrrd_flag==1 && end_cnt0 );
assign write_prech = state== WRITE && ( end_cnt0 ); //数据写完后还要延迟2个clk
assign read_prech = state== READ && ( end_cnt0 ); //数据读完都可以立即发下一命令
//cnt_max
always@( * ) begin
case(state)
WAIT : max =`TIME_WAIT ; //20us
PRECH : max =`TIME_TRP ; //预充电20ns
AREF : max =`TIME_TRC ; //刷新70ns
MRS : max =`TIME_TMRS ; //模式寄存器2clk
IDLE : max = 0 ; //
ACTI : max =`TIME_TRCD ; //激活行20ns//2
WRITE : max =(`BUSRT_MAX+2) ; //突发长度完+2clk恢复周期
READ : max = `BUSRT_MAX ; //突发长度+0clk
default:max =0;
endcase
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt0<=0;
end
else if(end_cnt0) begin
cnt0<=0;
end
else if(add_cnt0) begin
cnt0<=cnt0+1'b1;
end
end
assign add_cnt0=state!=IDLE ;
assign end_cnt0=add_cnt0 && cnt0==max-1;
//flag //预充电下一状态标志//开机到idle后才开始计数
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
flag<=0;
end
else if(mrs_idle) begin
flag<=1; //第一次模式寄存器标志
end
end
//cnt_ref; //定时刷新标志
//ref_flag;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cnt_ref<=0;
end
else if(cnt_ref==`TIME_REF-1) begin //780周期
cnt_ref<=1;
end
else if( flag ) begin
cnt_ref<=cnt_ref+1'b1;
end
end
//刷新标志
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
ref_flag<=0;
end
else if(cnt_ref==`TIME_REF-1) begin //780周期
ref_flag<=1;
end
else if(state==AREF) begin
ref_flag<=0;
end
end
//wrrd_flag //读或者写标志
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
wrrd_flag<=0;
end
else if(wr_en) begin //读写标志信号
wrrd_flag<=0;
end
else if(rd_en) begin
wrrd_flag<=1;
end
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
work <= 0;
end
else if(state==ACTI)begin
work <= 0;
end
else if( wr_en||rd_en )begin
work<=1'b1;
end
end
//命令cmd
assign cs_n = cmd[3] ;
assign ras_n= cmd[2] ;
assign cas_n= cmd[1] ;
assign we_n = cmd[0] ;
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cmd<=4'b1111;
end
else if(wait_prech) begin //预充电 0010 同时外加 地址a10 为1
cmd<=`CMD_PREC; //3
end
else if(prech_aref) begin //刷新0001
cmd<=`CMD_AREF; //7
end
else if(aref_mrs) begin //模式寄存器0000
cmd<=`CMD_MRS; //2
end
else if(idle_aref) begin //刷新0001
cmd<=`CMD_AREF; //7
end
else if(idle_acti) begin //激活行0011
cmd<=`CMD_ACTI; //3
end
else if(acti_write) begin //写 0100
cmd<=`CMD_WRITE;
end
else if(acti_read) begin //读 0101
cmd<=`CMD_RDAD;
end
else if(read_prech||write_prech) begin //预充电0010
cmd<=`CMD_PREC; //同时外加 地址a10 为1
end
else begin //空命令
cmd<=`CMD_NOP;
end
end
// sclk
//cke
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
cke<=0;
end
else begin
cke<=1;
end
end
assign sclk=~clk;//~sclk延迟半个周期
//ba_addr
//sdram_addr
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
ba_addr <= 2'b00;
sdram_addr<= 13'b0;
end
else if(wait_prech||write_prech||read_prech) begin //预充电模式
sdram_addr<=13'd1024; //a10为1,所有预充电所有,不需要bank地址
end
else if(aref_mrs) begin
ba_addr <= 2'b00;
sdram_addr<={ 3'b000 ,`OP , 2'b00 , `CAS_LTY , `BT , `BL}; //模式寄存器
end
else if(idle_acti) begin //激活
ba_addr <= bank;//bank
sdram_addr<=row; //行地址
end
else if(acti_write||acti_read) begin
ba_addr <= bank;//bank
sdram_addr<= col; //列地址
end
end
//done反馈信号
always@(*) begin
if(state!=IDLE||ref_flag ||work==1'b1 ) begin //780周期计数
done=3'b100 ; //正在工作中 ,不能给读写命令
end
else begin
done=3'b000;
end
end
//写数据
//dout_sdram_req
always@( posedge clk or negedge rst_n ) begin
if(!rst_n) begin
dout_sdram_req<=0;
end
else if(state==WRITE && cnt0==max-4) begin
dout_sdram_req<=0; //提示下下个周期停止给写数据,数据时序输出,打一拍
end
else if(state== ACTI&&wrrd_flag==0 && add_cnt0 && cnt0==max-2) begin
dout_sdram_req<=1'b1; //提示下下个周期给写数据+列地址,数据时序输出,打一拍
end
end
always@( posedge clk or negedge rst_n ) begin
if(!rst_n) begin
dq_out<=0;
end
else begin
dq_out<=dout_sdram;
end
end
//读数据
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
data_vld_r0<=0;
end
else if(read_prech) begin
data_vld_r0<=0;
end
else if( acti_read ) begin
data_vld_r0<=1; //突发写 ,经过CL列延迟个周期后,出数据
end
end
//组合输出
always@(*) begin
if(!rst_n) begin
data_ctrl=0;
end
else begin
data_ctrl=dq_in;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
data_ctrl_vld <=0;
data_vld_r1<=0;
end
else begin
data_vld_r1<=data_vld_r0;
//data_vld_r2<=data_vld_r1; //列延时为3的情况下选
data_ctrl_vld<=data_vld_r1;
end
end
// dq_en /只在写的状态下拉高
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
dq_en<=0;
end
else if(acti_write) begin
dq_en<=1;
end
else if(state==WRITE&&cnt0==max-3 ) begin
dq_en<=0;
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
dqm_out<=0;
end
else if(state==WRITE&&cnt0==max-3 ) begin //写后要拉高dqm_out
dqm_out<=2'b11;
end
else if(state!=WRITE) begin
dqm_out<=0;
end
end
endmodule
VGA显示模块
VGA显示模块
`include "param.v"
module vga_itfc(
input clk ,
input rst_n ,
input [15:0] din ,
input din_vld , //数据有效
output reg req ,//请求数据信号
output reg [15:0]dout_rgb,
output reg hsync ,
output reg vhync
);
reg [11:0] cnt_h; //行计数器
wire add_cnt_h;
wire end_cnt_h;
reg [9:0] cnt_v; //场计数器
wire add_cnt_v;
wire end_cnt_v;
reg h_vld ; //行有效数据
reg v_vld ; //场有效数据
reg rdreq ;
wire wrreq ;
wire empty ;
wire full ;
wire [15:0] q ;
wire [9:0] usedw ;
// cnt_h
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_h <= 0;
end
else if(add_cnt_h)begin
if(end_cnt_h)begin
cnt_h <= 0;
end
else begin
cnt_h <= cnt_h + 1'b1;
end
end
end
assign add_cnt_h = 1'b1; //上电即开始
assign end_cnt_h = add_cnt_h && cnt_h ==`H_TP-1 ; //1650
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_v <= 0;
end
else if(add_cnt_v)begin
if(end_cnt_v)begin
cnt_v <= 0;
end
else begin
cnt_v <= cnt_v + 1'b1;
end
end
end
assign add_cnt_v = end_cnt_h;
assign end_cnt_v = add_cnt_v && cnt_v == `V_TP-1; //750
//h_vld行有效数据
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
h_vld <= 0;
end
else if(add_cnt_h && cnt_h==(`H_SW+`H_BP-1) )begin //
h_vld<=1'b1;
end
else if(add_cnt_h&&cnt_h== (`H_SW +`H_BP +`H_AP-1) )begin
h_vld<=0;
end
end
//v_vld
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
v_vld <= 0;
end
else if(add_cnt_v &&cnt_v== (`V_SW +`V_BP-1 ) )begin
v_vld<=1'b1;
end
else if(add_cnt_v && cnt_v==(`V_SW +`V_BP +`V_AP-1) )begin
v_vld<=0;
end
end
//rdreq
always @( * )begin
if( v_vld && h_vld && empty==0 )begin
rdreq=1'b1;
end
else begin
rdreq=0;
end
end
//dout_rgb
always @(* )begin
if(!rst_n)begin
dout_rgb = 0;
end
else if( rdreq )begin
dout_rgb=q;
end
else begin
dout_rgb=0;
end
end
//hsync
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
hsync <= 0;
end
else if( add_cnt_h && cnt_h== `H_SW-1 )begin
hsync<=1'b1;
end
else if(end_cnt_h)begin
hsync<=0;
end
end
//vhync
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
vhync <= 0;
end
else if( add_cnt_v && cnt_v== `V_SW-1 )begin
vhync<=1'b1;
end
else if(end_cnt_v)begin
vhync<=0;
end
end
buffer u_buffer(
.clock ( clk ) ,
.data ( din ) ,
.rdreq ( rdreq ) ,
.wrreq ( wrreq ) ,
.empty ( empty ) ,
.full ( full ) ,
.q ( q ) ,
.usedw ( usedw )
);
//req向sdram读数据
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
req <= 0;
end
else if( usedw<200 )begin
req<=1'b1;
end
else if(usedw>900)begin
req<=0;
end
end
assign wrreq= (full==0) ? din_vld : 1'b0 ;
endmodule
参数模块
/**************************************sdram************************************/
//sdram工作参数
//`define BUSRT_1
//`define BUSRT_2
//`define BUSRT_4
//`define BUSRT_8
`define BUSRT_512 //突发长度
`ifdef BUSRT_1
`define BUSRT_MAX 1
`define BL 3'b000 //A0-A2
`elsif BUSRT_2
`define BUSRT_MAX 2
`define BL 3'b001
`elsif BUSRT_4
`define BUSRT_MAX 4
`define BL 3'b010
`elsif BUSRT_8
`define BUSRT_MAX 8
`define BL 3'b011
`define FIFO_L 300 //写FIFO的数量低值
`define FIFO_H 1500 //写FIFO的数量高值
`elsif BUSRT_512
`define BUSRT_MAX 512
`define BL 3'b111
`define FIFO_L 300 //写FIFO的数量低值
`define FIFO_H 1500 //写FIFO的数量高值
`endif
//连续突发
//`define BT 1 //A3
`define BT 1'b0
//突发写/单写
//`define OP 1
`define OP 1'b0 //A9
`define CAS_LTY1 //列选 延迟
//`define CAS_LTY2
`ifdef CAS_LTY1 //列选通延迟设置 延迟 A4-A6
`define CAS_LTY 3'b010
`define CL 2
`elsif CAS_LTY2
`define CAS_LTY 3'b011
`define CL 3
`endif
`define ADDR_MAX 16777215
//时间参数
`define TIME_TRC 7 //刷新命令生效时间
`define TIME_TRP 3 //预充电时间
`define TIME_TMRS 2 //2个clk时钟周期 模式寄存器
`define TIME_WAIT 20000 //上电等待20us
`define TIME_TRCD 3 //激活命令
`define TIME_REF 780 //自动刷新间隔
//cmd命令参数 cs_n使能,ras_n行选,cas_n列选,we_n写使能
`define CMD_WRITE 4'b0100 //写
`define CMD_RDAD 4'b0101 //读
`define CMD_ACTI 4'b0011 //激活
`define CMD_NOP 4'b0111 //空操作
`define CMD_MRS 4'b0000 //模式寄存器
`define CMD_AREF 4'b0001 //自刷新 A10==1
`define CMD_PREC 4'b0010 //预充电 所有bank
/**************************************图像采集模块************************************/
`define ROW_MAX 720 //一共720行
`define COL_MAX 2560 //一行1280个像素,一个像素2字节
`define FPS 921600 //一帧数据有多少个数据 9216
`define BUSRT_NUM 1800 //突发1800次,一帧数据完
/**************************************vga端口模块************************************/
//vga端口模块 1280*720像素
`define H_FP 110 //行前沿
`define H_SW 40 //行同步脉冲
`define H_BP 220 //行后沿
`define H_AP 1280 //行有效像素
`define H_TP 1650 //行总时序
`define V_FP 5 //场前沿
`define V_SW 5 //场同步脉冲
`define V_BP 20 //场后沿
`define V_AP 720 //场有效像素
`define V_TP 750 //场总时序
/**************************************I2C端口模块************************************/
//I2C端口模块
`define CMD_EN 5'b000_01 //[0]表示为开始工作
`define CMD_RDST 5'b000_10 //[1]表示发起始位
`define CMD_WR 5'b001_00 //[2] 表示写数据
`define CMD_RD 5'b010_00 //[3]表示接收数据
`define CMD_NO 5'b100_00 //[4] 表示发送停止位不应答
备注:fifo_ctrl模块中调用了两个FIFO深度为1024,宽度一个为18bit(包含sop,eop),一个16bit;
VGA显示模块调用FIFO深度为1024,宽度为16bit;
另外调用pll生成时钟24M(摄像头配置输输出时钟)、75M(VGA显示模块输出时钟)、100M(SDRAM时钟)