当前位置: 首页 > news >正文

RISCV学习(5)GD32VF103 MCU架构了解

RISCV学习(5)GD32VF103 MCU架构了解

1、芯片内核功能简介

GD32VF103 MCU架构,采用Bumblebee内核,芯来科技(Nuclei System Technology)与台湾晶心科技(Andes Technology)联合开发,由芯来科技(Nuclei System Technology)提供授权以及技术支持等服务。

架构特点:

  • CPU 内核(CPU Core)
    • 2 级变长流水线架构,采用一流的处理器架构设计,实现业界最高的能效比与最低的成本。
    • 简单的动态分支预测器。
    • 指令预取单元,能够按顺序预取两条指令, 从而隐藏指令的访存延迟。
    • 支持机器模式(Machine Mode)和用户模式(User Mode)。
  • 支持指令集架构(ISA, Instruction Set Architecture)
    • Bumblebee内核支持32位的RISC-V指令集架构,支持RV32IMAC指令子集的组合。
    • 硬件支持非对齐(Misalign)的存储器访问操作(Load/Store 指令)
  • 总线接口
    • 支持 32 比特宽的标准 AHB-Lite 系统总线接口,用于访问外部指令和数据。
    • 支持 32 比特宽的指令局部存储器(Instruction Local Memory, ILM)总线接口(支持标准的 AHB-Lite 或 SRAM 接口协议),用于连接私有的指令局部存储器
    • 支持 32 比特宽的数据局部存储器(Data Local Memory, DLM)总线接口(支持标准的 AHB-Lite 或 SRAM 接口协议),用于连接私有的数据局部存储器。
    • 支持 32 比特宽的私有设备总线(Private Peripheral Interface, PPI),支持标准的 APB接口协议,用于连接私有的外设
  • 调试功能
    • 支持标准 JTAG 接口。
    • 支持 RISC-V 调试标准。
    • 支持 4 个硬件断点(Hardware Breakpoints)。
    • 支持成熟的交互式调试工具
  • 低功耗管理
    • 支持 WFI(Wait For Interrupt)与 WFE(Wait For Event)进入休眠模式。
    • 支持两级休眠模式:浅度休眠与深度休眠
  • 内核私有的计时器单元(Machine Timer,简称 TIMER)
    • 64 比特宽的实时计时器,支持产生 RISC-V 标准定义的计时器中断。
  • 增强的内核中断控制器(Enhanced Core Level Interrupt Controller, ECLIC)
    • 支持 RISC-V 标准定义的的软件中断、计时器中断和外部中断。
    • 支持数十个外部中断源,中断源的数目和分配请参见具体 MCU 芯片的数据手册。
    • 支持 16 个中断级别和优先级,支持软件动态可编程修改中断级别和中断优先级的数值。
    • 支持基于中断级别的中断嵌套。
    • 支持快速向量中断处理机制。
    • 支持快速中断咬尾机制。
  • 支持NMI,不可屏蔽中断
    架构图如下图所示:
    在这里插入图片描述
    • ILM 和DLM:指令/数据 局部memory,AHB Lite(AHB的简单版本,高性能/低功耗的嵌入式版本)接口访问,周期基本可达到1 Cycle,相当于ARM 的TCM。
    • 访问SRAM或者外部memory,则需要经过system bus总线,总线周期会高一些,
    • RISCV的版本为 1.1版本,更高级的版本模式等有变化,比如有4个模式等等。

2、芯片指令集

  • RV32 架构: 32 位地址空间,通用寄存器宽度 32 位。
  • I:支持 32 个通用整数寄存器。
  • M: 支持整数乘法与除法指令
  • C:支持编码长度为 16 位的压缩指令,提高代码密度。
  • A:支持原子操作指令。
    按照 RISC-V 架构命名规则,以上指令子集的组合可表示为 RV32IMAC

2.1 指令集举例说明

