cpu的制作

cpu制作在最开始听起来是一个非常复杂的项目,但其实其所需要的基础非常简单,只需要一些简单的数字电路基础即可完成搭建。

而且自己制作cpu是一件非常有趣的一件事情,去理解身边的电脑,手机等等的一些运作的底层原理。

那么现在,我们就从最开始最简单的电路开始搭建吧。

电路搭建

与或非门,同或异或门

image-20231010180429855

与或非是计算机逻辑门的最基础的部件,其他任意的部件都可以通过与或非三种门来表示。

八位加法器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
module eight_bit_full_adder  (
input A,
input B,
output S,
output C
);
assign S = (A ^ B);
assign C = (A & B);
endmodule
module DIG_Add
#(
parameter Bits = 1
)
(
input [(Bits-1):0] a,
input [(Bits-1):0] b,
input c_i,
output [(Bits - 1):0] s,
output c_o
);
wire [Bits:0] temp;

assign temp = a + b + c_i;
assign s = temp [(Bits-1):0];
assign c_o = temp[Bits];
endmodule



module \????? (
input A1,
input B1,
input A2,
input B2,
input A3,
input B3,
input A4,
input B4,
input A5,
input B5,
input A6,
input B6,
input A7,
input B7,
input A0,
input B0,
output S0,
output S1,
output S2,
output S3,
output S4,
output S5,
output S6,
output S7,
output C
);
wire s8;
wire s9;
wire s10;
wire s11;
wire s12;
wire s13;
wire s14;
\??? \???_i0 (
.A( A0 ),
.B( B0 ),
.S( S0 ),
.C( s8 )
);
DIG_Add #(
.Bits(1)
)
DIG_Add_i1 (
.a( s8 ),
.b( A1 ),
.c_i( B1 ),
.s( S1 ),
.c_o( s9 )
);
DIG_Add #(
.Bits(1)
)
DIG_Add_i2 (
.a( A2 ),
.b( s9 ),
.c_i( B2 ),
.s( S2 ),
.c_o( s10 )
);
DIG_Add #(
.Bits(1)
)
DIG_Add_i3 (
.a( s10 ),
.b( A3 ),
.c_i( B3 ),
.s( S3 ),
.c_o( s11 )
);
DIG_Add #(
.Bits(1)
)
DIG_Add_i4 (
.a( s11 ),
.b( A4 ),
.c_i( B4 ),
.s( S4 ),
.c_o( s12 )
);
DIG_Add #(
.Bits(1)
)
DIG_Add_i5 (
.a( s12 ),
.b( A5 ),
.c_i( B5 ),
.s( S5 ),
.c_o( s13 )
);
DIG_Add #(
.Bits(1)
)
DIG_Add_i6 (
.a( s13 ),
.b( A6 ),
.c_i( B6 ),
.s( S6 ),
.c_o( s14 )
);
DIG_Add #(
.Bits(1)
)
DIG_Add_i7 (
.a( s14 ),
.b( A7 ),
.c_i( B7 ),
.s( S7 ),
.c_o( C )
);
endmodule

锁存器

与门可以存储下来0,或门可以存储下来1,我们把与门和或门进行一个组合,就能做出来第一个有用的电路结构。

引脚 解释
Dindatain 数据输入
WE write enable

当WE为高电平的时候,Din的数据可以被存储起来当WE为低电平的时候,out的值不发生变化。

1
2
3
4
5
6
7
8
9
module latch  (
input Din,
input WE,
output Out
);
wire Out_temp;
assign Out_temp = ((Out_temp | (Din & WE)) & ~ (~ Din & WE));
assign Out = Out_temp;
endmodule

 

寄存器

寄存器=8位锁存器/16/32位

一次可以存储8位数据。

引脚 解释
Dindatain 数据输入
WE write enable

当WE为高电平的时候,Din的数据可以被存储起来当WE为低电平的时候,out的值不发生变化。

image-20231011105654618

 

带边缘触发的锁存器

为什么需要时钟

大家都见过划船的,划船需要一个喊口号的主要原因是为了保证协调。通过喊口号,船员们可以同步动作,确保船在平稳且有效率地前进。
CPU需要clock来同步内部操作,如执行指令、进行数据传输等。Clock提供了精确的时间控制,确保每个内部操作在正确的顺序与速度内执行,从而确保CPU的正常工作。另外,Clock还与CPU的频率相关,通过控制Clock频率,可以控制CPU的速度。

