[嵌入式系统-40]:龙芯1B 开发学习套件 -10-PMON启动过程start.S详解

目录

一、龙芯向量表与启动程序的入口(复位向量)

1.1 复位向量:

1.2 代码执行流程

1.3 计算机的南桥 VS 北桥

二、PMON代码执行流程

三、Start.S详解

3.1 CPU初始化时所需要的宏定义

(1)与CPU相关的一些宏定义有

(2)编译指示

(3)程序入口指示

(4)第一条可执行的指令

(5)确保在uncache的地址空间运行代码,而不是在Cache地址空间运行代

(6)异常向量

3.2 异常向量

3.3 初始化内存

3.4 内存地址修正

3.5 初始化北桥和串口

3.6 初始化E2PROM,并且读取E2PROM参数

3.7 初始化Cache

3.8 PMON自身从ROM中拷贝到RAM(在Cache之后)

3.9 汇编到C语言

它山之石:


一、龙芯向量表与启动程序的入口(复位向量)

1.1 复位向量:

当整个板子起电后,CPU将从 0xBFC00000 取指令开始执行,而ROM在系统中的地址就是从该地址开始的。所以,其中0xBFC00000 处的第一条指令就是整个过程中 CPU 要执行的第一个指令

+++++++++++++++++++++++++++++++++++++++++

1.2 代码执行流程

+++++++++++++++++++++++++++++++++++++++++

  • 初始化CPU内的寄存器,清TLB. 
  • 初始化一些北桥的基本配置,以确保uart能够正常工作. 
  • 初始化uart,主要是设置波特率. 
  • 初始化内存DDR RAM(主要通过I2C协议从内存自带的EEPROM读取内存参数来进行设置). 
  • 初始化cache. 
  • 拷贝ROM中的pmon的代码到内存DDRM,然后通过 
  • la      v0, initmips
  • jalr    v0
  • nop
  • 从此代码便到内存中间去了,从这开始因为可以读写内存,所以有了栈,故可以用C的代码了,所以以后的程序便是C代码了

1.3 计算机的南桥 VS 北桥

南桥和北桥是早期计算机系统中的两个主要组件,它们起着连接和协调计算机内部各部件工作的重要作用。它们的功能在现代计算机体系结构中已经被集成到了更复杂的芯片组中,因此南桥和北桥的区分已经不那么明显了。不过,我们可以回顾一下它们的基本概念:

  1. 北桥(Northbridge)

    • 北桥是计算机系统中的一种芯片组件,通常位于主板上,靠近 CPU。
    • 它负责处理与高速组件的连接,例如内存(RAM)、图形处理器(GPU)和一些扩展插槽(例如PCI Express)。
    • 在早期计算机系统中,北桥也负责处理内存控制器功能,但随着技术的发展,内存控制器已经被集成到了 CPU 中,因此这个功能已经不再需要。
  2. 南桥(Southbridge)

    • 南桥也是一种芯片组件,通常位于主板上,相对于北桥位置更靠近外部连接端口(如USB、SATA、网络端口等)。
    • 它负责管理和控制低速设备的连接,例如硬盘驱动器、键盘、鼠标和其他外围设备。
    • 南桥也负责处理一些I/O功能,例如PCI总线、USB控制器等。

总的来说,北桥和南桥在早期计算机系统中扮演了连接和协调内部各个组件的角色,但随着技术的发展,这些功能已经被整合到更复杂的芯片组中,而不再是单独的北桥和南桥芯片。

二、PMON代码执行流程

当整个板子起电或复位异常后,CPU将从 0xBFC00000 取指令开始执行,而ROM在系统中的地址就是从该地址开始的。所以,ROM的第一条指令就是整个过程中 CPU 要执行的第一个指令。

  • 初始化CPU内的寄存器,清TLB.
  • 初始化一些北桥的基本配置,以确保uart能够正常工作.
  • 初始化uart,主要是设置波特率.
  • 初始化内存(主要通过I2C协议从内存的EEPROM读取内存参数来进行设置).
  • 初始化cache.
  • 拷贝pmon的代码到内存,然后通过
la      v0, initmips
jalr    v0
nop

从此代码便到内存中间去了,从这开始因为可以读写内存,所以有了栈,故可以用C的代码了,所以以后的程序便是C代码了.

三、Start.S详解

3.1 CPU初始化时所需要的宏定义

start.S文件在 /Targets/Bonito/Bonito 目录当中,是整个PMON代码的起点。我们首先研究它。

文件一开头是版权声明部分,然后是包括一些头文件,然后是一些宏定义,然后才是代码。

(1)与CPU相关的一些宏定义有

/*
 *   Register usage:
 *
 *    s0    link versus load offset, used to relocate absolute adresses.
 *    s1    free
 *    s2    memory size.
 *    s3    free.
 *    s4    Bonito base address.
 *    s5    dbg.
 *    s6    sdCfg.
 *    s7    rasave.
 *    s8    L3 Cache size.
 */
 
#define tmpsize        s1
#define msize        s2
#define bonito        s4
#define dbg            s5
#define sdCfg        s6

(2)编译指示

下面是程序的开头,不过并不生成实际的二进制数据,它告诉编译汇编器一些信息。

.set    noreorder
    .globl    _start
    .globl    start
    .globl    __main

(3)程序入口指示

_start:   //程序入口标识
start:
    .globl    stack
    stack = start - 0x4000         /* Place PMON stack below PMON start in RAM */

=====================
解释:
.set noreorder
是告诉汇编汇编器不要对后面的代码进行优化处理,比如重新排列执行代码。

.globl _start
.globl start
.globl __main
这里,定义了三个全局符号。可以PMON代码中的任何地方引用它。

_start:
start:
.globl stack
stack = start - 0x4000 /* Place PMON stack below PMON start in RAM */
在这里定义了子程序的名称 _start 和 start。
并定义了堆栈的栈底 stack 值,在 start 以下 16K 处。栈地址通常在RAM中,而不是ROM中。
这里的代码说明:

-- 启动前,RAM的地址空间在ROM的地址空间一下.

-- RAM地址空间的最高处保留了16K的地址空间用于系统堆栈
=====================

栈(Stack)是计算机科学中一种基本的数据结构,它是一种先进后出(Last In, First Out,LIFO)的数据结构。栈通常用于存储临时数据以及函数调用的上下文信息。

栈的特点包括:

  1. 后进先出(LIFO):最后存入栈的数据项首先被取出。这是栈的基本原则,也是它与其他数据结构(如队列)的主要区别之一。

  2. 压栈(Push):将数据项放入栈顶的操作称为压栈。新的数据项被添加到栈顶,使其成为栈的新顶部。

  3. 弹栈(Pop):从栈顶移除数据项的操作称为弹栈。弹栈操作会移除栈顶的数据项,并将栈顶指针向下移动。

  4. 栈顶指针:栈顶指针是指向栈顶元素的位置的指针,它指示了下一个压入栈的元素应该存放的位置,同时也指示了下一个弹出的元素是什么。

栈在计算机科学中有广泛的应用,包括但不限于:

  • 函数调用:每当调用一个函数时,其局部变量和返回地址会被压入栈中,函数执行完毕后再从栈中弹出这些信息。

  • 表达式求值:在编译器和解释器中,栈常用于表达式求值,特别是中缀表达式转换为后缀表达式以及后缀表达式的计算。

  • 内存管理:栈还被用于内存管理,例如函数调用栈可以用来跟踪内存分配和释放。

  • 递归:递归函数调用时,每一层递归调用都会在栈上创建一个新的帧,递归结束后这些帧会依次从栈中弹出。

总之,栈是一种非常重要的数据结构,它在计算机科学中有着广泛的应用。

(4)第一条可执行的指令

下面是程序执行的第一条语句

/* NOTE!! Not more that 16 instructions here!!! Right now it's FULL! */
    mtc0    zero, COP_0_STATUS_REG  //清除CP0状态寄存器
    mtc0    zero, COP_0_CAUSE_REG   //清除CP0原因寄存器
    li    t0, SR_BOOT_EXC_VEC        /* 这是一个加载立即数的指令:Exception to Boostrap Location */                                
    mtc0    t0, COP_0_STATUS_REG
                                /* SR's BEV bit is set so the CPU uses the ROM(kseg1) space exception entry point when reboot exception occurs
    la    sp, stack                
    la    gp, _gp                

=====================        
解释:
由于龙芯的地址空间决定,这里的代码不能超过0x100,即256字节,因为后面紧跟着的是中断向量的地址。

接着,就把 CP0 的状态寄存器 COP_0_STATUS_REG 和 COP_0_CAUSE_REG 寄存器全部清空为0。
li t0, SR_BOOT_EXC_VEC
接着设置状态寄存器的BEV位,这样就是让 CP0 运行在没有 TLB 的模式,并且一旦发生异常,
就进入ROM 的 bfc00000 位置重启。

后面两句主要设置引导程序的堆栈空间,
la sp, stack 是把栈底地址给 sp 寄存器,
la gp, _gp 是把编译器中的 _gp 全局地址给 gp 寄存器,这样做法是让全局变量可以作相对寄存器寻址。
其中_gp是在连接脚本文件里定义的。


Exception to Bootstrap Location” 是一个描述异常处理程序应该跳转到的引导位置的术语。在计算机系统中,当发生异常(如内存访问错误、除以零等)时,系统需要知道应该执行哪些操作来处理这些异常。因此,程序员会设置一个异常处理程序,用于处理不同类型的异常情况。

“Boostrap Location” 可以理解为引导程序的位置,即系统启动时执行的第一个位置。在异常发生时,系统可能需要跳转到引导位置,以便进行异常处理或其他必要的操作。

因此,“Exception to Boostrap Location” 可以被理解为设置异常处理程序应该跳转到的系统引导位置。这在上下文中通常用于设置异常向引导位置注册,以确保在发生异常时能够正确地处理异常情况并返回到引导位置进行进一步处理。
=====================

(5)确保在uncache的地址空间运行代码,而不是在Cache地址空间运行代

#define KUSEG_ADDR        0x0
#define CACHED_MEMORY_ADDR    0x80000000
#define UNCACHED_MEMORY_ADDR    0xa0000000
#define KSEG2_ADDR        0xc0000000
#define MAX_MEM_ADDR        0xbe000000
#define    RESERVED_ADDR        0xbfc80000

bal    uncached
nop
bal    locate
nop    

uncached:
or      ra, UNCACHED_MEMORY_ADDR
j    ra
nop

=====================        
解释:
这段程序先进行一个无条件跳转连接指令,这样做的目的很明确就是想清空预取指令和流水线的指令。
这样就跳到 uncached 这里运行。
先来看看bal指令会做些什么事情,
通常bal指令会算出跳转到的目的地址相对于PC寄存器的偏移量,
然后把PC+8指令地址放到ra寄存器里,也就是把bal locate指令地址放到RA寄存器,以便可以返回。

由于龙芯2E的加电时启动地址是 0xBFC0 0000,那么放在ra里的值就是 0xBFCO 0028(第8条指令)。
后面
or ra, UNCACHED_MEMORY_ADDR,这里进行是与0xA000 00000的或运算,
也就是说从ROM加载时,不会改变返回地址 ra 的值。
写这句的目的主要是保证要从ROM中运行后面的一段程序,而不是从其它地址(RAM中)运行。
所以接着就跳回来到 bal locate位置并执行 bal locate 指令,这样就跳到 locate 的位置执行程序了。
=====================

(6)异常向量

在MIPS中,异常处理入口有两套,通过 CP0 的 STATUS 寄存器位 BEV 来决定,

当 BEV=1 时,异常的入口地址为 0xBFC00000 开始的地址。

而 BEV=0,异常地址为 0x80000000 开始的地址,

所以PMON程序段开始处是一些异常的调入口,需要跳过这段空间,

程序就是通过这个 bal 指令跳到后面的

pmon-exception-vector.png

3.2 异常向量

下面是那段被跳过去的异常代码

/*
 *  Reboot vector usable from outside pmon. 
 */
    /* started in aligned address by 2^8=256 Bytes, that is 0xbfc00000 + 0x100 = 0xbfc00100 */
    .align    8                        
ext_map_and_reboot:
    bal    CPU_TLBClear                
    nop

    li    a0, 0xc0000000
    li    a1, 0x40000000
    bal    CPU_TLBInit
    nop
    la    v0, tgt_reboot                
    la    v1, start                    
    subu    v0, v1                    
    lui    v1, 0xffc0                    
    addu    v0, v1
    jr    v0
    nop

/*
 *  Exception vectors here for rom, before we are up and running. Catch
 *  whatever comes up before we have a fully fledged exception handler.
 */
    /* TLB refill exception */
    /* bfc00200, this code address is 0xbfc00200, 2^9 = 512 Bytes, it is a exception process function */
    .align    9            

    move    k0, ra            /* save ra */
    la    a0, v200_msg
    bal    stringserial
    nop
    b    exc_common

    .align    7            /* bfc00280 */
    move    k0, ra    #save ra
    la    a0, v280_msg
    bal    stringserial
    nop
    b    exc_common        // print the CP0 register's infomation

/* Cache error handler */
    .align    8            /* bfc00300 */
    PRINTSTR("\r\nPANIC! Unexpected Cache Error exception! ")
    mfc0    a0, COP_0_CACHE_ERR
    bal    hexserial
    nop
    b    exc_common

/* General exception handler */
    .align    7            /* bfc00380 */
    move    k0, ra    #save ra
    la    a0, v380_msg
    bal    stringserial
    nop
    b    exc_common

    .align    8            /* bfc00400 */
    move    k0, ra    #save ra
    la    a0, v400_msg
    bal    stringserial
    nop

/* when the exception occurs, do this code to present the CP0 register's content */
exc_common:
    PRINTSTR("\r\nCAUSE=")
    mfc0    a0, COP_0_CAUSE_REG
    bal    hexserial
    nop
    PRINTSTR("\r\nSTATUS=")
    mfc0    a0, COP_0_STATUS_REG
    bal    hexserial
    nop
    PRINTSTR("\r\nERRORPC=")
    mfc0    a0, COP_0_ERROR_PC
    bal    hexserial
    nop
    PRINTSTR("\r\nEPC=")
    mfc0    a0, COP_0_EXC_PC
    bal    hexserial
    nop
    PRINTSTR("\r\nBADVADDR=")
    mfc0    a0, COP_0_BAD_VADDR
    bal    hexserial
    nop
    PRINTSTR("\r\nRA=")
    move    a0, k0
    bal    hexserial
    nop

//    b    ext_map_and_reboot
    nop

    /* control the distribution of the code, here we insert a bank 256 Bytes. */
    .align 8    
        nop

    /* handler name table */
    .align 8
    .word read
    .word write
    .word open
    .word close
    .word nullfunction
    .word printf
    .word vsprintf
    .word nullfunction
    .word nullfunction
    .word getenv
    .word nullfunction
    .word nullfunction
    .word nullfunction
    .word nullfunction

3.3 初始化内存

让我们看看 locate 标号之后的代码是些什么

/*
 *  We get here from executing a bal to get the PC value of the current execute
 *  location into ra. Check to see if we run from ROM or if this is ramloaded.
 */
locate:
    la        s0, start                
    subu    s0, ra, s0                
    and        s0, 0xffff0000            

    li        t0, SR_BOOT_EXC_VEC
    mtc0    t0, COP_0_STATUS_REG
    mtc0    zero, COP_0_CAUSE_REG    

    .set noreorder
    /* the varible bonito is register s4, BONITO_REG_BASE is 0x1fe00000 */
     li        bonito, PHYS_TO_UNCACHED(BONITO_REG_BASE)  

    bal    1f    
    nop            /* now the value of ra is 0xbfc00xxx */

    /* bonito endianess */
    BONITO_BIC(BONITO_BONPONCFG, BONITO_BONPONCFG_CPUBIGEND)
    BONITO_BIC(BONITO_BONGENCFG, BONITO_BONGENCFG_BYTESWAP|BONITO_BONGENCFG_MSTRBYTESWAP)
    BONITO_BIS(BONITO_BONPONCFG, BONITO_BONPONCFG_IS_ARBITER)

    /*
     * In certain situations it is possible for the Bonito ASIC
     * to come up with the PCI registers uninitialised, so do them here
     */
    BONITO_INIT(BONITO_PCICLASS,(PCI_CLASS_BRIDGE << PCI_CLASS_SHIFT) | (PCI_SUBCLASS_BRIDGE_HOST << PCI_SUBCLASS_SHIFT))
    BONITO_INIT(BONITO_PCICMD, BONITO_PCICMD_PERR_CLR | BONITO_PCICMD_SERR_CLR | BONITO_PCICMD_MABORT_CLR | BONITO_PCICMD_MTABORT_CLR | BONITO_PCICMD_TABORT_CLR | BONITO_PCICMD_MPERR_CLR )
    //BONITO_INIT(BONITO_PCILTIMER, 0)
    BONITO_INIT(BONITO_PCILTIMER, 255)
    BONITO_INIT(BONITO_PCIBASE0, 0)
    BONITO_INIT(BONITO_PCIBASE1, 0)
    BONITO_INIT(BONITO_PCIBASE2, 0)
    BONITO_INIT(BONITO_PCIEXPRBASE, 0)
    BONITO_INIT(BONITO_PCIINT, 0)

    BONITO_BIS(BONITO_PCICMD, BONITO_PCICMD_PERRRESPEN)

    BONITO_BIS(BONITO_PCICMD, PCI_COMMAND_IO_ENABLE|PCI_COMMAND_MEM_ENABLE|PCI_COMMAND_MASTER_ENABLE)

    BONITO_BIC(BONITO_BONGENCFG, 0x80)  #½ûÖ¹iobc

    #BONITO_BIS(BONITO_BONGENCFG, BONITO_BONGENCFG_BUSERREN)

    /* Set debug mode */
    BONITO_BIS(BONITO_BONGENCFG, BONITO_BONGENCFG_DEBUGMODE)

    /******** added to init southbridge*/
#ifdef VGA_NOTEBOOK_V2
    ISA_BRMW_INIT(0,0x74,0xeb,0x0)
    ISA_BRMW_INIT(0,0x75,0xff,0x20)
    ISABWWR_INIT(4,0x48,0xb000)
    ISABBWR_INIT(4,0x41,0x80)

    RMW_INIT(MOD_W,(PCI_IO_SPACE+0xb04c),0xffffffdf,0x0)
#endif

    // SouthBridge settings

    /* Set the SMB base address */
    ISABWWR_INIT(4, SMBUS_IO_BASE_ADDR, SMBUS_IO_BASE_VALUE | 0x1)
    /* enable the host controller */
    ISABHWR_INIT(4, SMBUS_HOST_CONFIG_ADDR, SMBUS_HOST_CONFIG_ENABLE_BIT)
    /* enable the SMB IO ports */
    ISABBWR_INIT(4, PCI_COMMAND_STATUS_REG, PCI_COMMAND_IO_ENABLE)

    ISARD_INIT(CTC_PORT+PT_CONTROL)

    /* program i8254 ISA refresh counter */
    ISAWR_INIT(CTC_PORT+PT_CONTROL,PTCW_SC(PT_REFRESH)|PTCW_16B|PTCW_MODE(MODE_RG))
    ISAWR_INIT(CTC_PORT+PT_REFRESH, ISAREFRESH & 0xff)
    ISAWR_INIT(CTC_PORT+PT_REFRESH, ISAREFRESH >> 8)

    EXIT_INIT(0)

1:    
    move a0, ra            /* now the value of ra is 0xbfc00xxx */
reginit:            
    lw    t3, Init_Op(a0)
    lw    t0, Init_A0(a0)        /* Init_A0 is 4 */
    and    t4, t3, OP_MASK        /* OP_MASK is 0x000000fc, to keep 4 bytes aligned */

    /* 
     * EXIT(STATUS) 
     */
    bne    t4, OP_EXIT, 8f        /* OP_EXIT is 0x00000000 */
    nop
    move v0, t0                /* now v0 is the content of 4 bytes offset from 0xbfc000xx */
    b    .done
    nop

    /* 
     * DELAY(CYCLES) 
     */
8:    bne    t4, OP_DELAY, 8f    /* OP_DELAY is 0x00000008 */
    nop
1:    bnez    t0,1b            /* t0 不等于 0就在这死循环 */
    subu    t0,1
    b    .next
    nop    

    /*  
     * READ(ADDR) 
     */
8:    bne    t4, OP_RD, 8f        /* OP_RD is 0x00000010 */
    nop
    and    t4, t3, MOD_MASK    /* MOD_MASK is 0x00000003 */

    bne    t4, MOD_B, 1f        /* MOD_B is 0x00000000 ??? why not 0x01 or 0x03 */
    nop
    lbu    t5, 0(t0)
    b    .next
    nop
1:    bne    t4, MOD_H, 1f        /* MOD_H is 0x00000001 ??? why not 0x02 */
    nop
    lhu    t5, 0(t0)
    b    .next
    nop
1:    bne    t4, MOD_W, 1f        /* MOD_H is 0x00000002 ??? why not 0x00 */
    nop
#if __mips64
    lwu    t5, 0(t0)
#else 
    lw    t5, 0(t0)
#endif
    b    .next
    nop
1:    
#if __mips64
    lw    t5,0(t0)
    b    .next
    nop
#else
    b    .fatal
    nop
#endif

    /* 
     * WRITE(ADDR,VAL) 
     */
8:    bne    t4, OP_WR, 8f        /* OP_WR is 0x00000014 */
    nop
    lw    t1, Init_A1(a0)            /* Init_A1 is 8 */
    and    t4, t3, MOD_MASK    /* MOD_MASK is 0x00000003 */

    bne    t4, MOD_B, 1f
    nop
    sb    t1, 0(t0)
    b    .next
    nop
1:    bne    t4,MOD_H,1f
    nop
    sh    t1,0(t0)
    b    .next
    nop
1:    bne    t4,MOD_W,1f
    nop
    sw    t1,0(t0)
    b    .next
    nop

1:    
#if __mips64
    sd    t1,0(t0)
    b    .next
    nop
#else
    b    .fatal
    nop
#endif

    /* 
     * RMW(ADDR,AND,OR) 
     */
8:    bne    t4,OP_RMW,8f
    nop
    lw    t1,Init_A1(a0)
    lw    t2,Init_A2(a0)
    and    t4,t3,MOD_MASK

    bne    t4,MOD_B,1f
    nop
    lbu    t4,0(t0)
    and    t4,t1
    or    t4,t2
    sb    t4,0(t0)
    b    .next
    nop
1:    bne    t4,MOD_H,1f
    nop
    lhu    t4,0(t0)
    and    t4,t1
    or    t4,t2
    sh    t4,0(t0)
    b    .next
    nop
1:    bne    t4,MOD_W,1f
    nop
    lw    t4,0(t0)
    and    t4,t1
    or    t4,t2
    sw    t4,0(t0)
    b    .next
    nop

1:        
#if __mips64
    ld    t4,0(t0)
    and    t4,t1
    or    t4,t2
    sd    t4,0(t0)
    b    .next
    nop
#else    
    b    .fatal
    nop
#endif

    /* 
     * WAIT(ADDR,MASK,VAL) 
     */
8:    bne    t4,OP_WAIT,8f
    nop
    lw    t1,Init_A1(a0)
    lw    t2,Init_A2(a0)
    and    t4,t3,MOD_MASK

    bne    t4,MOD_B,1f
    nop
3:    lbu    t4,0(t0)
    and    t4,t1
    bne    t4,t2,3b
    nop
    b    .next
    nop
1:    bne    t4,MOD_H,1f
    nop
3:    lhu    t4,0(t0)
    and    t4,t1
    bne    t4,t2,3b
    nop
    b    .next
    nop
1:    bne    t4,MOD_W,1f
    nop
3:    lw    t4,0(t0)
    and    t4,t1
    bne    t4,t2,3b
    nop
    b    .next
    nop
1:        
#if __mips64
3:    ld    t4,0(t0)
    and    t4,t1
    bne    t4,t2,3b
    nop
    b    .next
    nop
#else    
    b    .fatal    
    nop
#endif

.next:    
    addu    a0, Init_Size        /* Init_Size is 16 */
    b    reginit                /* a big repeatation */
    nop    

8:
.fatal:    
    b .done
    nop

    bal stuck                /* these two sentences seem been ignored */
    nop

=====================        
解释:
locate:
    la        s0, start                
    subu    s0, ra, s0                
    and        s0, 0xffff0000        
此时,ra 中的地址值是前面 uncached 标号的地址,第二句作用是计算前面跳转时已运行过的代码的长度,最后一句把零头截掉。
这段代码是为了访问数据,因为这段汇编在Rom执行,而编译出来的数据段在 0x8002xxxx,
为了能够访问数据段的数据,需要进行一个地址的修正,s0 正是起到这种修正的目的。

    li        t0, SR_BOOT_EXC_VEC
    mtc0    t0, COP_0_STATUS_REG
    mtc0    zero, COP_0_CAUSE_REG    
为保险起见,再清理一遍配置寄存器

    .set noreorder
    /* the varible bonito is register s4, BONITO_REG_BASE is 0x1fe00000 */
     li        bonito, PHYS_TO_UNCACHED(BONITO_REG_BASE)  

    bal    1f    
    nop            /* now the value of ra is 0xbfc00xxx */
将 BONITO_REG_BASE 的物理地址值保存到 s4 寄存器
(通过映射到未经缓存的地址空间里,龙芯 CPU 访问外部空间,只能用映射后的地址),
然后跳转到后面1标号处执行。

1:    move a0, ra        /* now the value of ra is 0xbfc00xxx */
reginit:            /* local name */
    lw    t3, Init_Op(a0)
    lw    t0, Init_A0(a0)        // Init_A0 is 4
    and    t4, t3, OP_MASK        // OP_MASK is 0x000000fc, to keep 4 bytes aligned
在1标号的地方,取跳转时压入的RA寄存器的值,然后通过寄存器相对寻址的方式,取得跳转指令后面保存的参数,并保存到t3, t0寄存器。

上句说的就是这些参数
    /* bonito endianess */
    BONITO_BIC(BONITO_BONPONCFG, BONITO_BONPONCFG_CPUBIGEND)
    BONITO_BIC(BONITO_BONGENCFG, BONITO_BONGENCFG_BYTESWAP|BONITO_BONGENCFG_MSTRBYTESWAP)
    BONITO_BIS(BONITO_BONPONCFG, BONITO_BONPONCFG_IS_ARBITER)

    /*
     * In certain situations it is possible for the Bonito ASIC
     * to come up with the PCI registers uninitialised, so do them here
     */
    BONITO_INIT(BONITO_PCICLASS,(PCI_CLASS_BRIDGE << PCI_CLASS_SHIFT) | (PCI_SUBCLASS_BRIDGE_HOST << PCI_SUBCLASS_SHIFT))
    BONITO_INIT(BONITO_PCICMD, BONITO_PCICMD_PERR_CLR | BONITO_PCICMD_SERR_CLR | BONITO_PCICMD_MABORT_CLR | BONITO_PCICMD_MTABORT_CLR | BONITO_PCICMD_TABORT_CLR | BONITO_PCICMD_MPERR_CLR )
    //BONITO_INIT(BONITO_PCILTIMER, 0)
    BONITO_INIT(BONITO_PCILTIMER, 255)
    BONITO_INIT(BONITO_PCIBASE0, 0)
    BONITO_INIT(BONITO_PCIBASE1, 0)
    BONITO_INIT(BONITO_PCIBASE2, 0)
    BONITO_INIT(BONITO_PCIEXPRBASE, 0)
    BONITO_INIT(BONITO_PCIINT, 0)

    BONITO_BIS(BONITO_PCICMD, BONITO_PCICMD_PERRRESPEN)

    BONITO_BIS(BONITO_PCICMD, PCI_COMMAND_IO_ENABLE|PCI_COMMAND_MEM_ENABLE|PCI_COMMAND_MASTER_ENABLE)

    BONITO_BIC(BONITO_BONGENCFG, 0x80)  #½ûÖ¹iobc

    #BONITO_BIS(BONITO_BONGENCFG, BONITO_BONGENCFG_BUSERREN)

    /* Set debug mode */
    BONITO_BIS(BONITO_BONGENCFG, BONITO_BONGENCFG_DEBUGMODE)

    /******** added to init southbridge*/
