【RISC-V设计-08】- RISC-V处理器设计K0A之BMU

【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.顶层设计

【RISC-V设计-08】- RISC-V处理器设计K0A之BMU_第1张图片

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 总线写时序

【RISC-V设计-08】- RISC-V处理器设计K0A之BMU_第2张图片

在上图中,第一次传输为单次写操作,数据、地址同时发出,bus_avalid和bus_aready同时为高时,写数据传输完成。第二次传输时,bus_aready为低,在等到bus_aready为高时,写数据传输完成。第三次传输为连续写操作。第四次传输由于bus_aready为低,之后主机并未保持传输,而是切换为第五次传输,所有第四次传输被取消,数据D4不会被写入。

4.2 总线读时序

【RISC-V设计-08】- RISC-V处理器设计K0A之BMU_第3张图片

在上图中,第一次传输为单次读操作,地址发出后,bus_avalid和bus_aready同时为高时,读地址被接受,同时开始数据传输,在遇到bus_rvalid和bus_rready同时为高时,数据传输完成。第二次传输时,bus_aready为低,在等到bus_aready为高时,读地址被接受。第三次传输为连续读操作。第四次传输由于bus_aready为低,之后主机并未保持传输,而是切换为第五次传输,所有第四次传输被取消,数据D4不会被读出。

5.代码设计

// -------------------------------------------------------------------------------------------------
// Copyright 2024 Kearn Chen, [email protected]
//
// 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个指令缓存,电路结构简单。通过精细的逻辑控制和状态管理,实现了与外部总线的高效交互,支持指令的预取和数据的加载存储操作,是处理器设计中重要的组成部分。

你可能感兴趣的:(RISC-V设计专题,risc-v)