support fft on aiit-riscv64-board for nuttx

This commit is contained in:
wgz-code 2022-03-04 11:14:53 +08:00
parent 9946b33530
commit 0501122537
49 changed files with 20782 additions and 33 deletions

View File

@ -0,0 +1,6 @@
############################################################################
# Applications/knowing_app/Make.defs
############################################################################
ifneq ($(CONFIG_APPLICATION_KNOWING),)
include $(wildcard $(APPDIR)/../../../APP_Framework/Applications/knowing_app/*/Make.defs)
endif

View File

@ -0,0 +1,6 @@
############################################################################
# APP_Framework/Applications/knowing_app/k210_fft_test/Make.defs
############################################################################
ifneq ($(CONFIG_K210_FFT_TEST),)
CONFIGURED_APPS += $(APPDIR)/../../../APP_Framework/Applications/knowing_app/k210_fft_test
endif

View File

@ -0,0 +1,12 @@
include $(KERNEL_ROOT)/.config
ifeq ($(CONFIG_ADD_NUTTX_FETURES),y)
include $(APPDIR)/Make.defs
ifeq ($(CONFIG_K210_FFT_TEST), y)
CSRCS += fft_soft.c fft_test.c
endif
include $(APPDIR)/Application.mk
endif

View File

@ -71,7 +71,7 @@ void k210_fft_test(void)
data_soft[i].real = data_hard[i].real;
data_soft[i].imag = data_hard[i].imag;
}
for (int i = 0; i < FFT_N / 2; ++i)
for (i = 0; i < FFT_N / 2; ++i)
{
input_data = (fft_data_t *)&buffer_input[i];
input_data->R1 = data_hard[2 * i].real;
@ -118,7 +118,7 @@ void k210_fft_test(void)
printf("%3d : %f %f\n", i, hard_angel[i] * 180 / PI, soft_angel[i] * 180 / PI);
}
for (int i = 0; i < FFT_N / 2; ++i)
for (i = 0; i < FFT_N / 2; ++i)
{
input_data = (fft_data_t *)&buffer_input[i];
input_data->R1 = data_hard[2 * i].real;
@ -154,6 +154,24 @@ void k210_fft_test(void)
cycle[FFT_SOFT][FFT_DIR_FORWARD]/(sysctl_clock_get_freq(SYSCTL_CLOCK_CPU)/1000000),
cycle[FFT_SOFT][FFT_DIR_BACKWARD]/(sysctl_clock_get_freq(SYSCTL_CLOCK_CPU)/1000000));
}
#ifdef ADD_NUTTX_FETURES
void nuttx_k210_fft_test(void)
{
pthread_t thread;
int result;
pthread_attr_t attr = PTHREAD_ATTR_INITIALIZER;
attr.priority = 20;
attr.stacksize = 81920;
result = PrivTaskCreate(&thread, &attr, (void*)k210_fft_test, NULL);
if (result != 0)
{
printf("k210 fft test:task create failed, status=%d\n", result);
_exit(-1);
}
}
#endif
#ifdef __RT_THREAD_H__
MSH_CMD_EXPORT(k210_fft_test,k210 fft test );
#endif

View File

@ -647,6 +647,10 @@ config NSH_DISABLE_ADAPTER_LORATEST
bool "Disable sx128 AdapterLoraTest."
default n
config NSH_DISABLE_K210_FFT
bool "Disable the K210 fft device."
default n
endmenu
if MMCSD

View File

@ -1469,6 +1469,10 @@ int nsh_foreach_var(FAR struct nsh_vtbl_s *vtbl, nsh_foreach_var_t cb,
int cmd_AdapterLoraTest(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
#endif
#if defined(CONFIG_K210_FFT_TEST) && !defined(CONFIG_NSH_DISABLE_K210_FFT)
int cmd_fft(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv);
#endif
#if defined(__cplusplus)
}
#endif

View File

@ -46,6 +46,7 @@
#include <ctype.h>
#include <libgen.h>
#include <errno.h>
#include <debug.h>
#include "nsh.h"
#include "nsh_console.h"
@ -60,10 +61,10 @@ extern int FrameworkInit(void);
extern void HchoTb600bHcho1os(void);
int cmd_Hcho1os(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
HchoTb600bHcho1os();
return 0;
return OK;
}
#endif
@ -75,10 +76,10 @@ int cmd_Hcho1os(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern void TvocTb600bTvoc10(void);
int cmd_Tvoc10(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
TvocTb600bTvoc10();
return 0;
return OK;
}
#endif
@ -91,10 +92,10 @@ int cmd_Tvoc10(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern void IaqTb600bIaq10(void);
int cmd_Iaq10(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
IaqTb600bIaq10();
return 0;
return OK;
}
#endif
@ -106,10 +107,10 @@ int cmd_Iaq10(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern void Ch4As830(void);
int cmd_As830(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
Ch4As830();
return 0;
return OK;
}
#endif
@ -121,10 +122,10 @@ int cmd_As830(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern void Co2Zg09(void);
int cmd_Co2Zg09(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
Co2Zg09();
return 0;
return OK;
}
#endif
@ -136,10 +137,10 @@ int cmd_Co2Zg09(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern void Pm10Ps5308(void);
int cmd_Pm10Ps5308(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
Pm10Ps5308();
return 0;
return OK;
}
#endif
@ -151,10 +152,10 @@ int cmd_Pm10Ps5308(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern void Pm25Ps5308(void);
int cmd_Pm25Ps5308(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
Pm25Ps5308();
return 0;
return OK;
}
#endif
@ -166,10 +167,10 @@ int cmd_Pm25Ps5308(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern void Pm100Ps5308(void);
int cmd_Pm100Ps5308(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
Pm100Ps5308();
return 0;
return OK;
}
#endif
@ -181,10 +182,10 @@ int cmd_Pm100Ps5308(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern void VoiceD124(void);
int cmd_VoiceD124(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
VoiceD124();
return 0;
return OK;
}
#endif
@ -196,10 +197,10 @@ int cmd_VoiceD124(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern void TempHs300x(void);
int cmd_TempHs300x(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
TempHs300x();
return 0;
return OK;
}
#endif
@ -211,10 +212,10 @@ int cmd_TempHs300x(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern void HumiHs300x(void);
int cmd_HumiHs300x(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
HumiHs300x();
return 0;
return OK;
}
#endif
@ -227,10 +228,10 @@ int cmd_HumiHs300x(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern int openzigbee(void);
int cmd_openzigbee(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
openzigbee();
return 0;
return OK;
}
#endif
@ -239,10 +240,10 @@ int cmd_openzigbee(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern int sendzigbee(int argc, char *argv[]);
int cmd_sendzigbee(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
sendzigbee(argc,argv);
return 0;
return OK;
}
#endif
@ -250,10 +251,10 @@ int cmd_sendzigbee(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern int recvzigbee(void);
int cmd_recvzigbee(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
recvzigbee();
return 0;
return OK;
}
#endif
@ -261,9 +262,19 @@ int cmd_recvzigbee(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
extern int AdapterLoraTest(void);
int cmd_AdapterLoraTest(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
printf("Hello, world!\n");
nsh_output(vtbl, "Hello, world!\n");
FrameworkInit();
AdapterLoraTest();
return 0;
return OK;
}
#endif
#endif
#if defined(CONFIG_K210_FFT_TEST) && !defined(CONFIG_NSH_DISABLE_K210_FFT)
extern void nuttx_k210_fft_test(void);
int cmd_fft(FAR struct nsh_vtbl_s *vtbl, int argc, char **argv)
{
nsh_output(vtbl, "Hello, fft!\n");
nuttx_k210_fft_test();
return OK;
}
#endif

View File

@ -641,6 +641,10 @@ static const struct cmdmap_s g_cmdmap[] =
{ "AdapterLoraTest", cmd_AdapterLoraTest, 1, 1, "[Lora sx128 test.]" },
#endif
#if defined(CONFIG_K210_FFT_TEST) && !defined(CONFIG_NSH_DISABLE_K210_FFT)
{ "fft", cmd_fft, 1, 1, "[K210 fft function.]" },
#endif
{ NULL, NULL, 1, 1, NULL }
};

View File

@ -0,0 +1,251 @@
/****************************************************************************
* arch/risc-v/src/common/riscv_internal.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef __ARCH_RISCV_SRC_COMMON_UP_INTERNAL_H
#define __ARCH_RISCV_SRC_COMMON_UP_INTERNAL_H
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#ifndef __ASSEMBLY__
# include <nuttx/compiler.h>
# include <sys/types.h>
# include <stdint.h>
#endif
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* This is the value used to mark the stack for subsequent stack monitoring
* logic.
*/
#define STACK_COLOR 0xdeadbeef
#define INTSTACK_COLOR 0xdeadbeef
#define HEAP_COLOR 'h'
/* In the RISC_V model, the state is copied from the stack to the TCB, but
* only a referenced is passed to get the state from the TCB.
*/
#ifdef CONFIG_ARCH_RV64GC
#define riscv_savestate(regs) riscv_copystate(regs, (uint64_t*)CURRENT_REGS)
#define riscv_restorestate(regs) (CURRENT_REGS = regs)
#else
#define riscv_savestate(regs) riscv_copystate(regs, (uint32_t*)g_current_regs)
#define riscv_restorestate(regs) (g_current_regs = regs)
#endif
#define _START_TEXT &_stext
#define _END_TEXT &_etext
#define _START_BSS &_sbss
#define _END_BSS &_ebss
#define _DATA_INIT &_eronly
#define _START_DATA &_sdata
#define _END_DATA &_edata
#define _START_TDATA &_stdata
#define _END_TDATA &_etdata
#define _START_TBSS &_stbss
#define _END_TBSS &_etbss
/* Determine which (if any) console driver to use. If a console is enabled
* and no other console device is specified, then a serial console is
* assumed.
*/
#ifndef CONFIG_DEV_CONSOLE
# undef USE_SERIALDRIVER
# undef USE_EARLYSERIALINIT
#else
# if defined(CONFIG_CONSOLE_SYSLOG)
# undef USE_SERIALDRIVER
# undef USE_EARLYSERIALINIT
# else
# define USE_SERIALDRIVER 1
# define USE_EARLYSERIALINIT 1
# endif
#endif
/****************************************************************************
* Public Types
****************************************************************************/
#undef EXTERN
#if defined(__cplusplus)
#define EXTERN extern "C"
extern "C"
{
#else
#define EXTERN extern
#endif
#ifndef __ASSEMBLY__
#ifdef CONFIG_ARCH_RV64GC
#ifdef CONFIG_SMP
EXTERN volatile uint64_t *g_current_regs[CONFIG_SMP_NCPUS];
# define CURRENT_REGS (g_current_regs[up_cpu_index()])
#else
EXTERN volatile uint64_t *g_current_regs[1];
# define CURRENT_REGS (g_current_regs[0])
#endif
EXTERN uintptr_t g_idle_topstack;
#else
EXTERN volatile uint32_t *g_current_regs;
# define CURRENT_REGS (g_current_regs)
EXTERN uint32_t g_idle_topstack;
#endif
/* Address of the saved user stack pointer */
#if CONFIG_ARCH_INTERRUPTSTACK > 15
EXTERN uint32_t g_intstackalloc; /* Allocated stack base */
EXTERN uint32_t g_intstacktop; /* Initial top of interrupt stack */
#endif
/* These 'addresses' of these values are setup by the linker script. They
* are not actual uint32_t storage locations! They are only used meaningfully
* in the following way:
*
* - The linker script defines, for example, the symbol_sdata.
* - The declareion extern uint32_t _sdata; makes C happy. C will believe
* that the value _sdata is the address of a uint32_t variable _data (it
* is not!).
* - We can recoved the linker value then by simply taking the address of
* of _data. like: uint32_t *pdata = &_sdata;
*/
EXTERN uint32_t _stext; /* Start of .text */
EXTERN uint32_t _etext; /* End_1 of .text + .rodata */
EXTERN const uint32_t _eronly; /* End+1 of read only section (.text + .rodata) */
EXTERN uint32_t _sdata; /* Start of .data */
EXTERN uint32_t _edata; /* End+1 of .data */
EXTERN uint32_t _sbss; /* Start of .bss */
EXTERN uint32_t _ebss; /* End+1 of .bss */
EXTERN uint32_t _stdata; /* Start of .tdata */
EXTERN uint32_t _etdata; /* End+1 of .tdata */
EXTERN uint32_t _stbss; /* Start of .tbss */
EXTERN uint32_t _etbss; /* End+1 of .tbss */
#endif /* __ASSEMBLY__ */
/****************************************************************************
* Public Function Prototypes
***************************************************************************/
#ifndef __ASSEMBLY__
/* Memory allocation ********************************************************/
#if CONFIG_MM_REGIONS > 1
void riscv_addregion(void);
#else
# define riscv_addregion()
#endif
void up_allocat_eheap(FAR void **heap_start, size_t *heap_size);
/* IRQ initialization *******************************************************/
void up_irqinitialize(void);
void riscv_ack_irq(int irq);
#ifdef CONFIG_ARCH_RV64GC
void riscv_copystate(uint64_t *dest, uint64_t *src);
void riscv_copyfullstate(uint64_t *dest, uint64_t *src);
#else
void riscv_copystate(uint32_t *dest, uint32_t *src);
void riscv_copyfullstate(uint32_t *dest, uint32_t *src);
#endif
void riscv_sigdeliver(void);
int riscv_swint(int irq, void *context, void *arg);
uint32_t riscv_get_newintctx(void);
#ifdef CONFIG_ARCH_FPU
#ifdef CONFIG_ARCH_RV64GC
void riscv_savefpu(uint64_t *regs);
void riscv_restorefpu(const uint64_t *regs);
#else /* !CONFIG_ARCH_RV64GC */
void riscv_savefpu(uint32_t *regs);
void riscv_restorefpu(const uint32_t *regs);
#endif /* CONFIG_ARCH_RV64GC */
#else
# define riscv_savefpu(regs)
# define riscv_restorefpu(regs)
#endif
/* RISC-V PMP Config ********************************************************/
void riscv_config_pmp_region(uintptr_t region, uintptr_t attr,
uintptr_t base, uintptr_t size);
/* Power management *********************************************************/
void up_timer_initialize(void);
#ifdef CONFIG_PM
void riscv_pminitialize(void);
#else
# define riscv_pminitialize()
#endif
/* Low level serial output **************************************************/
void up_serialinit(void);
void riscv_lowputc(char ch);
void riscv_lowputs(const char *str);
void up_puts(const char *str);
#ifdef USE_SERIALDRIVER
void riscv_serialinit(void);
#endif
#ifdef USE_EARLYSERIALINIT
void riscv_earlyserialinit(void);
#endif
#ifdef CONFIG_RPMSG_UART
void rpmsg_serialinit(void);
#endif
/* Exception Handler ********************************************************/
void riscv_exception(uint32_t mcause, uint32_t *regs);
/* Debug ********************************************************************/
#ifdef CONFIG_STACK_COLORATION
void riscv_stack_color(void *stackbase, size_t nbytes);
#endif
/* The OS start routine **************************************************/
void nx_start(void);
#undef EXTERN
#ifdef __cplusplus
}
#endif
#endif /* __ASSEMBLY__ */
#endif /* __ARCH_RISCV_SRC_COMMON_UP_INTERNAL_H */

View File

@ -0,0 +1,45 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#
comment "K210 Configuration Options"
config K210_ENABLE_DPFPU
bool "K210 DP_FPU Support"
default y
select ARCH_HAVE_FPU
select ARCH_HAVE_DPFPU
select LIBC_FLOATINGPOINT
---help---
Enable the RISC-V Double-Precision Floating Point Unit (DP-FPU).
menu "K210 Peripheral Support"
# These "hidden" settings determine whether a peripheral option is available
# for the selected MCU
config K210_HAVE_UART0
bool
default y
select UART0_SERIALDRIVER
select ARCH_HAVE_SERIAL_TERMIOS
# These are the peripheral selections proper
config K210_UART0
bool "UART0"
default y
select ARCH_HAVE_UART0
select ARCH_HAVE_SERIAL_TERMIOS
select K210_UART
endmenu
menu "K210 Others"
config K210_WITH_QEMU
bool "qemu support"
default n
endmenu

View File

@ -0,0 +1,74 @@
############################################################################
# arch/risc-v/src/k210/Make.defs
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
# Specify our HEAD assembly file. This will be linked as
# the first object file, so it will appear at address 0
HEAD_ASRC = k210_vectors.S
# Specify our general Assembly files
CHIP_ASRCS = k210_head.S riscv_syscall.S
CMN_ASRCS += riscv_testset.S
# Specify C code within the common directory to be included
CMN_CSRCS += riscv_initialize.c riscv_swint.c
CMN_CSRCS += riscv_createstack.c riscv_exit.c riscv_fault.c
CMN_CSRCS += riscv_assert.c riscv_blocktask.c riscv_copystate.c riscv_initialstate.c
CMN_CSRCS += riscv_interruptcontext.c riscv_modifyreg32.c riscv_puts.c
CMN_CSRCS += riscv_releasepending.c riscv_reprioritizertr.c
CMN_CSRCS += riscv_releasestack.c riscv_stackframe.c riscv_schedulesigaction.c
CMN_CSRCS += riscv_sigdeliver.c riscv_unblocktask.c riscv_usestack.c
CMN_CSRCS += riscv_mdelay.c riscv_copyfullstate.c
ifeq ($(CONFIG_STACK_COLORATION),y)
CMN_CSRCS += riscv_checkstack.c
endif
ifeq ($(CONFIG_ARCH_HAVE_VFORK),y)
CMN_CSRCS += riscv_vfork.c
endif
ifeq ($(CONFIG_ARCH_FPU),y)
CMN_ASRCS += riscv_fpu.S
endif
# Specify our C code within this directory to be included
CHIP_CSRCS = k210_arch.c
CHIP_CSRCS += k210_allocateheap.c k210_clockconfig.c
CHIP_CSRCS += k210_idle.c k210_irq.c k210_irq_dispatch.c
CHIP_CSRCS += k210_lowputc.c k210_serial.c k210_fpioa.c
CHIP_CSRCS += k210_start.c k210_timerisr.c k210_gpiohs.c
CHIP_CSRCS += interrupt.c syscalls.c
CHIP_CSRCS += uarths.c plic.c sysctl.c fpioa.c clint.c gpio.c gpiohs.c utils.c
CHIP_CSRCS += dmac.c fft.c i2s.c apu.c
ifeq ($(CONFIG_SMP), y)
CHIP_CSRCS += k210_cpuidlestack.c k210_cpuindex.c
CHIP_CSRCS += k210_cpupause.c k210_cpustart.c
endif
ifeq ($(CONFIG_BUILD_PROTECTED),y)
CMN_CSRCS += riscv_task_start.c
CMN_CSRCS += riscv_pthread_start.c riscv_pthread_exit.c
CMN_CSRCS += riscv_signal_dispatch.c riscv_pmp.c
CMN_UASRCS += riscv_signal_handler.S
CHIP_CSRCS += k210_userspace.c
endif

View File

@ -0,0 +1,530 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <debug.h>
#include <stddef.h>
#include <stdint.h>
#include <math.h>
#include "syscalls.h"
#include "sysctl.h"
#include "apu.h"
#define BEAFORMING_BASE_ADDR (0x50250200)
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327950288
#endif
volatile apu_reg_t *const apu = (volatile apu_reg_t *)BEAFORMING_BASE_ADDR;
/*
Voice strength average value right shift factor. When performing sound direction detect,
the average value of samples from different channels is required, this right shift factor
is used to perform division.
0x0: no right shift; 0x1: right shift by 1-bit;
. . . . . .
0xF: right shift by 15-bit.
*/
void apu_set_audio_gain(uint16_t gain)
{
apu_ch_cfg_t ch_cfg = apu->bf_ch_cfg_reg;
ch_cfg.we_bf_target_dir = 0;
ch_cfg.we_bf_sound_ch_en = 0;
ch_cfg.we_data_src_mode = 0;
ch_cfg.we_audio_gain = 1;
ch_cfg.audio_gain = gain;
apu->bf_ch_cfg_reg = ch_cfg;
}
/*set sampling shift*/
void apu_set_smpl_shift(uint8_t smpl_shift)
{
apu_dwsz_cfg_t tmp = apu->bf_dwsz_cfg_reg;
tmp.smpl_shift_bits = smpl_shift;
apu->bf_dwsz_cfg_reg = tmp;
}
/*get sampling shift*/
uint8_t apu_get_smpl_shift(void)
{
apu_dwsz_cfg_t tmp = apu->bf_dwsz_cfg_reg;
return tmp.smpl_shift_bits;
}
/*
* APU unit sound channel enable control bits. Bit 'x' corresponds to enable bit for sound
* channel 'x' (x = 0, 1, 2, . . ., 7). APU sound channels are related with I2S host RX channels.
* APU sound channel 0/1 correspond to the left/right channel of I2S RX0; APU channel 2/3 correspond
* to left/right channels of I2S RX1; and things like that. Software write '1' to enable a sound
* channel and hardware automatically clear the bit after the sample buffers used for direction
* searching is filled full.
* 0x1: writing '1' to enable the corresponding APU sound channel.
*/
void apu_set_channel_enabled(uint8_t channel_bit)
{
apu_ch_cfg_t ch_cfg;
ch_cfg.we_audio_gain = 0;
ch_cfg.we_bf_target_dir = 0;
ch_cfg.we_bf_sound_ch_en = 1;
ch_cfg.bf_sound_ch_en = channel_bit;
apu->bf_ch_cfg_reg = ch_cfg;
}
/*
* APU unit sound channel enable control bits. Bit 'x' corresponds to enable bit for sound
* channel 'x' (x = 0, 1, 2, . . ., 7). APU sound channels are related with I2S host RX channels.
* APU sound channel 0/1 correspond to the left/right channel of I2S RX0; APU channel 2/3 correspond
* to left/right channels of I2S RX1; and things like that. Software write '1' to enable a sound
* channel and hardware automatically clear the bit after the sample buffers used for direction
* searching is filled full.
* 0x1: writing '1' to enable the corresponding APU sound channel.
*/
void apu_channel_enable(uint8_t channel_bit)
{
apu_ch_cfg_t ch_cfg = apu->bf_ch_cfg_reg;
ch_cfg.we_audio_gain = 0;
ch_cfg.we_bf_target_dir = 0;
ch_cfg.we_data_src_mode = 0;
ch_cfg.we_bf_sound_ch_en = 1;
ch_cfg.bf_sound_ch_en = channel_bit;
apu->bf_ch_cfg_reg = ch_cfg;
}
/*
* audio data source configure parameter. This parameter controls where the audio data source comes from.
* 0x0: audio data directly sourcing from apu internal buffer;
* 0x1: audio data sourcing from FFT result buffer.
*/
void apu_set_src_mode(uint8_t src_mode)
{
apu_ch_cfg_t ch_cfg = apu->bf_ch_cfg_reg;
ch_cfg.we_audio_gain = 0;
ch_cfg.we_bf_target_dir = 0;
ch_cfg.we_bf_sound_ch_en = 0;
ch_cfg.we_data_src_mode = 1;
ch_cfg.data_src_mode = src_mode;
apu->bf_ch_cfg_reg = ch_cfg;
}
/*
* I2S host beam-forming direction sample ibuffer read index configure register
*/
void apu_set_direction_delay(uint8_t dir_num, uint8_t *dir_bidx)
{
apu->bf_dir_bidx[dir_num][0] =(apu_dir_bidx_t)
{
.dir_rd_idx0 = dir_bidx[0],
.dir_rd_idx1 = dir_bidx[1],
.dir_rd_idx2 = dir_bidx[2],
.dir_rd_idx3 = dir_bidx[3]
};
apu->bf_dir_bidx[dir_num][1] =(apu_dir_bidx_t)
{
.dir_rd_idx0 = dir_bidx[4],
.dir_rd_idx1 = dir_bidx[5],
.dir_rd_idx2 = dir_bidx[6],
.dir_rd_idx3 = dir_bidx[7]
};
}
/*
* radius mic_num_a_circle: the num of mic per circle; center: 0: no center mic, 1:have center mic
*/
void apu_set_delay(float radius, uint8_t mic_num_a_circle, uint8_t center)
{
uint8_t offsets[16][8];
int i,j;
float seta[8], delay[8], hudu_jiao;
float cm_tick = (float)SOUND_SPEED * 100 / I2S_FS;/*distance per tick (cm)*/
float min;
for (i = 0; i < mic_num_a_circle; ++i)
{
seta[i] = 360 * i / mic_num_a_circle;
hudu_jiao = 2 * M_PI * seta[i] / 360;
delay[i] = radius * (1 - cos(hudu_jiao)) / cm_tick;
}
if(center)
delay[mic_num_a_circle] = radius / cm_tick;
for (i = 0; i < mic_num_a_circle + center; ++i)
{
offsets[0][i] = (int)(delay[i] + 0.5);
}
for(; i < 8; i++)
offsets[0][i] = 0;
for (j = 1; j < DIRECTION_RES; ++j)
{
for (i = 0; i < mic_num_a_circle; ++i)
{
seta[i] -= 360 / DIRECTION_RES;
hudu_jiao = 2 * M_PI * seta[i] / 360;
delay[i] = radius * (1 - cos(hudu_jiao)) / cm_tick;
}
if(center)
delay[mic_num_a_circle] = radius / cm_tick;
min = 2 * radius;
for (i = 0; i < mic_num_a_circle; ++i)
{
if(delay[i] < min)
min = delay[i];
}
if(min)
{
for (i = 0; i < mic_num_a_circle + center; ++i)
{
delay[i] = delay[i] - min;
}
}
for (i = 0; i < mic_num_a_circle + center; ++i)
{
offsets[j][i] = (int)(delay[i] + 0.5);
}
for(; i < 8; i++)
offsets[0][i] = 0;
}
for (i = 0; i < DIRECTION_RES; i++)
{
apu_set_direction_delay(i, offsets[i]);
}
}
/*
Sound direction searching enable bit. Software writes '1' to start sound direction searching function.
When all the sound sample buffers are filled full, this bit is cleared by hardware (this sample buffers
are used for direction detect only).
0x1: enable direction searching.
*/
void apu_dir_enable(void)
{
apu_ctl_t bf_en_tmp = apu->bf_ctl_reg;
bf_en_tmp.we_bf_dir_search_en = 1;
bf_en_tmp.bf_dir_search_en = 1;
apu->bf_ctl_reg = bf_en_tmp;
}
void apu_dir_reset(void)
{
apu_ctl_t bf_en_tmp = apu->bf_ctl_reg;
bf_en_tmp.we_search_path_rst = 1;
bf_en_tmp.search_path_reset = 1;
apu->bf_ctl_reg = bf_en_tmp;
}
/*
Valid voice sample stream generation enable bit. After sound direction searching is done, software can
configure this bit to generate a stream of voice samples for voice recognition.
0x1: enable output of voice sample stream.
0x0: stop the voice samlpe stream output.
*/
void apu_voc_enable(uint8_t enable_flag)
{
apu_ctl_t bf_en_tmp = apu->bf_ctl_reg;
bf_en_tmp.we_bf_stream_gen = 1;
bf_en_tmp.bf_stream_gen_en = enable_flag;
apu->bf_ctl_reg = bf_en_tmp;
}
void apu_voc_reset(void)
{
apu_ctl_t bf_en_tmp = apu->bf_ctl_reg;
bf_en_tmp.we_voice_gen_path_rst = 1;
bf_en_tmp.voice_gen_path_reset = 1;
apu->bf_ctl_reg = bf_en_tmp;
}
/*
Target direction select for valid voice output. When the source voice direaction searching
is done, software can use this field to select one from 16 sound directions for the following
voice recognition
0x0: select sound direction 0; 0x1: select sound direction 1;
. . . . . .
0xF: select sound direction 15.
*/
void apu_voc_set_direction(en_bf_dir_t direction)
{
apu_ch_cfg_t ch_cfg = apu->bf_ch_cfg_reg;
ch_cfg.we_bf_sound_ch_en = 0;
ch_cfg.we_audio_gain = 0;
ch_cfg.we_data_src_mode = 0;
ch_cfg.we_bf_target_dir = 1;
ch_cfg.bf_target_dir = direction;
apu->bf_ch_cfg_reg = ch_cfg;
apu_ctl_t bf_en_tmp = apu->bf_ctl_reg;
bf_en_tmp.we_update_voice_dir = 1;
bf_en_tmp.update_voice_dir = 1;
apu->bf_ctl_reg = bf_en_tmp;
}
/*
*I2S host beam-forming Filter FIR16 Coefficient Register
*/
void apu_dir_set_prev_fir(uint16_t *fir_coef)
{
uint8_t i = 0;
for (i = 0; i < 9; i++) {
apu->bf_pre_fir0_coef[i] =
(apu_fir_coef_t){
.fir_tap0 = fir_coef[i * 2],
.fir_tap1 = i == 8 ? 0 : fir_coef[i * 2 + 1]
};
}
}
void apu_dir_set_post_fir(uint16_t *fir_coef)
{
uint8_t i = 0;
for (i = 0; i < 9; i++) {
apu->bf_post_fir0_coef[i] =
(apu_fir_coef_t){
.fir_tap0 = fir_coef[i * 2],
.fir_tap1 = i == 8 ? 0 : fir_coef[i * 2 + 1]
};
}
}
void apu_voc_set_prev_fir(uint16_t *fir_coef)
{
uint8_t i = 0;
for (i = 0; i < 9; i++) {
apu->bf_pre_fir1_coef[i] =
(apu_fir_coef_t){
.fir_tap0 = fir_coef[i * 2],
.fir_tap1 = i == 8 ? 0 : fir_coef[i * 2 + 1]
};
}
}
void apu_voc_set_post_fir(uint16_t *fir_coef)
{
uint8_t i = 0;
for (i = 0; i < 9; i++) {
apu->bf_post_fir1_coef[i] =
(apu_fir_coef_t){
.fir_tap0 = fir_coef[i * 2],
.fir_tap1 = i == 8 ? 0 : fir_coef[i * 2 + 1]
};
}
}
void apu_set_fft_shift_factor(uint8_t enable_flag, uint16_t shift_factor)
{
apu->bf_fft_cfg_reg =
(apu_fft_cfg_t){
.fft_enable = enable_flag,
.fft_shift_factor = shift_factor
};
apu_ch_cfg_t ch_cfg = apu->bf_ch_cfg_reg;
ch_cfg.we_data_src_mode = 1;
ch_cfg.data_src_mode = enable_flag;
apu->bf_ch_cfg_reg = ch_cfg;
}
void apu_dir_set_down_size(uint8_t dir_dwn_size)
{
apu_dwsz_cfg_t tmp = apu->bf_dwsz_cfg_reg;
tmp.dir_dwn_siz_rate = dir_dwn_size;
apu->bf_dwsz_cfg_reg = tmp;
}
void apu_dir_set_interrupt_mask(uint8_t dir_int_mask)
{
apu_int_mask_t tmp = apu->bf_int_mask_reg;
tmp.dir_data_rdy_msk = dir_int_mask;
apu->bf_int_mask_reg = tmp;
}
void apu_voc_set_down_size(uint8_t voc_dwn_size)
{
apu_dwsz_cfg_t tmp = apu->bf_dwsz_cfg_reg;
tmp.voc_dwn_siz_rate = voc_dwn_size;
apu->bf_dwsz_cfg_reg = tmp;
}
void apu_voc_set_interrupt_mask(uint8_t voc_int_mask)
{
apu_int_mask_t tmp = apu->bf_int_mask_reg;
tmp.voc_buf_rdy_msk = voc_int_mask;
apu->bf_int_mask_reg = tmp;
}
void apu_set_down_size(uint8_t dir_dwn_size, uint8_t voc_dwn_size)
{
apu_dwsz_cfg_t tmp = apu->bf_dwsz_cfg_reg;
tmp.dir_dwn_siz_rate = dir_dwn_size;
tmp.voc_dwn_siz_rate = voc_dwn_size;
apu->bf_dwsz_cfg_reg = tmp;
}
void apu_set_interrupt_mask(uint8_t dir_int_mask, uint8_t voc_int_mask)
{
apu->bf_int_mask_reg =
(apu_int_mask_t){
.dir_data_rdy_msk = dir_int_mask,
.voc_buf_rdy_msk = voc_int_mask
};
}
void apu_dir_clear_int_state(void)
{
apu->bf_int_stat_reg =
(apu_int_stat_t){
.dir_search_data_rdy = 1
};
}
void apu_voc_clear_int_state(void)
{
apu->bf_int_stat_reg =
(apu_int_stat_t){
.voc_buf_data_rdy = 1
};
}
/* reset saturation_counter */
void apu_voc_reset_saturation_counter(void)
{
apu->saturation_counter = 1<<31;
}
/*get saturation counter*/
/*heigh 16 bit is counter, low 16 bit is total.*/
uint32_t apu_voc_get_saturation_counter(void)
{
return apu->saturation_counter;
}
/*set saturation limit*/
void apu_voc_set_saturation_limit(uint16_t upper, uint16_t bottom)
{
apu->saturation_limits = (uint32_t)bottom<<16 | upper;
}
/*get saturation limit*/
/*heigh 16 bit is counter, low 16 bit is total.*/
uint32_t apu_voc_get_saturation_limit(void)
{
return apu->saturation_limits;
}
static void print_fir(const char *member_name, volatile apu_fir_coef_t *pfir)
{
syslog(LOG_INFO," for(int i = 0; i < 9; i++){\n");
for (int i = 0; i < 9; i++) {
apu_fir_coef_t fir = pfir[i];
syslog(LOG_INFO," apu->%s[%d] = (apu_fir_coef_t){\n", member_name, i);
syslog(LOG_INFO," .fir_tap0 = 0x%x,\n", fir.fir_tap0);
syslog(LOG_INFO," .fir_tap1 = 0x%x\n", fir.fir_tap1);
syslog(LOG_INFO," };\n");
}
syslog(LOG_INFO," }\n");
}
void apu_print_setting(void)
{
syslog(LOG_INFO,"void apu_setting(void) {\n");
apu_ch_cfg_t bf_ch_cfg_reg = apu->bf_ch_cfg_reg;
syslog(LOG_INFO," apu->bf_ch_cfg_reg = (apu_ch_cfg_t){\n");
syslog(LOG_INFO," .we_audio_gain = 1, .we_bf_target_dir = 1, .we_bf_sound_ch_en = 1,\n");
syslog(LOG_INFO," .audio_gain = 0x%x, .bf_target_dir = %d, .bf_sound_ch_en = %d, .data_src_mode = %d\n",
bf_ch_cfg_reg.audio_gain, bf_ch_cfg_reg.bf_target_dir, bf_ch_cfg_reg.bf_sound_ch_en, bf_ch_cfg_reg.data_src_mode);
syslog(LOG_INFO," };\n");
apu_ctl_t bf_ctl_reg = apu->bf_ctl_reg;
syslog(LOG_INFO," apu->bf_ctl_reg = (apu_ctl_t){\n");
syslog(LOG_INFO," .we_bf_stream_gen = 1, .we_bf_dir_search_en = 1,\n");
syslog(LOG_INFO," .bf_stream_gen_en = %d, .bf_dir_search_en = %d\n",
bf_ctl_reg.bf_stream_gen_en, bf_ctl_reg.bf_dir_search_en);
syslog(LOG_INFO," };\n");
syslog(LOG_INFO," for(int i = 0; i < 16; i++){\n");
for (int i = 0; i < 16; i++) {
apu_dir_bidx_t bidx0 = apu->bf_dir_bidx[i][0];
apu_dir_bidx_t bidx1 = apu->bf_dir_bidx[i][1];
syslog(LOG_INFO," apu->bf_dir_bidx[%d][0] = (apu_dir_bidx_t){\n", i);
syslog(LOG_INFO," .dir_rd_idx0 = 0x%x,\n", bidx0.dir_rd_idx0);
syslog(LOG_INFO," .dir_rd_idx1 = 0x%x,\n", bidx0.dir_rd_idx1);
syslog(LOG_INFO," .dir_rd_idx2 = 0x%x,\n", bidx0.dir_rd_idx2);
syslog(LOG_INFO," .dir_rd_idx3 = 0x%x\n", bidx0.dir_rd_idx3);
syslog(LOG_INFO," };\n");
syslog(LOG_INFO," apu->bf_dir_bidx[%d][1] = (apu_dir_bidx_t){\n", i);
syslog(LOG_INFO," .dir_rd_idx0 = 0x%x,\n", bidx1.dir_rd_idx0);
syslog(LOG_INFO," .dir_rd_idx1 = 0x%x,\n", bidx1.dir_rd_idx1);
syslog(LOG_INFO," .dir_rd_idx2 = 0x%x,\n", bidx1.dir_rd_idx2);
syslog(LOG_INFO," .dir_rd_idx3 = 0x%x\n", bidx1.dir_rd_idx3);
syslog(LOG_INFO," };\n");
}
syslog(LOG_INFO," }\n");
print_fir("bf_pre_fir0_coef", apu->bf_pre_fir0_coef);
print_fir("bf_post_fir0_coef", apu->bf_post_fir0_coef);
print_fir("bf_pre_fir1_coef", apu->bf_pre_fir1_coef);
print_fir("bf_post_fir1_coef", apu->bf_post_fir1_coef);
apu_dwsz_cfg_t bf_dwsz_cfg_reg = apu->bf_dwsz_cfg_reg;
syslog(LOG_INFO," apu->bf_dwsz_cfg_reg = (apu_dwsz_cfg_t){\n");
syslog(LOG_INFO," .dir_dwn_siz_rate = %d, .voc_dwn_siz_rate = %d\n",
bf_dwsz_cfg_reg.dir_dwn_siz_rate, bf_dwsz_cfg_reg.voc_dwn_siz_rate);
syslog(LOG_INFO," };\n");
apu_fft_cfg_t bf_fft_cfg_reg = apu->bf_fft_cfg_reg;
syslog(LOG_INFO," apu->bf_fft_cfg_reg = (apu_fft_cfg_t){\n");
syslog(LOG_INFO," .fft_enable = %d, .fft_shift_factor = 0x%x\n",
bf_fft_cfg_reg.fft_enable, bf_fft_cfg_reg.fft_shift_factor);
syslog(LOG_INFO," };\n");
apu_int_mask_t bf_int_mask_reg = apu->bf_int_mask_reg;
syslog(LOG_INFO," apu->bf_int_mask_reg = (apu_int_mask_t){\n");
syslog(LOG_INFO," .dir_data_rdy_msk = %d, .voc_buf_rdy_msk = %d\n",
bf_int_mask_reg.dir_data_rdy_msk, bf_int_mask_reg.voc_buf_rdy_msk);
syslog(LOG_INFO," };\n");
syslog(LOG_INFO,"}\n");
}

View File

@ -0,0 +1,560 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _apu_H_
#define _apu_H_
#if defined(__cplusplus)
extern "C" {
#endif
#define DIRECTION_RES 16
#define I2S_FS 44100
#define SOUND_SPEED 340
typedef enum en_bf_dir
{
APU_DIR0 = 0,
APU_DIR1,
APU_DIR2,
APU_DIR3,
APU_DIR4,
APU_DIR5,
APU_DIR6,
APU_DIR7,
APU_DIR8,
APU_DIR9,
APU_DIR10,
APU_DIR11,
APU_DIR12,
APU_DIR13,
APU_DIR14,
APU_DIR15,
} en_bf_dir_t;
typedef struct _apu_ch_cfg
{
/**
* BF unit sound channel enable control bits.
* Bit 'x' corresponds to enable bit for sound channel 'x' (x = 0, 1, 2,
* . . ., 7). BF sound channels are related with I2S host RX channels.
* BF sound channel 0/1 correspond to the left/right channel of I2S RX0;
* BF channel 2/3 correspond to left/right channels of I2S RX1; and
* things like that. 0x1: writing '1' to enable the corresponding BF
* sound channel. 0x0: writing '0' to close the corresponding BF sound
* channel.
*/
uint32_t bf_sound_ch_en : 8;
/**
* Target direction select for valid voice output.
* When the source voice direaction searching is done, software can use
* this field to select one from 16 sound directions for the following
* voice recognition. 0x0: select sound direction 0; 0x1: select sound
* direction 1; . . . . . . 0xF: select sound direction 15.
*/
uint32_t bf_target_dir : 4;
/**
* This is the audio sample gain factor. Using this gain factor to
* enhance or reduce the stength of the sum of at most 8 source
* sound channel outputs. This is a unsigned 11-bit fix-point number,
* bit 10 is integer part and bit 9~0 are the fractional part.
*/
uint32_t audio_gain : 11;
uint32_t reserved1 : 1;
/**
* audio data source configure parameter. This parameter controls where
* the audio data source comes from. 0x0: audio data directly sourcing
* from apu internal buffer; 0x1: audio data sourcing from
* FFT result buffer.
*/
uint32_t data_src_mode : 1;
uint32_t reserved2 : 3;
/**
* write enable for bf_sound_ch_en parameter.
* 0x1: allowing updates made to 'bf_sound_ch_en'.
* Access Mode: write only
*/
uint32_t we_bf_sound_ch_en : 1;
/**
* write enable for bf_target_dir parameter.
* 0x1: allowing updates made to 'bf_target_dir'.
* Access Mode: write only
*/
uint32_t we_bf_target_dir : 1;
/**
* write enable for audio_gain parameter.
* 0x1: allowing updates made to 'audio_gain'.
* Access Mode: write only
*/
uint32_t we_audio_gain : 1;
/**
* write enable for data_out_mode parameter.
* 0x1: allowing updates made to 'data_src_mode'.
*/
uint32_t we_data_src_mode : 1;
} __attribute__((packed, aligned(4))) apu_ch_cfg_t;
typedef struct _apu_ctl_t
{
/**
* Sound direction searching enable bit.
* Software writes '1' to start sound direction searching function.
* When all the sound sample buffers are filled full, this bit is
* cleared by hardware (this sample buffers are used for direction
* detect only). 0x1: enable direction searching.
*/
uint32_t bf_dir_search_en : 1;
/*
*use this parameter to reset all the control logic on direction search processing path. This bit is self-clearing.
* 0x1: apply reset to direction searching control logic;
* 0x0: No operation.
*/
uint32_t search_path_reset : 1;
uint32_t reserved : 2;
/**
* Valid voice sample stream generation enable bit.
* After sound direction searching is done, software can configure this
* bit to generate a stream of voice samples for voice recognition. 0x1:
* enable output of voice sample stream. 0x0: stop the voice samlpe
* stream output.
*/
uint32_t bf_stream_gen_en : 1;
/*
*use this parameter to reset all the control logic on voice stream generating path. This bit is self-clearing.
* 0x1: apply reset to voice stream generating control logic;
* 0x0: No operation.
*/
uint32_t voice_gen_path_reset : 1;
/*
*use this parameter to switch to a new voice source direction. Software write '1' here and hardware will automatically clear it.
* 0x1: write '1' here to request switching to new voice source direction.
*/
uint32_t update_voice_dir : 1;
uint32_t reserved1 : 1;
//write enable for 'bf_dir_search_en' parameter.
uint32_t we_bf_dir_search_en : 1;
uint32_t we_search_path_rst : 1;
uint32_t we_bf_stream_gen : 1;
uint32_t we_voice_gen_path_rst : 1;
uint32_t we_update_voice_dir : 1;
uint32_t reserved2 : 19;
} __attribute__((packed, aligned(4))) apu_ctl_t;
typedef struct _apu_dir_bidx
{
uint32_t dir_rd_idx0 : 6;
uint32_t reserved : 2;
uint32_t dir_rd_idx1 : 6;
uint32_t reserved1 : 2;
uint32_t dir_rd_idx2 : 6;
uint32_t reserved2 : 2;
uint32_t dir_rd_idx3 : 6;
uint32_t reserved3 : 2;
} __attribute__((packed, aligned(4))) apu_dir_bidx_t;
typedef struct _apu_fir_coef
{
uint32_t fir_tap0 : 16;
uint32_t fir_tap1 : 16;
} __attribute__((packed, aligned(4))) apu_fir_coef_t;
typedef struct _apu_dwsz_cfg
{
/**
* The down-sizing ratio used for direction searching.
* 0x0: no down-sizing;
* 0x1: 1/2 down sizing;
* 0x2: 1/3 down sizing;
* . . . . . .
* 0xF: 1/16 down sizing.
*/
uint32_t dir_dwn_siz_rate : 4;
/**
* The down-sizing ratio used for voice stream generation.
* 0x0: no down-sizing;
* 0x1: 1/2 down sizing;
* 0x2: 1/3 down sizing;
* . . . . . .
* 0xF: 1/16 down sizing.
*/
uint32_t voc_dwn_siz_rate : 4;
/**
* This bit field is used to perform sample precision reduction when
* the source sound sample (from I2S0 host receiving channels)
* precision is 20/24/32 bits.
* 0x0: take bits 15~0 from the source sound sample;
* 0x1: take bits 16~1 from the source sound sample;
* 0x2: take bits 17~2 from the source sound sample;
* . . . . . .
* 0x10: take bits 31~16 from the source sound sample;
*/
uint32_t smpl_shift_bits : 5;
uint32_t reserved : 19;
} __attribute__((packed, aligned(4))) apu_dwsz_cfg_t;
/*0x31c*/
typedef struct _apu_fft_cfg
{
uint32_t fft_shift_factor : 9;
uint32_t reserved1 : 3;
uint32_t fft_enable : 1;
uint32_t reserved2 : 19;
} __attribute__((packed, aligned(4))) apu_fft_cfg_t;
/*0x328*/
typedef struct _apu_int_stat
{
/**
* sound direction searching data ready interrupt event.
* Writing '1' to clear this interrupt event.
* 0x1: data is ready for sound direction detect;
* 0x0: no event.
*/
uint32_t dir_search_data_rdy : 1;
/**
* voice output stream buffer data ready interrupt event.
* When a block of 512 voice samples are collected, this interrupt event
* is asserted. Writing '1' to clear this interrupt event. 0x1: voice
* output stream buffer data is ready; 0x0: no event.
*/
uint32_t voc_buf_data_rdy : 1;
uint32_t reserved : 30;
} __attribute__((packed, aligned(4))) apu_int_stat_t;
/*0x32c*/
typedef struct _apu_int_mask
{
/**
* This is the interrupt mask to dir searching data ready interrupt.
* 0x1: mask off this interrupt;
* 0x0: enable this interrupt.
*/
uint32_t dir_data_rdy_msk : 1;
/**
* This is the interrupt mask to voice output stream buffer ready
* interrupt. 0x1: mask off this interrupt; 0x0: enable this interrupt.
*/
uint32_t voc_buf_rdy_msk : 1;
uint32_t reserved : 30;
} __attribute__((packed, aligned(4))) apu_int_mask_t;
typedef struct _apu_reg
{
//0x200
apu_ch_cfg_t bf_ch_cfg_reg;
//0x204
apu_ctl_t bf_ctl_reg;
//0x208
apu_dir_bidx_t bf_dir_bidx[16][2];
//0x288
apu_fir_coef_t bf_pre_fir0_coef[9];
//0x2ac
apu_fir_coef_t bf_post_fir0_coef[9];
//0x2d0
apu_fir_coef_t bf_pre_fir1_coef[9];
//0x2f4
apu_fir_coef_t bf_post_fir1_coef[9];
//0x318
apu_dwsz_cfg_t bf_dwsz_cfg_reg;
//0x31c
apu_fft_cfg_t bf_fft_cfg_reg;
// 0x320
/**
* This is the read register for system DMA to read data stored in
* sample out buffers (the sample out buffers are used for sound
* direction detect). Each data contains two sound samples.
*/
volatile uint32_t sobuf_dma_rdata;
// 0x324
/**
* This is the read register for system DMA to read data stored in voice
* out buffers (the voice out buffers are used for voice recognition).
* Each data contains two sound samples.
*/
volatile uint32_t vobuf_dma_rdata;
/*0x328*/
apu_int_stat_t bf_int_stat_reg;
/*0x32c*/
apu_int_mask_t bf_int_mask_reg;
/*0x330*/
uint32_t saturation_counter;
/*0x334*/
uint32_t saturation_limits;
} __attribute__((packed, aligned(4))) apu_reg_t;
extern volatile apu_reg_t *const apu;
/**
* @brief Voice strength average value right shift factor. When performing sound direction detect,
* the average value of samples from different channels is required, this right shift factor
* is used to perform division.
*
* @param[in] gain value of audio gain.
* 0x0: no right shift;
* 0x1: right shift by 1-bit;
* . . . . . .
* 0xF: right shift by 15-bit.
*
*/
void apu_set_audio_gain(uint16_t gain);
/**
* @brief Set sampling shift.
*
* @param[in] smpl_shift vlaue of sampling shift
*
*/
void apu_set_smpl_shift(uint8_t smpl_shift);
/**
* @brief Get sampling shift
*
* @return vlaue of sampling shift
*/
uint8_t apu_get_smpl_shift(void);
/**
* @brief APU unit sound channel enable control bits. Bit 'x' corresponds to enable bit for sound
* channel 'x' (x = 0, 1, 2, . . ., 7). APU sound channels are related with I2S host RX channels.
* APU sound channel 0/1 correspond to the left/right channel of I2S RX0; APU channel 2/3 correspond
* to left/right channels of I2S RX1; and things like that. Software write '1' to enable a sound
* channel and hardware automatically clear the bit after the sample buffers used for direction
* searching is filled full.
*
*
* @param[in] channel_bit APU sound channel.0x1: writing
* '1' to enable the corresponding APU sound channel.
*
*/
void apu_set_channel_enabled(uint8_t channel_bit);
/**
* @brief I2S host beam-forming direction sample ibuffer read index configure register
*
* @param[in] dir_num the direction of index
* @param[in] dir_bidx
*
*/
void apu_set_direction_delay(uint8_t dir_num, uint8_t *dir_bidx);
/**
* @brief I2S host beam-forming direction sample ibuffer read index configure register
*
* @param[in] radius radius
* @param[in] mic_num_a_circle the num of mic per circle
* @param[in] center 0: no center mic, 1:have center mic
*
*/
void apu_set_delay(float radius, uint8_t mic_num_a_circle, uint8_t center);
/**
* @brief Set ffp shift factor
*
* @param[in] enable_flag enable fft
* @param[in] shift_factor shift factor
*
*/
void apu_set_fft_shift_factor(uint8_t enable_flag, uint16_t shift_factor);
/**
* @brief Set down-sizing ratio used for voice direction searching and voice stream generation.
*
* @param[in] dir_dwn_siz down-sizing ratio used for voice direction searching
* 0x0: no down-sizing
* 0x1: 1/2 down sizing
* 0x2: 1/3 down sizing
* . . . . . .
* 0xF: 1/16 down sizing
* @param[in] voc_dwn_siz down-sizing ratio used for voice stream generation
* 0x0: no down-sizing
* 0x1: 1/2 down sizing
* 0x2: 1/3 down sizing
* . . . . . .
* 0xF: 1/16 down sizing
*/
void apu_set_down_size(uint8_t dir_dwn_siz, uint8_t voc_dwn_siz);
/**
* @brief Set direction and voice interrupt mask
*
* @param[in] dir_int_mask direction interrupt mask
* @param[in] voc_int_mask voice interrupt mask
*
*/
void apu_set_interrupt_mask(uint8_t dir_int_mask, uint8_t voc_int_mask);
/**
* @brief Enable direction searching.
*
*/
void apu_dir_enable(void);
/**
* @brief Reset direction searching.
*
*/
void apu_dir_reset(void);
/**
* @brief I2S host beam-forming Filter FIR16 Coefficient Register
*
* @param[in] fir_coef direction prev FIR
*
*/
void apu_dir_set_prev_fir(uint16_t *fir_coef);
/**
* @brief I2S host beam-forming Filter FIR16 Coefficient Register
*
* @param[in] fir_coef direction post FIR
*
*/
void apu_dir_set_post_fir(uint16_t *fir_coef);
/**
* @brief Set down-sizing ratio used for voice direction searching
*
* @param[in] dir_dwn_siz down-sizing ratio used for voice direction searching
* 0x0: no down-sizing
* 0x1: 1/2 down sizing
* 0x2: 1/3 down sizing
* . . . . . .
* 0xF: 1/16 down sizing
*/
void apu_dir_set_down_size(uint8_t dir_dwn_size);
/**
* @brief Set direction searching interrupt mask
*
* @param[in] dir_int_mask direction interrupt mask
*
*/
void apu_dir_set_interrupt_mask(uint8_t dir_int_mask);
/**
* @brief Clear direction interrupt
*
*/
void apu_dir_clear_int_state(void);
/**
* @brief Valid voice sample stream generation enable bit. After sound direction searching is done, software can
* configure this bit to generate a stream of voice samples for voice recognition.
*
* @param[in] enable_flag 0x1: enable output of voice sample stream. 0x0: stop the voice samlpe stream output.
*
*/
void apu_voc_enable(uint8_t enable_flag);
/**
* @brief Reset voice sample
*
*/
void apu_voc_reset(void);
/**
* @brief Target direction select for valid voice output. When the source voice direaction searching
* is done, software can use this field to select one from 16 sound directions for the following
* voice recognition
*
* @param[in] direction 0x0: select sound direction 0;
* 0x1: select sound direction 1;
* . . . . . .
* 0xF: select sound direction 15.
*/
void apu_voc_set_direction(en_bf_dir_t direction);
/**
* @brief I2S host beam-forming Filter FIR16 Coefficient Register
*
* @param[in] fir_coef voice prev FIR
*
*/
void apu_voc_set_prev_fir(uint16_t *fir_coef);
/**
* @brief I2S host beam-forming Filter FIR16 Coefficient Register
*
* @param[in] fir_coef voice post FIR
*
*/
void apu_voc_set_post_fir(uint16_t *fir_coef);
/**
* @brief Set down-sizing ratio used for voice stream generation.
*
* @param[in] voc_dwn_siz down-sizing ratio used for voice stream generation
* 0x0: no down-sizing
* 0x1: 1/2 down sizing
* 0x2: 1/3 down sizing
* . . . . . .
* 0xF: 1/16 down sizing
*/
void apu_voc_set_down_size(uint8_t voc_dwn_size);
/**
* @brief Set voice stream generation interrupt mask
*
* @param[in] voc_int_mask voice interrupt mask
*
*/
void apu_voc_set_interrupt_mask(uint8_t voc_int_mask);
/**
* @brief Clear voice interrupt
*
*/
void apu_voc_clear_int_state(void);
/**
* @brief Reset saturation_counter
*
*/
void apu_voc_reset_saturation_counter(void);
/**
* @brief Get saturation counter
*
* @return vlaue of saturation counter.heigh 16 bit is counter, low 16 bit is total
*/
uint32_t apu_voc_get_saturation_counter(void);
/**
* @brief set saturation limit
*
* @param[in] upper heigh 16 bit is counter
* @param[in] bottom low 16 bit is total
*
*/
void apu_voc_set_saturation_limit(uint16_t upper, uint16_t bottom);
/**
* @brief Get saturation limit
*
* @return vlaue of saturation limit.heigh 16 bit is counter, low 16 bit is total
*/
uint32_t apu_voc_get_saturation_limit(void);
/**
* @brief Print apu setting for debug
*
*/
void apu_print_setting(void);
#if defined(__cplusplus)
}
#endif
#endif

View File

@ -0,0 +1,244 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _BSP_ATOMIC_H
#define _BSP_ATOMIC_H
#ifdef __cplusplus
extern "C" {
#endif
#define SPINLOCK_INIT \
{ \
0 \
}
#define CORELOCK_INIT \
{ \
.lock = SPINLOCK_INIT, \
.count = 0, \
.core = -1 \
}
/* Defination of memory barrier macro */
#define mb() \
{ \
asm volatile("fence" :: \
: "memory"); \
}
#define atomic_set(ptr, val) (*(volatile typeof(*(ptr))*)(ptr) = val)
#define atomic_read(ptr) (*(volatile typeof(*(ptr))*)(ptr))
#ifndef __riscv_atomic
#error "atomic extension is required."
#endif
#define atomic_add(ptr, inc) __sync_fetch_and_add(ptr, inc)
#define atomic_or(ptr, inc) __sync_fetch_and_or(ptr, inc)
#define atomic_swap(ptr, swp) __sync_lock_test_and_set(ptr, swp)
#define atomic_cas(ptr, cmp, swp) __sync_val_compare_and_swap(ptr, cmp, swp)
typedef struct _spinlock
{
int lock;
} spinlock_t;
typedef struct _semaphore
{
spinlock_t lock;
int count;
int waiting;
} semaphore_t;
typedef struct _corelock
{
spinlock_t lock;
int count;
int core;
} corelock_t;
static inline int spinlock_trylock(spinlock_t *lock)
{
int res = atomic_swap(&lock->lock, -1);
/* Use memory barrier to keep coherency */
mb();
return res;
}
static inline void spinlock_lock(spinlock_t *lock)
{
while (spinlock_trylock(lock));
}
static inline void spinlock_unlock(spinlock_t *lock)
{
/* Use memory barrier to keep coherency */
mb();
atomic_set(&lock->lock, 0);
asm volatile ("nop");
}
static inline void semaphore_signal(semaphore_t *semaphore, int i)
{
spinlock_lock(&(semaphore->lock));
semaphore->count += i;
spinlock_unlock(&(semaphore->lock));
}
static inline void semaphore_wait(semaphore_t *semaphore, int i)
{
atomic_add(&(semaphore->waiting), 1);
while (1)
{
spinlock_lock(&(semaphore->lock));
if (semaphore->count >= i)
{
semaphore->count -= i;
atomic_add(&(semaphore->waiting), -1);
spinlock_unlock(&(semaphore->lock));
break;
}
spinlock_unlock(&(semaphore->lock));
}
}
static inline int semaphore_count(semaphore_t *semaphore)
{
int res = 0;
spinlock_lock(&(semaphore->lock));
res = semaphore->count;
spinlock_unlock(&(semaphore->lock));
return res;
}
static inline int semaphore_waiting(semaphore_t *semaphore)
{
return atomic_read(&(semaphore->waiting));
}
static inline int corelock_trylock(corelock_t *lock)
{
int res = 0;
unsigned long core;
asm volatile("csrr %0, mhartid;"
: "=r"(core));
if(spinlock_trylock(&lock->lock))
{
return -1;
}
if (lock->count == 0)
{
/* First time get lock */
lock->count++;
lock->core = core;
res = 0;
}
else if (lock->core == core)
{
/* Same core get lock */
lock->count++;
res = 0;
}
else
{
/* Different core get lock */
res = -1;
}
spinlock_unlock(&lock->lock);
return res;
}
static inline void corelock_lock(corelock_t *lock)
{
unsigned long core;
asm volatile("csrr %0, mhartid;"
: "=r"(core));
spinlock_lock(&lock->lock);
if (lock->count == 0)
{
/* First time get lock */
lock->count++;
lock->core = core;
}
else if (lock->core == core)
{
/* Same core get lock */
lock->count++;
}
else
{
/* Different core get lock */
spinlock_unlock(&lock->lock);
do
{
while (atomic_read(&lock->count))
;
} while (corelock_trylock(lock));
return;
}
spinlock_unlock(&lock->lock);
}
static inline void corelock_unlock(corelock_t *lock)
{
unsigned long core;
asm volatile("csrr %0, mhartid;"
: "=r"(core));
spinlock_lock(&lock->lock);
if (lock->core == core)
{
/* Same core release lock */
lock->count--;
if (lock->count <= 0)
{
lock->core = -1;
lock->count = 0;
}
}
else
{
/* Different core release lock */
spinlock_unlock(&lock->lock);
register unsigned long a7 asm("a7") = 93;
register unsigned long a0 asm("a0") = 0;
register unsigned long a1 asm("a1") = 0;
register unsigned long a2 asm("a2") = 0;
asm volatile("scall"
: "+r"(a0)
: "r"(a1), "r"(a2), "r"(a7));
}
spinlock_unlock(&lock->lock);
}
#ifdef __cplusplus
}
#endif
#endif /* _BSP_ATOMIC_H */

View File

@ -0,0 +1,5 @@
#ifndef _KENDRYTE_BSP_H
#define _KENDRYTE_BSP_H
#include "atomic.h"
#include "encoding.h"
#endif

View File

@ -0,0 +1,261 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stddef.h>
#include <stdint.h>
#include "encoding.h"
#include "clint.h"
#include "sysctl.h"
volatile clint_t* const clint = (volatile clint_t*)CLINT_BASE_ADDR;
static clint_timer_instance_t clint_timer_instance[CLINT_NUM_CORES];
static clint_ipi_instance_t clint_ipi_instance[CLINT_NUM_CORES];
uint64_t clint_get_time(void)
{
/* No difference on cores */
return clint->mtime;
}
int clint_timer_init(void)
{
/* Read core id */
unsigned long core_id = current_coreid();
/* Clear the Machine-Timer bit in MIE */
clear_csr(mie, MIP_MTIP);
/* Fill core's instance with original data */
/* clang-format off */
clint_timer_instance[core_id] = (const clint_timer_instance_t)
{
.interval = 0,
.cycles = 0,
.single_shot = 0,
.callback = NULL,
.ctx = NULL,
};
/* clang-format on */
return 0;
}
int clint_timer_stop(void)
{
/* Clear the Machine-Timer bit in MIE */
clear_csr(mie, MIP_MTIP);
return 0;
}
uint64_t clint_timer_get_freq(void)
{
/* The clock is divided by CLINT_CLOCK_DIV */
return sysctl_clock_get_freq(SYSCTL_CLOCK_CPU) / CLINT_CLOCK_DIV;
}
int clint_timer_start(uint64_t interval, int single_shot)
{
/* Read core id */
unsigned long core_id = current_coreid();
/* Set timer interval */
if (clint_timer_set_interval(interval) != 0)
return -1;
/* Set timer single shot */
if (clint_timer_set_single_shot(single_shot) != 0)
return -1;
/* Check settings to prevent interval is 0 */
if (clint_timer_instance[core_id].interval == 0)
return -1;
/* Check settings to prevent cycles is 0 */
if (clint_timer_instance[core_id].cycles == 0)
return -1;
/* Add cycle interval to mtimecmp */
uint64_t now = clint->mtime;
uint64_t then = now + clint_timer_instance[core_id].cycles;
/* Set mtimecmp by core id */
clint->mtimecmp[core_id] = then;
/* Enable interrupts in general */
set_csr(mstatus, MSTATUS_MIE);
/* Enable the Machine-Timer bit in MIE */
set_csr(mie, MIP_MTIP);
return 0;
}
uint64_t clint_timer_get_interval(void)
{
/* Read core id */
unsigned long core_id = current_coreid();
return clint_timer_instance[core_id].interval;
}
int clint_timer_set_interval(uint64_t interval)
{
/* Read core id */
unsigned long core_id = current_coreid();
/* Check parameter */
if (interval == 0)
return -1;
/* Assign user interval with Millisecond(ms) */
clint_timer_instance[core_id].interval = interval;
/* Convert interval to cycles */
clint_timer_instance[core_id].cycles = interval * clint_timer_get_freq() / 1000ULL;
return 0;
}
int clint_timer_get_single_shot(void)
{
/* Read core id */
unsigned long core_id = current_coreid();
/* Get single shot mode by core id */
return clint_timer_instance[core_id].single_shot;
}
int clint_timer_set_single_shot(int single_shot)
{
/* Read core id */
unsigned long core_id = current_coreid();
/* Set single shot mode by core id */
clint_timer_instance[core_id].single_shot = single_shot;
return 0;
}
int clint_timer_register(clint_timer_callback_t callback, void *ctx)
{
/* Read core id */
unsigned long core_id = current_coreid();
/* Set user callback function */
clint_timer_instance[core_id].callback = callback;
/* Assign user context */
clint_timer_instance[core_id].ctx = ctx;
return 0;
}
int clint_timer_unregister(void)
{
/* Just assign NULL to user callback function and context */
return clint_timer_register(NULL, NULL);
}
int clint_ipi_init(void)
{
/* Read core id */
unsigned long core_id = current_coreid();
/* Clear the Machine-Software bit in MIE */
clear_csr(mie, MIP_MSIP);
/* Fill core's instance with original data */
/* clang-format off */
clint_ipi_instance[core_id] = (const clint_ipi_instance_t){
.callback = NULL,
.ctx = NULL,
};
/* clang-format on */
return 0;
}
int clint_ipi_enable(void)
{
/* Enable interrupts in general */
set_csr(mstatus, MSTATUS_MIE);
/* Set the Machine-Software bit in MIE */
set_csr(mie, MIP_MSIP);
return 0;
}
int clint_ipi_disable(void)
{
/* Clear the Machine-Software bit in MIE */
clear_csr(mie, MIP_MSIP);
return 0;
}
int clint_ipi_send(size_t core_id)
{
if (core_id >= CLINT_NUM_CORES)
return -1;
clint->msip[core_id].msip = 1;
return 0;
}
int clint_ipi_clear(size_t core_id)
{
if (core_id >= CLINT_NUM_CORES)
return -1;
if (clint->msip[core_id].msip)
{
clint->msip[core_id].msip = 0;
return 1;
}
return 0;
}
int clint_ipi_register(clint_ipi_callback_t callback, void *ctx)
{
/* Read core id */
unsigned long core_id = current_coreid();
/* Set user callback function */
clint_ipi_instance[core_id].callback = callback;
/* Assign user context */
clint_ipi_instance[core_id].ctx = ctx;
return 0;
}
int clint_ipi_unregister(void)
{
/* Just assign NULL to user callback function and context */
return clint_ipi_register(NULL, NULL);
}
uintptr_t handle_irq_m_timer(uintptr_t cause, uintptr_t epc)
{
/* Read core id */
uint64_t core_id = current_coreid();
uint64_t ie_flag = read_csr(mie);
clear_csr(mie, MIP_MTIP | MIP_MSIP);
set_csr(mstatus, MSTATUS_MIE);
if (clint_timer_instance[core_id].callback != NULL)
clint_timer_instance[core_id].callback(
clint_timer_instance[core_id].ctx);
clear_csr(mstatus, MSTATUS_MIE);
set_csr(mstatus, MSTATUS_MPIE | MSTATUS_MPP);
write_csr(mie, ie_flag);
/* If not single shot and cycle interval is not 0, repeat this timer */
if (!clint_timer_instance[core_id].single_shot && clint_timer_instance[core_id].cycles != 0)
{
/* Set mtimecmp by core id */
clint->mtimecmp[core_id] += clint_timer_instance[core_id].cycles;
}
else
clear_csr(mie, MIP_MTIP);
return epc;
}
uintptr_t handle_irq_m_soft(uintptr_t cause, uintptr_t epc)
{
/* Read core id */
uint64_t core_id = current_coreid();
/* Clear the Machine-Software bit in MIE to prevent call again */
clear_csr(mie, MIP_MSIP);
set_csr(mstatus, MSTATUS_MIE);
/* Clear ipi flag */
clint_ipi_clear(core_id);
if (clint_ipi_instance[core_id].callback != NULL)
clint_ipi_instance[core_id].callback(clint_ipi_instance[core_id].ctx);
clear_csr(mstatus, MSTATUS_MIE);
set_csr(mstatus, MSTATUS_MPIE | MSTATUS_MPP);
set_csr(mie, MIP_MSIP);
return epc;
}

View File

@ -0,0 +1,338 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* @brief The CLINT block holds memory-mapped control and status registers
* associated with local interrupts for a Coreplex.
*
* @note CLINT RAM Layout
*
* | Address -| Description |
* |------------|---------------------------------|
* | 0x02000000 | msip for core 0 |
* | 0x02000004 | msip for core 1 |
* | ... | ... |
* | 0x02003FF8 | msip for core 4094 |
* | | |
* | 0x02004000 | mtimecmp for core 0 |
* | 0x02004008 | mtimecmp for core 1 |
* | ... | ... |
* | 0x0200BFF0 | mtimecmp For core 4094 |
* | 0x0200BFF8 | mtime |
* | | |
* | 0x0200C000 | Reserved |
* | ... | ... |
* | 0x0200EFFC | Reserved |
*/
#ifndef _DRIVER_CLINT_H
#define _DRIVER_CLINT_H
#include <stddef.h>
#include <stdint.h>
#include "platform.h"
#ifdef __cplusplus
extern "C" {
#endif
/* clang-format off */
/* Register address offsets */
#define CLINT_MSIP (0x0000)
#define CLINT_MSIP_SIZE (0x4)
#define CLINT_MTIMECMP (0x4000)
#define CLINT_MTIMECMP_SIZE (0x8)
#define CLINT_MTIME (0xBFF8)
#define CLINT_MTIME_SIZE (0x8)
/* Max number of cores */
#define CLINT_MAX_CORES (4095)
/* Real number of cores */
#define CLINT_NUM_CORES (2)
/* Clock frequency division factor */
#define CLINT_CLOCK_DIV (50)
/* clang-format on */
/**
* @brief MSIP Registers
*
* Machine-mode software interrupts are generated by writing to a
* per-core memory-mapped control register. The msip registers are
* 32-bit wide WARL registers, where the LSB is reflected in the
* msip bit of the associated cores mip register. Other bits in
* the msip registers are hardwired to zero. The mapping supports
* up to 4095 machine-mode cores.
*/
typedef struct _clint_msip
{
uint32_t msip : 1; /*!< Bit 0 is msip */
uint32_t zero : 31; /*!< Bits [32:1] is 0 */
} __attribute__((packed, aligned(4))) clint_msip_t;
/**
* @brief Timer compare Registers Machine-mode timer interrupts are
* generated by a real-time counter and a per-core comparator. The
* mtime register is a 64-bit read-only register that contains the
* current value of the real-time counter. Each mtimecmp register
* holds its cores time comparator. A timer interrupt is pending
* whenever mtime is greater than or equal to the value in a
* cores mtimecmp register. The timer interrupt is reflected in
* the mtip bit of the associated cores mip register.
*/
typedef uint64_t clint_mtimecmp_t;
/**
* @brief Timer Registers
*
* The mtime register has a 64-bit precision on all RV32, RV64,
* and RV128 systems. Platforms provide a 64-bit memory-mapped
* machine-mode timer compare register (mtimecmp), which causes a
* timer interrupt to be posted when the mtime register contains a
* value greater than or equal to the value in the mtimecmp
* register. The interrupt remains posted until it is cleared by
* writing the mtimecmp register. The interrupt will only be taken
* if interrupts are enabled and the MTIE bit is set in the mie
* register.
*/
typedef uint64_t clint_mtime_t;
/**
* @brief CLINT object
*
* Coreplex-Local INTerrupts, which includes software interrupts,
* local timer interrupts, and other interrupts routed directly to
* a core.
*/
typedef struct _clint
{
/* 0x0000 to 0x3FF8, MSIP Registers */
clint_msip_t msip[CLINT_MAX_CORES];
/* Resverd space, do not use */
uint32_t resv0;
/* 0x4000 to 0xBFF0, Timer Compare Registers */
clint_mtimecmp_t mtimecmp[CLINT_MAX_CORES];
/* 0xBFF8, Time Register */
clint_mtime_t mtime;
} __attribute__((packed, aligned(4))) clint_t;
/**
* @brief Clint object instanse
*/
extern volatile clint_t* const clint;
/**
* @brief Definitions for the timer callbacks
*/
typedef int (*clint_timer_callback_t)(void *ctx);
/**
* @brief Definitions for local interprocessor interrupt callbacks
*/
typedef int (*clint_ipi_callback_t)(void *ctx);
typedef struct _clint_timer_instance
{
uint64_t interval;
uint64_t cycles;
uint64_t single_shot;
clint_timer_callback_t callback;
void *ctx;
} clint_timer_instance_t;
typedef struct _clint_ipi_instance
{
clint_ipi_callback_t callback;
void *ctx;
} clint_ipi_instance_t;
/**
* @brief Get the time form CLINT timer register
*
* @note The CLINT must init to get right time
*
* @return 64bit Time
*/
uint64_t clint_get_time(void);
/**
* @brief Init the CLINT timer
*
* @note MIP_MTIP will be clear after init. The MSTATUS_MIE must set by
* user.
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_timer_init(void);
/**
* @brief Stop the CLINT timer
*
* @note MIP_MTIP will be clear after stop
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_timer_stop(void);
/**
* @brief Start the CLINT timer
*
* @param[in] interval The interval with Millisecond(ms)
* @param[in] single_shot Single shot or repeat
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_timer_start(uint64_t interval, int single_shot);
/**
* @brief Get the interval of timer
*
* @return The interval with Millisecond(ms)
*/
uint64_t clint_timer_get_interval(void);
/**
* @brief Set the interval with Millisecond(ms)
*
* @param[in] interval The interval with Millisecond(ms)
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_timer_set_interval(uint64_t interval);
/**
* @brief Get whether the timer is a single shot timer
*
* @return result
* - 0 It is a repeat timer
* - 1 It is a single shot timer
*/
int clint_timer_get_single_shot(void);
/**
* @brief Set the timer working as a single shot timer or repeat timer
*
* @param[in] single_shot Single shot or repeat
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_timer_set_single_shot(int single_shot);
/**
* @brief Set user callback function when timer is timeout
*
* @param[in] callback The callback function
* @param[in] ctx The context
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_timer_register(clint_timer_callback_t callback, void *ctx);
/**
* @brief Deregister user callback function
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_timer_unregister(void);
/**
* @brief Initialize local interprocessor interrupt
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_ipi_init(void);
/**
* @brief Enable local interprocessor interrupt
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_ipi_enable(void);
/**
* @brief Disable local interprocessor interrupt
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_ipi_disable(void);
/**
* @brief Send local interprocessor interrupt to core by core id
*
* @param[in] core_id The core identifier
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_ipi_send(size_t core_id);
/**
* @brief Clear local interprocessor interrupt
*
* @param[in] core_id The core identifier
*
* @return result
* - 1 An IPI was pending
* - 0 Non IPI was pending
* - -1 Fail
*/
int clint_ipi_clear(size_t core_id);
/**
* @brief Set user callback function when interprocessor interrupt
*
* @param[in] callback The callback function
* @param[in] ctx The context
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_ipi_register(clint_ipi_callback_t callback, void *ctx);
/**
* @brief Deregister user callback function
*
* @return result
* - 0 Success
* - Other Fail
*/
int clint_ipi_unregister(void);
#ifdef __cplusplus
}
#endif
#endif /* _DRIVER_CLINT_H */

View File

@ -0,0 +1,798 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
#include "dmac.h"
#include "sysctl.h"
#include "fpioa.h"
#include "utils.h"
#include "plic.h"
#include "stdlib.h"
#include <nuttx/arch.h>
#include <arch/irq.h>
volatile dmac_t *const dmac = (dmac_t *)DMAC_BASE_ADDR;
typedef struct _dmac_context
{
dmac_channel_number_t dmac_channel;
plic_irq_callback_t callback;
void *ctx;
} dmac_context_t;
dmac_context_t dmac_context[6];
static int is_memory(uintptr_t address)
{
enum {
mem_len = 6 * 1024 * 1024,
mem_no_cache_len = 8 * 1024 * 1024,
};
return ((address >= 0x80000000) && (address < 0x80000000 + mem_len)) || ((address >= 0x40000000) && (address < 0x40000000 + mem_no_cache_len)) || (address == 0x50450040);
}
uint64_t dmac_read_id(void)
{
return dmac->id;
}
uint64_t dmac_read_version(void)
{
return dmac->compver;
}
uint64_t dmac_read_channel_id(dmac_channel_number_t channel_num)
{
return dmac->channel[channel_num].axi_id;
}
static void dmac_enable(void)
{
dmac_cfg_u_t dmac_cfg;
dmac_cfg.data = readq(&dmac->cfg);
dmac_cfg.cfg.dmac_en = 1;
dmac_cfg.cfg.int_en = 1;
writeq(dmac_cfg.data, &dmac->cfg);
}
void dmac_disable(void)
{
dmac_cfg_u_t dmac_cfg;
dmac_cfg.data = readq(&dmac->cfg);
dmac_cfg.cfg.dmac_en = 0;
dmac_cfg.cfg.int_en = 0;
writeq(dmac_cfg.data, &dmac->cfg);
}
void src_transaction_complete_int_enable(dmac_channel_number_t channel_num)
{
dmac_ch_intstatus_enable_u_t ch_intstat;
ch_intstat.data = readq(&dmac->channel[channel_num].intstatus_en);
ch_intstat.ch_intstatus_enable.enable_src_transcomp_intstat = 1;
writeq(ch_intstat.data, &dmac->channel[channel_num].intstatus_en);
}
void dmac_channel_enable(dmac_channel_number_t channel_num)
{
dmac_chen_u_t chen;
chen.data = readq(&dmac->chen);
switch (channel_num) {
case DMAC_CHANNEL0:
chen.dmac_chen.ch1_en = 1;
chen.dmac_chen.ch1_en_we = 1;
break;
case DMAC_CHANNEL1:
chen.dmac_chen.ch2_en = 1;
chen.dmac_chen.ch2_en_we = 1;
break;
case DMAC_CHANNEL2:
chen.dmac_chen.ch3_en = 1;
chen.dmac_chen.ch3_en_we = 1;
break;
case DMAC_CHANNEL3:
chen.dmac_chen.ch4_en = 1;
chen.dmac_chen.ch4_en_we = 1;
break;
case DMAC_CHANNEL4:
chen.dmac_chen.ch5_en = 1;
chen.dmac_chen.ch5_en_we = 1;
break;
case DMAC_CHANNEL5:
chen.dmac_chen.ch6_en = 1;
chen.dmac_chen.ch6_en_we = 1;
break;
default:
break;
}
writeq(chen.data, &dmac->chen);
}
void dmac_channel_disable(dmac_channel_number_t channel_num)
{
dmac_chen_u_t chen;
chen.data = readq(&dmac->chen);
switch (channel_num)
{
case DMAC_CHANNEL0:
chen.dmac_chen.ch1_en = 0;
chen.dmac_chen.ch1_en_we = 1;
break;
case DMAC_CHANNEL1:
chen.dmac_chen.ch2_en = 0;
chen.dmac_chen.ch2_en_we = 1;
break;
case DMAC_CHANNEL2:
chen.dmac_chen.ch3_en = 0;
chen.dmac_chen.ch3_en_we = 1;
break;
case DMAC_CHANNEL3:
chen.dmac_chen.ch4_en = 0;
chen.dmac_chen.ch4_en_we = 1;
break;
case DMAC_CHANNEL4:
chen.dmac_chen.ch5_en = 0;
chen.dmac_chen.ch5_en_we = 1;
break;
case DMAC_CHANNEL5:
chen.dmac_chen.ch6_en = 0;
chen.dmac_chen.ch6_en_we = 1;
break;
default:
break;
}
writeq(chen.data, &dmac->chen);
}
int32_t dmac_check_channel_busy(dmac_channel_number_t channel_num)
{
int32_t ret = 0;
dmac_chen_u_t chen_u;
chen_u.data = readq(&dmac->chen);
switch (channel_num) {
case DMAC_CHANNEL0:
if (chen_u.dmac_chen.ch1_en == 1)
ret = 1;
break;
case DMAC_CHANNEL1:
if (chen_u.dmac_chen.ch2_en == 1)
ret = 1;
break;
case DMAC_CHANNEL2:
if (chen_u.dmac_chen.ch3_en == 1)
ret = 1;
break;
case DMAC_CHANNEL3:
if (chen_u.dmac_chen.ch4_en == 1)
ret = 1;
break;
case DMAC_CHANNEL4:
if (chen_u.dmac_chen.ch5_en == 1)
ret = 1;
break;
case DMAC_CHANNEL5:
if (chen_u.dmac_chen.ch6_en == 1)
ret = 1;
break;
default:
break;
}
writeq(chen_u.data, &dmac->chen);
return ret;
}
int32_t dmac_set_list_master_select(dmac_channel_number_t channel_num,
dmac_src_dst_select_t sd_sel, dmac_master_number_t mst_num)
{
int32_t ret = 0;
uint64_t tmp = 0;
dmac_ch_ctl_u_t ctl;
ctl.data = readq(&dmac->channel[channel_num].ctl);
ret = dmac_check_channel_busy(channel_num);
if (ret == 0) {
if (sd_sel == DMAC_SRC || sd_sel == DMAC_SRC_DST)
ctl.ch_ctl.sms = mst_num;
if (sd_sel == DMAC_DST || sd_sel == DMAC_SRC_DST)
ctl.ch_ctl.dms = mst_num;
tmp |= *(uint64_t *)&dmac->channel[channel_num].ctl;
writeq(ctl.data, &dmac->channel[channel_num].ctl);
}
return ret;
}
void dmac_enable_common_interrupt_status(void)
{
dmac_commonreg_intstatus_enable_u_t intstatus;
intstatus.data = readq(&dmac->com_intstatus_en);
intstatus.intstatus_enable.enable_slvif_dec_err_intstat = 1;
intstatus.intstatus_enable.enable_slvif_wr2ro_err_intstat = 1;
intstatus.intstatus_enable.enable_slvif_rd2wo_err_intstat = 1;
intstatus.intstatus_enable.enable_slvif_wronhold_err_intstat = 1;
intstatus.intstatus_enable.enable_slvif_undefinedreg_dec_err_intstat = 1;
writeq(intstatus.data, &dmac->com_intstatus_en);
}
void dmac_enable_common_interrupt_signal(void)
{
dmac_commonreg_intsignal_enable_u_t intsignal;
intsignal.data = readq(&dmac->com_intsignal_en);
intsignal.intsignal_enable.enable_slvif_dec_err_intsignal = 1;
intsignal.intsignal_enable.enable_slvif_wr2ro_err_intsignal = 1;
intsignal.intsignal_enable.enable_slvif_rd2wo_err_intsignal = 1;
intsignal.intsignal_enable.enable_slvif_wronhold_err_intsignal = 1;
intsignal.intsignal_enable.enable_slvif_undefinedreg_dec_err_intsignal = 1;
writeq(intsignal.data, &dmac->com_intsignal_en);
}
static void dmac_enable_channel_interrupt(dmac_channel_number_t channel_num)
{
writeq(0xffffffff, &dmac->channel[channel_num].intclear);
writeq(0x2, &dmac->channel[channel_num].intstatus_en);
}
void dmac_disable_channel_interrupt(dmac_channel_number_t channel_num)
{
writeq(0, &dmac->channel[channel_num].intstatus_en);
}
void dmac_chanel_interrupt_clear(dmac_channel_number_t channel_num)
{
writeq(0xffffffff, &dmac->channel[channel_num].intclear);
}
int dmac_set_channel_config(dmac_channel_number_t channel_num,
dmac_channel_config_t *cfg_param)
{
dmac_ch_ctl_u_t ctl;
dmac_ch_cfg_u_t cfg;
dmac_ch_llp_u_t ch_llp;
if (cfg_param->ctl_sms > DMAC_MASTER2)
return -1;
if (cfg_param->ctl_dms > DMAC_MASTER2)
return -1;
if (cfg_param->ctl_src_msize > DMAC_MSIZE_256)
return -1;
if (cfg_param->ctl_drc_msize > DMAC_MSIZE_256)
return -1;
/**
* cfg register must configure before ts_block and
* sar dar register
*/
cfg.data = readq(&dmac->channel[channel_num].cfg);
cfg.ch_cfg.hs_sel_src = cfg_param->cfg_hs_sel_src;
cfg.ch_cfg.hs_sel_dst = cfg_param->cfg_hs_sel_dst;
cfg.ch_cfg.src_hwhs_pol = cfg_param->cfg_src_hs_pol;
cfg.ch_cfg.dst_hwhs_pol = cfg_param->cfg_dst_hs_pol;
cfg.ch_cfg.src_per = cfg_param->cfg_src_per;
cfg.ch_cfg.dst_per = cfg_param->cfg_dst_per;
cfg.ch_cfg.ch_prior = cfg_param->cfg_ch_prior;
cfg.ch_cfg.tt_fc = cfg_param->ctl_tt_fc;
cfg.ch_cfg.src_multblk_type = cfg_param->cfg_src_multblk_type;
cfg.ch_cfg.dst_multblk_type = cfg_param->cfg_dst_multblk_type;
writeq(cfg.data, &dmac->channel[channel_num].cfg);
ctl.data = readq(&dmac->channel[channel_num].ctl);
ctl.ch_ctl.sms = cfg_param->ctl_sms;
ctl.ch_ctl.dms = cfg_param->ctl_dms;
/* master select */
ctl.ch_ctl.sinc = cfg_param->ctl_sinc;
ctl.ch_ctl.dinc = cfg_param->ctl_dinc;
/* address incrememt */
ctl.ch_ctl.src_tr_width = cfg_param->ctl_src_tr_width;
ctl.ch_ctl.dst_tr_width = cfg_param->ctl_dst_tr_width;
/* transfer width */
ctl.ch_ctl.src_msize = cfg_param->ctl_src_msize;
ctl.ch_ctl.dst_msize = cfg_param->ctl_drc_msize;
/* Burst transaction length */
ctl.ch_ctl.ioc_blktfr = cfg_param->ctl_ioc_blktfr;
/* interrupt on completion of block transfer */
/* 0x1 enable BLOCK_TFR_DONE_IntStat field */
writeq(cfg_param->ctl_block_ts, &dmac->channel[channel_num].block_ts);
/* the number of (blcok_ts +1) data of width SRC_TR_WIDTF to be */
/* transferred in a dma block transfer */
dmac->channel[channel_num].sar = cfg_param->sar;
dmac->channel[channel_num].dar = cfg_param->dar;
ch_llp.data = readq(&dmac->channel[channel_num].llp);
ch_llp.llp.loc = cfg_param->llp_loc;
ch_llp.llp.lms = cfg_param->llp_lms;
writeq(ch_llp.data, &dmac->channel[channel_num].llp);
writeq(ctl.data, &dmac->channel[channel_num].ctl);
readq(&dmac->channel[channel_num].swhssrc);
return 0;
}
int dmac_set_channel_param(dmac_channel_number_t channel_num,
const void *src, void *dest, dmac_address_increment_t src_inc, dmac_address_increment_t dest_inc,
dmac_burst_trans_length_t dmac_burst_size,
dmac_transfer_width_t dmac_trans_width,
uint32_t blockSize)
{
dmac_ch_ctl_u_t ctl;
dmac_ch_cfg_u_t cfg_u;
int mem_type_src = is_memory((uintptr_t)src), mem_type_dest = is_memory((uintptr_t)dest);
dmac_transfer_flow_t flow_control;
if (mem_type_src == 0 && mem_type_dest == 0)
{
flow_control = DMAC_PRF2PRF_DMA;
}else if (mem_type_src == 1 && mem_type_dest == 0)
flow_control = DMAC_MEM2PRF_DMA;
else if (mem_type_src == 0 && mem_type_dest == 1)
flow_control = DMAC_PRF2MEM_DMA;
else
flow_control = DMAC_MEM2MEM_DMA;
/**
* cfg register must configure before ts_block and
* sar dar register
*/
cfg_u.data = readq(&dmac->channel[channel_num].cfg);
cfg_u.ch_cfg.tt_fc = flow_control;
cfg_u.ch_cfg.hs_sel_src = mem_type_src ? DMAC_HS_SOFTWARE : DMAC_HS_HARDWARE;
cfg_u.ch_cfg.hs_sel_dst = mem_type_dest ? DMAC_HS_SOFTWARE : DMAC_HS_HARDWARE;
cfg_u.ch_cfg.src_per = channel_num;
cfg_u.ch_cfg.dst_per = channel_num;
cfg_u.ch_cfg.src_multblk_type = 0;
cfg_u.ch_cfg.dst_multblk_type = 0;
writeq(cfg_u.data, &dmac->channel[channel_num].cfg);
dmac->channel[channel_num].sar = (uint64_t)src;
dmac->channel[channel_num].dar = (uint64_t)dest;
ctl.data = readq(&dmac->channel[channel_num].ctl);
ctl.ch_ctl.sms = DMAC_MASTER1;
ctl.ch_ctl.dms = DMAC_MASTER2;
/* master select */
ctl.ch_ctl.sinc = src_inc;
ctl.ch_ctl.dinc = dest_inc;
/* address incrememt */
ctl.ch_ctl.src_tr_width = dmac_trans_width;
ctl.ch_ctl.dst_tr_width = dmac_trans_width;
/* transfer width */
ctl.ch_ctl.src_msize = dmac_burst_size;
ctl.ch_ctl.dst_msize = dmac_burst_size;
writeq(ctl.data, &dmac->channel[channel_num].ctl);
writeq(blockSize - 1, &dmac->channel[channel_num].block_ts);
/*the number of (blcok_ts +1) data of width SRC_TR_WIDTF to be */
/* transferred in a dma block transfer */
return 0;
}
int dmac_get_channel_config(dmac_channel_number_t channel_num,
dmac_channel_config_t *cfg_param)
{
dmac_ch_ctl_u_t ctl;
dmac_ch_cfg_u_t cfg;
dmac_ch_llp_u_t ch_llp;
if (cfg_param == 0)
return -1;
if (channel_num < DMAC_CHANNEL0 ||
channel_num > DMAC_CHANNEL3)
return -1;
ctl.data = readq(&dmac->channel[channel_num].ctl);
cfg_param->ctl_sms = ctl.ch_ctl.sms;
cfg_param->ctl_dms = ctl.ch_ctl.dms;
cfg_param->ctl_sinc = ctl.ch_ctl.sinc;
cfg_param->ctl_dinc = ctl.ch_ctl.dinc;
cfg_param->ctl_src_tr_width = ctl.ch_ctl.src_tr_width;
cfg_param->ctl_dst_tr_width = ctl.ch_ctl.dst_tr_width;
cfg_param->ctl_src_msize = ctl.ch_ctl.src_msize;
cfg_param->ctl_drc_msize = ctl.ch_ctl.dst_msize;
cfg_param->ctl_ioc_blktfr = ctl.ch_ctl.ioc_blktfr;
cfg.data = readq(&dmac->channel[channel_num].cfg);
cfg_param->cfg_hs_sel_src = cfg.ch_cfg.hs_sel_src;
cfg_param->cfg_hs_sel_dst = cfg.ch_cfg.hs_sel_dst;
cfg_param->cfg_src_hs_pol = cfg.ch_cfg.src_hwhs_pol;
cfg_param->cfg_dst_hs_pol = cfg.ch_cfg.dst_hwhs_pol;
cfg_param->cfg_src_per = cfg.ch_cfg.src_per;
cfg_param->cfg_dst_per = cfg.ch_cfg.dst_per;
cfg_param->cfg_ch_prior = cfg.ch_cfg.ch_prior;
cfg_param->cfg_src_multblk_type = cfg.ch_cfg.src_multblk_type;
cfg_param->cfg_dst_multblk_type = cfg.ch_cfg.dst_multblk_type;
cfg_param->sar = dmac->channel[channel_num].sar;
cfg_param->dar = dmac->channel[channel_num].dar;
ch_llp.data = readq(&dmac->channel[channel_num].llp);
cfg_param->llp_loc = ch_llp.llp.loc;
cfg_param->llp_lms = ch_llp.llp.lms;
cfg_param->ctl_block_ts = readq(&dmac->channel[channel_num].block_ts);
return 0;
}
void dmac_set_address(dmac_channel_number_t channel_num, uint64_t src_addr,
uint64_t dst_addr)
{
writeq(src_addr, &dmac->channel[channel_num].sar);
writeq(dst_addr, &dmac->channel[channel_num].dar);
}
void dmac_set_block_ts(dmac_channel_number_t channel_num,
uint32_t block_size)
{
uint32_t block_ts;
block_ts = block_size & 0x3fffff;
writeq(block_ts, &dmac->channel[channel_num].block_ts);
}
void dmac_source_control(dmac_channel_number_t channel_num,
dmac_master_number_t master_select,
dmac_address_increment_t address_mode,
dmac_transfer_width_t tr_width,
dmac_burst_trans_length_t burst_length)
{
dmac_ch_ctl_u_t ctl_u;
ctl_u.data = readq(&dmac->channel[channel_num].ctl);
ctl_u.ch_ctl.sms = master_select;
ctl_u.ch_ctl.sinc = address_mode;
ctl_u.ch_ctl.src_tr_width = tr_width;
ctl_u.ch_ctl.src_msize = burst_length;
writeq(ctl_u.data, &dmac->channel[channel_num].ctl);
}
void dmac_master_control(dmac_channel_number_t channel_num,
dmac_master_number_t master_select,
dmac_address_increment_t address_mode,
dmac_transfer_width_t tr_width,
dmac_burst_trans_length_t burst_length)
{
dmac_ch_ctl_u_t ctl_u;
ctl_u.data = readq(&dmac->channel[channel_num].ctl);
ctl_u.ch_ctl.dms = master_select;
ctl_u.ch_ctl.dinc = address_mode;
ctl_u.ch_ctl.dst_tr_width = tr_width;
ctl_u.ch_ctl.dst_msize = burst_length;
writeq(ctl_u.data, &dmac->channel[channel_num].ctl);
}
void dmac_set_source_transfer_control(dmac_channel_number_t channel_num,
dmac_multiblk_transfer_type_t transfer_type,
dmac_sw_hw_hs_select_t handshak_select)
{
dmac_ch_cfg_u_t cfg_u;
cfg_u.data = readq(&dmac->channel[channel_num].cfg);
cfg_u.ch_cfg.src_multblk_type = transfer_type;
cfg_u.ch_cfg.hs_sel_src = handshak_select;
writeq(cfg_u.data, &dmac->channel[channel_num].cfg);
}
void dmac_set_destination_transfer_control(dmac_channel_number_t channel_num,
dmac_multiblk_transfer_type_t transfer_type,
dmac_sw_hw_hs_select_t handshak_select)
{
dmac_ch_cfg_u_t cfg_u;
cfg_u.data = readq(&dmac->channel[channel_num].cfg);
cfg_u.ch_cfg.dst_multblk_type = transfer_type;
cfg_u.ch_cfg.hs_sel_dst = handshak_select;
writeq(cfg_u.data, &dmac->channel[channel_num].cfg);
}
void dmac_set_flow_control(dmac_channel_number_t channel_num,
dmac_transfer_flow_t flow_control)
{
dmac_ch_cfg_u_t cfg_u;
cfg_u.data = readq(&dmac->channel[channel_num].cfg);
cfg_u.ch_cfg.tt_fc = flow_control;
writeq(cfg_u.data, &dmac->channel[channel_num].cfg);
}
void dmac_set_linked_list_addr_point(dmac_channel_number_t channel_num,
uint64_t *addr)
{
dmac_ch_llp_u_t llp_u;
llp_u.data = readq(&dmac->channel[channel_num].llp);
/* Cast pointer to uint64_t */
llp_u.llp.loc = (uint64_t)addr;
writeq(llp_u.data, &dmac->channel[channel_num].llp);
}
void dmac_init(void)
{
uint64_t tmp;
dmac_commonreg_intclear_u_t intclear;
dmac_cfg_u_t dmac_cfg;
dmac_reset_u_t dmac_reset;
sysctl_clock_enable(SYSCTL_CLOCK_DMA);
dmac_reset.data = readq(&dmac->reset);
dmac_reset.reset.rst = 1;
writeq(dmac_reset.data, &dmac->reset);
while (dmac_reset.reset.rst)
dmac_reset.data = readq(&dmac->reset);
/*reset dmac */
intclear.data = readq(&dmac->com_intclear);
intclear.com_intclear.cear_slvif_dec_err_intstat = 1;
intclear.com_intclear.clear_slvif_wr2ro_err_intstat = 1;
intclear.com_intclear.clear_slvif_rd2wo_err_intstat = 1;
intclear.com_intclear.clear_slvif_wronhold_err_intstat = 1;
intclear.com_intclear.clear_slvif_undefinedreg_dec_err_intstat = 1;
writeq(intclear.data, &dmac->com_intclear);
/* clear common register interrupt */
dmac_cfg.data = readq(&dmac->cfg);
dmac_cfg.cfg.dmac_en = 0;
dmac_cfg.cfg.int_en = 0;
writeq(dmac_cfg.data, &dmac->cfg);
/* disable dmac and disable interrupt */
while (readq(&dmac->cfg))
;
tmp = readq(&dmac->chen);
tmp &= ~0xf;
writeq(tmp, &dmac->chen);
/* disable all channel before configure */
dmac_enable();
}
static void list_add(struct list_head_t *new, struct list_head_t *prev,
struct list_head_t *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
void list_add_tail(struct list_head_t *new, struct list_head_t *head)
{
list_add(new, head->prev, head);
}
void INIT_LIST_HEAD(struct list_head_t *list)
{
list->next = list;
list->prev = list;
}
void dmac_link_list_item(dmac_channel_number_t channel_num,
uint8_t LLI_row_num, int8_t LLI_last_row,
dmac_lli_item_t *lli_item,
dmac_channel_config_t *cfg_param)
{
dmac_ch_ctl_u_t ctl;
dmac_ch_llp_u_t llp_u;
lli_item[LLI_row_num].sar = cfg_param->sar;
lli_item[LLI_row_num].dar = cfg_param->dar;
ctl.data = readq(&dmac->channel[channel_num].ctl);
ctl.ch_ctl.sms = cfg_param->ctl_sms;
ctl.ch_ctl.dms = cfg_param->ctl_dms;
ctl.ch_ctl.sinc = cfg_param->ctl_sinc;
ctl.ch_ctl.dinc = cfg_param->ctl_dinc;
ctl.ch_ctl.src_tr_width = cfg_param->ctl_src_tr_width;
ctl.ch_ctl.dst_tr_width = cfg_param->ctl_dst_tr_width;
ctl.ch_ctl.src_msize = cfg_param->ctl_src_msize;
ctl.ch_ctl.dst_msize = cfg_param->ctl_drc_msize;
ctl.ch_ctl.src_stat_en = cfg_param->ctl_src_stat_en;
ctl.ch_ctl.dst_stat_en = cfg_param->ctl_dst_stat_en;
if (LLI_last_row != LAST_ROW) {
ctl.ch_ctl.shadowreg_or_lli_valid = 1;
ctl.ch_ctl.shadowreg_or_lli_last = 0;
} else {
ctl.ch_ctl.shadowreg_or_lli_valid = 1;
ctl.ch_ctl.shadowreg_or_lli_last = 1;
}
lli_item[LLI_row_num].ctl = ctl.data;
lli_item[LLI_row_num].ch_block_ts = cfg_param->ctl_block_ts;
lli_item[LLI_row_num].sstat = 0;
lli_item[LLI_row_num].dstat = 0;
llp_u.data = readq(&dmac->channel[channel_num].llp);
if (LLI_last_row != LAST_ROW)
llp_u.llp.loc = ((uint64_t)&lli_item[LLI_row_num + 1]) >> 6;
else
llp_u.llp.loc = 0;
lli_item[LLI_row_num].llp = llp_u.data;
}
void dmac_update_shandow_register(dmac_channel_number_t channel_num,
int8_t last_block, dmac_channel_config_t *cfg_param)
{
dmac_ch_ctl_u_t ctl_u;
do {
ctl_u.data = readq(&dmac->channel[channel_num].ctl);
} while (ctl_u.ch_ctl.shadowreg_or_lli_valid);
writeq(cfg_param->sar, &dmac->channel[channel_num].sar);
writeq(cfg_param->dar, &dmac->channel[channel_num].dar);
writeq(cfg_param->ctl_block_ts, &dmac->channel[channel_num].block_ts);
ctl_u.ch_ctl.sms = cfg_param->ctl_sms;
ctl_u.ch_ctl.dms = cfg_param->ctl_dms;
ctl_u.ch_ctl.sinc = cfg_param->ctl_sinc;
ctl_u.ch_ctl.dinc = cfg_param->ctl_dinc;
ctl_u.ch_ctl.src_tr_width = cfg_param->ctl_src_tr_width;
ctl_u.ch_ctl.dst_tr_width = cfg_param->ctl_dst_tr_width;
ctl_u.ch_ctl.src_msize = cfg_param->ctl_src_msize;
ctl_u.ch_ctl.dst_msize = cfg_param->ctl_drc_msize;
ctl_u.ch_ctl.src_stat_en = cfg_param->ctl_src_stat_en;
ctl_u.ch_ctl.dst_stat_en = cfg_param->ctl_dst_stat_en;
if (last_block != LAST_ROW)
{
ctl_u.ch_ctl.shadowreg_or_lli_valid = 1;
ctl_u.ch_ctl.shadowreg_or_lli_last = 0;
} else {
ctl_u.ch_ctl.shadowreg_or_lli_valid = 1;
ctl_u.ch_ctl.shadowreg_or_lli_last = 1;
}
writeq(ctl_u.data, &dmac->channel[channel_num].ctl);
writeq(0, &dmac->channel[channel_num].blk_tfr);
}
void dmac_set_shadow_invalid_flag(dmac_channel_number_t channel_num)
{
dmac_ch_ctl_u_t ctl_u;
ctl_u.data = readq(&dmac->channel[channel_num].ctl);
ctl_u.ch_ctl.shadowreg_or_lli_valid = 1;
ctl_u.ch_ctl.shadowreg_or_lli_last = 0;
writeq(ctl_u.data, &dmac->channel[channel_num].ctl);
}
void dmac_set_single_mode(dmac_channel_number_t channel_num,
const void *src, void *dest, dmac_address_increment_t src_inc,
dmac_address_increment_t dest_inc,
dmac_burst_trans_length_t dmac_burst_size,
dmac_transfer_width_t dmac_trans_width,
size_t block_size) {
dmac_chanel_interrupt_clear(channel_num);
dmac_channel_disable(channel_num);
dmac_wait_idle(channel_num);
dmac_set_channel_param(channel_num, src, dest, src_inc, dest_inc,
dmac_burst_size, dmac_trans_width, block_size);
dmac_enable();
dmac_channel_enable(channel_num);
}
int dmac_is_done(dmac_channel_number_t channel_num)
{
if(readq(&dmac->channel[channel_num].intstatus) & 0x2)
return 1;
else
return 0;
}
void dmac_wait_done(dmac_channel_number_t channel_num)
{
dmac_wait_idle(channel_num);
}
int dmac_is_idle(dmac_channel_number_t channel_num)
{
dmac_chen_u_t chen;
chen.data = readq(&dmac->chen);
if((chen.data >> channel_num) & 0x1UL)
return 0;
else
return 1;
}
void dmac_wait_idle(dmac_channel_number_t channel_num)
{
while(!dmac_is_idle(channel_num));
dmac_chanel_interrupt_clear(channel_num); /* clear interrupt */
}
void dmac_set_src_dest_length(dmac_channel_number_t channel_num, const void *src, void *dest, size_t len)
{
if(src != NULL)
dmac->channel[channel_num].sar = (uint64_t)src;
if(dest != NULL)
dmac->channel[channel_num].dar = (uint64_t)dest;
if(len > 0)
dmac_set_block_ts(channel_num, len - 1);
dmac_channel_enable(channel_num);
}
static int dmac_irq_callback(int irq, void *ctx, void *arg)
{
dmac_context_t *v_dmac_context = (dmac_context_t *)(ctx);
dmac_channel_number_t v_dmac_channel = v_dmac_context->dmac_channel;
dmac_chanel_interrupt_clear(v_dmac_channel);
if(v_dmac_context->callback != NULL)
v_dmac_context->callback(v_dmac_context->ctx);
return 0;
}
void dmac_irq_register(dmac_channel_number_t channel_num , plic_irq_callback_t dmac_callback, void *ctx, uint32_t priority)
{
int ret;
dmac_context[channel_num].dmac_channel = channel_num;
dmac_context[channel_num].callback = dmac_callback;
dmac_context[channel_num].ctx = ctx;
dmac_enable_channel_interrupt(channel_num);
plic_set_priority(IRQN_DMA0_INTERRUPT + channel_num, priority);
ret = irq_attach(IRQN_DMA0_INTERRUPT + channel_num, dmac_irq_callback, &dmac_context[channel_num]);
if (ret == OK)
{
up_enable_irq(IRQN_DMA0_INTERRUPT + channel_num);
}
}
void __attribute__((weak, alias("dmac_irq_register"))) dmac_set_irq(dmac_channel_number_t channel_num , plic_irq_callback_t dmac_callback, void *ctx, uint32_t priority);
void dmac_irq_unregister(dmac_channel_number_t channel_num)
{
dmac_context[channel_num].callback = NULL;
dmac_context[channel_num].ctx = NULL;
up_disable_irq(IRQN_DMA0_INTERRUPT + channel_num);
irq_detach(IRQN_DMA0_INTERRUPT + channel_num);
}
void __attribute__((weak, alias("dmac_irq_unregister"))) dmac_free_irq(dmac_channel_number_t channel_num);

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,69 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stddef.h>
#include <assert.h>
#include "dmac.h"
#include "utils.h"
#include "sysctl.h"
#include "fft.h"
static volatile fft_t *const fft = (volatile fft_t *)FFT_BASE_ADDR;
static void fft_init(uint8_t point, uint8_t mode, uint16_t shift, uint8_t is_dma, uint8_t input_mode, uint8_t data_mode)
{
fft->fft_ctrl.fft_point = point;
fft->fft_ctrl.fft_mode = mode;
fft->fft_ctrl.fft_shift = shift;
fft->fft_ctrl.dma_send = is_dma;
fft->fft_ctrl.fft_enable = 1;
fft->fft_ctrl.fft_input_mode = input_mode;
fft->fft_ctrl.fft_data_mode = data_mode;
}
void fft_complex_uint16_dma(dmac_channel_number_t dma_send_channel_num, dmac_channel_number_t dma_receive_channel_num,
uint16_t shift, fft_direction_t direction, const uint64_t *input, size_t point_num, uint64_t *output)
{
fft_point_t point = FFT_512;
switch(point_num)
{
case 512:
point = FFT_512;
break;
case 256:
point = FFT_256;
break;
case 128:
point = FFT_128;
break;
case 64:
point = FFT_64;
break;
default:
ASSERT(!"fft point error");
break;
}
sysctl_clock_enable(SYSCTL_CLOCK_FFT);
sysctl_reset(SYSCTL_RESET_FFT);
fft_init(point, direction, shift, 1, 0, 0);
sysctl_dma_select(dma_receive_channel_num, SYSCTL_DMA_SELECT_FFT_RX_REQ);
sysctl_dma_select(dma_send_channel_num, SYSCTL_DMA_SELECT_FFT_TX_REQ);
dmac_set_single_mode(dma_receive_channel_num, (void *)(&fft->fft_output_fifo), output, DMAC_ADDR_NOCHANGE, DMAC_ADDR_INCREMENT,
DMAC_MSIZE_4, DMAC_TRANS_WIDTH_64, point_num>>1);
dmac_set_single_mode(dma_send_channel_num, input, (void *)(&fft->fft_input_fifo), DMAC_ADDR_INCREMENT, DMAC_ADDR_NOCHANGE,
DMAC_MSIZE_4, DMAC_TRANS_WIDTH_64, point_num>>1);
dmac_wait_done(dma_receive_channel_num);
}

View File

@ -0,0 +1,247 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _DRIVER_FFT_H
#define _DRIVER_FFT_H
#include <stdint.h>
#include "platform.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct _complex_hard
{
int16_t real;
int16_t imag;
} complex_hard_t;
typedef struct _fft_data
{
int16_t I1;
int16_t R1;
int16_t I2;
int16_t R2;
} fft_data_t;
typedef enum _fft_point
{
FFT_512,
FFT_256,
FFT_128,
FFT_64,
} fft_point_t;
typedef enum _fft_direction
{
FFT_DIR_BACKWARD,
FFT_DIR_FORWARD,
FFT_DIR_MAX,
} fft_direction_t;
/**
* @brief FFT algorithm accelerator register
*
* @note FFT algorithm accelerator register table
*
* | Offset | Name | Description |
* |-----------|----------------|-------------------------------------|
* | 0x00 | fft_input_fifo | input data fifo |
* | 0x08 | fft_ctrl | fft ctrl reg |
* | 0x10 | fifo_ctrl | fifo ctrl |
* | 0x18 | intr_mask | interrupt mask |
* | 0x20 | intr_clear | interrupt clear |
* | 0x28 | fft_status | fft status reg |
* | 0x30 | fft_status_raw | fft_status_raw |
* | 0x38 | fft_output_fifo| fft_output_fifo |
*
*/
/**
* @brief The calculation data is input through this register
*
* No. 0 Register (0x00)
*/
typedef struct _fft_input_fifo
{
uint64_t fft_input_fifo : 64;
} __attribute__((packed, aligned(8))) fft_input_fifo_t;
/**
* @brief fft ctrl reg
*
* No. 1 Register (0x08)
*/
typedef struct _fft_fft_ctrl
{
/**
*FFT calculation data length:
*b'000:512 point; b'001:256 point; b'010:128 point; b'011:64 point;
*/
uint64_t fft_point : 3;
/* FFT mode: b'0:FFT b'1:IFFT */
uint64_t fft_mode : 1;
/* Corresponding to the nine layer butterfly shift operation, 0x0: does not shift; 0x1: shift 1st layer. ...*/
uint64_t fft_shift : 9;
/* FFT enable: b'0:disable b'1:enable */
uint64_t fft_enable : 1;
/* FFT DMA enable: b'0:disable b'1:enable */
uint64_t dma_send : 1;
/**
*Input data arrangement: b'00:RIRI; b'01:only real part exist, RRRR;
*b'10:First input the real part and then input the imaginary part.
*/
uint64_t fft_input_mode : 2;
/* Effective width of input data. b'0:64bit effective; b'1:32bit effective */
uint64_t fft_data_mode : 1;
uint64_t reserved : 46;
} __attribute__((packed, aligned(8))) fft_fft_ctrl_t;
/**
* @brief fifo ctrl
*
* No. 2 Register (0x10)
*/
typedef struct _fft_fifo_ctrl
{
/* Response memory initialization flag.b'1:initialization */
uint64_t resp_fifo_flush_n : 1;
/* Command memory initialization flag.b'1:initialization */
uint64_t cmd_fifo_flush_n : 1;
/* Output interface memory initialization flag.b'1:initialization */
uint64_t gs_fifo_flush_n : 1;
uint64_t reserved : 61;
} __attribute__((packed, aligned(8))) fft_fifo_ctrl_t;
/**
* @brief interrupt mask
*
* No. 3 Register (0x18)
*/
typedef struct _fft_intr_mask
{
/**
*FFT return status set.
*b'0:FFT returns to the state after completion.
*b'1:FFT does not return to the state after completion
*/
uint64_t fft_done_mask : 1;
uint64_t reserved : 63;
} __attribute__((packed, aligned(8))) fft_intr_mask_t;
/**
* @brief interrupt clear
*
* No. 4 Register (0x20)
*/
typedef struct _fft_intr_clear
{
/* The interrupt state clears. b'1:clear current interrupt request */
uint64_t fft_done_clear : 1;
uint64_t reserved1 : 63;
} __attribute__((packed, aligned(8))) fft_intr_clear_t;
/**
* @brief fft status reg
*
* No. 5 Register (0x28)
*/
typedef struct _fft_status
{
/* FFT calculation state.b'0:not completed; b'1:completed */
uint64_t fft_done_status : 1;
uint64_t reserved1 : 63;
} __attribute__((packed, aligned(8))) fft_status_t;
/**
* @brief fft status raw
*
* No. 6 Register (0x30)
*/
typedef struct _fft_status_raw
{
/* FFT calculation state. b'1:done */
uint64_t fft_done_status_raw : 1;
/* FFT calculation state. b'1:working */
uint64_t fft_work_status_raw : 1;
uint64_t reserved : 62;
} __attribute__((packed, aligned(8))) fft_status_raw_t;
/**
* @brief Output of FFT calculation data through this register
*
* No. 7 Register (0x38)
*/
typedef struct _fft_output_fifo
{
uint64_t fft_output_fifo : 64;
} __attribute__((packed, aligned(8))) fft_output_fifo_t;
/**
* @brief Fast Fourier transform (FFT) algorithm accelerator object
*
* A fast Fourier transform (FFT) algorithm computes the discrete
* Fourier transform (DFT) of a sequence, or its inverse (IFFT).
* Fourier analysis converts a signal from its original domain
* (often time or space) to a representation in the frequency
* domain and vice versa. An FFT rapidly computes such
* transformations by factorizing the DFT matrix into a product of
* sparse (mostly zero) factors.
*/
typedef struct _fft
{
/* No. 0 (0x00): input data fifo */
fft_input_fifo_t fft_input_fifo;
/* No. 1 (0x08): fft ctrl reg */
fft_fft_ctrl_t fft_ctrl;
/* No. 2 (0x10): fifo ctrl */
fft_fifo_ctrl_t fifo_ctrl;
/* No. 3 (0x18): interrupt mask */
fft_intr_mask_t intr_mask;
/* No. 4 (0x20): interrupt clear */
fft_intr_clear_t intr_clear;
/* No. 5 (0x28): fft status reg */
fft_status_t fft_status;
/* No. 6 (0x30): fft_status_raw */
fft_status_raw_t fft_status_raw;
/* No. 7 (0x38): fft_output_fifo */
fft_output_fifo_t fft_output_fifo;
} __attribute__((packed, aligned(8))) fft_t;
/**
* @brief Do 16bit quantized complex FFT by DMA
*
* @param[in] dma_send_channel_num Dmac send channel number.
* @param[in] dma_receive_channel_num Dmac receive channel number.
* @param[in] shift The shifts selection in 9 stage
* @param[in] direction The direction
* @param[in] input The input data
* @param[in] point The FFT points count
* @param[out] output The output data
*/
void fft_complex_uint16_dma(dmac_channel_number_t dma_send_channel_num,
dmac_channel_number_t dma_receive_channel_num,
uint16_t shift,
fft_direction_t direction,
const uint64_t *input,
size_t point_num,
uint64_t *output);
#ifdef __cplusplus
}
#endif
#endif /* _DRIVER_FFT_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,81 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include "gpio.h"
#include "utils.h"
#include "fpioa.h"
#include "sysctl.h"
#define GPIO_MAX_PINNO 8
volatile gpio_t* const gpio = (volatile gpio_t*)GPIO_BASE_ADDR;
int gpio_init(void)
{
return sysctl_clock_enable(SYSCTL_CLOCK_GPIO);
}
void gpio_set_drive_mode(uint8_t pin, gpio_drive_mode_t mode)
{
ASSERT(pin < GPIO_MAX_PINNO);
int io_number = fpioa_get_io_by_function(FUNC_GPIO0 + pin);
ASSERT(io_number >= 0);
fpioa_pull_t pull;
uint32_t dir;
switch (mode)
{
case GPIO_DM_INPUT:
pull = FPIOA_PULL_NONE;
dir = 0;
break;
case GPIO_DM_INPUT_PULL_DOWN:
pull = FPIOA_PULL_DOWN;
dir = 0;
break;
case GPIO_DM_INPUT_PULL_UP:
pull = FPIOA_PULL_UP;
dir = 0;
break;
case GPIO_DM_OUTPUT:
pull = FPIOA_PULL_DOWN;
dir = 1;
break;
default:
ASSERT(!"GPIO drive mode is not supported.");
break;
}
fpioa_set_io_pull(io_number, pull);
set_gpio_bit(gpio->direction.u32, pin, dir);
}
gpio_pin_value_t gpio_get_pin(uint8_t pin)
{
ASSERT(pin < GPIO_MAX_PINNO);
uint32_t dir = get_gpio_bit(gpio->direction.u32, pin);
volatile uint32_t *reg = dir ? gpio->data_output.u32 : gpio->data_input.u32;
return get_gpio_bit(reg, pin);
}
void gpio_set_pin(uint8_t pin, gpio_pin_value_t value)
{
ASSERT(pin < GPIO_MAX_PINNO);
uint32_t dir = get_gpio_bit(gpio->direction.u32, pin);
volatile uint32_t *reg = dir ? gpio->data_output.u32 : gpio->data_input.u32;
ASSERT(dir == 1);
set_gpio_bit(reg, pin, value);
}

View File

@ -0,0 +1,168 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _DRIVER_GPIO_H
#define _DRIVER_GPIO_H
#include "platform.h"
#include <inttypes.h>
#include <stddef.h>
#include "gpio_common.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Structure for accessing GPIO registers by individual bit
*/
typedef struct _gpio_bits
{
uint32_t b0 : 1;
uint32_t b1 : 1;
uint32_t b2 : 1;
uint32_t b3 : 1;
uint32_t b4 : 1;
uint32_t b5 : 1;
uint32_t b6 : 1;
uint32_t b7 : 1;
uint32_t b8 : 1;
uint32_t b9 : 1;
uint32_t b10 : 1;
uint32_t b11 : 1;
uint32_t b12 : 1;
uint32_t b13 : 1;
uint32_t b14 : 1;
uint32_t b15 : 1;
uint32_t b16 : 1;
uint32_t b17 : 1;
uint32_t b18 : 1;
uint32_t b19 : 1;
uint32_t b20 : 1;
uint32_t b21 : 1;
uint32_t b22 : 1;
uint32_t b23 : 1;
uint32_t b24 : 1;
uint32_t b25 : 1;
uint32_t b26 : 1;
uint32_t b27 : 1;
uint32_t b28 : 1;
uint32_t b29 : 1;
uint32_t b30 : 1;
uint32_t b31 : 1;
} __attribute__((packed, aligned(4))) gpio_bits_t;
/**
* @brief Structure of templates for accessing GPIO registers
*/
typedef union _gpio_access_tp
{
/* 32x1 bit mode */
uint32_t u32[1];
/* 16x2 bit mode */
uint16_t u16[2];
/* 8x4 bit mode */
uint8_t u8[4];
/* 1 bit mode */
gpio_bits_t bits;
} __attribute__((packed, aligned(4))) gpio_access_tp_t;
/**
* @brief The GPIO address map
*/
typedef struct _gpio
{
/* Offset 0x00: Data (output) registers */
gpio_access_tp_t data_output;
/* Offset 0x04: Data direction registers */
gpio_access_tp_t direction;
/* Offset 0x08: Data source registers */
gpio_access_tp_t source;
/* Offset 0x10 - 0x2f: Unused registers, 9x4 bytes */
uint32_t unused_0[9];
/* Offset 0x30: Interrupt enable/disable registers */
gpio_access_tp_t interrupt_enable;
/* Offset 0x34: Interrupt mask registers */
gpio_access_tp_t interrupt_mask;
/* Offset 0x38: Interrupt level registers */
gpio_access_tp_t interrupt_level;
/* Offset 0x3c: Interrupt polarity registers */
gpio_access_tp_t interrupt_polarity;
/* Offset 0x40: Interrupt status registers */
gpio_access_tp_t interrupt_status;
/* Offset 0x44: Raw interrupt status registers */
gpio_access_tp_t interrupt_status_raw;
/* Offset 0x48: Interrupt debounce registers */
gpio_access_tp_t interrupt_debounce;
/* Offset 0x4c: Registers for clearing interrupts */
gpio_access_tp_t interrupt_clear;
/* Offset 0x50: External port (data input) registers */
gpio_access_tp_t data_input;
/* Offset 0x54 - 0x5f: Unused registers, 3x4 bytes */
uint32_t unused_1[3];
/* Offset 0x60: Sync level registers */
gpio_access_tp_t sync_level;
/* Offset 0x64: ID code */
gpio_access_tp_t id_code;
/* Offset 0x68: Interrupt both edge type */
gpio_access_tp_t interrupt_bothedge;
} __attribute__((packed, aligned(4))) gpio_t;
/**
* @brief Bus GPIO object instance
*/
extern volatile gpio_t *const gpio;
/**
* @brief Gpio initialize
*
* @return Result
* - 0 Success
* - Other Fail
*/
int gpio_init(void);
/**
* @brief Set Gpio drive mode
*
* @param[in] pin Gpio pin
* @param[in] mode Gpio pin drive mode
*/
void gpio_set_drive_mode(uint8_t pin, gpio_drive_mode_t mode);
/**
* @brief Get Gpio pin value
*
* @param[in] pin Gpio pin
* @return Pin value
*
* - GPIO_PV_Low Gpio pin low
* - GPIO_PV_High Gpio pin high
*/
gpio_pin_value_t gpio_get_pin(uint8_t pin);
/**
* @brief Set Gpio pin value
*
* @param[in] pin Gpio pin
* @param[in] value Gpio pin value
*/
void gpio_set_pin(uint8_t pin, gpio_pin_value_t value);
#ifdef __cplusplus
}
#endif
#endif /* _DRIVER_GPIO_H */