寻址

  • mv,传送寄存器到寄存器
    mv rd,rs,rs源寄存器送到目标寄存器rd
    在这里插入图片描述

  • lw,内存访问,load word,将32位寄存器加载到目标寄存器,4字节读出
    lw,x15,0x0(x23) --> x15 = [x23+0],x15和x23是寄存器
    在这里插入图片描述

  • lwsp:出栈指令

    lwsp x1,0x8(x2),x1 = [x2+0x8],x2是sp指针,sp+8的值 赋值给x1

  • sw,内存访问,store word,将将32位寄存器写到目标寄存器地址,4字节写入,
    sw,x27,0x0(x23) --> [x23+0] = x27
    在这里插入图片描述

  • swsp:压栈指令
    swsp x1,0x8(x2),[x2+0x8]=x1,x2是sp指针,将x1压栈到sp+8的地址

  • lui,直接传输 将立即数左移12位到目标寄存器
    lui x27,0x20000 —> x27 = 0x20000<<12;
    在这里插入图片描述

  • lbu,load byte unsigned,加载一个无符号数据到目标地址,lb是加载有符号数,会进行扩展
    lbu x27,0x0(x25) —> x27 = [x25 + 0]
    在这里插入图片描述
    在这里插入图片描述

  • sb,store byte,存储一个byte字节。
    sb x27, 0x0(x25)
    在这里插入图片描述

  • sd ,store dword,存储8字节
    在这里插入图片描述

  • ld,双字加载
    在这里插入图片描述

加减乘除法左移 右移指令

  • slli:左移指令
    在这里插入图片描述

  • srli:右移指令
    在这里插入图片描述

  • or:或指令
    在这里插入图片描述

  • addi:加法指令, i代表immediate ,立即数
    addi,x8,x8,0xC9
    在这里插入图片描述

跳转指令

  • j,直接跳转指令,后面直接跟地址
    j 0x080005ZF6
    在这里插入图片描述

  • jalr,带连接的跳转指令,一般和auipc一起用,
    auipc x1,0x1 将x1=x1 +1 <<12,即加上0x1000,4k地址
    jalr x1,-0x5CA(x1),先计算跳转地址,pc = x1- 0x5CA,然后将返回地址保存到x1,x1 =当前pc+4
    在这里插入图片描述

其他指令

  • ret 指令
    在这里插入图片描述

  • mret,异常返回指令
    在这里插入图片描述

  • ebreak
    在这里插入图片描述

  • ecall
    在这里插入图片描述
    来看一段汇编,理解一些基础指令
    在这里插入图片描述
    函数调用,传参数使用 x10 -x17,总共8个参数。

  • 左边第250行代码,函数led_blink函数,参数是test_counter_g。

  • mv x10,x27,将27的数据给了x10,说明x27就是test_counter_g的值,那就看x27的值

  • lw x27,0x0(x22),说明x27的值是通过x22地址读出来的,那就看x22的地址,

  • lui x22,0x20000,addi x22,x22,0x70, x22 = 0x20000+0x70 = 0x20000070 地址,符合地址要求
    在这里插入图片描述

  • 接下来,auipc x1, 0x1 那么 x1 = 080015FC

  • jalr x1,-0x3DA,那么x1 = 0x080045FC - 0x3DA = 0x08001222,就是led_blink 函数的地址

在这里插入图片描述

  • 接着再看上面的代码, lw x15,0x0(x23) , lw x27,0x0(x22) ,加载了x22和x23地址的数据到x15和x27,x22是test_counter_g的地址, x23 的地址是什么呢? 通过上面可以计算其地址
  • lui x23,0x20000,addi x23,x24,0x6C, x23 = 0x20000+0x6c = 0x2000006C 地址,是 current_test_count_g的地址
    在这里插入图片描述
  • 这样就比较好理解,加载了这两个变量的值,就是用来判断是否相等的。
  • beq x15,x27,0x8000632 如果不相等,直接跳走
  • 否则, c.mv x10,x24 ,把x24的值给了x10,说明又有函数调用
  • lui x24,0x40001 , addi x24,x24,-0x800 x24的地址为,x24 = 0x40001000 - 0x800 = 0x40000800,其是一个外设地址,是TIMER3的base地址。
    在这里插入图片描述
  • auipc x1,0x0 , jalr x1,0x4F6(x1) ,x1 = 0x08000612+0x4F6 = 0x08000B08,是timer_counter_read函数的地址,
    在这里插入图片描述
  • 接着是一个打印,刚刚x10 作为函数的返回值,这次作为打印的第三个参数,所以就是x12 寄存器的值,mv x12,x10
  • mv x11,x27 x27是 test_counter_g的值,是第二个参数
  • addi x10,x26,0x350 ,x10 = x26+x350,x26= 08000000,所以x10 = 08000350,字符串的地址
    在这里插入图片描述
  • 最后是一个跳转函数,auipc x1,0x1 jalr x1,-0x580(x1) ,跳转地址= 08001622 - 0x580 = 0x080010A2,则是test_printf的函数。
    在这里插入图片描述

