Skip to main content

总线

申明:以下的文字并不是教各位什么是总线或者怎么做ysyx总线的章节,这仅仅是我做总线章节写的一些笔记吧,或者踩的bug,文字可能会有些跳跃接不上,请见谅。若有问题,可直接与我联系

在写总线的第一个任务,重构NPC时,我先后进行了,verilog版NPC重构,学习chisel,用chisel写了一版NPC-chisel并接入仿真环境并且成功运行rt-thread,上面这两个花了我近一个月的时间(其实中间还有其他事情耽搁了,不过任务量确实挺大的),但是,这还不够😭😭,我还需要将npc-chisel改成支持模块间的通信协议的版本

总线异步通信协议

这个我理解了挺久的…..感觉确实挺难理解的的,但是我忽然想到了一个非常不错的理解办法,这个办法如果正确的话,我个人认为比讲义或者《cpu设计实战》关于这一块的理解方法更加通俗易懂,但是我问群里的各位大佬,大佬们都让我先写,确实需要“先实现后完美”的思想,不然根本不敢动手,我的理解的伪代码如下

写了一下似乎是对的,但是多周期和单周期又一些东西很不一样,并且我这个state的转化是在下一个周期写入的,也就是当fire的时候state并没有改变,state的写入是在下一个周期,也就是下一个周期才开始工作,这个是不是对的?并且有一些写入的操作写入也是在下一周期,这在单周期是没什么问题的,但是多周期可能出现下一周期时写入的数据发生变化

看此图,后面的5个state是五阶段的state,第一个state占两个周期,原因是IFU访问sram需要延迟一周期获得数据,所以一条指令需要6个周期,但是其实还是有点问题

指令在第二个周期取到,理应当此时就已经IFU和IDU Fire了,但是由于时序逻辑第三周期IDU的状态才会改变,在第三周期IDU和EXU就应该已经Fire了,但是在第四周期EXU的状态才会改变….

