#![no_std]
use core::arch::asm;
use core::ptr::{read_volatile, write_volatile};
#[macro_export]
macro_rules! kernel_entry {
() => {
#[unsafe(naked)]
#[unsafe(no_mangle)]
#[unsafe(link_section = ".text.init")]
pub extern "C" fn _start() -> ! {
::core::arch::naked_asm!(
".option push",
".option norelax",
"la gp, __global_pointer$",
".option pop",
"call entry_point",
"li a2, 0", "mv a1, a0", "li a0, 8", "ecall",
)
}
};
}
pub const CB_BASE: usize = 0x8004_F23000;
const CB_STRIDE: usize = 64;
const CB_BASE_PER_HART: usize = 24;
const CB_OFFSET_PER_HART: usize = 36;
const TRACE_TYPE_STRING: u16 = 0;
const ENTRY_HEADER_SIZE: usize = 16;
const TRACE_STRING_MAX: usize = 512;
#[inline(always)]
pub fn hart_id() -> u32 {
let v: u64;
unsafe { asm!("csrr {0}, 0xcd0", out(reg) v, options(nomem, nostack, preserves_flags)) };
v as u32
}
#[inline(always)]
pub fn shire_id() -> u32 {
hart_id() >> 6
}
#[inline(always)]
pub fn timestamp() -> u64 {
let v: u64;
unsafe { asm!("csrr {0}, 0xc03", out(reg) v, options(nomem, nostack, preserves_flags)) };
v
}
#[inline(always)]
pub fn fence() {
unsafe { asm!("fence rw, rw", options(nostack, preserves_flags)) };
}
#[inline(always)]
pub fn scp_shire_base(shire: u32) -> usize {
0x8000_0000usize + ((shire as usize) << 23)
}
#[inline(always)]
fn cb_index(hart: u32) -> usize {
if hart < 2048 {
hart as usize
} else {
(hart - 32) as usize
}
}
#[inline(always)]
fn align8(n: usize) -> usize {
(n + 7) & !7
}
pub fn trace_str(text: &[u8]) {
let hid = hart_id();
let str_len = align8(text.len() + 1).min(TRACE_STRING_MAX);
let cb = CB_BASE + cb_index(hid) * CB_STRIDE;
let base = unsafe { read_volatile((cb + CB_BASE_PER_HART) as *const u64) } as usize;
let offset = unsafe { read_volatile((cb + CB_OFFSET_PER_HART) as *const u32) };
let head = base + offset as usize;
unsafe {
write_volatile(head as *mut u64, timestamp());
write_volatile((head + 8) as *mut u32, str_len as u32);
write_volatile((head + 12) as *mut u16, hid as u16);
write_volatile((head + 14) as *mut u16, TRACE_TYPE_STRING);
let s = (head + ENTRY_HEADER_SIZE) as *mut u8;
let mut i = 0;
while i < str_len {
let byte = if i < text.len() { text[i] } else { 0 };
write_volatile(s.add(i), byte);
i += 1;
}
write_volatile(
(cb + CB_OFFSET_PER_HART) as *mut u32,
offset + (ENTRY_HEADER_SIZE + str_len) as u32,
);
}
}
pub struct MsgBuf {
buf: [u8; 192],
len: usize,
}
impl Default for MsgBuf {
fn default() -> Self {
Self::new()
}
}
impl MsgBuf {
pub fn new() -> Self {
MsgBuf {
buf: [0; 192],
len: 0,
}
}
pub fn str(&mut self, s: &[u8]) -> &mut Self {
let mut i = 0;
while i < s.len() && self.len < self.buf.len() {
self.buf[self.len] = s[i];
self.len += 1;
i += 1;
}
self
}
pub fn u64(&mut self, mut v: u64) -> &mut Self {
let mut tmp = [0u8; 20];
let mut c = 0;
loop {
tmp[c] = b'0' + (v % 10) as u8;
v /= 10;
c += 1;
if v == 0 {
break;
}
}
while c > 0 && self.len < self.buf.len() {
c -= 1;
self.buf[self.len] = tmp[c];
self.len += 1;
}
self
}
pub fn as_slice(&self) -> &[u8] {
&self.buf[..self.len]
}
}
pub use et_abi::CACHE_LINE;
pub struct Grid {
hart: u32,
n_harts: u32,
}
impl Grid {
pub fn new(n_harts: u32) -> Self {
Grid {
hart: hart_id(),
n_harts,
}
}
pub fn hart(&self) -> u32 {
self.hart
}
pub fn n_harts(&self) -> u32 {
self.n_harts
}
pub fn active(&self) -> bool {
self.hart < self.n_harts
}
fn range(&self, n: usize) -> (usize, usize) {
let h = self.hart as usize;
let p = (self.n_harts as usize).max(1);
let base = n / p;
let rem = n % p;
let start = h * base + h.min(rem);
let len = base + if h < rem { 1 } else { 0 };
(start, start + len)
}
pub fn my_slice<'a, T>(&self, data: &'a [T]) -> &'a [T] {
let (start, end) = self.range(data.len());
&data[start..end]
}
pub unsafe fn output_cell<'a, T>(&self, base: usize) -> &'a mut T {
unsafe { &mut *((base + self.hart as usize * CACHE_LINE) as *mut T) }
}
}
pub mod tensor;
pub mod pmu;
#[doc(hidden)]
pub mod simd;
pub unsafe fn device_slice<'a, T>(addr: usize, n: usize) -> &'a [T] {
unsafe { core::slice::from_raw_parts(addr as *const T, n) }
}