Skip to content

Repository files navigation

Azurite Logo

Azurite

Bluespec Verilog PowerISA Core.

Azurite is named for the vivid blue copper mineral found in Arizona's copper country. The name ties the project to the repo owner's home state, nods to Bluespec through its deep blue color, and points toward copper itself: the metal of conductors, traces, and electrical power. Like the mineral, the core is meant to reward close inspection: compact, structured, and transparent enough that behavior can be traced from ISA intent through RTL and verification.

This project takes inspiration from the Bluespec Piccolo / Flute projects. It is modeled and structured similarly to shorten bring-up of the initial core and verification flow.

Project status

Azurite is in early bring-up. The main remaining architectural bring-up items are a proper instruction prefetcher and complete architectural interrupt/exception entry and return sequencing, including external interrupt delivery, saved-state updates, vector dispatch, and return-to-program flow. The current repository contains:

  • Bluespec SystemVerilog source for the core shell, CPU stages, register bank, execution units, memory stage, cache shims, AXI4 fabric types, and PowerISA instruction declarations under src_Core/.
  • A cocotb/Verilator testbench under src_Testbench/.
  • PowerPC bring-up programs and payload artifacts under programs/bringup/.
  • PowerISA reference links, instruction implementation notes, architecture notes, implementation checklists, and extracted Appendix G CSV files under docs/PowerPC/.
  • A Docker-based build environment with Bluespec bsc, Verilator, cocotb, PowerPC cross compilers, and QEMU.
  • QEMU oracle tooling under tools/qemu_oracle/ for C/C++ source compilation, randomized instruction generation, signature checking, and retired-instruction trace comparison against RTL.

Repository layout

.
├── src_Core/
│   ├── Core/                 # mkCore top-level and external interface
│   ├── CPU/                  # PC, IF, ID, EX, MEM, RB, IC/DC, and pipeline modules
│   ├── ISA/                  # PowerISA type/form/opcode declarations
│   ├── Fabrics/              # AXI4 type and fabric definitions
│   └── BSV_Additional_Libs/  # local Bluespec utility modules
├── src_Testbench/            # cocotb testbench and simulation Makefile
├── programs/                 # bare-metal and QEMU bring-up programs
├── docs/PowerPC/             # ISA references and decoded instruction tables
├── docker/entrypoint.sh      # container startup helper
├── Dockerfile                # full toolchain image
└── Makefile                  # RTL generation and simulation entry points

What you need to begin

1. Development environment

The recommended starting point is the Docker image because the native dependency set is large.

You need:

  • Docker with BuildKit support.
  • GitHub SSH access if you build the current Dockerfile unchanged, because it clones this private repo during image build.
  • A working SSH agent with a GitHub key loaded.

The Docker image installs/builds:

  • Ubuntu 22.04 base tools.
  • Bluespec compiler bsc from B-Lang-org/bsc, default BSC_REF=2025.01.1.
  • Verilator from source, default VERILATOR_TAG=v5.036.
  • Python packages: pytest, pytest-xdist, cocotb, cocotb-test, cocotbext-axi, pyelftools.
  • PowerPC cross toolchains and binutils:
    • powerpc-linux-gnu-*
    • powerpc64-linux-gnu-*
    • powerpc64le-linux-gnu-*
  • QEMU 10.1.0 with PowerPC softmmu/user targets, including qemu-ppc64 and qemu-ppc64le for the oracle flow.
  • GTKWave for waveform viewing.

2. Build the Docker image

From the repository root:

$env:DOCKER_BUILDKIT = "1"
ssh-add -l
ssh-add $env:USERPROFILE\.ssh\id_ed25519

docker build --ssh default -t azurite-build .

On Linux/macOS:

export DOCKER_BUILDKIT=1
ssh-add -l || ssh-add ~/.ssh/id_ed25519

docker build --ssh default -t azurite-build .

Note: the current Dockerfile clones git@github.com:mgjaggers/Azurite.git into /workspace/repo/Azurite during the image build. If you want faster local iteration, consider changing the Dockerfile later to install only the toolchain and then bind-mount this checkout into the container.

3. Start the container

