forked from xuos/xiuos
support fft on aiit-riscv64-board for nuttx
This commit is contained in:
parent
9946b33530
commit
0501122537
|
@ -0,0 +1,6 @@
|
|||
############################################################################
|
||||
# Applications/knowing_app/Make.defs
|
||||
############################################################################
|
||||
ifneq ($(CONFIG_APPLICATION_KNOWING),)
|
||||
include $(wildcard $(APPDIR)/../../../APP_Framework/Applications/knowing_app/*/Make.defs)
|
||||
endif
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
};
|
||||
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
|
@ -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
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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 */
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#ifndef _KENDRYTE_BSP_H
|
||||
#define _KENDRYTE_BSP_H
|
||||
#include "atomic.h"
|
||||
#include "encoding.h"
|
||||
#endif
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 core’s 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 core’s time comparator. A timer interrupt is pending
|
||||
* whenever mtime is greater than or equal to the value in a
|
||||
* core’s mtimecmp register. The timer interrupt is reflected in
|
||||
* the mtip bit of the associated core’s 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 */
|
||||
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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 */
|
||||
|
|
@ -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;
|
||||
}
|
|
@ -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 device’s interrupt can be
|
||||
* enabled by setting the corresponding bit in that target’s
|
||||
* 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 source’s 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 target’s 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 target’s 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 */
|
|
@ -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);
|
||||
}
|
|
@ -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
|
@ -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;
|
||||
}
|
||||
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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 */
|
||||
|
|
@ -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
|
||||
|
Loading…
Reference in New Issue