#ifdef VGA_NOTEBOOK_V2
    ISA_BRMW_INIT(0,0x74,0xeb,0x0)
    ISA_BRMW_INIT(0,0x75,0xff,0x20)
    ISABWWR_INIT(4,0x48,0xb000)
    ISABBWR_INIT(4,0x41,0x80)

    RMW_INIT(MOD_W,(PCI_IO_SPACE+0xb04c),0xffffffdf,0x0)
#endif

    // SouthBridge settings

    /* Set the SMB base address */
    ISABWWR_INIT(4, SMBUS_IO_BASE_ADDR, SMBUS_IO_BASE_VALUE | 0x1)
    /* enable the host controller */
    ISABHWR_INIT(4, SMBUS_HOST_CONFIG_ADDR, SMBUS_HOST_CONFIG_ENABLE_BIT)
    /* enable the SMB IO ports */
    ISABBWR_INIT(4, PCI_COMMAND_STATUS_REG, PCI_COMMAND_IO_ENABLE)

    ISARD_INIT(CTC_PORT+PT_CONTROL)

    /* program i8254 ISA refresh counter */
    ISAWR_INIT(CTC_PORT+PT_CONTROL,PTCW_SC(PT_REFRESH)|PTCW_16B|PTCW_MODE(MODE_RG))
    ISAWR_INIT(CTC_PORT+PT_REFRESH, ISAREFRESH & 0xff)
    ISAWR_INIT(CTC_PORT+PT_REFRESH, ISAREFRESH >> 8)

        EXIT_INIT(0)
这些宏实际上不是语句,看定义后就知道它们只是定义了一些数据参数,在ROM中占据了一定的长度。

    /* 
     * EXIT(STATUS) 
     */
    bne    t4, OP_EXIT, 8f        // OP_EXIT is 0x00000000
    nop
    move v0, t0                // now v0 is the content of 4 bytes offset from 0xbfc000xx
    b    .done
    nop
接着就运行 
bne t4, OP_EXIT, 8f
这句了,在这里做是否初始化寄存器完成的判断,如果没有完成,就会跳到后面8标号处运行,然后经历一系列的设置(后面接着的那片代码)
DELAY(CYCLES) 
READ(ADDR) 
WRITE(ADDR,VAL) RMW(ADDR,AND,OR) 
WAIT(ADDR,MASK,VAL) 
后,直到 OP_EXIT 标志出现,才退出这个设置循环。看到前面有一行 EXIT_INIT(0),表示那个参数数据段结束了,它的宏定义如下:
#define EXIT_INIT(status) .word OP_EXIT, (status); .word 0,0
所以在最后一项的数据记录被读取后,总是能退出这个初始化循环的,接着就会跳到.done这个标号里运行。

不过,这段代码到底是要设置什么?由DELAY,READ,WRITE,RMW,WAIT 这些符号所标示的代码段实现其相应的功能没有?我还不清楚。

3.4 内存地址修正

la    s0, start            
subu    s0, ra, s0
and    s0, 0xffff0000 

这段代码是为了访问数据,因为这段汇编在Rom执行,而编译出来的数据段在0x8002xxxx,为了能够访问数据段的数据,需要进行一个地址的修正,s0这是起到这种修正的目的。
la      v0, initmips
jalr    v0
nop

从此代码便到内存中间去了,从这开始因为可以读写内存,所以有了栈,故可以用C的代码了,所以以后的程序便是C代码了.

3.5 初始化北桥和串口

接着看下面一段代码

.done:    
    bal superio_init        /* initialize the southbridge config register */
    nop

    bal    initserial            /* initialize the output of serial port, after this step */
    nop                        /* pmon can output some infomations from COM port*/

    PRINTSTR("\r\nPMON2000 MIPS Initializing. Standby...\r\n")

    /* begin to check some config registers on CP0 */
    PRINTSTR("ERRORPC=")
    mfc0    a0, COP_0_ERROR_PC
    bal    hexserial
    nop

    PRINTSTR(" CONFIG=")
    mfc0    a0, COP_0_CONFIG
    bal    hexserial
    nop
    PRINTSTR("\r\n")

    PRINTSTR(" PRID=")
    mfc0    a0, COP_0_PRID
    bal    hexserial
    nop
    PRINTSTR("\r\n")

=====================
解释:
在这段程序里,主要做了两件大事情,一是初始化南桥芯片VIA686B,一是初始化串口输出。
初始化VIA686B是调用子函数superio_init 实现的。初始化串口是调用子函数initserial实现的。
为了尽快地从串口输出调试信息,所以要先初始化VIA686B芯片,才能输出信息出来。
由于 VIA686B芯片包括所有外面接口的功能,比如串口, PS2,USB,并口,还有软盘等等。
只要能从串口输出字符,就已经是成功的第一步了。
在嵌入式的软件开发中,调试软件是最难的,只能根据芯片的管脚电平,或者串口发些调信息出来。
使用管脚调试,最简单的办法,就是加一个指示灯,这也叫“点灯大法”。
只要串口能输出字符串后,使用串口调试就成为基本的方法了。
后面,输出三个CP0寄存器的值,第一个寄存器是出错信息,第二个寄存器是CP0配置信息,第三个寄存器是CP0处理器的ID信息。
=====================

3.6 初始化E2PROM,并且读取E2PROM参数

下面一段代码从内存条上的SPD(eeprom)中读取内存参数,并且初始化内存窗口。这段代码放到另一篇文章中专门讲解吧。这里就不多说了。

/* 
     * Now determine DRAM configuration and size by
     * reading the I2C EEROM (SPD) on the DIMMS (DDR)
     */
    PRINTSTR("DIMM read\r\n")

    /* only one memory slot, slave address is 10100001b */
    li  a1, 0x0
1:
    li    a0, 0xa1    /* a0: slave address, a1: reg index to read */
    bal    i2cread
    nop

        /* save a1 */
    move t1, a1

    /* print */
    move a0, v0
    bal  hexserial
    nop

    PRINTSTR("\r\n")    

        /* restore a1 */
    move  a1,t1
    addiu a1,a1,1
    li   v0, 0x20
    bleu  a1, v0, 1b        /* repeat for 32 times */
    nop

    li    msize, 0            /* msize is register s2 */
    /* set some parameters for DDR333
        rank number and DDR type field will be filled later
        to check: fix TCAS?
    */
    li    sdCfg, 0x341043df        /* sdCfg is register s6 */

    /* read DIMM memory type (must be DDRAM) */
