1、CPU简介

  CPU的本质是在整个电路系统中为核心电路单元,主要的作用是对数据进行逻辑和数学运算的电路单元,运算时通过人机交互控制。

2、工作示意图

cpu tm function cputmfunction和c1e_数据

CU:控制单元,分析指令,给出控制信号

 IR:指令寄存器,分析指令,给出控制信号

 PC:程序计数器,存放下一条指令地址,有自动加1功能

ACC:累加器,用于存放操作数/操作结果

MQ:乘商寄存器

x:用于存放操作数

ALU:实现逻辑运算

完成一条完整的指令需要三个步骤:  1、取指令(PC)

                  2、分析指令(IR)

                  3、执行指令(CU)

3、本程序设计一种基础架构的RISC CPU

1、顶层架构

1 //当wr=1的时候data上数据写有效
 2 //当rd=1的时候data上数据读有效
 3 module my_risc_cpu(clk, rst_n, data, addr, wr, rd);
 4 
 5     input clk, rst_n;   //系统时钟、复位
 6     inout [7:0] data;   //指令/数据(数据总线)
 7     output [12:0] addr; //地址(地址总线)
 8     output wr;          //写有效使能->控制总线
 9     output rd;          //读有效使能->控制总线
10     
11     
12     wire load_ir;
13     wire clk_inc;
14     wire load_pc;
15     wire load_acc;
16     wire addr_sel;
17     wire data_link;
18     wire alu_en;
19     wire [7:0] alu_out;    //计算组件计算的结果
20     wire [7:0] acc;   //将计算组件计算的结果存入acc
21     wire [15:0] ir;
22     wire [12:0] pc_addr;
23     
24     
25     //时序控制组件
26     tm_gc tm_gc_ins(
27         .clk(clk), 
28         .rst_n(rst_n), 
29         .ir(ir[15:13]), 
30         .load_ir(load_ir), 
31         .clk_inc(clk_inc), 
32         .load_pc(load_pc), 
33         .load_acc(load_acc), 
34         .addr_sel(addr_sel), 
35         .data_link(data_link), 
36         .wr(wr), 
37         .rd(rd), 
38         .alu_en(alu_en)
39     );
40     
41     //计算组件
42     alu alu_ins(
43         .clk(clk), 
44         .rst_n(rst_n), 
45         .data(data), 
46         .acc(acc), 
47         .alu_en(alu_en), 
48         .ir(ir[15:13]), 
49         .alu_out(alu_out)
50     );
51     
52     //累加器
53     acc_m acc_m_ins(
54         .clk(clk), 
55         .rst_n(rst_n), 
56         .alu_out(alu_out), 
57         .load_acc(load_acc), 
58         .acc(acc)
59     );
60     
61     //指令寄存
62     ir_register ir_register_ins(
63         .clk(clk), 
64         .rst_n(rst_n), 
65         .data(data), 
66         .load_ir(load_ir), 
67         .ir(ir)
68     );
69     
70     //指令地址发生器
71     pc_addr_gen pc_addr_gen_ins(
72         .clk_inc(clk_inc), 
73         .rst_n(rst_n), 
74         .load_pc(load_pc), 
75         .pc_addr(pc_addr), 
76         .ir(ir[12:0])
77     );
78     
79     //数据选择
80     data_sel_m data_sel_m_ins(
81         .data_sel(data_link), 
82         .acc(acc), 
83         .data(data)
84     );
85     
86     //地址路由
87     addr_sel_m addr_sel_m_ins(
88         .ir(ir[12:0]), 
89         .pc_addr(pc_addr), 
90         .addr_sel(addr_sel), 
91         .addr(addr)
92     );
93 
94 endmodule

2、时序控制组件顶层

1 /*********************时序控制组件********************************/
 2 module tm_gc(clk, rst_n, ir, load_ir, clk_inc, load_pc, load_acc, addr_sel, data_link, wr, rd, alu_en);
 3 
 4     input clk, rst_n;
 5 
 6     input [15:13] ir;
 7     
 8     output load_ir;
 9     output clk_inc;
10     output load_pc;
11     output load_acc;
12     output addr_sel;
13     output data_link;
14     output wr;
15     output rd;
16     output alu_en;
17     
18     wire clk_ir;
19     
20     tm_gen tm_gen_ins(
21         .clk(clk), 
22         .rst_n(rst_n), 
23         .alu_en(alu_en), 
24         .clk_ir(clk_ir)
25     );
26     
27     tm_ctl tm_ctl_ins(
28         .clk(clk), 
29         .rst_n(rst_n), 
30         .clk_ir(clk_ir), 
31         .ir(ir), 
32         .load_ir(load_ir), 
33         .clk_inc(clk_inc), 
34         .load_pc(load_pc), 
35         .load_acc(load_acc), 
36         .addr_sel(addr_sel), 
37         .data_link(data_link), 
38         .wr(wr), 
39         .rd(rd)
40     );
41 
42 endmodule

2.1生成指令周期(8个机器周期)

1 //本模块主要产生指令周期的时钟信号clk_ir
 2 module tm_gen(clk, rst_n, alu_en, clk_ir);
 3 
 4     input clk, rst_n;
 5     output reg alu_en;
 6     output reg clk_ir;
 7     
 8     reg [2:0] cnt;  //本设计的指令周期为8个机器周期(8个clk),8051单片机也为8个机器周期
 9     