View File

@ -0,0 +1,51 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _GPIO_COMMON_H
#define _GPIO_COMMON_H
#ifdef __cplusplus
extern "C" {
#endif
typedef enum _gpio_drive_mode
{
GPIO_DM_INPUT,
GPIO_DM_INPUT_PULL_DOWN,
GPIO_DM_INPUT_PULL_UP,
GPIO_DM_OUTPUT,
} gpio_drive_mode_t;
typedef enum _gpio_pin_edge
{
GPIO_PE_NONE,
GPIO_PE_FALLING,
GPIO_PE_RISING,
GPIO_PE_BOTH,
GPIO_PE_LOW,
GPIO_PE_HIGH = 8,
} gpio_pin_edge_t;
typedef enum _gpio_pin_value
{
GPIO_PV_LOW,
GPIO_PV_HIGH
} gpio_pin_value_t;
#ifdef __cplusplus
}
#endif
#endif /* _GPIO_COMMON_H */

View File

@ -0,0 +1,226 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include "fpioa.h"
#include "gpiohs.h"
#include "sysctl.h"
#include "utils.h"
#include <nuttx/arch.h>
#include <arch/irq.h>
#define GPIOHS_MAX_PINNO 32
volatile gpiohs_t *const gpiohs = (volatile gpiohs_t *)GPIOHS_BASE_ADDR;
typedef struct _gpiohs_pin_instance
{
size_t pin;
gpio_pin_edge_t edge;
void (*callback)(void);
plic_irq_callback_t gpiohs_callback;
void *context;
} gpiohs_pin_instance_t;
static gpiohs_pin_instance_t pin_instance[32];
void gpiohs_set_drive_mode(uint8_t pin, gpio_drive_mode_t mode)
{
ASSERT(pin < GPIOHS_MAX_PINNO);
int io_number = fpioa_get_io_by_function(FUNC_GPIOHS0 + pin);
ASSERT(io_number >= 0);
fpioa_pull_t pull;
uint32_t dir;
switch(mode)
{
case GPIO_DM_INPUT:
pull = FPIOA_PULL_NONE;
dir = 0;
break;
case GPIO_DM_INPUT_PULL_DOWN:
pull = FPIOA_PULL_DOWN;
dir = 0;
break;
case GPIO_DM_INPUT_PULL_UP:
pull = FPIOA_PULL_UP;
dir = 0;
break;
case GPIO_DM_OUTPUT:
pull = FPIOA_PULL_DOWN;
dir = 1;
break;
default:
ASSERT(!"GPIO drive mode is not supported.");
break;
}
fpioa_set_io_pull(io_number, pull);
volatile uint32_t *reg = dir ? gpiohs->output_en.u32 : gpiohs->input_en.u32;
volatile uint32_t *reg_d = !dir ? gpiohs->output_en.u32 : gpiohs->input_en.u32;
set_gpio_bit(reg_d, pin, 0);
set_gpio_bit(reg, pin, 1);
}
gpio_pin_value_t gpiohs_get_pin(uint8_t pin)
{
ASSERT(pin < GPIOHS_MAX_PINNO);
return get_gpio_bit(gpiohs->input_val.u32, pin);
}
void gpiohs_set_pin(uint8_t pin, gpio_pin_value_t value)
{
ASSERT(pin < GPIOHS_MAX_PINNO);
set_gpio_bit(gpiohs->output_val.u32, pin, value);
}
void gpiohs_set_pin_edge(uint8_t pin, gpio_pin_edge_t edge)
{
set_gpio_bit(gpiohs->rise_ie.u32, pin, 0);
set_gpio_bit(gpiohs->rise_ip.u32, pin, 1);
set_gpio_bit(gpiohs->fall_ie.u32, pin, 0);
set_gpio_bit(gpiohs->fall_ip.u32, pin, 1);
set_gpio_bit(gpiohs->low_ie.u32, pin, 0);
set_gpio_bit(gpiohs->low_ip.u32, pin, 1);
set_gpio_bit(gpiohs->high_ie.u32, pin, 0);
set_gpio_bit(gpiohs->high_ip.u32, pin, 1);
if(edge & GPIO_PE_FALLING)
{
set_gpio_bit(gpiohs->fall_ie.u32, pin, 1);
} else
{
set_gpio_bit(gpiohs->fall_ie.u32, pin, 0);
}
if(edge & GPIO_PE_RISING)
{
set_gpio_bit(gpiohs->rise_ie.u32, pin, 1);
} else
{
set_gpio_bit(gpiohs->rise_ie.u32, pin, 0);
}
if(edge & GPIO_PE_LOW)
{
set_gpio_bit(gpiohs->low_ie.u32, pin, 1);
} else
{
set_gpio_bit(gpiohs->low_ie.u32, pin, 0);
}
if(edge & GPIO_PE_HIGH)
{
set_gpio_bit(gpiohs->high_ie.u32, pin, 1);
} else
{
set_gpio_bit(gpiohs->high_ie.u32, pin, 0);
}
pin_instance[pin].edge = edge;
}
int gpiohs_pin_onchange_isr(int irq, void *userdata, void *arg)
{
gpiohs_pin_instance_t *ctx = (gpiohs_pin_instance_t *)userdata;
size_t pin = ctx->pin;
if(ctx->edge & GPIO_PE_FALLING)
{
set_gpio_bit(gpiohs->fall_ie.u32, pin, 0);
set_gpio_bit(gpiohs->fall_ip.u32, pin, 1);
set_gpio_bit(gpiohs->fall_ie.u32, pin, 1);
}
if(ctx->edge & GPIO_PE_RISING)
{
set_gpio_bit(gpiohs->rise_ie.u32, pin, 0);
set_gpio_bit(gpiohs->rise_ip.u32, pin, 1);
set_gpio_bit(gpiohs->rise_ie.u32, pin, 1);
}
if(ctx->edge & GPIO_PE_LOW)
{
set_gpio_bit(gpiohs->low_ie.u32, pin, 0);
set_gpio_bit(gpiohs->low_ip.u32, pin, 1);
set_gpio_bit(gpiohs->low_ie.u32, pin, 1);
}
if(ctx->edge & GPIO_PE_HIGH)
{
set_gpio_bit(gpiohs->high_ie.u32, pin, 0);
set_gpio_bit(gpiohs->high_ip.u32, pin, 1);
set_gpio_bit(gpiohs->high_ie.u32, pin, 1);
}
if(ctx->callback)
ctx->callback();
if(ctx->gpiohs_callback)
ctx->gpiohs_callback(ctx->context);
return 0;
}
void gpiohs_set_irq(uint8_t pin, uint32_t priority, void (*func)(void))
{
int ret;
pin_instance[pin].pin = pin;
pin_instance[pin].callback = func;
plic_set_priority(IRQN_GPIOHS0_INTERRUPT + pin, priority);
ret = irq_attach(IRQN_GPIOHS0_INTERRUPT + pin, gpiohs_pin_onchange_isr, &(pin_instance[pin]));
if (ret == OK)
{
up_enable_irq(IRQN_GPIOHS0_INTERRUPT + pin);
}
}
void gpiohs_irq_register(uint8_t pin, uint32_t priority, plic_irq_callback_t callback, void *ctx)
{
int ret;
pin_instance[pin].pin = pin;
pin_instance[pin].gpiohs_callback = callback;
pin_instance[pin].context = ctx;
plic_set_priority(IRQN_GPIOHS0_INTERRUPT + pin, priority);
ret = irq_attach(IRQN_GPIOHS0_INTERRUPT + pin, gpiohs_pin_onchange_isr, &(pin_instance[pin]));
if (ret == OK)
{
up_enable_irq(IRQN_GPIOHS0_INTERRUPT + pin);
}
}
void gpiohs_irq_unregister(uint8_t pin)
{
pin_instance[pin] = (gpiohs_pin_instance_t){
.callback = NULL,
.gpiohs_callback = NULL,
.context = NULL,
};
set_gpio_bit(gpiohs->rise_ie.u32, pin, 0);
set_gpio_bit(gpiohs->fall_ie.u32, pin, 0);
set_gpio_bit(gpiohs->low_ie.u32, pin, 0);
set_gpio_bit(gpiohs->high_ie.u32, pin, 0);
up_disable_irq(IRQN_GPIOHS0_INTERRUPT + pin);
irq_detach(IRQN_GPIOHS0_INTERRUPT + pin);
}
void gpiohs_irq_disable(size_t pin)
{
plic_irq_disable(IRQN_GPIOHS0_INTERRUPT + pin);
}