#if 0
    li    a0,0xa1
    li    a1,2
    bal    i2cread
    nop
    bne    v0,7,.nodimm
    nop
    PRINTSTR("read memory type\r\n") 
#endif

    /* read DIMM number of rows */
    li    a0, 0xa1
    li    a1, 3
    bal    i2cread
    nop    
    move    a0, v0        // v0 is the return value register
    subu    v0, 12
    move    s1, v0        // save for later use

    bgtu    v0, 2, .nodimm        // if v0 > 2 then jump to .nodimm
    nop
    PRINTSTR("read number of rows\r\n")

2:    /* read DIMM number of cols */
    li    a0, 0xa1
    li    a1, 4
    bal    i2cread
    nop

    subu    v0, 8                // v0 saved the return value
    bgtu    v0, 4, .nodimm
    nop

    // read and check ddr type, the combination of t1 and v0 represents a ddr type
    move    t1, s1
    bne    t1, 0, 10f
    nop
    bne    v0, 2, 20f
    nop
    li    v0, 0
    b    .ddrtype
    nop
20:    bne    v0, 1, 21f
    nop
    li    v0, 1
    b    .ddrtype
    nop
21:    bne    v0, 0, 22f
    nop
    li    v0, 2
    b    .ddrtype
    nop
22:    bne    v0, 3, 33f
    nop
    li    v0, 3
    b    .ddrtype
    nop
10:    bne    t1, 1, 11f
    nop
    bne    v0, 3, 20f
    nop
    li    v0, 4
    b    .ddrtype
    nop
20:    bne    v0, 2, 21f
    nop
    li    v0, 5
    b    .ddrtype
    nop
21:    bne    v0, 1, 22f
    nop
    li    v0, 6
    b    .ddrtype
    nop
22:    bne    v0, 4, 33f
    nop
    li    v0, 7
    b    .ddrtype
    nop
11:    bne    t1, 2, 33f
    nop
    bne    v0, 4, 20f
    nop
    li    v0, 8
    b    .ddrtype
    nop
20:    bne    v0, 3, 21f
    nop
    li    v0, 9
    b    .ddrtype
    nop
21:    bne    v0, 2, 33f
    nop
    li    v0, 10
    b    .ddrtype
    nop
33:    PRINTSTR("DDR type not supported!\r\n");
34:    b    34b
    nop

.ddrtype:
    #bit 25:22 is DDR type field
    sll    v0, 22 
    and    v0, 0x03c00000
    or    sdCfg, v0

    /* read DIMM memory size per side */
    li    a0, 0xa1
    li    a1, 31
    bal    i2cread
    nop
    beqz    v0,.nodimm
    nop
    sll    tmpsize,v0,22        # multiply by 4M
    PRINTSTR("read memory size per side\r\n") 

2:    /* read DIMM number of blocks-per-ddrram */
    li    a1,17
    bal    i2cread
    nop
    beq    v0,2,2f
    nop
    bne    v0,4,.nodimm
    nop
    PRINTSTR("read blocks per ddrram\r\n")

2:    /* read DIMM number of sides (banks) */
    li    a1,5
    bal    i2cread
    nop
    beq    v0,1,2f
    nop
    bne    v0,2,.nodimm
    nop
    sll    tmpsize,1    # msize *= 2    
    or  sdCfg, 0x1<<27
    PRINTSTR("read number of sides\r\n") 

2:    /* read DIMM width */
    li    a1,6
    bal    i2cread
    nop
    bleu    v0,36,2f
    nop
    bgtu    v0,72,.nodimm
    nop
    PRINTSTR("read width\r\n") 

2:    addu    msize,tmpsize
    b    2f
    nop    

.nodimm:
    move    dbg,a0        // dbg is s5
    PRINTSTR ("\r\nNo DIMM in slot ")
    move    a0,dbg
    bal    hexserial
    nop
    PRINTSTR("\r\n")
    move    a0,dbg
    #li  msize,0x10000000
    #li    sdCfg,0x3d9043df    #~133MHz
    li  msize,0x20000000
    li    sdCfg,0x3d5043df     #~133MHz

2:
    PRINTSTR("DIMM SIZE=")
    move    a0,msize
    bal    hexserial
    nop
    PRINTSTR("\r\n")

    li    t0, 0xbff00008
    sd    sdCfg, 0(t0)
    nop
    nop

    /* (uint32_t *)0xbfe00040 = 0x80000000
     * means only address below 1G will be sent to CPU
     */
    lui    t0, 0xbfe0
    li    t1, 0x80000000
    sw    t1, 0x40(t0)
    nop

    #### gx 2006-03-17: mode ####
    #li    t1,0x20
    li    t1,0x28
    li    t0, 0xbff00000
    sd    t1,0(t0)
    nop
    li    t1,0x0
    li    t0, 0xbff00000
    sd    t1,0x30(t0)
    nop

    ##fixed base address reg##
    sd    zero, 0x10(t0)
    nop
    lui    t1,0x2000
    sd    t1,0x20(t0)
    nop

    li      t1, 0x10000000
        blt     msize, t1, 1f
    nop

    ####bigger than 256MB####
    sd    t1, 0x18(t0)
    nop
    move    a0, msize
    subu    a0, t1
    nop
    nop
    nop
    sd    a0, 0x28(t0)
    nop
    b    2f

1:
    nop
    nop
    sd    msize, 0x18(t0)
    nop
    nop
    nop
    sd    zero, 0x28(t0)
    nop
    nop
    nop

2:
    PRINTSTR("sdcfg=");
    move    a0,sdCfg
    bal    hexserial
    nop
    PRINTSTR("\r\n");
    PRINTSTR("msize=");
    move    a0,msize
    bal    hexserial
    nop
    PRINTSTR("\r\n")

skipdimm:

    li    t1,0        # accumulate pcimembasecfg settings

    /* set bar0 mask and translation to point to SDRAM */
    sub    t0,msize,1
    not    t0
    srl    t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE0_MASK_SHIFT
    and    t0,BONITO_PCIMEMBASECFG_MEMBASE0_MASK
    or    t1,t0

    li    t0,0x00000000
    srl    t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE0_TRANS_SHIFT
    and    t0,BONITO_PCIMEMBASECFG_MEMBASE0_TRANS
    or    t1,t0
    or    t1,BONITO_PCIMEMBASECFG_MEMBASE0_CACHED

    /* set bar1 to minimum size to conserve PCI space */
    li    t0, ~0
    srl    t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE1_MASK_SHIFT
    and    t0,BONITO_PCIMEMBASECFG_MEMBASE1_MASK
    or    t1,t0

    li    t0,0x00000000
    srl    t0,BONITO_PCIMEMBASECFG_ASHIFT-BONITO_PCIMEMBASECFG_MEMBASE1_TRANS_SHIFT
    and    t0,BONITO_PCIMEMBASECFG_MEMBASE1_TRANS
    or    t1,t0
    or    t1,BONITO_PCIMEMBASECFG_MEMBASE1_CACHED

    sw    t1,BONITO_PCIMEMBASECFG(bonito)

    /* enable configuration cycles now */
    lw    t0,BONITO_BONPONCFG(bonito)
    and    t0,~BONITO_BONPONCFG_CONFIG_DIS
    sw    t0,BONITO_BONPONCFG(bonito)

    PRINTSTR("Init SDRAM Done!\r\n");

