为什么需要改用总线接口?
1.但是在实际应用中,程序的体积可能非常大,指令存储器就不能再集成在FPGA内部了,一般使用FPGA芯片外部的Flash作为指令存储器。同理,-般使用FPGA芯片外部的SDRAM作为数据存储器。
2.统一接口标准。 很多ip都是买的,需要统一的标准。 2. 功能增减很容易如果要增加新的master或slave,那么新增加一组总线接口就行了。 3. 减少互联线整个系统干净整洁、接口清晰。
Wishbone总线接口定义
需要注意的是:
WE读写使能信号:低位表示读操作,高位表示写操作
SEL数据总线选择信号:用于选择总线上哪些bit是有效的,宽度为位宽/总线粒度(一般为一个字节)
CYC总线周期信号:代表总线正在被占用(不一定是总线操作),在总线使用时须持续有效
STB总线选用信号:有效时代表发起一次总线操作
在发起一次总线操作后,这两个信号都要有效
ACK操作结束信号:操作结束给一个有效位
总线单次读写操作过程
单次读
1.主设备:
WE置低表示读操作,STB和CYC有效表示发起一次总线操作,发送地址ADR和选通信号SEL
2.从设备:
传输数据给主设备,ACK响应信号置高表示传输完成,
3.主设备:
发现ACK高后接收数据,拉低CYC和STB表示总线操作完成
4.从设备:
发现STB低后,拉低ACK信号
单次写操作
1.主设备:
WE置高表示写操作,STB和CYC有效表示发起一次总线操作,发送地址ADR、选通信号SEL、数据DATA
2.从设备:
锁存主设备的数据,ACK响应信号置高表示传输完成
3.主设备:
发现ACK高后接收数据,拉低CYC和STB表示总线操作完成
4.从设备:
发现STB低后,拉低ACK信号
选通信号SEL与数据DATA的关系
取指访存阶段信号改写
本质上是对存储器进行读写,需要注意因为程序放在flash上,数据再SDRAM上,读写需要两个时钟周期,所以取指和访存需要两个时钟周期,在读写未完成时要把流水线进行暂停。暂停在CTRL控制模块中实现。
内部具体实现
使用有限状态机
一共分为三种状态,初始,忙碌和暂停;
当开始使用总线(CE)时,转移到忙碌状态,当访问结束(接收到ACK),转移到空闲状态
需要考虑流水线暂停与异常发生,修改如下:
当开始使用总线(CE)且不处于异常状态时,转移到忙碌状态
当访问结束(接收到ACK)且没有流水线暂停情况,转移到空闲状态;如果有转移到暂停状态:
当暂停结束时,暂停状态才跳转到初始状态
CTRL模块实现
end else if(stallreq_from_if == `Stop) begin
stall <= 6'b000111;
flush <= 1'b0;
按照之前定义的信号,取指阶段暂停应该是stall赋值为000011,这里为什么是000111?
这是考虑一种特殊情况即延迟槽指令必须执行,试想一下当前指令是转移指令,那么下一条指令就是延迟槽指令,如果只是取指阶段暂停而译码阶段暂停(也就是插入空指令),那么就会把空指令当做延迟槽指令。
所以我们需要将译码阶段和取指一起暂停,保持同步。
总线的通信方式
共享总线
由仲裁器控制总线的占有权,但同一时刻只能有一对主从设备通信
交叉互联
主从设备交叉连接,允许同一时刻多对主从设备通信
交叉互联模块实现
主设备根据地址高四位选择从设备,此处使用的IP最多支持8个主设备16个从设备,
故寻址空间大小2^8=256M,寻址空间0x0-0xFFFFFFF,按照如下接口定义分配地址空间:
可以看到指令存储在FLASH中,地址从0x30000000开始,故要修改PC在初始情况的地址:
if (ce == `ChipDisable) begin
pc <= 32'h30000000;