View File

@ -0,0 +1,266 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _DRIVER_GPIOHS_H
#define _DRIVER_GPIOHS_H
#include <stddef.h>
#include <stdint.h>
#include "gpio_common.h"
#include "platform.h"
#include "plic.h"
#ifdef __cplusplus
extern "C" {
#endif
/* clang-format off */
/* Register address offsets */
#define GPIOHS_INPUT_VAL (0x00)
#define GPIOHS_INPUT_EN (0x04)
#define GPIOHS_OUTPUT_EN (0x08)
#define GPIOHS_OUTPUT_VAL (0x0C)
#define GPIOHS_PULLUP_EN (0x10)
#define GPIOHS_DRIVE (0x14)
#define GPIOHS_RISE_IE (0x18)
#define GPIOHS_RISE_IP (0x1C)
#define GPIOHS_FALL_IE (0x20)
#define GPIOHS_FALL_IP (0x24)
#define GPIOHS_HIGH_IE (0x28)
#define GPIOHS_HIGH_IP (0x2C)
#define GPIOHS_LOW_IE (0x30)
#define GPIOHS_LOW_IP (0x34)
#define GPIOHS_IOF_EN (0x38)
#define GPIOHS_IOF_SEL (0x3C)
#define GPIOHS_OUTPUT_XOR (0x40)
/* clang-format on */
/**
* @brief GPIO bits raw object
*/
typedef struct _gpiohs_raw
{
/* Address offset 0x00 */
uint32_t input_val;
/* Address offset 0x04 */
uint32_t input_en;
/* Address offset 0x08 */
uint32_t output_en;
/* Address offset 0x0c */
uint32_t output_val;
/* Address offset 0x10 */
uint32_t pullup_en;
/* Address offset 0x14 */
uint32_t drive;
/* Address offset 0x18 */
uint32_t rise_ie;
/* Address offset 0x1c */
uint32_t rise_ip;
/* Address offset 0x20 */
uint32_t fall_ie;
/* Address offset 0x24 */
uint32_t fall_ip;
/* Address offset 0x28 */
uint32_t high_ie;
/* Address offset 0x2c */
uint32_t high_ip;
/* Address offset 0x30 */
uint32_t low_ie;
/* Address offset 0x34 */
uint32_t low_ip;
/* Address offset 0x38 */
uint32_t iof_en;
/* Address offset 0x3c */
uint32_t iof_sel;
/* Address offset 0x40 */
uint32_t output_xor;
} __attribute__((packed, aligned(4))) gpiohs_raw_t;
/**
* @brief GPIO bits object
*/
typedef struct _gpiohs_bits
{
uint32_t b0 : 1;
uint32_t b1 : 1;
uint32_t b2 : 1;
uint32_t b3 : 1;
uint32_t b4 : 1;
uint32_t b5 : 1;
uint32_t b6 : 1;
uint32_t b7 : 1;
uint32_t b8 : 1;
uint32_t b9 : 1;
uint32_t b10 : 1;
uint32_t b11 : 1;
uint32_t b12 : 1;
uint32_t b13 : 1;
uint32_t b14 : 1;
uint32_t b15 : 1;
uint32_t b16 : 1;
uint32_t b17 : 1;
uint32_t b18 : 1;
uint32_t b19 : 1;
uint32_t b20 : 1;
uint32_t b21 : 1;
uint32_t b22 : 1;
uint32_t b23 : 1;
uint32_t b24 : 1;
uint32_t b25 : 1;
uint32_t b26 : 1;
uint32_t b27 : 1;
uint32_t b28 : 1;
uint32_t b29 : 1;
uint32_t b30 : 1;
uint32_t b31 : 1;
} __attribute__((packed, aligned(4))) gpiohs_bits_t;
/**
* @brief GPIO bits multi access union
*/
typedef union _gpiohs_u32
{
/* 32x1 bit mode */
uint32_t u32[1];
/* 16x2 bit mode */
uint16_t u16[2];
/* 8x4 bit mode */
uint8_t u8[4];
/* 1 bit mode */
gpiohs_bits_t bits;
} __attribute__((packed, aligned(4))) gpiohs_u32_t;
/**
* @brief GPIO object
*
* The GPIO controller is a peripheral device mapped in the
* internal memory map, discoverable in the Configuration String.
* It is responsible for low-level configuration of the actual
* GPIO pads on the device (direction, pull up-enable, and drive
* value), as well as selecting between various sources of the
* controls for these signals. The GPIO controller allows seperate
* configuration of each of N GPIO bits.
*
* Once the interrupt is pending, it will remain set until a 1 is
* written to the *_ip register at that bit.
*/
typedef struct _gpiohs
{
/* Address offset 0x00, Input Values */
gpiohs_u32_t input_val;
/* Address offset 0x04, Input enable */
gpiohs_u32_t input_en;
/* Address offset 0x08, Output enable */
gpiohs_u32_t output_en;
/* Address offset 0x0c, Onput Values */
gpiohs_u32_t output_val;
/* Address offset 0x10, Internal Pull-Ups enable */
gpiohs_u32_t pullup_en;
/* Address offset 0x14, Drive Strength */
gpiohs_u32_t drive;
/* Address offset 0x18, Rise interrupt enable */
gpiohs_u32_t rise_ie;
/* Address offset 0x1c, Rise interrupt pending */
gpiohs_u32_t rise_ip;
/* Address offset 0x20, Fall interrupt enable */
gpiohs_u32_t fall_ie;
/* Address offset 0x24, Fall interrupt pending */
gpiohs_u32_t fall_ip;
/* Address offset 0x28, High interrupt enable */
gpiohs_u32_t high_ie;
/* Address offset 0x2c, High interrupt pending */
gpiohs_u32_t high_ip;
/* Address offset 0x30, Low interrupt enable */
gpiohs_u32_t low_ie;
/* Address offset 0x34, Low interrupt pending */
gpiohs_u32_t low_ip;
/* Address offset 0x38, HW I/O Function enable */
gpiohs_u32_t iof_en;
/* Address offset 0x3c, HW I/O Function select */
gpiohs_u32_t iof_sel;
/* Address offset 0x40, Output XOR (invert) */
gpiohs_u32_t output_xor;
} __attribute__((packed, aligned(4))) gpiohs_t;
/**
* @brief GPIO High-speed object instanse
*/
extern volatile gpiohs_t *const gpiohs;
/**
* @brief Set Gpiohs drive mode
*
* @param[in] pin Gpiohs pin
* @param[in] mode Gpiohs pin drive mode
*/
void gpiohs_set_drive_mode(uint8_t pin, gpio_drive_mode_t mode);
/**
* @brief Get Gpiohs pin value
*
* @param[in] pin Gpiohs pin
* @return Pin value
*
* - GPIO_PV_Low Gpiohs pin low
* - GPIO_PV_High Gpiohs pin high
*/
gpio_pin_value_t gpiohs_get_pin(uint8_t pin);
/**
* @brief Set Gpiohs pin value
*
* @param[in] pin Gpiohs pin
* @param[in] value Gpiohs pin value
*/
void gpiohs_set_pin(uint8_t pin, gpio_pin_value_t value);
/**
* @brief Set Gpiohs pin edge for interrupt
*
* @param[in] pin Gpiohs pin
* @param[in] edge Gpiohs pin edge type
*/
void gpiohs_set_pin_edge(uint8_t pin, gpio_pin_edge_t edge);
/**
* @brief Set Gpiohs pin interrupt
*
* @param[in] pin Gpiohs pin
* @param[in] priority Gpiohs pin interrupt priority
* @param[in] func Gpiohs pin interrupt service routine
*/
void gpiohs_set_irq(uint8_t pin, uint32_t priority, void (*func)(void));
/**
* @brief Set Gpiohs pin interrupt
*
* @param[in] pin Gpiohs pin
* @param[in] priority Gpiohs pin interrupt priority
* @param[in] callback Gpiohs pin interrupt service routine
* @param[in] ctx Gpiohs interrupt param
*/
void gpiohs_irq_register(uint8_t pin, uint32_t priority, plic_irq_callback_t callback, void *ctx);
/**
* @brief Unregister Gpiohs pin interrupt
*
* @param[in] pin Gpiohs pin
*/
void gpiohs_irq_unregister(uint8_t pin);
#ifdef __cplusplus
}
#endif
#endif /* _DRIVER_GPIOHS_H */

