FPGA-音频模块开发(三)

frame_read_write.v

从RTL视图中可以看出,该模块由多个子模块(afifo_64i_16o_64、afifo_16i_64o_256、frame_fifo_write、frame_fifo_read)组成。

其中afifo_64i_16o_64.v作为一个连接audio_record_play_ctrl与frame_fifo_write的跨时域数据交互,前者的时钟为系统时钟,后者的时钟为MEM_CLK。我们可以看到在RTL视图中连接到这个fifo中几个主要的信号:

  1. 其中有两个异步时钟,read_clk(FPGA CLK)以及mem_clk(SDRAM CLK)
  2. 写使能/wrreq(write_en)、读使能/rdreq(wr_burst_data_req)

其中write_en由之前我们分析的模块audio_record_play_ctrl输出的

而wr_burst_data_req是由sdram_core这个模块输出的,前文我们已经详细介绍了SDRAM的读写控制。

  1. rdusedw指fifo里有多少数据可以读。
  2. aclr(write_fifo_aclr) 清空信号。
  3. data/q表示输入输出的数据,输入数据的位宽64位,输出数据的位宽16位。

frame_fifo_write.v主要负责将fifo中输出的数据,按照我们设定好的wr_burst_len、wr_burst_addr以及write_burst_data写入SDRAM控制器。它更像是在fifo和SDRAM中的一个粘合逻辑。

那么关于afifo_16i_64o_256以及frame_fifo_read就不需要多加赘述了。

接下来我们仔细看一下frame_fifo_write/frame_fifo_read这两个子模块。

frame_fifo_write.v

 

看任何逻辑模块的时候,我们首先观察该模块的状态机:S_IDLE、S_ACK、S_CHECK_FIFO、S_WRITE_BURST、S_WRITE_BURST_END、S_END。

在S_IDLE中我们看到了信号write_req_d2,查找信号来源,我们发现它是对write_req这个信号的三级锁存,write_req这个信号是由audio_record_play_ctrl模块输出到该模块,它也有CLK跨时钟域到MEM_CLK,所以我们进行了三级锁存,防止出现亚稳态,造成系统紊乱。我们接着回到状态机,当write_req_d2==1时,进入S_ACK这个状态,当时我不解write_req与write_req_ack这组信号的作用,原来我们可以在通过这组反馈信号,也是进入S_CHECK_FOFO之前将FIFO清空(fifo_aclr置1),并确定了write_burst_addr。接下来当audio_record_play_ctrl模块收到write_req_ack反馈信号,将write_req置0,从而进入S_CHECK_FIFO,在这里我们看到了一条判断语句else if(rdusedw >= BURST_SIZE),这也是我之前在FIFO拓展里提到的,他们都是基于16位数据的个数进行判断的。如果fifo中可读的数据个数大于等于我们一次要写入的数据长度,进入S_WRITE_BURST,同时我们将BURST_SIZE赋给write_burst_len,传递给SDRAM_CORE,并且wr_burst_req置1(使SDRAM控制器进入写操作)。之后进入到S_WRITE_BURST,这里我们看到wr_burst_finish信号,之前我们在SDRAM TEST这个实验里有讲过这个信号,它是一个标志信号,标志着完成了一次长度为wr_burst_len的连续写,当wr_burst_finish==1b1,wr_burst_req置0。有朋友会问write_cnt又是什么呢?write_len_latch又是什么?它和前文提到的write_burst_len又有什么关联?好!笔者带你们一一解惑,首先我们先来看write_len_latch这个寄存器,追根溯源,在S_ACK这个状态里,我们发现了它,write_len_latch<=write_len_d1;write_len_d1是对write_len的二级锁存。终于找到了它的真身,原来是外部输入进来的信号,在顶层文件里可以定义它的数值。之前有看过我SDRAM例程的同学们可以知道,每一确定bank,确定行地址的每一列储存一个16位的data,wr_burst_len就是在我下达一次write_burst_req(突发写请求)需要连续写的长度。而write_len_latch是顶层设定的目标写入的数据长度。这里我举一个较为恰当的例子,可以让同学们快速吸收,write_len_latch就像是领导下达的任务量,而write_burst_len像你每天能做到的最大工作量,而rduse>=BURST_SIZE就像检查你的知识储备是否可以胜任这项工作,通过之后给了你一次工作机会(write_burst_req==1),然后你开始着手项目,开始第一天的工作(state==S_WRITE_BURST),劳累一天(wr_burst_finish==1),做好报告(write_cnt<=write_cnt+BURST_SIZE),记录工作进度(wr_burst_addr<=wr_burst_addr+BURST_SIZE),这时候领导开始查阅你的报告,考察你的工作进度如何,是否完成了它的任务(write_cnt<write_len_latch),领导向来普遍都是理想主义的剥削者,在它眼里你应该很快的完成,他不会在乎你一天能做多少,没有完成它还会继续考核你(state==S_CHECK_FIFO),继续检验你的能力(rdusedw>=BURST_SIZE)是否可以胜任这项工作,日复一日,知道你完成工作,恭喜你完成了这个项目(state==S_END),你以为等待你的是鲜花?掌声?升职加薪吗?NO,你等来的只是大饼和新的工作(state==S_IDLE)。

状态机里我们常常看到这样一条判断语句if(write_req_d2==1b1),这是为什么呢?联想我们做的是一个什么项目?是一个可以完成录制播放功能的项目,如果没有这条,会出现,一旦我们按下一次,就不会停止。它起到了一个可以随时终止,随时录制的效果。

frame_fifo_read.v

这里与之前我们讲到的frame_fifo_write.v基本上的逻辑框架一致,唯一我们需要注意的是其中有一条判断逻辑wrusedw<(FIFO_DEPTH-BURST_SIZE),wrusedw指在FIFO中已经写入多少数据,而FIFO_DEPTH-BURST_SIZE是指FIFO中是否还够写入BURST_SIZE长度的数据。

除此之外我还有几点比较想讲一下,就是这两个FIFO。其中afifo_64i_16o_64作为写操作的数据缓冲(write_buffer),而afifo_16i_64o_256作为读操作的数据缓冲(read_buffer)。

write_buffer:

写使能 write_en

按键按下后,record信号置1,而此时如果WM8731完成了一次AD转换,并输出了64位的数字信号,data_valid置1。

write_en=record&data_valid,则写使能为1。

读使能 wr_burst_data_req

当sdram_core.v这个模块中输入的信号wr_burst_req==1,进入到写突发状态,会output

wr_burst_data_req,这也是为什么在frame_read_write.v中要有CHECK FIFO这个状态,因为要有长达BURST_LEN长度CLK的wr_burst_data_req,如果无法保证数据长度,有可能造成数据丢失。

read_buffer:

写使能 rd_burst_data_valid

读使能 read_en

按键释放后,play置1,我理解的这段代码所描述的read_data_en是对要在下一个周期DA转换的64位数据的数据请求,read_en=play&read_data_en。

  • 3
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值