赞
踩
上一篇博文中注释了通用寄存器reg.v模块,现在来介绍总线模块rib.v:
另外,在最后一个章节中会上传额外添加详细注释的工程代码,完全开源,如有需要可自行下载。
(这个RIB总线协议为工程原作者自定义的总线协议)
目录
设想一下一个没有总线的SoC,处理器核与外设之间的连接是怎样的。可能会如下图所示:
可见,处理器核core直接与每个外设进行交互。假设一个外设有一条地址总线和一条数据总线,总共有N个外设,那么处理器核就有N条地址总线和N条数据总线,而且每增加一个外设就要修改(改动还不小)core的代码。有了总线之后,处理器核只需要一条地址总线和一条数据总线,大大简化了处理器核与外设之间的连接。
目前已经有不少成熟、标准的总线,比如AMBA、wishbone、AXI等。设计CPU时大可以直接使用其中某一种,以节省开发时间。但是为了追求简单,tinyriscv并没有使用这些总线,而是自主设计了一种名为RIB(RISC-V Internal Bus)的总线。RIB总线支持多主多从连接,但是同一时刻只支持一主一从通信。RIB总线上的各个主设备之间采用固定优先级仲裁机制。
- input wire clk,
- input wire rst,
-
- // master 0 interface
- input wire[`MemAddrBus] m0_addr_i, // 主设备0读、写地址
- input wire[`MemBus] m0_data_i, // 主设备0写数据
- output reg[`MemBus] m0_data_o, // 主设备0读取到的数据
- input wire m0_req_i, // 主设备0访问请求标志
- input wire m0_we_i, // 主设备0写标志
-
- // master 1 interface
- input wire[`MemAddrBus] m1_addr_i, // 主设备1读、写地址
- input wire[`MemBus] m1_data_i, // 主设备1写数据
- output reg[`MemBus] m1_data_o, // 主设备1读取到的数据
- input wire m1_req_i, // 主设备1访问请求标志
- input wire m1_we_i, // 主设备1写标志
-
- ...
-
- // slave 0 interface
- output reg[`MemAddrBus] s0_addr_o, // 从设备0读、写地址
- output reg[`MemBus] s0_data_o, // 从设备0写数据
- input wire[`MemBus] s0_data_i, // 从设备0读取到的数据
- output reg s0_we_o, // 从设备0写标志
-
- // slave 1 interface
- output reg[`MemAddrBus] s1_addr_o, // 从设备1读、写地址
- output reg[`MemBus] s1_data_o, // 从设备1写数据
- input wire[`MemBus] s1_data_i, // 从设备1读取到的数据
- output reg s1_we_o, // 从设备1写标志
-
- ...
-
- output reg hold_flag_o // 暂停流水线标志
主设备的优先级仲裁和主设备选择访问从设备各对应一个always(*)组合逻辑模块:
首先,由各主机向总线发送访问请求req:
- // 主设备请求信号
- //m0_req_i:来自执行模块ex.v的访问请求信号
- //m1_req_i:pc_reg发来的读指令地址请求,一直为1,保持请求状态
- //m2_req_i:jtag的操作请求
- //m3_req_i:串口下载模块的请求信号
-
- assign req = {m3_req_i, m2_req_i, m1_req_i, m0_req_i};
然后由总线的仲裁机制对主机的访问请求进行仲裁,如下代码所示:
- // 仲裁逻辑
- // 固定优先级仲裁机制
- // 优先级由高到低:主设备3,主设备0,主设备2,主设备1
- always @ (*) begin
- if (req[3]) begin //m3_req_i:串口下载模块的请求信号
- grant = grant3;
- hold_flag_o = `HoldEnable; //只需要暂停pc寄存器
- end else if (req[0]) begin //m0_req_i:来自执行模块ex.v的访问请求信号
- grant = grant0;
- hold_flag_o = `HoldEnable;
- end else if (req[2]) begin //m2_req_i:jtag的操作请求
- grant = grant2;
- hold_flag_o = `HoldEnable;
- end else begin //m1_req_i:pc_reg发来的读指令地址请求,一直为1,保持请求状态
- grant = grant1;
- hold_flag_o = `HoldDisable;
- end
- end
主设备的优先级仲裁always(*)组合逻辑模块:
通过if_else根据优先级的顺序选择主设备进行相应的访问操作,对于主设备的仲裁,主设备优先级顺序为:uart串口下载、ex.v执行模块、jtag模块、pc_reg取指模块。
对这个优先级顺序的理解:
(前三个“主设备uart、ex.v、jtag”访问总线,需要暂停流水线)
主设备选择访问从设备always(*)组合逻辑模块:
- grant0: begin //主设备0
- case (m0_addr_i[31:28])
- slave_0: begin
- s0_we_o = m0_we_i; //主设备0的写使能信号——>从设备
- s0_addr_o = {{4'h0}, {m0_addr_i[27:0]}};//从设备0的地址
- s0_data_o = m0_data_i; //主设备0数据写入——>从设备0
- m0_data_o = s0_data_i; //从设备0数据写入——>主设备0
- end
- slave_1: begin
- s1_we_o = m0_we_i;
- s1_addr_o = {{4'h0}, {m0_addr_i[27:0]}};
- s1_data_o = m0_data_i;
- m0_data_o = s1_data_i;
- end
-
- ...
以一个主从和RIB之间的接口为例:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。