特权的DIY数码相框总结

时间:2021-07-02 16:14:41

本文是对DIY数码相框一章的总结,重在对整个数据通路的理解。

具体的需求参见该书的相关章节。整个数据流是:

sd卡控制模块,通过spi接口读sd卡中的数据,给wrfifo进行缓存,满了256byte后wrfifo通知sdram读取fifo中的数据。sdram中满了一帧,变传输给rdfifo中,rdfifo中提取出rgb分量组成8位传给vga显示。

整个的时钟由pll提供。

画出整体的各个模块的连接框图会很清晰的看到连接关系和数据通路。

下面分开叙述:

1、时钟部分:

例化pll,产生了3路时钟,其中一路为有3ns相移的100MHZ时钟直接给了sdram。这个是sdram的要求。其中还用到了特权一直强调的“异步复位、同步释放”。

2、spi部分:

由于是全双工,与iic不同,没有那么复杂,有单独的时钟线,所以很方便。spi的4根线硬件接口接到sd卡,另外一边,tx-en,rx-en接受ctrl模块发出的读写使能控制,同时tx_rdy与rx_rdy输出到ctrl模块告知数据已准备好。这个部分的顺序控制就由这个rdy和en信号来决定。

3、sd_ctrl部分

这个的一边是接的spi的部分,另外一边是3个信号导出去接到fifo模块上。sd_dout这里是数据发送8位或16位的接口,sd_fifowr表示写fifo的请求,sdwrad_clr表示下一幅图片了,清楚fifo中的上一幅图的内容。

