本章节分析基于以太网图像传输工程,其实上周就已经做完,只不过实在是难以总结,代码的理解有时候真的要自己去逐词逐句的分析,不然也就只能理解其过程,无法重新复现,工程下载链接:
http://www.corecourse.cn/forum.php?mod=viewthread&tid=27941&highlight=以太网
接下来对OV5640_eth_udp进行重点分析,整个项目分成四部分,分别是OV5640配置驱动模块、SDRAM配置驱动模块、UDP发送模块,UDP-SDRAM数据读取模块。
一、OV5640配置模块
在配置表中,数据的高16位为寄存器的地址,低8位为要向该寄存器写入的控制字。
调用时:
`elsif OV5640
wire [23:0]lut;
localparam lut_size = 252;
localparam device_id = 8'h78;
localparam addr_mode = 1'b1;
ov5640_init_table cmos_init_table(
.addr(cnt),
.clk(Clk),
.q(lut)
);
assign addr = lut[23:8];
assign wrdata = lut[7:0];
实现方式,在初始化模块执行一个在上电之后,使用一个计数器进行计数,经过20ms也就是20_000_000ns,所以将计数到20ms左右,20’hffffe时候产生一个高脉冲,清零配置模块计数器,开始数据写入配置操作。
在每次读写操作完成且收到应答信号后,配置表映射地址加一,读取出下一个要配置的寄存器地址及数据。
在从配置表读取出的数据量小于lut_size时,执行状态机,该状态机的机制就是:
空闲时为IDLE,在检测到Go信号之后跳转到state1,也就是发出读写请求(wrreg_reg),然后跳转到state2等待读写操作完毕,再返回state2。
在i2c_control.v模块检测到读写请求后,开启计数器计数使能,同时根据cmd指令中是否包含产生起始信号指令,是的话就跳转到GNE_STA,根据cnt计数器对SDA、SCL进行操作。
在cnt=3时,将SCL拉低,为接下来的数据写入做准备,同时根据CMD中是否存在写地址跳转到相应的WR_DATA状态。
在WR_DATA状态中,使用线性序列机进行数据操作,将一bit数据操作分成四部分,
(1)、将数据放到SDA总线,SDA输出使能
(2)、将SCL拉高,SCL的上升沿将数据送入到OV5640中
(3)、SCL保持高电平
(4)、将SCL拉低,为下一次数据写入做准备。
在cnt==31时,跳转到Check_ACK状态。
在Check_ACK状态,cnt从0计数到3,在cnt=3时跳转,分成四部分:
(1)、 关闭输出使能,即将i2c_sdat_oe <= 1'd0,同时将SCL拉低(防止SDA变化导致误触发)
(2)、将SCL拉高,在SCL的上升沿将SDA应答数据读出
(3)、将应答信号给ack_o,SCL保持为高
(4)、将SCL拉低,为下一次的数据传输做准备。
之后根据是否有GNT_STO产生停止信号,分为四个步骤:
(1)、SDA输出使能打开,将SDA拉低
(2)、将SCL拉高。
(3~4)、保持,4时跳转到IDLE。
在5640配置完成之后,丢弃前10帧图像。
该部分包含以太网发送eth_send、CRC校验crc32_d4、校验和checksum、异步fifo send_dcfifo四个模块。
可异步清零,作为一个异步fifo,该模块的读写操作分开。
对于写数据操作:
当img_send模块的写入数据有效且package_state为1时即为有效的数据写入请求。
为何写请求操作还要受到package_state的控制?
因为package是eop一个周期的延迟,eop为1时vcnt_full为1,此时一帧数据传输完毕,将进行场消隐操作,所以关闭异步fifo写入使能,在检测到帧起始信号后再将package_state信号拉高,允许数据写入。(等待fifo有足够的写入空间)
那么此时从SDRAM中读取出的数据如何处理?(删除此部分)
从sdram的读取请求信号是收到state控制的,在state==1时,向sdram发出读取请求信号,而跳转到state==1说明跳转前fifo_available,在写入一行像素数据后,状态机跳回到state==0,此时,可能不会有fifo_available信号,需要等待fifoavaliable从而将sop拉高,所以此时,如fifoavailable==0时,不会发出SDRAM读取请求,sop也不会为1,在fifoavailbale==1时,sop为1,继续下一次的数据写入。
对于读数据操作:
udp数据长度为1282,当DCFIFO中数据长度大于1282x2时(读取数据宽度为4),发送使能信号拉高一个周期,在TX_GO拉高时对源主机mac地址、数据长度进行寄存。同时将发送使能控制寄存器拉高,开启帧数据发送,对于其中的数据部分,根据cnt控制是否使能数据发送。
该模块从SDRAM读取像素数据,将像素数据发送到UDP发送模块的异步FIFO中。
在每次上电后,需要对SDRAM中的数据进行清除,所以使用frame_sync作为标志信号进行清零操作。
延时一段时间后,将send_en置一。
在send_en==1时,判断fifo_available信号,在send模块的fifo有空闲空间时,state==1,每发送完一行数据,跳转回state==0。
在state==1时,对hcnt进行计数,计数到一行的最大值,在计数到最大值之后将hcnt_full置一,为什么要有hcnt_full,是因为在hcnt=0时发送的是行号,在hcnt=1时发送第一列,在hcnt=WIDTH-1时候发送第W-1列,在hcnt_full时发送最后一列的像素数据。
在hcnt_full等于1时,将vcnt加1,加满后清零。