

cpu设计和实现(数据访问)
source link: https://blog.csdn.net/feixiaoxing/article/details/128106384
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
在cpu设计当中,数据访问是比较重要的一个环节。一般认为,数据访问就是内存访问。其实不然。我们都知道,cpu访问一般有指令访问和数据访问两种。指令访问就是rom访问,这个比较纯粹。但是数据访问就比较复杂一点。因为,除了单纯的ram读取访问之外,数据访问还担负着io访问的功能。一般的外设访问都是通过ip来完成的,这些io都有自己的设备地址空间,那么cpu如何通过这些设备地址空间来控制这些外设呢?答案就是数据访问。
1、一般数据访问
对于cpu自身来说,其实并不关心外面访问的数据是ram数据、还是io数据,对它来说都是一样的。所以,首先cpu一般需要先定义一个外部空间,不妨称之为data_ram.v,

有了这个data_ram.v,那么mem.v就有了访问的空间了,可以输出地址和数据了,

涉及到的命令有load和store两种,我们可以挑选lb这一个命令进行解析,其他命令都是一个道理。首先,从代码上看,很明显mem.v传递给data_ram.v的地址是mem_addr_o。有了这个地址之后,mem.v就可以从data_ram.v那里获取mem_data_i的数据了。并且,还可以根据mem_addr_i末两位,决定最终写回的数据w_data_o取哪一部分。看到这里,大家可能会有一个疑问,那mem_addr_i又是哪里来的,通过查看ex_mem.v文件,发现了这么一句,
mem_mem_addr <= ex_mem_addr;
这说明,在exe阶段,地址其实就已经准备好了。这样,我们继续查看ex.v文件,发现了很有趣的这三条语句,
这三条语句告诉我们,它们就是为mem访存做准备的。其中,mem_addr_o就是访存的最终地址。在exe阶段,地址就已经准备好了。而且,aluop_o需要继续递延到mem访存阶段,因为到时候还需要根据它继续判断执行的访存指令是哪一个。
有了这些内容,我们才感觉到mem.v有了点实际的意义,不再像以前一样,只是负责数据的透传,没有什么具体的用途。当然,在整个测试的过程中,我们也发现2个问题,
1)在defines.v中,既要把InstMemNum调整为64,还要把DataMemNum调整为64,不然iverilog编译不了;
2)原来openmips.v Line375有编译错误,需要修改下,把ram_ce_o修改成1位wire即可,
有了上面这些内容做铺垫,就可以通过dumpfile、dumpvars的方法生成vcd文件调试波形了。

2、ll和sc命令
在cpu里面完成原子操作有很多办法。最最常见的方法,就是关中断、开中断。这个方法非常容易想到,毕竟如果把中断都关掉了,那么基本上cpu就不会受到任何外来的干扰了。但是,mips cpu在这个基础上又想到了另外一个方法,那就是ll和sc。
ll和sc通过影子寄存器llbit的方法,同样可以实现原子访问。首先ll访问的时候,cpu会从ram中读取数据,并且将llbit置为1。接着sc保存的时候,cpu会检测llbit是否为1。如果是1,则成功写入数据,llbit置为0,返回的寄存器置为1;如果是0,则取消写入过程,llbit不变,返回的寄存器置为0。
整个过程最为精妙的地方在于两点,
1)ll和sc一定要配对使用,单独使用是不成立的;
2)影子寄存器对用户来说是不可见的,并且sc中写入数据的那个寄存器,同时担当了返回值的作用。

这就是llbit寄存器的读写代码。注意,llbit的读取是在mem访问的阶段进行的,它与reg访问在id阶段完成、mfhi&mflo在exe阶段完成都不一样。写入则和所有寄存器一样,都是wb阶段完成的。
最后,我们还得回到一个老生常谈的话题。那就是llbit有可能读取不正确的问题。因为llbit是mem阶段被读取的,那么完全有可能llbit处于wb写回阶段,但是还没有赋值给reg,那么这个时候数据预取的工作就又要做一遍了,这从mem.v代码也可以完全看得出来,
3、load指令导致的流水线暂停
前面我们讨论过,exe中出现madd这样指令的时候会出现流水线暂停。其实大家思考下,如果出现这样两条指令的时候,也会出现流水线暂停,
前者刚从memory取出一个数据,保存到寄存器1。紧接着寄存器1和寄存器2马上就要进行比较判断了,判断的结果决定了pc后续的跳转地址是哪里。但是这个时候,lw才刚刚到exe阶段,还没有到达mem阶段。应该怎么处理呢?其实,也没啥好办法,就是让流水线暂停一会,

直接查看第二个判断条件。如果前面一条指令是load指令,并且需要写入某一个寄存器,除此之外,写入的寄存器还是当前要读入的寄存器,那么stallreq_for_reg1_loadrelate直接设置为1。当然,不仅仅是stallreq_for_reg1_loadrelate,还有可能stallreq_for_reg2_loadrelate也可能设置为1。两者共同决定了stallreq的最终结果。
今天的知识点有点多,大家可以多多思考,多多体会,多多练习。最后还是感谢《自己动手写cpu》这本书,今天谈到的这些代码都可以在Chapter9_1、Chapter9_2、Chapter9_3目录里面找到。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK