困住整整一周了,工作进行不下去,中午偶遇导师,指导意见是有两种可能:
1. FPGA编译器优化代码,可以考虑把综合过程中所有的warning排查一下
2. verilog里有不可综合的语句。
又及,原有的功能模块完全正确,自己改写的不行,导师说那是自己写的时序不对,仿正确的模块看modesim里自己写的和正确的模块时序差在哪里,修改自己的。
添加各种(*keep="true"*),未果
排查所有warning,没看到会和这个问题相关
相信大神导师写的代码不会有问题
一下午加晚上各种折腾,甚至画卡诺图与非门硬写了一个bit_mask,嗯,AHB总线的HSIZE+ADDR选择屏蔽哪个字节的代码有了:
等综合的时候胡乱看modelsim,突然发现了个小毛刺:
开始怀疑写的时候没有对齐,引出hready信号加在写使能寄存器控制端,最后一遍综合,没有报希望,开始收拾桌子,打算明天再搞了,然后,生活永远比电视剧精彩,峰回路转,柳暗花明。
其实最开始分析代码的时候考虑过这个问题,还试了,只是加在了byte_mask寄存器的控制端,无效。当时也隐约考虑过这个写使能信号好可疑的样子,然后...不知道为什么没有追究下去,中间modelsim仿真看波形各种折腾两天。
想起回家时候爷爷经常叨叨我的话,现在觉得好有道理。
问题找到了,第二天用了大半个下午分析原因:
正常的总线读写时序应该是这样:
因为加了HREADY控制就好了,本来是怀疑第二拍写数据的时候数据没有准备好,那HREADY定然是低电平,像这样:
怎么分析都觉得好有道理的样子,嗯,定然是这样,由于没有用HREADY来控制写使能信号,导致寄存器在不该更新的时候更新了。可是仔细推敲,又傻了,因为mask信号是用HREADY控制的,如果数据没有准备好,mask不会变,进而写入的还是会正确。
仔细再想,觉得问题不是出在总线数据没准备好,是HREADY有效总会比其他控制信号有效到的晚,是这样:
根据verilog,写使能信号
ce_re = HSEL&HWRITE&HTRANS[1]&HADDR[相应的地址]
如果在时钟上升沿,这些信号已经都有效了,就会导致不用HREADY控制的写使能寄存器比期待的提前一拍置位(写使能应该在写数据周期有效,而不应该在写地址周期有效),而此刻mask信号还在等HREADY,不会有效,从而导致写寄存器错误。
附带两个小现象也解释的通:
1. 完全相同的逻辑,m0p没有这个问题 - m0p访问指令、数据、外设都用这同一组总线,总线表示自己很忙,控制信号可能不会跑的那么快(只是这么猜测,不确定)。
2. KEIL调试的时候一个现象,对于如下代码:
*(int *)(0x40000000) = 0x12345678;
*(char *)(0x40000000) = 0x55;
如果单步调试,第二拍写入会导致0x40000000地址处其他字节都变为零0x00000055,但如果在中间塞一个访问总线的语句,比如这样:
1 *(int *)(0x40000000) = 0x12345678;
2 c0 = *(int *)(0x40000000)
3 *(char *)(0x40000000) = 0x55;
就没问题,执行完第三行,0x40000000地址处数据为0x12345655,正确写入不影响其他。又想起cm4手册里对LDR STR指令的注释:总线流水可达到一周期仿问。
因为总线流水的时候,din刚刚好保持了上一个时钟里给写进来的数据,虽然寄存器变了,但下一拍的寄存器数据是这一拍数据和寄存器的组合电路,由于mask和din的状态,保证刚好下一拍写入寄存器的数据没有变。就是这样:
数据的正确不是因为总线流水的时候写使能信号怎么了,是因为上一拍的写入数据刚好在。