【RISC-V设计-08】- RISC-V处理器设计K0A之BMU
文章目录
- 【RISC-V设计-08】- RISC-V处理器设计K0A之BMU
- 1.简介
- 2.顶层设计
- 3.端口说明
- 4.总线时序
- 4.1 总线写时序
- 4.2 总线读时序
- 5.代码设计
- 6.总结
1.简介
总线管理单元(Bus Management Unit,简称 BMU)是 CPU 中负责取指以及数据加载与存储的模块。其主要涵盖两个功能:其一为指令获取,当 CPU 开始运行,BMU 模块会依据程序计数器 PC,依序逐步加载指令,随后交付给译码、执行单元处理。其二是数据的加载与存储,当执行到加载指令时,按照译码、执行单元的指示,向总线发起读请求,所读取到的数据会返回至通用寄存器组;而在执行到数据的存储指令时,同样依据译码、执行单元的指示,向总线发起写请求。在 BMU 的设计当中,数据的加载与存储具备高优先级,而指令的获取则具有低优先级。
2.顶层设计
3.端口说明
序号 | 端口 | 位宽 | 方向 | 说明 |
---|---|---|---|---|
1 | core_clk | 1 | input | 内核时钟 |
2 | core_rstn | 1 | input | 内核复位信号,低有效 |
3 | bus_avalid | 1 | output | 总线的地址有效信号 |
4 | bus_aready | 1 | input | 总线的地址就绪信号 |
5 | bus_write | 1 | output | 总线的写使能信号 |
6 | bus_addr | 18 | output | 总线地址 |
7 | bus_strb | 4 | output | 总线写字节有效信号 |
8 | bus_wdata | 32 | output | 总线写数据 |
9 | bus_rvalid | 1 | input | 总线读有效信号 |
10 | bus_rready | 1 | output | 总线读就绪信号 |
11 | bus_rdata | 32 | input | 总线读数据 |
12 | idu2bmu_pc_set | 1 | input | 程序计数器设置 |
13 | idu2bmu_pc_new | 18 | input | 新的程序计数器 |
14 | bmu2idu_pc_ack | 1 | output | 程序计数器应答 |
15 | idu2bmu_ls_req | 1 | input | 加载与存储请求 |
16 | idu2bmu_ls_cmd | 1 | input | 加载与存储命令,1写0读 |
17 | idu2bmu_ls_size | 2 | input | 加载与存储写数据字节数 |
18 | idu2bmu_ls_addr | 20 | input | 加载与存储的地址 |
19 | idu2bmu_ls_wdata | 32 | input | 加载与存储的写数据 |
20 | bmu2idu_ls_rdata | 32 | output | 加载与存储的读数据 |
21 | bmu2idu_valid | 1 | output | 指令有效指示 |
22 | bmu2idu_instr | 32 | output | 需要执行的指令 |
23 | bmu2idu_pc_cur | 18 | output | 当前指令的PC |
24 | bmu2idu_pc_nxt | 18 | output | 下一条指令的PC |
4.总线时序
此总线分为两个通道,分别是主机向丛机传输的地址和写数据通道,丛机向数据传输的读数据通道。两个通道均采用双向握手机制,在valid和ready同时为高时,通道内数据(包含地址、控制信号)传输完成。
4.1 总线写时序
在上图中,第一次传输为单次写操作,数据、地址同时发出,bus_avalid和bus_aready同时为高时,写数据传输完成。第二次传输时,bus_aready为低,在等到bus_aready为高时,写数据传输完成。第三次传输为连续写操作。第四次传输由于bus_aready为低,之后主机并未保持传输,而是切换为第五次传输,所有第四次传输被取消,数据D4不会被写入。
4.2 总线读时序
在上图中,第一次传输为单次读操作,地址发出后,bus_avalid和bus_aready同时为高时,读地址被接受,同时开始数据传输,在遇到bus_rvalid和bus_rready同时为高时,数据传输完成。第二次传输时,bus_aready为低,在等到bus_aready为高时,读地址被接受。第三次传输为连续读操作。第四次传输由于bus_aready为低,之后主机并未保持传输,而是切换为第五次传输,所有第四次传输被取消,数据D4不会被读出。
5.代码设计
// -------------------------------------------------------------------------------------------------
// Copyright 2024 Kearn Chen, kearn.chen@aliyun.com
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -------------------------------------------------------------------------------------------------
// Description :
// 1. Bus Management Unit
// -------------------------------------------------------------------------------------------------module k0a_core_bmu (input wire core_clk ,input wire core_rstn ,output reg bus_avalid ,input wire bus_aready ,output wire bus_write ,output wire [17:0] bus_addr ,output wire [3:0] bus_strb ,output wire [31:0] bus_wdata ,input wire bus_rvalid ,output wire bus_rready ,input wire [31:0] bus_rdata ,input wire idu2bmu_pc_set ,input wire [17:0] idu2bmu_pc_new ,output wire bmu2idu_pc_ack ,input wire idu2bmu_ls_req ,input wire idu2bmu_ls_cmd ,input wire [1:0] idu2bmu_ls_size ,input wire [19:0] idu2bmu_ls_addr ,input wire [31:0] idu2bmu_ls_wdata ,output wire [31:0] bmu2idu_ls_rdata ,output wire bmu2idu_valid ,output wire [31:0] bmu2idu_instr ,output reg [17:0] bmu2idu_pc_cur ,output wire [17:0] bmu2idu_pc_nxt
);reg discard;
reg load_data;
reg [17:0] prefetch_pc;reg instr_wsel;
reg instr_rsel;
reg [1:0] instr_size;
reg [31:0] instr_buf0;
reg [31:0] instr_buf1;wire bus_addr_fire = bus_avalid & bus_aready;
wire bus_resp_fire = bus_rvalid & bus_rready;
wire bus_data_fire = bus_resp_fire & ~discard & load_data;
wire bus_inst_fire = bus_resp_fire & ~discard & ~load_data;wire bus_load_fire = idu2bmu_ls_req & ~idu2bmu_ls_cmd & bus_data_fire;
wire bus_store_fire = idu2bmu_ls_req & idu2bmu_ls_cmd & bus_addr_fire;
wire instr_out_fire = bmu2idu_valid & (bus_store_fire | bus_load_fire | ~idu2bmu_ls_req);wire byte_at_0 = idu2bmu_ls_size[1] | idu2bmu_ls_size[0] & ~idu2bmu_ls_addr[1] | idu2bmu_ls_addr[1:0] == 2'd0;
wire byte_at_1 = idu2bmu_ls_size[1] | idu2bmu_ls_size[0] & ~idu2bmu_ls_addr[1] | idu2bmu_ls_addr[1:0] == 2'd1;
wire byte_at_2 = idu2bmu_ls_size[1] | idu2bmu_ls_size[0] & idu2bmu_ls_addr[1] | idu2bmu_ls_addr[1:0] == 2'd2;
wire byte_at_3 = idu2bmu_ls_size[1] | idu2bmu_ls_size[0] & idu2bmu_ls_addr[1] | idu2bmu_ls_addr[1:0] == 2'd3;wire pc_set_fire = idu2bmu_pc_set & bmu2idu_pc_ack;assign bmu2idu_pc_ack = instr_out_fire;assign bus_write = idu2bmu_ls_cmd;assign bus_strb = {byte_at_3, byte_at_2, byte_at_1, byte_at_0};assign bus_addr = idu2bmu_ls_req & ~load_data ? idu2bmu_ls_addr[19:2] : prefetch_pc;assign bus_wdata = idu2bmu_ls_wdata;assign bus_rready = 1'b1;assign bmu2idu_ls_rdata = bus_rdata;assign bmu2idu_valid = |instr_size;assign bmu2idu_instr = instr_rsel ? instr_buf1 : instr_buf0;assign bmu2idu_pc_nxt = bmu2idu_pc_cur + 1'b1;always @(posedge core_clk or negedge core_rstn)
beginif(!core_rstn)discard <= 1'b0;else if(pc_set_fire)discard <= 1'b1;else if(bus_addr_fire)discard <= 1'b0;
endalways @(posedge core_clk or negedge core_rstn)
beginif(!core_rstn)load_data <= 1'b0;else if(bus_addr_fire & idu2bmu_ls_req & ~load_data & ~idu2bmu_ls_cmd)load_data <= 1'b1;else if(bus_data_fire)load_data <= 1'b0;
endalways @(posedge core_clk or negedge core_rstn)
beginif(!core_rstn)prefetch_pc <= 18'd0;else if(pc_set_fire)prefetch_pc <= idu2bmu_pc_new;else if(bus_addr_fire & (~idu2bmu_ls_req | load_data))prefetch_pc <= prefetch_pc + 1'b1;
endalways @(posedge core_clk or negedge core_rstn)
beginif(!core_rstn)bus_avalid <= 1'b0;elsebus_avalid <= 1'b1;
endalways @(posedge core_clk or negedge core_rstn)
beginif(!core_rstn)bmu2idu_pc_cur <= 18'd0;else if(pc_set_fire)bmu2idu_pc_cur <= idu2bmu_pc_new;else if(instr_out_fire)bmu2idu_pc_cur <= bmu2idu_pc_nxt;
endalways @(posedge core_clk or negedge core_rstn)
beginif(!core_rstn)instr_wsel <= 1'b0;else if(pc_set_fire)instr_wsel <= 1'b0;else if(bus_inst_fire)instr_wsel <= ~instr_wsel;
endalways @(posedge core_clk or negedge core_rstn)
beginif(!core_rstn)instr_rsel <= 1'b0;else if(pc_set_fire)instr_rsel <= 1'b0;else if(instr_out_fire)instr_rsel <= ~instr_rsel;
endalways @(posedge core_clk or negedge core_rstn)
beginif(!core_rstn)instr_size <= 2'd0;else if(pc_set_fire)instr_size <= 2'd0;elseinstr_size <= instr_size + bus_inst_fire - instr_out_fire;
endalways @(posedge core_clk)
beginif(~instr_wsel & bus_inst_fire)instr_buf0 <= bus_rdata;
endalways @(posedge core_clk)
beginif(instr_wsel & bus_inst_fire)instr_buf1 <= bus_rdata;
endendmodule
6.总结
本文设计了总线管理单元,内部包含了2个指令缓存,电路结构简单。通过精细的逻辑控制和状态管理,实现了与外部总线的高效交互,支持指令的预取和数据的加载存储操作,是处理器设计中重要的组成部分。