docker run --rm -it azurite-build

For local iteration against your working tree instead of the copy baked into the image:

docker run --rm -it \
  -v "$PWD":/workspace/repo/Azurite \
  -w /workspace/repo/Azurite \
  azurite-build

4. Generate Verilog from Bluespec

Inside the container, from the repository root:

make

This compiles src_Core/Core/Core.bsv with top module mkCore and writes the selected Verilog variant to:

build/rtl/mkCore.v

build/rtl/mkCore.v is the active generated core. Each build command below overwrites that file with the selected configuration. Bluespec intermediate artifacts are kept in variant-specific directories under build/bld/, and build/rtl/core_params.txt records the compile-time defines used for the current mkCore.v.

Default Configuration Make Commands

Goal Command
Default core: big-endian, passthrough cache shims make
Explicit big-endian passthrough core make CORE_ENDIAN=be
Little-endian passthrough core make CORE_ENDIAN=le
Big-endian core with concrete L1 I-cache and D-cache make CORE_ENDIAN=be ENABLE_L1_CACHES=1
Little-endian core with concrete L1 I-cache and D-cache make CORE_ENDIAN=le ENABLE_L1_CACHES=1
Big-endian core with only concrete I-cache make CORE_ENDIAN=be ENABLE_ICACHE=1
Big-endian core with only concrete D-cache make CORE_ENDIAN=be ENABLE_DCACHE=1

The same switches also work with the explicit RTL target:

make CORE_ENDIAN=le ENABLE_L1_CACHES=1 build/rtl/mkCore.v

Build Switches

Make variable Default Effect
CORE_ENDIAN be Selects core memory byte order. Use be/big/big-endian or le/little/little-endian.
ENABLE_L1_CACHES 0 Enables both concrete L1 caches.
ENABLE_ICACHE 0 Enables only the concrete instruction cache unless ENABLE_L1_CACHES=1.
ENABLE_DCACHE 0 Enables only the concrete data cache unless ENABLE_L1_CACHES=1.

These switches map to the following Bluespec defines:

FABRIC64                         default core/fabric build
AZURITE_CORE_LITTLE_ENDIAN        selected by CORE_ENDIAN=le
AZURITE_ENABLE_ICACHE             selected by ENABLE_ICACHE=1 or ENABLE_L1_CACHES=1
AZURITE_ENABLE_DCACHE             selected by ENABLE_DCACHE=1 or ENABLE_L1_CACHES=1

The instruction and data memory paths always pass through cache-facing shim interfaces. With the default settings those shims are AXI passthrough modules. With cache defines enabled, the concrete caches are 4 KiB, 2-way set associative, 32-byte line caches with round-robin replacement. The I-cache is read-allocate. The D-cache is read-allocate, write-through, and write-allocate; a D-cache store miss refills the line, merges the store into the cached copy, installs the line, then performs the write-through to AXI.

Normal halfword/word loads and stores follow CORE_ENDIAN; byte-reverse instructions invert the normal transfer order for that mode. make sim passes CORE_ENDIAN into cocotb and defaults AZURITE_QEMU_ENDIAN to the same value, so the compiler, QEMU target, payload, signature parsing, and RTL memory byte ordering stay matched. If you manually override AZURITE_QEMU_ENDIAN, keep it matched to CORE_ENDIAN; intentional mismatches will fail byte-lane-sensitive tests such as lbz_byte_lane_1.

See docs/PowerPC/Architecture.md for the current stage and cache-shim architecture.

5. Run Simulation and Verification

The default simulation builds and runs the big-endian passthrough core:

make sim

Common simulation variants:

# Little-endian core and matching LE payload/oracle
make CORE_ENDIAN=le sim

# Concrete L1 caches
make CORE_ENDIAN=be ENABLE_L1_CACHES=1 sim
make CORE_ENDIAN=le ENABLE_L1_CACHES=1 sim

# Focused cocotb test
make CORE_ENDIAN=le ENABLE_L1_CACHES=1 TESTCASE=test_integer_memory_load_store_instructions sim

The default simulation parameters are controlled by the top-level Makefile:

CORE_ENDIAN ?= be
PROGRAM   ?= programs/bringup/bare-metal/$(CORE_ENDIAN_CANON)_payload.elf
LOAD_BASE ?= 0x80000000
MEM_SIZE  ?= 0x02000000
BUS_ROLE  ?= master

Override them on the command line when needed:

make CORE_ENDIAN=le sim PROGRAM=$(pwd)/programs/bringup/bare-metal/le_payload.elf LOAD_BASE=0x80000000

The cocotb testbench can also compile C/C++ sources into PowerPC ELFs, load those ELFs into the RTL memory model, run the same ELFs under QEMU, and compare final signatures or retired-instruction traces.

Run one C source through the ELF oracle test:

AZURITE_ORACLE_SOURCE=programs/oracle/longer_signature.c \
AZURITE_ORACLE_SIGNATURE_WORDS=8 \
AZURITE_ORACLE_MAX_CYCLES=1600 \
make sim TESTCASE=test_qemu_oracle_elf_source_from_env

Run randomized source generation and QEMU-vs-RTL checks:

# Generate one deterministic random C source without running RTL
python3 tools/qemu_oracle/random_source.py --seed 1 --ops 32 --signature-words 16 --profile stable

# Run a randomized signature check
python3 tools/qemu_oracle/run_random.py --seed 1 --count 1 --ops 32 --signature-words 16 --profile stable

# Run a short seed sweep with retired-instruction trace comparison
python3 tools/qemu_oracle/run_random.py --seed 1 --count 10 --ops 64 --signature-words 16 --profile stable --trace

Generate and run per-instruction stress cases:

# Generate 100 unrolled instances of each target instruction corner case
python3 tools/qemu_oracle/instruction_stress.py --repeat 100 --signature-words 16

# Run one instruction family through QEMU-vs-RTL trace/register checking
python3 tools/qemu_oracle/instruction_stress.py --instruction addi --repeat 100 --signature-words 16 --run --trace

Generated random sources and ELFs are written under build/qemu_oracle/. Generated stress sources and their manifest.json are written under build/qemu_oracle/instruction_stress/. The QEMU user-mode oracle defaults to QEMU_CPU=power10; override with AZURITE_QEMU_CPU if needed. Direct Python entry points inherit your shell environment, so set CORE_ENDIAN and AZURITE_QEMU_ENDIAN together when running LE cases outside make sim.

Little-endian QEMU stress runs exclude lmw/stmw because GNU rejects those mnemonics in little-endian mode; deterministic cocotb coverage still exercises the RTL implementation.

Coverage collection combines deterministic cocotb tests and QEMU stress tests:

make coverage
make coverage-smoke

6. Run the PowerPC/QEMU Bring-Up Payload

For the existing bare-metal payload:

cd programs/bringup
qemu-system-ppc64 -M powernv10 -cpu power10 -nographic -kernel ./bare-metal/be_payload.elf

See also:

programs/README.md
programs/bringup/bare-metal/README.md

Native setup checklist

If you do not use Docker, install equivalent native tools:

  • make
  • bsc
  • verilator
  • python3, pip, and cocotb-config
  • Python packages: cocotb, cocotb-test, cocotbext-axi, pyelftools, pytest
  • PowerPC cross compilers/binutils for big-endian and little-endian payloads
  • qemu-system-ppc64 / PowerPC QEMU targets
  • Optional: gtkwave

Quick sanity checks:

bsc -v
verilator --version
cocotb-config --version
powerpc64le-linux-gnu-gcc --version
qemu-system-ppc64 --version

Troubleshooting

  • bsc: command not found — use the Docker image or install Bluespec bsc natively.
  • cocotb-config: No such file or directory — install cocotb in the active Python environment.
  • No rule to make target '/Makefile.sim'cocotb-config --makefiles failed, usually because cocotb is not installed.
  • fatal: cannot exec 'ssh' while building/cloning — verify SSH is installed, an SSH agent is running, and your GitHub key is loaded.
  • Docker cannot access the daemon — start Docker Desktop/daemon, or on Linux try sudo docker ... depending on your setup.

References

License

See LICENSE.

About

Bluespec Verilog PowerISA core

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages