【RISC-V设计-08】- RISC-V处理器设计K0A之BMU
文章目录
- [【RISC-V设计-08】- RISC-V处理器设计K0A之BMU](#【RISC-V设计-08】- RISC-V处理器设计K0A之BMU)
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.代码设计
verilog
// -------------------------------------------------------------------------------------------------
// 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)
begin
if(!core_rstn)
discard <= 1'b0;
else if(pc_set_fire)
discard <= 1'b1;
else if(bus_addr_fire)
discard <= 1'b0;
end
always @(posedge core_clk or negedge core_rstn)
begin
if(!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;
end
always @(posedge core_clk or negedge core_rstn)
begin
if(!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;
end
always @(posedge core_clk or negedge core_rstn)
begin
if(!core_rstn)
bus_avalid <= 1'b0;
else
bus_avalid <= 1'b1;
end
always @(posedge core_clk or negedge core_rstn)
begin
if(!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;
end
always @(posedge core_clk or negedge core_rstn)
begin
if(!core_rstn)
instr_wsel <= 1'b0;
else if(pc_set_fire)
instr_wsel <= 1'b0;
else if(bus_inst_fire)
instr_wsel <= ~instr_wsel;
end
always @(posedge core_clk or negedge core_rstn)
begin
if(!core_rstn)
instr_rsel <= 1'b0;
else if(pc_set_fire)
instr_rsel <= 1'b0;
else if(instr_out_fire)
instr_rsel <= ~instr_rsel;
end
always @(posedge core_clk or negedge core_rstn)
begin
if(!core_rstn)
instr_size <= 2'd0;
else if(pc_set_fire)
instr_size <= 2'd0;
else
instr_size <= instr_size + bus_inst_fire - instr_out_fire;
end
always @(posedge core_clk)
begin
if(~instr_wsel & bus_inst_fire)
instr_buf0 <= bus_rdata;
end
always @(posedge core_clk)
begin
if(instr_wsel & bus_inst_fire)
instr_buf1 <= bus_rdata;
end
endmodule
6.总结
本文设计了总线管理单元,内部包含了2个指令缓存,电路结构简单。通过精细的逻辑控制和状态管理,实现了与外部总线的高效交互,支持指令的预取和数据的加载存储操作,是处理器设计中重要的组成部分。