自己动手写CPU - 1-CSDN博客https://blog.csdn.net/weixin_46766770/article/details/144933071自己动手写CPU - 2-CSDN博客https://blog.csdn.net/weixin_46766770/article/details/144935050自己动手写CPU - 3-CSDN博客https://blog.csdn.net/weixin_46766770/article/details/144935080
书接上文.
上一节中有一个问题未解决: 仔细看了下, 应该还无法完成从1加到10的任务. 没有条件跳转, 需要利用CMP的结果来控制跳转, 例如: 比较循环次数到了10了没? 小于10则继续循环.
这一节补上它, 只加了四五行代码吧. 直接上代码.
增加了两个指令, 相等则跳, 不相等则跳, 不满足条件时输出R7+1
新加了eq寄存器, 如果op是5则更新它, 用in1==in2是否成立
6/7两个新加的指令, 按代码的逻辑看, 也不一定只能作用于R7, 它也可以作用于R0-R6, 但它在不成立时返回的是in1+1, 操作R0/R6似乎没有明确的含义(没有使用场合), 写程序时只用来操作R7.
全部代码如下(仿真时发现没有rst无法仿真, 全部信号是xxxxx, 也加上了rst)
module cpu(
input clk,rst,
input [15:0]op, //[15:11]指令 [10:8]目标Reg索引 [7:0]立即数或源Reg索引
output reg eq,
output reg[7:0]R[8] // R0-R7组织成寄存器组, 方便用寄存器索引 选择
);
reg [7:0] in1,in2, out1;
always @ (*) begin
in1 = R[op[10:8]];
in2 = !op[11]? R[op[2:0]] : op[7:0];
end
always @ (*) begin
case(op[15:12])
0: out1 = in2; // ldr
1: out1 = in1 + in2; // add
2: out1 = in1 - in2; // sub
3: out1 = in1 & in2; // and
4: out1 = in1 | in2; // or
5: out1 = in1; // cmp
6: out1 = eq ? in2:in1+1;// jeq
7: out1 = !eq ? in2:in1+1;// jne
default: out1 = 0;
endcase
end
wire [7:0]sel;
decode38 decode38_inst1(
.abc(op[10:8]),
.out(sel)
);
always @(posedge clk or negedge rst) begin
if (!rst) begin
eq=0; R[0:7]={0,0,0,0,0,0,0,0};
end else begin
if(op[15:12]==5) eq=(in1==in2);
if(sel[0]) R[0]=out1;
if(sel[1]) R[1]=out1;
if(sel[2]) R[2]=out1;
if(sel[3]) R[3]=out1;
if(sel[4]) R[4]=out1;
if(sel[5]) R[5]=out1;
if(sel[6]) R[6]=out1;
if(sel[7])
R[7]=out1;
else
R[7]=R[7]+1;
end
end
endmodule
生成的RTL变化也不大, 下图将增加的单元也标注了出来.
RTL逻辑电路图
直接使用vivado跑测试, 测试代码
//`timescale 1ns / 1ps
module tb_cpu();
reg clk,rst;
reg [15:0]op;
wire eq;
wire [7:0]R[8];
cpu cpu_inst1(
.clk(clk),
.rst(rst),
.op(op),
.eq(eq),
.R(R)
);
initial begin
clk = 0;
rst=0;
#5 rst=1;
op = 'h0001+'h0800; //ldr R0, #1
#20 op = 'h0111+'h0800; //ldr R1, #11
#20 op = 'h0222+'h0800; //ldr R2, #22
#20 op = 'h0666+'h0800; //ldr R6, #66
#20 op = 'h0602; //mov R6, R2
#20 op = 'h1022+'h0800; //add R0, #22
#20 op = 'h1106; //add R1, R6
#20 op = 'h2100; //sub R1, R0
#20 op = 'h5101; //cmp R1, R1
#20 op = 'h6701+'h0800; //beq #01
#20 op = 'h5102; //cmp R1, R2
end
always #10 clk = ~clk;
endmodule
测试代码比较简单, 先例化一个cpu, 连上信号线. 设好时钟, 每20个时间单位给一条指令, 然后观察R0-R7以及eq状态的变化. 仿真图如下:
可以看到, ldr add sub 执行的都是对的, cmp可以影响到eq状态, beq也成功修改了R7的值.
一开始提出的目标可以实现.
(数了下, 一共53行代码. 周六下午开始写, 写到凌晨.
周日本来不写了, 天黑时又有了想法, 还是把它写完吧, 正好0点收工)