View File

@ -0,0 +1,709 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdint.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include "i2s.h"
#include "sysctl.h"
#include "stdlib.h"
#include "utils.h"
volatile i2s_t *const i2s[3] =
{
(volatile i2s_t *)I2S0_BASE_ADDR,
(volatile i2s_t *)I2S1_BASE_ADDR,
(volatile i2s_t *)I2S2_BASE_ADDR
};
typedef struct _i2s_instance
{
i2s_device_number_t i2s_num;
i2s_transfer_mode_t transfer_mode;
dmac_channel_number_t dmac_channel;
plic_instance_t i2s_int_instance;
} i2s_instance_t;
static i2s_instance_t g_i2s_send_instance[3];
static i2s_instance_t g_i2s_recv_instance[3];
static int i2s_recv_channel_enable(i2s_device_number_t device_num,
i2s_channel_num_t channel_num, uint32_t enable)
{
rer_t u_rer;
if (channel_num < I2S_CHANNEL_0 || channel_num > I2S_CHANNEL_3)
return -1;
u_rer.reg_data = readl(&i2s[device_num]->channel[channel_num].rer);
u_rer.rer.rxchenx = enable;
writel(u_rer.reg_data, &i2s[device_num]->channel[channel_num].rer);
return 0;
}
static int i2s_transmit_channel_enable(i2s_device_number_t device_num,
i2s_channel_num_t channel_num, uint32_t enable)
{
ter_t u_ter;
if (channel_num < I2S_CHANNEL_0 || channel_num > I2S_CHANNEL_3)
return -1;
u_ter.reg_data = readl(&i2s[device_num]->channel[channel_num].ter);
u_ter.ter.txchenx = enable;
writel(u_ter.reg_data, &i2s[device_num]->channel[channel_num].ter);
return 0;
}
static void i2s_receive_enable(i2s_device_number_t device_num, i2s_channel_num_t channel_num)
{
irer_t u_irer;
u_irer.reg_data = readl(&i2s[device_num]->irer);
u_irer.irer.rxen = 1;
writel(u_irer.reg_data, &i2s[device_num]->irer);
/* Receiver block enable */
i2s_recv_channel_enable(device_num, channel_num, 1);
/* Receive channel enable */
}
static void i2s_transimit_enable(i2s_device_number_t device_num, i2s_channel_num_t channel_num)
{
iter_t u_iter;
u_iter.reg_data = readl(&i2s[device_num]->iter);
u_iter.iter.txen = 1;
writel(u_iter.reg_data, &i2s[device_num]->iter);
/* Transmitter block enable */
i2s_transmit_channel_enable(device_num, channel_num, 1);
/* Transmit channel enable */
}
static void i2s_set_enable(i2s_device_number_t device_num, uint32_t enable)
{
ier_t u_ier;
u_ier.reg_data = readl(&i2s[device_num]->ier);
u_ier.ier.ien = enable;
writel(u_ier.reg_data, &i2s[device_num]->ier);
}
static void i2s_disable_block(i2s_device_number_t device_num, i2s_transmit_t rxtx_mode)
{
irer_t u_irer;
iter_t u_iter;
if (rxtx_mode == I2S_RECEIVER)
{
u_irer.reg_data = readl(&i2s[device_num]->irer);
u_irer.irer.rxen = 0;
writel(u_irer.reg_data, &i2s[device_num]->irer);
/* Receiver block disable */
}
else
{
u_iter.reg_data = readl(&i2s[device_num]->iter);
u_iter.iter.txen = 0;
writel(u_iter.reg_data, &i2s[device_num]->iter);
/* Transmitter block disable */
}
}
static int i2s_set_rx_word_length(i2s_device_number_t device_num,
i2s_word_length_t word_length,
i2s_channel_num_t channel_num)
{
rcr_tcr_t u_rcr;
if (word_length > RESOLUTION_32_BIT || word_length < IGNORE_WORD_LENGTH)
return -1;
if (channel_num < I2S_CHANNEL_0 || channel_num > I2S_CHANNEL_3)
return -1;
u_rcr.reg_data = readl(&i2s[device_num]->channel[channel_num].rcr);
u_rcr.rcr_tcr.wlen = word_length;
writel(u_rcr.reg_data, &i2s[device_num]->channel[channel_num].rcr);
return 0;
}
static int i2s_set_tx_word_length(i2s_device_number_t device_num,
i2s_word_length_t word_length,
i2s_channel_num_t channel_num)
{
rcr_tcr_t u_tcr;
if (word_length > RESOLUTION_32_BIT || word_length < IGNORE_WORD_LENGTH)
return -1;
if (channel_num < I2S_CHANNEL_0 || channel_num > I2S_CHANNEL_3)
return -1;
u_tcr.reg_data = readl(&i2s[device_num]->channel[channel_num].tcr);
u_tcr.rcr_tcr.wlen = word_length;
writel(u_tcr.reg_data, &i2s[device_num]->channel[channel_num].tcr);
return 0;
}
static void i2s_master_configure(i2s_device_number_t device_num,
i2s_word_select_cycles_t word_select_size,
i2s_sclk_gating_cycles_t gating_cycles,
i2s_work_mode_t word_mode)
{
ASSERT(!(word_select_size < SCLK_CYCLES_16 ||
word_select_size > SCLK_CYCLES_32));
ASSERT(!(gating_cycles < NO_CLOCK_GATING ||
gating_cycles > CLOCK_CYCLES_24));
ccr_t u_ccr;
cer_t u_cer;
u_ccr.reg_data = readl(&i2s[device_num]->ccr);
u_ccr.ccr.clk_word_size = word_select_size;
u_ccr.ccr.clk_gate = gating_cycles;
u_ccr.ccr.align_mode = word_mode;
writel(u_ccr.reg_data, &i2s[device_num]->ccr);
u_cer.reg_data = readl(&i2s[device_num]->cer);
u_cer.cer.clken = 1;
writel(u_cer.reg_data, &i2s[device_num]->cer);
/* Clock generation enable */
}
static int i2s_set_rx_threshold(i2s_device_number_t device_num,
i2s_fifo_threshold_t threshold,
i2s_channel_num_t channel_num)
{
rfcr_t u_rfcr;
if (threshold < TRIGGER_LEVEL_1 || threshold > TRIGGER_LEVEL_16)
return -1;
if (channel_num < I2S_CHANNEL_0 || channel_num > I2S_CHANNEL_3)
return -1;
u_rfcr.reg_data = readl(&i2s[device_num]->channel[channel_num].rfcr);
u_rfcr.rfcr.rxchdt = threshold;
writel(u_rfcr.reg_data, &i2s[device_num]->channel[channel_num].rfcr);
return 0;
}
static int i2s_set_tx_threshold(i2s_device_number_t device_num,
i2s_fifo_threshold_t threshold,
i2s_channel_num_t channel_num)
{
tfcr_t u_tfcr;
if (threshold < TRIGGER_LEVEL_1 || threshold > TRIGGER_LEVEL_16)
return -1;
if (channel_num < I2S_CHANNEL_0 || channel_num > I2S_CHANNEL_3)
return -1;
u_tfcr.reg_data = readl(&i2s[device_num]->channel[channel_num].tfcr);
u_tfcr.tfcr.txchet = threshold;
writel(u_tfcr.reg_data, &i2s[device_num]->channel[channel_num].tfcr);
return 0;
}
static int i2s_set_mask_interrupt(i2s_device_number_t device_num,
i2s_channel_num_t channel_num,
uint32_t rx_available_int, uint32_t rx_overrun_int,
uint32_t tx_empty_int, uint32_t tx_overrun_int)
{
imr_t u_imr;
if (channel_num < I2S_CHANNEL_0 || channel_num > I2S_CHANNEL_3)
return -1;
u_imr.reg_data = readl(&i2s[device_num]->channel[channel_num].imr);
if (rx_available_int == 1)
u_imr.imr.rxdam = 1;
else
u_imr.imr.rxdam = 0;
if (rx_overrun_int == 1)
u_imr.imr.rxfom = 1;
else
u_imr.imr.rxfom = 0;
if (tx_empty_int == 1)
u_imr.imr.txfem = 1;
else
u_imr.imr.txfem = 0;
if (tx_overrun_int == 1)
u_imr.imr.txfom = 1;
else
u_imr.imr.txfom = 0;
writel(u_imr.reg_data, &i2s[device_num]->channel[channel_num].imr);
return 0;
}
static int i2s_transmit_dma_enable(i2s_device_number_t device_num, uint32_t enable)
{
ccr_t u_ccr;
if (device_num >= I2S_DEVICE_MAX)
return -1;
u_ccr.reg_data = readl(&i2s[device_num]->ccr);
u_ccr.ccr.dma_tx_en = enable;
writel(u_ccr.reg_data, &i2s[device_num]->ccr);
return 0;
}
static int i2s_receive_dma_enable(i2s_device_number_t device_num, uint32_t enable)
{
ccr_t u_ccr;
if (device_num >= I2S_DEVICE_MAX)
return -1;
u_ccr.reg_data = readl(&i2s[device_num]->ccr);
u_ccr.ccr.dma_rx_en = enable;
writel(u_ccr.reg_data, &i2s[device_num]->ccr);
return 0;
}
int i2s_set_dma_divide_16(i2s_device_number_t device_num, uint32_t enable)
{
ccr_t u_ccr;
if (device_num >= I2S_DEVICE_MAX)
return -1;
u_ccr.reg_data = readl(&i2s[device_num]->ccr);
u_ccr.ccr.dma_divide_16 = enable;
writel(u_ccr.reg_data, &i2s[device_num]->ccr);
return 0;
}
int i2s_get_dma_divide_16(i2s_device_number_t device_num)
{
if (device_num >= I2S_DEVICE_MAX)
return -1;
ccr_t u_ccr;
u_ccr.reg_data = readl(&i2s[device_num]->ccr);
return u_ccr.ccr.dma_divide_16;
}
int i2s_receive_data(i2s_device_number_t device_num, i2s_channel_num_t channel_num, uint64_t *buf, size_t buf_len)
{
uint32_t i = 0;
isr_t u_isr;
readl(&i2s[device_num]->channel[channel_num].ror);
/*clear over run*/
for (i = 0; i < buf_len;)
{
u_isr.reg_data = readl(&i2s[device_num]->channel[channel_num].isr);
if (u_isr.isr.rxda == 1)
{
buf[i] = readl(&i2s[device_num]->channel[channel_num].left_rxtx);
buf[i] <<= 32;
buf[i++] |= readl(&i2s[device_num]->channel[channel_num].right_rxtx);
}
}
return 0;
}
void i2s_receive_data_dma(i2s_device_number_t device_num, uint32_t *buf,
size_t buf_len, dmac_channel_number_t channel_num)
{
dmac_wait_idle(channel_num);
sysctl_dma_select((sysctl_dma_channel_t)channel_num, SYSCTL_DMA_SELECT_I2S0_RX_REQ + device_num * 2);
dmac_set_single_mode(channel_num, (void *)(&i2s[device_num]->rxdma), buf, DMAC_ADDR_NOCHANGE, DMAC_ADDR_INCREMENT,
DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, buf_len);
}
int i2s_rx_to_tx(i2s_device_number_t device_src_num, i2s_device_number_t device_dest_num,
size_t buf_len, dmac_channel_number_t channel_num)
{
static uint8_t dmac_recv_flag[6] = {0,0,0,0,0,0};
if(dmac_recv_flag[channel_num])
dmac_wait_done(channel_num);
else
dmac_recv_flag[channel_num] = 1;
sysctl_dma_select((sysctl_dma_channel_t)channel_num, SYSCTL_DMA_SELECT_I2S0_RX_REQ + device_src_num * 2);
dmac_set_single_mode(channel_num, (void *)(&i2s[device_src_num]->rxdma), (void *)(&i2s[device_dest_num]->txdma), DMAC_ADDR_NOCHANGE, DMAC_ADDR_NOCHANGE,
DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, buf_len);
return 0;
}
int i2s_send_data(i2s_device_number_t device_num, i2s_channel_num_t channel_num, const uint8_t *pcm, size_t buf_len,
size_t single_length)
{
isr_t u_isr;
uint32_t left_buffer = 0;
uint32_t right_buffer = 0;
uint32_t i = 0;
uint32_t j = 0;
if (channel_num < I2S_CHANNEL_0 || channel_num > I2S_CHANNEL_3)
return -1;
buf_len = buf_len / (single_length / 8) / 2; /* sample num */
readl(&i2s[device_num]->channel[channel_num].tor);
/* read clear overrun flag */
for (j = 0; j < buf_len;)
{
u_isr.reg_data = readl(&i2s[device_num]->channel[channel_num].isr);
if (u_isr.isr.txfe == 1)
{
switch(single_length)
{
case 16:
left_buffer = ((uint16_t *)pcm)[i++];
right_buffer = ((uint16_t *)pcm)[i++];
break;
case 24:
left_buffer = 0;
left_buffer |= pcm[i++];
left_buffer |= pcm[i++] << 8;
left_buffer |= pcm[i++] << 16;
right_buffer = 0;
right_buffer |= pcm[i++];
right_buffer |= pcm[i++] << 8;
right_buffer |= pcm[i++] << 16;
break;
case 32:
left_buffer = ((uint32_t *)pcm)[i++];
right_buffer = ((uint32_t *)pcm)[i++];
break;
default:
left_buffer = pcm[i++];
right_buffer = pcm[i++];
break;
}
writel(left_buffer, &i2s[device_num]->channel[channel_num].left_rxtx);
writel(right_buffer, &i2s[device_num]->channel[channel_num].right_rxtx);
j++;
}
}
return 0;
}
void i2s_send_data_dma(i2s_device_number_t device_num, const void *buf, size_t buf_len, dmac_channel_number_t channel_num)
{
dmac_wait_idle(channel_num);
sysctl_dma_select((sysctl_dma_channel_t)channel_num, SYSCTL_DMA_SELECT_I2S0_TX_REQ + device_num * 2);
dmac_set_single_mode(channel_num, buf, (void *)(&i2s[device_num]->txdma), DMAC_ADDR_INCREMENT,
DMAC_ADDR_NOCHANGE, DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, buf_len);
}
static void i2s_parse_voice(i2s_device_number_t device_num, uint32_t *buf, const uint8_t *pcm, size_t length, size_t bits_per_sample,
uint8_t track_num, size_t *send_len)
{
uint32_t i,j=0;
*send_len = length * 2;
switch(bits_per_sample)
{
case 16:
for(i = 0; i < length; i++)
{
buf[2*i] = ((uint16_t *)pcm)[i];
buf[2*i+1] = 0;
}
break;
case 24:
for(i = 0; i < length; i++)
{
buf[2*i] = 0;
buf[2*i] |= pcm[j++];
buf[2*i] |= pcm[j++] << 8;
buf[2*i] |= pcm[j++] << 16;
buf[2*i+1] = 0;
if(track_num == 2)
{
buf[2*i+1] |= pcm[j++];
buf[2*i+1] |= pcm[j++] << 8;
buf[2*i+1] |= pcm[j++] << 16;
}
}
break;
case 32:
default:
for(i = 0; i < length; i++)
{
buf[2*i] = ((uint32_t *)pcm)[i];
buf[2*i+1] = 0;
}
break;
}
}
void i2s_play(i2s_device_number_t device_num, dmac_channel_number_t channel_num,
const uint8_t *buf, size_t buf_len, size_t frame, size_t bits_per_sample, uint8_t track_num)
{
const uint8_t *trans_buf;
uint32_t i;
size_t sample_cnt = buf_len / ( bits_per_sample / 8 ) / track_num;
size_t frame_cnt = sample_cnt / frame;
size_t frame_remain = sample_cnt % frame;
i2s_set_dma_divide_16(device_num, 0);
if (bits_per_sample == 16 && track_num == 2)
{
i2s_set_dma_divide_16(device_num, 1);
for (i = 0; i < frame_cnt; i++)
{
trans_buf = buf + i * frame * (bits_per_sample / 8) * track_num;
i2s_send_data_dma(device_num,trans_buf, frame, channel_num);
}
if(frame_remain)
{
trans_buf = buf + frame_cnt * frame * (bits_per_sample / 8) * track_num;
i2s_send_data_dma(device_num, trans_buf, frame_remain, channel_num);
}
}
else if (bits_per_sample == 32 && track_num == 2)
{
for (i = 0; i < frame_cnt; i++)
{
trans_buf = buf + i * frame * (bits_per_sample / 8) * track_num;
i2s_send_data_dma(device_num,trans_buf, frame * 2, channel_num);
}
if(frame_remain)
{
trans_buf = buf + frame_cnt * frame * (bits_per_sample / 8) * track_num;
i2s_send_data_dma(device_num, trans_buf, frame_remain * 2, channel_num);
}
}
else
{
uint32_t *buff[2];
buff[0] = malloc(frame * 2 * sizeof(uint32_t) * 2);
buff[1] = buff[0] + frame * 2;
uint8_t flag = 0;
size_t send_len = 0;
for (i = 0; i < frame_cnt; i++)
{
trans_buf = buf + i * frame * (bits_per_sample / 8) * track_num;
i2s_parse_voice(device_num, buff[flag], trans_buf, frame, bits_per_sample, track_num, &send_len);
i2s_send_data_dma(device_num,buff[flag], send_len, channel_num);
flag = !flag;
}
if (frame_remain)
{
trans_buf = buf + frame_cnt * frame * (bits_per_sample / 8) * track_num;
i2s_parse_voice(device_num, buff[flag], trans_buf, frame_remain, bits_per_sample, track_num, &send_len);
i2s_send_data_dma(device_num, trans_buf, send_len, channel_num);
}
free(buff[0]);
}
}
static inline void i2s_set_sign_expand_en(i2s_device_number_t device_num, uint32_t enable)
{
ccr_t u_ccr;
u_ccr.reg_data = readl(&i2s[device_num]->ccr);
u_ccr.ccr.sign_expand_en = enable;
writel(u_ccr.reg_data, &i2s[device_num]->ccr);
}
void i2s_rx_channel_config(i2s_device_number_t device_num,
i2s_channel_num_t channel_num,
i2s_word_length_t word_length,
i2s_word_select_cycles_t word_select_size,
i2s_fifo_threshold_t trigger_level,
i2s_work_mode_t word_mode)
{
i2s_recv_channel_enable(device_num, channel_num, 0);
/* Receive channel disable */
writel(0, &i2s[device_num]->channel[channel_num].ter);
/* disable tx */
writel(1, &i2s[device_num]->channel[channel_num].rff);
/* flash individual fifo */
writel(1, &i2s[device_num]->rxffr);
/* flush tx fifo*/
i2s_set_rx_word_length(device_num, word_length, channel_num);
/* Word buf_len is RESOLUTION_32_BIT */
i2s_master_configure(device_num,
word_select_size, NO_CLOCK_GATING, word_mode);
/* word select size is 32 bits,no clock gating */
i2s_set_rx_threshold(device_num, trigger_level, channel_num);
/* Interrupt trigger when FIFO level is 8 */
readl(&i2s[device_num]->channel[channel_num].ror);
readl(&i2s[device_num]->channel[channel_num].tor);
i2s_recv_channel_enable(device_num, channel_num, 1);
}
void i2s_tx_channel_config(i2s_device_number_t device_num,
i2s_channel_num_t channel_num,
i2s_word_length_t word_length,
i2s_word_select_cycles_t word_select_size,
i2s_fifo_threshold_t trigger_level,
i2s_work_mode_t word_mode)
{
writel(0, &i2s[device_num]->channel[channel_num].rer);
/* disable rx */
i2s_transmit_channel_enable(device_num, channel_num, 0);
/* Transmit channel disable */
writel(1, &i2s[device_num]->txffr);
/* flush tx fifo */
writel(1, &i2s[device_num]->channel[channel_num].tff);
/* flush individual fifo */
i2s_set_tx_word_length(device_num, word_length, channel_num);
/* Word buf_len is RESOLUTION_16_BIT */
i2s_master_configure(device_num, word_select_size, NO_CLOCK_GATING, word_mode);
/* word select size is 16 bits,gating after 16 bit */
i2s_set_tx_threshold(device_num, trigger_level, channel_num);
/* Interrupt trigger when FIFO level is 8 */
i2s_transmit_channel_enable(device_num, channel_num, 1);
}
void i2s_init(i2s_device_number_t device_num, i2s_transmit_t rxtx_mode, uint32_t channel_mask)
{
sysctl_clock_enable(SYSCTL_CLOCK_I2S0 + device_num);
sysctl_reset(SYSCTL_RESET_I2S0 + device_num);
sysctl_clock_set_threshold(SYSCTL_THRESHOLD_I2S0 + device_num, 7);
/*96k:5,44k:12,24k:23,22k:25 16k:35 sampling*/
/*sample rate*32bit*2 =75MHz/((N+1)*2) */
i2s_set_enable(device_num, 1);
i2s_disable_block(device_num, I2S_TRANSMITTER);
i2s_disable_block(device_num, I2S_RECEIVER);
if (rxtx_mode == I2S_TRANSMITTER)
{
for (int i=0; i<4; i++)
{
if ((channel_mask & 0x3) == 0x3)
{
i2s_set_mask_interrupt(device_num, I2S_CHANNEL_0 + i, 1, 1, 1, 1);
i2s_transimit_enable(device_num, I2S_CHANNEL_0 + i);
}
else
{
i2s_transmit_channel_enable(device_num, I2S_CHANNEL_0 + i, 0);
}
channel_mask >>= 2;
}
i2s_transmit_dma_enable(device_num, 1);
}
else
{
for (int i=0; i<4; i++)
{
if ((channel_mask & 0x3) == 0x3)
{
i2s_set_mask_interrupt(device_num, I2S_CHANNEL_0 + i, 1, 1, 1, 1);
i2s_receive_enable(device_num, I2S_CHANNEL_0 + i);
}
else
{
i2s_recv_channel_enable(device_num, I2S_CHANNEL_0 + i, 0);
}
channel_mask >>= 2;
}
/* Set expand_en when receive */
i2s_set_sign_expand_en(device_num, 1);
i2s_receive_dma_enable(device_num, 1);
}
}
uint32_t i2s_set_sample_rate(i2s_device_number_t device_num, uint32_t sample_rate)
{
ccr_t u_ccr;
uint32_t pll2_clock = 0;
pll2_clock = sysctl_pll_get_freq(SYSCTL_PLL2);
u_ccr.reg_data = readl(&i2s[device_num]->ccr);
/* 0x0 for 16sclk cycles, 0x1 for 24 sclk cycles 0x2 for 32 sclk */
uint32_t v_clk_word_size = (u_ccr.ccr.clk_word_size + 2) * 8;
uint32_t threshold = round(pll2_clock / (sample_rate * 2.0 * v_clk_word_size * 2.0) - 1);
sysctl_clock_set_threshold(SYSCTL_THRESHOLD_I2S0 + device_num, threshold);
return sysctl_clock_get_freq(SYSCTL_CLOCK_I2S0 + device_num);
}
int i2s_dmac_irq(void *ctx)
{
i2s_instance_t *v_instance = (i2s_instance_t *)ctx;
dmac_irq_unregister(v_instance->dmac_channel);
if(v_instance->i2s_int_instance.callback)
{
v_instance->i2s_int_instance.callback(v_instance->i2s_int_instance.ctx);
}
return 0;
}
void i2s_handle_data_dma(i2s_device_number_t device_num, i2s_data_t data, plic_interrupt_t *cb)
{
ASSERT(device_num < I2S_DEVICE_MAX);
if(data.transfer_mode == I2S_SEND)
{
ASSERT(data.tx_buf && data.tx_len);
if(!data.nowait_dma_idle)
{
dmac_wait_idle(data.tx_channel);
}
if(cb)
{
g_i2s_send_instance[device_num].i2s_int_instance.callback = cb->callback;
g_i2s_send_instance[device_num].i2s_int_instance.ctx = cb->ctx;
g_i2s_send_instance[device_num].dmac_channel = data.tx_channel;
g_i2s_send_instance[device_num].transfer_mode = I2S_SEND;
dmac_irq_register(data.tx_channel, i2s_dmac_irq, &g_i2s_send_instance[device_num], cb->priority);
}
sysctl_dma_select((sysctl_dma_channel_t)data.tx_channel, SYSCTL_DMA_SELECT_I2S0_TX_REQ + device_num * 2);
dmac_set_single_mode(data.tx_channel, data.tx_buf, (void *)(&i2s[device_num]->txdma), DMAC_ADDR_INCREMENT,
DMAC_ADDR_NOCHANGE, DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, data.tx_len);
if(!cb && data.wait_dma_done)
{
dmac_wait_done(data.tx_channel);
}
}
else
{
ASSERT(data.rx_buf && data.rx_len);
if(!data.nowait_dma_idle)
{
dmac_wait_idle(data.rx_channel);
}
if(cb)
{
g_i2s_recv_instance[device_num].i2s_int_instance.callback = cb->callback;
g_i2s_recv_instance[device_num].i2s_int_instance.ctx = cb->ctx;
g_i2s_recv_instance[device_num].dmac_channel = data.rx_channel;
g_i2s_recv_instance[device_num].transfer_mode = I2S_RECEIVE;
dmac_irq_register(data.rx_channel, i2s_dmac_irq, &g_i2s_recv_instance[device_num], cb->priority);
}
sysctl_dma_select((sysctl_dma_channel_t)data.rx_channel, SYSCTL_DMA_SELECT_I2S0_RX_REQ + device_num * 2);
dmac_set_single_mode(data.rx_channel, (void *)(&i2s[device_num]->rxdma), data.rx_buf, DMAC_ADDR_NOCHANGE, DMAC_ADDR_INCREMENT,
DMAC_MSIZE_1, DMAC_TRANS_WIDTH_32, data.rx_len);
if(!cb && data.wait_dma_done)
{
dmac_wait_done(data.rx_channel);
}
}
}

View File

@ -0,0 +1,783 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _DRIVER_I2S_H
#define _DRIVER_I2S_H
#include <stdint.h>
#include <stddef.h>
#include "platform.h"
#include "io.h"
#include "dmac.h"
#include "bsp.h"
#ifdef __cplusplus
extern "C" {
#endif
#define I2S0_IN_D0 90
#define I2S0_SCLK 88
#define I2S0_WS 89
typedef enum _i2s_device_number
{
I2S_DEVICE_0 = 0,
I2S_DEVICE_1 = 1,
I2S_DEVICE_2 = 2,
I2S_DEVICE_MAX
} i2s_device_number_t;
typedef enum _i2s_channel_num
{
I2S_CHANNEL_0 = 0,
I2S_CHANNEL_1 = 1,
I2S_CHANNEL_2 = 2,
I2S_CHANNEL_3 = 3
} i2s_channel_num_t;
typedef enum _i2s_transmit
{
I2S_TRANSMITTER = 0,
I2S_RECEIVER = 1
} i2s_transmit_t;
typedef enum _i2s_work_mode
{
STANDARD_MODE = 1,
RIGHT_JUSTIFYING_MODE = 2,
LEFT_JUSTIFYING_MODE = 4
} i2s_work_mode_t;
typedef enum _sclk_gating_cycles
{
/* Clock gating is diable */
NO_CLOCK_GATING = 0x0,
/* Gating after 12 sclk cycles */
CLOCK_CYCLES_12 = 0x1,
/* Gating after 16 sclk cycles */
CLOCK_CYCLES_16 = 0x2,
/* Gating after 20 sclk cycles */
CLOCK_CYCLES_20 = 0x3,
/* Gating after 24 sclk cycles */
CLOCK_CYCLES_24 = 0x4
} i2s_sclk_gating_cycles_t;
typedef enum _word_select_cycles
{
/* 16 sclk cycles */
SCLK_CYCLES_16 = 0x0,
/* 24 sclk cycles */
SCLK_CYCLES_24 = 0x1,
/* 32 sclk cycles */
SCLK_CYCLES_32 = 0x2
} i2s_word_select_cycles_t;
typedef enum _word_length
{
/* Ignore the word length */
IGNORE_WORD_LENGTH = 0x0,
/* 12-bit data resolution of the receiver */
RESOLUTION_12_BIT = 0x1,
/* 16-bit data resolution of the receiver */
RESOLUTION_16_BIT = 0x2,
/* 20-bit data resolution of the receiver */
RESOLUTION_20_BIT = 0x3,
/* 24-bit data resolution of the receiver */
RESOLUTION_24_BIT = 0x4,
/* 32-bit data resolution of the receiver */
RESOLUTION_32_BIT = 0x5
} i2s_word_length_t;
typedef enum _fifo_threshold
{
/* Interrupt trigger when FIFO level is 1 */
TRIGGER_LEVEL_1 = 0x0,
/* Interrupt trigger when FIFO level is 2 */
TRIGGER_LEVEL_2 = 0x1,
/* Interrupt trigger when FIFO level is 3 */
TRIGGER_LEVEL_3 = 0x2,
/* Interrupt trigger when FIFO level is 4 */
TRIGGER_LEVEL_4 = 0x3,
/* Interrupt trigger when FIFO level is 5 */
TRIGGER_LEVEL_5 = 0x4,
/* Interrupt trigger when FIFO level is 6 */
TRIGGER_LEVEL_6 = 0x5,
/* Interrupt trigger when FIFO level is 7 */
TRIGGER_LEVEL_7 = 0x6,
/* Interrupt trigger when FIFO level is 8 */
TRIGGER_LEVEL_8 = 0x7,
/* Interrupt trigger when FIFO level is 9 */
TRIGGER_LEVEL_9 = 0x8,
/* Interrupt trigger when FIFO level is 10 */
TRIGGER_LEVEL_10 = 0x9,
/* Interrupt trigger when FIFO level is 11 */
TRIGGER_LEVEL_11 = 0xa,
/* Interrupt trigger when FIFO level is 12 */
TRIGGER_LEVEL_12 = 0xb,
/* Interrupt trigger when FIFO level is 13 */
TRIGGER_LEVEL_13 = 0xc,
/* Interrupt trigger when FIFO level is 14 */
TRIGGER_LEVEL_14 = 0xd,
/* Interrupt trigger when FIFO level is 15 */
TRIGGER_LEVEL_15 = 0xe,
/* Interrupt trigger when FIFO level is 16 */
TRIGGER_LEVEL_16 = 0xf
} i2s_fifo_threshold_t;
typedef struct _i2s_ier
{
/* Bit 0 is ien, 0 for disable i2s and 1 for enable i2s */
uint32_t ien : 1;
/* Bits [31:1] is reserved */
uint32_t resv : 31;
} __attribute__((packed, aligned(4))) i2s_ier_t;
typedef union _ier_u
{
i2s_ier_t ier;
uint32_t reg_data;
} ier_t;
typedef struct _i2s_irer
{
/* Bit 0 is receiver block enable,
* 0 for receiver disable
* 1 for receiver enable
*/
uint32_t rxen : 1;
/* Bits [31:1] is reserved */
uint32_t resv : 31;
} __attribute__((packed, aligned(4))) i2s_irer_t;
typedef union _irer_u
{
i2s_irer_t irer;
uint32_t reg_data;
} irer_t;
typedef struct _i2s_iter
{
uint32_t txen : 1;
/* Bit 0 is transmitter block enable,
* 0 for transmitter disable
* 1 for transmitter enable
*/
uint32_t resv : 31;
/* Bits [31:1] is reserved */
} __attribute__((packed, aligned(4))) i2s_iter_t;
typedef union _iter_u
{
i2s_iter_t iter;
uint32_t reg_data;
} iter_t;
typedef struct _i2s_cer
{
uint32_t clken : 1;
/* Bit 0 is clock generation enable/disable,
* 0 for clock generation disable,
* 1 for clock generation enable
*/
uint32_t resv : 31;
/* Bits [31:1] is reserved */
} __attribute__((packed, aligned(4))) i2s_cer_t;
typedef union _cer_u
{
i2s_cer_t cer;
uint32_t reg_data;
} cer_t;
typedef struct _i2s_ccr
{
/* Bits [2:0] is used to program the gating of sclk,
* 0x0 for clock gating is diable,
* 0x1 for gating after 12 sclk cycles
* 0x2 for gating after 16 sclk cycles
* 0x3 for gating after 20 sclk cycles
* 0x4 for gating after 24 sclk cycles
*/
uint32_t clk_gate : 3;
/* Bits [4:3] used program the number of sclk cycles for which the
* word select line stayd in the left aligned or right aligned mode.
* 0x0 for 16sclk cycles, 0x1 for 24 sclk cycles 0x2 for 32 sclk
* cycles
*/
uint32_t clk_word_size : 2;
/* Bit[5:7] is alignment mode setting.
* 0x1 for standard i2s format
* 0x2 for right aligned format
* 0x4 for left aligned format
*/
uint32_t align_mode : 3;
/* Bit[8] is DMA transmit enable control */
uint32_t dma_tx_en : 1;
/* Bit[9] is DMA receive enable control */
uint32_t dma_rx_en : 1;
uint32_t dma_divide_16 : 1;
/* Bit[10] split 32bit data to two 16 bit data and filled in left
* and right channel. Used with dma_tx_en or dma_rx_en
*/
uint32_t sign_expand_en : 1;
uint32_t resv : 20;
/* Bits [31:11] is reseved */
} __attribute__((packed, aligned(4))) i2s_ccr_t;
typedef union _ccr_u
{
i2s_ccr_t ccr;
uint32_t reg_data;
} ccr_t;
typedef struct _i2s_rxffr
{
uint32_t rxffr : 1;
/* Bit 0 is receiver FIFO reset,
* 0 for does not flush RX FIFO, 1 for flush RX FIFO
*/
uint32_t resv : 31;
/* Bits [31:1] is reserved */
} __attribute__((packed, aligned(4))) i2s_rxffr_t;
typedef union _rxffr_u
{
i2s_rxffr_t rxffr;
uint32_t reg_data;
} rxffr_t;
typedef struct _i2s_lrbrthr
{
uint32_t fifo : 16;
/* Bits [15:0] if used data receive or transmit */
uint32_t resv : 16;
} i2s_lrbrthr_t;
typedef union _lrbthr_u
{
i2s_lrbrthr_t buffer;
uint32_t reg_data;
} lrbthr_t;
typedef struct _i2s_rthr
{
/* Bits [15:0] is right stereo data transmitted serially
* from transmit channel input
*/
uint32_t rthrx : 16;
/* Bits [31:16] is reserved */
uint32_t resv : 16;
} __attribute__((packed, aligned(4))) i2s_rthr_t;
typedef union _rthr_u
{
i2s_rthr_t rthr;
uint32_t reg_data;
} rthr_t;
typedef struct _i2s_rer
{
/* Bit 0 is receive channel enable/disable, 0 for receive channel disable,
*1 for receive channel enable
*/
uint32_t rxchenx : 1;
/* Bits [31:1] is reseved */
uint32_t resv : 31;
} __attribute__((packed, aligned(4))) i2s_rer_t;
typedef union _rer_u
{
i2s_rer_t rer;
uint32_t reg_data;
} rer_t;
typedef struct _i2s_ter
{
/* Bit 0 is transmit channel enable/disable, 0 for transmit channel disable,
* 1 for transmit channel enable
*/
uint32_t txchenx : 1;
/* Bits [31:1] is reseved */
uint32_t resv : 31;
} __attribute__((packed, aligned(4))) i2s_ter_t;
typedef union _ter_u
{
i2s_ter_t ter;
uint32_t reg_data;
} ter_t;
typedef struct _i2s_rcr_tcr
{
/* Bits [2:0] is used to program desired data resolution of
* receiver/transmitter,
* 0x0 for ignore the word length
* 0x1 for 12-bit data resolution of the receiver/transmitter,
* 0x2 for 16-bit data resolution of the receiver/transmitter,
* 0x3 for 20-bit data resolution of the receiver/transmitter,
* 0x4 for 24-bit data resolution of the receiver/transmitter,
* 0x5 for 32-bit data resolution of the receiver/transmitter
*/
uint32_t wlen : 3;
/* Bits [31:3] is reseved */
uint32_t resv : 29;
} __attribute__((packed, aligned(4))) i2s_rcr_tcr_t;
typedef union _rcr_tcr_u {
i2s_rcr_tcr_t rcr_tcr;
uint32_t reg_data;
} rcr_tcr_t;
typedef struct _i2s_isr
{
/* Bit 0 is status of receiver data avaliable interrupt
* 0x0 for RX FIFO trigger level not reached
* 0x1 for RX FIFO trigger level is reached
*/
uint32_t rxda : 1;
/* Bit 1 is status of data overrun interrupt for rx channel
* 0x0 for RX FIFO write valid
* 0x1 for RX FIFO write overrun
*/
uint32_t rxfo : 1;
/* Bits [3:2] is reserved */
uint32_t resv1 : 2;
/* Bit 4 is status of transmit empty triger interrupt
* 0x0 for TX FIFO triiger level is reach
* 0x1 for TX FIFO trigger level is not reached
*/
uint32_t txfe : 1;
/* BIt 5 is status of data overrun interrupt for the TX channel
* 0x0 for TX FIFO write valid
* 0x1 for TX FIFO write overrun
*/
uint32_t txfo : 1;
/* BIts [31:6] is reserved */
uint32_t resv2 : 26;
} __attribute__((packed, aligned(4))) i2s_isr_t;
typedef union _isr_u
{
i2s_isr_t isr;
uint32_t reg_data;
} isr_t;
typedef struct _i2s_imr
{
/* Bit 0 is mask RX FIFO data available interrupt
* 0x0 for unmask RX FIFO data available interrupt
* 0x1 for mask RX FIFO data available interrupt
*/
uint32_t rxdam : 1;
/* Bit 1 is mask RX FIFO overrun interrupt
* 0x0 for unmask RX FIFO overrun interrupt
* 0x1 for mask RX FIFO overrun interrupt
*/
uint32_t rxfom : 1;
/* Bits [3:2] is reserved */
uint32_t resv1 : 2;
/* Bit 4 is mask TX FIFO empty interrupt,
* 0x0 for unmask TX FIFO empty interrupt,
* 0x1 for mask TX FIFO empty interrupt
*/
uint32_t txfem : 1;
/* BIt 5 is mask TX FIFO overrun interrupt
* 0x0 for mask TX FIFO overrun interrupt
* 0x1 for unmash TX FIFO overrun interrupt
*/
uint32_t txfom : 1;
/* Bits [31:6] is reserved */
uint32_t resv2 : 26;
} __attribute__((packed, aligned(4))) i2s_imr_t;
typedef union _imr_u
{
i2s_imr_t imr;
uint32_t reg_data;
} imr_t;
typedef struct _i2s_ror
{
/* Bit 0 is read this bit to clear RX FIFO data overrun interrupt
* 0x0 for RX FIFO write valid,
*0x1 for RX FIFO write overrun
*/
uint32_t rxcho : 1;
/* Bits [31:1] is reserved */
uint32_t resv : 31;
} __attribute__((packed, aligned(4))) i2s_ror_t;
typedef union _ror_u
{
i2s_ror_t ror;
uint32_t reg_data;
} ror_t;
typedef struct _i2s_tor
{
/* Bit 0 is read this bit to clear TX FIFO data overrun interrupt
* 0x0 for TX FIFO write valid,
*0x1 for TX FIFO write overrun
*/
uint32_t txcho : 1;
/* Bits [31:1] is reserved */
uint32_t resv : 31;
} __attribute__((packed, aligned(4))) i2s_tor_t;
typedef union _tor_u
{
i2s_tor_t tor;
uint32_t reg_data;
} tor_t;
typedef struct _i2s_rfcr
{
/* Bits [3:0] is used program the trigger level in the RX FIFO at
* which the receiver data available interrupt generate,
* 0x0 for interrupt trigger when FIFO level is 1,
* 0x2 for interrupt trigger when FIFO level is 2,
* 0x3 for interrupt trigger when FIFO level is 4,
* 0x4 for interrupt trigger when FIFO level is 5,
* 0x5 for interrupt trigger when FIFO level is 6,
* 0x6 for interrupt trigger when FIFO level is 7,
* 0x7 for interrupt trigger when FIFO level is 8,
* 0x8 for interrupt trigger when FIFO level is 9,
* 0x9 for interrupt trigger when FIFO level is 10,
* 0xa for interrupt trigger when FIFO level is 11,
* 0xb for interrupt trigger when FIFO level is 12,
* 0xc for interrupt trigger when FIFO level is 13,
* 0xd for interrupt trigger when FIFO level is 14,
* 0xe for interrupt trigger when FIFO level is 15,
* 0xf for interrupt trigger when FIFO level is 16
*/
uint32_t rxchdt : 4;
/* Bits [31:4] is reserved */
uint32_t rsvd_rfcrx : 28;
} __attribute__((packed, aligned(4))) i2s_rfcr_t;
typedef union _rfcr_u
{
i2s_rfcr_t rfcr;
uint32_t reg_data;
} rfcr_t;
typedef struct _i2s_tfcr
{
/* Bits [3:0] is used program the trigger level in the TX FIFO at
* which the receiver data available interrupt generate,
* 0x0 for interrupt trigger when FIFO level is 1,
* 0x2 for interrupt trigger when FIFO level is 2,
* 0x3 for interrupt trigger when FIFO level is 4,
* 0x4 for interrupt trigger when FIFO level is 5,
* 0x5 for interrupt trigger when FIFO level is 6,
* 0x6 for interrupt trigger when FIFO level is 7,
* 0x7 for interrupt trigger when FIFO level is 8,
* 0x8 for interrupt trigger when FIFO level is 9,
* 0x9 for interrupt trigger when FIFO level is 10,
* 0xa for interrupt trigger when FIFO level is 11,
* 0xb for interrupt trigger when FIFO level is 12,
* 0xc for interrupt trigger when FIFO level is 13,
* 0xd for interrupt trigger when FIFO level is 14,
* 0xe for interrupt trigger when FIFO level is 15,
* 0xf for interrupt trigger when FIFO level is 16
*/
uint32_t txchet : 4;
/* Bits [31:4] is reserved */
uint32_t rsvd_tfcrx : 28;
} __attribute__((packed, aligned(4))) i2s_tfcr_t;
typedef union _tfcr_u
{
i2s_tfcr_t tfcr;
uint32_t reg_data;
} tfcr_t;
typedef struct _i2s_rff
{
/* Bit 0 is receiver channel FIFO reset,
* 0x0 for does not flush an individual RX FIFO,
* 0x1 for flush an indiviadual RX FIFO
*/
uint32_t rxchfr : 1;
/*< Bits [31:1] is reserved ,write only */
uint32_t rsvd_rffx : 31;
} __attribute__((packed, aligned(4))) i2s_rff_t;
typedef union _rff_u
{
i2s_rff_t rff;
uint32_t reg_data;
} rff_t;
typedef struct _i2s_tff
{
/* Bit 0 is transmit channel FIFO reset,
* 0x0 for does not flush an individual TX FIFO,
* 0x1 for flush an indiviadual TX FIFO
*/
uint32_t rtxchfr : 1;
/*< Bits [31:1] is reserved ,write only */
uint32_t rsvd_rffx : 31;
} __attribute__((packed, aligned(4))) i2s_tff_t;
typedef union tff_u
{
i2s_tff_t tff;
uint32_t reg_data;
} tff_t;
typedef struct _i2s_channel
{
/* Left Receive or Left Transmit Register (0x20) */
volatile uint32_t left_rxtx;
/* Right Receive or Right Transmit Register (0x24) */
volatile uint32_t right_rxtx;
/* Receive Enable Register (0x28) */
volatile uint32_t rer;
/* Transmit Enable Register (0x2c) */
volatile uint32_t ter;
/* Receive Configuration Register (0x30) */
volatile uint32_t rcr;
/* Transmit Configuration Register (0x34) */
volatile uint32_t tcr;
/* Interrupt Status Register (0x38) */
volatile uint32_t isr;
/* Interrupt Mask Register (0x3c) */
volatile uint32_t imr;
/* Receive Overrun Register (0x40) */
volatile uint32_t ror;
/* Transmit Overrun Register (0x44) */
volatile uint32_t tor;
/* Receive FIFO Configuration Register (0x48) */
volatile uint32_t rfcr;
/* Transmit FIFO Configuration Register (0x4c) */
volatile uint32_t tfcr;
/* Receive FIFO Flush Register (0x50) */
volatile uint32_t rff;
/* Transmit FIFO Flush Register (0x54) */
volatile uint32_t tff;
/* reserved (0x58-0x5c) */
volatile uint32_t reserved1[2];
} __attribute__((packed, aligned(4))) i2s_channel_t;
/****is* i2s.api/dw_i2s_portmap
* NAME
* i2s_t
* DESCRIPTION
* This is the structure used for accessing the i2s register
* portmap.
* EXAMPLE
* struct i2s_t *portmap;
* portmap = (struct dw_i2s_portmap *) DW_APB_I2S_BASE;
* SOURCE
*/
typedef struct _i2s
{
/* I2S Enable Register (0x00) */
volatile uint32_t ier;
/* I2S Receiver Block Enable Register (0x04) */
volatile uint32_t irer;
/* I2S Transmitter Block Enable Register (0x08) */
volatile uint32_t iter;
/* Clock Enable Register (0x0c) */
volatile uint32_t cer;
/* Clock Configuration Register (0x10) */
volatile uint32_t ccr;
/* Receiver Block FIFO Reset Register (0x04) */
volatile uint32_t rxffr;
/* Transmitter Block FIFO Reset Register (0x18) */
volatile uint32_t txffr;
/* reserved (0x1c) */
volatile uint32_t reserved1;
volatile i2s_channel_t channel[4];
/* reserved (0x118-0x1bc) */
volatile uint32_t reserved2[40];
/* Receiver Block DMA Register (0x1c0) */
volatile uint32_t rxdma;
/* Reset Receiver Block DMA Register (0x1c4) */
volatile uint32_t rrxdma;
/* Transmitter Block DMA Register (0x1c8) */
volatile uint32_t txdma;
/* Reset Transmitter Block DMA Register (0x1cc) */
volatile uint32_t rtxdma;
/* reserved (0x1d0-0x1ec) */
volatile uint32_t reserved3[8];
/* Component Parameter Register 2 (0x1f0) */
volatile uint32_t i2s_comp_param_2;
/* Component Parameter Register 1 (0x1f4) */
volatile uint32_t i2s_comp_param_1;
/* I2S Component Version Register (0x1f8) */
volatile uint32_t i2s_comp_version_1;
/* I2S Component Type Register (0x1fc) */
volatile uint32_t i2s_comp_type;
} __attribute__((packed, aligned(4))) i2s_t;
typedef enum _i2s_transfer_mode
{
I2S_SEND,
I2S_RECEIVE,
} i2s_transfer_mode_t;
typedef struct _i2s_data_t
{
dmac_channel_number_t tx_channel;
dmac_channel_number_t rx_channel;
uint32_t *tx_buf;
size_t tx_len;
uint32_t *rx_buf;
size_t rx_len;
i2s_transfer_mode_t transfer_mode;
bool nowait_dma_idle;
bool wait_dma_done;
} i2s_data_t;
/**
* @brief I2S object instance
*/
extern volatile i2s_t *const i2s[3];
/**
* @brief I2s init
*
* @param[in] device_num The device number
* @param[in] rxtx_mode I2s work mode
* @param[in] channel_mask Channel mask to which channel work
*
*/
void i2s_init(i2s_device_number_t device_num, i2s_transmit_t rxtx_mode, uint32_t channel_mask);
/**
* @brief Read pcm data from dma
*
* @param[in] device_num which of device
* @param[in] buf save read data
* @param[in] buf_len the length to read form i2s
* @param[in] channel_num The dma channel number
*
* @return result
* - 0 Success
* - Other Fail
*/
void i2s_receive_data_dma(i2s_device_number_t device_num, uint32_t *buf, size_t buf_len,
dmac_channel_number_t channel_num);
/**
* @brief Write pcm data to channel_num channel by dma, first wait dmac done
*
* @param[in] device_num which of device
* @param[in] pcm Send data
* @param[in] buf_len Send data length
* @param[in] channel_num dmac channel
*
*/
void i2s_send_data_dma(i2s_device_number_t device_num, const void *buf, size_t buf_len, dmac_channel_number_t channel_num);
/**
* @brief I2S receive channel configure
*
* @param[in] device_num The device number
* @param[in] channel_num The channel number
* @param[in] word_length The word length
* @param[in] word_select_size The word select size
* @param[in] trigger_level The trigger level
*/
void i2s_rx_channel_config(i2s_device_number_t device_num,
i2s_channel_num_t channel_num,
i2s_word_length_t word_length,
i2s_word_select_cycles_t word_select_size,
i2s_fifo_threshold_t trigger_level,
i2s_work_mode_t word_mode);
/**
* @brief I2S transmit channel enable
*
* @param[in] device_num The device number
* @param[in] channel_num The channel number
* @param[in] word_length The word length
* @param[in] word_select_size The word select size
* @param[in] trigger_level The trigger level
*/
void i2s_tx_channel_config(i2s_device_number_t device_num,
i2s_channel_num_t channel_num,
i2s_word_length_t word_length,
i2s_word_select_cycles_t word_select_size,
i2s_fifo_threshold_t trigger_level,
i2s_work_mode_t word_mode);
/**
* @brief Play PCM format audio
*
* @param[in] device_num The device number
* @param[in] channel_num The channel number
* @param[in] buf PCM data
* @param[in] buf_len PCM data length
* @param[in] frame Transmit amount once
* @param[in] bits_per_sample Sample bit length
* @param[in] track_num Track amount
*/
void i2s_play(i2s_device_number_t device_num, dmac_channel_number_t channel_num,
const uint8_t *buf, size_t buf_len, size_t frame, size_t bits_per_sample, uint8_t track_num);
/**
* @brief Play PCM format audio
*
* @param[in] device_num The device number
* @param[in] sample_rate The Sample rate
*
*
* @return The real sample rate
*/
uint32_t i2s_set_sample_rate(i2s_device_number_t device_num, uint32_t sample_rate);
/**
* @brief Set dma_divide_16 split 32bit data to two 16 bit data and filled in left
* and right channel. Used with dma_tx_en or dma_rx_en
*
* @param[in] device_num The device number
* @param[in] enable The value of dma_divide_16 0:disable 1:enable
*
* @return result
* - 0 Success
* - Other Fail
*/
int i2s_set_dma_divide_16(i2s_device_number_t device_num, uint32_t enable);
/**
* @brief Get dma_divide_16.
*
* @param[in] device_num The device number
*
* @return result
* - <0 Fail
* - other value of dma_divide_16
*/
int i2s_get_dma_divide_16(i2s_device_number_t device_num);
/**
* @brief I2s handle transfer data operations
*
* @param[in] device_num I2s device number
* @param[in] data I2s data information
* @param[in] cb I2s dma callback
*
*/
void i2s_handle_data_dma(i2s_device_number_t device_num, i2s_data_t data, plic_interrupt_t *cb);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,65 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Enable kernel-mode log API */
#include <stdint.h>
#include <stdlib.h>
#include "interrupt.h"
#include "encoding.h"
#include "syscalls.h"
uintptr_t __attribute__((weak))
handle_irq_dummy(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
//dump_core("unhandled interrupt", cause, epc, regs, fregs);
sys_exit(1337);
return epc;
}
uintptr_t __attribute__((weak, alias("handle_irq_dummy")))
handle_irq_m_soft(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32]);
uintptr_t __attribute__((weak, alias("handle_irq_dummy")))
handle_irq_m_timer(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32]);
extern uintptr_t
handle_irq_m_ext(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32]);
uintptr_t __attribute__((weak))
handle_irq(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Woverride-init"
#endif
/* clang-format off */
static uintptr_t (* const irq_table[])(
uintptr_t cause,
uintptr_t epc,
uintptr_t regs[32],
uintptr_t fregs[32]) =
{
[0 ... 14] = handle_irq_dummy,
[IRQ_M_SOFT] = handle_irq_m_soft,
[IRQ_M_TIMER] = handle_irq_m_timer,
[IRQ_M_EXT] = handle_irq_m_ext,
};
/* clang-format on */
#if defined(__GNUC__)
#pragma GCC diagnostic warning "-Woverride-init"
#endif
return irq_table[cause & CAUSE_MACHINE_IRQ_REASON_MASK](cause, epc, regs, fregs);
}

View File

@ -0,0 +1,47 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _BSP_INTERRUPT_H
#define _BSP_INTERRUPT_H
#ifdef __cplusplus
extern "C" {
#endif
/* clang-format off */
/* Machine interrupt mask for 64 bit system, 0x8000 0000 0000 0000 */
#define CAUSE_MACHINE_IRQ_MASK (0x1ULL << 63)
/* Machine interrupt reason mask for 64 bit system, 0x7FFF FFFF FFFF FFFF */
#define CAUSE_MACHINE_IRQ_REASON_MASK (CAUSE_MACHINE_IRQ_MASK - 1)
/* Hypervisor interrupt mask for 64 bit system, 0x8000 0000 0000 0000 */
#define CAUSE_HYPERVISOR_IRQ_MASK (0x1ULL << 63)
/* Hypervisor interrupt reason mask for 64 bit system, 0x7FFF FFFF FFFF FFFF */
#define CAUSE_HYPERVISOR_IRQ_REASON_MASK (CAUSE_HYPERVISOR_IRQ_MASK - 1)
/* Supervisor interrupt mask for 64 bit system, 0x8000 0000 0000 0000 */
#define CAUSE_SUPERVISOR_IRQ_MASK (0x1ULL << 63)
/* Supervisor interrupt reason mask for 64 bit system, 0x7FFF FFFF FFFF FFFF */
#define CAUSE_SUPERVISOR_IRQ_REASON_MASK (CAUSE_SUPERVISOR_IRQ_MASK - 1)
/* clang-format on */
#ifdef __cplusplus
}
#endif
#endif /* _BSP_INTERRUPT_H */

View File

@ -0,0 +1,50 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _DRIVER_IO_H
#define _DRIVER_IO_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define readb(addr) (*(volatile uint8_t *)(addr))
#define readw(addr) (*(volatile uint16_t *)(addr))
#define readl(addr) (*(volatile uint32_t *)(addr))
#define readq(addr) (*(volatile uint64_t *)(addr))
#define writeb(v, addr) \
{ \
(*(volatile uint8_t *)(addr)) = (v); \
}
#define writew(v, addr) \
{ \
(*(volatile uint16_t *)(addr)) = (v); \
}
#define writel(v, addr) \
{ \
(*(volatile uint32_t *)(addr)) = (v); \
}
#define writeq(v, addr) \
{ \
(*(volatile uint64_t *)(addr)) = (v); \
}
#ifdef __cplusplus
}
#endif
#endif /* _DRIVER_IO_H */

View File

@ -0,0 +1,42 @@
/************************************************************************************
* arch/risc-v/src/nr5m100/nr5_arch.c
*
* Copyright (C) 2016 Ken Pettit. All rights reserved.
* Author: Ken Pettit <pettitkd@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
************************************************************************************/
#include <string.h>
#include <nuttx/arch.h>
#include "k210.h"
#include "uarths.h"
void up_puts(const char *p)
{
uarths_puts(p);
}

View File

@ -0,0 +1,256 @@
/****************************************************************************
* arch/risc-v/src/k210/k210_head.S
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <arch/csr.h>
#include <arch/rv64gc/irq.h>
#include "chip.h"
#include "k210_memorymap.h"
#include "riscv_internal.h"
/****************************************************************************
* Public Symbols
****************************************************************************/
.global exception_common
/* Imported symbols */
.extern __trap_vec
.section .text
.global __start
__start:
#if defined(__riscv_flen) && __riscv_flen > 0
/* Enable FPU */
li a0, MSTATUS_FS
csrs mstatus, a0
csrw fcsr, x0
#endif
/* Load mhartid (cpuid) */
csrr a0, mhartid
/* Set stack pointer to the idle thread stack */
bnez a0, 1f
la sp, K210_IDLESTACK0_TOP
j 2f
1:
la sp, K210_IDLESTACK1_TOP
/* In case of single CPU config, stop here */
#if !(defined CONFIG_SMP) || (CONFIG_SMP_NCPUS == 1)
wfi
#endif
2:
/* Disable all interrupts (i.e. timer, external) in mie */
csrw mie, zero
/* Initialize the Machine Trap Vector */
la t0, __trap_vec
csrw mtvec, t0
/* Jump to __k210_start with mhartid */
j __k210_start
/* We shouldn't return from __k210_start */
.global _init
.global _fini
_init:
_fini:
/* These don't have to do anything since we use init_array/fini_array. */
ret
/****************************************************************************
* Name: exception_common
****************************************************************************/
exception_common:
#if 0
csrr gp, mcause /* exception cause */
addi tp, zero, 10 /* 10 = machine ecall */
bgtu gp, tp, normal_irq
ld sp, g_fstack_top /* Set sp to fault stack */
normal_irq:
addi gp, zero, 0 /* clear */
#endif
addi sp, sp, -XCPTCONTEXT_SIZE
sd x1, 1*8(sp) /* ra */
sd x3, 3*8(sp) /* gp */
sd x4, 4*8(sp) /* tp */
sd x5, 5*8(sp) /* t0 */
sd x6, 6*8(sp) /* t1 */
sd x7, 7*8(sp) /* t2 */
sd x8, 8*8(sp) /* s0 */
sd x9, 9*8(sp) /* s1 */
sd x10, 10*8(sp) /* a0 */
sd x11, 11*8(sp) /* a1 */
sd x12, 12*8(sp) /* a2 */
sd x13, 13*8(sp) /* a3 */
sd x14, 14*8(sp) /* a4 */
sd x15, 15*8(sp) /* a5 */
sd x16, 16*8(sp) /* a6 */
sd x17, 17*8(sp) /* a7 */
sd x18, 18*8(sp) /* s2 */
sd x19, 19*8(sp) /* s3 */
sd x20, 20*8(sp) /* s4 */
sd x21, 21*8(sp) /* s5 */
sd x22, 22*8(sp) /* s6 */
sd x23, 23*8(sp) /* s7 */
sd x24, 24*8(sp) /* s8 */
sd x25, 25*8(sp) /* s9 */
sd x26, 26*8(sp) /* s10 */
sd x27, 27*8(sp) /* s11 */
sd x28, 28*8(sp) /* t3 */
sd x29, 29*8(sp) /* t4 */
sd x30, 30*8(sp) /* t5 */
sd x31, 31*8(sp) /* t6 */
csrr s0, mstatus
sd s0, 32*8(sp) /* mstatus */
addi s0, sp, XCPTCONTEXT_SIZE
sd s0, 2*8(sp) /* original SP */
/* Setup arg0(exception cause), arg1(context) */
csrr a0, mcause /* exception cause */
csrr s0, mepc
sd s0, 0(sp) /* exception PC */
mv a1, sp /* context = sp */
#if CONFIG_ARCH_INTERRUPTSTACK > 15
/* Load mhartid (cpuid) */
csrr s0, mhartid
/* Switch to interrupt stack */
bnez s0, 3f
la sp, g_intstacktop
j 4f
3:
la sp, g_intstacktop
addi sp, sp, -(CONFIG_ARCH_INTERRUPTSTACK & ~15)
4:
#endif
/* Call interrupt handler in C */
jal x1, k210_dispatch_irq
/* If context switch is needed, return a new sp */
mv sp, a0
ld s0, 0(sp) /* restore mepc */
csrw mepc, s0
ld s0, 32*8(sp) /* restore mstatus */
li s1, MSTATUS_FS
or s0, s0, s1
csrw mstatus, s0
ld x3, 3*8(sp) /* gp */
ld x4, 4*8(sp) /* tp */
ld x5, 5*8(sp) /* t0 */
ld x6, 6*8(sp) /* t1 */
ld x7, 7*8(sp) /* t2 */
ld x8, 8*8(sp) /* s0 */
ld x9, 9*8(sp) /* s1 */
ld x10, 10*8(sp) /* a0 */
ld x11, 11*8(sp) /* a1 */
ld x12, 12*8(sp) /* a2 */
ld x13, 13*8(sp) /* a3 */
ld x14, 14*8(sp) /* a4 */
ld x15, 15*8(sp) /* a5 */
ld x16, 16*8(sp) /* a6 */
ld x17, 17*8(sp) /* a7 */
ld x18, 18*8(sp) /* s2 */
ld x19, 19*8(sp) /* s3 */
ld x20, 20*8(sp) /* s4 */
ld x21, 21*8(sp) /* s5 */
ld x22, 22*8(sp) /* s6 */
ld x23, 23*8(sp) /* s7 */
ld x24, 24*8(sp) /* s8 */
ld x25, 25*8(sp) /* s9 */
ld x26, 26*8(sp) /* s10 */
ld x27, 27*8(sp) /* s11 */
ld x28, 28*8(sp) /* t3 */
ld x29, 29*8(sp) /* t4 */
ld x30, 30*8(sp) /* t5 */
ld x31, 31*8(sp) /* t6 */
ld x1, 1*8(sp) /* ra */
ld sp, 2*8(sp) /* restore original sp */
/* Return from Machine Interrupt */
mret
/************************************************************************************
* Name: g_intstackalloc and g_intstacktop
************************************************************************************/
#if CONFIG_ARCH_INTERRUPTSTACK > 15
.bss
.balign 16
.global g_intstackalloc
.global g_intstacktop
.type g_intstackalloc, object
.type g_intstacktop, object
g_intstackalloc:
#ifndef CONFIG_SMP
.skip ((CONFIG_ARCH_INTERRUPTSTACK + 8) & ~15)
#else
.skip (((CONFIG_ARCH_INTERRUPTSTACK * CONFIG_SMP_NCPUS) + 8) & ~15)
#endif
g_intstacktop:
.size g_intstacktop, 0
#ifndef CONFIG_SMP
.size g_intstackalloc, (CONFIG_ARCH_INTERRUPTSTACK & ~15)
#else
.size g_intstackalloc, ((CONFIG_ARCH_INTERRUPTSTACK * CONFIG_SMP_NCPUS) & ~15)
#endif
#endif

View File

@ -0,0 +1,162 @@
/****************************************************************************
* arch/risc-v/src/k210/k210_start.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/init.h>
#include <nuttx/arch.h>
#include <arch/board/board.h>
#include <debug.h>
#include "riscv_arch.h"
#include "k210_clockconfig.h"
#include "k210_userspace.h"
#include "k210.h"
#include "chip.h"
#include "plic.h"
#include "uarths.h"
#include "fpioa.h"
#include "dmac.h"
#include "fft.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#ifdef CONFIG_DEBUG_FEATURES
# define showprogress(c) riscv_lowputc(c)
#else
# define showprogress(c)
#endif
/****************************************************************************
* Public Data
****************************************************************************/
/* NOTE: g_idle_topstack needs to point the top of the idle stack
* for CPU0 and this value is used in up_initial_state()
*/
uintptr_t g_idle_topstack = K210_IDLESTACK0_TOP;
volatile bool g_serial_ok = false;
extern void k210_cpu_boot(uint32_t);
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: k210_start
****************************************************************************/
void __k210_start(uint32_t mhartid)
{
const uint32_t *src;
uint32_t *dest;
if (0 < mhartid)
{
goto cpu1;
}
/* Clear .bss. We'll do this inline (vs. calling memset) just to be
* certain that there are no issues with the state of global variables.
*/
for (dest = &_sbss; dest < &_ebss; )
{
*dest++ = 0;
}
/* Move the initialized data section from his temporary holding spot in
* FLASH into the correct place in SRAM. The correct place in SRAM is
* give by _sdata and _edata. The temporary location is in FLASH at the
* end of all of the other read-only data (.text, .rodata) at _eronly.
*/
for (src = &_eronly, dest = &_sdata; dest < &_edata; )
{
*dest++ = *src++;
}
/* Setup PLL */
k210_clockconfig();
/* Init PLIC */
plic_init();
/* Init UART */
uarths_init();
/* Init FPIOA */
fpioa_init();
/* Init DMA */
dmac_init();
k210_lowsetup();
showprogress('A');
#ifdef USE_EARLYSERIALINIT
riscv_earlyserialinit();
#endif
showprogress('B');
g_serial_ok = true;
/* Do board initialization */
k210_boardinitialize();
showprogress('C');
/* For the case of the separate user-/kernel-space build, perform whatever
* platform specific initialization of the user memory is required.
* Normally this just means initializing the user space .data and .bss
* segments.
*/
#ifdef CONFIG_BUILD_PROTECTED
k210_userspace();
showprogress('D');
#endif
/* Call nx_start() */
nx_start();
cpu1:
showprogress('a');
#if defined(CONFIG_SMP) && (CONFIG_SMP_NCPUS == 2)
k210_cpu_boot(mhartid);
#endif
while (true)
{
asm("WFI");
}
}

View File

@ -0,0 +1,99 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _BSP_PLATFORM_H
#define _BSP_PLATFORM_H
#ifdef __cplusplus
extern "C" {
#endif
/* clang-format off */
/* Register base address */
/* Under Coreplex */
#define CLINT_BASE_ADDR (0x02000000U)
#define PLIC_BASE_ADDR (0x0C000000U)
/* Under TileLink */
#define UARTHS_BASE_ADDR (0x38000000U)
#define GPIOHS_BASE_ADDR (0x38001000U)
/* Under AXI 64 bit */
#define RAM_BASE_ADDR (0x80000000U)
//#define RAM_SIZE (6 * 1024 * 1024U)
#define IO_BASE_ADDR (0x40000000U)
#define IO_SIZE (6 * 1024 * 1024U)
#define AI_RAM_BASE_ADDR (0x80600000U)
#define AI_RAM_SIZE (2 * 1024 * 1024U)
#define AI_IO_BASE_ADDR (0x40600000U)
#define AI_IO_SIZE (2 * 1024 * 1024U)
#define AI_BASE_ADDR (0x40800000U)
#define AI_SIZE (12 * 1024 * 1024U)
#define FFT_BASE_ADDR (0x42000000U)
#define FFT_SIZE (4 * 1024 * 1024U)
#define ROM_BASE_ADDR (0x88000000U)
#define ROM_SIZE (128 * 1024U)
/* Under AHB 32 bit */
#define DMAC_BASE_ADDR (0x50000000U)
/* Under APB1 32 bit */
#define GPIO_BASE_ADDR (0x50200000U)
#define UART1_BASE_ADDR (0x50210000U)
#define UART2_BASE_ADDR (0x50220000U)
#define UART3_BASE_ADDR (0x50230000U)
#define SPI_SLAVE_BASE_ADDR (0x50240000U)
#define I2S0_BASE_ADDR (0x50250000U)
#define I2S1_BASE_ADDR (0x50260000U)
#define I2S2_BASE_ADDR (0x50270000U)
#define I2C0_BASE_ADDR (0x50280000U)
#define I2C1_BASE_ADDR (0x50290000U)
#define I2C2_BASE_ADDR (0x502A0000U)
#define FPIOA_BASE_ADDR (0x502B0000U)
#define SHA256_BASE_ADDR (0x502C0000U)
#define TIMER0_BASE_ADDR (0x502D0000U)
#define TIMER1_BASE_ADDR (0x502E0000U)
#define TIMER2_BASE_ADDR (0x502F0000U)
/* Under APB2 32 bit */
#define WDT0_BASE_ADDR (0x50400000U)
#define WDT1_BASE_ADDR (0x50410000U)
#define OTP_BASE_ADDR (0x50420000U)
#define DVP_BASE_ADDR (0x50430000U)
#define SYSCTL_BASE_ADDR (0x50440000U)
#define AES_BASE_ADDR (0x50450000U)
#define RTC_BASE_ADDR (0x50460000U)
/* Under APB3 32 bit */
#define SPI0_BASE_ADDR (0x52000000U)
#define SPI1_BASE_ADDR (0x53000000U)
#define SPI3_BASE_ADDR (0x54000000U)
/* clang-format on */
#ifdef __cplusplus
}
#endif
#endif /* _BSP_PLATFORM_H */

View File

@ -0,0 +1,209 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stddef.h>
#include <stdint.h>
#include "encoding.h"
#include "plic.h"
#include "syscalls.h"
#include "syslog.h"
volatile plic_t* const plic = (volatile plic_t*)PLIC_BASE_ADDR;
static plic_instance_t plic_instance[PLIC_NUM_CORES][IRQN_MAX];
void plic_init(void)
{
int i = 0;
/* Get current core id */
unsigned long core_id = current_coreid();
/* Disable all interrupts for the current core. */
for (i = 0; i < ((PLIC_NUM_SOURCES + 32u) / 32u); i++)
plic->target_enables.target[core_id].enable[i] = 0;
static uint8_t s_plic_priorities_init_flag = 0;
/* Set priorities to zero. */
if(s_plic_priorities_init_flag == 0)
{
for (i = 0; i < PLIC_NUM_SOURCES; i++)
plic->source_priorities.priority[i] = 0;
s_plic_priorities_init_flag = 1;
}
/* Set the threshold to zero. */
plic->targets.target[core_id].priority_threshold = 0;
/* Clear PLIC instance for every cores */
for (i = 0; i < IRQN_MAX; i++)
{
/* clang-format off */
plic_instance[core_id][i] = (const plic_instance_t){
.callback = NULL,
.ctx = NULL,
};
/* clang-format on */
}
/*
* A successful claim will also atomically clear the corresponding
* pending bit on the interrupt source. A target can perform a claim
* at any time, even if the EIP is not set.
*/
i = 0;
while (plic->targets.target[core_id].claim_complete > 0 && i < 100)
{
/* This loop will clear pending bit on the interrupt source */
i++;
}
/* Enable machine external interrupts. */
set_csr(mie, MIP_MEIP);
}
int plic_irq_enable(plic_irq_t irq_number)
{
/* Check parameters */
if (PLIC_NUM_SOURCES < irq_number || 0 > irq_number)
return -1;
unsigned long core_id = current_coreid();
/* Get current enable bit array by IRQ number */
uint32_t current = plic->target_enables.target[core_id].enable[irq_number / 32];
/* Set enable bit in enable bit array */
current |= (uint32_t)1 << (irq_number % 32);
/* Write back the enable bit array */
plic->target_enables.target[core_id].enable[irq_number / 32] = current;
return 0;
}
int plic_irq_disable(plic_irq_t irq_number)
{
/* Check parameters */
if (PLIC_NUM_SOURCES < irq_number || 0 > irq_number)
return -1;
unsigned long core_id = current_coreid();
/* Get current enable bit array by IRQ number */
uint32_t current = plic->target_enables.target[core_id].enable[irq_number / 32];
/* Clear enable bit in enable bit array */
current &= ~((uint32_t)1 << (irq_number % 32));
/* Write back the enable bit array */
plic->target_enables.target[core_id].enable[irq_number / 32] = current;
return 0;
}
int plic_set_priority(plic_irq_t irq_number, uint32_t priority)
{
/* Check parameters */
if (PLIC_NUM_SOURCES < irq_number || 0 > irq_number)
return -1;
/* Set interrupt priority by IRQ number */
plic->source_priorities.priority[irq_number] = priority;
return 0;
}
uint32_t plic_get_priority(plic_irq_t irq_number)
{
/* Check parameters */
if (PLIC_NUM_SOURCES < irq_number || 0 > irq_number)
return 0;
/* Get interrupt priority by IRQ number */
return plic->source_priorities.priority[irq_number];
}
uint32_t plic_irq_claim(void)
{
unsigned long core_id = current_coreid();
/* Perform IRQ claim */
return plic->targets.target[core_id].claim_complete;
}
int plic_irq_complete(uint32_t source)
{
unsigned long core_id = current_coreid();
/* Perform IRQ complete */
plic->targets.target[core_id].claim_complete = source;
return 0;
}
void plic_irq_register(plic_irq_t irq, plic_irq_callback_t callback, void *ctx)
{
/* Read core id */
unsigned long core_id = current_coreid();
/* Set user callback function */
plic_instance[core_id][irq].callback = callback;
/* Assign user context */
plic_instance[core_id][irq].ctx = ctx;
}
void plic_irq_unregister(plic_irq_t irq)
{
/* Just assign NULL to user callback function and context */
plic_irq_register(irq, NULL, NULL);
}
void __attribute__((weak, alias("plic_irq_unregister"))) plic_irq_deregister(plic_irq_t irq);
plic_instance_t (*plic_get_instance(void))[IRQN_MAX]
{
return plic_instance;
}
/*Entry Point for PLIC Interrupt Handler*/
uintptr_t __attribute__((weak))
handle_irq_m_ext(uintptr_t cause, uintptr_t epc)
{
/*
* After the highest-priority pending interrupt is claimed by a target
* and the corresponding IP bit is cleared, other lower-priority
* pending interrupts might then become visible to the target, and so
* the PLIC EIP bit might not be cleared after a claim. The interrupt
* handler can check the local meip/heip/seip/ueip bits before exiting
* the handler, to allow more efficient service of other interrupts
* without first restoring the interrupted context and taking another
* interrupt trap.
*/
if (read_csr(mip) & MIP_MEIP)
{
/* Get current core id */
uint64_t core_id = current_coreid();
/* Get primitive interrupt enable flag */
uint64_t ie_flag = read_csr(mie);
/* Get current IRQ num */
uint32_t int_num = plic->targets.target[core_id].claim_complete;
/* Get primitive IRQ threshold */
uint32_t int_threshold = plic->targets.target[core_id].priority_threshold;
/* Set new IRQ threshold = current IRQ threshold */
plic->targets.target[core_id].priority_threshold = plic->source_priorities.priority[int_num];
/* Disable software interrupt and timer interrupt */
clear_csr(mie, MIP_MTIP | MIP_MSIP);
/* Enable global interrupt */
set_csr(mstatus, MSTATUS_MIE);
if (plic_instance[core_id][int_num].callback)
plic_instance[core_id][int_num].callback(
plic_instance[core_id][int_num].ctx);
/* Perform IRQ complete */
plic->targets.target[core_id].claim_complete = int_num;
/* Disable global interrupt */
clear_csr(mstatus, MSTATUS_MIE);
/* Set MPIE and MPP flag used to MRET instructions restore MIE flag */
set_csr(mstatus, MSTATUS_MPIE | MSTATUS_MPP);
/* Restore primitive interrupt enable flag */
write_csr(mie, ie_flag);
/* Restore primitive IRQ threshold */
plic->targets.target[core_id].priority_threshold = int_threshold;
}
return epc;
}

View File

@ -0,0 +1,499 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* @brief The PLIC complies with the RISC-V Privileged Architecture
* specification, and can support a maximum of 1023 external
* interrupt sources targeting up to 15,872 core contexts.
*
* @note PLIC RAM Layout
*
* | Address | Description |
* |-----------|---------------------------------|
* |0x0C000000 | Reserved |
* |0x0C000004 | source 1 priority |
* |0x0C000008 | source 2 priority |
* |... | ... |
* |0x0C000FFC | source 1023 priority |
* | | |
* |0x0C001000 | Start of pending array |
* |... | (read-only) |
* |0x0C00107C | End of pending array |
* |0x0C001080 | Reserved |
* |... | ... |
* |0x0C001FFF | Reserved |
* | | |
* |0x0C002000 | target 0 enables |
* |0x0C002080 | target 1 enables |
* |... | ... |
* |0x0C1F1F80 | target 15871 enables |
* |0x0C1F2000 | Reserved |
* |... | ... |
* |0x0C1FFFFC | Reserved |
* | | |
* |0x0C200000 | target 0 priority threshold |
* |0x0C200004 | target 0 claim/complete |
* |... | ... |
* |0x0C201000 | target 1 priority threshold |
* |0x0C201004 | target 1 claim/complete |
* |... | ... |
* |0x0FFFF000 | target 15871 priority threshold |
* |0x0FFFF004 | target 15871 claim/complete |
*
*/
#ifndef _DRIVER_PLIC_H
#define _DRIVER_PLIC_H
#include <stdint.h>
#include "encoding.h"
#include "platform.h"
/* For c++ compatibility */
#ifdef __cplusplus
extern "C" {
#endif
/* clang-format off */
/* IRQ number settings */
#define PLIC_NUM_SOURCES (IRQN_MAX - 1)
#define PLIC_NUM_PRIORITIES (7)
/* Real number of cores */
#define PLIC_NUM_CORES (2)
/* clang-format on */
/**
* @brief PLIC External Interrupt Numbers
*
* @note PLIC interrupt sources
*
* | Source | Name | Description |
* |--------|--------------------------|------------------------------------|
* | 0 | IRQN_NO_INTERRUPT | The non-existent interrupt |
* | 1 | IRQN_SPI0_INTERRUPT | SPI0 interrupt |
* | 2 | IRQN_SPI1_INTERRUPT | SPI1 interrupt |
* | 3 | IRQN_SPI_SLAVE_INTERRUPT | SPI_SLAVE interrupt |
* | 4 | IRQN_SPI3_INTERRUPT | SPI3 interrupt |
* | 5 | IRQN_I2S0_INTERRUPT | I2S0 interrupt |
* | 6 | IRQN_I2S1_INTERRUPT | I2S1 interrupt |
* | 7 | IRQN_I2S2_INTERRUPT | I2S2 interrupt |
* | 8 | IRQN_I2C0_INTERRUPT | I2C0 interrupt |
* | 9 | IRQN_I2C1_INTERRUPT | I2C1 interrupt |
* | 10 | IRQN_I2C2_INTERRUPT | I2C2 interrupt |
* | 11 | IRQN_UART1_INTERRUPT | UART1 interrupt |
* | 12 | IRQN_UART2_INTERRUPT | UART2 interrupt |
* | 13 | IRQN_UART3_INTERRUPT | UART3 interrupt |
* | 14 | IRQN_TIMER0A_INTERRUPT | TIMER0 channel 0 or 1 interrupt |
* | 15 | IRQN_TIMER0B_INTERRUPT | TIMER0 channel 2 or 3 interrupt |
* | 16 | IRQN_TIMER1A_INTERRUPT | TIMER1 channel 0 or 1 interrupt |
* | 17 | IRQN_TIMER1B_INTERRUPT | TIMER1 channel 2 or 3 interrupt |
* | 18 | IRQN_TIMER2A_INTERRUPT | TIMER2 channel 0 or 1 interrupt |
* | 19 | IRQN_TIMER2B_INTERRUPT | TIMER2 channel 2 or 3 interrupt |
* | 20 | IRQN_RTC_INTERRUPT | RTC tick and alarm interrupt |
* | 21 | IRQN_WDT0_INTERRUPT | Watching dog timer0 interrupt |
* | 22 | IRQN_WDT1_INTERRUPT | Watching dog timer1 interrupt |
* | 23 | IRQN_APB_GPIO_INTERRUPT | APB GPIO interrupt |
* | 24 | IRQN_DVP_INTERRUPT | Digital video port interrupt |
* | 25 | IRQN_AI_INTERRUPT | AI accelerator interrupt |
* | 26 | IRQN_FFT_INTERRUPT | FFT accelerator interrupt |
* | 27 | IRQN_DMA0_INTERRUPT | DMA channel0 interrupt |
* | 28 | IRQN_DMA1_INTERRUPT | DMA channel1 interrupt |
* | 29 | IRQN_DMA2_INTERRUPT | DMA channel2 interrupt |
* | 30 | IRQN_DMA3_INTERRUPT | DMA channel3 interrupt |
* | 31 | IRQN_DMA4_INTERRUPT | DMA channel4 interrupt |
* | 32 | IRQN_DMA5_INTERRUPT | DMA channel5 interrupt |
* | 33 | IRQN_UARTHS_INTERRUPT | Hi-speed UART0 interrupt |
* | 34 | IRQN_GPIOHS0_INTERRUPT | Hi-speed GPIO0 interrupt |
* | 35 | IRQN_GPIOHS1_INTERRUPT | Hi-speed GPIO1 interrupt |
* | 36 | IRQN_GPIOHS2_INTERRUPT | Hi-speed GPIO2 interrupt |
* | 37 | IRQN_GPIOHS3_INTERRUPT | Hi-speed GPIO3 interrupt |
* | 38 | IRQN_GPIOHS4_INTERRUPT | Hi-speed GPIO4 interrupt |
* | 39 | IRQN_GPIOHS5_INTERRUPT | Hi-speed GPIO5 interrupt |
* | 40 | IRQN_GPIOHS6_INTERRUPT | Hi-speed GPIO6 interrupt |
* | 41 | IRQN_GPIOHS7_INTERRUPT | Hi-speed GPIO7 interrupt |
* | 42 | IRQN_GPIOHS8_INTERRUPT | Hi-speed GPIO8 interrupt |
* | 43 | IRQN_GPIOHS9_INTERRUPT | Hi-speed GPIO9 interrupt |
* | 44 | IRQN_GPIOHS10_INTERRUPT | Hi-speed GPIO10 interrupt |
* | 45 | IRQN_GPIOHS11_INTERRUPT | Hi-speed GPIO11 interrupt |
* | 46 | IRQN_GPIOHS12_INTERRUPT | Hi-speed GPIO12 interrupt |
* | 47 | IRQN_GPIOHS13_INTERRUPT | Hi-speed GPIO13 interrupt |
* | 48 | IRQN_GPIOHS14_INTERRUPT | Hi-speed GPIO14 interrupt |
* | 49 | IRQN_GPIOHS15_INTERRUPT | Hi-speed GPIO15 interrupt |
* | 50 | IRQN_GPIOHS16_INTERRUPT | Hi-speed GPIO16 interrupt |
* | 51 | IRQN_GPIOHS17_INTERRUPT | Hi-speed GPIO17 interrupt |
* | 52 | IRQN_GPIOHS18_INTERRUPT | Hi-speed GPIO18 interrupt |
* | 53 | IRQN_GPIOHS19_INTERRUPT | Hi-speed GPIO19 interrupt |
* | 54 | IRQN_GPIOHS20_INTERRUPT | Hi-speed GPIO20 interrupt |
* | 55 | IRQN_GPIOHS21_INTERRUPT | Hi-speed GPIO21 interrupt |
* | 56 | IRQN_GPIOHS22_INTERRUPT | Hi-speed GPIO22 interrupt |
* | 57 | IRQN_GPIOHS23_INTERRUPT | Hi-speed GPIO23 interrupt |
* | 58 | IRQN_GPIOHS24_INTERRUPT | Hi-speed GPIO24 interrupt |
* | 59 | IRQN_GPIOHS25_INTERRUPT | Hi-speed GPIO25 interrupt |
* | 60 | IRQN_GPIOHS26_INTERRUPT | Hi-speed GPIO26 interrupt |
* | 61 | IRQN_GPIOHS27_INTERRUPT | Hi-speed GPIO27 interrupt |
* | 62 | IRQN_GPIOHS28_INTERRUPT | Hi-speed GPIO28 interrupt |
* | 63 | IRQN_GPIOHS29_INTERRUPT | Hi-speed GPIO29 interrupt |
* | 64 | IRQN_GPIOHS30_INTERRUPT | Hi-speed GPIO30 interrupt |
* | 65 | IRQN_GPIOHS31_INTERRUPT | Hi-speed GPIO31 interrupt |
*
*/
/* clang-format off */
typedef enum _plic_irq
{
IRQN_NO_INTERRUPT = 0, /*!< The non-existent interrupt */
IRQN_SPI0_INTERRUPT = 1, /*!< SPI0 interrupt */
IRQN_SPI1_INTERRUPT = 2, /*!< SPI1 interrupt */
IRQN_SPI_SLAVE_INTERRUPT = 3, /*!< SPI_SLAVE interrupt */
IRQN_SPI3_INTERRUPT = 4, /*!< SPI3 interrupt */
IRQN_I2S0_INTERRUPT = 5, /*!< I2S0 interrupt */
IRQN_I2S1_INTERRUPT = 6, /*!< I2S1 interrupt */
IRQN_I2S2_INTERRUPT = 7, /*!< I2S2 interrupt */
IRQN_I2C0_INTERRUPT = 8, /*!< I2C0 interrupt */
IRQN_I2C1_INTERRUPT = 9, /*!< I2C1 interrupt */
IRQN_I2C2_INTERRUPT = 10, /*!< I2C2 interrupt */
IRQN_UART1_INTERRUPT = 11, /*!< UART1 interrupt */
IRQN_UART2_INTERRUPT = 12, /*!< UART2 interrupt */
IRQN_UART3_INTERRUPT = 13, /*!< UART3 interrupt */
IRQN_TIMER0A_INTERRUPT = 14, /*!< TIMER0 channel 0 or 1 interrupt */
IRQN_TIMER0B_INTERRUPT = 15, /*!< TIMER0 channel 2 or 3 interrupt */
IRQN_TIMER1A_INTERRUPT = 16, /*!< TIMER1 channel 0 or 1 interrupt */
IRQN_TIMER1B_INTERRUPT = 17, /*!< TIMER1 channel 2 or 3 interrupt */
IRQN_TIMER2A_INTERRUPT = 18, /*!< TIMER2 channel 0 or 1 interrupt */
IRQN_TIMER2B_INTERRUPT = 19, /*!< TIMER2 channel 2 or 3 interrupt */
IRQN_RTC_INTERRUPT = 20, /*!< RTC tick and alarm interrupt */
IRQN_WDT0_INTERRUPT = 21, /*!< Watching dog timer0 interrupt */
IRQN_WDT1_INTERRUPT = 22, /*!< Watching dog timer1 interrupt */
IRQN_APB_GPIO_INTERRUPT = 23, /*!< APB GPIO interrupt */
IRQN_DVP_INTERRUPT = 24, /*!< Digital video port interrupt */
IRQN_AI_INTERRUPT = 25, /*!< AI accelerator interrupt */
IRQN_FFT_INTERRUPT = 26, /*!< FFT accelerator interrupt */
IRQN_DMA0_INTERRUPT = 27, /*!< DMA channel0 interrupt */
IRQN_DMA1_INTERRUPT = 28, /*!< DMA channel1 interrupt */
IRQN_DMA2_INTERRUPT = 29, /*!< DMA channel2 interrupt */
IRQN_DMA3_INTERRUPT = 30, /*!< DMA channel3 interrupt */
IRQN_DMA4_INTERRUPT = 31, /*!< DMA channel4 interrupt */
IRQN_DMA5_INTERRUPT = 32, /*!< DMA channel5 interrupt */
IRQN_UARTHS_INTERRUPT = 33, /*!< Hi-speed UART0 interrupt */
IRQN_GPIOHS0_INTERRUPT = 34, /*!< Hi-speed GPIO0 interrupt */
IRQN_GPIOHS1_INTERRUPT = 35, /*!< Hi-speed GPIO1 interrupt */
IRQN_GPIOHS2_INTERRUPT = 36, /*!< Hi-speed GPIO2 interrupt */
IRQN_GPIOHS3_INTERRUPT = 37, /*!< Hi-speed GPIO3 interrupt */
IRQN_GPIOHS4_INTERRUPT = 38, /*!< Hi-speed GPIO4 interrupt */
IRQN_GPIOHS5_INTERRUPT = 39, /*!< Hi-speed GPIO5 interrupt */
IRQN_GPIOHS6_INTERRUPT = 40, /*!< Hi-speed GPIO6 interrupt */
IRQN_GPIOHS7_INTERRUPT = 41, /*!< Hi-speed GPIO7 interrupt */
IRQN_GPIOHS8_INTERRUPT = 42, /*!< Hi-speed GPIO8 interrupt */
IRQN_GPIOHS9_INTERRUPT = 43, /*!< Hi-speed GPIO9 interrupt */
IRQN_GPIOHS10_INTERRUPT = 44, /*!< Hi-speed GPIO10 interrupt */
IRQN_GPIOHS11_INTERRUPT = 45, /*!< Hi-speed GPIO11 interrupt */
IRQN_GPIOHS12_INTERRUPT = 46, /*!< Hi-speed GPIO12 interrupt */
IRQN_GPIOHS13_INTERRUPT = 47, /*!< Hi-speed GPIO13 interrupt */
IRQN_GPIOHS14_INTERRUPT = 48, /*!< Hi-speed GPIO14 interrupt */
IRQN_GPIOHS15_INTERRUPT = 49, /*!< Hi-speed GPIO15 interrupt */
IRQN_GPIOHS16_INTERRUPT = 50, /*!< Hi-speed GPIO16 interrupt */
IRQN_GPIOHS17_INTERRUPT = 51, /*!< Hi-speed GPIO17 interrupt */
IRQN_GPIOHS18_INTERRUPT = 52, /*!< Hi-speed GPIO18 interrupt */
IRQN_GPIOHS19_INTERRUPT = 53, /*!< Hi-speed GPIO19 interrupt */
IRQN_GPIOHS20_INTERRUPT = 54, /*!< Hi-speed GPIO20 interrupt */
IRQN_GPIOHS21_INTERRUPT = 55, /*!< Hi-speed GPIO21 interrupt */
IRQN_GPIOHS22_INTERRUPT = 56, /*!< Hi-speed GPIO22 interrupt */
IRQN_GPIOHS23_INTERRUPT = 57, /*!< Hi-speed GPIO23 interrupt */
IRQN_GPIOHS24_INTERRUPT = 58, /*!< Hi-speed GPIO24 interrupt */
IRQN_GPIOHS25_INTERRUPT = 59, /*!< Hi-speed GPIO25 interrupt */
IRQN_GPIOHS26_INTERRUPT = 60, /*!< Hi-speed GPIO26 interrupt */
IRQN_GPIOHS27_INTERRUPT = 61, /*!< Hi-speed GPIO27 interrupt */
IRQN_GPIOHS28_INTERRUPT = 62, /*!< Hi-speed GPIO28 interrupt */
IRQN_GPIOHS29_INTERRUPT = 63, /*!< Hi-speed GPIO29 interrupt */
IRQN_GPIOHS30_INTERRUPT = 64, /*!< Hi-speed GPIO30 interrupt */
IRQN_GPIOHS31_INTERRUPT = 65, /*!< Hi-speed GPIO31 interrupt */
IRQN_MAX
} plic_irq_t;
/* clang-format on */
/**
* @brief Interrupt Source Priorities
*
* Each external interrupt source can be assigned a priority by
* writing to its 32-bit memory-mapped priority register. The
* number and value of supported priority levels can vary by
* implementa- tion, with the simplest implementations having all
* devices hardwired at priority 1, in which case, interrupts with
* the lowest ID have the highest effective priority. The priority
* registers are all WARL.
*/
typedef struct _plic_source_priorities
{
/* 0x0C000000: Reserved, 0x0C000004-0x0C000FFC: 1-1023 priorities */
uint32_t priority[1024];
} __attribute__((packed, aligned(4))) plic_source_priorities_t;
/**
* @brief Interrupt Pending Bits
*
* The current status of the interrupt source pending bits in the
* PLIC core can be read from the pending array, organized as 32
* words of 32 bits. The pending bit for interrupt ID N is stored
* in bit (N mod 32) of word (N/32). Bit 0 of word 0, which
* represents the non-existent interrupt source 0, is always
* hardwired to zero. The pending bits are read-only. A pending
* bit in the PLIC core can be cleared by setting enable bits to
* only enable the desired interrupt, then performing a claim. A
* pending bit can be set by instructing the associated gateway to
* send an interrupt service request.
*/
typedef struct _plic_pending_bits
{
/* 0x0C001000-0x0C00107C: Bit 0 is zero, Bits 1-1023 is pending bits */
uint32_t u32[32];
/* 0x0C001080-0x0C001FFF: Reserved */
uint8_t resv[0xF80];
} __attribute__((packed, aligned(4))) plic_pending_bits_t;
/**
* @brief Target Interrupt Enables
*
* For each interrupt target, each devices interrupt can be
* enabled by setting the corresponding bit in that targets
* enables registers. The enables for a target are accessed as a
* contiguous array of 32×32-bit words, packed the same way as the
* pending bits. For each target, bit 0 of enable word 0
* represents the non-existent interrupt ID 0 and is hardwired to
* 0. Unused interrupt IDs are also hardwired to zero. The enables
* arrays for different targets are packed contiguously in the
* address space. Only 32-bit word accesses are supported by the
* enables array in RV32 systems. Implementations can trap on
* accesses to enables for non-existent targets, but must allow
* access to the full enables array for any extant target,
* treating all non-existent interrupt sources enables as
* hardwired to zero.
*/
typedef struct _plic_target_enables
{
/* 0x0C002000-0x0C1F1F80: target 0-15871 enables */
struct
{
uint32_t enable[32 * 2];/* Offset 0x00-0x7C: Bit 0 is zero, Bits 1-1023 is bits*/
} target[15872 / 2];
/* 0x0C1F2000-0x0C1FFFFC: Reserved, size 0xE000 */
uint8_t resv[0xE000];
} __attribute__((packed, aligned(4))) plic_target_enables_t;
/**
* @brief PLIC Targets
*
* Target Priority Thresholds The threshold for a pending
* interrupt priority that can interrupt each target can be set in
* the targets threshold register. The threshold is a WARL field,
* where different implementations can support different numbers
* of thresholds. The simplest implementation has a threshold
* hardwired to zero.
*
* Target Claim Each target can perform a claim by reading the
* claim/complete register, which returns the ID of the highest
* priority pending interrupt or zero if there is no pending
* interrupt for the target. A successful claim will also
* atomically clear the corresponding pending bit on the interrupt
* source. A target can perform a claim at any time, even if the
* EIP is not set. The claim operation is not affected by the
* setting of the targets priority threshold register.
*
* Target Completion A target signals it has completed running a
* handler by writing the interrupt ID it received from the claim
* to the claim/complete register. This is routed to the
* corresponding interrupt gateway, which can now send another
* interrupt request to the PLIC. The PLIC does not check whether
* the completion ID is the same as the last claim ID for that
* target. If the completion ID does not match an interrupt source
* that is currently enabled for the target, the completion is
* silently ignored.
*/
typedef struct _plic_target
{
/* 0x0C200000-0x0FFFF004: target 0-15871 */
struct {
uint32_t priority_threshold;/* Offset 0x000 */
uint32_t claim_complete; /* Offset 0x004 */
uint8_t resv[0x1FF8]; /* Offset 0x008, Size 0xFF8 */
} target[15872 / 2];
} __attribute__((packed, aligned(4))) plic_target_t;
/**
* @brief Platform-Level Interrupt Controller
*
* PLIC is Platform-Level Interrupt Controller. The PLIC complies
* with the RISC-V Privileged Architecture specification, and can
* support a maximum of 1023 external interrupt sources targeting
* up to 15,872 core contexts.
*/
typedef struct _plic
{
/* 0x0C000000-0x0C000FFC */
plic_source_priorities_t source_priorities;
/* 0x0C001000-0x0C001FFF */
const plic_pending_bits_t pending_bits;
/* 0x0C002000-0x0C1FFFFC */
plic_target_enables_t target_enables;
/* 0x0C200000-0x0FFFF004 */
plic_target_t targets;
} __attribute__((packed, aligned(4))) plic_t;
extern volatile plic_t *const plic;
/**
* @brief Definitions for the interrupt callbacks
*/
typedef int (*plic_irq_callback_t)(void *ctx);
/**
* @brief Definitions for IRQ table instance
*/
typedef struct _plic_instance_t
{
plic_irq_callback_t callback;
void *ctx;
} plic_instance_t;
typedef struct _plic_callback_t
{
plic_irq_callback_t callback;
void *ctx;
uint32_t priority;
} plic_interrupt_t;
/**
* @brief Initialize PLIC external interrupt
*
* @note This function will set MIP_MEIP. The MSTATUS_MIE must set by user.
*
* @return result
* - 0 Success
* - Other Fail
*/
void plic_init(void);
/**
* @brief Enable PLIC external interrupt
*
* @param[in] irq_number external interrupt number
*
* @return result
* - 0 Success
* - Other Fail
*/
int plic_irq_enable(plic_irq_t irq_number);
/**
* @brief Disable PLIC external interrupt
*
* @param[in] irq_number The external interrupt number
*
* @return result
* - 0 Success
* - Other Fail
*/
int plic_irq_disable(plic_irq_t irq_number);
/**
* @brief Set IRQ priority
*
* @param[in] irq_number The external interrupt number
* @param[in] priority The priority of external interrupt number
*
* @return result
* - 0 Success
* - Other Fail
*/
int plic_set_priority(plic_irq_t irq_number, uint32_t priority);
/**
* @brief Get IRQ priority
*
* @param[in] irq_number The external interrupt number
*
* @return The priority of external interrupt number
*/
uint32_t plic_get_priority(plic_irq_t irq_number);
/**
* @brief Claim an IRQ
*
* @return The current IRQ number
*/
uint32_t plic_irq_claim(void);
/**
* @brief Complete an IRQ
*
* @param[in] source The source IRQ number to complete
*
* @return result
* - 0 Success
* - Other Fail
*/
int plic_irq_complete(uint32_t source);
/**
* @brief Register user callback function by IRQ number
*
* @param[in] irq The irq
* @param[in] callback The callback
* @param ctx The context
*
* @return result
* - 0 Success
* - Other Fail
*/
void plic_irq_register(plic_irq_t irq, plic_irq_callback_t callback, void *ctx);
/**
* @brief Deegister user callback function by IRQ number
*
* @param[in] irq The irq
*
* @return result
* - 0 Success
* - Other Fail
*/
void plic_irq_deregister(plic_irq_t irq);
/**
* @brief Deegister user callback function by IRQ number
*
* @param[in] irq The irq
*
* @return result
* - 0 Success
* - Other Fail
*/
void plic_irq_unregister(plic_irq_t irq);
/**
* @brief Get IRQ table, Usage:
* plic_instance_t (*plic_instance)[IRQN_MAX] = plic_get_instance();
* ... plic_instance[x][y] ...;
*
* @return the point of IRQ table
*/
plic_instance_t (*plic_get_instance(void))[IRQN_MAX];
/* For c++ compatibility */
#ifdef __cplusplus
}
#endif
#endif /* _DRIVER_PLIC_H */

View File