sd卡,首先是40us的等待初始化,一个计数器wire delay_done = (delay_cnt == 10'd1000);后续根据delay_done来判断前个步骤是否可以。接下来是一个标准的三段式状态机来实现初始化,第一段状态更新,第二段根据信号的变化(一个sd卡的配置指令是否发完,接受到的sd卡的反馈是否正常)实现不同的状态跳转,第三段根据不同的状态(sdinit_nstate提前一个状态)提前准备好要发送的指令(通过cmd、arg等寄存器)。接下来是发送指令状态机,也是3段式。第一段状态更新,第二段状态机的跳转,根据上一个状态机的那几个状态应该发送什么指令,设置了一些变量,根据这些变量来实现跳转。第3段根据前面cmd的状态来实现spi_tx_enr等控制信号来控制spi是读还是写。

着重看下一些控制信号,spi_tx_en是如何产生来控制spi写的?如CMD_CLKS状态下,if(spi_tx_enr) spi_tx_dbr <= {2'b01,cmd};//起始字节命令送人数据发送寄存器,而前一个状态下会讲spi_tx_enr打开。当sd卡处于CMD_RD状态:

spi_rx_dbr <= spi_rx_db; //接收SPI响应字节数据

if(spi_rx_enr) cnt512 <= cnt512+1'b1;

是361行if(cnt512 == 10'd514)主动去停止读取这个信号:

CMD_RD: begin
     if(cnt512 == 10'd514) cmd_nstate <= CMD_DELAY;//直到读取512字节+2字节CRC完成
     else cmd_nstate <= CMD_RD;

SD_DELAY中有一个5s的延时让状态机循环,这样显示下一幅图。sd_fifowr当每收到一个数据,就打开提示要写到fifo中去,512个byte写完就不写了。

wire sdwrad_clr = (cnt5s == 28'hffffff0);   每到5s就把后面的fifo清空一次开始下一次的图片的读取了

4、fifo部分:

前面通过sd_fifowr来表示需要向fifo中写数据,sdwrad_clr 表示需要清屏显示下一幅。这样通过数据链路传过来,只有这几个信号启动了,fifo才会开始工作。

fifo中画下内部的顶层框图就知道,其例化了256个16bit的wrfifo,一个rdfifo,还有一个ram。均是调用的ip核。

其一方面接受sd卡传进来的数据与控制信号,另一方面与sdram联系紧密,wrfifo接受sd卡传来的数据,向sdram发出写请求,发出写的地址和数据总线;rdfifo发出读请求,发出读请求的地址和接受sdram传来的数据的总线,最后接受vga的使能控制,经由ram发出提取整理后的8位VGA数据经过dis_data传出。

fifo的接口,原理就不说了有一个wrf_use 9位的信号表示wrfifo中已经写满了多少数据,这个会判断转化成对是sdram的写请求。读也类似,不过他的数据输出重组后给了ram模块。

分开来看:fifo_ctrl中捕捉wr和rd的ack,产生写入sdram的地址,从0开始,一个数据加1。读的话,从sdram'的尾部地址开始读。合并产生实际的地址输出给sdram。后面是bmp的解码,rdfifo读出的数据解码重组成rr_din后写入ram中,最后dis_data直接连到ram的输出。

下面以信号的端口来分析下:sdram_wr_req = (wrf_use >= 9'd256);wrf_use 应该会变小,所以sdram那边应该是有一次请求就得读出fifo中所有的数据。sdram_rd_req = (rdf_use < 9'd480) & vga_validr当rdfifo中没有数据且那边vga要数据。rdfifo只有通过判断fifo存了多少数据给sdram一个反馈,并没有别的反馈,所以sdram王rdfifo中写完全是由sdram来操作的。

另外vga的解码,是4个byte中的后3个中,取2/3/3个位拼成一个8位的有效data

信号流路:wrf_wrreq是sd传给fifo的使能信号,表示要写了,fifo这边这个控制信号只有为1的情况下,才能使能wrfifo同时cnt才能计数。当wrfifo接受大于256是才回使能sdram_wr_req从而控制sdram开始接受。而这里的rdfifo也是要fifo中数据少且vga要数据才发出sdram_rd_req使能sdram往这里发,rdfifo直接给到ram,ram完全受vga的控制了。

5、sdram

sdram这里的功能是接受缓存sd卡读出的数据,然后发送给rdfifo解码后给到ram由vga显示。

sdram由3个部分cmd、ctrl、wr_data组成。ctrl中,首先是200us延时,然后是初始化状态机,多次刷新,根据的是一个计数器的时间变化,1个计数器使每60ms产生一个sdram_ref_req刷新请求。接下来是一个sdram的读写的状态机,包含了状态机改怎么变和读写信号的变化。sdram_wr_ack等信号是根据处于哪一个状态且时序计数器ok'了。最后是一个时序计数器状态机,产生正确的时序。注意这里面的“`end_trp”在para文件中可以发现是`define end_trpcnt_clk_r== TRP_CLK,是一个延时。cnt_rst_n是0还是1来判断cnt_clk_r是被清零还是接着自己加,从而控制时序。

值得注意的是,这里的ctrl模块把init_state,work_state,cnt_clk均导出去给了wrdata和cmd模块做同步,这个是不常见的。因为cmd主要是跟硬件连接,这ctrl中的init_state和work_state中,cmd模块需要对cke等信号写0写1,而wr_data只连接了work_state,其与硬件的连接只有16位的数据总线。

cmd模块中,根据前面的初始化的状态下不同的状态,设置{sdram_cke,sdram_cs_n,sdram_ras_n,sdram_cas_n,sdram_we_n} = sdram_cmd_r这些信号线的高低,同时给出sdram_ba_r,sdram_addr_r地址的值。当初始化完了,判断工作下的状态,还是一样,不同的状态下这几个值不一样。这几个值都是直接接到sdram硬件驱动的。

wr_data中,根据work_state的状态来判断是读还是写。

分析整个数据流路,只有在work_state状态机中,有fifo中来的sdram_wr_req或者sdram_rd_req才能离开idle状态。cmd模块和wrdata模块都是根据ctrl模块中的状态和计数来发出相应的信号的。

由于工程文件有问题,所以这里面没有体现sdram是否是主动停止了对rdfifo的写还是由rdfifo主动发命令控制sdram停止对其的写。

6、vga_ctl

比较简单,重点关注rdf_rdreq与vga_valid信号的产生。


虽然整个的数据流路我们分析了,但是每个点有很多细节。比如sd卡、bmp的格式等等。其中最值得关心的是sdram的相关的信息,比如存在那个地址?怎么读取等等。

后面参考了《FPGA设计技巧与案例开发详解》一书,相关部分的整理见后续的博客。

                                                                                                                                                                                                                             2016-5-16于lab