时钟信号是什么

时钟信号就是周期性的高低电平变化的信号
我们可以用两个普通的寄存器加上一个非门,组成一个带有边缘触发的寄存器。
在按钮按下的一瞬间,电压从低电平到高电平的一瞬间,Din的数据被存储起来。

 

寄存器REG

引脚 解释
D 数据输入
C 时钟信号
en 使能端口,高电平工作

存储器-寄存器

可以做的扩展,增加输入使能WE和输出使能OE
寄存器访问速度快,因为寄存器的每一条数据线都是直接接出来的。

 

十六位的内存

内存地址:从并行到串行
内存单元格要自己自己在哪一行和哪一列,需要有row和column
内存单元要有ld(load)读的控制
内存单元要有str(store)有存的控制
内存单元要有数据的输入

引脚 解释
row&column 确定需要储存的地址
ld(load) 读的控制
str(store) 存的控制

内存地址:从并行到串行
内存地址的作用主要是为了节省数据线,简化电路数量有了内存地址的概念后,输入和输出只需要1条数据线了先选中需要读写的内存单元,再输入输出

 

8位的寄存器

设计8位(bit)的寄存器,用于CPU存储的临时计算的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
module DIG_Register_BUS #(
parameter Bits = 1
)
(
input C,
input en,
input [(Bits - 1):0]D,
output [(Bits - 1):0]Q
);

reg [(Bits - 1):0] state = 'h0;

assign Q = state;

always @ (posedge C) begin
if (en)
state <= D;

end
endmodule

module DriverBus#(
parameter Bits = 2
)
(
input [(Bits-1):0] in,
input sel,
output [(Bits-1):0] out
);
assign out = (sel == 1'b1)? in : {Bits{1'bz}};
endmodule

module 8_REG (
input Clock,
input [7:0] Din,
input WE,
input OE,
output [7:0] Stored_Data,
output [7:0] Dout
);
wire [7:0] Stored_Data_temp;
DIG_Register_BUS #(
.Bits(8)
)
DIG_Register_BUS_i0 (
.D( Din ),
.C( Clock ),
.en( WE ),
.Q( Stored_Data_temp )
);
DriverBus #(
.Bits(8)
)
DriverBus_i1 (
.in( Stored_Data_temp ),
.sel( OE ),
.out( Dout )
);
assign Stored_Data = Stored_Data_temp;
endmodule

基于上面的原理图,我们分别设计4位和8位的寄存器4位的寄存器用于程序计数器(PC)和MAR(内存地址寄存器)
8位的寄存器用于指令寄存器(IR),CPU通用临时寄存器(RegA)(RegB),内存缓存寄存器(MBR)等。分别测试4位和8位的寄存器。

image-20231011154050156

 

逻辑和算数运算单元(ALU)

引脚 解释
RegA 8位的寄存器数据
RegB 8位的寄存器数据
OE 输出允许
ALUResult ALU内部计算结果,方便观察调试
Carry 溢出位引出1位信号线
Dout 输出数据8位,默认高阻态

image-20231011155356141

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
* module DIG_Add
#(
parameter Bits = 1
)
(
input [(Bits-1):0] a,
input [(Bits-1):0] b,
input c_i,
output [(Bits - 1):0] s,
output c_o
);
wire [Bits:0] temp;

assign temp = a + b + c_i;
assign s = temp [(Bits-1):0];
assign c_o = temp[Bits];
endmodule



module DriverBus#(
parameter Bits = 2
)
(
input [(Bits-1):0] in,
input sel,
output [(Bits-1):0] out
);
assign out = (sel == 1'b1)? in : {Bits{1'bz}};
endmodule

module ALU (
input OE,
input [7:0] RegA,
input [7:0] RegB,
output [7:0] Dout,
output [7:0] ALU_Result,
output Carry
);
wire [7:0] ALU_Result_temp;
DIG_Add #(
.Bits(8)
)
DIG_Add_i0 (
.a( RegA ),
.b( RegB ),
.c_i( 1'b0 ),
.s( ALU_Result_temp ),
.c_o( Carry )
);
DriverBus #(
.Bits(8)
)
DriverBus_i1 (
.in( ALU_Result_temp ),
.sel( OE ),
.out( Dout )
);
assign ALU_Result = ALU_Result_temp;
endmodule

 

CPU框架搭建

image-20231011161355384

image-20231011165424214