@ -0,0 +1,381 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* Enable kernel-mode log API */
#include <nuttx/config.h>
#include <assert.h>
#include <nuttx/irq.h>
#include <nuttx/arch.h>
#include <nuttx/board.h>
#include <arch/board/board.h>
#include "riscv_arch.h"
#include "riscv_internal.h"
#include "group/group.h"
#include <sys/stat.h>
#include <sys/time.h>
#include <machine/syscall.h>
#include <stdbool.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <debug.h>
#include "syscalls.h"
#include "clint.h"
#include "interrupt.h"
#include "sysctl.h"
#include "syslog.h"
#include "encoding.h"
/**
* @note System call list
*
* See also riscv-newlib/libgloss/riscv/syscalls.c
*
* | System call | Number |
* |------------------|--------|
* | SYS_exit | 93 |
* | SYS_exit_group | 94 |
* | SYS_getpid | 172 |
* | SYS_kill | 129 |
* | SYS_read | 63 |
* | SYS_write | 64 |
* | SYS_open | 1024 |
* | SYS_openat | 56 |
* | SYS_close | 57 |
* | SYS_lseek | 62 |
* | SYS_brk | 214 |
* | SYS_link | 1025 |
* | SYS_unlink | 1026 |
* | SYS_mkdir | 1030 |
* | SYS_chdir | 49 |
* | SYS_getcwd | 17 |
* | SYS_stat | 1038 |
* | SYS_fstat | 80 |
* | SYS_lstat | 1039 |
* | SYS_fstatat | 79 |
* | SYS_access | 1033 |
* | SYS_faccessat | 48 |
* | SYS_pread | 67 |
* | SYS_pwrite | 68 |
* | SYS_uname | 160 |
* | SYS_getuid | 174 |
* | SYS_geteuid | 175 |
* | SYS_getgid | 176 |
* | SYS_getegid | 177 |
* | SYS_mmap | 222 |
* | SYS_munmap | 215 |
* | SYS_mremap | 216 |
* | SYS_time | 1062 |
* | SYS_getmainvars | 2011 |
* | SYS_rt_sigaction | 134 |
* | SYS_writev | 66 |
* | SYS_gettimeofday | 169 |
* | SYS_times | 153 |
* | SYS_fcntl | 25 |
* | SYS_getdents | 61 |
* | SYS_dup | 23 |
*
*/
#ifndef UNUSED
#define UNUSED(x) (void)(x)
#endif
void __attribute__((noreturn)) sys_exit(int code)
{
/* Read core id */
unsigned long core_id = current_coreid();
/* First print some diagnostic information. */
syslog(LOG_WARNING, "SYSCALL sys_exit called by core %ld with 0x%lx\n", core_id, (uint64_t)code);
while (1)
continue;
}
static int sys_nosys(long a0, long a1, long a2, long a3, long a4, long a5, unsigned long n)
{
UNUSED(a3);
UNUSED(a4);
UNUSED(a5);
syslog(LOG_ERR, "SYSCALL Unsupported syscall %ld: a0=%lx, a1=%lx, a2=%lx!\n", n, a0, a1, a2);
while (1)
continue;
return -ENOSYS;
}
static int sys_success(void)
{
return 0;
}
uintptr_t __attribute__((weak))
handle_ecall_u(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
return epc;
}
uintptr_t __attribute__((weak))
handle_ecall_h(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
return epc;
}
uintptr_t __attribute__((weak))
handle_ecall_s(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
return epc;
}
uintptr_t __attribute__((weak))
handle_ecall_m(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
uint64_t* ptr = (uint64_t*)regs;
CURRENT_REGS = (uint64_t*)regs;
ptr[0] = epc+4;
ptr[2] = ptr[2] + 0x200 ;
irq_dispatch(K210_IRQ_MSOFT, ptr);
void* tmp = g_current_regs;
epc = ((uint64_t*)tmp)[0];
CURRENT_REGS = NULL;
return epc;
}
uintptr_t __attribute__((weak))
handle_misaligned_fetch(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
//dump_core("misaligned fetch", cause, epc, regs, fregs);
sys_exit(1337);
return epc;
}
uintptr_t __attribute__((weak))
handle_fault_fetch(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
//dump_core("fault fetch", cause, epc, regs, fregs);
sys_exit(1337);
return epc;
}
uintptr_t __attribute__((weak))
handle_illegal_instruction(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
//dump_core("illegal instruction", cause, epc, regs, fregs);
sys_exit(1337);
return epc;
}
uintptr_t __attribute__((weak))
handle_breakpoint(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
//dump_core("breakpoint", cause, epc, regs, fregs);
sys_exit(1337);
return epc;
}
uintptr_t __attribute__((weak))
handle_misaligned_load(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
/* notice this function only support 16bit or 32bit instruction */
bool compressed = (*(unsigned short *)epc & 3) != 3;
bool fpu = 0; /* load to fpu ? */
uintptr_t addr = 0; /* src addr */
uint8_t src = 0; /* src register */
uint8_t dst = 0; /* dst register */
uint8_t len = 0; /* data length */
int offset = 0; /* addr offset to addr in reg */
bool unsigned_ = 0; /* unsigned */
uint64_t data_load = 0;/* real data load */
if (compressed)
{
/* compressed instruction should not get this fault. */
goto on_error;
}
else
{
uint32_t instruct = *(uint32_t *)epc;
uint8_t opcode = instruct&0x7F;
dst = (instruct >> 7)&0x1F;
len = (instruct >> 12)&3;
unsigned_ = (instruct >> 14)&1;
src = (instruct >> 15)&0x1F;
offset = (instruct >> 20);
len = 1 << len;
switch (opcode)
{
case 3:/* load */
break;
case 7:/* fpu load */
fpu = 1;
break;
default:
goto on_error;
}
}
if (offset >> 11)
offset = -((offset & 0x3FF) + 1);
addr = (uint64_t)((uint64_t)regs[src] + offset);
for (int i = 0; i < len; ++i)
data_load |= ((uint64_t)*((uint8_t *)addr + i)) << (8 * i);
if (!unsigned_ & !fpu)
{
/* adjust sign */
switch (len)
{
case 1:
data_load = (uint64_t)(int64_t)((int8_t)data_load);
break;
case 2:
data_load = (uint64_t)(int64_t)((int16_t)data_load);
break;
case 4:
data_load = (uint64_t)(int64_t)((int32_t)data_load);
break;
default:
break;
}
}
if (fpu)
fregs[dst] = data_load;
else
regs[dst] = data_load;
syslog(LOG_DEBUG, "misaligned load recovered at %08lx. len:%02d,addr:%08lx,reg:%02d,data:%016lx,signed:%1d,float:%1d", (uint64_t)epc, len, (uint64_t)addr, dst, data_load, !unsigned_, fpu);
return epc + (compressed ? 2 : 4);
on_error:
//dump_core("misaligned load", cause, epc, regs, fregs);
sys_exit(1337);
return epc;
}
uintptr_t __attribute__((weak))
handle_fault_load(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
//dump_core("fault load", cause, epc, regs, fregs);
sys_exit(1337);
return epc;
}
uintptr_t __attribute__((weak))
handle_misaligned_store(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
/* notice this function only support 16bit or 32bit instruction */
bool compressed = (*(unsigned short *)epc & 3) != 3;
bool fpu = 0; /* store to fpu*/
uintptr_t addr = 0; /* src addr*/
uint8_t src = 0; /* src register*/
uint8_t dst = 0; /* dst register*/
uint8_t len = 0; /* data length*/
int offset = 0; /* addr offset to addr in reg*/
uint64_t data_store = 0;/* real data store*/
if (compressed)
{
/* compressed instruction should not get this fault. */
goto on_error;
}
else
{
uint32_t instruct = *(uint32_t *)epc;
uint8_t opcode = instruct&0x7F;
len = (instruct >> 12)&7;
dst = (instruct >> 15)&0x1F;
src = (instruct >> 20)&0x1F;
offset = ((instruct >> 7)&0x1F) | ((instruct >> 20)&0xFE0);
len = 1 << len;
switch (opcode)
{
case 0x23:/* store */
break;
case 0x27:/* fpu store */
fpu = 1;
break;
default:
goto on_error;
}
}
if (offset >> 11)
offset = -((offset & 0x3FF) + 1);
addr = (uint64_t)((uint64_t)regs[dst] + offset);
if (fpu)
data_store = fregs[src];
else
data_store = regs[src];
for (int i = 0; i < len; ++i)
*((uint8_t *)addr + i) = (data_store >> (i*8)) & 0xFF;
syslog(LOG_DEBUG, "misaligned store recovered at %08lx. len:%02d,addr:%08lx,reg:%02d,data:%016lx,float:%1d", (uint64_t)epc, len, (uint64_t)addr, src, data_store, fpu);
return epc + (compressed ? 2 : 4);
on_error:
//dump_core("misaligned store", cause, epc, regs, fregs);
sys_exit(1337);
return epc;
}
uintptr_t __attribute__((weak))
handle_fault_store(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
//dump_core("fault store", cause, epc, regs, fregs);
sys_exit(1337);
return epc;
}
uintptr_t handle_syscall(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32])
{
static uintptr_t (* const cause_table[])(uintptr_t cause, uintptr_t epc, uintptr_t regs[32], uintptr_t fregs[32]) =
{
[CAUSE_MISALIGNED_FETCH] = handle_misaligned_fetch,
[CAUSE_FAULT_FETCH] = handle_fault_fetch,
[CAUSE_ILLEGAL_INSTRUCTION] = handle_illegal_instruction,
[CAUSE_BREAKPOINT] = handle_breakpoint,
[CAUSE_MISALIGNED_LOAD] = handle_misaligned_load,
[CAUSE_FAULT_LOAD] = handle_fault_load,
[CAUSE_MISALIGNED_STORE] = handle_misaligned_store,
[CAUSE_FAULT_STORE] = handle_fault_store,
[CAUSE_USER_ECALL] = handle_ecall_u,
[CAUSE_SUPERVISOR_ECALL] = handle_ecall_h,
[CAUSE_HYPERVISOR_ECALL] = handle_ecall_s,
[CAUSE_MACHINE_ECALL] = handle_ecall_m,
};
return cause_table[cause](cause, epc, regs, fregs);
}

View File

@ -0,0 +1,44 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _BSP_SYSCALLS_H
#define _BSP_SYSCALLS_H
#include <machine/syscall.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
void __attribute__((noreturn)) sys_exit(int code);
void setStats(int enable);
#undef putchar
int putchar(int ch);
void printstr(const char *s);
void printhex(uint64_t x);
#ifdef __cplusplus
}
#endif
#endif /* _BSP_SYSCALLS_H */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,182 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdint.h>
#include <stdio.h>
#include "uarths.h"
#include "sysctl.h"
#include "encoding.h"
#include <nuttx/arch.h>
#include <arch/irq.h>
volatile uarths_t *const uarths = (volatile uarths_t *)UARTHS_BASE_ADDR;
typedef struct _uarths_instance
{
plic_irq_callback_t callback;
void *ctx;
uarths_interrupt_mode_t uarths_interrupt_mode;
} uarths_instance_t;
uarths_instance_t g_uarths_instance;
uarths_interrupt_mode_t uarths_get_interrupt_mode(void)
{
uint32_t v_rx_interrupt = uarths->ip.rxwm;
uint32_t v_tx_interrupt = uarths->ip.txwm;
return (v_rx_interrupt << 1) | v_tx_interrupt;
}
int uarths_irq_callback(int irq, void *ctx, void *arg)
{
uarths_instance_t *uart_context = (uarths_instance_t *)ctx;
if(uart_context->callback)
uart_context->callback(uart_context->ctx);
return 0;
}
void uarths_set_interrupt_cnt(uarths_interrupt_mode_t interrupt_mode, uint8_t cnt)
{
switch(interrupt_mode)
{
case UARTHS_SEND:
uarths->txctrl.txcnt = cnt;
break;
case UARTHS_RECEIVE:
uarths->rxctrl.rxcnt = cnt;
break;
case UARTHS_SEND_RECEIVE:
default:
uarths->txctrl.txcnt = cnt;
uarths->rxctrl.rxcnt = cnt;
break;
}
}
void uarths_set_irq(uarths_interrupt_mode_t interrupt_mode, plic_irq_callback_t uarths_callback, void *ctx, uint32_t priority)
{
int ret;
g_uarths_instance.callback = uarths_callback;
g_uarths_instance.ctx = ctx;
switch(interrupt_mode)
{
case UARTHS_SEND:
uarths->ie.txwm = 1;
uarths->ie.rxwm = 0;
break;
case UARTHS_RECEIVE:
uarths->ie.txwm = 0;
uarths->ie.rxwm = 1;
break;
default:
uarths->ie.txwm = 1;
uarths->ie.rxwm = 1;
break;
}
g_uarths_instance.uarths_interrupt_mode = interrupt_mode;
plic_set_priority(IRQN_UARTHS_INTERRUPT, priority);
ret = irq_attach(IRQN_UARTHS_INTERRUPT, uarths_irq_callback, &g_uarths_instance);
if (ret == OK)
{
up_enable_irq(IRQN_UARTHS_INTERRUPT);
}
}
static inline int uarths_putc(char c)
{
while (uarths->txdata.full)
continue;
uarths->txdata.data = (uint8_t)c;
return 0;
}
size_t uarths_receive_data(uint8_t *buf, size_t buf_len)
{
size_t i;
for(i = 0; i < buf_len; i++)
{
uarths_rxdata_t recv = uarths->rxdata;
if(recv.empty)
break;
else
buf[i] = (recv.data & 0xFF);
}
return i;
}
size_t uarths_send_data(const uint8_t *buf, size_t buf_len)
{
size_t write = 0;
while (write < buf_len)
{
uarths_putc(*buf++);
write++;
}
return write;
}
int uarths_getc(void)
{
/* while not empty */
uarths_rxdata_t recv = uarths->rxdata;
if (recv.empty)
return EOF;
else
return recv.data;
}
int uarths_putchar(char c)
{
return uarths_putc(c);
}
int uarths_puts(const char *s)
{
while (*s)
if (uarths_putc(*s++) != 0)
return -1;
return 0;
}
void uarths_init(void)
{
uint32_t freq = sysctl_clock_get_freq(SYSCTL_CLOCK_CPU);
uint16_t div = freq / 115200 - 1;
/* Set UART registers */
uarths->div.div = div;
uarths->txctrl.txen = 1;
uarths->rxctrl.rxen = 1;
uarths->txctrl.txcnt = 0;
uarths->rxctrl.rxcnt = 0;
uarths->ip.txwm = 1;
uarths->ip.rxwm = 1;
uarths->ie.txwm = 0;
uarths->ie.rxwm = 1;
}
void uarths_config(uint32_t baud_rate, uarths_stopbit_t stopbit)
{
uint32_t freq = sysctl_clock_get_freq(SYSCTL_CLOCK_CPU);
uint16_t div = freq / baud_rate - 1;
uarths->div.div = div;
uarths->txctrl.nstop = stopbit;
}

View File

@ -0,0 +1,293 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* @brief Universal Asynchronous Receiver/Transmitter (UART)
*
* The UART peripheral supports the following features:
*
* - 8-N-1 and 8-N-2 formats: 8 data bits, no parity bit, 1 start
* bit, 1 or 2 stop bits
*
* - 8-entry transmit and receive FIFO buffers with programmable
* watermark interrupts
*
* - 16× Rx oversampling with 2/3 majority voting per bit
*
* The UART peripheral does not support hardware flow control or
* other modem control signals, or synchronous serial data
* tranfesrs.
*
* @note UART RAM Layout
*
* | Address | Name | Description |
* |-----------|----------|---------------------------------|
* | 0x000 | txdata | Transmit data register |
* | 0x004 | rxdata | Receive data register |
* | 0x008 | txctrl | Transmit control register |
* | 0x00C | rxctrl | Receive control register |
* | 0x010 | ie | UART interrupt enable |
* | 0x014 | ip | UART Interrupt pending |
* | 0x018 | div | Baud rate divisor |
*
*/
#ifndef _DRIVER_UARTHS_H
#define _DRIVER_UARTHS_H
#include <stddef.h>
#include <stdint.h>
#include "platform.h"
#include "plic.h"
#ifdef __cplusplus
extern "C" {
#endif
/* clang-format off */
/* Register address offsets */
#define UARTHS_REG_TXFIFO (0x00)
#define UARTHS_REG_RXFIFO (0x04)
#define UARTHS_REG_TXCTRL (0x08)
#define UARTHS_REG_RXCTRL (0x0c)
#define UARTHS_REG_IE (0x10)
#define UARTHS_REG_IP (0x14)
#define UARTHS_REG_DIV (0x18)
/* TXCTRL register */
#define UARTHS_TXEN (0x01)
#define UARTHS_TXWM(x) (((x) & 0xffff) << 16)
/* RXCTRL register */
#define UARTHS_RXEN (0x01)
#define UARTHS_RXWM(x) (((x) & 0xffff) << 16)
/* IP register */
#define UARTHS_IP_TXWM (0x01)
#define UARTHS_IP_RXWM (0x02)
/* clang-format on */
typedef struct _uarths_txdata
{
/* Bits [7:0] is data */
uint32_t data : 8;
/* Bits [30:8] is 0 */
uint32_t zero : 23;
/* Bit 31 is full status */
uint32_t full : 1;
} __attribute__((packed, aligned(4))) uarths_txdata_t;
typedef struct _uarths_rxdata
{
/* Bits [7:0] is data */
uint32_t data : 8;
/* Bits [30:8] is 0 */
uint32_t zero : 23;
/* Bit 31 is empty status */
uint32_t empty : 1;
} __attribute__((packed, aligned(4))) uarths_rxdata_t;
typedef struct _uarths_txctrl
{
/* Bit 0 is txen, controls whether the Tx channel is active. */
uint32_t txen : 1;
/* Bit 1 is nstop, 0 for one stop bit and 1 for two stop bits */
uint32_t nstop : 1;
/* Bits [15:2] is reserved */
uint32_t resv0 : 14;
/* Bits [18:16] is threshold of interrupt triggers */
uint32_t txcnt : 3;
/* Bits [31:19] is reserved */
uint32_t resv1 : 13;
} __attribute__((packed, aligned(4))) uarths_txctrl_t;
typedef struct _uarths_rxctrl
{
/* Bit 0 is txen, controls whether the Tx channel is active. */
uint32_t rxen : 1;
/* Bits [15:1] is reserved */
uint32_t resv0 : 15;
/* Bits [18:16] is threshold of interrupt triggers */
uint32_t rxcnt : 3;
/* Bits [31:19] is reserved */
uint32_t resv1 : 13;
} __attribute__((packed, aligned(4))) uarths_rxctrl_t;
typedef struct _uarths_ip
{
/* Bit 0 is txwm, raised less than txcnt */
uint32_t txwm : 1;
/* Bit 1 is txwm, raised greater than rxcnt */
uint32_t rxwm : 1;
/* Bits [31:2] is 0 */
uint32_t zero : 30;
} __attribute__((packed, aligned(4))) uarths_ip_t;
typedef struct _uarths_ie
{
/* Bit 0 is txwm, raised less than txcnt */
uint32_t txwm : 1;
/* Bit 1 is txwm, raised greater than rxcnt */
uint32_t rxwm : 1;
/* Bits [31:2] is 0 */
uint32_t zero : 30;
} __attribute__((packed, aligned(4))) uarths_ie_t;
typedef struct _uarths_div
{
/* Bits [31:2] is baud rate divisor register */
uint32_t div : 16;
/* Bits [31:16] is 0 */
uint32_t zero : 16;
} __attribute__((packed, aligned(4))) uarths_div_t;
typedef struct _uarths
{
/* Address offset 0x00 */
uarths_txdata_t txdata;
/* Address offset 0x04 */
uarths_rxdata_t rxdata;
/* Address offset 0x08 */
uarths_txctrl_t txctrl;
/* Address offset 0x0c */
uarths_rxctrl_t rxctrl;
/* Address offset 0x10 */
uarths_ie_t ie;
/* Address offset 0x14 */
uarths_ip_t ip;
/* Address offset 0x18 */
uarths_div_t div;
} __attribute__((packed, aligned(4))) uarths_t;
typedef enum _uarths_interrupt_mode
{
UARTHS_SEND = 1,
UARTHS_RECEIVE = 2,
UARTHS_SEND_RECEIVE = 3,
} uarths_interrupt_mode_t;
typedef enum _uarths_stopbit
{
UARTHS_STOP_1,
UARTHS_STOP_2
} uarths_stopbit_t;
extern volatile uarths_t *const uarths;
/**
* @brief Initialization Core UART
*
* @return result
* - 0 Success
* - Other Fail
*/
void uarths_init(void);
/**
* @brief Put a char to UART
*
* @param[in] c The char to put
*
* @note If c is '\n', a '\r' will be appended automatically
*
* @return result
* - 0 Success
* - Other Fail
*/
int uarths_putchar(char c);
/**
* @brief Send a string to UART
*
* @param[in] s The string to send
*
* @note The string must ending with '\0'
*
* @return result
* - 0 Success
* - Other Fail
*/
int uarths_puts(const char *s);
/**
* @brief Get a byte from UART
*
* @return byte as int type from UART
*/
int uarths_getc(void);
/**
* @brief Set uarths interrupt callback
*
* @param[in] interrupt_mode Interrupt mode recevice or send
* @param[in] uarths_callback Interrupt callback
* @param[in] ctx Param of callback
* @param[in] priority Interrupt priority
*
*/
void uarths_set_irq(uarths_interrupt_mode_t interrupt_mode, plic_irq_callback_t uarths_callback, void *ctx, uint32_t priority);
/**
* @brief Uarths receive data
*
* @param[in] buf The data received
* @param[in] buf_len The length of data
*
* @return Number of received data
*/
size_t uarths_receive_data(uint8_t *buf, size_t buf_len);
/**
* @brief Uarths receive data
*
* @param[in] buf The data sended
* @param[in] buf_len The length of data
*
* @return Number of sended data
*/
size_t uarths_send_data(const uint8_t *buf, size_t buf_len);
/**
* @brief Get interrupt mode
*
* @return Mode of interrupt
*/
uarths_interrupt_mode_t uarths_get_interrupt_mode(void);
/**
* @brief Set uarths baud rate and stopbit
*
* @param[in] baud_rate The baud rate
* @param[in] stopbit The stopbit of data
*
*/
void uarths_config(uint32_t baud_rate, uarths_stopbit_t stopbit);
/**
* @brief Set uart interrupt condition
*
* @param[in] interrupt_mode The interrupt mode
* @param[in] cnt The count of tigger
*
*/
void uarths_set_interrupt_cnt(uarths_interrupt_mode_t interrupt_mode, uint8_t cnt);
#ifdef __cplusplus
}
#endif
#endif /* _DRIVER_UARTHS_H */

View File

@ -0,0 +1,44 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stddef.h>
#include "encoding.h"
#include "utils.h"
void set_bit(volatile uint32_t *bits, uint32_t mask, uint32_t value)
{
uint32_t org = (*bits) & ~mask;
*bits = org | (value & mask);
}
void set_bit_offset(volatile uint32_t *bits, uint32_t mask, size_t offset, uint32_t value)
{
set_bit(bits, mask << offset, value << offset);
}
void set_gpio_bit(volatile uint32_t *bits, size_t offset, uint32_t value)
{
set_bit_offset(bits, 1, offset, value);
}
uint32_t get_bit(volatile uint32_t *bits, uint32_t mask, size_t offset)
{
return ((*bits) & (mask << offset)) >> offset;
}
uint32_t get_gpio_bit(volatile uint32_t *bits, size_t offset)
{
return get_bit(bits, 1, offset);
}

View File

@ -0,0 +1,340 @@
/* Copyright 2018 Canaan Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _DRIVER_UTILS_H
#define _DRIVER_UTILS_H
#include "uarths.h"
#ifdef __cplusplus
#include <cstdbool>
#include <cstddef>
#include <cstdint>
#else /* __cplusplus */
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#endif /* __cplusplus */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#define KENDRYTE_MIN(a, b) ((a) > (b) ? (b) : (a))
#define KENDRYTE_MAX(a, b) ((a) > (b) ? (a) : (b))
#ifdef __ASSEMBLY__
#define KENDRYTE_CAST(type, ptr) ptr
#else /* __ASSEMBLY__ */
/**
* @brief Cast the pointer to specified pointer type.
*
* @param[in] type The pointer type to cast to
* @param[in] ptr The pointer to apply the type cast to
*/
#define KENDRYTE_CAST(type, ptr) ((type)(ptr))
#endif /* __ASSEMBLY__ */
/**
* @addtogroup UTIL_RW_FUNC Memory Read/Write Utilities
*
* This section implements read and write functionality for various
* memory untis. The memory unit terms used for these functions are
* consistent with those used in the ARM Architecture Reference Manual
* ARMv7-A and ARMv7-R edition manual. The terms used for units of memory are:
*
* Unit of Memory | Abbreviation | Size in Bits
* :---------------|:-------------|:------------:
* Byte | byte | 8
* Half Word | hword | 16
* Word | word | 32
* Double Word | dword | 64
*
*/
/**
* @brief Write the 8 bit byte to the destination address in device memory.
*
* @param[in] dest Write destination pointer address
* @param[in] src 8 bit data byte to write to memory
*/
#define kendryte_write_byte(dest, src) \
(*KENDRYTE_CAST(volatile uint8_t*, (dest)) = (src))
/**
* @brief Read and return the 8 bit byte from the source address in device memory.
*
* @param[in] src Read source pointer address
*
* @return 8 bit data byte value
*/
#define kendryte_read_byte(src) (*KENDRYTE_CAST(volatile uint8_t*, (src)))
/**
* @brief Write the 16 bit half word to the destination address in device memory.
*
* @param[in] dest Write destination pointer address
* @param[in] src 16 bit data half word to write to memory
*/
#define kendryte_write_hword(dest, src) \
(*KENDRYTE_CAST(volatile uint16_t*, (dest)) = (src))
/**
* @brief Read and return the 16 bit half word from the source address in device
*
* @param[in] src Read source pointer address
*
* @return 16 bit data half word value
*/
#define kendryte_read_hword(src) (*KENDRYTE_CAST(volatile uint16_t*, (src)))
/**
* @brief Write the 32 bit word to the destination address in device memory.
*
* @param[in] dest Write destination pointer address
* @param[in] src 32 bit data word to write to memory
*/
#define kendryte_write_word(dest, src) \
(*KENDRYTE_CAST(volatile uint32_t*, (dest)) = (src))
/**
* @brief Read and return the 32 bit word from the source address in device memory.
*
* @param[in] src Read source pointer address
*
* @return 32 bit data half word value
*/
#define kendryte_read_word(src) (*KENDRYTE_CAST(volatile uint32_t*, (src)))
/**
* @brief Write the 64 bit double word to the destination address in device memory.
*
* @param[in] dest Write destination pointer address
* @param[in] src 64 bit data word to write to memory
*/
#define kendryte_write_dword(dest, src) \
(*KENDRYTE_CAST(volatile uint64_t*, (dest)) = (src))
/**
* @brief Read and return the 64 bit double word from the source address in device
*
* @param[in] src Read source pointer address
*
* @return 64 bit data half word value
*/
#define kendryte_read_dword(src) (*KENDRYTE_CAST(volatile uint64_t*, (src)))
/**
* @brief Set selected bits in the 8 bit byte at the destination address in device
*
* @param[in] dest Destination pointer address
* @param[in] bits Bits to set in destination byte
*/
#define kendryte_setbits_byte(dest, bits) \
(kendryte_write_byte(dest, kendryte_read_byte(dest) | (bits)))
/**
* @brief Clear selected bits in the 8 bit byte at the destination address in device
*
* @param[in] dest Destination pointer address
* @param[in] bits Bits to clear in destination byte
*/
#define kendryte_clrbits_byte(dest, bits) \
(kendryte_write_byte(dest, kendryte_read_byte(dest) & ~(bits)))
/**
* @brief Change or toggle selected bits in the 8 bit byte at the destination address
*
* @param[in] dest Destination pointer address
* @param[in] bits Bits to change in destination byte
*/
#define kendryte_xorbits_byte(dest, bits) \
(kendryte_write_byte(dest, kendryte_read_byte(dest) ^ (bits)))
/**
* @brief Replace selected bits in the 8 bit byte at the destination address in device
*
* @param[in] dest Destination pointer address
* @param[in] msk Bits to replace in destination byte
* @param[in] src Source bits to write to cleared bits in destination byte
*/
#define kendryte_replbits_byte(dest, msk, src) \
(kendryte_write_byte(dest, (kendryte_read_byte(dest) & ~(msk)) | ((src) & (msk))))
/**
* @brief Set selected bits in the 16 bit halfword at the destination address in
*
* @param[in] dest Destination pointer address
* @param[in] bits Bits to set in destination halfword
*/
#define kendryte_setbits_hword(dest, bits) \
(kendryte_write_hword(dest, kendryte_read_hword(dest) | (bits)))
/**
* @brief Clear selected bits in the 16 bit halfword at the destination address in
*
* @param[in] dest Destination pointer address
* @param[in] bits Bits to clear in destination halfword
*/
#define kendryte_clrbits_hword(dest, bits) \
(kendryte_write_hword(dest, kendryte_read_hword(dest) & ~(bits)))
/**
* @brief Change or toggle selected bits in the 16 bit halfword at the destination
*
* @param[in] dest Destination pointer address
* @param[in] bits Bits to change in destination halfword
*/
#define kendryte_xorbits_hword(dest, bits) \
(kendryte_write_hword(dest, kendryte_read_hword(dest) ^ (bits)))
/**
* @brief Replace selected bits in the 16 bit halfword at the destination address in
*
* @param[in] dest Destination pointer address
* @param[in] msk Bits to replace in destination byte
* @param[in] src Source bits to write to cleared bits in destination halfword
*/
#define kendryte_replbits_hword(dest, msk, src) \
(kendryte_write_hword(dest, (kendryte_read_hword(dest) & ~(msk)) | ((src) & (msk))))
/**
* @brief Set selected bits in the 32 bit word at the destination address in device
*
* @param[in] dest Destination pointer address
* @param[in] bits Bits to set in destination word
*/
#define kendryte_setbits_word(dest, bits) \
(kendryte_write_word(dest, kendryte_read_word(dest) | (bits)))
/**
* @brief Clear selected bits in the 32 bit word at the destination address in device
*
* @param[in] dest Destination pointer address
* @param[in] bits Bits to clear in destination word
*/
#define kendryte_clrbits_word(dest, bits) \
(kendryte_write_word(dest, kendryte_read_word(dest) & ~(bits)))
/**
* @brief Change or toggle selected bits in the 32 bit word at the destination address
*
* @param[in] dest Destination pointer address
* @param[in] bits Bits to change in destination word
*/
#define kendryte_xorbits_word(dest, bits) \
(kendryte_write_word(dest, kendryte_read_word(dest) ^ (bits)))
/**
* @brief Replace selected bits in the 32 bit word at the destination address in
*
* @param[in] dest Destination pointer address
* @param[in] msk Bits to replace in destination word
* @param[in] src Source bits to write to cleared bits in destination word
*/
#define kendryte_replbits_word(dest, msk, src) \
(kendryte_write_word(dest, (kendryte_read_word(dest) & ~(msk)) | ((src) & (msk))))
/**
* @brief Set selected bits in the 64 bit doubleword at the destination address in
*
* @param[in] dest Destination pointer address
* @param[in] bits Bits to set in destination doubleword
*/
#define kendryte_setbits_dword(dest, bits) \
(kendryte_write_dword(dest, kendryte_read_dword(dest) | (bits)))
/**
* @brief Clear selected bits in the 64 bit doubleword at the destination address in
*
* @param[in] dest Destination pointer address
* @param[in] bits Bits to clear in destination doubleword
*/
#define kendryte_clrbits_dword(dest, bits) \
(kendryte_write_dword(dest, kendryte_read_dword(dest) & ~(bits)))
/**
* @brief Change or toggle selected bits in the 64 bit doubleword at the destination
*
* @param[in] dest Destination pointer address
* @param[in] bits Bits to change in destination doubleword
*/
#define kendryte_xorbits_dword(dest, bits) \
(kendryte_write_dword(dest, kendryte_read_dword(dest) ^ (bits)))
/**
* @brief Replace selected bits in the 64 bit doubleword at the destination address in
*
* @param[in] dest Destination pointer address
* @param[in] msk its to replace in destination doubleword
* @param[in] src Source bits to write to cleared bits in destination word
*/
#define kendryte_replbits_dword(dest, msk, src) \
(kendryte_write_dword(dest, (kendryte_read_dword(dest) & ~(msk)) | ((src) & (msk))))
/**
* @brief Set value by mask
*
* @param[in] bits The one be set
* @param[in] mask mask value
* @param[in] value The value to set
*/
void set_bit(volatile uint32_t *bits, uint32_t mask, uint32_t value);
/**
* @brief Set value by mask
*
* @param[in] bits The one be set
* @param[in] mask Mask value
* @param[in] offset Mask's offset
* @param[in] value The value to set
*/
void set_bit_offset(volatile uint32_t *bits, uint32_t mask, size_t offset, uint32_t value);
/**
* @brief Set bit for gpio, only set one bit
*
* @param[in] bits The one be set
* @param[in] idx Offset value
* @param[in] value The value to set
*/
void set_gpio_bit(volatile uint32_t *bits, size_t idx, uint32_t value);
/**
* @brief Get bits value of mask
*
* @param[in] bits The source data
* @param[in] mask Mask value
* @param[in] offset Mask's offset
*
* @return The bits value of mask
*/
uint32_t get_bit(volatile uint32_t *bits, uint32_t mask, size_t offset);
/**
* @brief Get a bit value by offset
*
* @param[in] bits The source data
* @param[in] offset Bit's offset
*
*
* @return The bit value
*/
uint32_t get_gpio_bit(volatile uint32_t *bits, size_t offset);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* _DRIVER_COMMON_H */

View File

@ -0,0 +1,123 @@
/****************************************************************************
* arch/riscv/src/rv32im/up_syscall.S
*
* Copyright (C) 2011, 2015 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Modified for RISC-V:
*
* Copyright (C) 2016 Ken Pettit. All rights reserved.
* Author: Ken Pettit <pettitkd@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX nor the names of its contributors may be
* used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
/****************************************************************************
* Public Symbols
****************************************************************************/
.file "up_syscall0.S"
.global sys_call0
.global sys_call1
.global sys_call2
.global sys_call3
.global sys_call4
.global sys_call5
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: up_syscall0, up_syscall1, up_syscall2, up_syscall3
*
* Description:
* up_syscall0 - System call SYS_ argument and no additional parameters.
* up_syscall1 - System call SYS_ argument and one additional parameter.
* up_syscall2 - System call SYS_ argument and two additional parameters.
* up_syscall3 - System call SYS_ argument and three additional parameters.
* up_syscall4 - System call SYS_ argument and four additional parameters.
* up_syscall5 - System call SYS_ argument and five additional parameters.
*
* Assumption:
* All interrupts are disabled except for the software interrupts.
*
****************************************************************************/
.text
sys_call0: /* a0 holds the syscall number */
sys_call1: /* a0 holds the syscall number, argument in a1 */
sys_call2: /* a0 holds the syscall number, arguments in a1 and a2 */
sys_call3: /* a0 holds the syscall number, arguments in a1, a2, and a3 */
sys_call4: /* a0 holds the syscall number, arguments in a1, a2, a3 and a4 */
sys_call5: /* a0 holds the syscall number, arguments in a1, a2, a3, a4 and a5 */
/* Issue the ECALL opcode to perform a SW interrupt to the OS */
ecall
/* The actual interrupt may not a occur for a few more cycles. Let's
* put a few nop's here in hope that the SW interrupt occurs during
* the sequence of nops.
*/
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
nop
/* Then return with the result of the software interrupt in v0 */
ret
nop