我不知道多周期是不是就是这样的,我感觉好像是对的,那就这样吧,我感觉其实挺完美的(好累

但是讲义上的意思似乎是让我两周期解决….我觉得要改多周期就一口气改了吧,这种既是多周期但是又没改干净的感觉很奇怪

第二个任务,我感觉跟第一个没啥区别,但是我的多周期处理器可能需要7周期解决load指令,并且在不是load指令时只需要6周期,后发现store指令也需要7周期,啊啊啊啊,没想到困住我的竟然是如何模拟出访存延迟,但是我握手信号应该都是没啥问题了

AXI-Lite

axi-lite感觉比之前多了很多信号,这最主要的原因就是真实的内存访问的情况是多变的,为了应对多变的情况,设置了很多握手信号

在RTFM之前的一些想法:很不能理解为什么会出现死锁或者活锁的情况,ready或者valid信号难道不是完全取决于自己这个模块的状态吗,master完成工作设置valid信号,slave在可以接受时设置ready信号,这两个信号之间为什么会有依赖呢?不理解

RTFM时的笔记

(因为找不到这些约束具体在哪里,我必须完整的读某些可能出现的章节,读都读了,就记点笔记吧)

1.clock:说valid ready信号必须要受到clock的约束,不是很能理解

2.reset:规定了所有valid信号必须为0,允许异步复位,并且valid的变化必须在reset变化的后一个rising edge

3.valid ready information的关系

不妙,我之前写的好像并不是很对,information在握手成功的下一周期并不一定保持,但是我之前写的代码是握手成功后state的输入改变,并在下一个周期state才变化,state变化之后模块行为才开始改变,但此时information并不一定保持相同,莫非我需要让state变成组合逻辑?不清楚,让我试试

我写的时候一直报循环电路的错误,打个比方,valid和ready在某周期握手,如果state在某周期就改变,那就回根据state进行下一步的行为,但是由于下一步的行为可能会对valid和ready进行修改,他们的修改又会改变state,造成循环,所以我忽然理解第一点了,valid和ready受到时钟的约束,但是state不受时钟的约束,之前都弄反了,让我试试

感觉没啥问题,rtt也能跑,握手模版修改

终于找到约束的部分了

还有一点不好截图,我叙述一下:就是valid信号一旦assert,就不能取消until ready is asserted(避免活锁)

看起来并不是很难哎,这个最难写的反而是SRAM模块,但是一下多了这么多握手信号,估计要写好多状态机啰,挺麻烦的

分一下步吧

1.改接口

2.改IFU

(错误)好难🤯,想了想,IFU对于外部模块的握手情况肯定会影响IFU和IMEM的握手情况,所以,我需要将IFU和IMEM的握手状态放在IFU和外部模块之后(其实也不分先后啦,感性认识是有先后的)

上述想法似乎不太对,躺床上写着写着发现写不下去了,IFU与内部模块的两次握手发生在IFU的工作周期,也就是伪代码的IFU的s_betweenFire12状态,在IFU的这个状态可以确保不被任何其他事情打扰(WBU和其他模块的输入不变,并对IDU的valid信号为0),也就是IFU的arvalid(valid pc)始终可以置1,rready也始终可以置1,甚至都不需要管arready,只要一直将arvalid置1就好,imem做完工作之后自然会将rvalid置1,并设置相应的rresp,IFU将这两个信号连接到和IDU的valid上面,等待Fire2即可,IFU回到空闲状态,将arvalid和rready全部置0,这么想似乎就很简单了呀哈哈哈

3.改IDU

这个就真的要处理全部(暂时)的AXI4-Lite的信号了

感觉addr的握手和data的握手分开的意义不大呀(当然是对我来说),可能是更加复杂的情况才需要,对于我来说意义不大的话,那我直接让他们信号一直一致就好了

写IFU和IDU时,我几乎都没有管所有的ready信号哎

4,改SRAM模块

我打算Imem和Dmem都用复用同一套逻辑,以便后面可能需要合并两者

他有5个通道,有些通道之间有依赖关系,就像IFU和IDU,fire1和fire2必须交错

也就是AR和R之间有依赖关系,AW,W和B之间有依赖关系

最终改bug时IFU和LSU分别需要都有4个状态

1,SRAM读写延迟全部为5,10,20时全部正确启动rtt,且启动速度有很大不同✅

2,给SRAM添加LSFR,依然成功启动rtt✅

3,IFU和LSU加LSFR,成功启动rtt✅

Arbiter

又是一个全新的任务捏,分一下步吧

1,改接口,把IFU和LSU的接口接到arb上面,arb接mem

2,写arb

有个bug,传回来的值,比如说读出之后设置的valid值,由于途径arb中的一个寄存器,会比没有加这个寄存器的情况慢一拍,导致IFU的handshake无法及时完成,导致握手之后应该设置ready为0的传输会慢一拍

于是,需要在arb中完成这一步,当arb和mem握手成功时,由arb关闭对于mem的所有访问(即所有valid ready),并且在arb和ifu或者lsu握手成功后,将控制权再交由ifu或者lsu

并且之后由于只有一个mem了,你在仿真环境中想要读出inst,需要配合IFU的一些状态,我之前没配合好,一直segment fault,沃靠,这种错误真的很难debug,你根本不知道错误出在处理器上还是仿真环境上面,就算出在仿真环境上面,你也无法通过assert等方法定位错误,还好,凭借着我“过人”的感知能力,我在pmem的两个dpic上面都设过边界,说明并不是读取内存出现的段错误,我的直觉告诉我是llvm相关的代码出错,说明我给的inst有问题,我之后对着波形看了一下正确的inst对应的状态,改了就好了

我的arb现在的调度策略就是哪个握手成功就选哪个,因为暂时不会出现两者同时访问的情况,如果之后要应用一些调度策略的话,也只需要修改一下状态迁移的条件即可,比较好扩展(虽然我感觉我写得像一坨屎,在用chisel的语法写verilog…)

记一下我做完这一章节之后还需要干的事情:1,优化代码 (input/output)2,实现rresp的功能 3,增加更多的异常机制

Xbar

这才是我心目中的总线

分一下步吧

1,实现设备寄存器

是不是直接复制mem模块的实现然后把pmem_write改成$write就好了?让我试试

2,实现xbar

靠,感觉和arb好像,只是从select状态转移的逻辑不一样,arb是靠调度策略,xbar则是靠地址译码

有了之前的经验,除了一点笔误de了一下bug,应该算是一遍写对

将uart提出来可能会导致之前的difftest机制无法工作,可以在uart模块中添加一个dpic函数来告知仿真环境什么时候该skip掉这一条指令

总线章节最后一个任务了

1,先实现CLINT(只有mtime的CLINT)

将之前的UART复制过来就好了

2,接入系统

需要在XBAR中多添加一个设备,就是多加一个状态而已,EZ

3,修改仿真环境和am相关代码

我看am之前读出的时间是us单位的,于是我只需要看仿真的1个clock等于多少us即可,这个需要在仿真环境中加计时器和计数器

但是我根据200左右测出的速率并不太准,需要更大的基数

似乎就是1 clock = 1us哎,感觉就在这个附近浮动,既然如此,我就不需要改了

嘶…….但是运行时间测试时感觉还是不太对,最后我将读出来的时间*4之后勉强接近真实时间了(我发现不能乘小数,否则编译报错,因为可能我的npc没有实现浮点相关的扩展吧,其实3.7左右应该最合适)

总结

啊啊啊啊啊啊终于写完了,我看了一下我第一次尝试写总线是4月13号,写了两天写不下去了,第二次尝试写总线是5月16号,在5月24号全部写完,所以我在这里应该卡了一个多月,中间我学习了了chisel,写了一版chisel的单周期,然后在单周期chisel下写完的总线,真正写的时候我发现开头是最最难的,因为握手这个东西涉及多个周期的变化以及模块之间信号的交互,而一开始的单周期就是一个周期解决所有事情,无需考虑任何时序逻辑,但是开头之后就很顺了,基本卡住我的就是一些小bug,并没有理解方面被卡住的,并且,最后的xbar+uart+clint是我一个下午多一点点时间写完的,因为大部分都是重复代码,并且里面的一些坑基本上在arb就踩过了,所有后面的模块基本都是一遍过,挺快乐的

列举一下我之后需要优化的地方

  • chisel代码整体优化,特别是arb和xbar的代码(比如如何减少重复代码,io可否分成input/output)
  • 状态机是否能优化?
  • rresp和wresp带来的异常可否加入core的异常处理机制(现在仅有ecall异常)

但是我现在状态火热,加之后面马上要期末考试了,我决定趁宝贵的最后一点时间做一点后面一个章节–“SoC”