Computer Organization and Design

数电&计组笔记

  • 门电路
  • CPU设计
  • SpinalHDL语法

数电

晶体管

一般画电路图如图

晶体管有3or4种工作状态(注意到e端接地):

  1. 截止状态,相当于断路

    • 条件:
    • 结果:
  2. 放大状态

    • 条件:
    • 结果:,其中为放大系数
  3. 饱和状态,相当于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
2
3
4
5
6
7
8
9
10
11
12
module tri_state_logic (
inout signal_io
);

wire signal_i;
wire signal_o;
wire signal_t;

assign signal_io = signal_t ? 1'bz : signal_o;
assign signal_i = signal_io;

endmodule

计组

第一讲

字长

  1. 机器字长:CPU进行一次整数运算所能处理的二进制数据的位数。
    1. CPU总线的宽度=运算器的位数=通用寄存器的宽度=数据总线的宽度=机器字长。
    2. 不加修饰的字长一般为机器字长。
    3. 内存中用于指明一个存储位置的地址经常是以字节为单位的。
  2. 存储字长:一个存储单元存储二进制代码的位数。
    1. 一般存储字长小于等于机器字长
  3. 指令字长:一个指令字包含二进制代码的位数。

符号扩展

保持数的值不变同时增加数的位数

RISC-V指令集

RISC-V指令集

多周期CPU

CPU结构图

CPU结构图

CPU信号表和状态转移表

信号表

对文档中的信号稍作改变,定义了7个与当前状态无关的信号。

  1. alu_src:alu的第二个操作数来源,1表示立即数,0表示寄存器值。
  2. alu_op:alu的操作码,2位,00表示加法,01表示减法,10表示根据funct3和funct7选择操作。
    1. funct3=000,表示add
    2. funct3=111,表示and
  3. pc_src:下一步pc值,0表示pc+4,1表示pc+imm。
  4. mem_read:是否进行内存读取。
  5. mem_write:是否进行内存写入。
  6. mem_to_reg:1表示alu计算结果写入寄存器,0表示内存读取结果写入寄存器。
  7. 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指令执行的五个步骤:

  1. 取指令(IF):从内存中取出指令。
  2. 译码(ID):读寄存器并译码指令,确定指令的操作码和操作数。
  3. 执行(EX):根据操作码和操作数执行指令或计算地址。
  4. 访存(MEM):如果指令需要访存,则进行访存操作。
  5. 写回(WB):将结果写回寄存器。

指令设计

    • RISCV所有指令等长,可以简化取指令和指令译码。
    • 所以现代x86指令在实现时,先将变长指令转为类似RISCV的简单操作,再流水化这些简单操作。
    • RISCV中,存储器操作只出现在loadstore指令中。所以可以用执行阶段计算存储器地址,下一阶段访问存储器。
    • x86中,别的指令也可以访存,就需要扩展原先的三四阶段,扩展为地址计算、存储器访问、执行,会加长流水线

流水线冒险

冒险:在下一个时钟周期下一条指令无法执行。

有三种冒险:

  1. 结构冒险:硬件资源冲突,比如一条指令读存储器另一条写相同的存储器。
  2. 数据冒险:一个步骤必须等待另一条指令完成。
    1. 连续计算:一条加法指令之后跟着以加法和为操作数的指令。
      1. 通过前递(or 旁路)解决:执行阶段计算完毕后直接将数据给下一条指令。
    2. 载入-使用:一条载入指令之后跟着使用载入的数据的指令。
      1. 不能通过前递解决,因为载入指令的数据在访存阶段才能得到,此时下一条指令已经在执行阶段。
      2. 可以重排代码,提前load指令避免停顿。
  3. 控制冒险:分支指令的目标地址未知,需要等待分支指令执行完毕。
    1. 通过分支预测解决:预测分支的目标地址,如果预测正确则不会有停顿。

中断与异常

mode

RISCV有三种状态:

  1. User Mode
  2. Supervisor Mode
  3. 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
val myBits1 = Bits(32 bits)
val myBits2 = B(25, 8 bits)
val myBits3 = B"8'xFF" // Base could be x,h (base 16)
// d (base 10)
// o (base 8)
// b (base 2)
val myBits4 = B"1001_0011" // _ can be used for readability

// Bits with all ones ("11111111")
val myBits5 = B(8 bits, default -> True)

// initialize with "10111000" through a few elements
val myBits6 = B(8 bits, (7 downto 5) -> B"101", 4 -> true, 3 -> True, default -> false)

// "10000000" (For assignment purposes, you can omit the B)
val myBits7 = Bits(8 bits)
myBits7 := (7 -> true, default -> false)

移位

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
2
3
4
5
6
object UartCtrlTxState extends SpinalEnum {
val sIdle, sStart, sData, sParity, sStop = newElement()
}

val state = UartCtrlTxState()
state := UartCtrlTxState.sIdle

注意比较的写法

1
2
state === UartCtrlTxState.sIdle     
state =/= UartCtrlTxState.sIdle

Bundle

类似Struct,定义多个信号的集合。

可以从Master,Slave的角度定义信号的输入输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
case class HandShake(payloadWidth: Int) extends Bundle with IMasterSlave {
val valid = Bool()
val ready = Bool()
val payload = Bits(payloadWidth bits)

// You have to implement this asMaster function.
// This function should set the direction of each signals from an master point of view
override def asMaster(): Unit = {
out(valid, payload)
in(ready)
}
}

val io = new Bundle {
val input = slave(HandShake(8))
val output = master(HandShake(8))
}

Vector

顾名思义,不同元素的宽度可以不同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
val myVecOfSInt = Vec.fill(2)(SInt(8 bits))
myVecOfSInt(0) := 2
myVecOfSInt(1) := myVecOfSInt(0) + 3

val myVecOfMixedUInt = Vec(UInt(3 bits), UInt(5 bits), UInt(8 bits))

val x, y, z = UInt(8 bits)
// 创建的是引用,不会创建新的硬件信号
val myVecOf_xyz_ref = Vec(x, y, z)

// Iterate on a vector
for(element <- myVecOf_xyz_ref) {
element := 0
}

// Map on vector
myVecOfMixedUInt.map(_ := 0)

结构设计

一个组件的实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class AdderCell() extends Component {
// Declaring external ports in a Bundle called `io` is recommended
val io = new Bundle {
val a, b, cin = in port Bool()
val sum, cout = out port Bool()
}
// Do some logic
io.sum := io.a ^ io.b ^ io.cin
io.cout := (io.a & io.b) | (io.a & io.cin) | (io.b & io.cin)
}

class Adder(width: Int) extends Component {
...
// Create 2 AdderCell instances
val cell0 = new AdderCell()
val cell1 = new AdderCell()
cell1.io.cin := cell0.io.cout // Connect cout of cell0 to cin of cell1

// Another example which creates an array of ArrayCell instances
val cellArray = Array.fill(width)(new AdderCell())
cellArray(1).io.cin := cellArray(0).io.cout // Connect cout of cell(0) to cin of cell(1)
...
}

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