3.7 初始化Cache

下面这段是缓存配置的代码

/*
 *  Reset and initialize caches to a known state.
 */
#define IndexStoreTagI    0x08
#define IndexStoreTagD    0x09
#define IndexStoreTagS    0x0b
#define IndexStoreTagT    0x0a
#define FillI        0x14

/*
 *  caches config register bits.
 */
#define CF_7_SE         (1 << 3)        /* Secondary cache enable */
#define CF_7_SC         (1 << 31)       /* Secondary cache not present */
#define CF_7_TE         (1 << 12)       /* Tertiary cache enable */
#define CF_7_TC         (1 << 17)       /* Tertiary cache not present */
#define CF_7_TS         (3 << 20)       /* Tertiary cache size */
#define CF_7_TS_AL      20              /* Shift to align */
#define NOP8 nop;nop;nop;nop;nop;nop;nop;nop
do_caches:
    TTYDBG("Sizing caches...\r\n");

    mfc0    t3, COP_0_CONFIG    /* t3 = original config */
    and    t3, 0xffffeff0        /* Make sure coherency is OK */

    and    t3, ~(CF_7_TE|CF_7_SE|CF_7_TC|CF_7_SC)  /* disable L2/L3 cache */
    mtc0    t3, COP_0_CONFIG

    li    t2, 4096

    srl    t1, t3, 9
    and    t1, 3
    sllv    s3, t2, t1        /* s3 = I cache size */

#ifdef CONFIG_CACHE_64K_4WAY 
        sll     s3,2
#endif

    and    t1, t3, 0x20
    srl    t1, t1, 1
    addu    s4, t1, 16        /* s4 = I cache line size */

    srl    t1, t3, 6
    and    t1, 3
    sllv    s5, t2, t1        /* s5 = D cache size */

#ifdef CONFIG_CACHE_64K_4WAY
        sll     s5,2
#endif

    and    t1, t3, 0x10
    addu    s6, t1, 16        /* s6 = D cache line size */
    TTYDBG("Init caches...\r\n")

    li    s7, 0                   /* no L2 cache */
    li    s8, 0                   /* no L3 cache */

#if 0
        mfc0    a0, COP_0_PRID
        li      a1, 0x6301
        bne     a0,a1,1f
        nop
#endif
    TTYDBG("godson2 caches found\r\n")
        bal     godson2_cache_init
        nop
#####xuhua########open cp1 
#if 1
        mfc0   t0,COP_0_STATUS_REG
          and    t0,0xdbffffff
         or     t0,t0,0x24000000 
        mtc0   t0,COP_0_STATUS_REG
#endif
#################

    /* close L2 cache */
    li      a0, 0xbfe00164
        sw      zero, 0(a0);

        mfc0   a0,COP_0_CONFIG
        and    a0,a0,~((1<<12) | 3)
    or     a0,a0,2
        mtc0   a0,COP_0_CONFIG

#ifdef DEBUG_LOCORE
    TTYDBG("Init caches done, cfg = ")
    mfc0    a0, COP_0_CONFIG
    bal    hexserial
    nop
    TTYDBG("\r\n\r\n")
#endif

3.8 PMON自身从ROM中拷贝到RAM(在Cache之后)

下面这段代码是把PMON自身从ROM中拷贝到RAM中去

// copy self code segment
    TTYDBG("Copy PMON to execute location...\r\n")
#ifdef DEBUG_LOCORE
    TTYDBG("  start = 0x")
    la    a0, start
    bal    hexserial
    nop
    TTYDBG("\r\n  s0 = 0x")
    move    a0, s0
    bal    hexserial
    nop
    TTYDBG("\r\n")
#endif
    la    a0, start
    li    a1, 0xbfc00000
    la    a2, _edata
        or      a0, 0xa0000000
        or      a2, 0xa0000000
    subu    t1, a2, a0
    srl    t1, t1, 2

    move    t0, a0
    move    t1, a1
    move    t2, a2

    /* copy text section */

1:    and    t3,t0,0x0000ffff
    bnez    t3,2f
    nop
    move    a0,t0
    bal    hexserial
    nop
    li    a0,'\r'
    bal     tgt_putchar
    nop
2:    lw    t3, 0(t1)
    nop
    sw    t3, 0(t0)
    addu    t0, 4
    addu    t1, 4
    bne    t2, t0, 1b
    nop

    PRINTSTR("\ncopy text section done.\r\n")

    /* Clear BSS */
    la    a0, _edata
    la    a2, _end
2:    sw    zero, 0(a0)
    bne    a2, a0, 2b
    addu    a0, 4

    TTYDBG("Copy PMON to execute location done.\r\n")

3.9 汇编到C语言

下面这段代码从汇编世界跳到C世界中去了。

TTYDBG("sp=");
    move a0, sp
    bal    hexserial
    nop

#if 1
        mfc0   a0,COP_0_CONFIG
        and    a0,a0,0xfffffff8
        or     a0,a0,0x3
        mtc0   a0,COP_0_CONFIG
#endif

    li    a0, 4096*1024
    sw    a0, CpuTertiaryCacheSize /* Set L3 cache size */
    move    a0,msize
    srl    a0,20

        /* pass pointer to kseg1 tgt_putchar */
        la  a1, tgt_putchar
    addu a1,a1,s0            // la    s0,start
                            // subu    s0,ra,s0            ??? ra is the returning address
                            // and    s0,0xffff0000        ??? now what does s0 mean?

        la  a2, stringserial
    addu a2,a2,s0

    la    v0, initmips        // further ENTRY of PMON
    jalr    v0
    nop

它山之石:

