Thank you for your interest in contributing to FROST. We welcome contributions of all sizes, from documentation fixes to new features.
Quick start: Fork the repo, make your changes, run the tests, and open a PR. Pre-commit hooks enforce formatting automatically.
This document provides guidelines for contributors. The detailed style sections are primarily for reference.
- Project Overview
- Getting Started
- Development Workflow
- Coding Style Guide
- Testing Requirements
- Adding New Components
- Types of Contributions
- Code Review Process
- Questions?
FROST is a 6-stage pipelined RISC-V processor implementing RV32IMACB with full machine-mode privilege support. Understanding the architecture helps you contribute effectively:
IF → PD → ID → EX → MA → WB
│ │ │ │ │ └─ Write-back to register file
│ │ │ │ └─ Memory access (load/store completion)
│ │ │ └─ Execute (ALU, branch resolution)
│ │ └─ Instruction decode, register file read
│ └─ Pre-decode (C-extension decompression)
└─ Instruction fetch, branch prediction
- Portability: No vendor-specific primitives in core CPU (board wrappers may use them)
- Timing optimization: Critical paths carefully managed with registered outputs
- Comprehensive verification: Cocotb-based testing with 16,000+ random instructions
- Multi-simulator support: Tested on Verilator and Icarus Verilog
| Address Range | Description |
|---|---|
0x0000_0000 - 0x0000_FFFF |
Main memory (64KB) |
0x4000_0000 |
MMIO region (UART, FIFOs) |
Before contributing, ensure you have the required tools installed. See the main README for validated versions.
Required tools:
- RISC-V GCC (
riscv-none-elf-gcc) - for compiling bare-metal software - Cocotb - Python-based verification framework
- Simulator (one or more): Verilator or Icarus Verilog
- Yosys - for synthesis verification
-
Fork and clone the repository:
git clone https://github.com/YOUR_USERNAME/frost.git cd frost -
Initialize submodules (required for FreeRTOS demo):
git submodule update --init --recursive
-
Verify your setup by running the test suite:
pytest tests/test_run_cocotb.py -s
-
Build sample software to verify toolchain:
cd sw/apps/hello_world && make
-
Create a new branch for your feature or fix:
git checkout -b feature/your-feature-name
-
Run the existing test suite to ensure everything passes:
pytest tests/ -s
- Ensure all tests pass
- Update documentation if your change affects user-facing behavior
- Add Apache 2.0 license headers to new files (see License Headers)
- Write a clear commit message:
Short summary (50 chars or less) More detailed explanation if needed. Explain the problem this commit solves and why this approach was chosen. - Push your branch and open a pull request
- Respond to review feedback
Pre-commit hooks automatically enforce formatting:
- SystemVerilog: Verible formatter
- C: clang-format and clang-tidy
- Python: Ruff formatter and linter, mypy for type checking
The guidelines below document the project conventions for reference.
All source files must include the Apache 2.0 license header. Use the appropriate comment style for each language:
SystemVerilog/Verilog:
/*
* Copyright 2024-2025 Two Sigma Open Source, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* ...
*/Python:
# Copyright 2024-2025 Two Sigma Open Source, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# ...C:
// Copyright 2024-2025 Two Sigma Open Source, LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// ...| Element | Convention | Example |
|---|---|---|
| Input ports | i_ prefix |
i_clk, i_rst, i_data_valid |
| Output ports | o_ prefix |
o_result, o_mem_addr |
| Internal signals | No prefix, snake_case |
data_valid, next_state |
| Registered signals | *_registered suffix |
branch_target_registered |
| Type names | CamelCase or *_t suffix |
interrupt_t, PipelineState |
| Enum values | UPPER_CASE |
OPC_ADD, STATE_IDLE |
| Parameters | UPPER_CASE |
XLEN, MEM_DEPTH |
| Module names | snake_case |
hazard_resolution_unit |
- Indentation: 2 spaces (no tabs)
- Line length: Keep reasonable (~100 chars), break long port lists
- Alignment: Align port assignments and signal declarations for readability
/*
* Example Module - Brief description of purpose
*
* Detailed explanation of functionality, interfaces, and behavior.
* Include ASCII diagrams for complex data flow.
*/
module example_unit #(
parameter int unsigned DATA_WIDTH = 32,
parameter int unsigned DEPTH = 16
) (
input logic i_clk,
input logic i_rst,
input logic i_valid,
input logic [DATA_WIDTH-1:0] i_data,
output logic o_ready,
output logic [DATA_WIDTH-1:0] o_result
);
// =========================================================================
// Internal Signals
// =========================================================================
logic [DATA_WIDTH-1:0] data_registered;
logic valid_registered;
// =========================================================================
// Sequential Logic
// =========================================================================
always_ff @(posedge i_clk) begin
if (i_rst) begin
data_registered <= '0;
valid_registered <= 1'b0;
end else begin
data_registered <= i_data;
valid_registered <= i_valid;
end
end
// =========================================================================
// Combinational Logic
// =========================================================================
always_comb begin
o_ready = ~valid_registered;
o_result = data_registered;
end
endmodule- Module headers: Include purpose, behavior, and ASCII diagrams for complex modules
- Section dividers: Use
// =====to organize logical sections - Inline comments: Explain "why" not "what"; highlight timing-critical paths
- Timing notes: Document critical path considerations
// This comparison is timing-critical: keep as single-cycle operation
// Alternative: pipeline if frequency target increases
assign cache_hit = (tag_stored == tag_incoming);- No vendor-specific primitives in core CPU (
hw/rtl/cpu_and_mem/) - Synthesis attributes are acceptable for optimization hints
- Board-specific code goes in
boards/directory - Library primitives (
hw/rtl/lib/) should be generic or have vendor alternatives
- Follow PEP 8 style guidelines
- Use type hints on all public functions
- Use dataclasses for configuration objects
- Maximum line length: 100 characters
| Element | Convention | Example |
|---|---|---|
| Functions/variables | snake_case |
encode_instruction() |
| Classes | CamelCase |
InstructionEncoder |
| Constants | UPPER_CASE |
MASK32, XLEN |
| Private functions | _underscore_prefix |
_validate_input() |
| Type aliases | CamelCase with _t |
RegisterValue_t |
"""Module docstring explaining purpose.
Detailed description of module functionality.
Usage:
>>> from config import MASK32
"""
# Standard library imports
from dataclasses import dataclass
from typing import Final, Optional
# Third-party imports
import cocotb
# Local imports
from config import XLEN
# ============================================================================
# Constants
# ============================================================================
MASK32: Final[int] = 0xFFFF_FFFF
"""Mask for 32-bit values."""
# ============================================================================
# Classes
# ============================================================================
@dataclass
class TestConfig:
"""Configuration for test execution.
Attributes:
num_instructions: Number of random instructions to generate.
seed: Random seed for reproducibility.
"""
num_instructions: int = 1000
seed: Optional[int] = None
# ============================================================================
# Functions
# ============================================================================
def encode_instruction(opcode: int, rd: int, rs1: int) -> int:
"""Encode a RISC-V instruction.
Args:
opcode: 7-bit opcode field.
rd: Destination register (0-31).
rs1: Source register 1 (0-31).
Returns:
32-bit encoded instruction.
Raises:
ValueError: If register indices are out of range.
"""
if not (0 <= rd <= 31 and 0 <= rs1 <= 31):
raise ValueError("Register index out of range")
return (opcode & 0x7F) | ((rd & 0x1F) << 7) | ((rs1 & 0x1F) << 15)- Use
pytestwith Cocotb integration - Document test purpose and coverage
- Use appropriate markers:
@pytest.mark.cocotb,@pytest.mark.synthesis - Keep test configuration explicit (pass config objects, avoid global state)
- Indentation: 4 spaces
- Use
stdint.htypes for hardware-related variables (uint32_t,uint8_t) - Use
volatilefor MMIO pointers - Minimize dynamic memory allocation
| Element | Convention | Example |
|---|---|---|
| Functions | snake_case |
uart_printf(), read_timer() |
| Variables | snake_case |
timer_value, byte_count |
| Constants/Macros | UPPER_CASE |
UART_BASE_ADDR, MAX_BUFFER_SIZE |
| Types | snake_case_t |
uart_config_t |
/**
* Brief description of function.
*
* Detailed explanation of behavior, parameters, and return value.
*
* @param value Input value to process
* @return Processed result
*/
uint32_t process_value(uint32_t value)
{
volatile uint32_t *status_reg = (volatile uint32_t *)MMIO_STATUS_ADDR;
// Wait for hardware ready (explain why this wait is needed)
while ((*status_reg & STATUS_READY_MASK) == 0) {
// Spin wait
}
return value * 2;
}- No standard library (
-nostdlib,-ffreestanding) - Use provided libraries in
sw/lib/or implement minimal versions - Test on actual hardware when possible
- Memory layout defined in
sw/common/link.ld
- Use
?=for overridable defaults - Add comments explaining non-obvious build steps
- Use descriptive variable names
- Group related variables and targets
# RISC-V toolchain configuration
RISCV_PREFIX ?= riscv-none-elf-
CC := $(RISCV_PREFIX)gcc
# Compilation flags
# -march: Specify ISA extensions (I=base, M=multiply, A=atomics, C=compressed)
# -mabi: ABI (ilp32 = 32-bit int/long/pointer)
CFLAGS := -march=rv32imac -mabi=ilp32 -O3 -Wall -Wextra
# Build targets
.PHONY: all clean
all: $(TARGET).elf $(TARGET).hex
clean:
rm -f *.o *.elf *.hex- Add shebang:
#!/bin/bash - Use
set -efor error handling - Quote variables:
"$variable" - Add usage comments at top of file
- Add comments explaining each major step
- Validate inputs and provide clear error messages
- Use descriptive variable names
- Handle errors gracefully with informative messages
The project uses pytest markers to categorize tests:
| Marker | Description | When to Run |
|---|---|---|
@pytest.mark.cocotb |
Cocotb simulation tests | RTL changes |
@pytest.mark.synthesis |
Yosys synthesis tests | RTL changes |
| (default) | Pure Python tests | Python changes |
Run the full CPU test suite:
# Full random instruction test (16,000+ instructions)
pytest tests/test_run_cocotb.py::TestCPU -s
# ISA compliance tests
pytest tests/test_run_cocotb.py::TestRealPrograms::test_frost_isa_test -s
# Synthesis verification
pytest tests/test_run_yosys.py -sTest with multiple simulators when possible:
# Icarus Verilog
pytest tests/test_run_cocotb.py -s --sim icarus
# Verilator
pytest tests/test_run_cocotb.py -s --sim verilatorBuild and run Hello World:
cd sw/apps/hello_world && make
pytest tests/test_run_cocotb.py::TestRealPrograms::test_frost_hello_world -sBuild all applications to verify no breakage:
cd sw/apps && ./build_all_apps.pyRun the integration tests:
pytest tests/test_run_cocotb.py -s-
Create board wrapper in
boards/<board_name>/:boards/ └── new_board/ ├── new_board_frost.sv # Top-level wrapper └── new_board.xdc # Constraints file -
The wrapper should:
- Instantiate
xilinx_frost_subsystem(for Xilinx boards) or create equivalent - Configure clock generation (use MMCM/PLL for target frequency)
- Map board-specific I/O (UART pins, LEDs, buttons)
- Handle reset synchronization
- Instantiate
-
Add build configuration in
fpga/build/if needed -
Document the board in
boards/README.md
-
Create directory in
sw/apps/<app_name>/:sw/apps/ └── new_app/ ├── new_app.c # Main source file └── Makefile # Build configuration -
Makefile template:
# Application name APP := new_app # Source files SRC_C := new_app.c # Include common build rules include ../../common/common.mk
-
Add to
build_all_apps.pyif it should be built in CI -
Add test case in
tests/test_run_cocotb.pyif needed
-
For Cocotb tests, add to
verif/cocotb_tests/:@cocotb.test() async def test_new_feature(dut): """Test description.""" # Test implementation
-
For pytest integration, add to
tests/test_run_cocotb.py:def test_new_feature(self): """Test description.""" # Call Cocotb test
-
Use appropriate fixtures from
tests/conftest.py
- Create peripheral module in
hw/rtl/peripherals/ - Add memory-mapped interface following existing patterns
- Update memory map documentation in
hw/rtl/README.md - Add software driver in
sw/lib/ - Create test application in
sw/apps/
When reporting bugs, please include:
- FROST version or commit hash
- Simulator and version used
- Minimal reproduction steps
- Expected vs. actual behavior
- Relevant log output or waveforms
Feature requests are welcome! Please describe:
- The use case for the feature
- How it fits with FROST's goals (simplicity, portability, educational value)
- Any implementation ideas you have
We welcome contributions in these areas:
| Area | Examples |
|---|---|
| Bug fixes | Pipeline hazards, instruction encoding, timing issues |
| ISA extensions | F (floating-point), D (double), Zfinx, custom extensions |
| Privilege modes | S-mode (supervisor), U-mode (user) support |
| Board support | New FPGA boards, SoC integrations |
| Performance | Branch predictor improvements, cache enhancements |
| Peripherals | SPI, I2C, GPIO, timers |
| Documentation | Architecture guides, tutorials, examples |
| Verification | New test cases, coverage improvements, formal verification |
Documentation improvements are always appreciated:
- Fix typos or unclear explanations
- Add examples and tutorials
- Improve ASCII diagrams
- Document edge cases and design decisions
All contributions go through code review. Reviewers will check:
| Aspect | What We Look For |
|---|---|
| Correctness | Does the code work as intended? Are edge cases handled? |
| Style | Does it follow project conventions (naming, formatting, comments)? |
| Testing | Are there adequate tests? Do existing tests still pass? |
| Documentation | Are changes documented? Are comments clear? |
| Portability | Does it maintain cross-simulator and cross-tool compatibility? |
| Performance | Does it meet timing on target FPGAs? Are there regressions? |
If you have questions about contributing, feel free to:
- Open an issue for discussion
- Review existing issues and pull requests for context
- Check the documentation in
hw/rtl/README.mdfor architecture details - See
fpga/README.mdfor FPGA-specific questions - See
boards/README.mdfor board support questions
Thank you for contributing to FROST!