【计算机系统设计】实践笔记(2)数据通路构建:第一类R型指令分析(2)
待办事项
- 时钟频率高,取指周期长,远大于执行周期,如何处理?
- 不可综合逻辑的处理
接上一篇
【计算机系统设计】实践笔记(2)数据通路构建:第一类R型指令分析(1)
8.2 ALU运算器
`timescale 1ns / 1ps // // Engineer:jht // Create Date: 2020/11/14 22:30:23 // Module Name: ALU_1 //module ALU_1(// datainput [31:0] A,input [31:0] B,// controlinput [3:0] ALUop,output reg [31:0] ALUresult);// convert A and B to signed numbers wire signed [31:0] A_signed = A; wire signed [31:0] B_signed = B;always @(*) begincase (ALUop)4'b0000: // addbeginALUresult <= A + B;end4'b0001: // addubeginALUresult <= A + B;end4'b0010: // subbeginALUresult <= A - B;end4'b0011: // sububeginALUresult <= A - B;end4'b0100: // andbeginALUresult <= A & B;end4'b0101: // orbeginALUresult <= A | B;end4'b0110: // xorbeginALUresult <= A ^ B;end4'b0111: // norbeginALUresult <= ~(A | B);end4'b1000: // slt // note:********signed********//beginif(A_signed < B_signed)ALUresult <= 1;elseALUresult <= 0;end4'b1001: // sltubeginif(A < B)ALUresult <= 1;elseALUresult <= 0;end4'b1010: // sllvbeginALUresult <= A << B;end4'b1011: // srlvbeginALUresult <= A >> B;end4'b1100: // srav // note: ******signed*******//beginALUresult <= A_signed >>> B;enddefault:beginALUresult <= 0;endendcase endendmodule测试文件tb_ALU_1.v
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/27 10:36:19 // Design Name: // Module Name: tb_ALU_1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module tb_ALU_1;// ALU_1 Inputs reg [31:0] A = 0 ; reg [31:0] B = 0 ; reg [3:0] ALUop = 0 ;// ALU_1 Outputs wire [31:0] ALUresult ;ALU_1 u_ALU_1 (.A ( A [31:0] ),.B ( B [31:0] ),.ALUop ( ALUop [3:0] ),.ALUresult ( ALUresult [31:0] ));initial begin#10ALUop = 0;A = 1;B = 4;#10ALUop = 1;A = 1;B = 5;#10ALUop = 2;A = 4;B = 1;#10ALUop = 3;A = 4;B = 2;// and#10ALUop = 4;A = 32'b1001111;B = 32'b1001001;#10ALUop = 5;A = 32'b1001111;B = 32'b1001001;#10ALUop = 6;A = 32'b1001111;B = 32'b1001001;#10ALUop = 7;A = 32'b1001111;B = 32'b1001001;// slt#30ALUop = 8;A = -1;B = 3;#10ALUop = 9;A = -1;B = 3;#10ALUop = 9;A = 1;B = 3;// sllv#30ALUop = 10;A = 32'b1001111;B = 32'd4;#10ALUop = 11;A = 32'hABCDabcd;B = 32'd4;// srav#30ALUop = 12;A = 32'hABCDabcd;B = 32'd4;#40ALUop = 4'b1111;endendmodule功能仿真成功!
8.2.1 注意事项:有无符号数的运算和比较
主要针对slt sltu srlv srav这几条指令中,涉及到的对有无符号数进行的操作。
原则:Verilog默认都是无符号数,需要显式地声明signed才能进行带符号数运算。
注意代码中的
// convert A and B to signed numbers wire signed [31:0] A_signed = A; wire signed [31:0] B_signed = B;这是将无符号数声明为带符号数的方法。
8.3 Register Files 寄存器堆
reg_files.v
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/14 22:31:09 // Design Name: // Module Name: reg_files_1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module reg_files_1(input clk,input rst_n,/*** read port 1 ***/input [4:0] rA, // rs fieldoutput reg [31:0] A,/*** read port 2 ***/input [4:0] rB, // rtoutput reg [31:0] B,/*** write port ***/input [4:0] rW, // rd or rtinput [31:0] writeData, // datainput RegWrite // if RegWrite == 1,you can write data to reg files);// reg files reg [31:0] register [0:31]; integer i; initial beginfor (i = 0;i < 32;i = i + 1)beginregister[i] <= 0;end end/******* write operation *******/always @(posedge clk) // sequential logic beginif(rst_n == 0) // reset is invalidbeginif((RegWrite == 1'b1) && (rW != 5'b0)) // write is valid and address is not equal zerobeginregister[rW] <= writeData;endelse;endelse; end/******* rA read operation *******/ always @(*) // combinational logic beginif(rst_n == 1)beginA <= 32'b0;endelse if(rA == 5'b0)beginA <= 32'b0;endelsebeginA <= register[rA];end end/******* rB read operation *******/ always @(*) // combinational logic beginif(rst_n == 1)beginB <= 32'b0;endelse if(rB == 5'b0) // $zerobeginB <= 32'b0;endelsebeginB <= register[rB];end endendmodule测试文件tb_reg_files_1.v
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/27 10:11:14 // Design Name: // Module Name: tb_reg_files_1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module tb_reg_files_1;// reg_files_1 Parameters parameter PERIOD = 10;// reg_files_1 Inputs reg clk = 0 ; reg rst_n = 1 ; reg [4:0] rA = 0 ; reg [4:0] rB = 0 ; reg [4:0] rW = 0 ; reg [31:0] writeData = 0 ; reg RegWrite = 0 ;// reg_files_1 Outputs wire [31:0] A ; wire [31:0] B ;initial beginforever#(PERIOD/2) clk=~clk; endinitial begin#(PERIOD*2) rst_n = 0; endreg_files_1 u_reg_files_1 (.clk ( clk ),.rst_n ( rst_n ),.rA ( rA [4:0] ),.rB ( rB [4:0] ),.rW ( rW [4:0] ),.writeData ( writeData [31:0] ),.RegWrite ( RegWrite ),.A ( A [31:0] ),.B ( B [31:0] ));initial begin#20RegWrite = 1;rW = 0;writeData = 32'hff;#10rW = 1;writeData = 32'hff;#10rA = 1;#10rB = 1;#10rA = 0;rB = 0; endendmodule初步功能仿真成功!
9 连接已有器件
9.1 增加ROM
使用IP核,参考东南大学计算机系统设计MOOC9.1节的做法。
9.2 将已有部件连接起来!
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/27 11:41:34 // Design Name: // Module Name: datapath_1 // Project Name: // Target Devices: // Tool Versions: // Description: 仅仅实现了几个简单的R类指令的最简单的数据通路,不与外界交互 // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module datapath_1(input clk,input rst_n);/******** PC ********/// pc_1 Outputs wire [31:0] pcOld;pc_1 u_pc_1 (.clk ( clk ),.rst_n ( rst_n ),.pcNew ( pcOld ), // pcNew = pcOld + 4; no selection.pcOld ( pcOld ));/******** Instruction ROM ********/// blk_mem_gen_0 Inputs wire [13:0] addra = pcOld[15:2];// blk_mem_gen_0 Outputs // instructions wire [31:0] instruction;blk_mem_gen_0 u_blk_mem_gen_0 (.clka ( clk ),.addra ( addra ),.douta ( instruction ));/******** Reg Files ********/// reg_files_1 Inputs wire [4:0] rA = instruction[25:21]; wire [4:0] rB = instruction[20:16]; wire [4:0] rW = instruction[15:11]; wire [31:0] writeData; wire RegWrite;// reg_files_1 Outputs wire [31:0] A; wire [31:0] B;reg_files_1 u_reg_files_1 (.clk ( clk ),.rst_n ( rst_n ),.rA ( rA ),.rB ( rB ),.rW ( rW ),.writeData ( writeData ),.RegWrite ( RegWrite ),.A ( A ),.B ( B ) );/******** ALU ********/// ALU_1 Inputs // wire [31:0] A; // wire [31:0] B; wire [3:0] ALUop;// ALU_1 Outputs wire [31:0] ALUresult = writeData;ALU_1 u_ALU_1 (.A ( A ),.B ( B ),.ALUop ( ALUop ),.ALUresult ( ALUresult ) );/******** controler ********/// control_1 Inputs wire [5:0] op = instruction[31:26]; wire [5:0] func = instruction[5:0];// control_1 Outputs // wire RegWrite // wire [3:0] ALUop;control_1 u_control_1 (.op ( op ),.func ( func ),.RegWrite ( RegWrite ),.ALUop ( ALUop ) );endmodule9.3 测试我们的数据通路
注意赋值的是i而不是0了。
测试文件如下
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/27 12:12:14 // Design Name: // Module Name: tb_datapath_1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module tb_datapath_1;// datapath_1 Parameters parameter PERIOD = 10;// datapath_1 Inputs reg clk = 0 ; reg rst_n = 1 ;// datapath_1 Outputsinitial beginforever #(PERIOD/2) clk=~clk; endinitial begin#(PERIOD*2) rst_n = 0; enddatapath_1 u_datapath_1 (.clk ( clk ),.rst_n ( rst_n ) );endmoduleRTL优化
测试指令如下
以上错误!因为一次性连接了太多器件,不符合单元测试原则,重新开始,重要的是,ROM的IP核没有测试!
9.4 构建取值模块
我们先把PC和ROM连接起来测试。
然后发现……很诡异,PC似乎对ROM不起作用?
这个IP核……居然有延迟??不是瞬间取得指令……需要等一个周期后,再等待上升沿,才能取指。也就是说,ROM的取指需要等待一个额外的时钟周期,这才是真实世界。
9.5 插叙:带延迟的ROM
实际上,访存时间更长,取指比较慢,这个事实我们都知道,现在,我们真地面临这个问题了。
理想取指的时序图,与带一个时钟周期延迟的时序图,是不一样的!
在单周期CPU中
- 理想瞬间取指,那么更新PC值需要是下降沿,而写寄存器堆需要是上升沿
- 带一个时钟周期延迟的,就可以都是上升沿,因为取下一条指令的过程占一个时钟周期,此时CPU就讲当前指令执行完了,也就是取下一条指令和CPU执行当前指令,同步进行。
特别注意:默认值0的指令会比较诡异
在我们的设计中,pc默认是0,因此……0号地址的指令会被直接取出来,但是如果没有复位也是不能指令的,这个情况下,其实可以用nop指令(全是0)作为0号地址的指令。
9.6 完整数据通路的实现与测试
让我们返回看看。
datapath_1.v
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/27 11:41:34 // Design Name: // Module Name: datapath_1 // Project Name: // Target Devices: // Tool Versions: // Description: 仅仅实现了几个简单的R类指令的最简单的数据通路,不与外界交互 // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module datapath_1(input clk,input rst_n);/******** PC ********/// pc_1 Outputs wire [31:0] pcOld;pc_1 u_pc_1 (.clk ( clk ),.rst_n ( rst_n ),.pcNew ( pcOld ), // pcNew = pcOld + 4; no selection.pcOld ( pcOld ));/******** Instruction ROM ********/// blk_mem_gen_0 Inputs wire [13:0] addra = pcOld[15:2];// blk_mem_gen_0 Outputs // instructions wire [31:0] instruction;blk_mem_gen_0 u_blk_mem_gen_0 (.clka ( clk ),.addra ( addra ),.douta ( instruction ));/******** Reg Files ********/// reg_files_1 Inputs wire [4:0] rA = instruction[25:21]; wire [4:0] rB = instruction[20:16]; wire [4:0] rW = instruction[15:11]; wire [31:0] writeData; wire RegWrite;// reg_files_1 Outputs wire [31:0] A; wire [31:0] B;reg_files_1 u_reg_files_1 (.clk ( clk ),.rst_n ( rst_n ),.rA ( rA ),.rB ( rB ),.rW ( rW ),.writeData ( writeData ),.RegWrite ( RegWrite ),.A ( A ),.B ( B ));/******** ALU ********/// ALU_1 Inputs // wire [31:0] A; // wire [31:0] B; wire [3:0] ALUop;// ALU_1 Outputs // wire [31:0] ALUresult = writeData;【】【为什么不能用?】ALU_1 u_ALU_1 (.A ( A ),.B ( B ),.ALUop ( ALUop ),.ALUresult ( writeData ));/******** controler ********/// control_1 Inputs wire [5:0] op = instruction[31:26]; wire [5:0] func = instruction[5:0];// control_1 Outputs // wire RegWrite // wire [3:0] ALUop;control_1 u_control_1 (.op ( op ),.func ( func ),.RegWrite ( RegWrite ),.ALUop ( ALUop ));endmoduleRTL优化
测试文件 tb_datapath_1.v
`timescale 1ns / 1ps // // Company: // Engineer: // // Create Date: 2020/11/27 12:12:14 // Design Name: // Module Name: tb_datapath_1 // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // //module tb_datapath_1;// datapath_1 Parameters parameter PERIOD = 10;// datapath_1 Inputs reg clk = 0 ; reg rst_n = 1 ;// datapath_1 Outputsinitial beginforever #(PERIOD/2) clk=~clk; endinitial begin#(PERIOD*2) rst_n = 0; enddatapath_1 u_datapath_1 (.clk ( clk ),.rst_n ( rst_n ) );endmodule仿真结果
测试指令
指令编码
00000000 00430820 00811021 00412022 00832823 00e83024 00c83825 00c83826 00e64027 016c502a 018b502b 01a56004 01a56006 01e57007注意事项
10 惊人的事实:我们已经构建了完整的数据通路
你可能感到惊讶,但这就是事实,我们已经,构建好了一个CPU,并且它能够执行13条指令!
这简直太酷了不是吗!难以想象……你可能会说?这……就完成了?是的没错,如果我们只需要13条指令的CPU,并且不需要与外界交互的话,真的已经完成了,当然……这个CPU没什么价值,不过后续我们会改进它的不是吗?这很有趣的!
我们会一步步地完成一个完整的CPU,最终变成五级流水线CPU,这简直太棒了!让我们一起加油!
来看看示意图,注意,只是示意图,pc的位宽并不是标准的32位而是8位,总之,这就是完整的数据通路了。
只不过这个CPU还不能与外界交互……但是,它的确能够执行指令了不是吗?后续我们慢慢改进就是了。
10.1 构建我们的第一个CPU
在上面我们已经运行测试过了,不再重复。
综合实现看下面:
实践笔记(2)插叙:综合与实现
10.2 值得优化的点
在有些时候,我们的指令没有准备好,但是Reg已经读取出去了,可能有隐患,我们可以给Reg Files加上读使能信号,但是本次不加了。
11 数据流建模传输问题:不止连线
数据流建模传输问题:赋值传输有方向
经验教训,RTL建模看不出来传输方向! 行为仿真很必要呀!
12 取指延迟
我们都知道,访存是很慢的(相比于CPU执行),在本次示例中,ROM取指,需要2个时钟周期,因此,我们的单周期CPU,更新PC和更新寄存器堆,都可以上升沿触发。
PC也能够保存下一条指令的地址,在取下一条指令的同时,当前指令也在执行,取完下一条指令,当前指令也执行完成了。
我们看时序图,从PC更新,到取得PC对应的指令,需要2个时钟周期。
也就是说,向内存发出指令地址之后,需要两个时钟周期,指令才能被取得,在此期间,CPU内的指令还是原来的指令,该指令也已经在一个时钟周期执行完成,执行一条指令需要三个时钟周期。
你可能疑惑,那pc每个时钟周期更新一次,两个时钟周期才能够取到指令,不会冲突吗?当然不会。想象一下高速公路的汽车! pc发出的时间不一样,是可以排队的,没关系的,不会超车插队,另外,数据变化需要时间的,暂时浅显理解即可。
13 疑惑点
当取指非常慢的时候,由应该如何处理?此时如果仍然每个周期PC + 4,但是10个周期(假设)才能取指,是否仍然可行?
我们试试,将周期改为2ns,也就是500MHz的时钟频率。
好吧……看起来没有问题,这个问题以后再解决。
500MHz都没问题了,那基本就没事了其实。
开发板使用的100MHz,也就是时钟周期是10ns,足够满足本科阶段需求了,这个细节问题目前不需要关注,无伤大雅,应当先抓住主要矛盾。
总结
以上是生活随笔为你收集整理的【计算机系统设计】实践笔记(2)数据通路构建:第一类R型指令分析(2)的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 打胎不孕不育
- 下一篇: 【计算机系统设计】实践笔记(2)插叙:综