Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Ask DeepWiki zread Document Buckyball CI

BuckyBall

BuckyBall is a scalable framework for Domain Specific Architecture, built on RISC-V architecture and optimized for high-performance computing and machine learning accelerator design.

Project Overview

The BuckyBall framework provides a complete hardware design, simulation verification, and software development toolchain, supporting the full development process from RTL design to system-level verification. The framework adopts a modular design that supports flexible configuration and extension, suitable for various specialized computing scenarios.

Quick Start

Environment Dependencies

Before getting started, please ensure your system meets the following dependency requirements:

Required Software:

  • Anaconda/Miniconda (Python environment management)
  • Ninja Build System
  • GTKWave (waveform viewer)
  • Bash Shell environment (doesn't need to be the primary shell)

Installing Dependencies:

# Install Anaconda
# Download from: https://www.anaconda.com/download/

# Install system tools
sudo apt install ninja-build gtkwave

# Optional: FireSim passwordless configuration
# Add to /etc/sudoers: user_name ALL=(ALL) NOPASSWD:ALL

Source Build

1. Clone Repository

git clone https://github.com/DangoSys/buckyball.git
cd buckyball

2. Initialize Environment

./scripts/init.sh

Note: Initialization takes approximately 3 hours, including dependency downloads and compilation

3. Environment Activation

source buckyball/env.sh

Verify Installation

Run Verilator simulation test to verify installation:

bbdev verilator --run '--jobs 16 --binary ctest_vecunit_matmul_ones_singlecore-baremetal --batch'

Docker Quick Experience

Notice:

  • Docker images are provided only for specific release versions.
  • Docker image may not be the latest version, source build is recommended.

Pull Docker Image

docker pull ghcr.io/dangosys/buckyball:latest

Run Docker Container

docker run -it ghcr.io/dangosys/buckyball:latest
# Activate environment
source buckyball/env.sh
# Run Verilator simulation test
bbdev verilator --run '--jobs 16 --binary ctest_vecunit_matmul_ones_singlecore-baremetal --batch'

Additional Resources

You can learn more from DeepWiki and Zread

Community

Join our discussion on Slack

Contributors

Thank you for considering contributing to Buckyball!

BuckyBall 项目结构概览

BuckyBall 是一个面向领域特定架构的可扩展框架,项目采用模块化设计,各目录职责明确,支持从硬件设计到软件开发的完整工具链。

主要目录结构

核心架构模块

  • arch/ - 硬件架构实现,包含 Scala/Chisel 编写的 RTL 代码
    • 基于 Rocket-chip 和 Chipyard 框架
    • 实现自定义 RoCC 协处理器和内存子系统
    • 支持多种配置和扩展选项

测试验证模块

  • bb-tests/ - 统一测试框架
    • workloads/ - 应用工作负载测试
    • customext/ - 自定义扩展验证
    • sardine/ - Sardine 测试框架
    • uvbb/ - 单元测试套件

仿真环境模块

  • sims/ - 仿真器和验证环境
    • 支持 Verilator、VCS 等多种仿真器
    • 集成 FireSim FPGA 加速仿真
    • 提供性能分析和调试工具

开发工具模块

  • scripts/ - 构建和部署脚本

    • 环境初始化脚本
    • 自动化构建工具
    • 依赖管理和配置
  • workflow/ - 开发工作流和自动化

    • CI/CD 流水线配置
    • 文档生成工具
    • 代码质量检查

文档系统

  • docs/ - 项目文档
    • bb-note/ - 基于 mdBook 的技术文档
    • img/ - 文档图片资源
    • 支持自动生成和更新

第三方依赖

  • thirdparty/ - 外部依赖模块 (submodules)
    • chipyard/ - Berkeley Chipyard SoC 设计框架
    • circt/ - CIRCT 电路编译器工具链

开发工作流

  1. 环境准备: 使用 scripts/init.sh 初始化开发环境
  2. 架构开发: 在 arch/ 目录进行硬件设计和修改
  3. 测试验证: 使用 bb-tests/ 中的测试套件进行功能验证
  4. 仿真调试: 通过 sims/ 目录的仿真环境进行性能分析
  5. 文档更新: 自动生成或手动更新 docs/ 中的技术文档

构建系统

项目支持多种构建方式:

  • Make: 传统 Makefile 构建
  • SBT: Scala 项目构建工具
  • CMake: 测试框架构建系统
  • Conda: Python 环境和依赖管理

版本管理说明

  • Submodules: thirdparty/ 下的模块需要独立更新
  • 主仓库: 核心代码和配置随主分支同步更新
  • 文档: 支持自动生成,与代码变更保持同步

Tutorial for buckyball

by -王博涵

本文档会逐步更新,作者也在不断出现的问题的中尝试解决并总结。

本文档用于说明一个完整的buckyball开发流程的步骤顺序和各种问题的解决思路。我们以构建一个用于执行relu()函数的ball算子模块为例:

第一步我们需要完成该模块的硬件代码编写,即编写scala语言的chisel硬件语言代码,并生成对应的verilog代码。

第二步我们需要编写测试软件去实现relu(),可以编写一个在cpu执行软件代码的对照函数和一个在我们第一步编写的专用硬件执行软件代码的实验函数,测试结果一致即成功,或者做第三步来测试。

第三步,在硬件层上进行仿真,查看波形图进行debug。此外,还有一些其他的细节,比如编译文档的更改,指令集的更新等细节,下文会依次说明。

在开发中遇到问题,可访问DangoSys/buckyball | DeepWiki或者项目概览 - Buckyball Technical Documentation

Chisel学习资源:binder

在正式开始之前,我们先启动环境:

cd /path/to/buckyball  
source env.sh 
// source ./env.sh 若报错试试这个
// 全文所有路径都是以./buckyball为起点的相对路径

一、 编写Chisel硬件模块

arch/src/main/scala/prototype/ 目录下创建ReLU加速器的Chisel实现。参考现有的加速器结构,建议在 prototype/ 下创建新的子目录,例如 prototype/relu/Relu.scala,编写硬件代码。

二、硬件指令解码

接下来对硬件指令进行解码,需要在硬件端添加 ReLU 指令的支持,让硬件解码器认识这个指令,编写注册该ball的指令集。

该工作主要分为下面五个方面:

  • 指令枚举(DISA)定义了 func7 → 指令名(RELU)
  • 解码器(DomainDecoder)定义了 func7 → 解码规则(读/写/地址/iter)→ BID(例如 4)
  • 总线注册(busRegister)定义了 BID → 实际的 Ball 实例(索引为 4 的 ReluBall)
  • 保留站注册(rsRegister)用于 RS/发射描述,与 BID 对齐,便于系统的发射/完成管理与调试 若任一环缺失或不一致,都会导致 ReLU 这条指令无法正确被识别/路由/落到实际硬件执行。
  • 创建一个新的 Ball 执行单元class ReluUnit来处理 ReLU操作。

1. 在 DISA.scala 中定义 RELU_BITPAT

arch/src/main/scala/examples/toy/balldomain/DISA.scala 定义 Ball 指令的 funct7 编码(BitPat),比如 TRANSPOSE、IM2COL 等。可以视作“指令集的枚举表”,供解码器匹配。

在此文件中添加 ReLU 指令的位模式定义:

val RELU_BITPAT = BitPat("b0100110") // func7 = 38 = 0x23

2. 在 Ball 域解码器中添加 ReLU 指令

arch/src/main/scala/examples/toy/balldomain/DomainDecoder.scala 是Ball域解码器。 作用如下:

  • 输入:来自全局解码的 PostGDCmd(已经判断这是 Ball 类别的命令)。
  • 输出:结构化的 BallDecodeCmd,包括:
    • 是否使用 op1/op2、是否写回 scratchpad、操作数是否来自 scratchpad
    • 操作数/写回的 bank 与地址
    • 迭代次数 iter
    • 目标 Ball ID(BID)
    • 其它专用字段 special 等
  • 内部通过 ListLookup(func7, ...),把不同 funct7 的指令映射到一套布尔开关和字段抽取规则。

此文件中在解码列表中添加 ReLU 指令的解码项。参考其他指令(如 TRANSPOSE_FUNC7 = 38)的实现方式,您需要:

// 在 BallDecodeFields 的 ListLookup 中添加  
RELU                 -> List(Y,N,Y,Y,N, rs1(spAddrLen-1,0), 0.U(spAddrLen.W), rs2(spAddrLen-1,0), rs2(spAddrLen + 9,spAddrLen), 7.U, rs2(63,spAddrLen + 10), Y) // 根据 ReLU 指令的具体需求填写解码字段,列表参数的数量一定要一致,可以参考其他指令

3. 添加ReLuBall生成器并进行注册

a. arch/src/main/scala/examples/toy/balldomain/bbus/busRegister.scala是Ball 总线注册表,用一个 Seq(() => new 某Ball(...)) 注册系统里实际要实例化的 Ball 模块。

在此文件中找到并添加ReLuBall的新ID。

class BBusModule(implicit b: CustomBuckyBallConfig, p: Parameters)
    extends BBus(
      // 定义要注册的Ball设备生成器
      Seq(
        () => new examples.toy.balldomain.vecball.VecBall(0),
        () => new examples.toy.balldomain.matrixball.MatrixBall(1),
        () => new examples.toy.balldomain.im2colball.Im2colBall(2),
        () => new examples.toy.balldomain.transposeball.TransposeBall(3),
        ...
        () =>new examples.toy.balldomain.reluball.ReluBall(7) // Ball ID 7 - 新添加
      )
    ) {
  override lazy val desiredName = "BBusModule"
}

b. arch/src/main/scala/examples/toy/balldomain/rs/rsRegister.scala是"Ball 保留站"的注册表,用一个列表注册系统里有哪些 Ball(按 ballId 指定 ID、指定名称)。保留站(RS)负责管理 Ball 的发射、占用、完成等元信息,通常也用于可视化/统计、命名与日志。

在此文件中进行ReluBall的注册:

class BallRSModule(implicit b: CustomBuckyBallConfig, p: Parameters)
    extends BallReservationStation(
      // 定义要注册的Ball设备信息
      Seq(
        BallRsRegist(ballId = 0, ballName = "VecBall"),
        BallRsRegist(ballId = 1, ballName = "MatrixBall"),
        BallRsRegist(ballId = 2, ballName = "Im2colBall"),
        BallRsRegist(ballId = 3, ballName = "TransposeBall"),
        ...
        BallRsRegist(ballId = 7, ballName = "ReluBall") // Ball ID 7 - 新添加
      )
    ) {
  override lazy val desiredName = "BallRSModule"
}

4. 编写ReluBall的接口文件

arch/src/main/scala/examples/toy/balldomain文件中创建reluball文件夹,进入文件夹后创建ReluBall.scala编写接口代码。

三、 编写测试软件与编译设置

1. 创建测试文件

bb-tests/workloads/src/CTest/ 下创建 relu_test.c, 编写测试代码,代码中核心函数会执行void bb_relu(uint32_t op1_addr, uint32_t wr_addr, uint32_t iter); 下文中要注意该函数的声明和定义。

2. 修改CMakeLists.txt

bb-tests/workloads/src/CTest/CMakeLists.txt 中添加测试目标: CMakeLists.txt:120-127

add_cross_platform_test_target(ctest_relu_test relu_test.c)

并在总构建目标中添加: CMakeLists.txt:137-162

add_custom_target(buckyball-CTest-build ALL DEPENDS
  # ... 其他测试 ...
  ctest_relu_test
  COMMENT "Building all workloads for Buckyball"
  VERBATIM)

3. 需要添加ReLU指令API

a. isa.h

  • bb-tests/workloads/lib/bbhw/isa/isa.h 中添加ReLU指令的声明: isa.h:33-43

  • InstructionType 枚举中添加:

RELU_FUNC7 = 38,  // 0x26 - ReLU function code (或您选择的其他值)
  • 在函数声明部分添加: isa.h:72-73
void bb_relu(uint32_t op1_addr, uint32_t wr_addr, uint32_t iter);

b. isa.c

  • bb-tests/workloads/lib/bbhw/isa添加38_relu.c,在里面实现void bb_relu(uint32_t op1_addr, uint32_t wr_addr, uint32_t iter)

  • bb-tests/workloads/lib/bbhw/isa/isa.c 中添加声明: isa.c:53-76

case RELU_FUNC7:
	return &relu_config;
  • isa.c:37-47
extern const InstructionConfig relu_config;

4. 更新 CMakeLists.txt

bb-tests/workloads/lib/bbhw/isa/CMakeLists.txt 中的三个编译命令中都添加 38_relu.c 的编译和链接:

  1. Linux 版本:在 add_custom_commandCOMMAND 中添加:

    && riscv64-unknown-linux-gnu-gcc -c ${CMAKE_CURRENT_SOURCE_DIR}/38_relu.c -march=rv64gc -I${CMAKE_CURRENT_SOURCE_DIR} -I${CMAKE_CURRENT_SOURCE_DIR}/.. -o linux-38_relu.o
    

    并在 ar rcs 命令中添加 linux-38_relu.o

  2. Baremetal 版本:在 add_custom_commandCOMMAND 中添加:

    && riscv64-unknown-elf-gcc -c ${CMAKE_CURRENT_SOURCE_DIR}/38_relu.c -g -fno-common -O2 -static -march=rv64gc -mcmodel=medany -fno-builtin-printf -D__BAREMETAL__ -I${CMAKE_CURRENT_SOURCE_DIR} -I${CMAKE_CURRENT_SOURCE_DIR}/.. -o baremetal-38_relu.o
    

    并在 ar rcs 命令中添加 baremetal-38_relu.o

  3. x86 版本:在 add_custom_commandCOMMAND 中添加:

    && gcc -c ${CMAKE_CURRENT_SOURCE_DIR}/38_relu.c -fPIC -D__x86_64__ -I${CMAKE_CURRENT_SOURCE_DIR} -I${CMAKE_CURRENT_SOURCE_DIR}/.. -o x86-38_relu.o
    

    并在 ar rcs 命令中添加 x86-38_relu.o

  4. 开头的ISA子模块库需要添加对应的38_relu.c文件。

四、 测试操作步骤

步骤1: 编译测试程序

cd bb-tests/build
rm -rf *
cmake -G Ninja ../

Warning:执行rm -rf * 之前一定要检查是否在bb-tests/build 目录里面,否则在错误的文件夹里面强制删除重要文件将会带来灾难!

若灾难发生了,可以从GitHub重新拉取初始文档,但自己在服务器端更新的文件无法复原。

ninja ctest_relu_test // 软件编译
ninja sync-bin  // 同步二进制文件

ninja ctest_relu_test执行后报错,这是软件编译没有通过,请检查**”三、 编写测试软件“**等相关文件。

步骤2: 生成Verilog

cd buckyball
bbdev verilator --verilog

bbdev verilator --verilog执行后报错,这是硬件编译没有通过,请检查**“一、 编写Chisel硬件模块 二、编译适配准备”**相关文件。

步骤3: 运行仿真

bbdev verilator --run '--jobs 16 --binary ctest_relu_test_singlecore-baremetal --batch'

bbdev verilator --verilog执行后报错,这是硬件系统有超时,卡死等问题,请检查一、 编写Chisel硬件模块相关文件。

步骤5:查看仿真文件

arch/waveform/仿真文件名(E.g.2025-10-08-00-03-ctest_vecunit_matmul_random1_singlecore-baremetal)中,将waveform.fst文件用本地系统的Filezilla等软件下载到本地上,在本地的仿真波形查看器(E.g. GTKWave)进行波形查看。

注意,仿真文件所在的文件夹只能由waveform.fst一个文件,若存在waveform.fst.hier文件则代表仿真失败.

若波形没有满足理论情况,请在软件测试代码没有问题的情况下检查一、 编写Chisel硬件模块相关文件。

软件代码是否有问题可以参考其在cpu执行的结果,可以更改relu_test.c文件暂时完全移除硬件加速器调用,只测试 CPU 版本。

五、仿真波形

waveform.fst导入到本地后,用GTKWAVE,在工程索引中寻找: TOP.TestHarness.chiptop0.system.tile_prci_domain.element_reset_domain_tile.buckyball.ballDomain.bbus.balls_4.reluUnit该文件下的常量就是我们Relu.scala用到的所用硬件常量,双击便可查看波形!

不同例程的一些命名可能不会完全一样,但基本相差不大

BuckyBall 架构设计概览

BuckyBall 架构模块包含了完整的硬件设计实现,基于 RISC-V 指令集架构,采用 Scala/Chisel 硬件描述语言开发。架构设计遵循模块化和可扩展原则,支持多种配置和自定义扩展。

架构层次结构

系统级架构

BuckyBall 采用分层设计,从上到下包括:

  • SoC 子系统: 集成多核处理器、缓存层次、互连网络
  • 处理器核心: 基于 Rocket 核心的定制实现
  • 协处理器: 支持 RoCC 接口的专用加速器
  • 内存子系统: 高性能内存控制器和 DMA 引擎

核心特性

  • 可配置性: 支持核心数量、缓存大小、总线宽度等参数配置
  • 扩展性: 提供标准化的协处理器接口和扩展机制
  • 兼容性: 保持与标准 RISC-V 生态系统的兼容性
  • 性能优化: 针对特定应用场景的性能优化设计

目录结构

arch/
├── src/main/scala/
│   └── framework/          - BuckyBall 框架核心
│       ├── rocket/         - Rocket 核心定制实现
│       └── builtin/        - 内置组件库
│           └── memdomain/  - 内存域实现
│               ├── mem/    - 存储器组件
│               └── dma/    - DMA 引擎
└── thirdparty/            - 第三方依赖
    └── chipyard/          - Chipyard 框架

设计原则

模块化设计

每个功能模块都具有清晰的接口定义和独立的实现,便于测试、验证和复用。模块间通过标准化的接口进行通信,降低耦合度。

参数化配置

所有硬件模块都支持参数化配置,通过 Scala 的类型系统和配置框架实现灵活的硬件生成。配置参数包括:

  • 数据路径宽度
  • 缓存大小和组织方式
  • 并行度和流水线深度
  • 协处理器类型和数量

性能优化

针对目标应用场景进行专门的性能优化:

  • 内存访问模式优化
  • 数据流水线设计
  • 并行计算支持
  • 低延迟通信机制

开发工作流

  1. 需求分析: 确定目标应用的性能和功能需求
  2. 架构设计: 选择合适的配置参数和扩展模块
  3. RTL 实现: 使用 Chisel 进行硬件描述和实现
  4. 功能验证: 通过单元测试和集成测试验证功能正确性
  5. 性能评估: 使用仿真器和 FPGA 进行性能分析和优化

工具链支持

  • Chisel/FIRRTL: 硬件描述和综合工具链
  • Verilator: 快速仿真和验证
  • VCS: 商业级仿真工具
  • FireSim: FPGA 加速仿真平台
  • Chipyard: 集成开发环境和工具链

BuckyBall Scala 源码

该目录包含了 BuckyBall 项目的所有 Scala/Chisel 硬件描述语言源码,实现了硬件架构设计和仿真环境。

概述

BuckyBall 采用 Scala/Chisel 作为硬件描述语言,基于 Berkeley 的 Rocket-chip 和 Chipyard 框架构建。该目录包含了从底层硬件组件到系统级集成的实现。

主要功能模块包括:

  • framework: 核心框架实现,包含处理器核心、内存子系统、总线互连等
  • prototype: 专用加速器的原型实现
  • examples: 示例配置和参考设计
  • sims: 仿真环境配置和接口
  • Util: 通用工具类和辅助函数

代码结构

scala/
├── framework/          - BuckyBall 核心框架
│   ├── blink/          - Blink 通信组件
│   ├── builtin/        - 内置硬件组件
│   │   ├── frontend/   - 前端处理组件
│   │   ├── memdomain/  - 内存域实现
│   │   └── util/       - 框架工具类
│   └── rocket/         - Rocket 核心扩展
├── prototype/          - 专用加速器原型
│   ├── format/         - 数据格式处理
│   ├── im2col/         - 图像处理加速
│   ├── matrix/         - 矩阵运算引擎
│   ├── transpose/      - 矩阵转置加速
│   └── vector/         - 向量处理单元
├── examples/           - 示例和配置
│   └── toy/            - 玩具示例系统
├── sims/               - 仿真配置
│   ├── firesim/        - FireSim FPGA 仿真
│   └── verilator/      - Verilator 仿真
└── Util/               - 通用工具类

模块说明

framework/ - 核心框架

实现了 BuckyBall 的核心架构组件,包括:

  • 处理器核心和扩展
  • 内存子系统和缓存层次
  • 总线互连和通信协议
  • 系统配置和参数化机制

prototype/ - 加速器原型

包含专用计算加速器的硬件实现:

  • 机器学习加速器(矩阵运算、卷积等)
  • 数据处理加速器(格式转换、转置等)
  • 向量处理单元(SIMD、多线程等)

examples/ - 示例配置

提供系统配置示例和参考设计:

  • 基础配置模板
  • 自定义扩展示例
  • 集成测试用例

sims/ - 仿真环境

支持多种仿真器和验证环境:

  • Verilator 仿真
  • FireSim FPGA 仿真
  • 性能分析和调试工具

开发指南

构建系统

BuckyBall 使用 Mill 作为构建工具:

# 编译所有模块
mill arch.compile

# 生成 Verilog
mill arch.runMain examples.toy.ToyBuckyBall

# 运行测试
mill arch.test

代码规范

  • 遵循 Scala 和 Chisel 编码规范
  • 使用 ScalaFmt 进行代码格式化
  • 每个模块包含文档和测试
  • 配置参数化使用 Chipyard Config 系统

扩展开发

  1. 新增加速器: 在 prototype/ 目录下创建新模块
  2. 修改框架: 在 framework/ 目录下扩展现有组件
  3. 添加配置: 在 examples/ 目录下创建新的配置文件
  4. 集成测试: 使用 sims/ 目录下的仿真环境验证

相关文档

BuckyBall 工具函数库

概述

该目录包含了 BuckyBall 框架中的通用工具函数和辅助模块,主要提供可复用的硬件设计组件。目录位于 arch/src/main/scala/Util 下,在整个架构中作为基础工具层,为其他模块提供通用的硬件构建块。

主要功能包括:

  • Pipeline: 流水线控制和管理工具
  • 通用的硬件设计模式实现

代码结构

Util/
└── Pipeline.scala    - 流水线控制实现

文件依赖关系

Pipeline.scala (基础工具层)

  • 提供通用的流水线控制逻辑
  • 被其他需要流水线功能的模块引用
  • 实现标准的流水线接口和控制信号

模块说明

Pipeline.scala

主要功能: 提供通用的流水线控制和管理功能

关键组件:

class Pipeline extends Module {
  val io = IO(new Bundle {
    val flush = Input(Bool())
    val stall = Input(Bool())
    val valid_in = Input(Bool())
    val ready_out = Output(Bool())
    val valid_out = Output(Bool())
  })

  // 流水线控制逻辑
  val pipeline_valid = RegInit(false.B)

  when(io.flush) {
    pipeline_valid := false.B
  }.elsewhen(!io.stall) {
    pipeline_valid := io.valid_in
  }

  io.ready_out := !io.stall
  io.valid_out := pipeline_valid && !io.flush
}

流水线控制信号:

  • flush: 流水线冲刷信号,清空所有流水线级
  • stall: 流水线暂停信号,保持当前状态
  • valid_in: 输入数据有效信号
  • ready_out: 准备接收新数据信号
  • valid_out: 输出数据有效信号

输入输出:

  • 输入: 控制信号(flush, stall)和数据有效信号
  • 输出: 流水线状态和数据有效指示
  • 边缘情况: flush优先级高于stall,确保正确的流水线行为

依赖项: Chisel3基础库,标准的Module和Bundle接口

使用方法

使用方法

集成流水线控制:

class MyModule extends Module {
  val pipeline = Module(new Pipeline)

  // 连接控制信号
  pipeline.io.flush := flush_condition
  pipeline.io.stall := stall_condition
  pipeline.io.valid_in := input_valid

  // 使用流水线输出
  val output_enable = pipeline.io.valid_out
}

设计模式

流水线级联:

  • 支持多级流水线的级联连接
  • 提供标准的ready/valid握手协议
  • 确保数据流的正确性和时序

背压处理:

  • 实现标准的背压传播机制
  • 支持上游模块的暂停和恢复
  • 保证数据不丢失和不重复

注意事项

  1. 时序约束: flush信号应该在时钟上升沿同步断言
  2. 复位行为: 流水线在复位时应该清空所有有效位
  3. 组合逻辑: ready信号是组合逻辑,避免时序路径问题
  4. 扩展性: 设计支持参数化的流水线深度和数据宽度

BuckyBall 框架核心

概述

该目录包含了 BuckyBall 框架的核心实现,是整个硬件架构的基础层。目录位于 arch/src/main/scala/framework 下,在系统架构中作为核心框架层,提供处理器核心、内置组件和系统互连的完整实现。

主要功能模块包括:

  • rocket: 基于Rocket-chip的处理器核心定制实现
  • builtin: 内置硬件组件库,包含内存域、前端等模块
  • blink: 系统互连和通信框架

代码结构

framework/
├── rocket/           - Rocket核心定制实现
├── builtin/          - 内置组件库
│   ├── memdomain/    - 内存域实现
│   ├── frontend/     - 前端组件
│   └── util/         - 框架工具函数
└── blink/            - 系统互连框架

文件依赖关系

rocket/ (处理器核心层)

  • 实现BuckyBall定制的Rocket处理器核心
  • 扩展标准Rocket-chip功能
  • 提供RoCC协处理器接口

builtin/ (内置组件层)

  • 提供标准化的硬件组件实现
  • 包含内存子系统、前端处理等模块
  • 为上层应用提供基础硬件抽象

blink/ (互连层)

  • 实现系统级互连和通信协议
  • 提供总线仲裁和路由功能
  • 支持多核和多域通信

数据流向

应用层 → rocket核心 → builtin组件 → blink互连 → 物理接口
         ↓           ↓            ↓
    RoCC协处理器  内存域组件   系统总线

模块说明

rocket/ - Rocket核心实现

主要功能: 提供BuckyBall定制的RISC-V处理器核心

关键特性:

  • 基于Berkeley Rocket-chip架构
  • 扩展RoCC协处理器接口
  • 支持自定义指令和CSR
  • 集成BuckyBall特有的功能扩展

核心文件:

  • RocketCoreBB.scala: BuckyBall版本的Rocket核心
  • RocketTileBB.scala: 处理器Tile实现
  • LazyRoCCBB.scala: RoCC协处理器框架
  • CSRBB.scala: 控制状态寄存器扩展

builtin/ - 内置组件库

主要功能: 提供标准化的硬件组件实现

组件分类:

  • memdomain: 内存域组件,包含存储器和DMA引擎
  • frontend: 前端处理组件
  • util: 框架级工具函数

设计特点:

  • 模块化和可配置设计
  • 标准化的接口定义
  • 支持参数化实例化

主要功能: 实现系统级互连和通信协议

关键组件:

  • blink.scala: 互连协议实现
  • bbus.scala: 系统总线定义
  • ball.scala: 球域通信机制

互连特性:

  • 支持多种总线协议
  • 提供仲裁和路由功能
  • 延迟和带宽管理

使用方法

框架集成

配置系统:

class BuckyBallConfig extends Config(
  new WithBuckyBallRocket ++
  new WithBuiltinComponents ++
  new WithBlinkInterconnect ++
  new BaseConfig
)

模块实例化:

class BuckyBallSystem(implicit p: Parameters) extends LazyModule {
  val rocket = LazyModule(new RocketTileBB)
  val memdomain = LazyModule(new MemDomain)
  val interconnect = LazyModule(new BlinkInterconnect)

  // 连接各模块
  interconnect.node := rocket.masterNode
  memdomain.node := interconnect.slaveNode
}

扩展开发

添加新组件:

  1. 在builtin目录下创建新的组件模块
  2. 实现标准的LazyModule接口
  3. 在配置系统中注册新组件
  4. 更新互连和路由逻辑

自定义处理器:

  1. 扩展RocketCoreBB实现
  2. 添加自定义指令解码
  3. 实现相应的执行单元
  4. 更新CSR和异常处理

注意事项

  1. 参数传递: 使用Chipyard的Parameters系统进行配置传递
  2. 时钟域: 注意不同组件间的时钟域crossing
  3. 复位策略: 确保各模块的复位顺序和依赖关系
  4. 性能优化: 关注关键路径和时序约束
  5. 调试支持: 集成必要的调试和监控接口

相关文档

Blink 互连系统实现

概述

该目录实现了 BuckyBall 的 Blink 互连系统,基于 Diplomacy 框架提供 Ball 和 BBus 之间的连接协商。位于 arch/src/main/scala/framework/blink 下,作为系统互连层,负责管理 SRAM 带宽资源的分配和协商。

实现的核心组件:

  • ball.scala: Ball 模块,作为 Diplomacy source 端
  • bbus.scala: BBus 模块,作为 Diplomacy sink 端
  • blink.scala: Blink 协议定义和 nexus 节点

代码结构

blink/
├── ball.scala    - Ball 模块(source)
├── bbus.scala    - BBus 模块(sink)
└── blink.scala   - Blink 协议和 nexus

ball节点请求带宽需求 bbus节点给出系统的带宽能力 两者在blinkNode协商

Ball nodes -> BlinkNode (nexus) -> BBusNode (sink)

文件依赖关系

blink.scala (协议定义层)

  • 定义 BBusParams, BallParams, BlinkParams
  • 实现 BlinkNodeImp 和各种 Node 类型
  • 提供 BlinkBundle 接口定义

ball.scala (source 端)

  • 继承 LazyModule,使用 BallNode
  • 向上发送带宽需求参数
  • 提供默认的接口实现

bbus.scala (sink 端)

  • 继承 LazyModule,使用 BBusNode
  • 接收来自 Ball 的连接请求
  • 实现带宽资源的分配

模块说明

blink.scala

主要功能: 定义 Blink 协议的参数类型和 Diplomacy 节点

参数定义:

case class BBusParams (sramReadBW: Int = 2, sramWriteBW: Int = 1)  // DownParam
case class BlinkParams(sramReadBW: Int = 2, sramWriteBW: Int = 1)  // EdgeParam
case class BallParams (sramReadBW: Int = 2, sramWriteBW: Int = 1)  // UpParam

协议接口:

class BlinkBundle(params: BlinkParams) extends Bundle {
  val cmd = new Bundle {
    val req = Flipped(Decoupled(new BallRsIssue))
    val resp = Decoupled(new BallRsComplete)
  }
  val data = new Bundle {
    val sramRead = Vec(params.sramReadBW, Flipped(new SramReadIO(...)))
    val sramWrite = Vec(params.sramWriteBW, Flipped(new SramWriteIO(...)))
  }
  val status = Decoupled(new BlinkStatus())
}

带宽协商逻辑:

def edge(pd: BBusParams, pu: BallParams, p: Parameters, sourceInfo: SourceInfo) = {
  require(pd.sramReadBW >= pu.sramReadBW, "BBus 读带宽必须大于等于 Ball 需求")
  require(pd.sramWriteBW >= pu.sramWriteBW, "BBus 写带宽必须大于等于 Ball 需求")
  BlinkParams(pd.sramReadBW, pd.sramWriteBW)
}

ball.scala

主要功能: Ball 模块作为 Diplomacy source 端,向上发送带宽需求

关键实现:

class Ball(params: BallParams) extends LazyModule {
  val node = new BallNode(Seq(BBusParams(params.sramReadBW, params.sramWriteBW)))

  lazy val module = new LazyModuleImp(this) {
    val edgeParams = node.edges.out.head
    val io = IO(new BlinkBundle(edgeParams))
    node.out.head._1 <> io
  }
}

默认接口行为:

// Ball接口默认值(由各个具体Ball实现来覆盖)
io.cmd.req.ready := false.B
io.cmd.resp.valid := false.B
io.cmd.resp.bits := DontCare

bbus.scala

主要功能: BBus 模块作为 Diplomacy sink 端,接收 Ball 的连接

关键实现:

class BBus(params: BallParams) extends LazyModule {
  val node = new BBusNode(params)

  lazy val module = new LazyModuleImp(this) {
    val edgeParams = node.edges.in.head
    val io = IO(new Bundle {
      val blink = Flipped(new BlinkBundle(edgeParams))
    })
    node.in.head._1 <> io.blink
  }
}

接口规范:

前端输入: RS cmd, MemDomain bridge: n0 * sramRead(), m0 * sramWrite()
后端输出: Ball cmd, n1 * sramRead(), m1 * sramWrite()
要求: n0 >= n1, m0 >= m1

使用方法

使用方法

创建 Ball 和 BBus 连接:

val ball = LazyModule(new Ball(BallParams(sramReadBW = 2, sramWriteBW = 1)))
val bbus = LazyModule(new BBus(BallParams(sramReadBW = 4, sramWriteBW = 2)))
bbus.node := ball.node

Blink nexus 使用:

val blink = LazyModule(new Blink())
// 连接多个 Ball 和 BBus

注意事项

  1. 带宽约束: BBus 的带宽必须大于等于连接的 Ball 带宽需求
  2. Diplomacy 协商: 参数在编译时通过 Diplomacy 框架协商确定
  3. 接口连接: 使用 <> 操作符连接 Diplomacy 生成的接口
  4. 默认实现: Ball 和 BBus 提供默认接口行为,需要具体实现覆盖

BuckyBall Rocket Core Framework

这个目录包含了 BuckyBall 框架中对 Rocket 核心的定制化实现。整个架构建立在 Chipyard/Rocket-chip 框架的基础之上,但为了支持 BuckyBall 的自定义 RoCC 协处理器进行了深度的扩展和修改。BuckyBall 的设计哲学是在保持与上游 Rocket-chip 兼容性的同时,通过并行的类层次结构来实现自己的功能扩展,这样既避免了直接修改上游代码带来的维护问题,又能充分利用 Rocket-chip 的成熟架构。

在 Chipyard 的层次结构中,最顶层是 SoC 子系统,它负责整合多个处理器核心、缓存子系统、互连总线、内存控制器以及各种外设。BuckyBall 通过 RocketSubsystem.scala 来定义自己的子系统实现,其中 RocketSubsystem 类继承自 Chipyard 的 BaseSubsystem 并混入了多个特质来获得必要的功能支持。这些特质包括 InstantiatesHierarchicalElements 用于管理层次化组件的实例化,HasTileNotificationSinksHasTileInputConstants 用于处理 tile 间的通信,CanHavePeripheryCLINTCanHavePeripheryPLIC 用于中断控制器的支持,以及 HasPeripheryDebug 用于调试支持。通过这种多继承的设计模式,BuckyBall 能够复用 Chipyard 的大部分基础设施,同时在需要的地方进行定制化扩展。重要的是,RocketSubsystem 还定义了 RocketTileAttachParamsBB 来描述如何将 BuckyBall 版本的 Rocket tile 连接到子系统中,这个参数类指定了 tile 的配置参数以及跨时钟域的连接方式。

往下一层是 tile 级别,这里 RocketTileBB.scala 定义了单个 Rocket tile 的完整实现。在 Chipyard 的设计中,一个 tile 是一个相对独立的处理单元,包含了处理器核心、L1 指令和数据缓存、可选的向量单元、RoCC 协处理器接口,以及与系统总线的连接接口。RocketTileBB 类通过继承 BaseTile 获得了基本的 tile 功能,同时混入了多个关键的特质。SinksExternalInterruptsSourcesExternalNotifications 处理外部中断和通知的接收与发送,HasLazyRoCCBB 是 BuckyBall 特有的特质,用于支持 BuckyBall 的 RoCC 协处理器框架,HasHellaCache 提供了与 L1 数据缓存的接口,HasICacheFrontend 则提供了取指前端的实现。这种多特质组合的设计让 RocketTileBB 能够获得所有必要的功能,同时保持代码的模块化和可维护性。特别值得注意的是,RocketTileBB 定义了自己的参数类型 RocketTileParamsBB,这个参数类包含了 tile 的所有配置信息,包括核心参数、缓存参数、BTB 参数等,并且通过 InstantiableTileParams 特质提供了实例化的接口。

在 tile 内部,最核心的组件是处理器核心本身,这由 RocketCoreBB.scala 实现。这个文件包含了对原始 Rocket 核心的重新实现,其中 RocketBB 类继承自 CoreModule 并混入了 HasRocketCoreParametersHasRocketCoreIOBB 特质。CoreModule 提供了核心模块的基本框架,HasRocketCoreParameters 提供了各种核心参数的访问接口,而 HasRocketCoreIOBB 则定义了 BuckyBall 特有的核心 IO 接口。这个 IO 接口与标准的 Rocket 核心 IO 最大的区别在于使用了 RoCCCoreIOBB 而不是标准的 RoCCCoreIO,这样就能支持 BuckyBall 特有的 RoCC 接口扩展。在核心的实现中,最关键的修改是指令解码表的处理,原始的 Rocket 核心会根据 usingRoCC 参数来决定是否包含 RoCC 指令的解码逻辑,但由于 BuckyBall 使用的是 BuildRoCCBB 而不是标准的 BuildRoCC,这会导致 usingRoCC 返回 false,从而使得 RoCC 指令无法被正确解码。为了解决这个问题,BuckyBall 强制在解码表中包含 RoCCDecode,确保自定义指令能够被正确识别和处理。

RoCC 协处理器的支持是通过 LazyRoCCBB.scala 来实现的,这个文件定义了 BuckyBall 特有的 RoCC 框架。HasLazyRoCCBB 特质是这个框架的核心,它负责管理 RoCC 协处理器的实例化和连接。这个特质会根据 BuildRoCCBB 配置来创建相应的 RoCC 实例,并且为每个 RoCC 分配独立的 CSR 地址空间。HasLazyRoCCModuleBB 特质则负责在模块级别进行 RoCC 的连接,它实例化了 RoccCommandRouterBB 来负责指令的路由。这个路由器会根据指令的 opcode 来决定将指令发送给哪个具体的 RoCC 实例,同时也负责将来自不同 RoCC 的响应进行仲裁后返回给核心。路由器的设计考虑了指令的并发执行和响应的顺序性,确保系统的正确性和性能。

CSRBB.scala 包含了对控制状态寄存器子系统的重新实现,这是 BuckyBall 框架中最复杂的部分之一。CSR 子系统负责处理所有的控制状态寄存器访问,包括标准的 RISC-V CSR 以及 BuckyBall 特有的扩展 CSR。这个实现基于原始的 Rocket CSR 实现,但针对 BuckyBall 的需求进行了扩展,支持更灵活的 CSR 地址分配和更复杂的读写逻辑。特别是对于 RoCC 相关的 CSR,BuckyBall 实现了动态的地址分配机制,允许不同的 RoCC 实例拥有独立的 CSR 空间,避免了地址冲突的问题。

RoCCFragments.scala 定义了 BuckyBall 版本的 RoCC 接口数据结构,这些结构在保持与标准 RoCC 接口兼容的同时,提供了额外的扩展能力。这包括扩展的命令格式、响应格式,以及额外的控制信号。这些接口定义是整个 BuckyBall RoCC 生态系统的基础,确保了不同组件之间的正确通信。

Configs.scala 包含了丰富的配置定义,这些配置类通过 Chipyard 的配置系统来指定各种硬件参数。配置系统使用了函数式编程的思想,通过配置函数的组合来构建复杂的系统配置。BuckyBall 的配置定义了如何将 BuckyBall 特有的组件集成到整个系统中,包括 RoCC 的配置、CSR 的配置、以及各种性能参数的设置。

整个架构最关键的设计挑战在于参数传递和配置的一致性。Chipyard/Rocket-chip 框架广泛使用了 Scala 的隐式参数机制和 Parameters 配置系统,这个系统允许配置信息在整个硬件层次结构中进行传递和覆盖。BuckyBall 面临的主要问题是如何确保自己的配置信息(特别是 BuildRoCCBB 中定义的 RoCC 配置)能够正确地传递到所有需要这些信息的组件中。由于原始的 Rocket 核心只认识 BuildRoCC 而不知道 BuildRoCCBB 的存在,BuckyBall 采用了一个巧妙的解决方案:在 RocketTileBB 创建 RocketBB 核心实例时,动态地修改传递给核心的 Parameters 对象,将 BuildRoCCBB 中的内容合并到 BuildRoCC 中。这样,从核心的视角来看,BuckyBall 的 RoCC 就像是标准的 RoCC 一样,所有基于 BuildRoCC 的逻辑都能正常工作,包括 usingRoCC 参数的计算、端口数量的计算、以及指令解码表的构建。这种设计既保持了与上游代码的兼容性,又实现了 BuckyBall 所需的功能扩展,体现了软件工程中适配器模式的优雅应用。

BuckyBall 内置组件库

概述

该目录包含了 BuckyBall 框架的内置硬件组件实现,提供标准化的可复用硬件模块。位于 arch/src/main/scala/framework/builtin 下,作为组件库层,为上层系统提供经过验证的硬件构建块。

主要组件模块:

  • memdomain: 内存域组件,包含存储器和DMA引擎
  • frontend: 前端处理组件
  • util: 框架级工具函数
  • BaseConfigs.scala: 基础配置定义

代码结构

builtin/
├── BaseConfigs.scala - 基础配置参数定义
├── memdomain/        - 内存域实现
├── frontend/         - 前端组件
└── util/             - 工具函数库

文件依赖关系

BaseConfigs.scala (配置基础层)

  • 定义所有内置组件的基础配置参数
  • 提供默认配置和参数验证
  • 被所有子模块引用作为配置源

memdomain/ (内存子系统)

  • 依赖 BaseConfigs 获取内存相关配置
  • 实现存储器、DMA、地址管理等功能
  • 为其他组件提供内存访问服务

frontend/ (前端处理)

  • 使用 BaseConfigs 中的前端配置参数
  • 实现指令获取、解码等前端功能
  • 与处理器核心紧密集成

util/ (工具库)

  • 提供通用的硬件设计工具函数
  • 被其他组件广泛使用
  • 独立于具体配置参数

模块说明

BaseConfigs.scala

主要功能: 定义内置组件的基础配置参数和默认值

关键组件:

trait BaseConfig extends Config {
  // 内存域配置
  case object MemDomainKey extends Field[MemDomainParams](MemDomainParams())

  // 前端配置
  case object FrontendKey extends Field[FrontendParams](FrontendParams())

  // 工具配置
  case object UtilKey extends Field[UtilParams](UtilParams())
}

case class MemDomainParams(
  spBanks: Int = 16,           // 暂存器Bank数量
  spBankEntries: Int = 1024,   // 每个Bank条目数
  accBanks: Int = 4,           // 累加器Bank数量
  accBankEntries: Int = 256,   // 累加器Bank条目数
  dmaEngines: Int = 2          // DMA引擎数量
)

配置参数:

  • MemDomainParams: 内存域相关参数
  • FrontendParams: 前端处理参数
  • UtilParams: 工具函数参数

参数验证:

require(spBanks > 0, "SP banks must be positive")
require(isPow2(spBankEntries), "SP bank entries must be power of 2")
require(accBanks > 0, "ACC banks must be positive")

输入输出:

  • 输入: 用户自定义配置覆盖
  • 输出: 验证后的完整配置参数
  • 边缘情况: 参数冲突检测和错误报告

memdomain/ 子模块

主要功能: 实现完整的内存域功能

包含组件:

  • mem/: 存储器组件(SramBank, AccBank, Scratchpad)
  • dma/: DMA引擎(BBStreamReader, BBStreamWriter, LocalAddr)

接口定义:

class MemDomainIO(implicit p: Parameters) extends Bundle {
  val exec = new Bundle {
    val cmd = Flipped(Decoupled(new MemCmd))
    val resp = Decoupled(new MemResp)
  }
  val dma = new Bundle {
    val read = Decoupled(new DMAReadReq)
    val write = Flipped(Decoupled(new DMAWriteReq))
  }
}

frontend/ 子模块

主要功能: 实现处理器前端功能

核心功能:

  • 指令获取和缓存
  • 分支预测和跳转处理
  • 指令解码预处理

util/ 子模块

主要功能: 提供通用工具函数

工具类别:

  • 数学运算工具
  • 接口转换工具
  • 调试和监控工具

使用方法

配置使用

基础配置继承:

class MySystemConfig extends Config(
  new BaseConfig ++
  new WithCustomMemDomain(spBanks = 32) ++
  new WithCustomFrontend(icacheWays = 8)
)

参数访问:

class MyModule(implicit p: Parameters) extends Module {
  val memParams = p(MemDomainKey)
  val spBanks = memParams.spBanks
  val accBanks = memParams.accBanks
}

扩展开发

添加新组件:

  1. 在相应子目录创建新模块
  2. 在 BaseConfigs.scala 中添加配置参数
  3. 实现标准的 LazyModule 接口
  4. 添加相应的测试用例

自定义配置:

case class MyComponentParams(
  param1: Int = 16,
  param2: Boolean = true
)

trait WithMyComponent extends Config {
  case object MyComponentKey extends Field[MyComponentParams](MyComponentParams())
}

注意事项

  1. 配置一致性: 确保相关组件的配置参数兼容
  2. 资源约束: 注意硬件资源的合理分配
  3. 时序优化: 关注跨组件的时序路径
  4. 接口标准: 遵循统一的接口设计规范
  5. 测试覆盖: 为每个组件提供充分的测试用例

前端处理组件

概述

该目录实现了 BuckyBall 的指令解码和调度功能,位于 arch/src/main/scala/framework/builtin/frontend 下,作为指令处理前端,负责指令的全局解码和后续调度管理。

实现的核心组件:

  • GlobalDecoder: 全局指令解码器,区分指令类型
  • rs/: 保留站相关组件,包含指令调度和重排序缓冲

代码结构

frontend/
├── GobalDecoder.scala    - 全局指令解码器
└── rs/                   - 保留站组件
    ├── CommitQueue.scala        - 提交队列
    ├── IssueQueue.scala         - 发射队列
    ├── NextROBIdCounter.scala   - ROB ID计数器
    ├── ReorderBuffer.scala      - 重排序缓冲
    └── ReservationStation.scala - 保留站

文件依赖关系

GobalDecoder.scala (指令解码层)

  • 接收 RoCCCommandBB 指令
  • 区分 Ball 指令和内存指令
  • 输出 PostGDCmd 给后续处理

rs/ (指令调度层)

  • 依赖 GlobalDecoder 的输出
  • 实现乱序执行的指令调度
  • 管理指令的发射、执行和提交

模块说明

GobalDecoder.scala

主要功能: 对 RoCC 指令进行全局解码和分类

关键组件:

class PostGDCmd(implicit b: CustomBuckyBallConfig, p: Parameters) extends Bundle {
  val is_ball = Bool()    // Ball指令(包括FENCE)
  val is_mem = Bool()     // 内存指令(load/store)
  val raw_cmd = new RoCCCommandBB  // 原始指令信息
}

class GlobalDecoder(implicit b: CustomBuckyBallConfig, p: Parameters) extends Module {
  val io = IO(new Bundle {
    val id_i = Flipped(Decoupled(new Bundle {
      val cmd = new RoCCCommandBB
    }))
    val id_o = Decoupled(new PostGDCmd)
  })
}

指令分类逻辑:

val func7 = io.id_i.bits.cmd.inst.funct
val is_mem_instr = (func7 === MVIN_BITPAT) || (func7 === MVOUT_BITPAT)
val is_ball_instr = !is_mem_instr

输入输出:

  • 输入: RoCCCommandBB 指令流
  • 输出: 分类后的 PostGDCmd
  • 边缘情况: 通过 ready/valid 握手处理背压

依赖项: framework.rocket.RoCCCommandBB, framework.builtin.memdomain.DISA

rs/ 保留站组件

主要功能: 实现乱序执行的指令调度机制

组件说明:

  • ReservationStation: 保留站主体,管理指令的暂存和调度
  • IssueQueue: 发射队列,控制指令的发射时机
  • ReorderBuffer: 重排序缓冲,保证指令按序提交
  • CommitQueue: 提交队列,管理指令的最终提交
  • NextROBIdCounter: ROB ID 分配计数器

数据流向:

GlobalDecoder → ReservationStation → IssueQueue → 执行单元
                      ↓
              ReorderBuffer → CommitQueue → 提交

使用方法

使用方法

指令处理流程:

  1. RoCC 指令进入 GlobalDecoder
  2. 根据 funct 字段区分指令类型
  3. 分类后的指令进入对应的保留站
  4. 保留站管理指令的调度和执行

配置参数:

// 在 CustomBuckyBallConfig 中配置
implicit val config: CustomBuckyBallConfig

注意事项

  1. 指令分类: 仅区分内存指令(MVIN/MVOUT)和其他指令
  2. 背压处理: 使用标准的 Decoupled 接口处理流控
  3. 依赖关系: 需要正确配置 DISA 模块的指令位模式
  4. ROB 管理: 保留站需要与 ROB 协调保证指令顺序

前端保留站 (Frontend Reservation Station)

概述

该目录包含了 BuckyBall 框架前端保留站的实现代码,用于支持乱序执行和指令调度。注意:当前所有实现文件都被注释掉,表明这是一个未完成或正在重构的模块。

二、文件结构

rs/
├── ReservationStation.scala  - 主保留站模块(已注释)
├── ReorderBuffer.scala      - 重排序缓冲区(已注释)
├── IssueQueue.scala         - 发射队列(已注释)
├── CommitQueue.scala        - 提交队列(已注释)
└── NextROBIdCounter.scala   - ROB ID 计数器(已注释)

三、设计概念

原始设计目标

基于注释掉的代码,该模块原本设计用于:

ReservationStation - 主保留站

  • 连接前端指令解码和后端执行单元
  • 管理指令的分配、发射和提交流程
  • 提供 RoCC 接口支持

ReorderBuffer - 重排序缓冲区

  • 维护指令的程序顺序
  • 支持乱序完成但顺序提交
  • 实现 Load/Store 互斥约束
  • 支持 Fence 指令的同步语义

IssueQueue - 发射队列

  • 根据指令类型分发到不同执行单元
  • 支持 Load、Store、Execute 三种指令类型
  • 实现指令队列管理

CommitQueue - 提交队列

  • 处理多个执行单元的完成信号
  • 使用轮询仲裁器管理提交顺序
  • 向 ROB 报告指令完成状态

四、架构特点

指令流水线

指令解码 → ROB分配 → 发射队列 → 执行单元 → 提交队列 → ROB提交

指令类型支持

  • Load指令 (cmd_type = 1): 内存读取操作
  • Store指令 (cmd_type = 2): 内存写入操作
  • Execute指令 (cmd_type = 3): Ball域计算操作
  • Fence指令 (cmd_type = 4): 同步屏障操作

约束机制

  • Load/Store互斥: 防止内存访问冲突
  • Execute延迟: Execute指令间的最小间隔约束
  • 顺序提交: 保证程序语义的正确性

五、状态管理

ROB 条目状态

object RoBState extends ChiselEnum {
  val sInvalid  = Value   // 无效状态
  val sWaiting  = Value   // 等待分发
  val sIssued   = Value   // 已发射到执行单元
  val sComplete = Value   // 执行完成,等待提交
}

执行跟踪

  • load_in_flight: 跟踪正在执行的Load指令
  • store_in_flight: 跟踪正在执行的Store指令
  • ex_in_flight: 跟踪正在执行的Execute指令

六、当前状态

实现状态

  • 所有文件已注释: 当前实现不可用
  • 编译状态: 无法编译和使用
  • 功能验证: 未经测试验证

可能原因

  1. 架构重构: 可能正在进行架构重新设计
  2. 功能迁移: 功能可能已迁移到其他模块
  3. 实验性代码: 可能是实验性实现,暂时禁用
  4. 依赖问题: 可能存在依赖关系问题

七、替代方案

当前可用的保留站实现

  • Ball域保留站: examples/toy/balldomain/rs/ 目录下的实现
  • 简化版本: 针对特定应用场景的简化实现

迁移建议

如果需要使用保留站功能,建议:

  1. 使用 Ball 域的保留站实现
  2. 参考注释代码进行自定义实现
  3. 等待官方重新启用此模块

八、重新启用指南

如果需要重新启用此模块:

  1. 取消注释: 移除所有文件开头的注释标记
  2. 依赖检查: 确保所有依赖的类和包可用
  3. 接口适配: 可能需要适配新的接口定义
  4. 测试验证: 进行完整的功能测试

潜在问题

  • 接口不兼容: 可能与当前系统接口不匹配
  • 依赖缺失: 某些依赖的类可能已被移除或重命名
  • 配置参数: 配置参数可能需要更新

九、开发建议

如果要基于此代码开发:

  1. 理解设计: 仔细研究注释掉的代码逻辑
  2. 模块化重构: 考虑将功能分解为更小的模块
  3. 接口标准化: 使用标准化的接口定义
  4. 测试驱动: 采用测试驱动的开发方式

设计改进建议

  • 简化约束逻辑: 当前的Load/Store互斥可能过于严格
  • 提高并行度: 支持更多指令的并行执行
  • 动态调度: 实现更智能的指令调度算法

十、相关文档

十一、注意事项

⚠️ 重要提醒

  • 当前代码不可直接使用
  • 需要大量修改才能重新启用
  • 建议优先使用其他可用的保留站实现
  • 如有疑问,请联系项目维护者

框架工具函数库

概述

该目录包含了 BuckyBall 框架级别的工具函数和辅助模块,提供通用的硬件设计工具。位于 arch/src/main/scala/framework/builtin/util 下,作为工具函数层,为框架内的其他组件提供可复用的硬件构建块和实用函数。

主要工具类别:

  • 数学运算和位操作工具
  • 接口转换和适配器
  • 调试和性能监控工具
  • 通用硬件模式实现

代码结构

util/
└── (具体工具文件待分析)

工具分类

数学工具

  • 位宽计算和对数函数
  • 数值转换和格式化
  • 算术运算优化实现

接口工具

  • 协议转换适配器
  • 信号同步和跨时钟域
  • 握手协议实现

调试工具

  • 性能计数器模板
  • 调试信号输出
  • 状态监控接口

模块说明

数学运算工具

主要功能: 提供常用的数学运算和位操作函数

关键函数:

object MathUtils {
  // 计算log2向上取整
  def log2Ceil(x: Int): Int = {
    require(x > 0)
    (log(x) / log(2)).ceil.toInt
  }

  // 判断是否为2的幂
  def isPow2(x: Int): Boolean = x > 0 && (x & (x - 1)) == 0

  // 计算最小的2的幂大于等于x
  def nextPow2(x: Int): Int = {
    if (isPow2(x)) x else 1 << log2Ceil(x)
  }
}

位操作工具:

object BitUtils {
  // 位反转
  def reverseBits(data: UInt, width: Int): UInt = {
    VecInit((0 until width).map(i => data(i))).asUInt
  }

  // 计算汉明重量(1的个数)
  def popCount(data: UInt): UInt = {
    PopCount(data)
  }

  // 前导零计数
  def leadingZeros(data: UInt, width: Int): UInt = {
    PriorityEncoder(Reverse(data))
  }
}

接口转换工具

主要功能: 提供常用的接口转换和适配功能

协议转换器:

class DecoupledToValid[T <: Data](gen: T) extends Module {
  val io = IO(new Bundle {
    val in = Flipped(Decoupled(gen))
    val out = Valid(gen)
  })

  io.out.valid := io.in.valid
  io.out.bits := io.in.bits
  io.in.ready := true.B
}

class ValidToDecoupled[T <: Data](gen: T) extends Module {
  val io = IO(new Bundle {
    val in = Flipped(Valid(gen))
    val out = Decoupled(gen)
  })

  io.out.valid := io.in.valid
  io.out.bits := io.in.bits
}

跨时钟域同步:

class AsyncFIFO[T <: Data](gen: T, depth: Int) extends Module {
  val io = IO(new Bundle {
    val enq_clock = Input(Clock())
    val enq_reset = Input(Bool())
    val enq = Flipped(Decoupled(gen))

    val deq_clock = Input(Clock())
    val deq_reset = Input(Bool())
    val deq = Decoupled(gen)
  })

  // 异步FIFO实现
  // 使用格雷码指针避免亚稳态
}

调试监控工具

主要功能: 提供调试和性能监控的通用工具

性能计数器:

class PerfCounter(name: String) extends Module {
  val io = IO(new Bundle {
    val inc = Input(Bool())
    val value = Output(UInt(64.W))
  })

  val counter = RegInit(0.U(64.W))
  when(io.inc) {
    counter := counter + 1.U
  }
  io.value := counter

  // 可选的调试输出
  when(io.inc) {
    printf(s"[PerfCounter] $name: %d\n", counter + 1.U)
  }
}

调试信号输出:

object DebugUtils {
  def debugPrint(cond: Bool, fmt: String, args: Bits*): Unit = {
    when(cond) {
      printf(fmt, args: _*)
    }
  }

  def assert(cond: Bool, msg: String): Unit = {
    chisel3.assert(cond, msg)
  }

  def cover(cond: Bool, msg: String): Unit = {
    chisel3.cover(cond, msg)
  }
}

使用方法

使用示例

数学工具使用:

import util.MathUtils._

class MyModule extends Module {
  val addrBits = log2Ceil(entries)
  val bankSize = nextPow2(requestedSize)

  require(isPow2(bankSize), "Bank size must be power of 2")
}

接口转换使用:

val converter = Module(new DecoupledToValid(UInt(32.W)))
converter.io.in <> some_decoupled_signal
val valid_signal = converter.io.out

性能监控使用:

val hit_counter = Module(new PerfCounter("cache_hits"))
hit_counter.io.inc := cache_hit

val miss_counter = Module(new PerfCounter("cache_misses"))
miss_counter.io.inc := cache_miss

扩展开发

添加新工具:

  1. 确定工具的通用性和复用价值
  2. 实现标准的Chisel模块接口
  3. 添加充分的参数化支持
  4. 提供使用示例和测试用例

工具设计原则:

  • 保持接口简洁明确
  • 支持参数化配置
  • 提供良好的错误检查
  • 考虑硬件实现效率

注意事项

  1. 硬件开销: 工具函数应该考虑硬件实现的开销
  2. 时序影响: 避免在关键路径上使用复杂工具
  3. 参数验证: 在编译时进行充分的参数检查
  4. 文档完整: 为每个工具提供清晰的使用说明
  5. 测试覆盖: 确保工具函数的正确性和边界情况处理

内存域 (MemDomain) 实现

概述

内存域模块是 BuckyBall 框架中负责内存管理和数据传输的核心组件。该模块位于 framework/builtin/memdomain 路径下,实现了高性能的内存子系统,包括内存控制器、DMA 引擎、TLB 管理和地址转换等功能。

内存域在整个系统架构中扮演关键角色,为上层的处理器核心和加速器提供统一的内存访问接口,同时管理片上存储器(scratchpad、accumulator)和外部内存之间的数据流动。

二、文件结构

memdomain/
├── MemDomain.scala         - 内存域顶层模块
├── MemController.scala     - 内存控制器
├── MemLoader.scala         - 内存加载器
├── MemStorer.scala         - 内存存储器
├── DomainDecoder.scala     - 域解码器
├── DISA.scala             - 地址空间管理
├── dma/                   - DMA 引擎实现
├── mem/                   - 存储器管理
├── rs/                    - 保留站实现
└── tlb/                   - TLB 管理

三、核心组件

MemDomain - 内存域顶层模块

MemDomain 是内存域的顶层模块,负责:

  • 集成所有内存子系统组件
  • 提供统一的外部接口
  • 管理 DMA 和 Ball 域的访问协调

主要接口

class MemDomain extends Module {
  val io = IO(new Bundle {
    // 全局解码器输入
    val gDecoderIn = Flipped(Decoupled(new PostGDCmd))

    // Ball 域 SRAM 接口
    val ballDomain = new Bundle {
      val sramRead = Vec(sp_banks, new SramReadIO)
      val sramWrite = Vec(sp_banks, new SramWriteIO)
      val accRead = Vec(acc_banks, new SramReadIO)
      val accWrite = Vec(acc_banks, new SramWriteIO)
    }

    // DMA 接口
    val dma = new Bundle {
      val read = new Bundle {
        val req = Decoupled(new BBReadRequest)
        val resp = Flipped(Decoupled(new BBReadResponse))
      }
      val write = new Bundle {
        val req = Decoupled(new BBWriteRequest)
        val resp = Flipped(Decoupled(new BBWriteResponse))
      }
    }

    // TLB 接口
    val tlb = Vec(2, Flipped(new BBTLBIO))
    val ptw = Vec(2, new TLBPTWIO)
    val tlbExp = Vec(2, new BBTLBExceptionIO)
  })
}

MemController - 内存控制器

MemController 封装了 scratchpad 和 accumulator 的控制逻辑:

功能特性

  • 双端口设计: 同时支持 DMA 和 Ball 域访问
  • 存储器抽象: 提供统一的存储器访问接口
  • 资源管理: 管理 scratchpad 和 accumulator 资源

接口设计

class MemController extends Module {
  val io = IO(new Bundle {
    // DMA 接口 - 用于 MemLoader 和 MemStorer
    val dma = new Bundle {
      val sramRead = Vec(sp_banks, new SramReadIO)
      val sramWrite = Vec(sp_banks, new SramWriteIO)
      val accRead = Vec(acc_banks, new SramReadIO)
      val accWrite = Vec(acc_banks, new SramWriteIO)
    }

    // Ball 域接口 - 用于 BallController
    val ballDomain = new Bundle {
      val sramRead = Vec(sp_banks, new SramReadIO)
      val sramWrite = Vec(sp_banks, new SramWriteIO)
      val accRead = Vec(acc_banks, new SramReadIO)
      val accWrite = Vec(acc_banks, new SramWriteIO)
    }
  })
}

存储器架构

Scratchpad 存储器

  • 用途: 存储输入数据和中间结果
  • 配置: b.sp_banks 个 bank,每个 b.spad_bank_entries 条目
  • 位宽: b.spad_w 位数据位宽
  • 访问模式: 支持随机访问和顺序访问

Accumulator 存储器

  • 用途: 存储累加结果和最终输出
  • 配置: b.acc_banks 个 bank,每个 b.acc_bank_entries 条目
  • 位宽: b.acc_w 位数据位宽
  • 访问模式: 主要用于累加操作

四、数据流架构

访问路径

外部内存 ←→ DMA引擎 ←→ MemController ←→ Scratchpad/Accumulator
                                    ↕
                              Ball域加速器

双端口访问机制

MemController 实现了双端口访问机制:

  1. DMA 端口: 用于与外部内存的数据传输
  2. Ball 域端口: 用于加速器的计算访问
// 连接示例
io.dma.sramRead <> spad.io.dma.sramread
io.ballDomain.sramRead <> spad.io.exec.sramread

五、子模块说明

dma/ - DMA 引擎

实现高性能的直接内存访问:

  • BBStreamReader: 流式数据读取器
  • BBStreamWriter: 流式数据写入器
  • 地址转换: 集成 TLB 支持虚拟地址

mem/ - 存储器管理

包含存储器的具体实现:

  • Scratchpad: 片上暂存器实现
  • SRAM接口: 标准化的存储器访问接口
  • Bank管理: 多 bank 并行访问支持

tlb/ - TLB 管理

提供虚拟地址转换服务:

  • BBTLBCluster: TLB 集群管理器
  • 地址转换: 虚拟到物理地址映射
  • 异常处理: TLB 缺失和权限异常

rs/ - 保留站

内存域专用的保留站实现:

  • 指令调度: 内存访问指令的调度
  • 依赖管理: 内存访问依赖关系管理

六、配置参数

关键配置项

class CustomBuckyBallConfig {
  val sp_banks = 4              // Scratchpad bank 数量
  val spad_bank_entries = 1024  // 每个 bank 的条目数
  val spad_w = 64              // Scratchpad 数据位宽
  val spad_mask_len = 8        // 掩码长度

  val acc_banks = 2            // Accumulator bank 数量
  val acc_bank_entries = 512   // 每个 bank 的条目数
  val acc_w = 64              // Accumulator 数据位宽
  val acc_mask_len = 8        // 掩码长度
}

性能调优

  • Bank 数量: 影响并行访问能力
  • 条目数量: 影响存储容量
  • 数据位宽: 影响传输带宽
  • TLB 大小: 影响地址转换性能

七、使用示例

基本配置

// 实例化内存域
implicit val config = new CustomBuckyBallConfig
val memDomain = Module(new MemDomain)

// 连接全局解码器
memDomain.io.gDecoderIn <> globalDecoder.io.memDomainOut

// 连接 Ball 域
ballController.io.sramRead <> memDomain.io.ballDomain.sramRead
ballController.io.sramWrite <> memDomain.io.ballDomain.sramWrite

DMA 操作

// 配置 DMA 读取
memDomain.io.dma.read.req.valid := true.B
memDomain.io.dma.read.req.bits.addr := sourceAddress
memDomain.io.dma.read.req.bits.len := transferLength

// 处理 DMA 响应
when(memDomain.io.dma.read.resp.valid) {
  // 处理读取的数据
  val data = memDomain.io.dma.read.resp.bits.data
}

八、性能特性

并发访问

  • 双端口设计: DMA 和 Ball 域可并发访问
  • 多 Bank 并行: 支持多个 bank 的并行操作
  • 流水线处理: 实现深度流水线提高吞吐量

带宽优化

  • 智能调度: 优化内存访问调度
  • 缓存策略: 实现数据缓存和预取
  • 带宽管理: 动态分配访问带宽

九、调试和监控

状态监控

  • 访问统计: 监控各个 bank 的访问频率
  • 带宽利用率: 监控实际带宽使用情况
  • TLB 命中率: 监控地址转换性能

性能分析

  • 延迟统计: 测量内存访问延迟
  • 吞吐量监控: 监控数据传输吞吐量
  • 冲突检测: 检测访问冲突和瓶颈

十、相关文档

Memory Bank Implementation Module

概述

该目录包含了 BuckyBall 架构中内存域(MemDomain)的核心存储单元实现,主要负责提供高性能的片上存储器组件。该模块位于 arch/src/main/scala/framework/builtin/memdomain/mem 路径下,在整个系统架构中扮演底层硬件存储抽象的角色。

该目录实现了三种主要的存储器组件:

  • SramBank: 基础SRAM存储Bank,提供同步读写功能
  • AccBank: 累加器存储Bank,支持读-修改-写操作
  • Scratchpad: 暂存器模块,管理多个存储Bank并提供仲裁机制

这些模块在系统中处于 framework.builtin.memdomain 的底层,为上层的内存控制器(MemController)、内存加载器(MemLoader)和内存存储器(MemStorer)提供基础的存储服务。

代码结构

mem/
├── SramBank.scala     - 基础SRAMBank实现
├── AccBank.scala      - 累加器Bank实现
└── Scratchpad.scala   - 暂存器管理模块

文件依赖关系

SramBank.scala (基础层)

  • 定义了基础的SRAM接口(SramReadIO, SramWriteIO)
  • 提供同步读写存储器的底层实现
  • 被AccBank和Scratchpad模块所使用

AccBank.scala (扩展层)

  • 依赖SramBank作为底层存储
  • 实现累加器流水线(AccPipe)和读请求路由器(AccReadRouter)
  • 扩展了SramWriteIOAccWriteIO以支持累加操作

Scratchpad.scala (管理层)

  • 聚合多个SramBank和AccBank实例
  • 实现DMA和执行单元之间的请求仲裁
  • 依赖配置文件BaseConfig进行参数化

数据流向

执行单元/DMA → Scratchpad → AccBank/SramBank → 物理存储器
                    ↓
                仲裁机制处理多路访问请求

模块说明

SramBank.scala

主要功能: 提供基础的同步读写SRAM存储器实现

关键组件:

  1. 接口定义:
class SramReadReq(val n: Int) extends Bundle {
  val addr = UInt(log2Ceil(n).W)
  val fromDMA = Bool()
}

class SramWriteReq(val n: Int, val w: Int, val mask_len: Int) extends Bundle {
  val addr = UInt(log2Ceil(n).W)
  val mask = Vec(mask_len, Bool())
  val data = UInt(w.W)
}
  1. 核心逻辑:
val mem = SyncReadMem(n, Vec(mask_len, mask_elem))

// 读写冲突仲裁
assert(!(io.read.req.valid && io.write.req.valid),
       "SramBank: Read and write requests is not allowed at the same time")

io.read.req.ready := !io.write.req.valid
io.write.req.ready := !io.read.req.valid

输入输出:

  • 输入: 读/写请求,包含地址、数据、掩码信息
  • 输出: 读响应数据,带有延迟的有效信号
  • 边缘情况: 不允许同周期读写同一Bank

依赖项: Chisel3 SyncReadMem,framework.builtin.util.Util

AccBank.scala

主要功能: 实现支持累加操作的存储Bank

关键组件:

  1. 累加流水线(AccPipe):
when (io.write_in.is_acc || RegNext(io.write_in.is_acc)) {
  // Stage 1: 读请求
  io.read.req.valid := io.write_in.req.valid

  // Stage 2: 累加运算
  val acc_data = data_reg + io.read.resp.bits.data

  // Stage 3: 写回
  io.write_out.req.bits.data := acc_data
}
  1. 读请求路由器(AccReadRouter):
val req_arbiter = Module(new Arbiter(new SramReadReq(n), 2))
req_arbiter.io.in(0) <> io.read_in2.req  // 高优先级
req_arbiter.io.in(1) <> io.read_in1.req  // 低优先级

// 响应分发
val resp_to_in1 = RegNext(req_arbiter.io.chosen === 1.U && req_arbiter.io.out.fire)

输入输出:

  • 输入: 带累加标志的写请求,读请求
  • 输出: 累加结果写入底层SRAM
  • 边缘情况: 流水线背压处理,读写请求冲突仲裁

依赖项: SramBank模块,Chisel3 Arbiter

Scratchpad.scala

主要功能: 管理多个存储Bank,提供统一的暂存器接口

关键组件:

  1. Bank实例化:
val spad_mems = Seq.fill(sp_banks) { Module(new SramBank(
  spad_bank_entries, spad_w, aligned_to, sp_singleported
)) }

val acc_mems = Seq.fill(acc_banks) { Module(new AccBank(
  acc_bank_entries, acc_w, aligned_to, sp_singleported
)) }
  1. 请求仲裁机制:
// 读请求仲裁:优先级 exec > dma
val exec_read_sel = exec_read_req.valid
val main_read_sel = main_read_req.valid && !exec_read_sel

// 响应分发
val resp_to_main = RegNext(main_read_sel && bank.io.read.req.fire)
val resp_to_exec = RegNext(exec_read_sel && bank.io.read.req.fire)

输入输出:

  • 输入: DMA和执行单元的读写请求
  • 输出: 仲裁后的存储器访问
  • 边缘情况: 确保OpA和OpB不同时访问同一Bank

依赖项: BaseConfig配置,SramBank和AccBank模块,RocketChip tile参数

使用方法

注意事项

  1. 单端口限制: 配置中强制使用单端口SRAM(sp_singleported = true),不允许同周期读写操作

  2. 仲裁优先级: 在所有模块中,执行单元(exec)的请求优先级高于DMA请求

  3. 流水线设计: AccBank采用三级流水线设计(读-累加-写),需要考虑数据依赖和背压处理

  4. 配置参数化: 所有模块都支持通过BaseConfig进行参数化配置,包括Bank数量、容量、数据位宽等

  5. 断言检查: 代码中包含多个运行时断言,用于检测非法的并发访问和配置错误

  6. 掩码支持: 支持按字节粒度的写掩码操作,掩码长度由数据位宽和对齐要求计算得出

DMA Engine Implementation Module

概述

该目录包含了 BuckyBall 架构中内存域(MemDomain)的DMA引擎实现,主要负责提供高性能的内存数据传输服务。该模块位于 arch/src/main/scala/framework/builtin/memdomain/dma 路径下,在整个系统架构中扮演内存域与外部存储器之间的数据传输桥梁角色。

该目录实现了两种主要的DMA组件:

  • BBStreamReader: 流式数据读取器,支持批量从外部内存读取数据到片上存储
  • BBStreamWriter: 流式数据写入器,支持批量从片上存储写入数据到外部内存
  • LocalAddr: 本地地址管理工具,用于处理暂存器(Scratchpad)和累加器(Accumulator)的地址映射

这些模块在系统中处于 framework.builtin.memdomain 的数据传输层,为上层的内存加载器(MemLoader)和内存存储器(MemStorer)提供底层的DMA传输服务,并通过TileLink协议与外部内存系统进行通信。

代码结构

dma/
├── DMA.scala         - 流式DMA读写器实现
└── LocalAddr.scala   - 本地地址管理工具

文件依赖关系

DMA.scala (核心传输层)

  • 定义了BBReadRequestBBReadResponseBBWriteRequestBBWriteResponse等DMA传输接口
  • 实现了BBStreamReaderBBStreamWriter两个核心DMA引擎
  • 依赖TileLink协议进行外部内存访问
  • 集成TLB虚拟地址转换功能

LocalAddr.scala (地址管理层)

  • 定义了LocalAddr Bundle,用于统一管理本地存储器地址
  • 支持暂存器(SP)和累加器(ACC)两种地址类型的区分和转换
  • 提供地址运算、比较和越界检查功能
  • 被上层MemLoader、MemStorer等模块广泛使用

数据流向

外部内存 ← TileLink ← BBStreamWriter ← MemStorer ← MemDomain ← 执行单元
外部内存 → TileLink → BBStreamReader → MemLoader → MemDomain → 执行单元
                            ↓
                    LocalAddr地址管理
                            ↓
                    暂存器/累加器寻址

模块说明

DMA.scala

主要功能: 提供基于TileLink协议的流式DMA数据传输实现

关键组件:

1. DMA请求响应接口定义

class BBReadRequest()(implicit p: Parameters) extends CoreBundle {
  val vaddr = UInt(coreMaxAddrBits.W)  // 虚拟地址
  val len = UInt(16.W)                 // 读取长度(字节)
  val status = new MStatus             // 处理器状态
}

class BBWriteRequest(dataWidth: Int)(implicit p: Parameters) extends CoreBundle {
  val vaddr = UInt(coreMaxAddrBits.W)      // 虚拟地址
  val data = UInt(dataWidth.W)             // 写入数据
  val len = UInt(16.W)                     // 写入长度(字节)
  val mask = UInt((dataWidth / 8).W)       // 字节掩码
  val status = new MStatus                 // 处理器状态
}

2. BBStreamReader核心逻辑

// 状态机定义
val s_idle :: s_req_new_block :: Nil = Enum(2)
val state = RegInit(s_idle)

// 字节计数管理
val bytesRequested = Reg(UInt(16.W))  // 已发出请求的字节数
val bytesReceived = Reg(UInt(16.W))   // 已接收响应的字节数
val bytesLeft = req.len - bytesRequested

// TileLink请求构造
val get = edge.Get(
  fromSource = xactId,
  toAddress = 0.U,
  lgSize = log2Ceil(beatBytes).U
)._2

3. TLB地址转换管道

class TLBundleAWithInfo extends Bundle {
  val tl_a = tl.a.bits.cloneType
  val vaddr = Output(UInt(vaddrBits.W))
  val status = Output(new MStatus)
}

// TLB请求处理
io.tlb.req.bits.tlb_req.vaddr := tlb_q.io.deq.bits.vaddr
io.tlb.req.bits.tlb_req.cmd := M_XRD  // 读操作
io.tlb.req.bits.status := tlb_q.io.deq.bits.status

4. BBStreamWriter写入控制

// 选择合适的Put操作类型
val use_put_full = req.mask === ~0.U(beatBytes.W)
val selected_put = Mux(use_put_full, putFull, putPartial)

// 响应处理
io.resp.valid := tl.d.valid && edge.last(tl.d)
io.resp.bits.done := true.B

输入输出:

  • 输入: 虚拟地址读写请求,包含地址、长度、数据、掩码
  • 输出: 数据响应流,带有last标志和地址计数器
  • 边缘情况: TLB缺失处理,事务ID管理,流水线背压

依赖项: TileLink协议,RocketChip TLB,Chisel3队列模块

LocalAddr.scala

主要功能: 统一管理暂存器和累加器的本地地址映射

关键组件:

1. 地址结构定义

class LocalAddr(sp_banks: Int, sp_bank_entries: Int, acc_banks: Int, acc_bank_entries: Int) extends Bundle {
  val is_acc_addr = Bool()         // 是否为累加器地址
  val accumulate = Bool()          // 是否执行累加操作
  val read_full_acc_row = Bool()   // 是否读取完整累加器行
  val data = UInt(memAddrBits.W)   // 实际地址数据
}

2. 地址分解函数

// 暂存器地址分解
def sp_bank(dummy: Int = 0) = if (spAddrBits == spBankRowBits) 0.U
                             else data(spAddrBits - 1, spBankRowBits)
def sp_row(dummy: Int = 0) = data(spBankRowBits - 1, 0)

// 累加器地址分解
def acc_bank(dummy: Int = 0) = if (accAddrBits == accBankRowBits) 0.U
                              else data(accAddrBits - 1, accBankRowBits)
def acc_row(dummy: Int = 0) = data(accBankRowBits - 1, 0)

3. 地址运算操作

// 地址加法运算
def +(other: UInt) = {
  val result = WireInit(this)
  result.data := data + other
  result
}

// 带溢出检查的加法
def add_with_overflow(other: UInt): Tuple2[LocalAddr, Bool] = {
  val sum = data +& other
  val overflow = Mux(is_acc_addr, sum(accAddrBits), sum(spAddrBits))
  (result, overflow)
}

4. 特殊地址处理

// 垃圾地址检查
def is_garbage(dummy: Int = 0) = is_acc_addr && accumulate && read_full_acc_row &&
                                data.andR && garbage_bit.asBool

// 生成垃圾地址
def make_this_garbage(dummy: Int = 0): Unit = {
  is_acc_addr := true.B
  accumulate := true.B
  read_full_acc_row := true.B
  data := ~(0.U(maxAddrBits.W))
}

输入输出:

  • 输入: Bank配置参数,地址数据
  • 输出: Bank索引、行索引、地址比较结果
  • 边缘情况: 地址溢出、下溢检查,垃圾地址处理

依赖项: Chisel3基础库,要求Bank entries为2的幂次

使用方法

注意事项

  1. 地址对齐要求: DMA操作需要考虑TileLink协议的对齐要求,BBStreamReader使用固定的beatBytes大小进行传输

  2. 事务ID管理: 两个DMA引擎都实现了事务ID的分配和回收机制,支持多个并发内存访问请求

  3. TLB集成: DMA引擎完全集成了虚拟地址转换功能,支持用户态和内核态的内存访问

  4. 流水线设计: 读写器都采用流水线设计,包含地址转换、TileLink请求、响应处理等多个阶段

  5. 错误处理: 实现了TLB缺失处理,但未包含重试机制,依赖上层软件处理访问失败

  6. 性能优化: BBStreamWriter支持完整写和部分写两种模式,根据掩码自动选择最优的TileLink操作类型

  7. 地址约束: LocalAddr要求Bank entries必须为2的幂次,这简化了地址计算但限制了配置灵活性

  8. 配置参数化: DMA引擎支持通过构造参数配置并发事务数、数据位宽、最大传输字节数等关键参数

TLB 模块 (Translation Lookaside Buffer)

概述

TLB 模块实现了虚拟地址到物理地址的转换缓存功能,位于 framework/builtin/memdomain/tlb 路径下。该模块基于 Rocket-chip 的 TLB 实现,提供了 BuckyBall 特定的 TLB 封装和集群管理功能。

文件结构

tlb/
├── BBTLB.scala           - BuckyBall TLB 实现
├── TLBCluster.scala      - TLB 集群管理器
├── spec-BBTLB.md         - BBTLB 规范文档
└── spec-BBTLBCluster.md  - TLB 集群规范文档

核心组件

BBTLB - BuckyBall TLB

BBTLB 是对 Rocket-chip TLB 的封装,提供 BuckyBall 特定的接口和异常处理:

class BBTLB(entries: Int, maxSize: Int)(implicit edge: TLEdgeOut, p: Parameters)
  extends CoreModule {

  val lgMaxSize = log2Ceil(maxSize)
  val io = IO(new Bundle {
    val req = Flipped(Valid(new BBTLBReq(lgMaxSize)))
    val resp = new TLBResp
    val ptw = new TLBPTWIO
    val exp = new BBTLBExceptionIO
  })
}

请求接口

class BBTLBReq(val lgMaxSize: Int)(implicit p: Parameters) extends CoreBundle {
  val tlb_req = new TLBReq(lgMaxSize)    // TLB 请求
  val status = new MStatus               // 处理器状态
}

异常处理接口

class BBTLBExceptionIO extends Bundle {
  val interrupt = Output(Bool())         // 中断输出
  val flush_retry = Input(Bool())        // 重试刷新
  val flush_skip = Input(Bool())         // 跳过刷新

  def flush(dummy: Int = 0): Bool = flush_retry || flush_skip
}

核心实现

BBTLB 内部实例化 Rocket-chip 的 TLB:

val tlb = Module(new TLB(false, lgMaxSize, TLBConfig(nSets=1, nWays=entries)))
tlb.io.req.valid := io.req.valid
tlb.io.req.bits := io.req.bits.tlb_req
io.resp := tlb.io.resp
tlb.io.kill := false.B

异常检测

val exception = io.req.valid && Mux(
  io.req.bits.tlb_req.cmd === M_XRD,
  tlb.io.resp.pf.ld || tlb.io.resp.ae.ld,
  tlb.io.resp.pf.st || tlb.io.resp.ae.st
)
when (exception) { interrupt := true.B }

BBTLBCluster - TLB 集群

BBTLBCluster 管理多个 TLB 实例,为多个客户端提供地址转换服务:

class BBTLBCluster(nClients: Int, entries: Int, maxSize: Int)
                 (implicit edge: TLEdgeOut, p: Parameters) extends CoreModule {

  val io = IO(new Bundle {
    val clients = Flipped(Vec(nClients, new BBTLBIO))
    val ptw = Vec(nClients, new TLBPTWIO)
    val exp = Vec(nClients, new BBTLBExceptionIO)
  })
}

客户端接口

class BBTLBIO(implicit p: Parameters) extends CoreBundle {
  val lgMaxSize = log2Ceil(coreDataBytes)
  val req = Valid(new BBTLBReq(lgMaxSize))    // TLB 请求
  val resp = Flipped(new TLBResp)             // TLB 响应
}

L0 TLB 缓存

每个客户端都有一个 L0 TLB 缓存,用于缓存最近的地址转换:

val last_translated_valid = RegInit(false.B)
val last_translated_vpn = RegInit(0.U(vaddrBits.W))
val last_translated_ppn = RegInit(0.U(paddrBits.W))

val l0_tlb_hit = last_translated_valid &&
  ((client.req.bits.tlb_req.vaddr >> pgIdxBits).asUInt ===
   (last_translated_vpn >> pgIdxBits).asUInt)

地址转换流程

  1. L0 缓存检查:首先检查 L0 TLB 缓存是否命中
  2. L1 TLB 查询:L0 缓存未命中时查询 L1 TLB
  3. 页表遍历:TLB 未命中时通过 PTW 进行页表遍历
  4. 结果缓存:成功转换后更新 L0 缓存
when (tlbReqFire && !tlb.io.resp.miss) {
  last_translated_valid := true.B
  last_translated_vpn := tlbReq.tlb_req.vaddr
  last_translated_ppn := tlb.io.resp.paddr
}

配置参数

TLB 配置

  • entries: TLB 条目数量
  • maxSize: 最大传输大小
  • nClients: 客户端数量

TLB 配置示例

val tlbConfig = TLBConfig(
  nSets = 1,           // TLB 组数
  nWays = 32           // 每组的路数
)

使用方法

创建单个 TLB

val bbtlb = Module(new BBTLB(entries = 32, maxSize = 64))

// 连接请求
bbtlb.io.req.valid := tlbReqValid
bbtlb.io.req.bits.tlb_req := tlbRequest
bbtlb.io.req.bits.status := processorStatus

// 获取响应
val tlbResp = bbtlb.io.resp
val physicalAddr = tlbResp.paddr
val tlbMiss = tlbResp.miss

// 连接页表遍历器
ptw <> bbtlb.io.ptw

// 处理异常
when(bbtlb.io.exp.interrupt) {
  // 处理 TLB 异常
}

创建 TLB 集群

val tlbCluster = Module(new BBTLBCluster(
  nClients = 4,
  entries = 32,
  maxSize = 64
))

// 连接客户端
for (i <- 0 until nClients) {
  tlbCluster.io.clients(i).req.valid := clientReqValid(i)
  tlbCluster.io.clients(i).req.bits := clientReq(i)
  clientResp(i) := tlbCluster.io.clients(i).resp
}

// 连接页表遍历器
for (i <- 0 until nClients) {
  ptw(i) <> tlbCluster.io.ptw(i)
}

地址转换过程

虚拟地址格式

Virtual Address (64-bit):
[63:39] [38:30] [29:21] [20:12] [11:0]
  VPN3    VPN2    VPN1    VPN0   Offset

物理地址格式

Physical Address:
[PPN][Offset]

转换流程

  1. 地址解析:解析虚拟地址的 VPN 和偏移量
  2. TLB 查找:在 TLB 中查找 VPN 对应的 PPN
  3. 页表遍历:TLB 未命中时通过 PTW 遍历页表
  4. 权限检查:检查访问权限和保护位
  5. 地址合成:将 PPN 和偏移量合成物理地址

异常处理

TLB 异常类型

  • 页错误 (Page Fault):访问无效页面
  • 访问异常 (Access Exception):权限不足
  • TLB 未命中 (TLB Miss):需要页表遍历

异常处理流程

val exception = io.req.valid && Mux(
  io.req.bits.tlb_req.cmd === M_XRD,
  tlb.io.resp.pf.ld || tlb.io.resp.ae.ld,    // 读异常
  tlb.io.resp.pf.st || tlb.io.resp.ae.st     // 写异常
)

TLB 刷新

tlb.io.sfence.valid := io.exp.flush()
tlb.io.sfence.bits.rs1 := false.B
tlb.io.sfence.bits.rs2 := false.B

性能优化

L0 TLB 缓存

  • 减少 L1 TLB 访问延迟
  • 提高地址转换吞吐量
  • 降低功耗消耗

并行处理

  • 多个客户端并行访问
  • 独立的页表遍历器
  • 分离的异常处理

调试和监控

性能计数器

  • TLB 命中率统计
  • 页表遍历次数
  • 异常发生频率

调试接口

  • TLB 条目状态查看
  • 地址转换跟踪
  • 异常信息记录

相关模块

保留站模块 (Reservation Station)

概述

保留站模块实现了内存域的指令调度和乱序执行管理,位于 framework/builtin/memdomain/rs 路径下。该模块包含保留站 (Reservation Station) 和重排序缓冲区 (ROB) 的实现,支持内存指令的发射、执行和完成管理。

文件结构

rs/
├── reservationStation.scala  - 内存保留站实现
├── rob.scala                 - 重排序缓冲区实现
└── ringFifo.scala           - 环形 FIFO 实现 (未使用)

核心组件

MemReservationStation - 内存保留站

内存保留站负责管理内存指令的调度和执行:

class MemReservationStation(implicit b: CustomBuckyBallConfig, p: Parameters) extends Module {
  val io = IO(new Bundle {
    val mem_decode_cmd_i = Flipped(new DecoupledIO(new MemDecodeCmd))
    val rs_rocc_o = new Bundle {
      val resp  = new DecoupledIO(new RoCCResponseBB()(p))
      val busy  = Output(Bool())
    }
    val issue_o     = new MemIssueInterface
    val commit_i    = new MemCommitInterface
  })
}

接口定义

输入接口

  • mem_decode_cmd_i: 来自内存域解码器的指令
  • commit_i: 来自内存加载器/存储器的完成信号

输出接口

  • issue_o: 向内存加载器/存储器发射指令
  • rs_rocc_o: 向 RoCC 接口返回响应

发射接口

class MemIssueInterface(implicit b: CustomBuckyBallConfig, p: Parameters) extends Bundle {
  val ld = Decoupled(new MemRsIssue)    // 加载指令发射
  val st = Decoupled(new MemRsIssue)    // 存储指令发射
}

完成接口

class MemCommitInterface(implicit b: CustomBuckyBallConfig, p: Parameters) extends Bundle {
  val ld = Flipped(Decoupled(new MemRsComplete))    // 加载指令完成
  val st = Flipped(Decoupled(new MemRsComplete))    // 存储指令完成
}

ROB - 重排序缓冲区

ROB 管理指令的顺序执行和乱序完成:

class ROB (implicit b: CustomBuckyBallConfig, p: Parameters) extends Module {
  val io = IO(new Bundle {
    val alloc = Flipped(new DecoupledIO(new MemDecodeCmd))
    val issue = new DecoupledIO(new RobEntry)
    val complete = Flipped(new DecoupledIO(UInt(log2Up(b.rob_entries).W)))
    val empty = Output(Bool())
    val full  = Output(Bool())
  })
}

ROB 条目

class RobEntry(implicit b: CustomBuckyBallConfig, p: Parameters) extends Bundle {
  val cmd    = new MemDecodeCmd                    // 内存指令
  val rob_id = UInt(log2Up(b.rob_entries).W)      // ROB 标识符
}

核心数据结构

val robFifo = Module(new Queue(new RobEntry, b.rob_entries))
val robIdCounter = RegInit(0.U(log2Up(b.rob_entries).W))
val robTable = Reg(Vec(b.rob_entries, Bool()))
  • robFifo: FIFO 队列,维护指令顺序
  • robIdCounter: ROB ID 计数器
  • robTable: 完成状态表,跟踪指令完成状态

工作流程

指令分配

  1. 接收指令:从内存域解码器接收 MemDecodeCmd
  2. 分配 ROB ID:为指令分配唯一的 ROB ID
  3. 入队操作:将指令和 ROB ID 存入 ROB FIFO
  4. 状态初始化:在完成状态表中标记为未完成
robFifo.io.enq.valid       := io.alloc.valid
robFifo.io.enq.bits.cmd    := io.alloc.bits
robFifo.io.enq.bits.rob_id := robIdCounter

when(io.alloc.fire) {
  robIdCounter := robIdCounter + 1.U
  robTable(robIdCounter) := false.B
}

指令发射

  1. 检查头部指令:检查 ROB 头部的未完成指令
  2. 类型分离:根据指令类型分离加载和存储操作
  3. 发射控制:只有在对应执行单元就绪时才发射
io.issue_o.ld.valid := rob.io.issue.valid && rob.io.issue.bits.cmd.is_load
io.issue_o.st.valid := rob.io.issue.valid && rob.io.issue.bits.cmd.is_store

rob.io.issue.ready  := (rob.io.issue.bits.cmd.is_load && io.issue_o.ld.ready) ||
                       (rob.io.issue.bits.cmd.is_store && io.issue_o.st.ready)

指令完成

  1. 完成仲裁:使用仲裁器处理多个完成信号
  2. 状态更新:在完成状态表中标记指令为已完成
  3. 乱序支持:支持指令乱序完成
val completeArb = Module(new Arbiter(UInt(log2Up(b.rob_entries).W), 2))
completeArb.io.in(0).valid  := io.commit_i.ld.valid
completeArb.io.in(1).valid  := io.commit_i.st.valid

when(io.complete.fire) {
  robTable(io.complete.bits) := true.B
}

头部指令管理

ROB 只发射头部的未完成指令:

val headEntry     = robFifo.io.deq.bits
val headCompleted = robTable(headEntry.rob_id)
io.issue.valid   := robFifo.io.deq.valid && !headCompleted
robFifo.io.deq.ready := io.issue.ready && !headCompleted

配置参数

ROB 配置

通过 CustomBuckyBallConfig 配置 ROB 参数:

class CustomBuckyBallConfig extends Config((site, here, up) => {
  case "rob_entries" => 16    // ROB 条目数量
})

使用示例

// 创建内存保留站
val memRS = Module(new MemReservationStation())

// 连接指令输入
memRS.io.mem_decode_cmd_i <> memDecoder.io.cmd_out

// 连接发射接口
memLoader.io.req <> memRS.io.issue_o.ld
memStorer.io.req <> memRS.io.issue_o.st

// 连接完成接口
memRS.io.commit_i.ld <> memLoader.io.resp
memRS.io.commit_i.st <> memStorer.io.resp

// 连接 RoCC 响应
rocc.io.resp <> memRS.io.rs_rocc_o.resp

执行模型

顺序发射,乱序完成

  • 顺序发射:指令按程序顺序从 ROB 头部发射
  • 乱序完成:指令可以乱序完成,通过 ROB ID 跟踪
  • 顺序提交:指令按程序顺序提交 (当前实现中简化)

内存一致性

  • 加载存储分离:加载和存储指令分别处理
  • 依赖检查:通过 ROB 维护内存访问顺序
  • 异常处理:支持内存访问异常的处理

状态监控

ROB 状态

val isEmpty = robTable.reduce(_ && _)    // 所有指令都已完成
val isFull = !robFifo.io.enq.ready      // ROB 已满

io.empty := isEmpty
io.full  := isFull

忙碌信号

io.rs_rocc_o.busy := !rob.io.empty      // ROB 非空时保留站忙碌

性能考虑

吞吐量优化

  • 支持加载和存储指令并行发射
  • 使用仲裁器处理多个完成信号
  • 最小化 ROB 查找延迟

资源利用

  • ROB 大小可配置,平衡性能和面积
  • 完成状态表使用位向量,节省存储
  • FIFO 队列提供高效的顺序管理

相关模块

BuckyBall 原型加速器

该目录包含了 BuckyBall 框架中各种专用计算加速器的原型实现,涵盖了机器学习、数值计算和数据处理等多个领域的硬件加速器设计。

目录结构

prototype/
├── format/      - 数据格式转换加速器
├── im2col/      - 图像到列转换加速器
├── matrix/      - 矩阵运算加速器
├── transpose/   - 矩阵转置加速器
└── vector/      - 向量处理单元

加速器组件

format/ - 数据格式处理

实现了各种数据格式转换和算术运算的硬件加速:

  • Arithmetic.scala: 定制化算术运算单元
  • Dataformat.scala: 数据格式转换和编码

应用场景:

  • 浮点数格式转换
  • 定点数运算优化
  • 数据压缩和解压缩

im2col/ - 图像处理加速

专门用于卷积神经网络中的 im2col 操作加速:

  • im2col.scala: 图像到列矩阵转换的硬件实现

应用场景:

  • CNN 卷积层加速
  • 图像预处理流水线
  • 特征提取优化

matrix/ - 矩阵运算引擎

矩阵运算加速器实现,包含多个模块:

  • bbfp_buffer.scala: 矩阵数据缓冲管理
  • bbfp_control.scala: 运算控制逻辑
  • bbfp_ex.scala: 执行单元实现
  • bbfp_load.scala: 数据加载单元
  • bbfp_pe.scala: 处理单元(PE)阵列
  • bbfpIns_decode.scala: 指令解码器

应用场景:

  • 深度学习训练和推理
  • 科学计算加速
  • 线性代数运算

transpose/ - 矩阵转置

高效的矩阵转置操作硬件实现:

  • Transpose.scala: 矩阵转置加速器

应用场景:

  • 矩阵运算预处理
  • 数据重排和转换
  • 内存访问模式优化

vector/ - 向量处理单元

向量处理架构,支持 SIMD 和多线程处理:

  • VecCtrlUnit.scala: 向量控制单元
  • VecEXUnit.scala: 向量执行单元
  • VecLoadUnit.scala: 向量加载单元
  • VecStoreUnit.scala: 向量存储单元
  • VecUnit.scala: 向量处理器顶层模块
  • bond/: 绑定和同步机制
  • op/: 向量操作实现
  • thread/: 多线程支持
  • warp/: 线程束管理

应用场景:

  • 并行数值计算
  • 信号处理加速
  • 高性能计算应用

设计特点

模块化设计

每个加速器都采用模块化设计,便于:

  • 独立开发和测试
  • 灵活组合和配置
  • 性能调优和扩展

流水线架构

大多数加速器采用深度流水线设计:

  • 提高吞吐量和频率
  • 支持连续数据流处理
  • 优化资源利用率

可配置参数

支持丰富的配置参数:

  • 数据位宽和精度
  • 并行度和流水线深度
  • 缓存大小和组织方式
  • 接口协议和时序

集成方式

RoCC 接口

所有加速器都通过标准的 RoCC 接口与 Rocket 核心集成:

class CustomAccelerator extends LazyRoCC {
  // 加速器实现
}

内存接口

支持多种内存访问模式:

  • DMA 批量传输
  • 缓存一致性访问
  • 暂存器直接访问

配置集成

通过 BuckyBall 配置系统进行参数化:

class AcceleratorConfig extends Config(
  new WithCustomAccelerator ++
  new BuckyBallConfig
)

性能优化

数据局部性

  • 优化数据访问模式
  • 减少内存带宽需求
  • 提高缓存命中率

并行处理

  • 多级并行设计
  • 流水线并行
  • 数据并行和任务并行

资源共享

  • 算术单元复用
  • 存储资源共享
  • 控制逻辑优化

验证和测试

每个加速器都配有相应的测试用例:

  • 功能正确性验证
  • 性能基准测试
  • 边界条件检查
  • 随机测试生成

扩展开发

  1. 新增加速器: 参考现有实现创建新的专用加速器
  2. 性能优化: 针对特定应用场景进行优化
  3. 接口扩展: 支持新的数据格式和协议
  4. 工具链集成: 开发编译器和软件支持

数据格式处理模块

概述

该目录实现了 BuckyBall 中的数据格式定义和算术运算抽象,提供统一的数据类型处理接口。位于 arch/src/main/scala/prototype/format 下,作为数据格式层,为其他原型加速器提供类型安全的数据格式支持。

实现的核心组件:

  • Dataformat.scala: 数据格式定义和工厂类
  • Arithmetic.scala: 算术运算类型类实现

代码结构

format/
├── Dataformat.scala  - 数据格式定义
└── Arithmetic.scala  - 算术运算抽象

文件依赖关系

Dataformat.scala (格式定义层)

  • 定义 DataFormat 抽象类和具体格式实现
  • 提供 DataFormatFactory 工厂类
  • 实现 DataFormatParams 参数类

Arithmetic.scala (运算抽象层)

  • 定义 Arithmetic 类型类接口
  • 实现 UIntArithmetic 具体运算
  • 提供 ArithmeticFactory 工厂类

模块说明

Dataformat.scala

主要功能: 定义支持的数据格式类型

格式定义:

abstract class DataFormat {
  def width: Int
  def dataType: Data
  def name: String
}

支持的格式:

class INT8Format extends DataFormat {
  override def width: Int = 8
  override def dataType: Data = UInt(8.W)
  override def name: String = "INT8"
}

class FP16Format extends DataFormat {
  override def width: Int = 16
  override def dataType: Data = UInt(16.W)
  override def name: String = "FP16"
}

class FP32Format extends DataFormat {
  override def width: Int = 32
  override def dataType: Data = UInt(32.W)
  override def name: String = "FP32"
}

工厂类:

object DataFormatFactory {
  def create(formatType: String): DataFormat = formatType.toUpperCase match {
    case "INT8" => new INT8Format
    case "FP16" => new FP16Format
    case "FP32" => new FP32Format
    case _ => throw new IllegalArgumentException(...)
  }
}

参数类:

case class DataFormatParams(formatType: String = "INT8") {
  def format: DataFormat = DataFormatFactory.create(formatType)
  def width: Int = format.width
  def dataType: Data = format.dataType
}

Arithmetic.scala

主要功能: 提供类型安全的算术运算抽象

类型类定义:

abstract class Arithmetic[T <: Data] {
  def add(x: T, y: T): T
  def sub(x: T, y: T): T
  def mul(x: T, y: T): T
  def div(x: T, y: T): T
  def gt(x: T, y: T): Bool
}

UInt 实现:

class UIntArithmetic extends Arithmetic[UInt] {
  override def add(x: UInt, y: UInt): UInt = x + y
  override def sub(x: UInt, y: UInt): UInt = x - y
  override def mul(x: UInt, y: UInt): UInt = x * y
  override def div(x: UInt, y: UInt): UInt = Mux(y =/= 0.U, x / y, 0.U)
  override def gt(x: UInt, y: UInt): Bool = x > y
}

工厂类:

object ArithmeticFactory {
  def createArithmetic[T <: Data](dataType: T): Arithmetic[T] = {
    dataType match {
      case _: UInt => new UIntArithmetic().asInstanceOf[Arithmetic[T]]
      case _ => throw new IllegalArgumentException(...)
    }
  }
}

使用方法

注意事项

  1. 浮点支持: FP16 和 FP32 目前使用 UInt 表示,后续可扩展为真正的浮点类型
  2. 除零保护: UInt 除法运算包含除零检查,返回 0 作为默认值
  3. 类型安全: 使用 Scala 类型系统确保运算的类型安全性
  4. 扩展性: 工厂模式支持添加新的数据格式和算术实现
  5. 参数化: DataFormatParams 提供便捷的参数化配置接口

Im2col 图像处理加速器

概述

该目录实现了 BuckyBall 的 Im2col 操作加速器,用于卷积神经网络中的图像到列矩阵转换。位于 arch/src/main/scala/prototype/im2col 下,作为图像处理加速器,将卷积操作转换为矩阵乘法操作以提高计算效率。

实现的核心组件:

  • im2col.scala: Im2col 加速器主体实现

代码结构

im2col/
└── im2col.scala  - Im2col 加速器实现

模块职责

Im2col.scala (加速器实现层)

  • 实现图像到列矩阵的转换逻辑
  • 管理 SRAM 读写操作
  • 提供 Ball 域命令接口

模块说明

im2col.scala

主要功能: 实现卷积窗口的滑动和数据重排

状态机定义:

val idle :: read :: read_and_convert :: complete :: Nil = Enum(4)
val state = RegInit(idle)

关键寄存器:

val ConvertBuffer = RegInit(VecInit(Seq.fill(4)(VecInit(Seq.fill(b.veclane)(0.U(b.inputType.getWidth.W))))))
val rowptr = RegInit(0.U(10.W))    // 卷积窗口左上角行指针
val colptr = RegInit(0.U(5.W))     // 卷积窗口左上角列指针
val krow_reg = RegInit(0.U(log2Up(b.veclane).W))  // 卷积核行数
val kcol_reg = RegInit(0.U(log2Up(b.veclane).W))  // 卷积核列数

命令解析:

when(io.cmdReq.fire) {
  rowptr := io.cmdReq.bits.cmd.special(37,28)      // 起始行
  colptr := io.cmdReq.bits.cmd.special(27,23)      // 起始列
  kcol_reg := io.cmdReq.bits.cmd.special(3,0)      // 卷积核列数
  krow_reg := io.cmdReq.bits.cmd.special(7,4)      // 卷积核行数
  incol_reg := io.cmdReq.bits.cmd.special(12,8)    // 输入矩阵列数
  inrow_reg := io.cmdReq.bits.cmd.special(22,13)   // 输入矩阵行数
}

数据转换逻辑:

// 填充窗口数据
for (i <- 0 until 4; j <- 0 until 4) {
  when(i.U < krow_reg && j.U < kcol_reg) {
    val bufferRow = (rowcnt + i.U) % krow_reg
    val bufferCol = (colptr + j.U) % incol_reg
    window((i.U * kcol_reg) + j.U) := ConvertBuffer(bufferRow)(bufferCol)
  }.otherwise {
    window((i.U * kcol_reg) + j.U) := 0.U
  }
}

SRAM 接口:

val io = IO(new Bundle {
  val cmdReq = Flipped(Decoupled(new BallRsIssue))
  val cmdResp = Decoupled(new BallRsComplete)
  val sramRead = Vec(b.sp_banks, Flipped(new SramReadIO(...)))
  val sramWrite = Vec(b.sp_banks, Flipped(new SramWriteIO(...)))
})

处理流程:

  1. idle: 等待命令,解析卷积参数
  2. read: 读取初始的卷积核大小的数据到缓冲区
  3. read_and_convert: 滑动窗口,转换数据并写回
  4. complete: 发送完成信号

输入输出:

  • 输入: Ball 域命令,包含卷积参数和地址信息
  • 输出: 转换后的列矩阵数据,完成信号
  • 边缘情况: 边界处理时填充零值

使用方法

算法原理

Im2col 转换: 将卷积操作转换为矩阵乘法

  • 输入: H×W 的图像,K×K 的卷积核
  • 输出: (H-K+1)×(W-K+1) 个 K×K 的窗口,展开为列向量

滑动窗口:

  • 按行优先顺序滑动卷积窗口
  • 每个窗口位置生成一个列向量
  • 使用循环缓冲区优化内存访问

注意事项

  1. 缓冲区管理: 使用 4×veclane 的转换缓冲区存储窗口数据
  2. 边界处理: 超出图像边界的位置填充零值
  3. 地址计算: 支持可配置的起始地址和 bank 选择
  4. 流水线优化: 在转换过程中提前发送下一行的读请求
  5. 参数限制: 最大支持 4×4 的卷积核大小

矩阵运算加速器

概述

该目录实现了 BuckyBall 的矩阵运算加速器,用于矩阵乘法和相关运算。位于 arch/src/main/scala/prototype/matrix 下,作为矩阵计算加速器,支持多种数据格式和运算模式。

实现的核心组件:

  • bbfp_control.scala: 矩阵运算控制器
  • bbfp_pe.scala: 处理单元(PE)和MAC单元
  • bbfp_buffer.scala: 数据缓冲管理
  • bbfp_load.scala: 数据加载单元
  • bbfp_ex.scala: 执行单元
  • bbfpIns_decode.scala: 指令解码器

代码结构

matrix/
├── bbfp_control.scala   - 控制器主体
├── bbfp_pe.scala        - 处理单元实现
├── bbfp_buffer.scala    - 缓冲管理
├── bbfp_load.scala      - 加载单元
├── bbfp_ex.scala        - 执行单元
└── bbfpIns_decode.scala - 指令解码

文件依赖关系

bbfp_control.scala (控制器层)

  • 集成各个子模块(ID, LU, EX等)
  • 管理 SRAM 和 Accumulator 接口
  • 处理 Ball 域命令

bbfp_pe.scala (计算核心层)

  • 实现 MacUnit 乘累加单元
  • 定义 PEControl 控制信号
  • 处理有符号/无符号运算

其他模块 (功能支持层)

  • 提供数据缓冲、加载、执行等支持功能

模块说明

bbfp_control.scala

主要功能: 矩阵运算加速器的顶层控制模块

模块集成:

class BBFP_Control extends Module {
  val BBFP_ID = Module(new BBFP_ID)
  val ID_LU = Module(new ID_LU)
  val BBFP_LoadUnit = Module(new BBFP_LoadUnit)
  val LU_EX = Module(new LU_EX)
}

接口定义:

val io = IO(new Bundle {
  val cmdReq = Flipped(Decoupled(new BallRsIssue))
  val cmdResp = Decoupled(new BallRsComplete)
  val is_matmul_ws = Input(Bool())
  val sramRead = Vec(b.sp_banks, Flipped(new SramReadIO(...)))
  val sramWrite = Vec(b.sp_banks, Flipped(new SramWriteIO(...)))
  val accRead = Vec(b.acc_banks, Flipped(new SramReadIO(...)))
  val accWrite = Vec(b.acc_banks, Flipped(new SramWriteIO(...)))
})

数据流向:

cmdReq → BBFP_ID → ID_LU → BBFP_LoadUnit → LU_EX
                              ↓
                         SRAM/ACC 接口

bbfp_pe.scala

主要功能: 实现矩阵运算的基本处理单元

MAC 单元定义:

class MacUnit extends Module {
  val io = IO(new Bundle {
    val in_a = Input(UInt(7.W))    // [6]=sign, [5]=flag, [4:0]=value
    val in_b = Input(UInt(7.W))    // [6]=sign, [5]=flag, [4:0]=value
    val in_c = Input(UInt(32.W))   // [31]=sign, [30:0]=value
    val out_d = Output(UInt(32.W)) // 输出结果
  })
}

数据格式处理:

// 提取符号位和数值
val sign_a = io.in_a(6)
val sign_b = io.in_b(6)
val flag_a = io.in_a(5)
val flag_b = io.in_b(5)
val value_a = io.in_a(4, 0)
val value_b = io.in_b(4, 0)

// 根据flag位决定是否左移
val shifted_a = Mux(flag_a === 1.U, value_a << 2, value_a)
val shifted_b = Mux(flag_b === 1.U, value_b << 2, value_b)

有符号运算:

val a_signed = Mux(sign_a === 1.U, -(shifted_a.zext), shifted_a.zext).asSInt
val b_signed = Mux(sign_b === 1.U, -(shifted_b.zext), shifted_b.zext).asSInt

控制信号:

class PEControl extends Bundle {
  val propagate = UInt(1.W)   // 传播控制
}

使用方法

数据格式

输入格式: 7位压缩格式

  • bit[6]: 符号位 (0=正数, 1=负数)
  • bit[5]: 标志位 (1=左移2位)
  • bit[4:0]: 5位数值

输出格式: 32位有符号数

  • bit[31]: 符号位
  • bit[30:0]: 31位数值

运算特性

MAC 运算: 乘累加操作 (Multiply-Accumulate)

  • 支持有符号和无符号运算
  • 可配置的位移操作
  • 32位累加器输出

流水线结构:

  • ID: 指令解码阶段
  • LU: 加载单元阶段
  • EX: 执行单元阶段

注意事项

  1. 数据格式: 使用自定义的7位压缩格式减少存储开销
  2. 符号处理: 支持有符号数的正确运算和符号扩展
  3. 位移优化: 通过flag位控制数据的预处理位移
  4. 接口兼容: 与 SRAM 和 Accumulator 接口完全兼容
  5. 流水线设计: 多级流水线提高吞吐量

矩阵转置加速器

概述

该目录实现了 BuckyBall 的矩阵转置加速器,用于矩阵转置操作。位于 arch/src/main/scala/prototype/transpose 下,作为矩阵转置加速器,支持流水线化的转置操作。

实现的核心组件:

  • Transpose.scala: 流水线化转置器实现

代码结构

transpose/
└── Transpose.scala  - 流水线转置器

模块职责

Transpose.scala (转置实现层)

  • 实现 PipelinedTransposer 模块
  • 管理矩阵数据的读取、转置和写回
  • 提供 Ball 域命令接口

模块说明

Transpose.scala

主要功能: 实现流水线化的矩阵转置操作

状态机定义:

val idle :: sRead :: sWrite :: complete :: Nil = Enum(4)
val state = RegInit(idle)

存储结构:

// 矩阵存储寄存器 (veclane x veclane)
val regArray = Reg(Vec(b.veclane, Vec(b.veclane, UInt(b.inputType.getWidth.W))))

计数器管理:

val readCounter = RegInit(0.U(log2Ceil(b.veclane + 1).W))
val respCounter = RegInit(0.U(log2Ceil(b.veclane + 1).W))
val writeCounter = RegInit(0.U(log2Ceil(b.veclane + 1).W))

指令寄存器:

val robid_reg = RegInit(0.U(10.W))    // ROB ID
val waddr_reg = RegInit(0.U(10.W))    // 写地址
val wbank_reg = RegInit(0.U(log2Up(b.sp_banks).W))  // 写bank
val raddr_reg = RegInit(0.U(10.W))    // 读地址
val rbank_reg = RegInit(0.U(log2Up(b.sp_banks).W))  // 读bank
val iter_reg = RegInit(0.U(10.W))     // 迭代计数

接口定义:

val io = IO(new Bundle {
  val cmdReq = Flipped(Decoupled(new BallRsIssue))
  val cmdResp = Decoupled(new BallRsComplete)
  val sramRead = Vec(b.sp_banks, Flipped(new SramReadIO(...)))
  val sramWrite = Vec(b.sp_banks, Flipped(new SramWriteIO(...)))
})

处理流程:

  1. idle: 等待命令,解析转置参数
  2. sRead: 按行读取矩阵数据到寄存器阵列
  3. sWrite: 按列写回转置后的数据
  4. complete: 发送完成信号

转置算法:

  • 使用 veclane×veclane 的寄存器阵列存储矩阵
  • 按行读取,按列写回实现转置
  • 支持任意大小矩阵的分块转置

使用方法

实现细节

状态机:

val idle :: sRead :: sWrite :: complete :: Nil = Enum(4)
  • idle: 等待指令
  • sRead: 读取矩阵数据
  • sWrite: 写入转置结果
  • complete: 完成并响应

寄存器阵列:

val regArray = Reg(Vec(b.veclane, Vec(b.veclane, UInt(b.inputType.getWidth.W))))

使用 veclane×veclane 的寄存器阵列缓存矩阵数据。

转置操作:

  • 读取阶段:按行读取数据存入 regArray(row)(col)
  • 写入阶段:按列读取 regArray(i)(col) 组成新行写出

配置参数

矩阵大小: 由 b.veclane 参数决定 数据位宽: 由 b.inputType.getWidth 决定 Bank 配置: 支持多 bank SRAM 访问

注意事项

  1. 矩阵大小限制: 最大支持 veclane×veclane 的矩阵
  2. 内存带宽: 转置操作对内存带宽要求较高
  3. 寄存器开销: 需要 veclane² 个寄存器存储矩阵
  4. 地址计算: 转置后的地址计算需要正确处理
  5. 流水线控制: 读写计数器需要正确同步

向量处理单元 (Vector Processing Unit)

概述

向量处理单元是 BuckyBall 框架中的专用计算加速器,位于 prototype/vector 路径下。该模块实现了完整的向量处理流水线,包括控制单元、加载单元、执行单元和存储单元,支持向量数据的并行处理。

文件结构

vector/
├── VecUnit.scala         - 向量处理单元顶层模块
├── VecCtrlUnit.scala     - 向量控制单元
├── VecLoadUnit.scala     - 向量加载单元
├── VecEXUnit.scala       - 向量执行单元
├── VecStoreUnit.scala    - 向量存储单元
├── bond/                 - 绑定和同步机制
├── op/                   - 向量操作实现
├── thread/               - 线程管理
└── warp/                 - 线程束管理

核心组件

VecUnit - 向量处理单元顶层

VecUnit 是向量处理器的顶层模块,集成了所有子单元:

class VecUnit(implicit b: CustomBuckyBallConfig, p: Parameters) extends Module {
  val io = IO(new Bundle {
    val cmdReq = Flipped(Decoupled(new BallRsIssue))
    val cmdResp = Decoupled(new BallRsComplete)

    // 连接到Scratchpad的SRAM读写接口
    val sramRead = Vec(b.sp_banks, Flipped(new SramReadIO(b.spad_bank_entries, spad_w)))
    val sramWrite = Vec(b.sp_banks, Flipped(new SramWriteIO(b.spad_bank_entries, spad_w, b.spad_mask_len)))
    // 连接到Accumulator的读写接口
    val accRead = Vec(b.acc_banks, Flipped(new SramReadIO(b.acc_bank_entries, b.acc_w)))
    val accWrite = Vec(b.acc_banks, Flipped(new SramWriteIO(b.acc_bank_entries, b.acc_w, b.acc_mask_len)))
  })
}

接口说明

命令接口

  • cmdReq: 来自保留站的向量指令请求
  • cmdResp: 向保留站返回的完成响应

存储接口

  • sramRead/sramWrite: 连接到 Scratchpad 的读写接口
  • accRead/accWrite: 连接到 Accumulator 的读写接口

VecCtrlUnit - 向量控制单元

向量控制单元负责指令解码和流水线控制:

class VecCtrlUnit(implicit b: CustomBuckyBallConfig, p: Parameters) extends Module {
  val io = IO(new Bundle{
    val cmdReq = Flipped(Decoupled(new BallRsIssue))
    val cmdResp_o = Decoupled(new BallRsComplete)

    val ctrl_ld_o = Decoupled(new ctrl_ld_req)
    val ctrl_st_o = Decoupled(new ctrl_st_req)
    val ctrl_ex_o = Decoupled(new ctrl_ex_req)

    val cmdResp_i = Flipped(Valid(new Bundle {val commit = Bool()}))
  })
}

控制状态

val rob_id_reg    = RegInit(0.U(log2Up(b.rob_entries).W))
val iter          = RegInit(0.U(10.W))
val op1_bank      = RegInit(0.U(2.W))
val op1_bank_addr = RegInit(0.U(12.W))
val op2_bank_addr = RegInit(0.U(12.W))
val op2_bank      = RegInit(0.U(2.W))
val wr_bank       = RegInit(0.U(2.W))
val wr_bank_addr  = RegInit(0.U(12.W))
val is_acc        = RegInit(false.B)

数据流架构

向量处理单元采用流水线架构,数据流如下:

指令输入 → VecCtrlUnit → 控制信号分发
                ↓
        VecLoadUnit (加载数据)
                ↓
        VecEXUnit (执行计算)
                ↓
        VecStoreUnit (存储结果)
                ↓
            完成响应

模块连接

// 控制单元
val VecCtrlUnit = Module(new VecCtrlUnit)
VecCtrlUnit.io.cmdReq <> io.cmdReq
io.cmdResp <> VecCtrlUnit.io.cmdResp_o

// 加载单元
val VecLoadUnit = Module(new VecLoadUnit)
VecLoadUnit.io.ctrl_ld_i <> VecCtrlUnit.io.ctrl_ld_o

// 执行单元
val VecEX = Module(new VecEXUnit)
VecEX.io.ctrl_ex_i <> VecCtrlUnit.io.ctrl_ex_o
VecEX.io.ld_ex_i <> VecLoadUnit.io.ld_ex_o

// 存储单元
val VecStoreUnit = Module(new VecStoreUnit)
VecStoreUnit.io.ctrl_st_i <> VecCtrlUnit.io.ctrl_st_o
VecStoreUnit.io.ex_st_i <> VecEX.io.ex_st_o

存储系统集成

Scratchpad 连接

向量处理单元通过多个 Bank 连接到 Scratchpad:

for (i <- 0 until b.sp_banks) {
  io.sramRead(i).req <> VecLoadUnit.io.sramReadReq(i)
  VecLoadUnit.io.sramReadResp(i) <> io.sramRead(i).resp
}

Accumulator 连接

执行结果通过存储单元写入 Accumulator:

for (i <- 0 until b.acc_banks) {
  io.accWrite(i) <> VecStoreUnit.io.accWrite(i)
}

配置参数

向量配置

通过 CustomBuckyBallConfig 配置向量处理器参数:

class CustomBuckyBallConfig extends Config((site, here, up) => {
  case "veclane" => 16              // 向量通道数
  case "sp_banks" => 4              // Scratchpad Bank 数
  case "acc_banks" => 2             // Accumulator Bank 数
  case "spad_bank_entries" => 1024  // 每个 Bank 的条目数
  case "acc_bank_entries" => 512    // Accumulator 条目数
})

数据位宽

val spad_w = b.veclane * b.inputType.getWidth  // Scratchpad 位宽
val acc_w = b.outputType.getWidth              // Accumulator 位宽

使用方法

创建向量处理单元

val vecUnit = Module(new VecUnit())

// 连接命令接口
vecUnit.io.cmdReq <> reservationStation.io.issue
reservationStation.io.complete <> vecUnit.io.cmdResp

// 连接存储系统
for (i <- 0 until sp_banks) {
  scratchpad.io.read(i) <> vecUnit.io.sramRead(i)
  scratchpad.io.write(i) <> vecUnit.io.sramWrite(i)
}

for (i <- 0 until acc_banks) {
  accumulator.io.read(i) <> vecUnit.io.accRead(i)
  accumulator.io.write(i) <> vecUnit.io.accWrite(i)
}

向量指令格式

向量指令通过 BallRsIssue 接口传递:

class BallRsIssue extends Bundle {
  val cmd = new Bundle {
    val iter = UInt(10.W)           // 迭代次数
    val op1_bank = UInt(2.W)        // 操作数1的Bank
    val op1_bank_addr = UInt(12.W)  // 操作数1的地址
    val op2_bank = UInt(2.W)        // 操作数2的Bank
    val op2_bank_addr = UInt(12.W)  // 操作数2的地址
    val wr_bank = UInt(2.W)         // 写入Bank
    val wr_bank_addr = UInt(12.W)   // 写入地址
  }
  val rob_id = UInt(log2Up(rob_entries).W)
}

执行模型

流水线执行

  1. 指令解码:VecCtrlUnit 解码向量指令
  2. 数据加载:VecLoadUnit 从 Scratchpad 加载操作数
  3. 向量计算:VecEXUnit 执行向量运算
  4. 结果存储:VecStoreUnit 将结果写入 Accumulator
  5. 完成响应:向保留站返回完成信号

并行处理

  • 多通道并行:支持多个向量通道并行计算
  • Bank 级并行:多个存储 Bank 支持并行访问
  • 流水线重叠:不同阶段可以重叠执行

子模块说明

绑定机制 (Bond)

提供线程间的同步和数据绑定功能,支持生产者-消费者模式的数据传递。

向量操作 (Op)

实现具体的向量计算操作,包括算术运算、逻辑运算和特殊函数。

线程管理 (Thread)

提供线程抽象和管理功能,支持不同类型的向量线程。

线程束管理 (Warp)

实现线程束的组织和调度,支持大规模并行计算。

性能特性

  • 高并行度:支持多通道向量并行处理
  • 流水线化:多级流水线提高吞吐量
  • 存储优化:多 Bank 存储系统减少访问冲突
  • 灵活配置:支持不同的向量长度和数据类型

相关模块

绑定模块 (Bond)

概述

绑定模块实现了向量处理单元中的数据接口和同步机制,位于 prototype/vector/bond 路径下。该模块定义了线程间的数据传递接口,支持不同类型的数据绑定模式。

文件结构

bond/
├── BondWrapper.scala    - 绑定包装器基类
└── vvv.scala           - VVV 绑定实现

核心组件

VVV - 向量到向量绑定

VVV (Vector-Vector-Vector) 绑定实现了双输入向量到单输出向量的数据接口:

class VVV(implicit p: Parameters) extends Bundle {
  val lane = p(ThreadKey).get.lane
  val bondParam = p(ThreadBondKey).get
  val inputWidth = bondParam.inputWidth
  val outputWidth = bondParam.outputWidth

  // Input interface (Flipped Decoupled)
  val in = Flipped(Decoupled(new Bundle {
    val in1 = Vec(lane, UInt(inputWidth.W))
    val in2 = Vec(lane, UInt(inputWidth.W))
  }))

  // Decoupled output interface
  val out = Decoupled(new Bundle {
    val out = Vec(lane, UInt(outputWidth.W))
  })
}

接口说明

输入接口

  • in.bits.in1: 第一个输入向量,位宽为 inputWidth
  • in.bits.in2: 第二个输入向量,位宽为 inputWidth
  • in.valid: 输入数据有效信号
  • in.ready: 输入就绪信号

输出接口

  • out.bits.out: 输出向量,位宽为 outputWidth
  • out.valid: 输出数据有效信号
  • out.ready: 输出就绪信号

参数配置

VVV 绑定的参数通过配置系统获取:

val lane = p(ThreadKey).get.lane                    // 向量通道数
val bondParam = p(ThreadBondKey).get                // 绑定参数
val inputWidth = bondParam.inputWidth               // 输入位宽
val outputWidth = bondParam.outputWidth             // 输出位宽

CanHaveVVVBond - VVV 绑定特质

CanHaveVVVBond 特质为线程提供 VVV 绑定功能:

trait CanHaveVVVBond { this: BaseThread =>
  val vvvBond = params(ThreadBondKey).filter(_.bondType == "vvv").map { bondParam =>
    IO(new VVV()(params))
  }

  def getVVVBond = vvvBond
}

使用方式

线程类通过混入该特质获得 VVV 绑定能力:

class MulThread(implicit p: Parameters) extends BaseThread
  with CanHaveMulOp
  with CanHaveVVVBond {

  // 连接操作和绑定
  for {
    op <- mulOp
    bond <- vvvBond
  } {
    op.io.in <> bond.in
    op.io.out <> bond.out
  }
}

BondWrapper - 绑定包装器

BondWrapper 提供了基于 Diplomacy 的绑定封装:

abstract class BondWrapper(implicit p: Parameters) extends LazyModule {
  val bondName = "vvv"

  def to[T](name: String)(body: => T): T = {
    LazyScope(s"bond_to_${name}", s"Bond_${bondName}_to_${name}") { body }
  }

  def from[T](name: String)(body: => T): T = {
    LazyScope(s"bond_from_${name}", s"Bond_${bondName}_from_${name}") { body }
  }
}

作用域管理

BondWrapper 提供了命名作用域管理功能:

  • to(): 创建输出方向的绑定作用域
  • from(): 创建输入方向的绑定作用域

绑定类型

VVV 绑定模式

VVV 绑定支持以下数据流模式:

  1. 双输入单输出:两个向量输入,一个向量输出
  2. 位宽转换:支持输入和输出位宽不同
  3. 向量并行:支持多通道并行数据传输

数据流控制

VVV 绑定使用 Decoupled 接口进行流控:

// 生产者端
producer.io.out.valid := dataReady
producer.io.out.bits.in1 := inputVector1
producer.io.out.bits.in2 := inputVector2

// 消费者端
consumer.io.in.ready := canAcceptData
when(consumer.io.in.fire) {
  processData(consumer.io.in.bits.out)
}

配置参数

绑定参数

绑定参数通过 BondParam 定义:

case class BondParam(
  bondType: String,           // 绑定类型 ("vvv")
  inputWidth: Int = 8,        // 输入位宽
  outputWidth: Int = 32       // 输出位宽
)

配置示例

val bondConfig = BondParam(
  bondType = "vvv",
  inputWidth = 8,
  outputWidth = 32
)

val threadConfig = ThreadParam(
  lane = 16,
  attr = "vector",
  threadName = "mul_thread",
  Op = OpParam("mul", bondConfig)
)

使用方法

创建 VVV 绑定

// 在线程中使用 VVV 绑定
class CustomThread(implicit p: Parameters) extends BaseThread
  with CanHaveVVVBond {

  // 获取绑定接口
  for (bond <- vvvBond) {
    // 连接输入
    bond.in.valid := inputValid
    bond.in.bits.in1 := inputVector1
    bond.in.bits.in2 := inputVector2

    // 连接输出
    outputValid := bond.out.valid
    outputVector := bond.out.bits.out
    bond.out.ready := outputReady
  }
}

绑定连接

// 连接两个模块的绑定接口
val producer = Module(new ProducerThread())
val consumer = Module(new ConsumerThread())

// 直接连接绑定接口
for {
  prodBond <- producer.vvvBond
  consBond <- consumer.vvvBond
} {
  consBond.in <> prodBond.out
}

同步机制

握手协议

VVV 绑定使用标准的 Decoupled 握手协议:

  1. 数据准备:生产者设置 validbits
  2. 接收就绪:消费者设置 ready
  3. 数据传输:当 valid && ready 时完成传输
  4. 状态更新:双方更新内部状态

背压处理

绑定接口支持背压机制:

// 当下游未就绪时,上游会等待
when(!downstream.ready) {
  upstream.valid := false.B
  // 保持数据不变
}

扩展性

新绑定类型

可以通过类似的模式定义新的绑定类型:

// 单输入单输出绑定
class VV(implicit p: Parameters) extends Bundle {
  val in = Flipped(Decoupled(Vec(lane, UInt(inputWidth.W))))
  val out = Decoupled(Vec(lane, UInt(outputWidth.W)))
}

// 对应的特质
trait CanHaveVVBond { this: BaseThread =>
  val vvBond = params(ThreadBondKey).filter(_.bondType == "vv").map { _ =>
    IO(new VV()(params))
  }
}

参数化支持

绑定模块支持完全参数化配置:

  • 向量通道数可配置
  • 输入输出位宽可配置
  • 绑定类型可扩展

相关模块

向量操作模块 (Vector Operations)

概述

向量操作模块实现了向量处理单元中的具体计算操作,位于 prototype/vector/op 路径下。该模块提供了不同类型的向量运算实现,包括乘法操作和级联操作。

文件结构

op/
├── cascade.scala    - 级联加法操作
└── mul.scala       - 乘法操作

核心组件

CascadeOp - 级联加法操作

CascadeOp 实现向量元素的逐元素加法操作:

class CascadeOp(implicit p: Parameters) extends Module {
  val lane = p(ThreadKey).get.lane
  val bondParam = p(ThreadBondKey).get
  val outputWidth = bondParam.outputWidth

  val io = IO(new VVV()(p))
}

操作逻辑

val reg1 = RegInit(VecInit(Seq.fill(lane)(0.U(outputWidth.W))))
val valid1 = RegInit(false.B)

when (io.in.valid) {
  valid1 := true.B
  reg1 := io.in.bits.in1.zip(io.in.bits.in2).map { case (a, b) => a + b }
}

功能说明

  • 接收两个输入向量 in1in2
  • 执行逐元素加法:out[i] = in1[i] + in2[i]
  • 使用寄存器缓存计算结果
  • 支持流水线操作

流控机制

io.in.ready := io.out.ready

when (io.out.ready && valid) {
  io.out.valid := true.B
  io.out.bits.out := reg1
}.otherwise {
  io.out.valid := false.B
  io.out.bits.out := VecInit(Seq.fill(lane)(0.U(outputWidth.W)))
}

MulOp - 乘法操作

MulOp 实现向量乘法操作,支持广播模式:

class MulOp(implicit p: Parameters) extends Module {
  val lane = p(ThreadKey).get.lane
  val bondParam = p(ThreadBondKey).get
  val inputWidth = bondParam.inputWidth

  val io = IO(new VVV()(p))
}

操作逻辑

val reg1 = RegInit(VecInit(Seq.fill(lane)(0.U(inputWidth.W))))
val reg2 = RegInit(VecInit(Seq.fill(lane)(0.U(inputWidth.W))))
val cnt = RegInit(0.U(log2Ceil(lane).W))
val active = RegInit(false.B)

when (io.in.valid) {
  reg1 := io.in.bits.in1
  reg2 := io.in.bits.in2
  cnt := 0.U
  active := true.B
}

功能说明

  • 接收两个输入向量并缓存到寄存器
  • 使用计数器 cnt 控制输出序列
  • 实现广播乘法:out[i] = reg1[cnt] * reg2[i]

序列输出

for (i <- 0 until lane) {
  io.out.bits.out(i) := reg1(cnt) * reg2(i)
}

when (active && io.out.ready) {
  cnt := cnt + 1.U
  when (cnt === (lane-1).U) {
    active := false.B
  }
}

输出模式

  • 每个周期输出一组乘法结果
  • reg1[cnt]reg2 的所有元素相乘
  • 计数器递增,实现序列输出

操作特质

CanHaveCascadeOp - 级联操作特质

trait CanHaveCascadeOp { this: BaseThread =>
  val cascadeOp = params(ThreadOpKey).filter(_.OpType == "cascade").map { opParam =>
    Module(new CascadeOp()(params))
  }

  def getCascadeOp = cascadeOp
}

CanHaveMulOp - 乘法操作特质

trait CanHaveMulOp { this: BaseThread =>
  val mulOp = params(ThreadOpKey).filter(_.OpType == "mul").map { opParam =>
    Module(new MulOp()(params))
  }

  def getMulOp = mulOp
}

使用方法

在线程中使用操作

class CasThread(implicit p: Parameters) extends BaseThread
  with CanHaveCascadeOp
  with CanHaveVVVBond {

  // 连接操作和绑定
  for {
    op <- cascadeOp
    bond <- vvvBond
  } {
    op.io.in <> bond.in
    op.io.out <> bond.out
  }
}

配置操作参数

val opParam = OpParam(
  OpType = "cascade",                    // 操作类型
  bondType = BondParam(
    bondType = "vvv",
    inputWidth = 32,
    outputWidth = 32
  )
)

操作类型对比

CascadeOp vs MulOp

特性CascadeOpMulOp
操作类型逐元素加法广播乘法
输入位宽任意通常较小
输出位宽任意通常较大
延迟1 周期lane 周期
吞吐量每周期 1 组每 lane 周期 1 组
资源消耗加法器 × lane乘法器 × lane

应用场景

CascadeOp 适用于

  • 向量加法运算
  • 累加操作
  • 数据合并

MulOp 适用于

  • 矩阵向量乘法
  • 卷积运算
  • 缩放操作

数据流模式

CascadeOp 数据流

输入: [a0, a1, ..., an], [b0, b1, ..., bn]
      ↓
计算: [a0+b0, a1+b1, ..., an+bn]
      ↓
输出: [c0, c1, ..., cn] (1 周期)

MulOp 数据流

输入: [a0, a1, ..., an], [b0, b1, ..., bn]
      ↓
周期0: [a0*b0, a0*b1, ..., a0*bn]
周期1: [a1*b0, a1*b1, ..., a1*bn]
...
周期n: [an*b0, an*b1, ..., an*bn]

扩展操作

添加新操作

可以通过类似的模式添加新的向量操作:

class SubOp(implicit p: Parameters) extends Module {
  val io = IO(new VVV()(p))

  // 实现减法操作
  io.out.bits.out := io.in.bits.in1.zip(io.in.bits.in2).map {
    case (a, b) => a - b
  }
}

trait CanHaveSubOp { this: BaseThread =>
  val subOp = params(ThreadOpKey).filter(_.OpType == "sub").map { _ =>
    Module(new SubOp()(params))
  }
}

复杂操作

对于更复杂的操作,可以组合多个基础操作:

class FMAOp(implicit p: Parameters) extends Module {
  // 融合乘加操作: out = a * b + c
  val mulOp = Module(new MulOp())
  val addOp = Module(new CascadeOp())

  // 连接操作流水线
  addOp.io.in.bits.in1 <> mulOp.io.out.bits.out
  // ...
}

性能优化

流水线优化

  • 使用寄存器缓存中间结果
  • 支持连续数据流处理
  • 最小化组合逻辑延迟

资源优化

  • 根据操作类型选择合适的硬件资源
  • 支持资源共享和复用
  • 可配置的并行度

相关模块

线程模块 (Thread)

概述

线程模块实现了向量处理单元中的线程抽象,位于 prototype/vector/thread 路径下。该模块定义了线程的基本结构和具体实现,通过组合不同的操作 (Op) 和绑定 (Bond) 来构建特定功能的线程。

文件结构

thread/
├── BaseThread.scala    - 线程基类定义
├── CasThread.scala     - 级联操作线程
└── MulThread.scala     - 乘法操作线程

核心组件

BaseThread - 线程基类

BaseThread 是所有线程的基类,定义了线程的基本参数和配置:

class BaseThread(implicit p: Parameters) extends Module {
  val io = IO(new Bundle {})
  val params = p
  val threadMap = p(ThreadMapKey)
  val threadParam = threadMap.getOrElse(
    p(ThreadKey).get.threadName,
    throw new Exception(s"ThreadParam not found for threadName: ${p(ThreadKey).get.threadName}")
  )
  val opParam = p(ThreadOpKey).get
  val bondParam = p(ThreadBondKey).get
}

参数定义

线程模块使用以下参数结构:

case class ThreadParam(lane: Int, attr: String, threadName: String, Op: OpParam)
case class OpParam(OpType: String, bondType: BondParam)
case class BondParam(bondType: String, inputWidth: Int = 8, outputWidth: Int = 32)

参数说明:

  • lane: 向量通道数量
  • threadName: 线程名称标识
  • OpType: 操作类型 ("cascade", "mul")
  • bondType: 绑定类型 ("vvv")
  • inputWidth: 输入数据位宽,默认 8 位
  • outputWidth: 输出数据位宽,默认 32 位

具体线程实现

CasThread - 级联操作线程

CasThread 实现级联加法操作,组合了 CascadeOp 和 VVVBond:

class CasThread(implicit p: Parameters) extends BaseThread
  with CanHaveCascadeOp
  with CanHaveVVVBond {

  // 连接CascadeOp和VVVBond
  for {
    op <- cascadeOp
    bond <- vvvBond
  } {
    op.io.in <> bond.in
    op.io.out <> bond.out
  }
}

功能:对两个输入向量执行逐元素加法操作。

MulThread - 乘法操作线程

MulThread 实现乘法操作,组合了 MulOp 和 VVVBond:

class MulThread(implicit p: Parameters) extends BaseThread
  with CanHaveMulOp
  with CanHaveVVVBond {

  // 连接MulOp和VVVBond
  for {
    op <- mulOp
    bond <- vvvBond
  } {
    op.io.in <> bond.in
    op.io.out <> bond.out
  }
}

功能:实现向量乘法操作,支持逐周期输出结果。

配置系统

线程模块使用 Chipyard 的配置系统进行参数化:

case object ThreadKey extends Field[Option[ThreadParam]](None)
case object ThreadOpKey extends Field[Option[OpParam]](None)
case object ThreadBondKey extends Field[Option[BondParam]](None)
case object ThreadMapKey extends Field[Map[String, ThreadParam]](Map.empty)

配置键说明:

  • ThreadKey: 当前线程参数
  • ThreadOpKey: 操作参数
  • ThreadBondKey: 绑定参数
  • ThreadMapKey: 线程映射表

使用方法

创建线程实例

// 配置参数
val threadParam = ThreadParam(
  lane = 4,
  attr = "vector",
  threadName = "mul_thread",
  Op = OpParam("mul", BondParam("vvv", 8, 32))
)

// 创建线程
val mulThread = Module(new MulThread()(
  new Config((site, here, up) => {
    case ThreadKey => Some(threadParam)
    case ThreadOpKey => Some(threadParam.Op)
    case ThreadBondKey => Some(threadParam.Op.bondType)
  })
))

连接接口

线程通过 VVV 绑定接口进行数据交互:

// 输入数据
mulThread.io.in.valid := inputValid
mulThread.io.in.bits.in1 := inputVector1
mulThread.io.in.bits.in2 := inputVector2

// 输出数据
outputValid := mulThread.io.out.valid
outputVector := mulThread.io.out.bits.out
mulThread.io.out.ready := outputReady

相关模块

线程束模块 (Warp)

概述

线程束模块实现了向量处理单元中的线程束管理功能,位于 prototype/vector/warp 路径下。该模块将多个线程组织成网格结构,实现并行计算和数据流管理。

文件结构

warp/
├── MeshWarp.scala    - 网格线程束实现
└── VecBall.scala     - 向量球形处理器

核心组件

MeshWarp - 网格线程束

MeshWarp 实现了一个 32 线程的网格结构,包含 16 个乘法线程和 16 个级联线程:

class MeshWarp(implicit p: Parameters) extends Module {
  val io = IO(new Bundle {
    val in = Flipped(Decoupled(new MeshWarpInput))
    val out = Decoupled(new MeshWarpOutput)
  })
}

输入输出接口

class MeshWarpInput extends Bundle {
  val op1 = Vec(16, UInt(8.W))        // 第一个操作数向量
  val op2 = Vec(16, UInt(8.W))        // 第二个操作数向量
  val thread_id = UInt(10.W)          // 线程标识符
}

class MeshWarpOutput extends Bundle {
  val res = Vec(16, UInt(32.W))       // 结果向量
}

线程配置

网格中的线程按以下规则配置:

val threadMap = (0 until 32).map { i =>
  val threadName = i.toString
  val opType = if (i < 16) "mul" else "cascade"
  val bond = if (opType == "mul") {
    BondParam("vvv", inputWidth = 8, outputWidth = 32)
  } else {
    BondParam("vvv", inputWidth = 32, outputWidth = 32)
  }
  val op = OpParam(opType, bond)
  val thread = ThreadParam(16, s"attr$threadName", threadName, op)
  threadName -> thread
}.toMap

线程分配:

  • 线程 0-15:乘法操作线程 (8位输入 → 32位输出)
  • 线程 16-31:级联操作线程 (32位输入 → 32位输出)

数据流连接

网格中的数据流按以下方式连接:

// 连接mul线程的输出到cascade线程的输入
casBond.in.bits.in1 := mulBond.out.bits.out
mulBond.out.ready   := casBond.in.ready

// 级联连接cascade线程
if (i == 0) {
  casBond.in.bits.in2 := VecInit(Seq.fill(16)(0.U(32.W)))
} else {
  casBond.in.bits.in2 := prevCasBond.out.bits.out
}

数据流路径:

  1. 输入数据 → 乘法线程 (thread 0-15)
  2. 乘法结果 → 级联线程 (thread 16-31)
  3. 级联线程间串行连接
  4. 最终结果从 thread 31 输出

VecBall - 向量球形处理器

VecBall 是 MeshWarp 的封装器,提供状态管理和迭代控制:

class VecBall(implicit p: Parameters) extends Module {
  val io = IO(new VecBallIO())
}

接口定义

class VecBallIO extends BallIO {
  val op1In = Flipped(Valid(Vec(16, UInt(8.W))))    // 操作数1输入
  val op2In = Flipped(Valid(Vec(16, UInt(8.W))))    // 操作数2输入
  val rstOut = Decoupled(Vec(16, UInt(32.W)))       // 结果输出
}

class BallIO extends Bundle {
  val iterIn = Flipped(Decoupled(UInt(10.W)))       // 迭代次数输入
  val iterOut = Valid(UInt(10.W))                   // 当前迭代输出
}

状态管理

VecBall 维护以下内部状态:

val start  = RegInit(false.B)      // 开始标志
val arrive = RegInit(false.B)      // 到达标志
val done   = RegInit(false.B)      // 完成标志
val iter   = RegInit(0.U(10.W))    // 总迭代次数
val iterCounter = RegInit(0.U(10.W)) // 当前迭代计数

线程调度

VecBall 使用轮转调度分配线程:

val threadId = RegInit(0.U(4.W))
when (io.op1In.valid && io.op2In.valid && threadId < 15.U) {
  threadId := threadId + 1.U
} .elsewhen (io.op1In.valid && io.op2In.valid && threadId === 15.U) {
  threadId := 0.U
}

使用方法

创建 MeshWarp 实例

val meshWarp = Module(new MeshWarp()(p))

// 连接输入
meshWarp.io.in.valid := inputValid
meshWarp.io.in.bits.op1 := operand1
meshWarp.io.in.bits.op2 := operand2
meshWarp.io.in.bits.thread_id := selectedThread

// 连接输出
outputValid := meshWarp.io.out.valid
result := meshWarp.io.out.bits.res
meshWarp.io.out.ready := outputReady

创建 VecBall 实例

val vecBall = Module(new VecBall()(p))

// 设置迭代次数
vecBall.io.iterIn.valid := iterValid
vecBall.io.iterIn.bits := totalIterations

// 输入数据
vecBall.io.op1In.valid := dataValid
vecBall.io.op1In.bits := inputVector1
vecBall.io.op2In.valid := dataValid
vecBall.io.op2In.bits := inputVector2

// 获取结果
outputReady := vecBall.io.rstOut.ready
when(vecBall.io.rstOut.valid) {
  result := vecBall.io.rstOut.bits
}

计算模式

向量乘法累加

MeshWarp 实现的计算模式:

  1. 乘法阶段:16 个乘法线程并行计算 op1[i] * op2[i]
  2. 累加阶段:16 个级联线程串行累加乘法结果
  3. 输出阶段:输出最终的累加向量

迭代处理

VecBall 支持多次迭代处理:

  1. 设置迭代次数 iterIn
  2. 循环输入数据对
  3. 监控迭代计数 iterOut
  4. 检查完成状态

性能特性

  • 并行度:16 个乘法操作并行执行
  • 流水线:支持连续数据流处理
  • 吞吐量:每周期可处理一个 16 元素向量对
  • 延迟:乘法 + 级联的组合延迟

相关模块

BuckyBall 示例配置实现

概述

该目录包含了 BuckyBall 框架的示例配置和参考实现,用于展示如何配置和扩展 BuckyBall 系统。目录位于 arch/src/main/scala/examples 下,在整个架构中作为配置层,为开发者提供配置模板和系统实例。

主要功能包括:

  • BuckyBallConfig: 全局配置参数定义
  • toy 系统: 示例系统实现,包含自定义协处理器和 CSR 扩展

代码结构

examples/
├── BuckyBallConfig.scala  - 全局配置定义
└── toy/                   - 完整示例系统
    ├── balldomain/        - 球域组件实现
    ├── CSR.scala          - 自定义CSR实现
    ├── CustomConfigs.scala - 系统配置组合
    └── ToyBuckyBall.scala - 系统顶层模块

文件依赖关系

BuckyBallConfig.scala (基础配置层)

  • 定义全局配置参数和默认值
  • 被所有其他配置文件继承和扩展
  • 提供系统级的配置接口

toy/CustomConfigs.scala (配置组合层)

  • 继承 BuckyBallConfig 并添加自定义参数
  • 组合多个配置片段形成完整配置
  • 为 ToyBuckyBall 提供配置支持

toy/ToyBuckyBall.scala (系统实例化层)

  • 使用 CustomConfigs 实例化完整系统
  • 作为 mill 构建的入口点
  • 生成最终的 Verilog 代码

模块说明

BuckyBallConfig.scala

主要功能: 定义 BuckyBall 框架的全局配置参数

关键组件:

class BuckyBallConfig extends Config(
  new WithNBigCores(1) ++
  new WithRV32 ++
  new WithBuckyBallRoCC ++
  new WithL1ICacheWays(4) ++
  new WithL1DCacheWays(4) ++
  new BaseConfig
)

配置参数:

  • WithNBigCores(1): 配置单核 Rocket 处理器
  • WithRV32: 使用 32 位 RISC-V 指令集
  • WithBuckyBallRoCC: 启用 BuckyBall 自定义协处理器
  • WithL1ICacheWays(4): L1 指令缓存 4 路组相联
  • WithL1DCacheWays(4): L1 数据缓存 4 路组相联

输入输出:

  • 输入: 无直接输入,通过配置系统传递参数
  • 输出: 配置参数供其他模块使用
  • 边缘情况: 配置冲突时按优先级覆盖

toy/CustomConfigs.scala

主要功能: 组合多个配置片段,为 toy 系统提供完整配置

关键组件:

class ToyBuckyBallConfig extends Config(
  new WithToyBallDomain ++
  new WithCustomCSR ++
  new BuckyBallConfig
)

配置组合:

  • WithToyBallDomain: 添加球域组件配置
  • WithCustomCSR: 启用自定义 CSR 支持
  • BuckyBallConfig: 继承基础配置

toy/ToyBuckyBall.scala

主要功能: 系统顶层模块,实例化完整的 toy 系统

关键组件:

object ToyBuckyBall extends App {
  implicit val config = new ToyBuckyBallConfig

  val gen = () => LazyModule(new BuckyBallSystem).module

  (new ChiselStage).execute(args, Seq(
    ChiselGeneratorAnnotation(gen),
    TargetDirAnnotation("generated-src/toy")
  ))
}

构建流程:

  1. 加载 ToyBuckyBallConfig 配置
  2. 实例化 BuckyBallSystem LazyModule
  3. 通过 ChiselStage 生成 Verilog
  4. 输出到 generated-src/toy 目录

输入输出:

  • 输入: 命令行参数 (args)
  • 输出: Verilog 文件和相关构建产物
  • 边缘情况: 配置错误时构建失败

toy/CSR.scala

主要功能: 实现自定义控制状态寄存器

关键组件:

class CustomCSR extends Module {
  val io = IO(new Bundle {
    val csr_req = Flipped(Valid(new CSRReq))
    val csr_resp = Valid(new CSRResp)
  })

  // CSR 地址映射
  val custom_csr_addr = 0x800.U

  when(io.csr_req.valid && io.csr_req.bits.addr === custom_csr_addr) {
    // 处理自定义 CSR 读写
  }
}

CSR 功能:

  • 扩展标准 RISC-V CSR 空间
  • 提供自定义控制和状态接口
  • 支持读写操作和权限检查

使用方法

使用方法

构建 toy 系统:

cd arch
mill arch.runMain examples.toy.ToyBuckyBall

自定义配置开发:

  1. 复制 CustomConfigs.scala 作为模板
  2. 修改配置参数满足需求
  3. 实现必要的自定义组件
  4. 更新顶层模块引用新配置

注意事项

  1. 配置优先级: 配置链中后面的配置会覆盖前面的同名参数
  2. 依赖管理: 确保自定义组件的依赖在配置中正确声明
  3. 构建路径: 生成的文件路径由 TargetDirAnnotation 指定
  4. 参数验证: 配置参数在实例化时进行验证,错误配置会导致构建失败

Toy BuckyBall 示例实现

概述

该目录包含了 BuckyBall 框架的完整示例实现,展示了如何构建一个基于 RoCC 接口的自定义协处理器。位于 arch/src/main/scala/examples/toy 下,作为 BuckyBall 系统的参考实现,集成了全局解码器、Ball域和内存域。

实现的核心组件:

  • ToyBuckyBall.scala: 主要的 RoCC 协处理器实现
  • CustomConfigs.scala: 系统配置和 RoCC 集成配置
  • CSR.scala: 自定义控制状态寄存器
  • balldomain/: Ball域相关组件实现

代码结构

toy/
├── ToyBuckyBall.scala    - 主协处理器实现
├── CustomConfigs.scala   - 配置定义
├── CSR.scala            - CSR实现
└── balldomain/          - Ball域组件

文件依赖关系

ToyBuckyBall.scala (核心实现层)

  • 继承 LazyRoCCBB,实现 RoCC 协处理器接口
  • 集成 GlobalDecoder、BallDomain、MemDomain
  • 管理 TileLink 连接和 DMA 组件

CustomConfigs.scala (配置层)

  • 定义 BuckyBallCustomConfig 和 BuckyBallToyConfig
  • 配置 RoCC 集成和系统参数
  • 提供多核配置支持

CSR.scala (寄存器层)

  • 实现 FenceCSR 控制寄存器
  • 提供简单的 64 位寄存器接口

模块说明

ToyBuckyBall.scala

主要功能: 实现完整的 BuckyBall RoCC 协处理器

关键组件:

class ToyBuckyBall(val b: CustomBuckyBallConfig)(implicit p: Parameters)
  extends LazyRoCCBB (opcodes = b.opcodes, nPTWPorts = 2) {

  val reader = LazyModule(new BBStreamReader(...))
  val writer = LazyModule(new BBStreamWriter(...))
  val xbar_node = TLXbar()
}

系统架构:

// 前端:全局解码器
val gDecoder = Module(new GlobalDecoder)

// 后端:Ball域和内存域
val ballDomain = Module(new BallDomain)
val memDomain = Module(new MemDomain)

// 响应仲裁
val respArb = Module(new Arbiter(new RoCCResponseBB()(p), 2))

TileLink 连接:

xbar_node := TLBuffer() := reader.node
xbar_node := TLBuffer() := writer.node
id_node := TLWidthWidget(b.dma_buswidth/8) := TLBuffer() := xbar_node

输入输出:

  • 输入: RoCC 命令接口,PTW 接口
  • 输出: RoCC 响应,TileLink 内存访问
  • 边缘情况: Fence 操作时的忙等待处理

CustomConfigs.scala

主要功能: 定义系统配置和 RoCC 集成

配置类定义:

class BuckyBallCustomConfig(
  buckyballConfig: CustomBuckyBallConfig = CustomBuckyBallConfig()
) extends Config((site, here, up) => {
  case BuildRoCCBB => up(BuildRoCCBB) ++ Seq(
    (p: Parameters) => {
      val buckyball = LazyModule(new ToyBuckyBall(buckyballConfig))
      buckyball
    }
  )
})

系统配置:

class BuckyBallToyConfig extends Config(
  new framework.rocket.WithNBuckyBallCores(1) ++
  new BuckyBallCustomConfig(CustomBuckyBallConfig()) ++
  new chipyard.config.WithSystemBusWidth(128) ++
  new WithCustomBootROM ++
  new chipyard.config.AbstractConfig
)

多核支持:

class WithMultiRoCCToyBuckyBall(harts: Int*) extends Config(...)

CSR.scala

主要功能: 提供自定义控制状态寄存器

实现:

object FenceCSR {
  def apply(): UInt = RegInit(0.U(64.W))
}

Fence 处理逻辑:

val fenceCSR = FenceCSR()
val fenceSet = ballDomain.io.fence_o
val allDomainsIdle = !ballDomain.io.busy && !memDomain.io.busy

when (fenceSet) {
  fenceCSR := 1.U
  io.cmd.ready := allDomainsIdle
}

使用方法

系统集成

RoCC 接口集成:

  • 通过 BuildRoCCBB 配置键注册协处理器
  • 支持多核心配置
  • 提供 2 个 PTW 端口用于地址转换

域间通信:

// BallDomain -> MemDomain 桥接
ballDomain.io.sramRead <> memDomain.io.ballDomain.sramRead
ballDomain.io.sramWrite <> memDomain.io.ballDomain.sramWrite

DMA 连接:

memDomain.io.dma.read.req <> outer.reader.module.io.req
memDomain.io.dma.write.req <> outer.writer.module.io.req

注意事项

  1. Fence 语义: 使用 CSR 实现 Fence 操作的同步
  2. 忙等待检测: 防止仿真长时间停顿的断言检查
  3. TLB 集成: TLB 功能集成在 MemDomain 内部
  4. 响应仲裁: BallDomain 优先级高于 MemDomain
  5. 配置依赖: 需要正确配置 CustomBuckyBallConfig 参数

BallDomain 球域示例实现

概述

该目录包含了 BuckyBall 框架中球域(BallDomain)的完整示例实现,展示了如何构建一个自定义的计算域来管理专用加速器。球域是 BuckyBall 架构中的核心概念,用于封装和管理一组相关的计算单元,提供统一的控制和数据流管理。

该目录实现了球域架构,包括:

  • BallDomain: 球域顶层模块,管理整个计算域
  • BallController: 球域控制器,负责指令调度和执行控制
  • DISA: 分布式指令调度架构
  • DomainDecoder: 域指令解码器
  • 专用加速器: 包含矩阵、向量、im2col等多种加速器实现

代码结构

balldomain/
├── BallDomain.scala      - 球域顶层模块
├── BallController.scala  - 球域控制器
├── DISA.scala           - 分布式指令调度架构
├── DomainDecoder.scala  - 域指令解码器
├── bbus/                - 球域总线系统
├── im2col/              - 图像到列转换加速器
├── matrixball/          - 矩阵运算球域
├── rs/                  - 保留站实现
└── vecball/             - 向量运算球域

文件依赖关系

BallDomain.scala (顶层模块)

  • 集成所有子模块,提供统一的球域接口
  • 管理球域内部的数据流和控制流
  • 连接到系统总线和RoCC接口

BallController.scala (控制层)

  • 实现球域的指令调度和执行控制
  • 管理多个加速器之间的协调
  • 提供状态管理和错误处理

DISA.scala (调度层)

  • 分布式指令调度架构实现
  • 支持多指令并发执行
  • 提供动态负载均衡

DomainDecoder.scala (解码层)

  • 球域专用指令解码
  • 指令分发到相应的执行单元
  • 支持复杂指令的分解和重组

模块说明

BallDomain.scala

主要功能: 球域顶层模块,集成所有计算单元和控制逻辑

关键组件:

class BallDomain(implicit p: Parameters) extends LazyModule {
  val controller = LazyModule(new BallController)
  val matrixBall = LazyModule(new MatrixBall)
  val vecBall = LazyModule(new VecBall)
  val im2colUnit = LazyModule(new Im2colUnit)

  // 球域总线连接
  val bbus = LazyModule(new BBus)
  bbus.node := controller.node
  matrixBall.node := bbus.node
  vecBall.node := bbus.node
}

输入输出:

  • 输入: RoCC指令接口,内存访问接口
  • 输出: 计算结果,状态信息
  • 边缘情况: 指令冲突处理,资源竞争管理

BallController.scala

主要功能: 球域控制器,负责整个球域的运行控制

关键组件:

class BallController extends Module {
  val io = IO(new Bundle {
    val rocc = Flipped(new RoCCCoreIO)
    val mem = new HellaCacheIO
    val domain_ctrl = new DomainControlIO
  })

  // 指令队列和调度逻辑
  val inst_queue = Module(new Queue(new RoCCInstruction, 16))
  val scheduler = Module(new InstructionScheduler)
}

调度策略:

  • 基于指令类型的静态调度
  • 动态资源分配和负载均衡
  • 支持指令流水线和并发执行

DISA.scala

主要功能: 分布式指令调度架构

关键组件:

class DISA extends Module {
  val io = IO(new Bundle {
    val inst_in = Flipped(Decoupled(new Instruction))
    val exec_units = Vec(numUnits, new ExecutionUnitIO)
    val completion = Decoupled(new CompletionInfo)
  })

  // 分布式调度表
  val dispatch_table = Reg(Vec(numUnits, new DispatchEntry))
  val load_balancer = Module(new LoadBalancer)
}

调度算法:

  • 轮询调度保证公平性
  • 优先级调度支持关键任务
  • 动态调度适应负载变化

DomainDecoder.scala

主要功能: 球域指令解码器

关键组件:

class DomainDecoder extends Module {
  val io = IO(new Bundle {
    val inst = Input(UInt(32.W))
    val decoded = Output(new DecodedInstruction)
    val valid = Output(Bool())
  })

  // 指令解码表
  val decode_table = Array(
    MATRIX_OP -> MatrixOpDecoder,
    VECTOR_OP -> VectorOpDecoder,
    IM2COL_OP -> Im2colOpDecoder
  )
}

解码功能:

  • 支持多种指令格式
  • 复杂指令的微码展开
  • 指令依赖分析和优化

使用方法

设计特点

  1. 模块化架构: 每个加速器都是独立的模块,便于扩展和维护
  2. 统一接口: 所有加速器通过统一的球域总线进行通信
  3. 灵活调度: 支持多种调度策略,适应不同的计算模式
  4. 可扩展性: 易于添加新的加速器类型和功能

性能优化

  1. 流水线设计: 指令解码、调度、执行采用流水线架构
  2. 并发执行: 支持多个加速器同时工作
  3. 数据管理: 数据缓存和访问管理
  4. 工作负载: 工作负载分配

使用示例

// 创建球域实例
val ballDomain = LazyModule(new BallDomain)

// 连接到RoCC接口
rocc.cmd <> ballDomain.module.io.rocc.cmd
rocc.resp <> ballDomain.module.io.rocc.resp

// 配置球域参数
ballDomain.module.io.config := ballDomainConfig

注意事项

  1. 资源管理: 需要合理分配计算资源,避免资源冲突
  2. 时序约束: 注意不同模块间的时序关系和数据同步
  3. 功耗控制: 实现动态功耗管理,在不使用时关闭相应模块
  4. 调试支持: 调试接口和状态监控功能

BBus 球域总线系统

概述

该目录包含了 BuckyBall 球域总线系统的实现,主要负责管理球域内多个Ball节点对SRAM资源的访问。总线系统基于 framework.blink 中的 BBusNode 实现,提供了SRAM资源的仲裁和路由功能。

该目录实现了两个核心组件:

  • BallBus: 球域总线主模块,管理多个Ball节点的SRAM访问
  • BBusRouter: 总线路由器,提供Blink接口的路由功能

代码结构

bbus/
├── BallBus.scala    - 球域总线主模块
└── router.scala     - 总线路由器实现

文件依赖关系

BallBus.scala (主模块)

  • 创建多个BBusNode实例来管理Ball节点
  • 连接外部SRAM接口到各个Ball节点
  • 实现SRAM资源的分配和仲裁

router.scala (路由模块)

  • 基于BBusNode实现路由功能
  • 提供Blink协议的接口封装

模块说明

BallBus.scala

主要功能: 球域总线主模块,管理多个Ball节点对SRAM资源的访问

关键组件:

class BallBus(maxReadBW: Int, maxWriteBW: Int, numBalls: Int) extends LazyModule {
  // 创建多个BBusNode
  val ballNodes = Seq.fill(numBalls) {
    new BBusNode(BallParams(sramReadBW = maxReadBW, sramWriteBW = maxWriteBW))
  }

  // 外部SRAM接口
  val io = IO(new Bundle {
    val sramRead = Vec(b.sp_banks, Flipped(new SramReadIO(...)))
    val sramWrite = Vec(b.sp_banks, Flipped(new SramWriteIO(...)))
    val accRead = Vec(b.acc_banks, Flipped(new SramReadIO(...)))
    val accWrite = Vec(b.acc_banks, Flipped(new SramWriteIO(...)))
  })
}

资源分配策略:

  • sp_banks 个端口连接到scratchpad SRAM
  • 接下来 acc_banks 个端口连接到accumulator SRAM
  • 多余的端口设置为无效状态
  • 所有Ball节点共享相同的SRAM资源

输入输出:

  • 输入: 来自各Ball节点的SRAM访问请求
  • 输出: 连接到外部SRAM的读写接口
  • 边缘情况: 处理超出配置范围的端口,设置为DontCare

依赖项: framework.blink.BBusNode, framework.builtin.memdomain.mem

router.scala

主要功能: 总线路由器,提供Blink协议接口的路由功能

关键组件:

class BBusRouter extends LazyModule {
  val node = new BBusNode(BallParams(
    sramReadBW = b.sp_banks,
    sramWriteBW = b.sp_banks
  ))

  val io = IO(new Bundle {
    val blink = Flipped(new BlinkBundle(node.edges.in.head))
  })
}

路由功能:

  • 基于BBusNode实现标准的Ball节点接口
  • 提供Blink协议的封装和转换
  • 支持配置化的读写带宽参数

输入输出:

  • 输入: Blink协议接口
  • 输出: BBusNode标准接口
  • 边缘情况: 依赖node.edges.in.head的有效性

依赖项: framework.blink.BlinkBundle, framework.blink.BBusNode

使用方法

配置参数

总线系统的配置通过以下参数控制:

  • maxReadBW: 最大读带宽(端口数量)
  • maxWriteBW: 最大写带宽(端口数量)
  • numBalls: Ball节点数量
  • b.sp_banks: Scratchpad Bank数量
  • b.acc_banks: Accumulator Bank数量

资源管理

  1. SRAM端口分配: 按照scratchpad优先、accumulator次之的顺序分配端口
  2. 多Ball共享: 所有Ball节点共享相同的SRAM资源池
  3. 端口复用: 超出配置的端口被设置为无效状态以节省资源

使用示例

// 创建球域总线
val ballBus = LazyModule(new BallBus(
  maxReadBW = 8,
  maxWriteBW = 8,
  numBalls = 4
))

// 连接外部SRAM
scratchpad.io.read <> ballBus.module.io.sramRead
scratchpad.io.write <> ballBus.module.io.sramWrite
accumulator.io.read <> ballBus.module.io.accRead
accumulator.io.write <> ballBus.module.io.accWrite

注意事项

  1. 资源冲突: 多个Ball节点可能同时访问相同的SRAM资源,需要上层协调
  2. 带宽限制: 实际可用带宽受限于配置的最大读写带宽参数
  3. 端口映射: 确保SRAM端口数量与配置参数匹配,避免越界访问
  4. 时序约束: BBusNode的时序要求需要与外部SRAM接口匹配

im2colball

MatrixBall - 矩阵处理球

概述

MatrixBall 是 BuckyBall 系统中的矩阵处理加速器封装,它将 BBFP(BuckyBall Floating Point)控制器集成到 Ball 域架构中。MatrixBall 专门用于高性能的矩阵运算,特别是浮点矩阵乘法和相关的线性代数操作。

二、文件结构

matrixball/
├── MatrixBall.scala  - MatrixBall 封装实现
└── README.md        - 本文档

三、核心功能

MatrixBall LazyModule

MatrixBall 是一个 LazyModule,负责:

  • 封装 BBFP_Control 核心矩阵处理逻辑
  • 通过 Diplomacy 协议协商存储带宽
  • 连接 scratchpad 和 accumulator 存储器
  • 提供标准的 Ball 域接口
  • 支持矩阵乘法的权重静态(Weight Stationary)模式

主要特性

  • 浮点运算: 专门优化的浮点矩阵运算
  • 存储带宽协商: 自动请求所需的存储带宽
  • 权重静态支持: 支持 Weight Stationary 数据流模式
  • 标准接口: 提供统一的命令请求/响应接口

四、架构设计

Diplomacy 节点配置

val node = new BallNode(Seq(BBusParams(
  sramReadBW = b.sp_banks + b.acc_banks,   // 读带宽需求
  sramWriteBW = b.sp_banks + b.acc_banks   // 写带宽需求
)))

MatrixBall 请求的带宽包括:

  • scratchpad banks: b.sp_banks 个读写端口,用于存储输入矩阵
  • accumulator banks: b.acc_banks 个读写端口,用于存储输出结果

核心组件连接

BBFP_Control 实例化

val bbfpControl = Module(new BBFP_Control)

存储器连接策略

// Scratchpad 连接 - 存储输入矩阵数据
for (i <- 0 until b.sp_banks) {
  bundle.data.sramRead(i) <> bbfpControl.io.sramRead(i)
  bundle.data.sramWrite(i) <> bbfpControl.io.sramWrite(i)
}

// Accumulator 连接 - 存储累加结果
for (i <- 0 until b.acc_banks) {
  val readIdx = b.sp_banks + i
  val writeIdx = b.sp_banks + i
  bundle.data.sramRead(readIdx) <> bbfpControl.io.accRead(i)
  bundle.data.sramWrite(writeIdx) <> bbfpControl.io.accWrite(i)
}

接口设计

val io = IO(new Bundle {
  val cmdReq = Flipped(Decoupled(new BallRsIssue))     // 命令请求输入
  val cmdResp = Decoupled(new BallRsComplete)          // 命令响应输出
  val is_matmul_ws = Input(Bool())                     // 权重静态模式控制
})

五、工作流程

矩阵乘法执行流程

  1. 命令接收: 从保留站接收矩阵运算命令
  2. 模式配置: 根据 is_matmul_ws 信号配置数据流模式
  3. 数据加载: 从 scratchpad 加载输入矩阵数据
  4. 矩阵运算: BBFP_Control 执行浮点矩阵乘法
  5. 结果存储: 将计算结果存储到 accumulator
  6. 完成响应: 向保留站发送执行完成信号

权重静态模式

Weight Stationary 特点

  • 权重矩阵保持在本地存储器中
  • 输入数据流式传输
  • 减少权重数据的重复加载
  • 提高计算效率和能耗比
bbfpControl.io.is_matmul_ws := io.is_matmul_ws

六、存储器访问模式

Scratchpad 使用模式

  • 输入矩阵A: 存储在 scratchpad 的前半部分
  • 权重矩阵B: 存储在 scratchpad 的后半部分
  • 访问模式: 支持行优先和列优先访问
  • 访问策略: 数据缓存和访问管理

Accumulator 使用模式

  • 部分积累: 存储矩阵乘法的中间结果
  • 最终结果: 存储完整的输出矩阵
  • 累加模式: 支持多次累加操作
  • 输出格式: 支持多种输出数据格式

七、性能特性

计算性能

  • 浮点精度: 支持单精度和半精度浮点运算
  • 并行度: 多个乘加单元并行工作
  • 吞吐量: 高吞吐量的矩阵乘法操作
  • 延迟优化: 流水线设计减少计算延迟

存储性能

  • 带宽利用: 高效利用存储带宽
  • 数据重用: 最大化数据重用减少访存
  • 缓存命中: 优化的缓存策略提高命中率

八、配置参数

关键配置项

class MyBuckyBallConfig extends CustomBuckyBallConfig {
  override val sp_banks = 8     // Scratchpad bank 数量
  override val acc_banks = 4    // Accumulator bank 数量
  // BBFP 相关配置
  val matrix_size = 64          // 支持的矩阵大小
  val fp_precision = 32         // 浮点精度位数
}

性能调优参数

  • bank 数量: 影响并行访存能力
  • 矩阵分块大小: 影响缓存效率
  • 流水线深度: 影响延迟和吞吐量平衡

九、使用示例

基本矩阵乘法

// 配置 MatrixBall
val matrixBall = LazyModule(new MatrixBall)

// 执行矩阵乘法
matrixBall.module.io.cmdReq.valid := true.B
matrixBall.module.io.cmdReq.bits.cmd.bid := 2.U  // BBFP ball ID
matrixBall.module.io.is_matmul_ws := true.B      // 启用权重静态模式

系统集成

class BallDomain extends LazyModule {
  val matrixBall = LazyModule(new MatrixBall)
  val reservationStation = Module(new BallReservationStation)

  // 连接保留站
  matrixBall.module.io.cmdReq <> reservationStation.io.issue_o.ball2
  reservationStation.io.commit_i.ball2 <> matrixBall.module.io.cmdResp
}

十、应用场景

机器学习

  • 神经网络推理: 前向传播中的矩阵乘法
  • 训练加速: 反向传播中的梯度计算
  • 权重更新: 参数更新中的矩阵运算

科学计算

  • 线性代数: 基础的矩阵运算
  • 数值求解: 线性方程组求解
  • 信号处理: 滤波器和变换运算

图形处理

  • 3D 变换: 顶点变换矩阵运算
  • 着色器计算: GPU 风格的并行计算
  • 图像处理: 卷积和滤波操作

十一、调试和监控

性能监控

  • 计算吞吐量: 每秒执行的矩阵运算数
  • 存储带宽利用率: 实际使用的存储带宽
  • 缓存命中率: 数据缓存的命中统计
  • 功耗分析: 各个组件的功耗分布

调试接口

  • 状态寄存器: 查看内部状态和配置
  • 性能计数器: 统计各种性能指标
  • 错误检测: 检测计算错误和异常情况

十二、优化建议

算法优化

  • 分块策略: 优化矩阵分块大小
  • 数据布局: 改进数据在存储器中的布局
  • 计算顺序: 优化计算的执行顺序

硬件优化

  • 流水线深度: 调整流水线深度平衡延迟和面积
  • 并行度: 增加计算单元提高并行度
  • 存储层次: 优化存储器层次结构

十三、相关文档

保留站和重排序缓冲区 (Reservation Station & ROB)

概述

该模块实现了 BuckyBall 系统中的保留站(Reservation Station)和重排序缓冲区(ROB),用于支持乱序执行和指令调度。保留站负责管理指令的发射和完成,而 ROB 确保指令按程序顺序提交,维护处理器的精确异常语义。

二、文件结构

rs/
├── reservationStation.scala  - 保留站实现
└── rob.scala                - 重排序缓冲区实现

三、核心组件

BallReservationStation - Ball域保留站

保留站是连接指令解码器和执行单元的关键组件,负责:

主要功能

  • 接收来自 Ball 域解码器的指令
  • 根据指令类型分发到不同的执行单元
  • 管理指令的发射和完成状态
  • 生成 RoCC 响应

支持的执行单元

  • ball1: VecUnit(向量处理单元)
  • ball2: BBFP(浮点处理单元)
  • ball3: im2col(图像处理加速器)
  • ball4: transpose(矩阵转置加速器)

接口设计

class BallReservationStation extends Module {
  val io = IO(new Bundle {
    // 指令输入
    val ball_decode_cmd_i = Flipped(DecoupledIO(new BallDecodeCmd))

    // RoCC 响应输出
    val rs_rocc_o = new Bundle {
      val resp = DecoupledIO(new RoCCResponseBB)
      val busy = Output(Bool())
    }

    // 执行单元接口
    val issue_o = new BallIssueInterface    // 发射接口
    val commit_i = new BallCommitInterface  // 完成接口
  })
}

指令分发逻辑

// 根据 bid (Ball ID) 分发指令
io.issue_o.ball1.valid := rob.io.issue.valid && rob.io.issue.bits.cmd.bid === 1.U  // VecUnit
io.issue_o.ball2.valid := rob.io.issue.valid && rob.io.issue.bits.cmd.bid === 2.U  // BBFP
io.issue_o.ball3.valid := rob.io.issue.valid && rob.io.issue.bits.cmd.bid === 3.U  // im2col
io.issue_o.ball4.valid := rob.io.issue.valid && rob.io.issue.bits.cmd.bid === 4.U  // transpose

ROB - 重排序缓冲区

ROB 实现了指令的顺序管理和乱序完成支持:

设计特点

  • 使用 FIFO 队列维护指令顺序
  • 使用完成状态表跟踪指令执行状态
  • 支持乱序完成但顺序发射
  • 提供 ROB ID 用于指令标识

核心数据结构

class RobEntry extends Bundle {
  val cmd = new BallDecodeCmd           // 指令内容
  val rob_id = UInt(log2Up(rob_entries).W)  // ROB 标识符
}

状态管理

val robFifo = Module(new Queue(new RobEntry, rob_entries))  // 指令队列
val robTable = Reg(Vec(rob_entries, Bool()))               // 完成状态表
val robIdCounter = RegInit(0.U(log2Up(rob_entries).W))     // ID 计数器

四、工作流程

指令分配流程

  1. 指令入队:解码器发送的指令进入 ROB
  2. 分配 ROB ID:为每条指令分配唯一的 ROB ID
  3. 状态初始化:在完成状态表中标记为未完成
when(io.alloc.fire) {
  robIdCounter := robIdCounter + 1.U
  robTable(robIdCounter) := false.B  // 标记为未完成
}

指令发射流程

  1. 头部检查:检查 ROB 头部指令是否未完成
  2. 类型分发:根据 bid 将指令发射到对应执行单元
  3. 就绪控制:只有目标执行单元就绪时才发射
val headEntry = robFifo.io.deq.bits
val headCompleted = robTable(headEntry.rob_id)
io.issue.valid := robFifo.io.deq.valid && !headCompleted

指令完成流程

  1. 完成仲裁:多个执行单元的完成信号通过仲裁器处理
  2. 状态更新:根据 ROB ID 更新完成状态表
  3. 队列出队:已完成的头部指令从 ROB 中移除
val completeArb = Module(new Arbiter(UInt(log2Up(rob_entries).W), 4))
when(io.complete.fire) {
  robTable(io.complete.bits) := true.B  // 标记为已完成
}

五、配置参数

关键配置项

  • rob_entries: ROB 条目数量,影响乱序执行窗口大小
  • 执行单元数量: 当前支持 4 个 Ball 执行单元
  • 仲裁策略: 使用轮转仲裁处理多个完成信号

性能考虑

  • ROB 大小: 更大的 ROB 支持更多乱序执行,但增加硬件开销
  • 发射带宽: 当前每周期最多发射一条指令
  • 完成带宽: 支持每周期多个指令完成

六、接口协议

BallIssueInterface - 发射接口

class BallIssueInterface extends Bundle {
  val ball1 = Decoupled(new BallRsIssue)  // VecUnit 发射
  val ball2 = Decoupled(new BallRsIssue)  // BBFP 发射
  val ball3 = Decoupled(new BallRsIssue)  // im2col 发射
  val ball4 = Decoupled(new BallRsIssue)  // transpose 发射
}

BallCommitInterface - 完成接口

class BallCommitInterface extends Bundle {
  val ball1 = Flipped(Decoupled(new BallRsComplete))  // VecUnit 完成
  val ball2 = Flipped(Decoupled(new BallRsComplete))  // BBFP 完成
  val ball3 = Flipped(Decoupled(new BallRsComplete))  // im2col 完成
  val ball4 = Flipped(Decoupled(new BallRsComplete))  // transpose 完成
}

七、使用示例

基本配置

// 在 CustomBuckyBallConfig 中配置 ROB 大小
class MyBuckyBallConfig extends CustomBuckyBallConfig {
  override val rob_entries = 16  // 16 条目 ROB
}

// 实例化保留站
val reservationStation = Module(new BallReservationStation)

连接执行单元

// 连接 VecUnit
vecUnit.io.cmd <> reservationStation.io.issue_o.ball1
reservationStation.io.commit_i.ball1 <> vecUnit.io.resp

// 连接 BBFP
bbfp.io.cmd <> reservationStation.io.issue_o.ball2
reservationStation.io.commit_i.ball2 <> bbfp.io.resp

八、调试和监控

状态信号

  • io.rs_rocc_o.busy: 保留站忙碌状态
  • rob.io.empty: ROB 空状态
  • rob.io.full: ROB 满状态

性能计数器

可以添加以下性能计数器进行监控:

  • 指令发射计数
  • 指令完成计数
  • ROB 利用率
  • 各执行单元的负载分布

九、扩展说明

添加新执行单元

  1. BallIssueInterface 中添加新的发射端口
  2. BallCommitInterface 中添加对应的完成端口
  3. 在保留站中添加相应的分发和仲裁逻辑
  4. 更新完成信号仲裁器的端口数量

优化建议

  • 多发射支持: 可以扩展为每周期发射多条指令
  • 动态调度: 实现更复杂的调度算法
  • 负载均衡: 在多个同类型执行单元间进行负载均衡

十、相关文档

VecBall - 向量处理球

概述

VecBall 是 BuckyBall 系统中的向量处理加速器封装,它将向量处理单元(VecUnit)集成到 Ball 域架构中。VecBall 通过 Diplomacy 协议与系统的存储子系统进行连接,提供高性能的向量计算能力。

二、文件结构

vecball/
└── VecBall.scala  - VecBall 封装实现

三、核心功能

VecBall LazyModule

VecBall 是一个 LazyModule,负责:

  • 封装 VecUnit 核心向量处理逻辑
  • 通过 Diplomacy 协议协商存储带宽
  • 连接 scratchpad 和 accumulator 存储器
  • 提供标准的 Ball 域接口

主要特性

  • 存储带宽协商: 自动请求所需的 scratchpad 和 accumulator 带宽
  • 灵活连接: 支持多 bank 的存储器连接
  • 标准接口: 提供统一的命令请求/响应接口

四、架构设计

Diplomacy 节点配置

val node = new BallNode(Seq(BBusParams(
  sramReadBW = b.sp_banks + b.acc_banks,   // 读带宽需求
  sramWriteBW = b.sp_banks + b.acc_banks   // 写带宽需求
)))

VecBall 请求的带宽包括:

  • scratchpad banks: b.sp_banks 个读写端口
  • accumulator banks: b.acc_banks 个读写端口

存储器连接策略

Scratchpad 连接

// 前 b.sp_banks 个端口连接到 scratchpad
for (i <- 0 until b.sp_banks) {
  bundle.data.sramRead(i) <> vecUnit.io.sramRead(i)
  bundle.data.sramWrite(i) <> vecUnit.io.sramWrite(i)
}

Accumulator 连接

// 接下来 b.acc_banks 个端口连接到 accumulator
for (i <- 0 until b.acc_banks) {
  val readIdx = b.sp_banks + i
  val writeIdx = b.sp_banks + i
  bundle.data.sramRead(readIdx) <> vecUnit.io.accRead(i)
  bundle.data.sramWrite(writeIdx) <> vecUnit.io.accWrite(i)
}

接口设计

val io = IO(new Bundle {
  val cmdReq = Flipped(Decoupled(new BallRsIssue))     // 命令请求输入
  val cmdResp = Decoupled(new BallRsComplete)          // 命令响应输出
})

五、工作流程

初始化阶段

  1. 带宽协商: 通过 Diplomacy 协议向系统请求所需的存储带宽
  2. 参数验证: 检查协商后的带宽是否满足 VecUnit 的需求
  3. 连接建立: 将 VecUnit 的存储接口连接到系统存储器

运行阶段

  1. 命令接收: 从保留站接收向量处理命令
  2. 向量执行: VecUnit 执行向量计算操作
  3. 存储访问: 通过 scratchpad 和 accumulator 进行数据读写
  4. 完成响应: 向保留站发送执行完成信号

带宽管理

// 检查协商后的参数
val negotiatedParams = node.edges.out.map(e => (e.sramReadBW, e.sramWriteBW))
require(negotiatedParams.forall(p =>
  p._1 >= (b.sp_banks + b.acc_banks) &&
  p._2 >= (b.sp_banks + b.acc_banks)),
  "negotiated bandwidth must support VecUnit requirements")

六、配置参数

关键配置项

  • sp_banks: Scratchpad bank 数量
  • acc_banks: Accumulator bank 数量
  • 向量长度: VecUnit 支持的向量长度
  • 数据位宽: 向量元素的位宽

带宽计算

总带宽需求 = scratchpad banks + accumulator banks

  • 读带宽: b.sp_banks + b.acc_banks
  • 写带宽: b.sp_banks + b.acc_banks

七、存储器访问模式

Scratchpad 访问

  • 用途: 存储输入向量数据和中间结果
  • 访问模式: 支持随机访问和顺序访问
  • bank 数量: 可配置,影响并行访问能力

Accumulator 访问

  • 用途: 存储累加结果和最终输出
  • 访问模式: 主要用于累加操作
  • bank 数量: 通常较少,专门用于累加计算

八、性能特性

并行处理能力

  • 多 bank 并行: 支持多个存储 bank 的并行访问
  • 向量并行: VecUnit 内部的 SIMD 并行处理
  • 流水线执行: 支持指令级流水线

带宽优化

  • 智能分配: 根据实际需求动态分配带宽
  • 冗余处理: 自动处理多余的端口连接
  • 错误检查: 验证带宽协商结果

九、使用示例

基本配置

// 配置 VecBall 参数
class MyBuckyBallConfig extends CustomBuckyBallConfig {
  override val sp_banks = 4    // 4 个 scratchpad banks
  override val acc_banks = 2   // 2 个 accumulator banks
}

// 实例化 VecBall
val vecBall = LazyModule(new VecBall)

系统集成

// 在 Ball 域中集成 VecBall
class BallDomain extends LazyModule {
  val vecBall = LazyModule(new VecBall)
  val memorySystem = LazyModule(new MemorySystem)

  // 通过 Diplomacy 连接
  memorySystem.node := vecBall.node
}

十、调试和监控

状态监控

  • 命令队列状态: 监控命令请求和响应的状态
  • 存储器利用率: 监控各个 bank 的使用情况
  • 带宽利用率: 监控实际带宽使用情况

性能分析

  • 吞吐量: 每秒处理的向量操作数
  • 延迟: 单个向量操作的完成时间
  • 资源利用率: 各个组件的利用率统计

十一、扩展和优化

功能扩展

  • 多 VecUnit 支持: 支持多个 VecUnit 实例
  • 参数配置: 向量处理参数配置
  • 错误处理: 增强的错误检测和恢复机制

性能优化

  • 缓存优化: 改进数据缓存策略
  • 预取机制: 实现数据预取提高性能
  • 负载均衡: 在多个处理单元间平衡负载

十二、相关文档

仿真配置模块

该目录包含了 BuckyBall 项目中各种仿真器的配置和接口实现,为不同的仿真环境提供统一的配置管理。

目录结构

sims/
├── firesim/
│   └── TargetConfigs.scala    - FireSim FPGA仿真配置
└── verilator/
    └── Elaborate.scala        - Verilator仿真顶层生成

FireSim 配置 (firesim/)

TargetConfigs.scala

定义了在 FireSim FPGA 平台上运行的目标配置:

class FireSimBuckyBallConfig extends Config(
  new WithDefaultFireSimBridges ++
  new WithDefaultMemModel ++
  new WithFireSimConfigTweaks ++
  new BuckyBallConfig
)

关键配置项:

  • Bridge 配置: UART、BlockDevice、NIC 等 I/O 桥接
  • 内存模型: DDR3/DDR4 内存控制器配置
  • 时钟域: 多时钟域管理和时钟生成
  • 调试接口: JTAG 和 Debug Module 配置

使用场景:

  • 大规模系统仿真
  • 长时间运行的工作负载测试
  • 多核系统性能评估
  • I/O 密集型应用验证

Verilator 配置 (verilator/)

Elaborate.scala

Verilator 仿真的顶层模块生成器:

object Elaborate extends App {
  val config = new BuckyBallConfig
  val gen = () => LazyModule(new BuckyBallSystem()(config)).module

  (new ChiselStage).execute(args, Seq(
    ChiselGeneratorAnnotation(gen),
    TargetDirAnnotation("generated-src")
  ))
}

生成流程:

  1. 解析命令行参数和配置
  2. 实例化 BuckyBall 系统模块
  3. 生成 Verilog RTL 代码
  4. 输出仿真所需的辅助文件

输出文件:

  • BuckyBallSystem.v - 主 Verilog 文件
  • BuckyBallSystem.anno.json - FIRRTL 注解文件
  • BuckyBallSystem.fir - FIRRTL 中间表示

配置参数化

通用参数

// 处理器核心配置
case object RocketTilesKey extends Field[Seq[RocketTileParams]]

// 内存系统配置
case object MemoryBusKey extends Field[MemoryBusParams]

// 外设配置
case object PeripheryBusKey extends Field[PeripheryBusParams]

仿真特定参数

// Verilator 仿真参数
case object VerilatorDRAMKey extends Field[Boolean](false)

// FireSim 仿真参数
case object FireSimBridgesKey extends Field[Seq[BridgeIOAnnotation]]

构建和使用

Verilator 仿真构建

# 生成 Verilog
cd arch
mill arch.runMain sims.verilator.Elaborate

# 编译仿真器
cd generated-src
verilator --cc BuckyBallSystem.v --exe sim_main.cpp
make -C obj_dir -f VBuckyBallSystem.mk

FireSim 仿真部署

# 配置 FireSim 环境
cd firesim
source sourceme-f1-manager.sh

# 构建 FPGA 镜像
firesim buildbitstream

# 运行仿真
firesim runworkload

调试和优化

Verilator 调试

  • 波形生成: 使用 --trace 选项生成 VCD 文件
  • 性能分析: 使用 --prof-cfuncs 进行性能剖析
  • 覆盖率: 使用 --coverage 生成覆盖率报告

FireSim 调试

  • Printf 调试: 使用 printf 语句输出调试信息
  • 断言检查: 启用运行时断言验证
  • 性能计数器: 集成 HPM 计数器监控

扩展开发

添加新仿真器支持

  1. 创建新的配置目录 (如 vcs/)
  2. 实现仿真器特定的配置类
  3. 添加构建脚本和 Makefile
  4. 更新文档和测试用例

自定义配置

class MyCustomConfig extends Config(
  new WithMyCustomParameters ++
  new BuckyBallConfig
)

相关文档

FireSim 仿真配置

概述

该目录包含了 BuckyBall 系统在 FireSim 平台上的仿真配置。FireSim 是一个基于 FPGA 的开源仿真平台,提供硬件仿真环境,支持系统级仿真和性能分析。

二、文件结构

firesim/
└── TargetConfigs.scala  - FireSim 目标配置

三、配置说明

TargetConfigs.scala

该文件定义了 BuckyBall 系统在 FireSim 平台上的配置:

WithBootROM 配置

class WithBootROM extends Config((site, here, up) => {
  case BootROMLocated(x) => {
    // 自动选择 BootROM 路径
    val chipyardBootROM = new File("./thirdparty/chipyard/generators/testchipip/bootrom/bootrom.rv${MaxXLen}.img")
    val firesimBootROM = new File("./thirdparty/chipyard/target-rtl/chipyard/generators/testchipip/bootrom/bootrom.rv${MaxXLen}.img")

    // 优先使用 chipyard 路径,如果不存在则使用 firesim 路径
    val bootROMPath = if (chipyardBootROM.exists()) {
      chipyardBootROM.getAbsolutePath()
    } else {
      firesimBootROM.getAbsolutePath()
    }
  }
})

FireSimBuckyballToyConfig 配置

class FireSimBuckyballToyConfig extends Config(
  new WithBootROM ++                              // BootROM 配置
  new firechip.chip.WithDefaultFireSimBridges ++ // 默认 FireSim 桥接
  new firechip.chip.WithFireSimConfigTweaks ++   // FireSim 配置调整
  new examples.toy.BuckyBallToyConfig            // BuckyBall 玩具配置
)

高级配置

自定义 BootROM

class MyFireSimConfig extends Config(
  new WithBootROM ++
  new MyCustomBuckyBallConfig ++
  // 其他配置...
)

Verilator 仿真配置

一、Overview

该目录包含了 BuckyBall 系统在 Verilator 平台上的仿真配置。Verilator 是一个开源的 Verilog/SystemVerilog 仿真器,能够将 RTL 代码编译为高性能的 C++ 仿真模型,提供快速的功能仿真和验证环境。

二、文件结构

verilator/
└── Elaborate.scala  - Verilator 详细化配置

三、核心实现

Elaborate.scala

该文件实现了 BuckyBall 系统的 Verilog 生成和详细化过程:

object Elaborate extends App {
  val config = new examples.toy.BuckyBallToyConfig
  val params = config.toInstance

  ChiselStage.emitSystemVerilogFile(
    new chipyard.harness.TestHarness()(config.toInstance),
    firtoolOpts = args,
    args = Array.empty  // 直接传递命令行
  )
}

Compiler Build Guide

Basic Workload Compilation

To build the workload, follow these steps:

mkdir build && cd build
cmake -G Ninja ..
ninja

Model-Level Testing

To enable model-level testing with specific models and architectures:

mkdir build && cd build
cmake -G Ninja .. \
    -DMODEL="lenet,resnet18,mobilenetv3,bert,stablediffusion,llama2,deepseekr1" \
    -DARCH="gemmini,buckyball"
ninja

注意:

  1. bert, whisper, stable-diffusion, llama2, DeepseekR1 模型下载需要提前配置好 huggingface 的访问
  2. whisper 暂不支持
  3. llama2 的模型下载需要额外填写 API-key 或使用缓存的凭证

Tile Dialect 重构文档

重构背景与目标

本次重构的核心目标是在 Linalg Dialect 和 Buckyball Dialect 之间引入一个新的中间层 —— Tile Dialect,以实现更清晰的职责分离和更好的代码组织。在原有架构中,从 linalg.matmul 到硬件指令的转换是通过 convert-linalg-to-buckyball 一步完成的,这导致 Buckyball Dialect 既要处理任意尺寸矩阵的切片逻辑,又要处理硬件级别的内存管理和计算调度,职责过于混杂。新架构将转换过程拆分为两个阶段:convert-linalg-to-tileconvert-tile-to-buckyball,使得每一层都有明确且单一的职责。

新架构设计

整个编译流程现在分为三个清晰的层次。首先是 Linalg 层,它表示高层次的线性代数操作,比如 linalg.matmul 表示任意尺寸的矩阵乘法,这一层不关心硬件约束。接下来是新引入的 Tile 层,它的核心职责是将任意尺寸的矩阵操作切分成符合硬件约束的固定大小块。Tile 层通过 tile.tile_matmul 操作来表示这种高层次的切片意图,具体的切片策略、循环生成和边界处理都在 convert-tile-to-buckyball 这个 pass 中实现。最后是 Buckyball 层,它专注于硬件级别的操作,buckyball.bb_matmul 接收已经切分好的固定大小矩阵块,负责生成精确的硬件指令序列,包括数据搬移(mvin/mvout)、计算调度(mul_warp16)和内存地址计算。

Tile Dialect 设计细节

Tile Dialect 定义了 TileMatMulOp 操作,它接受三个 memref 参数分别代表矩阵 A、B 和 C。这个操作的语义是:对输入的任意尺寸矩阵执行乘法,自动处理切片、填充和循环。在实现上,TileMatMulOp 会被 convert-tile-to-buckyball pass 转换成多个 buckyball.bb_matmul 操作以及相应的 memref.subview 操作。这个转换过程会考虑硬件的 scratchpad 大小限制、warp 和 lane 的并行度约束,生成最优的切片策略。Tile 层的设计理念是提供一个平台无关的中间表示,使得上层优化可以在不了解具体硬件细节的情况下进行矩阵操作的变换。

Buckyball Dialect 简化

在新架构中,Buckyball Dialect 被大幅简化。原来的 VecTileMatMulOpMergeTileMatMulOpMetaTileMatMulOpVecMulWarp16Op 这四个操作被统一为单一的 MatMulOp。这个简化是合理的,因为切片逻辑已经上移到 Tile 层,Buckyball 层只需要表达"对一个已经符合硬件约束的矩阵块执行硬件级乘法"这一个概念。buckyball.bb_matmul 的 lowering 过程会直接生成 LLVM intrinsics:首先通过 Mvin_IntrOp 将 A 和 B 矩阵加载到 scratchpad,然后根据 warp 和 lane 参数生成多个 Mul_Warp16_IntrOp 进行计算,最后通过 Mvout_IntrOp 将结果写回主内存。所有的地址计算、编码都在这个 lowering 过程中完成。

关键实现细节

在实现 convert-linalg-to-tile pass 时,核心逻辑非常简单:匹配 linalg.matmul 操作,直接替换为 tile.tile_matmul,传递相同的三个 memref 操作数。这个 pass 的作用主要是类型和语义的转换,表明我们从通用的线性代数操作域进入了面向硬件的 tile 操作域。

convert-tile-to-buckyball pass 是整个重构中最复杂的部分。它需要从 tile.tile_matmul 的操作数中提取矩阵的维度信息(M、K、N),然后根据硬件参数(dim、warp、lane)计算最优的切片策略。对于 K 维度,会按照 warp 大小进行切片;对于 M 和 N 维度,会考虑 scratchpad 的容量限制。每个切片对应一个 buckyball.bb_matmul 操作,切片之间通过 memref.subview 来创建矩阵的视图。特别需要注意的是边界情况的处理:当矩阵维度不能被切片大小整除时,需要计算最后一个切片的实际大小,避免越界访问。

在实现 BuckyballMatMulLowering 时,我们遇到了 MLIR 类型转换系统的一个重要概念:OpAdaptor。在 conversion pattern 中,原始操作的类型(比如 memref<32x16xi8>)在 lowering 过程中会被 TypeConverter 转换为 LLVM 类型(比如 LLVM 的 struct 类型)。OpAdaptor 提供的是转换后的值,而我们需要从原始操作中获取类型信息(比如 shape),因为这些静态信息在转换后可能不再以相同形式存在。因此,正确的做法是:从 matMulOp.getOperandTypes() 获取原始的 MemRefType 来提取 shape 信息,用于地址计算和循环生成;而对于实际的值操作(比如 ExtractAlignedPointerAsIndexOp),则使用原始的 memref value,因为 MLIR 的 memref 操作仍然需要 MemRefType。

另一个关键的设计决策是:MatMulOp 的 lowering 应该直接生成 intrinsic operations(Mvin_IntrOpMul_Warp16_IntrOpMvout_IntrOp),而不是生成 MvinOpMvoutOp 然后再等它们被 lower。这样做的原因是在 LLVM lowering 阶段,类型系统已经发生了转换,再创建高层次的 Buckyball 操作会导致类型不匹配的问题。直接生成 intrinsics 避免了多次类型转换,也使得代码更加清晰高效。参考 Gemmini dialect 的实现,我们采用了相同的策略。

测试体系

为了验证新架构的正确性,我们在 bb-tests/workloads/src/OpTest/tile/ 目录下创建了完整的测试用例。测试分为两类:分阶段测试和端到端测试。

tile-matmul.mlir 测试 Linalg 到 Tile 的转换,验证 linalg.matmul 是否正确转换为 tile.tile_matmul,这是最基础的类型转换测试。tile-to-buckyball.mlir 测试 Tile 到 Buckyball 的转换,验证切片逻辑是否正确,是否生成了正确数量的 buckyball.bb_matmul 操作和 memref.subview 操作。buckyball-to-llvm.mlir 测试 Buckyball MatMulOp 到 LLVM intrinsics 的转换,验证是否生成了正确的 buckyball.intr.bb_mvinbuckyball.intr.bb_mul_warp16buckyball.intr.bb_mvout 指令序列。

end-to-end.mlir 是最重要的测试,它测试完整的转换流程:从 linalg.matmul 开始,依次经过 -convert-linalg-to-tile-convert-tile-to-buckyball-lower-buckyball 三个 pass,最终生成 LLVM intrinsics。这个测试确保整个 pipeline 的每个环节都能正常工作,并且环节之间的衔接没有问题。

Pass 注册与工具链集成

新增的两个 pass 需要在多个地方进行注册。首先在 InitAll.cpp 中注册 pass 的创建函数 registerLowerLinalgToTilePass()registerLowerTileToBuckyballPass(),同时注册 buddy::tile::TileDialect。在 buddy-opt 工具中,需要在 dialect registry 中添加 buddy::tile::TileDialect,使得工具能够识别和解析 tile dialect 的操作。CMake 构建系统中,需要将新的库 BuddyTileLowerLinalgToTilePassLowerTileToBuckyballPass 添加到链接依赖中,并确保依赖关系正确。

特别值得注意的是,在 LegalizeForLLVMExport.cppconfigureBuckyballLegalizeForExportTarget 函数中,我们需要添加 target.addLegalDialect<memref::MemRefDialect>()target.addLegalDialect<arith::ArithDialect>(),因为在 MatMulOp 的 lowering 过程中会使用 memref 和 arith 操作。如果不将这些 dialect 标记为 legal,conversion framework 会尝试 lower 这些操作,导致类型转换冲突。

Agent 工作流

BuckyBall 框架中的 AI 助手工作流,提供与 AI 模型的对话交互功能。

API 使用说明

chat

端点: POST /agent/chat

功能: 与 AI 助手进行对话交互

参数:

  • message [必选] - 发送给 AI 的消息内容
  • model - 使用的 AI 模型,默认 "deepseek-chat"

示例:

# 基本对话
bbdev agent --chat "--message 'Hello, can you help me with BuckyBall development?'"

# 指定模型
bbdev agent --chat "--message 'Explain this Scala code' --model deepseek-chat"

# 代码分析
bbdev agent --chat "--message 'Please analyze this Chisel module and suggest optimizations'"

响应:

{
  "traceId": "unique-trace-id",
  "status": "success"
}

注意事项

  • 需要配置 AI 模型的 API 密钥
  • 响应采用流式输出
  • 注意消息长度限制

Compiler 工作流

BuckyBall 框架中的编译器构建工作流,用于构建 BuckyBall 编译器工具链。

API 使用说明

build

端点: POST /compiler/build

功能: 构建 BuckyBall 编译器

参数: 无特定参数

示例:

bbdev compiler --build

响应:

{
  "status": 200,
  "body": {
    "success": true,
    "processing": false,
    "return_code": 0
  }
}

注意事项

  • 确保系统具备必要的构建工具和依赖

Doc-Agent 工作流

BuckyBall 框架中的文档生成工作流,提供自动化的代码文档生成功能。

API 使用说明

generate

端点: POST /doc/generate

功能: 为指定目录生成文档

参数:

  • target_path [必选] - 目标目录路径
  • mode [必选] - 生成模式,可选值: "create", "update"

示例:

# 为指定目录创建新文档
bbdev doc --generate "--target_path arch/src/main/scala/framework --mode create"

# 更新现有文档
bbdev doc --generate "--target_path arch/src/main/scala/framework --mode update"

响应:

{
  "traceId": "unique-trace-id",
  "status": "success",
  "message": "Documentation generated successfully"
}

支持的文档类型

  • RTL 硬件文档
  • 测试文档
  • 脚本文档
  • 仿真器文档
  • 工作流文档

注意事项

  • 需要配置 AI 模型的 API 密钥
  • 生成的文档会自动集成到 mdBook 系统中
  • 支持符号链接管理和 SUMMARY.md 自动更新

FireSim 工作流

BuckyBall 框架中的 FireSim FPGA 仿真工作流,提供基于 FPGA 的硬件仿真环境。

API 使用说明

buildbitstream

端点: POST /firesim/buildbitstream

功能: 构建 FPGA 比特流文件

参数: 无特定参数

示例:

bbdev firesim --buildbitstream

infrasetup

端点: POST /firesim/infrasetup

功能: 设置 FireSim 基础设施

参数: 无特定参数

示例:

bbdev firesim --infrasetup

runworkload

端点: POST /firesim/runworkload

功能: 在 FireSim 上运行工作负载

参数: 无特定参数

示例:

bbdev firesim --runworkload

典型工作流程

# 1. 构建比特流
bbdev firesim --buildbitstream

# 2. 设置基础设施
bbdev firesim --infrasetup

# 3. 运行工作负载
bbdev firesim --runworkload

注意事项

  • 比特流构建需要数小时时间
  • infrasetup 需要配置云计算资源

FuncSim 工作流

BuckyBall 框架中的功能仿真工作流,提供快速的功能验证环境。

API 使用说明

build

端点: POST /funcsim/build

功能: 构建功能仿真器

参数: 无特定参数

示例:

bbdev funcsim --build

sim

端点: POST /funcsim/sim

功能: 运行功能仿真

参数:

  • binary - 要仿真的二进制文件路径
  • ext - 扩展参数

示例:

# 基本仿真
bbdev funcsim --sim "--binary /path/to/test.elf"

# 带扩展参数
bbdev funcsim --sim "--binary /path/to/test.elf --ext additional_args"

典型工作流程

# 1. 构建功能仿真器
bbdev funcsim --build

# 2. 运行仿真
bbdev funcsim --sim "--binary ${buckyball}/bb-tests/workloads/build/src/CTest/ctest_basic-baremetal"

注意事项

  • 只提供功能级别的仿真,不包含时序信息
  • 确保二进制文件路径正确且可访问

Marshal 工作流

BuckyBall 框架中的 Marshal 工作流,用于构建和启动 Marshal 组件。

API 使用说明

build

端点: POST /marshal/build

功能: 构建 Marshal 组件

参数: 无特定参数

示例:

bbdev marshal --build

launch

端点: POST /marshal/launch

功能: 启动 Marshal 服务

参数: 无特定参数

示例:

bbdev marshal --launch

典型工作流程

# 1. 构建 Marshal
bbdev marshal --build

# 2. 启动 Marshal 服务
bbdev marshal --launch

响应格式

所有 API 调用返回统一格式:

{
  "status": 200,
  "body": {
    "success": true,
    "processing": false,
    "return_code": 0
  }
}

Sardine 工作流

BuckyBall 框架中的 Sardine 工作流,用于运行 Sardine 相关任务。

API 使用说明

run

端点: POST /sardine/run

功能: 运行 Sardine 任务

参数:

  • workload - 指定要运行的工作负载

示例:

# 运行指定工作负载
bbdev sardine --run "--workload /path/to/workload"

# 运行默认工作负载
bbdev sardine --run

响应:

{
  "status": 200,
  "body": {
    "success": true,
    "processing": false,
    "return_code": 0
  }
}

UVM 工作流

BuckyBall 框架中的 UVM (Universal Verification Methodology) 工作流,用于构建和运行 UVM 验证环境。

API 使用说明

builddut

端点: POST /uvm/builddut

功能: 构建 DUT (Design Under Test)

参数:

  • jobs - 并行构建任务数,默认 16

示例:

# 使用默认并行数构建 DUT
bbdev uvm --builddut

# 指定并行任务数
bbdev uvm --builddut "--jobs 8"

build

端点: POST /uvm/build

功能: 构建 UVM 可执行文件

参数:

  • jobs - 并行构建任务数,默认 16

示例:

# 使用默认并行数构建 UVM
bbdev uvm --build

# 指定并行任务数
bbdev uvm --build "--jobs 8"

典型工作流程

# 1. 构建 DUT
bbdev uvm --builddut

# 2. 构建 UVM 环境
bbdev uvm --build

响应格式:

{
  "status": 200,
  "body": {
    "success": true,
    "processing": false,
    "return_code": 0
  }
}

Verilator 仿真工作流

BuckyBall 框架中基于 Verilator 的硬件仿真工作流,提供从 RTL 生成到仿真执行的完整自动化流程。Verilator 是一个高性能的 Verilog 仿真器,支持快速的功能验证和性能分析。

二、原始API使用说明

run

端点: POST /verilator/run

功能: 执行完整流程。清理build目录,生成Verilog,然后编译Verilator为仿真文件,并直接运行仿真

参数:

  • jobs - 并行编译任务数
    • 默认值: 16
  • binary [必选] - 测试二进制文件路径
    • 默认值: ""

示例:

# bbdev封装
bbdev verilator --run "jobs 256 --binary ${buckyball}/bb-tests/workloads/build/src/CTest/ctest_mvin_mvout_alternate_test_singlecore-baremetal --batch"

# 原始命令
curl -X POST http://localhost:5000/verilator/run -H "Content-Type: application/json" -d '{"jobs": 8, "binary": "/home/user/test.elf"}'

clean

端点: POST /verilator/clean

功能: 清空build文件夹

参数: 无

示例:

curl -X POST http://localhost:5000/verilator/clean

verilog

端点: POST /verilator/verilog

功能: 仅生成Verilog代码,不进行编译和仿真

参数: 无

示例:

curl -X POST http://localhost:5000/verilator/verilog -d '{"jobs": 8}'

build

端点: POST /verilator/build

功能: 将verilog源文件和cpp源文件编译为可执行仿真文件

参数:

  • jobs - 并行编译任务数
    • 默认值: 16

示例:

curl -X POST http://localhost:5000/verilator/build -d '{"jobs": 16}'

sim

端点: POST /verilator/sim

功能: 运行已存在的仿真可执行文件

参数:

  • binary[必选] - 自定义测试二进制文件路径

示例:

curl -X POST http://localhost:5000/verilator/sim \
  -H "Content-Type: application/json" \
  -d '{"binary": "/home/user/test_program.elf"}'

二、开发者文档

目录结构

steps/verilator/
├── 00_start_node_noop_step.py      # 工作流入口节点定义
├── 00_start_node_noop_step.tsx     # 前端UI组件
├── 01_run_api_step.py              # 完整工作流API入口
├── 01_clean_api_step.py            # 清理API端点
├── 01_verilog_api_step.py          # Verilog生成API端点
├── 01_build_api_step.py            # 编译API端点
├── 01_sim_api_step.py              # 仿真API端点
├── 02_clean_event_step.py          # 清理构建目录
├── 03_verilog_event_step.py        # Verilog代码生成
├── 04_build_event_step.py          # Verilator编译
├── 05_sim_event_step.py            # 仿真运行
├── 99_complete_event_step.py       # 完成处理
├── 99_error_event_step.py          # 错误处理
└── README.md                       # 本文档

工作流步骤详解

1. 入口节点 (00_start_node_noop_step.py)

  • 类型: noop 节点
  • 功能: 提供UI界面入口
  • 前端: "Start Build Verilator"按钮

2. API端点

  • 完整工作流API (01_run_api_step.py): /verilatorverilator.run
  • 清理API (01_clean_api_step.py): /verilator/cleanverilator.clean
  • Verilog生成API (01_verilog_api_step.py): /verilator/verilogverilator.verilog
  • 编译API (01_build_api_step.py): /verilator/buildverilator.build
  • 仿真API (01_sim_api_step.py): /verilator/simverilator.sim

3. 清理步骤 (02_clean_event_step.py)

  • 类型: event 步骤
  • 订阅: verilator.run, verilator.clean
  • 发出: verilator.verilog, verilator.complete
  • 功能: 删除build目录,为工作流或单独操作服务

4. Verilog生成 (03_verilog_event_step.py)

  • 类型: event 步骤
  • 订阅: verilator.verilog
  • 发出: verilator.build, verilator.complete
  • 功能: 使用mill生成Verilog代码到build目录

5. Verilator编译 (04_build_event_step.py)

  • 类型: event 步骤
  • 订阅: verilator.build
  • 发出: verilator.sim, verilator.complete
  • 功能: 编译Verilog和C++源文件为可执行仿真文件

6. 仿真运行 (05_sim_event_step.py)

  • 类型: event 步骤
  • 订阅: verilator.sim
  • 发出: verilator.complete
  • 功能: 运行仿真,支持自定义binary参数

7. 完成处理 (99_complete_event_step.py)

  • 类型: event 步骤
  • 订阅: verilator.complete
  • 功能: 打印成功消息,标记工作流完成

8. 错误处理 (99_error_event_step.py)

  • 类型: event 步骤
  • 订阅: verilator.error
  • 功能: 打印错误消息,处理工作流异常

工作流程图

graph TD;
    API[POST /verilator<br/>完整工作流] --> RUN[verilator.run]

    CLEAN_DIRECT[verilator.clean<br/>单步清理] --> CLEAN_STEP[02_clean_event_step]
    VERILOG_DIRECT[verilator.verilog<br/>单步生成] --> VERILOG_STEP[03_verilog_event_step]
    BUILD_DIRECT[verilator.build<br/>单步编译] --> BUILD_STEP[04_build_event_step]
    SIM_DIRECT[verilator.sim<br/>单步仿真] --> SIM_STEP[05_sim_event_step]

    RUN --> CLEAN_STEP
    CLEAN_STEP --> |工作流模式| VERILOG_STEP
    CLEAN_STEP --> |单步模式| COMPLETE[verilator.complete]

    VERILOG_STEP --> |工作流模式| BUILD_STEP
    VERILOG_STEP --> |单步模式| COMPLETE

    BUILD_STEP --> |工作流模式| SIM_STEP
    BUILD_STEP --> |单步模式| COMPLETE

    SIM_STEP --> COMPLETE

    COMPLETE --> COMPLETE_STEP[99_complete_event_step]

    CLEAN_STEP -.-> |错误| ERROR[verilator.error]
    VERILOG_STEP -.-> |错误| ERROR
    BUILD_STEP -.-> |错误| ERROR
    SIM_STEP -.-> |错误| ERROR

    ERROR --> ERROR_STEP[99_error_event_step]

    classDef apiNode fill:#e1f5fe
    classDef eventNode fill:#f3e5f5
    classDef stepNode fill:#e8f5e8
    classDef endNode fill:#fff3e0

Workload 工作流

BuckyBall 框架中的工作负载构建工作流,用于构建测试工作负载和基准程序。

API 使用说明

build

端点: POST /workload/build

功能: 构建工作负载

参数:

  • workload - 指定要构建的工作负载名称

示例:

# 构建指定工作负载
bbdev workload --build "--workload test_program"

# 构建所有工作负载
bbdev workload --build

响应:

{
  "status": 200,
  "body": {
    "success": true,
    "processing": false,
    "return_code": 0
  }
}

注意事项

  • 工作负载源码位于 bb-tests/workload 目录
  • 构建结果通常输出到 bb-tests/workloads/build 目录

贡献者

感谢所有为 BuckyBall 项目做出贡献的开发者和研究人员。

核心开发团队

BuckyBall 项目由 DangoSys 团队主导开发,致力于构建高性能的领域特定架构框架。

贡献方式

我们欢迎各种形式的贡献:

代码贡献

  • 硬件架构设计和优化
  • 软件工具链改进
  • 测试用例和基准程序
  • 文档编写和维护

问题反馈

  • Bug 报告和修复建议
  • 功能需求和改进建议
  • 性能优化建议
  • 使用体验反馈

学术合作

  • 研究论文和技术报告
  • 会议演讲和技术分享
  • 开源社区推广

参与指南

  1. Fork 项目: 从 GitHub 创建项目分支
  2. 本地开发: 按照文档搭建开发环境
  3. 提交更改: 遵循代码规范和提交格式
  4. 创建 PR: 详细描述更改内容和测试结果
  5. 代码审查: 配合维护者完成代码审查流程

联系方式

致谢

特别感谢以下开源项目和社区:

  • Buddy-Compiler 开发团队
  • Chipyard 项目
  • RISCV 基金会
  • 所有测试用户和反馈者