Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Functional emulator bridges spike and t1devices #959

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
23 changes: 21 additions & 2 deletions difftest/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions difftest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ members = [
"dpi_t1emu",
"dpi_t1rocketemu",
"dpi_common",
"t1devices",
"t1emu",
]
exclude = [
"spike_interfaces"
Expand Down
23 changes: 21 additions & 2 deletions difftest/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
}:

assert let
available = [ "dpi_t1emu" "dpi_t1rocketemu" "offline_t1emu" "offline_t1rocketemu" ];
available = [ "dpi_t1emu" "dpi_t1rocketemu" "offline_t1emu" "offline_t1rocketemu" "t1emu" ];
in
lib.assertMsg (lib.elem moduleType available) "moduleType is not in ${lib.concatStringsSep ", " available}";

Expand All @@ -26,6 +26,8 @@ let
./offline_common
./offline_t1emu
./offline_t1rocketemu
./t1devices
./t1emu
./Cargo.lock
./Cargo.toml
./.rustfmt.toml
Expand Down Expand Up @@ -57,7 +59,7 @@ if (lib.hasPrefix "dpi" moduleType) then
dpiLibPath = "/lib/libdpi_${moduleType}.a";
};
}
else
else if (lib.hasPrefix "offline" moduleType) then
assert lib.assertMsg (emuType == "") "emuType shall not be set for offline";
rustPlatform.buildRustPackage {
name = outputName;
Expand Down Expand Up @@ -85,3 +87,20 @@ else

meta.mainProgram = "offline";
}
else
rustPlatform.buildRustPackage {
name = outputName;
src = rustSrc;

buildFeatures = [ ];
buildAndTestSubdir = "./${moduleType}";

env = {
SPIKE_LIB_DIR = "${libspike}/lib";
SPIKE_INTERFACES_LIB_DIR = "${libspike_interfaces}/lib";
};

cargoLock = {
lockFile = ./Cargo.lock;
};
}
2 changes: 1 addition & 1 deletion difftest/dpi_t1rocketemu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ crate-type = ["staticlib", "cdylib"]

[dependencies]
dpi_common = { path = "../dpi_common" }
t1devices = { path = "../t1devices" }
spike_rs = { path = "../spike_rs" }
tracing = { workspace = true }
svdpi = { workspace = true }
anyhow = { workspace = true }
hex = "0.4.3"
elf = "0.7.4"
png = "0.17.14"

[features]
6 changes: 3 additions & 3 deletions difftest/dpi_t1rocketemu/src/drive.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::get_t;
use crate::interconnect::simctrl::ExitFlagRef;
use crate::interconnect::{create_emu_addrspace, AddressSpace};
use svdpi::SvScope;
use t1devices::simctrl::ExitFlagRef;
use t1devices::{create_emu_addrspace, AddressSpace};

