Computer Organization and Design
数电&计组笔记
- 门电路
- CPU设计
- SpinalHDL语法
数电
晶体管
一般画电路图如图
晶体管有3or4种工作状态(注意到e端接地):
截止状态,相当于断路
- 条件:
- 结果:
- 条件:
放大状态
- 条件:
- 结果:
,其中 为放大系数
- 条件:
饱和状态,相当于ce之间连了一根导线
- 条件:
- 结果:
- 条件:
与非门
用晶体管可以实现与非门
T1是多发射极三极管,输入AB只要有一个为低,则三极管导通,若均为高,则三极管截止。
T1导通时,T2T5截止,T3T4导通,输出电流从T4向外流,此时输出位高电位(3.6V)。
T1截止时,T4截止,T5饱和,电流从输出流向T5,此时输出为低电位。
进一步抽象,T3-T4称“1”驱动级,T5称“0”驱动级,组成推-拉式输出结构,又称图腾柱结构(Totem)输出
门电路级联:前一个器件的输出就是后一个器件的输入,后一个是前一个的负载,两者要相互影响。当负载大于前一个的承受能力时,低电位会变高,高电位会变低。
想要实现“线与”,但不能把两个与或门直接连在一起,否则电流大,会烧坏器件。
所以使用集电极开路输出门电路,如图
但是由于OC门输出不是Totem结构,电路的上升延迟很大,所以使用三态门,三态门电路即保留了Totem输出结构,又具有OC门输出可以“线与”的特点
- 当控制G=1时,电路是一个图腾结构的与非门
- 当G=0,T3、T4、T5均截止,与非门输出F=Z(高阻态)
三态门
1 | module tri_state_logic ( |
计组
第一讲
字长
- 机器字长:CPU进行一次整数运算所能处理的二进制数据的位数。
- CPU总线的宽度=运算器的位数=通用寄存器的宽度=数据总线的宽度=机器字长。
- 不加修饰的字长一般为机器字长。
- 内存中用于指明一个存储位置的地址经常是以字节为单位的。
- CPU总线的宽度=运算器的位数=通用寄存器的宽度=数据总线的宽度=机器字长。
- 存储字长:一个存储单元存储二进制代码的位数。
- 一般存储字长小于等于机器字长
- 一般存储字长小于等于机器字长
- 指令字长:一个指令字包含二进制代码的位数。
符号扩展
保持数的值不变同时增加数的位数
RISC-V指令集
多周期CPU
CPU结构图

CPU信号表和状态转移表
信号表
对文档中的信号稍作改变,定义了7个与当前状态无关的信号。
- alu_src:alu的第二个操作数来源,1表示立即数,0表示寄存器值。
- alu_op:alu的操作码,2位,00表示加法,01表示减法,10表示根据funct3和funct7选择操作。
- funct3=000,表示add
- funct3=111,表示and
- funct3=000,表示add
- pc_src:下一步pc值,0表示pc+4,1表示pc+imm。
- mem_read:是否进行内存读取。
- mem_write:是否进行内存写入。
- mem_to_reg:1表示alu计算结果写入寄存器,0表示内存读取结果写入寄存器。
- reg_write:是否进行寄存器写入。
LB | ADDI ANDI |
SB SW |
ADD | LUI | BEQ | |
---|---|---|---|---|---|---|
opcode(0x) | 03 | 13 | 23 | 33 | 37 | 63 |
alu_src | 1 | 1 | 1 | 0 | 1 | 0 |
alu_op | 00 | 10 | 00 | 10 | 00 | 01 |
pc_src | 0 | 0 | 0 | 0 | 0 | 1 |
mem_read | 1 | 0 | 0 | 0 | 0 | 0 |
mem_write | 0 | 0 | 1 | 0 | 0 | 0 |
mem_to_reg | 1 | 0 | 0 | 0 | 0 | 0 |
reg_write | 1 | 1 | 0 | 1 | 1 | 0 |
其余时序信号见状态转移表。
状态转移表
原状态 | 新状态 | 条件 | 时序操作 | 主要功能 |
---|---|---|---|---|
IF_SET | IF_WAIT | True | wb_cyc<=1 wb_stb<=1 wb_we<=0 wb_adr<=pc wb_sel<=4'b1111 |
设置wishbone相关信号 读取指令 |
IF_WAIT | IF_WAIT | wb_ack=0 | none | 等待读取结束 |
IF_WAIT | ID | wb_ack=1 | wb_cyc<=0 wb_stb<=0 instr<=wb_dat |
读取结束,保存读取的指令 |
ID | EXE | True | 解析指令获得rd,rs1,imm等信息 rf_raddr_a <= rs1 rf_raddr_b <= rs2; |
译码指令 设置寄存器地址进行读取 |
EXE | MEM_SET | True | 设置alu的操作数及操作码 | 进行运算 |
MEM_SET | MEM_WAIT | True | wb_cyc<=1 wb_stb<=1 wb_we<=0 wb_adr<=... wb_sel<=... pc<=... |
设置wishbone相关信号 读取数据(如果需要) 更新pc |
MEM_WAIT | MEM_WAIT | wb_ack=0 | none | 等待读取结束 |
MEM_WAIT | WB | wb_ack=1 | wb_cyc<=0 wb_stb<=0 rf_waddr<=rd rf_we<=1 rf_wdata<=... |
读取结束 将计算/读取结果写入寄存器 (如果需要) |
WB | IF_SET | True | none | 空一周期 |
bug
在lb
指令中,读mem读出来的数据要重新移位到最低位才可以存到寄存器里。
以及,信号的位数写错了。。
流水线CPU
指令阶段
RISCV指令执行的五个步骤:
- 取指令(IF):从内存中取出指令。
- 译码(ID):读寄存器并译码指令,确定指令的操作码和操作数。
- 执行(EX):根据操作码和操作数执行指令或计算地址。
- 访存(MEM):如果指令需要访存,则进行访存操作。
- 写回(WB):将结果写回寄存器。
指令设计
RISCV
所有指令等长,可以简化取指令和指令译码。
- 所以现代
x86
指令在实现时,先将变长指令转为类似RISCV
的简单操作,再流水化这些简单操作。
RISCV
中,存储器操作只出现在load
和store
指令中。所以可以用执行阶段计算存储器地址,下一阶段访问存储器。
- 在
x86
中,别的指令也可以访存,就需要扩展原先的三四阶段,扩展为地址计算、存储器访问、执行,会加长流水线。
流水线冒险
冒险:在下一个时钟周期下一条指令无法执行。
有三种冒险:
- 结构冒险:硬件资源冲突,比如一条指令读存储器另一条写相同的存储器。
- 数据冒险:一个步骤必须等待另一条指令完成。
- 连续计算:一条加法指令之后跟着以加法和为操作数的指令。
- 通过前递(or 旁路)解决:执行阶段计算完毕后直接将数据给下一条指令。
- 载入-使用:一条载入指令之后跟着使用载入的数据的指令。
- 不能通过前递解决,因为载入指令的数据在访存阶段才能得到,此时下一条指令已经在执行阶段。
- 可以重排代码,提前load指令避免停顿。
- 连续计算:一条加法指令之后跟着以加法和为操作数的指令。
- 控制冒险:分支指令的目标地址未知,需要等待分支指令执行完毕。
- 通过分支预测解决:预测分支的目标地址,如果预测正确则不会有停顿。
中断与异常
mode
RISCV有三种状态:
- User Mode
- Supervisor Mode
- Machine Mode
CSR
缩略词 | 英文全称 | 中文解释 |
---|---|---|
mtvec | Machine Trap Vector | 发生异常时处理器需要跳转到的地址 |
mepc | Machine Exception PC | 指向发生异常的指令 |
mcause | Machine Exception Cause | 发生异常的种类 |
mie | Machine Interrupt Enable | 处理器目前能处理和必须忽略的中断 |
mip | Machine Interrupt Pending | 正准备处理的中断 |
mtval | Machine Trap Value | trap的附加信息:地址异常中出错的地址,非法指令的指令等 |
mscratch | Machine Scratch | 暂时存放一个字大小的数据 |
mstatus | Machine Status | 机器的状态 |
mcause
最高位,0表示中断,1表示异常
Interrupt / Exception | Exception Code | Description |
---|---|---|
mcause[XLEN-1] | mcause[XLEN-2:0] | |
1 | 1 | Supervisor software interrupt |
1 | 3 | Machine software interrupt |
1 | 5 | Supervisor timer interrupt |
1 | 7 | Machine timer interrupt |
1 | 9 | Supervisor external interrupt |
1 | 11 | Machine external interrupt |
0 | 0 | Instruction address misaligned |
0 | 1 | Instruction access fault |
0 | 2 | Illegal instruction |
0 | 3 | Breakpoint |
0 | 4 | Load address misaligned |
0 | 5 | Load access fault |
0 | 6 | Store address misaligned |
0 | 7 | Store access fault |
0 | 8 | Environment call from U-mode |
0 | 9 | Environment call from S-mode |
0 | 11 | Environment call from M-mode |
0 | 12 | Instruction page fault |
0 | 13 | Load page fault |
0 | 15 | Store page fault |
SpinalHDL
数据类型
Bits
表示多位向量
1 | val myBits1 = Bits(32 bits) |
移位
Operator | Description | Return type |
---|---|---|
x >> y (y = 1 // Int) | Logical shift right, y: Int | Bits(w(x) - y bits) |
x >> y (y = U(1) // UInt) | Logical shift right, y: UInt | Bits(w(x) bits) |
x << y (y = 1 // Int) | Logical shift left, y: Int | Bits(w(x) + y bits) |
x << y (y = U(1) // UInt) | Logical shift left, y: UInt | Bits(w(x) + max(y) bits) |
x 1>> y | Logical shift right, y: Int/UInt | Bits(w(x) bits) |
x 1<< y | Logical shift left, y: Int/UInt | Bits(w(x) bits) |
x.rotateLeft(y) | Logical left rotation, y: UInt/Int | Bits(w(x) bits) |
x.rotateRight(y) | Logical right rotation, y: UInt/Int | Bits(w(x) bits) |
注意均为逻辑移位,返回值的位数可能改变。
Uint/SInt
可以进行算术运算的Bits。
运算
Operator | Description | Return type |
---|---|---|
x >> y | Arithmetic shift right, y : Int | T(w(x) - y bits) |
x >> y | Arithmetic shift right, y : UInt | T(w(x) bits) |
x << y | Arithmetic shift left, y : Int | T(w(x) + y bits) |
x << y | Arithmetic shift left, y : UInt | T(w(x) + max(y) bits) |
x 1>> y | Logical shift right, y : Int/UInt | T(w(x) bits) |
x 1<< y | Logical shift left, y : Int/UInt | T(w(x) bits) |
x.rotateLeft(y) | Logical left rotation, y : UInt/Int | T(w(x) bits) |
x.rotateRight(y) | Logical right rotation, y : UInt/Int | T(w(x) bits) |
x + y | Addition | T(max(w(x), w(y)) bits) |
x +^ y | Addition with carry | T(max(w(x), w(y)) + 1 bits) |
x - y | Subtraction | T(max(w(x), w(y)) bits) |
x -^ y | Subtraction with carry | T(max(w(x), w(y)) + 1 bits) |
这里默认是算术移位
SpinalEnum
1 | object UartCtrlTxState extends SpinalEnum { |
注意比较的写法
1 | state === UartCtrlTxState.sIdle |
Bundle
类似Struct
,定义多个信号的集合。
可以从Master,Slave的角度定义信号的输入输出
1 | case class HandShake(payloadWidth: Int) extends Bundle with IMasterSlave { |
Vector
顾名思义,不同元素的宽度可以不同
1 | val myVecOfSInt = Vec.fill(2)(SInt(8 bits)) |
结构设计
一个组件的实例
1 | class AdderCell() extends Component { |
pipeline
Stream
信号 | 类型 | 驱动 | 描述 | 何时忽略 |
---|---|---|---|---|
valid | Bool | Master | 当为高时 => 接口上存在有效载荷(payload) | |
ready | Bool | Slave | 当为低时 => 从端不接收输出 | valid为低 |
payload | T | Master | 传输的有效载荷内容 | valid为低 |
Node
valid | ready | cancel | 描述 | isFiring | isMoving |
---|---|---|---|---|---|
0 | X | X | 无事务 | 0 | 0 |
1 | 1 | 0 | 正在进行 | 1 | 1 |
1 | 0 | 0 | 阻塞 | 0 | 0 |
1 | X | 1 | 取消 | 0 | 1 |
insert操作创建一个新的payload