3、芯片特权架构

Bumblebee 内核支持两个特权模式(Privilege Modes):

  • 机器模式(Machine Mode)是必须的模式,该 Privilege Mode 的编码是 0x3。
  • 用户模式(User Mode)是可配置的模式,该 Privilege Mode 的编码是 0x0。

RISCV的CSR 寄存器可以反应内核的状态,不是单个寄存器,是一系列寄存器。

3.1 机器模式(Machine Mode)

Bumblebee 内核有关 Machine Mode 的关键要点如下:

  • 处理器内核被复位后,默认处于 Machine Mode。
  • 在 Machine Mode 下,程序能够访问所有的 CSR 寄存器。
  • 跳转到用户模式。执行mret指令

3.2 用户模式(User Mode)

Bumblebee 内核有关 User Mode 的关键要点如下:

  • 在 User Mode 下只能够访问 User Mode 限定的 CSR 寄存器,
  • 从用户模式到机器模式,通过异常中断

3.3 机器子模式(Machine Sub-Mode)

Bumblebee 内核的 Machine Mode 可能处于四种不同的状态下,将之称之为机器子模式
(Machine Sub-Mode):

  • 正常机器模式(该 Machine Sub-Mode 的编码是 0x0):处理器内核被复位之后,处于此子模式之下。处理器复位后如果不产生异常、 NMI、中断,则一直正常运行于此模式之下。
  • 异常处理模式(该 Machine Sub-Mode 的编码是 0x2):响应异常后处理器内核处于此状态。
  • NMI 处理模式(该 Machine Sub-Mode 的编码是 0x3):响应 NMI 后处理器内核处于此状态。
  • 中断处理模式(该 Machine Sub-Mode 的编码是 0x1):响应中断后处理器内核处于此状态。

处理器内核当前处于的 Machine Sub-Mode 反映在 CSR 寄存器 msubm 的 TYP 域中,因此软件可以通过读取此 CSR 寄存器查看当前处于的 Machine Sub-Mode。
注意: 在 RISC-V 架构中,进入异常、 NMI 或者中断也被统称为 Trap
在这里插入图片描述

3.4 不支持PMP

  • PMP :physical Memory Protection,物理内存保护,即ARM 芯片里面的MPU

4、芯片中断机制

芯片中断主要有外部中断和内部中断,中断跳转模式主要有向量模式与非向量模式。
在这里插入图片描述
具体详情,可以参考笔者这篇文章RISCV学习(4)GD32VF103 MCU芯片学习。

5、芯片异常机制

RISCV的芯片异常机制没有自动保存寄存器,需要用户自身保存。

异常处理的流程如下:均是硬件行为,一个时钟周期内完成。

  • 停止执行当前程序流,转而从 CSR 寄存器 mtvec 定义的 PC 地址开始执行。
  • 更新相关 CSR 寄存器,分别是以下几个寄存器:
    • mcause(Machine Cause Register)
    • mepc(Machine Exception Program Counter)
    • mtval(Machine Trap Value Register )
    • mstatus(Machine Status Register)
  • 更新处理器内核的 Privilege Mode 以及 Machine Sub-Mode。
    在这里插入图片描述
    mtvec :硬件异常处理函数的统一入口,最低64位对齐。
    在这里插入图片描述
    如下图所示:mtvec 为 x080013C3,
  • mtvec ADDR:0异常地址:0x080013C0,而该地址则为trap_entry的地址。
  • mtvec MODE:0x11,则为ECLIC 中断模式
    在这里插入图片描述

在这里插入图片描述
笔者测试的异常代码如下:

if(test_counter_g == 3){current_test_count_g = 0;test_printf("%d\r\n",(test_counter_g/current_test_count_g));}