龙芯相关 - 心映真的空间 (wikidot.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/464700.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[游戏开发][UE5.3]GAS学习心得

GAS(GameplayAbilitySystem) UE提供的一套技能框架&#xff0c;这个框架也不是万能的&#xff0c;甚至各个部件你要进行封装开发&#xff0c;但这也比你从头写一套技能框架要容易很多。 GAS功能极其强大&#xff0c;所以它是一个庞大的系统&#xff0c;如果想运用得当&#x…

深度学习pytorch——基本运算(持续更新)

基本运算——加、减、乘、除 建议直接使用运算符&#xff0c;函数和运算符的效果相同 代码演示&#xff1a; #%% # 加减乘除 a torch.rand(3,4) b torch.rand(4) # 这里a、b可以相加&#xff0c;别忘了pytorch的broadcast机制 print(ab) print(torch.add(a,b)) print(torc…

MySQL中的索引失效情况介绍

MySQL中的索引是提高查询性能的重要工具。然而&#xff0c;在某些情况下&#xff0c;索引可能无法发挥作用&#xff0c;甚至导致查询性能下降。在本教程中&#xff0c;我们将探讨MySQL中常见的索引失效情况&#xff0c;以及它们的特点和简单的例子。 1. **索引失效的情况** …

代码算法训练营day9 | 28. 实现 strStr() 、459.重复的子字符串

day9&#xff1a; 28. 实现 strStr()KMP的主要应用&#xff1a;什么是前缀表&#xff1a;前缀表是如何记录的&#xff1a; 如何计算前缀表&#xff1a;构造next数组&#xff1a;1、初始化2、处理前后缀不相同的情况3、处理前后缀相同的情况 代码&#xff1a; 459.重复的子字符串…

Python入门(三)

序列 序列是有顺序的数据集合。序列包含的一个数据被称为元素&#xff0c;序列可以由一个或多个元素组成&#xff0c;也是可以没有任何元素的空序列。 序列的类型 元组&#xff08;定值表&#xff09;&#xff1a;一旦建立&#xff0c;各个元素不可再更变&#xff0c;所以一…

Linux文件操作

pwd命令 cd命令 ls命令 mkdir命令 同时创建父子目录 cp命令 mv命令&#xff08;相当于用cp复制之后&#xff0c;把源文件删除&#xff09; 用mv命令来冲命令 rm命令 可以看到&#xff0c;我们用当前目录的文件覆盖了目标路径上的文件&#xff0c;并且目标路径中多了一个以波浪…

5 张图带你了解分布式事务 Saga 模式中的状态机

大家好&#xff0c;我是君哥。 状态机在我们的工作中应用非常广泛&#xff0c;今天聊一聊分布式事务中间件 Seata 中 Saga 模式的状态机。 1 状态机简介 状态机是一个数学模型&#xff0c;它将工作中的运行状态和流转规则抽象出来&#xff0c;可以协调相关信号来完成预先设定…

构造-析构-拷贝构造-赋值运算符重载-const成员函数

1. 类的6个默认成员函数 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么时候都不写时&#xff0c;编译器会自动生成以下6个成员函数。 默认成员函数&#xff1a;用户没有显式实现&#xff0c;编译器…

C++之deque与vector、list对比分析

一.deque讲解 对于vector和list&#xff0c;前一个是顺序表&#xff0c;后一个是带头双向循环链表&#xff0c;前面我们已经实现过&#xff0c;这里就不再讲解了&#xff0c;直接上deque了。 deque&#xff1a;双端队列 常见接口大家可以查看下面链接&#xff1a; deque - …

STM32第九节(中级篇):RCC(第一节)——时钟树讲解

目录 前言 STM32第九节&#xff08;中级篇&#xff09;&#xff1a;RCC——时钟树讲解 时钟树主系统时钟讲解 HSE时钟 HSI时钟 锁相环时钟 系统时钟 SW位控制 HCLK时钟 PCLKI时钟 PCLK2时钟 RTC时钟 MCO时钟输出 6.2.7时钟安全系统(CSS&#xff09; 小结 前言 从…

单链表操作

单链表操作 1. 链表的概念2. 链表的分类2.1.单向或者双向2.2 带头或者不带头2.3 循环或者非循环2.4 常用的链表 3. 单链表的实现3.1 单链表的打印3.2 单链表的头插3.3 单链表的尾插3.4 单链表的头删3.5 单链表的尾删3.6 单链表的查询3.7 在pos前插入数据3.8 在pos后插入数据3.9…

Linux——进程通信(一) 匿名管道

目录 前言 一、进程间通信 二、匿名管道的概念 三、匿名管道的代码实现 四、管道的四种情况 1.管道无数据&#xff0c;读端需等待 2.管道被写满&#xff0c;写端需等待 3.写端关闭&#xff0c;读端一直读取 4.读端关闭&#xff0c;写端一直写入 五、管道的特性 前言 …

不锈钢多功能电工剥线钳分线绕线剪线剥线钳剥线压线扒皮钳子

品牌&#xff1a;银隆 型号&#xff1a;089B绿色 材质&#xff1a;镍铬钢&#xff08;不锈钢&#xff09; 颜色分类&#xff1a;089B灰色,089B红色,089B绿色,089B黑色,089B橙色 功能齐集一身&#xff0c;一钳多用&#xff0c;多功能剥线钳。剥线&#xff0c;剪线&#xff…

Java-CAS 原理与 JUC 原子类

由于 JVM 的 synchronized 重量级锁涉及到操作系统&#xff08;如 Linux&#xff09; 内核态下的互斥锁&#xff08;Mutex&#xff09;的使用&#xff0c; 其线程阻塞和唤醒都涉及到进程在用户态和到内核态频繁切换&#xff0c; 导致重量级锁开销大、性能低。 而 JVM 的 synchr…

免费阅读篇 | 芒果YOLOv8改进114:上采样Dysample:顶会ICCV2023,轻量级图像增采样器,通过学习采样来学习上采样,计算资源需求小

&#x1f4a1;&#x1f680;&#x1f680;&#x1f680;本博客 改进源代码改进 适用于 YOLOv8 按步骤操作运行改进后的代码即可 该专栏完整目录链接&#xff1a; 芒果YOLOv8深度改进教程 &#x1f680;&#x1f680;&#x1f680; DySample是一个超轻量级和有效的动态上采样器…

DDos攻击如何被高防服务器有效防范?

德迅云安全-领先云安全服务与解决方案提供商 什么是DDos攻击&#xff1f; DDos攻击是一种网络攻击手段&#xff0c;旨在通过使目标系统的服务不可用或中断&#xff0c;导致无法正常使用网络服务。DDos攻击可以采取多种方式实施&#xff0c;包括洪水攻击、压力测试、UDP Flood…

HTML静态网页成品作业(HTML+CSS)——游戏战地介绍设计制作(4个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有4个页面。 二、作品演示 三、代…

关于PXIE3U18槽背板原理拓扑关系

如今IT行业日新月异&#xff0c;飞速发展&#xff0c;随之带来的是数据吞吐量的急剧升高。大数据&#xff0c;大存储将成为未来数据通信的主流&#xff0c;建立快速、大容量的数据传输通道将成为电子系统的关键。随着集成技术和互连技术的发展&#xff0c;新的串口技术&#xf…

【QT+QGIS跨平台编译】之七十七:【QGIS_Gui跨平台编译】—【错误处理:字符串错误】

文章目录 一、字符串错误二、处理方法三、涉及到的文件一、字符串错误 常量中有换行符错误:(也有const char * 到 LPCWSTR 转换的错误) 二、处理方法 需要把对应的文档用记事本打开,另存为 “带有BOM的UTF-8” 三、涉及到的文件 src\gui\qgsadvanceddigitizingdockwidge…

ClickHouse中的设置的分类

ClickHouse中的各种设置 ClickHouse中的设置有几百个&#xff0c;下面对这些设置做了一个简单的分类。
最新文章