10     always @ (posedge clk, negedge rst_n)
11     begin
12         if(~rst_n)
13             cnt <= 3'b0;
14         else
15             if(cnt == 3'd7)
16                 cnt <= 3'd0;
17             else
18                 cnt <= cnt + 1'b1;
19     end
20     
21     always @ (posedge clk, negedge rst_n)
22     begin
23         if(~rst_n)
24             clk_ir <= 1'b0;
25         else
26             if(cnt == 3'd1)//2-5,四个时钟周期,尽量避免0作为开始
27                 clk_ir <= 1'b1;
28             else
29                 if(cnt == 3'd5)
30                     clk_ir <= 1'b0;
31     end
32     
33     //在clk_ir低电平的倒数第二个周期将alu_en拉高
34     always @ (posedge clk, negedge rst_n)
35     begin
36         if(~rst_n)
37             alu_en <= 1'b0;
38         else
39             if(cnt == 4'd7)
40                 alu_en <= 1'b1;
41             else
42                 alu_en <= 1'b0;
43     end
44     
45 endmodule

 

2.2实现时序控制

1 module tm_ctl(clk, rst_n, clk_ir, ir, load_ir, clk_inc, load_pc, load_acc, addr_sel, data_link, wr, rd);
  2 
  3     input clk, rst_n;
  4     
  5     input clk_ir;
  6     input [15:13] ir;
  7     
  8     output reg load_ir;
  9     output reg clk_inc;
 10     output reg load_pc;
 11     output reg load_acc;
 12     output reg addr_sel;
 13     output reg data_link;
 14     output reg wr;
 15     output reg rd;
 16     
 17     parameter   HLT    =   3'b000,
 18                 RD     =   3'b001,
 19                 WR     =   3'b010,
 20                 SFT    =   3'b011,
 21                 XOR    =   3'b100,
 22                 SUB    =   3'b101,
 23                 SKZ    =   3'b110,
 24                 NJMP   =   3'b111;
 25     
 26     
 27     parameter   FETCH1     =   3'd0,
 28                 FETCH2     =   3'd1,
 29                 FETCH3     =   3'd2,
 30                 EXZIT1     =   3'd3,
 31                 EXZIT2     =   3'd4,
 32                 EXZIT3     =   3'd5,
 33                 EXZIT4     =   3'd6,
 34                 EXZIT5     =   3'd7;
 35                 
 36     reg [2:0] state;
 37 
 38     always @ (posedge clk, negedge rst_n)
 39     begin
 40         if(~rst_n)
 41             state <= FETCH1;
 42         else
 43             case(state)
 44                 FETCH1  :   if(clk_ir) state <= FETCH2;
 45                 FETCH2  :   state <= FETCH3;
 46                 FETCH3  :   state <= EXZIT1;
 47                 EXZIT1  :   state <= EXZIT2;
 48                 EXZIT2  :   state <= EXZIT3;
 49                 EXZIT3  :   state <= EXZIT4;
 50                 EXZIT4  :   state <= EXZIT5;
 51                 EXZIT5  :   state <= FETCH1;
 52                 default :   state <= FETCH1;
 53             endcase
 54     end
 55     
 56     //此控制程序过程:在FETCH1状态拉高load_ir和rd两拍,在FETCH3拉低;读取一个完整的地址上的指令(因为数据是8位宽);
 57     //在FETCH1状态拉高load_ir和rd两拍,在FETCH3拉低;选择指令的地址pc_addr,在FETCH1拉高clk_inc一个周期让addr更新一次+1;
 58     //在EXZIT1拉高clk_inc信号使得pc_addr指向下一个新的地址(用于进行下一次运算取pc_addr上对应的操作码和数据的地址);
 59     //在EXZIT2时拉高rd信号读取数据地址上的数据,且此时addr_sel为低读取的是IR的地址,赋值给了addr;读取出数据
 60     //在EXZIT3时拉高alu_en信号,用IR的高三位(操作码)选择对应的操作运算
 61     //在EXZIT4时拉高load_acc信号,将alu计算的结果存入累加器中
 62     always @ (posedge clk, negedge rst_n)
 63     begin
 64         if(~rst_n)
 65             begin
 66                 load_ir <= 1'b0;
 67                 clk_inc <= 1'b0;
 68                 load_pc <= 1'b0;
 69                 load_acc <= 1'b0;
 70                 addr_sel <= 1'b0;
 71                 data_link <= 1'b0;
 72                 wr <= 1'b0;
 73                 rd <= 1'b0;
 74             end
 75         else
 76             case(state)
 77                 FETCH1  :   begin
 78                                 load_ir <= 1'b1;
 79                                 clk_inc <= 1'b0;
 80                                 load_pc <= 1'b0;
 81                                 load_acc <= 1'b0;
 82                                 addr_sel <= 1'b1;
 83                                 data_link <= 1'b0;
 84                                 wr <= 1'b0;
 85                                 rd <= 1'b1;
 86                             end
 87                 FETCH2  :   begin
 88                                 load_ir <= 1'b1;
 89                                 clk_inc <= 1'b1;    //一次
 90                                 load_pc <= 1'b0;
 91                                 load_acc <= 1'b0;
 92                                 addr_sel <= 1'b1;
 93                                 data_link <= 1'b0;
 94                                 wr <= 1'b0;
 95                                 rd <= 1'b1;
 96                             end
 97                 FETCH3  :   begin
 98                                 load_ir <= 1'b0;
 99                                 clk_inc <= 1'b0;
100                                 load_pc <= 1'b0;
101                                 load_acc <= 1'b0;
102                                 addr_sel <= 1'b0;
103                                 data_link <= 1'b0;
104                                 wr <= 1'b0;
105                                 rd <= 1'b0;
106                             end
107                 EXZIT1  :   begin
108                                 load_ir <= 1'b0;
109                                 clk_inc <= 1'b1;   //指向下一条指令首地址或(当执行跳过指令时:表示跳过下一条指令的高8位地址)
110                                 load_pc <= 1'b0;
111                                 load_acc <= 1'b0;
112                                 addr_sel <= 1'b0;
113                                 data_link <= 1'b0;
114                                 wr <= 1'b0;
115                                 rd <= 1'b0;
116                             end
117                 EXZIT2  :   begin           //在这个状态就要用ir操作码,前面状态都是相同的取操作码和进行操作数据的地址(可以读/写)
118                                 if((ir == RD ) || (ir == XOR) || (ir == SFT) || (ir == SUB))
119                                     begin
120                                         load_ir <= 1'b0;
121                                         clk_inc <= 1'b0;
122                                         load_pc <= 1'b0;
123                                         load_acc <= 1'b0;
124                                         addr_sel <= 1'b0;
125                                         data_link <= 1'b0;
126                                         wr <= 1'b0;
127                                         rd <= 1'b1;
128                                     end
129                                 else
130                                     if(ir == NJMP)
131                                         begin
132                                             load_ir <= 1'b0;
133                                             clk_inc <= 1'b0;        
134                                             load_pc <= 1'b1;        /******可以不需要这个可以给0;但要保证数据的准确性所以在这给了1*****/
135                                             load_acc <= 1'b0;
136                                             addr_sel <= 1'b0;
137                                             data_link <= 1'b0;
138                                             wr <= 1'b0;
139                                             rd <= 1'b0;
140                                         end
141                                     else
142                                         if(ir == WR)    //拉高data_link
143                                             begin
144                                                 load_ir <= 1'b0;
145                                                 clk_inc <= 1'b0;
146                                                 load_pc <= 1'b0;
147                                                 load_acc <= 1'b0;
148                                                 addr_sel <= 1'b0;
149                                                 data_link <= 1'b1;
150                                                 wr <= 1'b0;
151                                                 rd <= 1'b0;
152                                             end
153                                         else
154                                             begin
155                                                 load_ir <= 1'b0;
156                                                 clk_inc <= 1'b0;
157                                                 load_pc <= 1'b0;
158                                                 load_acc <= 1'b0;
159                                                 addr_sel <= 1'b0;
160                                                 data_link <= 1'b0;
161                                                 wr <= 1'b0;
162                                                 rd <= 1'b0;
163                                             end
164                             end
165                 EXZIT3  :   begin
166                                 if(ir == SKZ)//拉高clk_inc,表示跳过下一条指令的低8位地址
167                                     begin
168                                         load_ir <= 1'b0;
169                                         clk_inc <= 1'b1;
170                                         load_pc <= 1'b0;
171                                         load_acc <= 1'b0;
172                                         addr_sel <= 1'b0;
173                                         data_link <= 1'b0;
174                                         wr <= 1'b0;
175                                         rd <= 1'b0;
176                                     end
177                                 else
178                                     if(ir == NJMP)
179                                         begin
180                                             load_ir <= 1'b0;
181                                             clk_inc <= 1'b1;        /****当clk_inc为高*******/
182                                             load_pc <= 1'b1;        /******load_pc也为高时,才跳转到指令操作地址所指向的存储空间中执行新的指令*****/
183                                             load_acc <= 1'b0;
184                                             addr_sel <= 1'b0;
185                                             data_link <= 1'b0;
186                                             wr <= 1'b0;
187                                             rd <= 1'b0;
188                                         end
189                                 else
190                                     if(ir == WR)            //拉高data_link和wr
191                                         begin
192                                             load_ir <= 1'b0;
193                                             clk_inc <= 1'b0;
194                                             load_pc <= 1'b0;
195                                             load_acc <= 1'b0;
196                                             addr_sel <= 1'b0;
197                                             data_link <= 1'b1;
198                                             wr <= 1'b1;
199                                             rd <= 1'b0;
200                                         end
201                                     else
202                                     begin
203                                         load_ir <= 1'b0;
204                                         clk_inc <= 1'b0;
205                                         load_pc <= 1'b0;
206                                         load_acc <= 1'b0;
207                                         addr_sel <= 1'b0;
208                                         data_link <= 1'b0;
209                                         wr <= 1'b0;
210                                         rd <= 1'b0;
211                                     end
212                             end
213                 EXZIT4  :   begin   //在这个状态也要用ir操作码
214                                 if((ir == RD ) || (ir == XOR) || (ir == SFT) || (ir == SUB))begin
215                                     load_ir <= 1'b0;
216                                     clk_inc <= 1'b0;
217                                     load_pc <= 1'b0;
218                                     load_acc <= 1'b1;
219                                     addr_sel <= 1'b0;
220                                     data_link <= 1'b0;
221                                     wr <= 1'b0;
222                                     rd <= 1'b0;
223                                 end
224                                 else
225                                 begin
226                                     load_ir <= 1'b0;
227                                     clk_inc <= 1'b0;
228                                     load_pc <= 1'b0;
229                                     load_acc <= 1'b0;
230                                     addr_sel <= 1'b0;
231                                     data_link <= 1'b0;
232                                     wr <= 1'b0;
233                                     rd <= 1'b0;
234                                 end
235                             end
236                 EXZIT5  :   begin
237                                 if(ir == SKZ)       //拉高clk_inc,指向下一条指令首地址
238                                     begin
239                                         load_ir <= 1'b0;
240                                         clk_inc <= 1'b1;
241                                         load_pc <= 1'b0;
242                                         load_acc <= 1'b0;
243                                         addr_sel <= 1'b0;
244                                         data_link <= 1'b0;
245                                         wr <= 1'b0;
246                                         rd <= 1'b0;
247                                     end
248                                 else
249                                     begin
250                                         load_ir <= 1'b0;
251                                         clk_inc <= 1'b0;
252                                         load_pc <= 1'b0;
253                                         load_acc <= 1'b0;
254                                         addr_sel <= 1'b0;
255                                         data_link <= 1'b0;
256                                         wr <= 1'b0;
257                                         rd <= 1'b0;
258                                     end
259                             end
260                 default :   begin
261                                 load_ir <= 1'b0;
262                                 clk_inc <= 1'b0;
263                                 load_pc <= 1'b0;
264                                 load_acc <= 1'b0;
265                                 addr_sel <= 1'b0;
266                                 data_link <= 1'b0;
267                                 wr <= 1'b0;
268                                 rd <= 1'b0;
269                             end
270             endcase
271     end
272 
273 endmodule

 

3、计算组件

1 ///********************计算组件*************************/
 2 ////主要完成数学或者逻辑运算,是CPU的核心组件
 3 module alu(clk, rst_n, data, acc, alu_en, ir, alu_out);
 4 
 5     input clk, rst_n;   //系统时钟、复位
 6     //控制信号
 7     input alu_en;       //当alu_en=1计算 
 8     input [15:13] ir;   //操作码ir[15:13]    
 9     
10     input [7:0] data;   //外部存储器读取的数通过数据总线表示
11     input [7:0] acc;    //运算时候所有的指令需要累加器参与运算
12     
13     //输出运算结果
14     output reg [7:0] alu_out;   //计算输出
15     
16     parameter   HLT    =   3'b000,
17                 RD     =   3'b001,
18                 WR     =   3'b010,
19                 SFT    =   3'b011,
20                 XOR    =   3'b100,
21                 SUB    =   3'b101,
22                 SKZ    =   3'b110,
23                 NJMP   =   3'b111;
24     
25     reg [7:0] data_r;
26     always @ (posedge clk, negedge rst_n)
27     begin
28         if(~rst_n)
29             data_r <= 8'd0;
30         else
31             data_r <= data;
32     end
33     
34     always @ (posedge clk, negedge rst_n)
35     begin
36         if(~rst_n)
37             alu_out <= 8'd0;
38         else
39             if(alu_en)
40                 case(ir)
41                     HLT   :   alu_out <= acc;                     //HLT(空指令):该指令不做任何操作
42                     RD    :   alu_out <= data_r;                    //RD:将操作地址空间的数据读出,保存在累加器中
43                     WR    :   alu_out <= acc;                     //WR:将累加器的数据写入操作地址空间中
44                     SFT   :   alu_out <= {data_r[6:0], data_r[7]};    //SFT:将操作地址空间的数据读出(循环)左移1位,然后计算结果保存在累加器中
45                     XOR   :   alu_out <= data_r ^ acc;              //XOR:将操作地址空间的数据读出和累加器相异或,然后计算结果保存在累加器中
46                     SUB   :   alu_out <= data_r - acc;              //SUB:将操作地址空间的数据读出和累加器相减,然后计算结果保存在累加器中
47                     SKZ   :   alu_out <= acc;                     //SKZ:跳过下一条指令执行下下一条指令
48                     NJMP  :   alu_out <= acc;                     //NJMP:跳转到该指令所指向的地址空间处读取新的指令
49                     default :   alu_out <= alu_out;
50                 endcase
51     end
52 
53 endmodule

 

4、累加器

1 /*****************累加器***********************/
 2 //主要用于保存ALU的计算结果
 3 module acc_m(clk, rst_n, alu_out, load_acc, acc);
 4 
 5     input clk, rst_n;       //系统时钟、复位
 6     
 7     //控制信号
 8     input load_acc;         //当load_acc=1的时候将alu的计算结果保存到累加器中
 9     
10     input [7:0] alu_out;    //计算组件计算的结果
11     output reg [7:0] acc;   //将计算组件计算的结果存入acc
12     
13     always @ (posedge clk, negedge rst_n)
14     begin
15         if(~rst_n)
16             acc <= 8'd0;
17         else
18             if(load_acc)
19                acc <= alu_out;
20     end
21     
22 endmodule

 

5、指令寄存

1 /*******************指令集寄存器*******************/
 2 //寄存当前用于分析和执行的指令
 3 module ir_register(clk, rst_n, data, load_ir, ir);
 4 
 5     input clk, rst_n;   //系统时钟、复位(CPU核心工作时钟)
 6     //控制信号
 7     input load_ir;      //当load_ir=1的时候从数据总线加载指令
 8     //数据总线
 9     input [7:0] data;   //CPU数据总线默认8bit
10     //输出指令
11     output reg [15:0] ir;   //指令:默认16bit
12     
13     //Note:由于数据总线8位的,指令是16位,因此需要加载两次才能完成一次指令的加载,在这里先加载指令高8位,再加载指令低8位
14     always @ (posedge clk, negedge rst_n)
15     begin
16         if(~rst_n)
17             ir <= 16'd0;
18         else
19             if(load_ir)
20                 ir <= {ir[7:0], data[7:0]};
21     end
22 
23 endmodule

 

6、指令地址发生器

1 /******************指令地址发生器******************/
 2 //主要用于产生指令地址(指令放在存储器中)
 3 module pc_addr_gen(clk_inc, rst_n, load_pc, pc_addr, ir);
 4     
 5     input rst_n;        //系统复位
 6     //控制信号
 7     input clk_inc;      //用于控制指令地址变化,该信号上升沿地址自加
 8     input load_pc;      //当该信号为高电平的时候跳转指令地址所指向的存储空间读取指令;当load_pc=1,加载新的指令地址
 9     input  [12:0] ir;   //指令操作地址
10     //输出指令地址
11     output reg [12:0] pc_addr;
12 
13     always @ (posedge clk_inc, negedge rst_n)
14     begin
15         if(~rst_n)
16         pc_addr <= 13'd0;   //复位初始地址。文件地址初始为0,我们如果改初始地址,则将会跳过一些指令(不执行一些指令)
17         else
18             if(load_pc)
19                 pc_addr <= ir[12:0];
20             else
21                 pc_addr <= pc_addr + 1'b1;  //若没有跳转,则指令地址+1
22     end
23 
24 endmodule

 

7、数据选择

1 module data_sel_m(data_sel, acc, data);
2 
3     input data_sel;
4     input [7:0] acc;
5     inout [7:0] data;
6     
7     assign data = (data_sel) ? acc : 8'hzz; //进行读操作时,data为高阻状态,读取数据
8 
9 endmodule

 

8、地址路由

1 module addr_sel_m(ir, pc_addr, addr_sel, addr);
 2 
 3     input [12:0] ir;
 4     input [12:0] pc_addr;
 5     input addr_sel;
 6     output reg [12:0] addr;
 7     
 8     always @ (*)
 9     begin
10         if(addr_sel)
11             addr = pc_addr;
12         else
13             addr = ir;
14     end
15     
16 endmodule