use anyhow::Context;
use elf::{
Expand Down Expand Up @@ -50,7 +50,7 @@ pub(crate) struct Driver {

impl Driver {
pub(crate) fn new(scope: SvScope, args: &OnlineArgs) -> Self {
let (mut addr_space, exit_flag) = create_emu_addrspace();
let (mut addr_space, exit_flag) = create_emu_addrspace(get_t);
// pass e_entry to rocket
let (e_entry, _fn_sym_tab) =
Self::load_elf(Path::new(&args.elf_file), &mut addr_space).expect("fail creating simulator");
Expand Down
1 change: 0 additions & 1 deletion difftest/dpi_t1rocketemu/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
mod dpi;
mod drive;
mod interconnect;

// keep in sync with TestBench.verbatimModule
// the value is measured in simulation time unit
Expand Down
2 changes: 1 addition & 1 deletion difftest/spike_interfaces/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD 17)

find_package(libspike REQUIRED)

add_library(${CMAKE_PROJECT_NAME} STATIC spike_interfaces.cc)
add_library(${CMAKE_PROJECT_NAME} STATIC spike_interfaces.cc t1emu.cc)

target_link_libraries(${CMAKE_PROJECT_NAME} PUBLIC libspike)

Expand Down
148 changes: 148 additions & 0 deletions difftest/spike_interfaces/t1emu.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#include <type_traits>
#include <iostream>
#include <iomanip>

#include "cfg.h"
#include "decode_macros.h"
#include "disasm.h"
#include "mmu.h"
#include "processor.h"
#include "simif.h"

static_assert(std::is_same_v<reg_t, uint64_t>);

struct t1emu_memory_vtable_t {
uint8_t* (*addr_to_mem)(void* memory, reg_t addr);
int (*mmio_load)(void* memory, reg_t addr, size_t len, uint8_t* bytes);
int (*mmio_store)(void* memory, reg_t addr, size_t len, const uint8_t* bytes);
};

class t1emu_sim_t: public simif_t {
void* m_memory;
t1emu_memory_vtable_t m_vtable;

cfg_t m_cfg;
isa_parser_t m_isa_parser;
processor_t m_proc;

public:
t1emu_sim_t(
void* memory,
t1emu_memory_vtable_t const* vtable,
cfg_t cfg,
size_t vlen
):
m_memory(memory),
m_vtable(*vtable),
m_cfg(std::move(cfg)),
m_isa_parser(m_cfg.isa, m_cfg.priv),
m_proc(
&m_isa_parser,
&m_cfg,
this,
0,
true,
nullptr,
std::cerr
)
{
m_proc.VU.lane_num = vlen / 32;
m_proc.VU.lane_granularity = 32;
}

char* addr_to_mem(reg_t addr) override {
return (char*)m_vtable.addr_to_mem(m_memory, addr);
}

bool mmio_fetch(reg_t addr, size_t len, uint8_t *bytes) override {
// TODO: currently inst fetch is disallowed on mmio
return false;
}

bool mmio_load(reg_t addr, size_t len, uint8_t *bytes) override {
return (bool)m_vtable.mmio_load(m_memory, addr, len, bytes);
}

bool mmio_store(reg_t addr, size_t len, const uint8_t *bytes) override {
return (bool)m_vtable.mmio_store(m_memory, addr, len, bytes);
}

virtual void proc_reset(unsigned id) override {
// do nothing
}

virtual const char* get_symbol(uint64_t addr) override {
throw std::logic_error("t1emu_sim_t::get_symbol not implemented");
}

const cfg_t& get_cfg() const override {
return m_cfg;
}

const std::map<size_t, processor_t *> &
get_harts() const override {
throw std::logic_error("t1emu_sim_t::get_harts not implemented");
}

void reset_with_pc(reg_t new_pc) {
m_proc.reset();
m_proc.check_pc_alignment(new_pc);
m_proc.get_state()->pc = new_pc;
}

void step_one() {
reg_t pc = m_proc.get_state()->pc;
mmu_t* mmu = m_proc.get_mmu();
state_t* state = m_proc.get_state();

try {
insn_fetch_t fetch = mmu->load_insn(pc);
reg_t new_pc = fetch.func(&m_proc, fetch.insn, pc);
printf("pc=%08lx, new_pc=%08lx\n", pc, new_pc);
if ((new_pc & 1) == 0) {
state->pc = new_pc;
} else {
switch (new_pc) {
case PC_SERIALIZE_BEFORE: state->serialized = true; break;
case PC_SERIALIZE_AFTER: break;
default: throw std::logic_error("invalid PC after fetch.func");
}
}
} catch (trap_t &trap) {
std::cerr << "Error: spike trapped with " << trap.name()
<< " (tval=" << std::uppercase << std::setfill('0')
<< std::setw(8) << std::hex << trap.get_tval()
<< ", tval2=" << std::setw(8) << std::hex << trap.get_tval2()
<< ", tinst=" << std::setw(8) << std::hex << trap.get_tinst()
<< ")" << std::endl;
throw;
} catch (std::exception& e) {
std::cerr << e.what() << std::endl;
throw;
}
}
};

extern "C" {
t1emu_sim_t* t1emu_create(
void* memory,
t1emu_memory_vtable_t const* vtable,
const char* isa_set,
size_t vlen
) {
cfg_t cfg;
cfg.isa = strdup(isa_set);
cfg.priv = "M";

return new t1emu_sim_t(memory, vtable, cfg, vlen);
}
void t1emu_destroy(t1emu_sim_t* emu) {
delete emu;
}
void t1emu_reset_with_pc(t1emu_sim_t* emu, reg_t new_pc) {
emu->reset_with_pc(new_pc);
}
void t1emu_step_one(t1emu_sim_t* emu) {
emu->step_one();
}
}
9 changes: 9 additions & 0 deletions difftest/t1devices/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "t1devices"
edition = "2021"
version.workspace = true

[dependencies]
anyhow = { workspace = true }
tracing = { workspace = true }
png = "0.17.14"
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{env, fs::File, io::BufWriter, path::PathBuf};

use crate::interconnect::memcpy_mask;
use crate::memcpy_mask;

use super::{AddrInfo, Device};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::any::Any;

use framebuffer::FrameBuffer;
use simctrl::{ExitFlagRef, SimCtrl};
use simctrl::{ExitFlagRef, PfnGetCycle, SimCtrl};

pub mod framebuffer;
pub mod simctrl;
Expand Down Expand Up @@ -38,6 +38,12 @@ pub trait Device: Any + Send + Sync {
// NOTE: even if the device does not support partial write,
// it shall check mask and behave as a full write when mask is all active
fn mem_write_masked(&mut self, addr: AddrInfo, data: &[u8], mask: &[bool]);

// TODO: add more comments
// spike simif_t::addr_to_mem. Should only be implemented by RegularMemory
fn addr_to_mem(&mut self, offset: u32) -> Option<&mut u8> {
None
}
}

impl<T: Device> DeviceExt for T {}
Expand Down Expand Up @@ -119,6 +125,10 @@ impl Device for RegularMemory {
let mem_data = &mut self.data[addr.as_range()];
memcpy_mask(mem_data, data, mask);
}

fn addr_to_mem(&mut self, offset: u32) -> Option<&mut u8> {
self.data.get_mut(offset as usize)
}
}

fn memcpy_mask(dst: &mut [u8], src: &[u8], mask: &[bool]) {
Expand All @@ -139,6 +149,12 @@ pub struct AddressSpace {
}

impl AddressSpace {
pub fn addr_to_mem(&mut self, addr: u32) -> Option<&mut u8> {
let device_idx = self.find_device_idx(addr, 1)?;
let dev_entry = &mut self.devices[device_idx];
dev_entry.device.addr_to_mem(addr - dev_entry.base_and_size.0)
}

pub fn read_mem(&mut self, addr: u32, len: u32, data: &mut [u8]) {
assert_eq!(len as usize, data.len());
let Some(device_idx) = self.find_device_idx(addr, len) else {
Expand Down Expand Up @@ -196,7 +212,7 @@ impl AddressSpace {
/// - 0x0400_0000 - 0x0600_0000 : framebuffer
/// - 0x1000_0000 - 0x1000_1000 : simctrl
/// - 0x2000_0000 - 0xc000_0000 : sram
pub fn create_emu_addrspace() -> (AddressSpace, ExitFlagRef) {
pub fn create_emu_addrspace(get_cycle: PfnGetCycle) -> (AddressSpace, ExitFlagRef) {
const SRAM_BASE: u32 = 0x2000_0000;
const SRAM_SIZE: u32 = 0xa000_0000;

Expand All @@ -210,7 +226,7 @@ pub fn create_emu_addrspace() -> (AddressSpace, ExitFlagRef) {
let devices = vec![
RegularMemory::with_size(SRAM_SIZE).with_addr(SRAM_BASE, SRAM_SIZE),
FrameBuffer::new().with_addr(DISPLAY_BASE, DISPLAY_SIZE),
SimCtrl::new(exit_flag.clone()).with_addr(SIMCTRL_BASE, SIMCTRL_SIZE),
SimCtrl::new(exit_flag.clone(), get_cycle).with_addr(SIMCTRL_BASE, SIMCTRL_SIZE),
];
(AddressSpace { devices }, exit_flag)
}
Loading