diff --git a/RISC-V/Firmware/RISCV/drivers/n200_eclic.h b/RISC-V/Firmware/RISCV/drivers/n200_eclic.h new file mode 100644 index 0000000..a155748 --- /dev/null +++ b/RISC-V/Firmware/RISCV/drivers/n200_eclic.h @@ -0,0 +1,48 @@ +// See LICENSE file for licence details + +#ifndef N200_ECLIC_H +#define N200_ECLIC_H + +#include + +#define ECLICINTCTLBITS 4 + +//ECLIC memory map +// Offset +// 0x0000 1B RW ecliccfg +#define ECLIC_CFG_OFFSET 0x0 +// 0x0004 4B R eclicinfo +#define ECLIC_INFO_OFFSET 0x4 +// 0x000B 1B RW mintthresh +#define ECLIC_MTH_OFFSET 0xB +// +// 0x1000+4*i 1B/input RW eclicintip[i] +#define ECLIC_INT_IP_OFFSET _AC(0x1000,UL) +// 0x1001+4*i 1B/input RW eclicintie[i] +#define ECLIC_INT_IE_OFFSET _AC(0x1001,UL) +// 0x1002+4*i 1B/input RW eclicintattr[i] +#define ECLIC_INT_ATTR_OFFSET _AC(0x1002,UL) + +#define ECLIC_INT_ATTR_SHV 0x01 +#define ECLIC_INT_ATTR_TRIG_LEVEL 0x00 +#define ECLIC_INT_ATTR_TRIG_EDGE 0x02 +#define ECLIC_INT_ATTR_TRIG_POS 0x00 +#define ECLIC_INT_ATTR_TRIG_NEG 0x04 + +// 0x1003+4*i 1B/input RW eclicintctl[i] +#define ECLIC_INT_CTRL_OFFSET _AC(0x1003,UL) +// +// ... +// +#define ECLIC_ADDR_BASE 0xd2000000 + + +#define ECLIC_CFG_NLBITS_MASK _AC(0x1E,UL) +#define ECLIC_CFG_NLBITS_LSB (1u) + +#define MSIP_HANDLER eclic_msip_handler +#define MTIME_HANDLER eclic_mtip_handler +#define BWEI_HANDLER eclic_bwei_handler +#define PMOVI_HANDLER eclic_pmovi_handler + +#endif diff --git a/RISC-V/Firmware/RISCV/drivers/n200_func.c b/RISC-V/Firmware/RISCV/drivers/n200_func.c new file mode 100644 index 0000000..b589020 --- /dev/null +++ b/RISC-V/Firmware/RISCV/drivers/n200_func.c @@ -0,0 +1,398 @@ +// See LICENSE for license details. +#include +#include +#include +#include +#include + +#include "riscv_encoding.h" +#include "n200_func.h" + + // Configure PMP to make all the address space accesable and executable +void pmp_open_all_space(){ + // Config entry0 addr to all 1s to make the range cover all space + asm volatile ("li x6, 0xffffffff":::"x6"); + asm volatile ("csrw pmpaddr0, x6":::); + // Config entry0 cfg to make it NAPOT address mode, and R/W/X okay + asm volatile ("li x6, 0x7f":::"x6"); + asm volatile ("csrw pmpcfg0, x6":::); +} + +void switch_m2u_mode(){ + clear_csr (mstatus,MSTATUS_MPP); + //printf("\nIn the m2u function, the mstatus is 0x%x\n", read_csr(mstatus)); + //printf("\nIn the m2u function, the mepc is 0x%x\n", read_csr(mepc)); + asm volatile ("la x6, 1f ":::"x6"); + asm volatile ("csrw mepc, x6":::); + asm volatile ("mret":::); + asm volatile ("1:":::); +} + +uint32_t mtime_lo(void) +{ + return *(volatile uint32_t *)(TIMER_CTRL_ADDR + TIMER_MTIME); +} + + +uint32_t mtime_hi(void) +{ + return *(volatile uint32_t *)(TIMER_CTRL_ADDR + TIMER_MTIME + 4); +} + +uint64_t get_timer_value() +{ + while (1) { + uint32_t hi = mtime_hi(); + uint32_t lo = mtime_lo(); + if (hi == mtime_hi()) + return ((uint64_t)hi << 32) | lo; + } +} + +uint32_t get_timer_freq() +{ + return TIMER_FREQ; +} + +uint64_t get_instret_value() +{ + while (1) { + uint32_t hi = read_csr(minstreth); + uint32_t lo = read_csr(minstret); + if (hi == read_csr(minstreth)) + return ((uint64_t)hi << 32) | lo; + } +} + +uint64_t get_cycle_value() +{ + while (1) { + uint32_t hi = read_csr(mcycleh); + uint32_t lo = read_csr(mcycle); + if (hi == read_csr(mcycleh)) + return ((uint64_t)hi << 32) | lo; + } +} + +uint32_t __attribute__((noinline)) measure_cpu_freq(size_t n) +{ + uint32_t start_mtime, delta_mtime; + uint32_t mtime_freq = get_timer_freq(); + + // Don't start measuruing until we see an mtime tick + uint32_t tmp = mtime_lo(); + do { + start_mtime = mtime_lo(); + } while (start_mtime == tmp); + + uint32_t start_mcycle = read_csr(mcycle); + + do { + delta_mtime = mtime_lo() - start_mtime; + } while (delta_mtime < n); + + uint32_t delta_mcycle = read_csr(mcycle) - start_mcycle; + + return (delta_mcycle / delta_mtime) * mtime_freq + + ((delta_mcycle % delta_mtime) * mtime_freq) / delta_mtime; +} + +uint32_t get_cpu_freq() +{ + uint32_t cpu_freq; + + // warm up + measure_cpu_freq(1); + // measure for real + cpu_freq = measure_cpu_freq(100); + + return cpu_freq; +} + + + +// Note that there are no assertions or bounds checking on these +// parameter values. + + + + +void eclic_init ( uint32_t num_irq ) +{ + + typedef volatile uint32_t vuint32_t; + + //clear cfg register + *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_CFG_OFFSET)=0; + + //clear minthresh register + *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_MTH_OFFSET)=0; + + //clear all IP/IE/ATTR/CTRL bits for all interrupt sources + vuint32_t * ptr; + + vuint32_t * base = (vuint32_t*)(ECLIC_ADDR_BASE + ECLIC_INT_IP_OFFSET); + vuint32_t * upper = (vuint32_t*)(base + num_irq*4); + + for (ptr = base; ptr < upper; ptr=ptr+4){ + *ptr = 0; + } +} + + + +void eclic_enable_interrupt (uint32_t source) { + *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_INT_IE_OFFSET+source*4) = 1; +} + +void eclic_disable_interrupt (uint32_t source){ + *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_INT_IE_OFFSET+source*4) = 0; +} + +void eclic_set_pending(uint32_t source){ + *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_INT_IP_OFFSET+source*4) = 1; +} + +void eclic_clear_pending(uint32_t source){ + *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_INT_IP_OFFSET+source*4) = 0; +} + +void eclic_set_intctrl (uint32_t source, uint8_t intctrl){ + *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_INT_CTRL_OFFSET+source*4) = intctrl; +} + +uint8_t eclic_get_intctrl (uint32_t source){ + return *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_INT_CTRL_OFFSET+source*4); +} + +void eclic_set_intattr (uint32_t source, uint8_t intattr){ + *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_INT_ATTR_OFFSET+source*4) = intattr; +} + +uint8_t eclic_get_intattr (uint32_t source){ + return *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_INT_ATTR_OFFSET+source*4); +} + +void eclic_set_cliccfg (uint8_t cliccfg){ + *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_CFG_OFFSET) = cliccfg; +} + +uint8_t eclic_get_cliccfg (){ + return *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_CFG_OFFSET); +} + +void eclic_set_mth (uint8_t mth){ + *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_MTH_OFFSET) = mth; +} + +uint8_t eclic_get_mth (){ + return *(volatile uint8_t*)(ECLIC_ADDR_BASE+ECLIC_MTH_OFFSET); +} + +//sets nlbits +void eclic_set_nlbits(uint8_t nlbits) { + //shift nlbits to correct position + uint8_t nlbits_shifted = nlbits << ECLIC_CFG_NLBITS_LSB; + + //read the current cliccfg + uint8_t old_cliccfg = eclic_get_cliccfg(); + uint8_t new_cliccfg = (old_cliccfg & (~ECLIC_CFG_NLBITS_MASK)) | (ECLIC_CFG_NLBITS_MASK & nlbits_shifted); + + eclic_set_cliccfg(new_cliccfg); +} + +//get nlbits +uint8_t eclic_get_nlbits(void) { + //extract nlbits + uint8_t nlbits = eclic_get_cliccfg(); + nlbits = (nlbits & ECLIC_CFG_NLBITS_MASK) >> ECLIC_CFG_NLBITS_LSB; + return nlbits; +} + +//sets an interrupt level based encoding of nlbits and ECLICINTCTLBITS +void eclic_set_irq_lvl(uint32_t source, uint8_t lvl) { + //extract nlbits + uint8_t nlbits = eclic_get_nlbits(); + if (nlbits > ECLICINTCTLBITS) { + nlbits = ECLICINTCTLBITS; + } + + //shift lvl right to mask off unused bits + lvl = lvl >> (8-nlbits); + //shift lvl into correct bit position + lvl = lvl << (8-nlbits); + + //write to clicintctrl + uint8_t current_intctrl = eclic_get_intctrl(source); + //shift intctrl left to mask off unused bits + current_intctrl = current_intctrl << nlbits; + //shift intctrl into correct bit position + current_intctrl = current_intctrl >> nlbits; + + eclic_set_intctrl(source, (current_intctrl | lvl)); +} + +//gets an interrupt level based encoding of nlbits +uint8_t eclic_get_irq_lvl(uint32_t source) { + //extract nlbits + uint8_t nlbits = eclic_get_nlbits(); + if (nlbits > ECLICINTCTLBITS) { + nlbits = ECLICINTCTLBITS; + } + + uint8_t intctrl = eclic_get_intctrl(source); + + //shift intctrl + intctrl = intctrl >> (8-nlbits); + //shift intctrl + uint8_t lvl = intctrl << (8-nlbits); + + return lvl; +} + +void eclic_set_irq_lvl_abs(uint32_t source, uint8_t lvl_abs) { + //extract nlbits + uint8_t nlbits = eclic_get_nlbits(); + if (nlbits > ECLICINTCTLBITS) { + nlbits = ECLICINTCTLBITS; + } + + //shift lvl_abs into correct bit position + uint8_t lvl = lvl_abs << (8-nlbits); + + //write to clicintctrl + uint8_t current_intctrl = eclic_get_intctrl(source); + //shift intctrl left to mask off unused bits + current_intctrl = current_intctrl << nlbits; + //shift intctrl into correct bit position + current_intctrl = current_intctrl >> nlbits; + + eclic_set_intctrl(source, (current_intctrl | lvl)); +} + +uint8_t eclic_get_irq_lvl_abs(uint32_t source) { + //extract nlbits + uint8_t nlbits = eclic_get_nlbits(); + if (nlbits > ECLICINTCTLBITS) { + nlbits = ECLICINTCTLBITS; + } + + uint8_t intctrl = eclic_get_intctrl(source); + + //shift intctrl + intctrl = intctrl >> (8-nlbits); + //shift intctrl + uint8_t lvl_abs = intctrl; + + return lvl_abs; +} + +//sets an interrupt priority based encoding of nlbits and ECLICINTCTLBITS +uint8_t eclic_set_irq_priority(uint32_t source, uint8_t priority) { + //extract nlbits + uint8_t nlbits = eclic_get_nlbits(); + if (nlbits >= ECLICINTCTLBITS) { + nlbits = ECLICINTCTLBITS; + return 0; + } + + //shift priority into correct bit position + priority = priority << (8 - ECLICINTCTLBITS); + + //write to eclicintctrl + uint8_t current_intctrl = eclic_get_intctrl(source); + //shift intctrl right to mask off unused bits + current_intctrl = current_intctrl >> (8-nlbits); + //shift intctrl into correct bit position + current_intctrl = current_intctrl << (8-nlbits); + + eclic_set_intctrl(source, (current_intctrl | priority)); + + return priority; +} + +//gets an interrupt priority based encoding of nlbits +uint8_t eclic_get_irq_priority(uint32_t source) { + //extract nlbits + uint8_t nlbits = eclic_get_nlbits(); + if (nlbits > ECLICINTCTLBITS) { + nlbits = ECLICINTCTLBITS; + } + + uint8_t intctrl = eclic_get_intctrl(source); + + //shift intctrl + intctrl = intctrl << nlbits; + //shift intctrl + uint8_t priority = intctrl >> (nlbits+(8 - ECLICINTCTLBITS)); + + return priority; +} + +void eclic_mode_enable() { + uint32_t mtvec_value = read_csr(mtvec); + mtvec_value = mtvec_value & 0xFFFFFFC0; + mtvec_value = mtvec_value | 0x00000003; + write_csr(mtvec,mtvec_value); +} + +//sets vector-mode or non-vector mode +void eclic_set_vmode(uint32_t source) { + //read the current attr + uint8_t old_intattr = eclic_get_intattr(source); + // Keep other bits unchanged and only set the LSB bit + uint8_t new_intattr = (old_intattr | 0x1); + + eclic_set_intattr(source,new_intattr); +} + +void eclic_set_nonvmode(uint32_t source) { + //read the current attr + uint8_t old_intattr = eclic_get_intattr(source); + // Keep other bits unchanged and only clear the LSB bit + uint8_t new_intattr = (old_intattr & (~0x1)); + + eclic_set_intattr(source,new_intattr); +} + +//sets interrupt as level sensitive +//Bit 1, trig[0], is defined as "edge-triggered" (0: level-triggered, 1: edge-triggered); +//Bit 2, trig[1], is defined as "negative-edge" (0: positive-edge, 1: negative-edge). + +void eclic_set_level_trig(uint32_t source) { + //read the current attr + uint8_t old_intattr = eclic_get_intattr(source); + // Keep other bits unchanged and only clear the bit 1 + uint8_t new_intattr = (old_intattr & (~0x2)); + + eclic_set_intattr(source,new_intattr); +} + +void eclic_set_posedge_trig(uint32_t source) { + //read the current attr + uint8_t old_intattr = eclic_get_intattr(source); + // Keep other bits unchanged and only set the bit 1 + uint8_t new_intattr = (old_intattr | 0x2); + // Keep other bits unchanged and only clear the bit 2 + new_intattr = (old_intattr & (~0x4)); + + eclic_set_intattr(source,new_intattr); +} + +void eclic_set_negedge_trig(uint32_t source) { + //read the current attr + uint8_t old_intattr = eclic_get_intattr(source); + // Keep other bits unchanged and only set the bit 1 + uint8_t new_intattr = (old_intattr | 0x2); + // Keep other bits unchanged and only set the bit 2 + new_intattr = (old_intattr | 0x4); + + eclic_set_intattr(source,new_intattr); +} + +//void wfe() { +// core_wfe(); +//} + + +