mepc:保存进入异常之前的PC值,作为异常返回的地址,当然也可以作为分析出错的地址,2字节对齐。
在这里插入图片描述
如下图所述,mepc值为0x080005fa,指令为ebreak,确实会产生异常,可能编译器认为笔者是想进入异常,则编译成了该指令。
在这里插入图片描述在这里插入图片描述
mcause:异常原因寄存器,其值为0x38000003
在这里插入图片描述

  • INTERRUPT:其值为0,表示异常。
  • MPP:其值为3,表示特权模式,0为用户模式
  • MPIE:其值为1,表示允许中断,
  • EXCCODE:0x3,表示断点指令,可以进入异常。
    在这里插入图片描述
    mtval:表示异常的访问地址或者指令编码,笔者这里为0,表示该数据没有意义。

再比如一个例子:机器模式下面直接调用ecall,会触发进入异常。

test_entry:ecallret

在这里插入图片描述
在这里插入图片描述
笔者测试,即使非对齐访问和写入,不会触发异常,依然程序正常进行,待后续研究。

再来一个例子,非法指令

led_blink(test_counter_g);if(current_test_count_g != test_counter_g){test_printf("test_counter_g=%d timer_counter=%d\r\n",test_counter_g,timer_counter_read(TIMER3));test_printf("mcause=0x%x, int num=%d, time=%d\r\n", int_num_g, (int_num_g&0x3FF), rdtime());current_test_count_g = test_counter_g;}

异常编码为2,代表非法指令,pc指针为0800061e,然后mtval记录的指令地址为0xC01026F2,就是这个非法指令的值。
在这里插入图片描述
在这里插入图片描述

6、芯片timer

6.1 系统TIMER

系统timer属于外设,外设的地址由厂商自己定义,没有定义成CSR寄存器。64位的寄存器。

#define TIMER_MSIP 0xFFC
#define TIMER_MSIP_size   0x4
#define TIMER_MTIMECMP 0x8
#define TIMER_MTIMECMP_size 0x8
#define TIMER_MTIME 0x0
#define TIMER_MTIME_size 0x8#define TIMER_CTRL_ADDR           0xd1000000
unsigned int mtime_lo(void)
{return *(volatile unsigned int *)(TIMER_CTRL_ADDR + TIMER_MTIME);
}unsigned int mtime_hi(void)
{return *(volatile unsigned int *)(TIMER_CTRL_ADDR + TIMER_MTIME + 4);
}

寄存器地址如下:
在这里插入图片描述
测试效果如下:主循环打印

test_printf("mtime low=%d, diff=%d\r\n",mtime_lo(), (mtime_lo()-mtime_low_g));
mtime_low_g = mtime_lo();

在这里插入图片描述
timer里面可以软件中断:msip软件中断
msip软件中断 中断号为3,仍然需要设置中断模式以及向量等方式。

#if (INTERRUPT_MODE==INTERRUPT_VECTOR_MODE)__attribute__((interrupt)) void eclic_msip_handler()
#else
void eclic_msip_handler()
#endif
{msip_int_num_g =  read_csr(mcause);msip_int_clear();
}void set_software_int()
{#if (INTERRUPT_MODE == INTERRUPT_VECTOR_MODE) eclic_set_vmode(CLIC_INT_SFT);
#elseeclic_set_nonvmode(CLIC_INT_SFT);
#endifeclic_set_posedge_trig(CLIC_INT_SFT);eclic_irq_enable(CLIC_INT_SFT, 1, 0);
}void msip_int_set()
{*(volatile unsigned int *)(TIMER_CTRL_ADDR + TIMER_MSIP) = 1;
}int msip_int_get()
{return *(volatile unsigned int *)(TIMER_CTRL_ADDR + TIMER_MSIP) ;
}void msip_int_clear()
{*(volatile unsigned int *)(TIMER_CTRL_ADDR + TIMER_MSIP) = 0;
}msip_int_set();
test_printf("set msip int,reg=%d, int num=%d\r\n",msip_int_get(),  (msip_int_num_g&0x3FF));

在这里插入图片描述

6.2 指令周期计数器和指令计数器

mcycle 为:指令周期计数器
instret:指令计数器

这两者均是CSR寄存器。

