Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions backends/cpp_hart_gen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,13 @@ add_executable(test_version
target_include_directories(test_version PUBLIC ${CMAKE_SOURCE_DIR}/include)
target_link_libraries(test_version PRIVATE hart Catch2::Catch2WithMain)

add_executable(test_regfile
${CMAKE_SOURCE_DIR}/test/test_regfile.cpp
${CMAKE_SOURCE_DIR}/src/NotificationHandler.cpp
)
target_include_directories(test_regfile PUBLIC ${CMAKE_SOURCE_DIR}/include)
target_link_libraries(test_regfile PRIVATE hart Catch2::Catch2WithMain)

# add_executable(test_decode
# ${CMAKE_SOURCE_DIR}/test/test_decode.cpp
# )
Expand All @@ -224,6 +231,7 @@ target_link_libraries(test_version PRIVATE hart Catch2::Catch2WithMain)
include(CTest)
include(Catch)
catch_discover_tests(test_bits_directed)
catch_discover_tests(test_regfile)

foreach (random_test IN LISTS RANDOM_TESTS)
catch_discover_tests(${random_test})
Expand Down
13 changes: 13 additions & 0 deletions backends/cpp_hart_gen/cpp/include/udb/hart.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,19 @@ namespace udb {
virtual uint64_t xreg(unsigned num) const = 0;
virtual void set_xreg(unsigned num, uint64_t value) = 0;

virtual uint64_t freg(unsigned num) const {
throw std::runtime_error("No F register file in this configuration");
}
virtual void set_freg(unsigned num, uint64_t value) {
throw std::runtime_error("No F register file in this configuration");
}
virtual uint64_t vreg(unsigned num) const {
throw std::runtime_error("No V register file in this configuration");
}
virtual void set_vreg(unsigned num, uint64_t value) {
throw std::runtime_error("No V register file in this configuration");
}

virtual CsrBase* csr(unsigned address) = 0;
virtual const CsrBase* csr(unsigned address) const = 0;

Expand Down
7 changes: 2 additions & 5 deletions backends/cpp_hart_gen/cpp/src/iss.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
#define RISCV_REG_PC 0x20
#define RISCV_REG_FPR_FIRST 0x21
#define RISCV_REG_FPR_LAST 0x40
#define RISCV_REG_FPR_LAST 0x40
#define RISCV_REG_CSR_FIRST 0x41
#define RISCV_REG_CSR_LAST 0x1040

Expand Down Expand Up @@ -419,8 +418,7 @@ int InstructionSetSimulator::OnReadSingleRegister(int reg, uint64_t& value)
value = m_pHart->pc();
else if (reg >= RISCV_REG_FPR_FIRST && reg <= RISCV_REG_FPR_LAST)
{
//no FP so far
return -1;
value = m_pHart->freg(reg - RISCV_REG_FPR_FIRST);
Comment thread
dhower-qc marked this conversation as resolved.
Outdated
}
else if (reg >= RISCV_REG_CSR_FIRST && reg <= RISCV_REG_CSR_LAST)
{
Expand Down Expand Up @@ -456,8 +454,7 @@ int InstructionSetSimulator::OnWriteSingleRegister(int reg, uint64_t& value)
m_pHart->set_next_pc(value);
else if (reg >= RISCV_REG_FPR_FIRST && reg <= RISCV_REG_FPR_LAST)
{
//no FP so far
return -1;
m_pHart->set_freg(reg - RISCV_REG_FPR_FIRST, value);
}
Comment thread
dhower-qc marked this conversation as resolved.
else if (reg >= RISCV_REG_CSR_FIRST && reg <= RISCV_REG_CSR_LAST)
{
Expand Down
178 changes: 178 additions & 0 deletions backends/cpp_hart_gen/cpp/test/test_regfile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
// SPDX-License-Identifier: BSD-3-Clause-Clear

// Tests for register file storage and accessor generation (Layer 4a–4c).

#include <catch2/catch_test_macros.hpp>
#include <udb/hart_factory.hxx>
#include <udb/iss_soc_model.hpp>
#include <stdexcept>

// Use the rv64-riscv-tests config (known-working fully-configured rv64 with F extension).
static const std::string cfg_yaml = R"(
$schema: https://riscv.org/udb/schemas/config_schema-0.1.0.json
kind: architecture configuration
type: fully configured
name: rv64-riscv-tests
description: For register file testing

implemented_extensions:
- [Sm, "1.11.0"]
- [Smstateen, "1.0.0"]
- [I, "2.1"]
- [C, "2.0"]
- [M, "2.0"]
- [Zicsr, "2.0"]
- [Zicntr, "2.0"]
- [Smrnmi, "1.0"]
- [S, "1.11.0"]
- [U, "1.0.0"]
- [Zifencei, "2.0.0"]
- [Sv39, "1.11.0"]
- [Zca, "1.0.0"]
- [F, "2.2.0"]

params:
MXLEN: 64
MARCHID_IMPLEMENTED: true
ARCH_ID_VALUE: 1
MIMPID_IMPLEMENTED: true
IMP_ID_VALUE: 0
VENDOR_ID_BANK: 1
VENDOR_ID_OFFSET: 1
MISALIGNED_LDST: true
MISALIGNED_LDST_EXCEPTION_PRIORITY: low
MISALIGNED_MAX_ATOMICITY_GRANULE_SIZE: 4
MISALIGNED_SPLIT_STRATEGY: sequential_bytes
PRECISE_SYNCHRONOUS_EXCEPTIONS: true
TRAP_ON_ECALL_FROM_M: true
TRAP_ON_EBREAK: true
M_MODE_ENDIANNESS: little
TRAP_ON_ILLEGAL_WLRL: true
TRAP_ON_UNIMPLEMENTED_INSTRUCTION: true
TRAP_ON_RESERVED_INSTRUCTION: true
TRAP_ON_UNIMPLEMENTED_CSR: true
REPORT_VA_IN_MTVAL_ON_BREAKPOINT: true
REPORT_VA_IN_MTVAL_ON_LOAD_MISALIGNED: true
REPORT_VA_IN_MTVAL_ON_STORE_AMO_MISALIGNED: true
REPORT_VA_IN_MTVAL_ON_INSTRUCTION_MISALIGNED: true
REPORT_VA_IN_MTVAL_ON_LOAD_ACCESS_FAULT: true
REPORT_VA_IN_MTVAL_ON_STORE_AMO_ACCESS_FAULT: true
REPORT_VA_IN_MTVAL_ON_INSTRUCTION_ACCESS_FAULT: true
REPORT_ENCODING_IN_MTVAL_ON_ILLEGAL_INSTRUCTION: true
MTVAL_WIDTH: 32
PMA_GRANULARITY: 12
PHYS_ADDR_WIDTH: 57
MISA_CSR_IMPLEMENTED: true
MTVEC_ACCESS: rw
MTVEC_MODES: [0, 1]
MTVEC_BASE_ALIGNMENT_DIRECT: 4
MTVEC_BASE_ALIGNMENT_VECTORED: 4
MTVEC_ILLEGAL_WRITE_BEHAVIOR: retain
MUTABLE_MISA_C: false
MUTABLE_MISA_M: false
TIME_CSR_IMPLEMENTED: false
MUTABLE_MISA_S: false
ASID_WIDTH: 5
S_MODE_ENDIANNESS: little
SXLEN: [64]
REPORT_VA_IN_MTVAL_ON_LOAD_PAGE_FAULT: true
REPORT_VA_IN_MTVAL_ON_STORE_AMO_PAGE_FAULT: true
REPORT_VA_IN_MTVAL_ON_INSTRUCTION_PAGE_FAULT: true
REPORT_VA_IN_STVAL_ON_BREAKPOINT: true
REPORT_VA_IN_STVAL_ON_LOAD_MISALIGNED: true
REPORT_VA_IN_STVAL_ON_STORE_AMO_MISALIGNED: true
REPORT_VA_IN_STVAL_ON_INSTRUCTION_MISALIGNED: true
REPORT_VA_IN_STVAL_ON_LOAD_ACCESS_FAULT: true
REPORT_VA_IN_STVAL_ON_STORE_AMO_ACCESS_FAULT: true
REPORT_VA_IN_STVAL_ON_INSTRUCTION_ACCESS_FAULT: true
REPORT_VA_IN_STVAL_ON_LOAD_PAGE_FAULT: true
REPORT_VA_IN_STVAL_ON_STORE_AMO_PAGE_FAULT: true
REPORT_VA_IN_STVAL_ON_INSTRUCTION_PAGE_FAULT: true
REPORT_ENCODING_IN_STVAL_ON_ILLEGAL_INSTRUCTION: true
STVAL_WIDTH: 32
MCOUNTENABLE_EN: [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
SCOUNTENABLE_EN: [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
COUNTINHIBIT_EN: [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
STVEC_MODE_DIRECT: true
STVEC_MODE_VECTORED: true
SATP_MODE_BARE: true
TRAP_ON_ECALL_FROM_S: true
TRAP_ON_ECALL_FROM_U: true
MSTATUS_VS_LEGAL_VALUES: [0]
MSTATUS_FS_LEGAL_VALUES: [3, 2, 1, 0]
MSTATUS_TVM_IMPLEMENTED: false
NUM_PMP_ENTRIES: 16
PMP_GRANULARITY: 12
MUTABLE_MISA_U: false
U_MODE_ENDIANNESS: little
UXLEN: [64]
MSTATEEN_ENVCFG_TYPE: rw
HW_MSTATUS_FS_DIRTY_UPDATE: precise
MUTABLE_MISA_F: false
HPM_COUNTER_EN: [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
)";

// ---------------------------------------------------------------------------
// X register file tests (Layer 4a–4c)
// ---------------------------------------------------------------------------

TEST_CASE("X register storage has 32 entries", "[regfile]") {
udb::IssSocModel soc(1024 * 1024, 0);
auto* hart = udb::HartFactory::create("rv64", 0, cfg_yaml, soc);
// Write a known value to x31, verify it round-trips; index 32 throws.
REQUIRE_NOTHROW(hart->set_xreg(31, 42));
REQUIRE(hart->xreg(31) == 42);
REQUIRE_THROWS_AS(hart->xreg(32), std::out_of_range);
delete hart;
}

TEST_CASE("x0 is zero after reset", "[regfile]") {
udb::IssSocModel soc(1024 * 1024, 0);
auto* hart = udb::HartFactory::create("rv64", 0, cfg_yaml, soc);
REQUIRE(hart->xreg(0) == 0);
delete hart;
}

TEST_CASE("writing to x0 leaves it zero (arch_write)", "[regfile]") {
udb::IssSocModel soc(1024 * 1024, 0);
auto* hart = udb::HartFactory::create("rv64", 0, cfg_yaml, soc);
hart->set_xreg(0, 42);
REQUIRE(hart->xreg(0) == 0);
delete hart;
}

TEST_CASE("xreg throws out_of_range for index >= 32", "[regfile]") {
udb::IssSocModel soc(1024 * 1024, 0);
auto* hart = udb::HartFactory::create("rv64", 0, cfg_yaml, soc);
REQUIRE_THROWS_AS(hart->set_xreg(32, 0), std::out_of_range);
delete hart;
}

// ---------------------------------------------------------------------------
// F register file tests (Layer 4d: hart.hpp adds virtual freg() to HartBase)
// ---------------------------------------------------------------------------

TEST_CASE("F register storage has 32 entries", "[regfile]") {
udb::IssSocModel soc(1024 * 1024, 0);
auto* hart = udb::HartFactory::create("rv64", 0, cfg_yaml, soc);
REQUIRE_NOTHROW(hart->set_freg(31, 0xdeadbeef));
REQUIRE(hart->freg(31) == 0xdeadbeef);
REQUIRE_THROWS_AS(hart->freg(32), std::out_of_range);
delete hart;
}

TEST_CASE("freg round-trips a written value", "[regfile]") {
udb::IssSocModel soc(1024 * 1024, 0);
auto* hart = udb::HartFactory::create("rv64", 0, cfg_yaml, soc);
hart->set_freg(0, 0x3f800000);
REQUIRE(hart->freg(0) == 0x3f800000);
delete hart;
}

TEST_CASE("freg throws out_of_range for index >= 32", "[regfile]") {
udb::IssSocModel soc(1024 * 1024, 0);
auto* hart = udb::HartFactory::create("rv64", 0, cfg_yaml, soc);
REQUIRE_THROWS_AS(hart->set_freg(32, 0), std::out_of_range);
delete hart;
}
18 changes: 12 additions & 6 deletions backends/cpp_hart_gen/lib/gen_cpp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -843,9 +843,12 @@ def gen_cpp(symtab, indent = 0, indent_spaces: 2)
"#{' ' * indent}#{var.gen_cpp(symtab, 0, indent_spaces:)}.at(#{index.gen_cpp(symtab, 0)})"
end
else
if var.text_value.start_with?("X")
#"#{' '*indent}#{var.gen_cpp(symtab, 0, indent_spaces:)}[#{index.gen_cpp(symtab, 0, indent_spaces:)}]"
"#{' ' * indent} __UDB_HART->_xreg(#{index.gen_cpp(symtab, 0, indent_spaces:)})"
var_type = var.type(symtab)
if var_type.kind == :array &&
var_type.sub_type.is_a?(Idl::RegFileElementType) &&
var_type.qualifiers.include?(:global)
rf_name = var_type.sub_type.name.downcase
"#{' ' * indent}__UDB_HART->_#{rf_name}reg(#{index.gen_cpp(symtab, 0, indent_spaces:)})"
else
"#{' ' * indent}#{var.gen_cpp(symtab, 0, indent_spaces:)}.at(#{index.gen_cpp(symtab, 0, indent_spaces:)}.get())"
end
Expand Down Expand Up @@ -895,9 +898,12 @@ def gen_cpp(symtab, indent = 0, indent_spaces: 2)
class AryElementAssignmentAst < AstNode
sig { override.params(symtab: SymbolTable, indent: Integer, indent_spaces: Integer).returns(String) }
def gen_cpp(symtab, indent = 0, indent_spaces: 2)
if lhs.text_value.start_with?("X")
#"#{' '*indent} #{lhs.gen_cpp(symtab, 0, indent_spaces:)}[#{idx.gen_cpp(symtab, 0, indent_spaces:)}] = #{rhs.gen_cpp(symtab, 0, indent_spaces:)}"
"#{' ' * indent}__UDB_HART->_set_xreg( #{idx.gen_cpp(symtab, 0, indent_spaces:)}, #{rhs.gen_cpp(symtab, 0, indent_spaces:)})"
lhs_type = lhs.type(symtab)
if lhs_type.kind == :array &&
lhs_type.sub_type.is_a?(Idl::RegFileElementType) &&
lhs_type.qualifiers.include?(:global)
rf_name = lhs_type.sub_type.name.downcase
"#{' ' * indent}__UDB_HART->_set_#{rf_name}reg( #{idx.gen_cpp(symtab, 0, indent_spaces:)}, #{rhs.gen_cpp(symtab, 0, indent_spaces:)})"
elsif lhs.type(symtab).kind == :bits
"#{' ' * indent}#{lhs.gen_cpp(symtab, 0, indent_spaces:)}.setBit(#{idx.gen_cpp(symtab, 0, indent_spaces:)}, #{rhs.gen_cpp(symtab, 0, indent_spaces:)})"
else
Expand Down
40 changes: 40 additions & 0 deletions backends/cpp_hart_gen/lib/template_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,46 @@ def quot_str(val)
val
end
end

# C++ width string for a register file element type (e.g. "64" or "VLEN").
# Used in template parameters like PossiblyUnknownBits<WIDTH>.
def rf_elem_width(rf)
width = rf.eval_register_length(cfg_arch)
width.is_a?(String) ? width : width.to_s
end

# Compile an IDL expression string to an AST node that responds to gen_cpp.
def compile_idl_expr(idl_str)
cfg_arch.idl_compiler.compile_expression(idl_str, cfg_arch.symtab)
end

# IDL Type for one register file element (Bits<WIDTH>).
def rf_elem_type(rf)
width = rf.eval_register_length(cfg_arch)
Idl::Type.new(:bits, width: width.is_a?(String) ? rf.max_register_length : width)
end

def gen_arch_read_cpp(entry) = idl_body_to_cpp(entry.arch_read)
def gen_arch_write_cpp(entry) = idl_body_to_cpp(entry.arch_write)

# True if the named register file (e.g. "F") has register_class: floating_point.
def rf_floating_point?(rf_name)
cfg_arch.register_files.find { |rf| rf.name == rf_name }&.register_class == "floating_point"
end

# All register files defined for this architecture.
# The ISS generates storage and accessors for all register files since
# for a partially-configured arch, any extension may be present.
def applicable_register_files
cfg_arch.register_files
end

private

def idl_body_to_cpp(body)
expr_str = body.strip.sub(/\Areturn\s+/, "").sub(/;\z/, "").strip
compile_idl_expr(expr_str).gen_cpp(cfg_arch.symtab, 0)
end
end

class TemplateEnv
Expand Down
1 change: 1 addition & 0 deletions backends/cpp_hart_gen/tasks.rake
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,7 @@ namespace :test do
Dir.chdir "#{CPP_HART_GEN_DST}/#{build_name}/build" do
sh "make -j #{$jobs} test_bits_directed"
sh "make -j #{$jobs} test_bits_random"
sh "make -j #{$jobs} test_regfile"
sh "ctest -T coverage -T test"
end
end
Expand Down
Loading
Loading