
2 changed files with 446 additions and 0 deletions
@ -0,0 +1,48 @@ |
|||
// See LICENSE file for licence details
|
|||
|
|||
#ifndef N200_ECLIC_H |
|||
#define N200_ECLIC_H |
|||
|
|||
#include <riscv_const.h> |
|||
|
|||
#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 |
@ -0,0 +1,398 @@ |
|||
// See LICENSE for license details.
|
|||
#include <gd32vf103.h> |
|||
#include <stdio.h> |
|||
#include <stdlib.h> |
|||
#include <string.h> |
|||
#include <unistd.h> |
|||
|
|||
#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();
|
|||
//}
|
|||
|
|||
|
|||
|
Loading…
Reference in new issue