cycle_start_g = read_csr(mcycle) ;
current_test_count_g = test_counter_g;
test_printf("start=%d end=%d, cycle=%d\r\n", cycle_start_g, read_csr(mcycle), (read_csr(mcycle))-cycle_start_g);cycle_start_g = read_csr(instret) ;
current_test_count_g = test_counter_g;
test_printf("start=%d end=%d, cycle=%d\r\n", cycle_start_g, read_csr(instret), (read_csr(instret))-cycle_start_g);

在这里插入图片描述
指令完成计数器:总共完成了5条指令,
在这里插入图片描述
时钟周期:总共花费5个时钟周期,

在这里插入图片描述

7 附录参考

中断向量号

/* define interrupt number */
typedef enum IRQn
{CLIC_INT_RESERVED        	 = 0,      			/*!< RISC-V reserved		*/CLIC_INT_SFT         		 = 3,				/*!< Software interrupt		*/CLIC_INT_TMR         		 = 7,				/*!< CPU Timer interrupt	*/CLIC_INT_BWEI        		 = 17,				/*!< Bus Error interrupt	*/CLIC_INT_PMOVI       		 = 18,				/*!< Performance Monitor	*//* interruput numbers */WWDGT_IRQn                   = 19,      /*!< window watchDog timer interrupt                          */LVD_IRQn                     = 20,      /*!< LVD through EXTI line detect interrupt                   */TAMPER_IRQn                  = 21,      /*!< tamper through EXTI line detect                          */RTC_IRQn                     = 22,      /*!< RTC alarm interrupt                                      */FMC_IRQn                     = 23,      /*!< FMC interrupt                                            */RCU_CTC_IRQn                 = 24,      /*!< RCU and CTC interrupt                                    */EXTI0_IRQn                   = 25,      /*!< EXTI line 0 interrupts                                   */EXTI1_IRQn                   = 26,      /*!< EXTI line 1 interrupts                                   */EXTI2_IRQn                   = 27,      /*!< EXTI line 2 interrupts                                   */EXTI3_IRQn                   = 28,      /*!< EXTI line 3 interrupts                                   */EXTI4_IRQn                   = 29,     /*!< EXTI line 4 interrupts                                   */DMA0_Channel0_IRQn           = 30,     /*!< DMA0 channel0 interrupt                                  */DMA0_Channel1_IRQn           = 31,     /*!< DMA0 channel1 interrupt                                  */DMA0_Channel2_IRQn           = 32,     /*!< DMA0 channel2 interrupt                                  */DMA0_Channel3_IRQn           = 33,     /*!< DMA0 channel3 interrupt                                  */DMA0_Channel4_IRQn           = 34,     /*!< DMA0 channel4 interrupt                                  */DMA0_Channel5_IRQn           = 35,     /*!< DMA0 channel5 interrupt                                  */DMA0_Channel6_IRQn           = 36,     /*!< DMA0 channel6 interrupt                                  */ADC0_1_IRQn                  = 37,     /*!< ADC0 and ADC1 interrupt                                  */CAN0_TX_IRQn                 = 38,     /*!< CAN0 TX interrupts                                       */CAN0_RX0_IRQn                = 39,     /*!< CAN0 RX0 interrupts                                      */CAN0_RX1_IRQn                = 40,     /*!< CAN0 RX1 interrupts                                      */CAN0_EWMC_IRQn               = 41,     /*!< CAN0 EWMC interrupts                                     */EXTI5_9_IRQn                 = 42,     /*!< EXTI[9:5] interrupts                                     */TIMER0_BRK_IRQn              = 43,     /*!< TIMER0 break interrupts                                  */TIMER0_UP_IRQn               = 44,     /*!< TIMER0 update interrupts                                 */TIMER0_TRG_CMT_IRQn          = 45,     /*!< TIMER0 trigger and commutation interrupts                */TIMER0_Channel_IRQn          = 46,     /*!< TIMER0 channel capture compare interrupts                */TIMER1_IRQn                  = 47,     /*!< TIMER1 interrupt                                         */TIMER2_IRQn                  = 48,     /*!< TIMER2 interrupt                                         */TIMER3_IRQn                  = 49,     /*!< TIMER3 interrupts                                        */I2C0_EV_IRQn                 = 50,     /*!< I2C0 event interrupt                                     */I2C0_ER_IRQn                 = 51,     /*!< I2C0 error interrupt                                     */I2C1_EV_IRQn                 = 52,     /*!< I2C1 event interrupt                                     */I2C1_ER_IRQn                 = 53,     /*!< I2C1 error interrupt                                     */SPI0_IRQn                    = 54,     /*!< SPI0 interrupt                                           */SPI1_IRQn                    = 55,     /*!< SPI1 interrupt                                           */USART0_IRQn                  = 56,     /*!< USART0 interrupt                                         */USART1_IRQn                  = 57,     /*!< USART1 interrupt                                         */USART2_IRQn                  = 58,     /*!< USART2 interrupt                                         */EXTI10_15_IRQn               = 59,     /*!< EXTI[15:10] interrupts                                   */RTC_ALARM_IRQn               = 60,     /*!< RTC alarm interrupt EXTI                                 */USBFS_WKUP_IRQn              = 61,     /*!< USBFS wakeup interrupt                                   */EXMC_IRQn                    = 67,     /*!< EXMC global interrupt                                    */TIMER4_IRQn                  = 69,     /*!< TIMER4 global interrupt                                  */SPI2_IRQn                    = 70,     /*!< SPI2 global interrupt                                    */UART3_IRQn                   = 71,     /*!< UART3 global interrupt                                   */UART4_IRQn                   = 72,     /*!< UART4 global interrupt                                   */TIMER5_IRQn                  = 73,     /*!< TIMER5 global interrupt                                  */TIMER6_IRQn                  = 74,     /*!< TIMER6 global interrupt                                  */DMA1_Channel0_IRQn           = 75,     /*!< DMA1 channel0 global interrupt                           */DMA1_Channel1_IRQn           = 76,     /*!< DMA1 channel1 global interrupt                           */DMA1_Channel2_IRQn           = 77,     /*!< DMA1 channel2 global interrupt                           */DMA1_Channel3_IRQn           = 78,     /*!< DMA1 channel3 global interrupt                           */DMA1_Channel4_IRQn           = 79,     /*!< DMA1 channel3 global interrupt                           */CAN1_TX_IRQn                 = 82,     /*!< CAN1 TX interrupt                                        */CAN1_RX0_IRQn                = 83,     /*!< CAN1 RX0 interrupt                                       */CAN1_RX1_IRQn                = 84,     /*!< CAN1 RX1 interrupt                                       */CAN1_EWMC_IRQn               = 85,     /*!< CAN1 EWMC interrupt                                      */USBFS_IRQn                   = 86,     /*!< USBFS global interrupt                                   */ECLIC_NUM_INTERRUPTS
} IRQn_Type;

