跳转至

RISC-V 非特权级 ISA

4193 个字 22 行代码 预计阅读时间 14 分钟

Abstract

RISC-V 指令集的非特权级(用户级)部分。

参考:

RV32I 基础

寄存器

  • 一个 PC 寄存器(program counter)
  • 32 32 位寄存器(x0~x31)
    • 其中 x0 永远是 0

寄存器的常用用途如下:

寄存器 ABI 名称 用途描述 saver
x0 zero 硬件 0
x1 ra 返回地址(return address) caller
x2 sp 栈指针(stack pointer) callee
x3 gp 全局指针(global pointer)
x4 tp 线程指针(thread pointer)
x5 t0 临时变量 / 备用链接寄存器(alternate link reg) caller
x6-7 t1-2 临时变量 caller
x8 s0/fp 需要保存的寄存器 / 帧指针(frame pointer) callee
x9 s1 需要保存的寄存器 callee
x10-11 a0-1 函数参数 / 返回值 caller
x12-17 a2-7 函数参数 caller
x18-27 s2-11 需要保存的寄存器 callee
x28-31 t3-6 临时变量 caller

其中 sp s0-11 需要在函数调用前后保证一致,其它不用保证

指令格式

RV32I 4 种基础的指令格式(R/I/S/U,再根据立即数解码的不同又分出两种(B/J,总共六种指令格式

R 型指令

31 25 24 20 19 15 14 12 11 7 6 0
funct7 rs2 rs1 funct3 rd opcode

使用寄存器进行数字逻辑运算的指令格式,运算由 opcode funct3 funct7 决定,rd = rs1 op rs2(shift 类例外,它们用 rs2 位置表示移位数的立即数)

I 型指令

31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 funct3 rd opcode

使用寄存器和立即数进行数字逻辑运算,以及 load 类指令等的指令格式,运算类型等由 opcode funct3 决定,如果是 ALU 运算,则 rd = rs1 op imm

立即数是 {{20{inst[31]}}, inst[31:20]},也就是对 imm[11:0] 进行符号位扩展到 32

S 型指令

31 25 24 20 19 15 14 12 11 7 6 0
imm[11:5] rs2 rs1 funct3 imm[4:0] opcode

store 类指令,store 的大小由 funct3 决定,以变址模式进行寻址,即 rs1 = [rs2+imm]

立即数是 {{20{inst[31]}}, inst[31:25], inst[11:7]}

B 型指令

31 25 24 20 19 15 14 12 11 7 6 0
imm[12,10:5] rs2 rs1 funct3 imm[4:1,11] opcode

S 型指令分来,与之区别是立即数读取顺序不同,是所有分支类指令。是否分支由 funct3 rs1 rs2 决定

立即数是 {{19{inst[31]}}, inst[31], inst[7], inst[30:25], inst[11:8], 1'b0}

U 型指令

31 12 11 7 6 0
imm[31:12] rd opcode

LUI AUIPC,立即数都是在高 20 位,而且没有源操作数

立即数是 {inst[31:12], 12'b0}

J 型指令

31 12 11 7 6 0
imm[20,10:1,11,19:12] rd opcode

U 型指令分来,区别也是立即数读取不同,仅有 JAL 一个指令

立即数是 {{11{inst[31]}}, inst[31], inst[19:12], inst[20], inst[30:21], 1'b0}

RV32I 指令

整型计算指令

加减法指令

add R 型
31 25 24 20 19 15 14 12 11 7 6 0
0000000 rs2 rs1 000 rd 0110011
  • 指令格式:add rd, rs1, rs2
  • 指令作用:rd = rs1 + rs2
  • 注意:溢出会被忽略
sub r 型
31 25 24 20 19 15 14 12 11 7 6 0
0100000 rs2 rs1 000 rd 0110011
  • 指令格式:sub rd, rs1, rs2
  • 指令作用:rd = rs1 - rs2
  • 注意:溢出会被忽略
addi I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 000 rd 0010011
  • 指令格式:addi rd, rs1, imm
  • 指令作用:rd = rs1 + imm
  • 注意:溢出会被忽略,imm [-2048, 2047] 范围内

比较运算指令

slt r 型
31 25 24 20 19 15 14 12 11 7 6 0
0000000 rs2 rs1 010 rd 0110011
  • 指令格式:slt rd, rs1, rs2
  • 指令作用(set less than)如果 rs1 < rs2 rd = 1,否则 rd = 0
  • 注意rs1 rs2 会被视为有符号数进行比较
sltu r 型
31 25 24 20 19 15 14 12 11 7 6 0
0000000 rs2 rs1 011 rd 0110011
  • 指令格式:sltu rd, rs1, rs2
  • 指令作用(set less than unsigned)如果 rs1 < rs2 rd = 1,否则 rd = 0
  • 注意rs1 rs2 会被视为无符号数进行比较
slti I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 010 rd 0010011
  • 指令格式:slti rd, rs1, imm
  • 指令作用(set less than immediate)如果 rs1 < imm rd = 1,否则 rd = 0
  • 注意imm [-2048, 2047] 范围内,被视为有符号数进行比较
sltiu I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 011 rd 0010011
  • 指令格式:sltiu rd, rs1, imm
  • 指令作用(set less than immediate unsigned)如果 rs1 < imm rd = 1,否则 rd = 0
  • 注意imm [-2048, 2047] 范围内,rs1 imm 被视为无符号数进行比较

二进制位运算指令

and r 型
31 25 24 20 19 15 14 12 11 7 6 0
0000000 rs2 rs1 111 rd 0110011
  • 指令格式:and rd, rs1, rs2
  • 指令作用rd = rs1 & rs2 按位与
or r 型
31 25 24 20 19 15 14 12 11 7 6 0
0000000 rs2 rs1 110 rd 0110011
  • 指令格式:or rd, rs1, rs2
  • 指令作用rd = rs1 | rs2 按位或
xor r 型
31 25 24 20 19 15 14 12 11 7 6 0
0000000 rs2 rs1 100 rd 0110011
  • 指令格式:xor rd, rs1, rs2
  • 指令作用rd = rs1 ^ rs2 按位异或
andi I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 111 rd 0010011
  • 指令格式:andi rd, rs1, imm
  • 指令作用rd = rs1 & imm 按位与
  • 注意imm [-2048, 2047] 范围内,会扩展符号位
ori I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 110 rd 0010011
  • 指令格式:ori rd, rs1, imm
  • 指令作用rd = rs1 | imm 按位或
  • 注意imm [-2048, 2047] 范围内,会扩展符号位
xori I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 100 rd 0010011
  • 指令格式:xori rd, rs1, imm
  • 指令作用rd = rs1 ^ imm 按位异或
  • 注意imm [-2048, 2047] 范围内,会扩展符号位(xori rd, rs1, -1 相当于 rd = ~rs1

移位运算指令

sll r 型
31 25 24 20 19 15 14 12 11 7 6 0
0000000 rs2 rs1 001 rd 0110011
  • 指令格式:sll rd, rs1, rs2
  • 指令作用rd = rs1 << rs2[4:0] 左移(左侧丢掉,右侧补 0
  • 注意:会取 rs2 内数值的低 5 位进行运算
srl r 型
31 25 24 20 19 15 14 12 11 7 6 0
0000000 rs2 rs1 101 rd 0110011
  • 指令格式:srl rd, rs1, rs2
  • 指令作用rd = rs1 >> rs2[4:0] 逻辑右移(左侧补 0,右侧丢掉)
  • 注意:会取 rs2 内容的低 5
sra r 型
31 25 24 20 19 15 14 12 11 7 6 0
0100000 rs2 rs1 101 rd 0110011
  • 指令格式:sra rd, rs1, rs2
  • 指令作用rd = rs1 >>> rs2[4:0] 算数右移(左侧补符号位,右侧丢掉)
  • 注意:会取 rs2 内容的低 5 位进行运算
slli i 型(改)
31 25 24 20 19 15 14 12 11 7 6 0
0000000 shamt rs1 001 rd 0010011
  • 指令格式:slli rd, rs1, shamt
  • 指令作用rd = rs1 << shamt 左移(左侧丢掉,右侧补 0
  • 注意:shamt(shift amount)会编码到原来 rs2 的位置,它是一个立即数,正好有 5
srli i 型(改)
31 25 24 20 19 15 14 12 11 7 6 0
0000000 shamt rs1 101 rd 0010011
  • 指令格式:srli rd, rs1, shamt
  • 指令作用rd = rs1 >> shamt 逻辑右移(左侧补 0,右侧丢掉)
  • 注意:shamt(shift amount)会编码到原来 rs2 的位置,它是一个立即数,正好有 5
srai i 型(改)
31 25 24 20 19 15 14 12 11 7 6 0
0100000 shamt rs1 101 rd 0010011
  • 指令格式:srai rd, rs1, shamt
  • 指令作用rd = rs1 >>> shamt 算数右移(左侧补符号位,右侧丢掉)
  • 注意:shamt(shift amount)会编码到原来 rs2 的位置,它是一个立即数,正好有 5

数据加载指令

lui U 型
31 12 11 7 6 0
imm[31:12] rd 0110111
  • 指令格式:lui imm
  • 指令作用(load upper immediate)rd = imm << 12 imm 加载到 rd 的高 20
  • 注意imm 不能超过 20 位,rd 以十六进制表示就是 imm 后接三个 0
auipc U 型
31 12 11 7 6 0
imm[31:12] rd 0010111
  • 指令格式:auipc rd
  • 指令作用(add upper immediate with pc)rd = pc + imm << 12 imm 加载到高 20 位,然后加上 pc
  • 注意:常用来构建 pc 相对寻址的地址,imm 不能超过 20

控制流变化指令

jump 类无条件跳转指令

jal J 型
31 12 11 7 6 0
imm[20,10:1,11,19:12] rd 1101111
  • 指令格式:jal rd, imm
  • 指令作用(jump and link)rd = pc+4, pc = pc+imm 即将当前指令下一条指令的地址存入 rd,然后相对跳转到 imm
  • 注意imm 在汇编程序中一般用标号来指定,jal 可以跳到 ±1MiB 范围内的代码
jalr I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 000 rd 1100111
  • 指令格式:jalr rd, imm(rs1)
  • 指令作用rd = pc+4, pc = (imm+rs1) & 0xFFFFFFFE 即最低位会被设为 0
  • 注意:可以实现任意位置跳转

branch 类条件跳转指令

beq B 型
31 25 24 20 19 15 14 12 11 7 6 0
imm[12,10:5] rs2 rs1 000 imm[4:1,11] 1100011
  • 指令格式:beq rs1, rs2, imm
  • 指令作用(branch if equal)如果 rs1 == rs2,则 pc = pc+imm
  • 注意:可以跳转到 ±4KiB 范围内
bne B 型
31 25 24 20 19 15 14 12 11 7 6 0
imm[12,10:5] rs2 rs1 001 imm[4:1,11] 1100011
  • 指令格式:bne rs1, rs2, imm
  • 指令作用(branch if not equal)如果 rs1 != rs2,则 pc = pc+imm
  • 注意:可以跳转到 ±4KiB 范围内
blt B 型
31 25 24 20 19 15 14 12 11 7 6 0
imm[12,10:5] rs2 rs1 100 imm[4:1,11] 1100011
  • 指令格式:blt rs1, rs2, imm
  • 指令作用(branch if less than)如果 rs1 < rs2 pc = pc+imm
  • 注意rs1 rs2 视为有符号数进行比较,可以跳转到 ±4KiB 范围内
bge B 型
31 25 24 20 19 15 14 12 11 7 6 0
imm[12,10:5] rs2 rs1 101 imm[4:1,11] 1100011
  • 指令格式:bge rs1, rs2, imm
  • 指令作用(branch if greater than or equal)如果 rs1 >= rs2 pc = pc+imm
  • 注意rs1 rs2 视为有符号数进行比较,可以跳转到 ±4KiB 范围内
bltu B 型
31 25 24 20 19 15 14 12 11 7 6 0
imm[12,10:5] rs2 rs1 110 imm[4:1,11] 1100011
  • 指令格式:bltu rs1, rs2, imm
  • 指令作用(blt unsigned)如果 rs1 < rs2 pc = pc+imm
  • 注意rs1 rs2 视为无符号数进行比较,可以跳转到 ±4KiB 范围内
bgeu B 型
31 25 24 20 19 15 14 12 11 7 6 0
imm[12,10:5] rs2 rs1 111 imm[4:1,11] 1100011
  • 指令格式:bgeu rs1, rs2, imm
  • 指令作用(bge unsigned)如果 rs1 >= rs2 pc = pc+imm
  • 注意rs1 rs2 视为无符号数进行比较,可以跳转到 ±4KiB 范围内

装载存储指令

load 类装载指令

lb I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 000 rd 0000011
  • 指令格式:lb rd, imm(rs1)
  • 指令作用:从 rs1 + imm 处内存读取一个字节到 rd 低八位,再进行符号扩展
lh I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 001 rd 0000011
  • 指令格式:lh rd, imm(rs1)
  • 指令作用:从 rs1 + imm 处内存读取一个 16 位数到 rd 16 位,然后进行符号扩展
lw I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 010 rd 0000011
  • 指令格式:lw rd, imm(rs1)
  • 指令作用:从 rs1 + imm 处内存读取一个 32 位数到 rd
lbu I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 100 rd 0000011
  • 指令格式:lbu rd, imm(rs1)
  • 指令作用:从 rs1 + imm 处内存读取一个字节放到 rd 8 位,然后进行零扩展(高位全补 0
lhu I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 101 rd 0000011
  • 指令格式:lhu rd, imm(rs1)
  • 指令作用:从 rs1 + imm 处内存读取 16 位数存入 rd,并进行零扩展(高 16 位全为 0

store 类存储指令

sb S 型
31 25 24 20 19 15 14 12 11 7 6 0
imm[11:5] rs2 rs1 000 imm[4:0] 0100011
  • 指令格式:sb rs2, imm(rs1)
  • 指令作用:将 rs2 的低 8 位拷贝到 rs1 + imm 处内存中
sh S 型
31 25 24 20 19 15 14 12 11 7 6 0
imm[11:5] rs2 rs1 001 imm[4:0] 0100011
  • 指令格式:sh rs2, imm(rs1)
  • 指令作用:将 rs2 的低 16 位拷贝到 rs1 + imm 处内存中
sw S 型
31 25 24 20 19 15 14 12 11 7 6 0
imm[11:5] rs2 rs1 010 imm[4:0] 0100011
  • 指令格式:sw rs2, imm(rs1)
  • 指令作用:将 rs232 位)拷贝到 rs1 + imm 处内存中

环境调用和断点指令

ecall I 型
31 20 19 15 14 12 11 7 6 0
000000000000 00000 000 00000 1110011
  • 指令格式:ecall
  • 指令作用:请求环境调用(类似 syscallEEI 会定义相关参数规范(一般通过寄存器传参)
ebreak I 型
31 20 19 15 14 12 11 7 6 0
000000000001 00000 000 00000 1110011
  • 指令格式:ebreak
  • 指令作用:将控制流转到调试环境

汇编伪指令

前面的是 RV32I 的所有被编码的指令,下面是可以在 RV32I 汇编程序中写的伪指令,它们将被编译器编译为第二列中的实际指令

伪指令 实际指令 意义
la/lla rd, symbol auipc rd, delta[31 : 12] + delta[11]
addi rd, rd, delta[11:0]
加载绝对地址
delta = symbol - pc
l{b|h|w} rd, symbol auipc rd, delta[31 : 12] + delta[11]
l{b|h|w} rd, delta[11:0](rd)
加载全局变量
s{b|h|w} rd, symbol, rt auipc rt, delta[31 : 12] + delta[11]
s{b|h|w} rd, delta[11:0](rt)
保存全局变量
nop addi x0, x0, 0
li rd, imm ... 将立即数加载到 rd
mov rd, rs addi rd, rs, 0 rs 拷贝到 rd
not rd, rs xori rd, rs, -1 rd = ~rs 按位取反
neg rd, rs sub rd, x0, rs rd = -rs
seqz rd, rs sltiu rd, rs, 1 set rd if rs == 0
snez rd, rs sltu rd, x0, rs set rd if rs != 0
sltz rd, rs slt rd, rs, x0 set rd if rs < 0
sgtz rd, rs slt rd, x0, rs set rd if rs > 0
beqz rs, offset beq rs, x0, offset branch if rs == 0
bnez rs, offset bne rs, x0, offset branch if rs != 0
blez rs, offset bge x0, rs, offset branch if rs <= 0
bgez rs, offset bge rs, x0, offset branch if rs >= 0
bltz rs, offset blt rs, x0, offset branch if rs < 0
bgtz rs, offset blt x0, rs, offset branch if rs > 0
bgt rs, rt, offset blt rt, rs, offset branch if rs > rt
ble rs, rt, offset bge rt, rs, offset branch if rs <= rt
bgtu rs, rt, offset bltu rt, rs, offset branch if > unsigned
bleu rs, rt, offset bgeu rt, rs, offset branch if <= unsigned
j offset jal x0, offset 无条件跳转,不存返回地址
jal offset jal x1, offset 无条件跳转,返回地址存到 x1
jr rs jalr x0, 0(rs) 无条件跳转到 rs 位置,忽略返回地址
jalr rs jalr x1, 0(rs) 无条件跳转到 rs 位置,存返回地址
ret jalr x0, 0(x1) 通过返回地址 x1 返回
call offset auipc x1, offset[31 : 12] + offset[11]
jalr x1, offset[11:0](x1)
远调用
tail offset auipc x6, offset[31 : 12] + offset[11]
jalr x0, offset[11:0](x6)
忽略返回地址远调用

RV64I 附加指令

RV64I 的寄存器长度和地址空间长度都是 64 位,指令长度仍是 32 位。除此之外比 RV32I 多了一些针对 64/32 位的指令

整型计算指令

32 位整型计算

addiw I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 000 rd 0011011
  • 指令格式:addiw rd, rs1, imm
  • 指令作用:将 rs1 的低 32 位与 imm 相加,结果存到 rd 的低 32 位,并符号扩展到 64
  • 注意addiw rd, rs1, 0 相当于将 rs1 32 位符号扩展到 64 位,汇编伪代码可写为 sext.w rd, rs1
slliw i 型(改)
31 25 24 20 19 15 14 12 11 7 6 0
0000000 shamt rs1 001 rd 0011011
  • 指令格式:slliw rd, rs1, shamt
  • 指令作用:将 rs1 的低 32 位左移 shamt 位,结果存到 rd 的低 32 位,并符号扩展到 64
srliw i 型(改)
31 25 24 20 19 15 14 12 11 7 6 0
0000000 shamt rs1 101 rd 0011011
  • 指令格式:srliw rd, rs1, shamt
  • 指令作用:将 rs1 的低 32 位逻辑右移 shamt 位,结果存到 rd 的低 32 位,并符号扩展到 64
sraiw i 型(改)
31 25 24 20 19 15 14 12 11 7 6 0
0100000 shamt rs1 101 rd 0011011
  • 指令格式:sraiw rd, rs1, shamt
  • 指令作用:将 rs1 的低 32 位算数右移 shamt 位,结果存到 rd 的低 32 位,并符号扩展到 64
addw r 型
31 25 24 20 19 15 14 12 11 7 6 0
0000000 rs2 rs1 000 rd 0111011
  • 指令格式:addw rd, rs1, rs2
  • 指令作用:低 32 位加法,得到 32 位结果(忽略溢出,并符号扩展到 64
subw r 型
31 25 24 20 19 15 14 12 11 7 6 0
0100000 rs2 rs1 000 rd 0111011
  • 指令格式:subw rd, rs1, rs2
  • 指令作用:低 32 位减法,得到 32 位结果(忽略溢出,并符号扩展到 64
sllw r 型
31 25 24 20 19 15 14 12 11 7 6 0
0000000 rs2 rs1 001 rd 0111011
  • 指令格式:sllw rd, rs1, rs2
  • 指令作用:低 32 位左移,结果由 32 位符号扩展到 64
srlw r 型
31 25 24 20 19 15 14 12 11 7 6 0
0000000 rs2 rs1 101 rd 0111011
  • 指令格式:srlw rd, rs1, rs2
  • 指令作用:低 32 位逻辑右移,结果由 32 位符号扩展到 64
sraw r 型
31 25 24 20 19 15 14 12 11 7 6 0
0100000 rs2 rs1 101 rd 0111011
  • 指令格式:sraw rd, rs1, rs2
  • 指令作用:低 32 位算术右移,结果由 32 位符号扩展到 64

其它指令修改

slli、srli、srai 三条指令修改了格式:

slli i 型(改)
31 26 25 20 19 15 14 12 11 7 6 0
000000 shamt rs1 001 rd 0010011
  • 指令格式:slli rd, rs1, shamt
  • 指令作用:左移运算,shamt 6
  • 注意:格式上比 RV32I slli 多了一位 shamt
srli i 型(改)
31 26 25 20 19 15 14 12 11 7 6 0
000000 shamt rs1 101 rd 0010011
  • 指令格式:srli rd, rs1, shamt
  • 指令作用:逻辑右移运算,shamt 6
  • 注意:格式上比 RV32I srli 多了一位 shamt
srai i 型(改)
31 26 25 20 19 15 14 12 11 7 6 0
010000 shamt rs1 101 rd 0010011
  • 指令格式:srai rd, rs1, shamt
  • 指令作用:算数右移运算,shamt 6
  • 注意:格式上比 RV32I srai 多了一位 shamt

其它格式没有修改但功能有调整的指令:

  • SLL/SRL/SRA:移位位数取决于 rs2 的低 6 位(RV32I 是取决于 rs2 的低 5 位)
  • LUI:将 20 位立即数放入 rd 31~12 位,低 12 位置 0,然后符号扩展到 64
  • AUIPC:将 20 位立即数同 LUI 一样处理,然后加上 pc 值放入 rd

装载存储指令

ld I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 011 rd 0000011
  • 指令格式:ld rd, imm(rs1)
  • 指令作用:从 rs1 + imm 处内存读取一个 64 位数到 rd
lwu I 型
31 20 19 15 14 12 11 7 6 0
imm[11:0] rs1 110 rd 0000011
  • 指令格式:lwu rd, imm(rs1)
  • 指令作用:从 rs1 + imm 处内存读取一个 32 位数到 rd 中,然后零扩展到 64
sd S 型
31 25 24 20 19 15 14 12 11 7 6 0
imm[11:5] rs2 rs1 011 imm[4:0] 0100011
  • 指令格式:sd rs2, imm(rs1)
  • 指令作用:将 rs264 位)拷贝到 rs1 + imm 处内存中

Zicsr 扩展

特权级部分 > 控制和状态寄存器(CSRs)> CSR 指令(Zicsr 扩展)

汇编代码

    .text
    .align 2
    .globl main
main:
    addi sp, sp, -16
    sw ra, 12(sp)
    lui a0, %hi(string1)
    addi a0, a0, %lo(string1)
    lui a1, %hi(string2)
    addi a1, a1, %lo(string2)
    call printf
    lw ra, 12(sp)
    addi sp, sp, 16
    li a0, 0
    ret

    .section .rodata
    .balign 4
string1:
    .string "Hello, %s!\n"
string2:
    .string "world"
  • .text:进入代码段
  • .align 2:代码段对齐到 2^2 字节
  • .globl main:声明全局标号 main
  • .section .rodata:进入 rodata
  • .balign 4:对齐数据段到 4 字节
  • .string ... 定义字符串

其它指令:

  • .data:进入数据段
  • .bss:进入 bss
  • .byte b1, b2, ..., bn:存放一些字节
  • .half w1, w2, ..., wn:存放一些半字(16 位)
  • .word w1, w2, ..., wn:存放一些字(32 位)

最后更新: 2023年6月15日 22:13:10
创建日期: 2022年5月29日 12:21:50
回到页面顶部