相关文章:

  • 【AI News | 20250428】每日AI进展
  • transformer-实现单层encoder_layer
  • VINS-FUSION:跑通手机录制数据
  • C语言----操作符详解(万字详解)
  • 4月28日日记
  • 待验证---Oracle 19c 在 CentOS 7 上的快速安装部署指南
  • C#与SVN的深度集成:实现版本控制自动化管理​
  • MATLAB实现神经网络的OCR识别
  • Web 基础与 HTTP 协议
  • STM32的SysTick
  • 学成在线。。。
  • 【爬虫】码上爬第2题:headersi请求头验证
  • 排序算法详解笔记
  • 详解UnityWebRequest类
  • Day15(贪心算法)——LeetCode121.买卖股票的最佳时机55.跳跃游戏
  • 文献阅读(一)植物应对干旱的生理学反应 | The physiology of plant responses to drought
  • 机器学习基础理论 - 目标函数,损失函数,代价函数
  • C++入门(namespace/输入输出)
  • docker存储
  • 23.C语言指针相关知识点2
  • 金科服务:大股东博裕资本提出无条件强制性现金要约收购,总代价约17.86亿港元
  • “富卫保险冠军赛马日”创双纪录,打造赛马旅游盛宴,印证香港联通国际优势
  • 上海市市管干部任职前公示:赵亮拟为地区区长人选
  • 新加坡选情渐热:播客、短视频各显神通,总理反对身份政治
  • 伊朗港口爆炸已造成25人死亡,灭火行动已近尾声
  • 哈马斯同意释放剩余所有以方被扣押人员,以换取停火五年