0 回顾
前面的内容中,第一类R型指令分析,我们完成了一类R型指令的设计,完成了其数据通路,构建了相应的部件,并且完成了从ROM中取指,成功进行了基本的功能仿真,进行了综合和实现,但是没有完成综合和实现的时序仿真。
下面,我们要继续进行其他R型指令的分析,这次我们只分析三个移位指令,也就是sll srl sra
,然后改进我们已有的数据通路,完成对应的设计。
1 移位指令分析
我们先看看指令编码
instruction | op | rs | rt | rd | shamt | func |
---|---|---|---|---|---|---|
sll | 000000 | 00000 | rt | rd | shamt | 000000 |
srl | 000000 | 00000 | rt | rd | shamt | 000010 |
sra | 000000 | 00000 | rt | rd | shamt | 000011 |
再看看其功能逻辑
三个指令的逻辑都是一样的
- rd <- (rt op shamt)
- op:
- srl: >>
- sll: <<
- sra: >>>
指令的使用:instruction rd,rs,shamt
值得注意的是,shamt是5位,范围是0~31.
需要的器件和信号
我们依次分析一下,完成操作,需要改进哪些部件和信号。
很容易知道,我们只需要改进一下ALU的输入数据和控制即可,同时控制器要识别出这三个指令,增加一些相关信号。
- ALU
- 增加shamt的输入
- 移位操作不需要增加,之前已经实现过
- 移位操作的输入需要增加选择,使得rt和shamt被正确输入
- 为ALU增加控制信号
Sftmd
,表明是这几个移位操作
- 控制器:增加控制信号
Sftmd
输出到ALU
我们原来的数据通路就变成了
3 ALU和控制器设计改进
3.1 控制器设计
我们把之前的也拿出来,同时标出新增内容。
- 输入信号:
op func
- 输出信号:
RegWrite ALUop
,新增Sftmd
instruction | op | func | ALUop | RegWrite | Sftmd 新增 |
---|---|---|---|---|---|
add | 000000 | 100000 | 0000 | 1 | 0 |
addu | 000000 | 100001 | 0001 | 1 | 0 |
sub | 000000 | 100010 | 0010 | 1 | 0 |
subu | 000000 | 100011 | 0011 | 1 | 0 |
and | 000000 | 100100 | 0100 | 1 | 0 |
or | 000000 | 100101 | 0101 | 1 | 0 |
xor | 000000 | 100110 | 0110 | 1 | 0 |
nor | 000000 | 100111 | 0111 | 1 | 0 |
slt | 000000 | 101010 | 1000 | 1 | 0 |
sltu | 000000 | 101011 | 1001 | 1 | 0 |
sllv | 000000 | 101010 | 1010 | 1 | 0 |
srlv | 000000 | 000110 | 1011 | 1 | 0 |
srav | 000000 | 000111 | 1100 | 1 | 0 |
新增 | |||||
sll | 000000 | 000000 | 1010 | 1 | 1 |
srl | 000000 | 000010 | 1011 | 1 | 1 |
sra | 000000 | 000011 | 1100 | 1 | 1 |
3.2 ALU设计
- 数据输入:
R[rs] R[rd]
,新增shamt
- 控制输入:
ALUop
,新增Sftmd
- 数据输出:
R[rd]
特别的,当Sftmd == 1
(表明是移位指令)的时候,ALU增加了多路选择器,将sllv
等指令的输入由rt
和rs
变成了rt
和shamt
。
也就是在rs
和shamt
之间增加了选择器,当Shtmd == 1
使用shamt
作为输入,注意需要0扩展为32位进行运算。
注意:改了输入输出端口,相应的实例也要改。
4 Verilog实现
我们下面改进代码!
4.1 Bug修复:sllv等指令的ALU错误
设计中发现,之前的设计有错误!是在移位指令的时候!我们先来改正它,并且改进我们的测试实例。
以sllv
举例,它是使用方法是sllv rd,rt,rs
,逻辑是rd <- rt << rs
。
汇编程序员视角
是正常指令的使用,逻辑也是正常的,但是,内部逻辑却不一样了。
内部逻辑
例如sllv $5,$10,$9
,功能操作是$5 = $10 << $9
,而内部是
- rt = $10 对应ALU的
B
- rs = $9 对应ALU的
A
- ALU操作是
B << A
,而不是A << B
这里之前犯的错误是,改变了程序员视角的指令……,将上层和底层搞混了……
4.2 新的ALU
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/11/14 22:30:23
// Design Name:
// Module Name: ALU_1
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//module ALU_1(// datainput [31:0] A,input [31:0] B,input [4:0] shamt,// controlinput [3:0] ALUop,input Sftmd, // shift instruction controloutput 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;// for shift instructions
// select data: if (Sftmd == 1) input shamt else input rs
wire [31:0] A_or_Shift = (Sftmd == 0) ? A : {27'b0,shamt};/* calculate */
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: // sllv 10beginALUresult <= B << A_or_Shift; // NOTE: not A << B!end4'b1011: // srlvbeginALUresult <= B >> A_or_Shift; // NOTE: not A >> B!end4'b1100: // srav // note: ******signed*******//beginALUresult <= B_signed >>> A_or_Shift; // NOTE: not A_signed >> B!enddefault:beginALUresult <= 0;endendcase
endendmodule
关注移位指令的部分即可,同时注意最开始的多路选择器。
4.3 新的control
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2020/11/14 22:30:48
// Design Name:
// Module Name: control_1
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//module control_1(input [5:0] op,input [5:0] func,output reg RegWrite,output reg Sftmd, // indicate the instruction is sll/srl/sraoutput reg [3:0] ALUop);always @(*)
beginif(op == 6'b0)begincase (func)6'b100000: // addbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0000;end6'b100001: // addubeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0001;end6'b100010: // subbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0010;end6'b100011: // sububeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0011;end6'b100100: // andbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0100;end6'b100101: // orbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0101;end6'b100110: // xorbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0110;end6'b100111: // norbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b0111;end6'b101010: // sltbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b1000;end6'b101011: // sltubeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b1001;end6'b000100: // sllvbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b1010;end6'b000110: // srlvbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b1011;end6'b000111: // sravbeginRegWrite <= 1;Sftmd <= 0;ALUop <= 4'b1100;end6'b000000: // sllbeginRegWrite <= 1;Sftmd <= 1;ALUop <= 4'b1010;end6'b000010: // srlbeginRegWrite <= 1;Sftmd <= 1;ALUop <= 4'b1011;end6'b000011: // srabeginRegWrite <= 1;Sftmd <= 1;ALUop <= 4'b1100;enddefault:beginRegWrite <= 0;Sftmd <= 0;ALUop <= 4'b1111;endendcaseendelsebeginRegWrite <= 0;Sftmd <= 0;ALUop <= 4'b1111;end
endendmodule
注意新增的信号。
4.4 新的datapath
`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,output [31:0] result // 测试syntheses,没有输出的模块是恐怖的);/******** 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 ( pcOld[15:2] ),.douta ( instruction ));/******** Reg Files ********/// reg_files_1 Inputs
wire [31:0] ALUresult;/// 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 = ALUresult;
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 ( instruction[25:21] ),.rB ( instruction[20:16] ),.rW ( instruction[15:11] ),.writeData ( ALUresult ),.RegWrite ( RegWrite ),.A ( A ),.B ( B ));/******** ALU ********/// ALU_1 Inputs
// wire [31:0] A;
// wire [31:0] B;
wire [3:0] ALUop;
wire Sftmd;// ALU_1 OutputsALU_1 u_ALU_1 (.A ( A ),.B ( B ),.shamt ( instruction[10:6]),.ALUop ( ALUop ),.Sftmd ( Sftmd ),.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 ( instruction[31:26] ),.func ( instruction[5:0] ),.RegWrite ( RegWrite ),.Sftmd ( Sftmd ),.ALUop ( ALUop ));assign result = ALUresult;endmodule
注意实例化的时候,信号不要弄错。
4.5 旧的测试:tb_datapath
激励块内容不变。
4.6 新的测试用例
nop
add $1,$2,$3 # $1 = 2 + 3 = 5
addu $2,$4,$1 # $2 = 4 + 5 = 9
sub $4,$2,$1 # $4 = 9 - 5 = 4
subu $5,$4,$3 # $5 = 4 - 3 = 1and $6,$7,$8 # $6 = 0111 and 1000 = 0
or $7,$6,$8 # $7 = 0 or 1000 = 8
xor $7,$6,$8 # $7 = 0000 xor 1000 = 1000 = 8
nor $8,$7,$6 # $8 = not (1000 or 0) = 11111111111110111slt $10,$11,$12 # $10 = 11 < 12 = 1 # 应该用负数验证,以后再说
sltu $10,$12,$11 # $10 = 12 > 11 = 0# sllv $12,$5,$13 # $12 = 1101 << 1 = 1101_0 = 1A 【注意此处的倒置问题! sllv rd,rt,rs】
# srlv $12,$5,$13 # $12 = 1101 >> 1 = 110 = 6
# srav $14,$5,$15 # $14 = 1111 >>> 1 = 111 = 7 应该用负数验证,以后再说# 上面3条是错误的!我们应该改的不是使用,而是内部运算逻辑
# 对于使用者来说,逻辑就是 $13 << $5
# 而实际的编码是 rt = $13,rs = $5,这与一般的指令不一样
# 因此,我们在ALU运算中 rt--B,rs--A,应该是 【B << A】,而不是 A >> B。
sllv $12,$13,$5 # $12 = 1101 << 1 = 1101_0 = 1A
srlv $12,$13,$5 # $12 = 1101 >> 1 = 110 = 6
srav $14,$15,$5 # $14 = 1111 >>> 1 = 111 = 7 应该用负数验证,以后再说sll $16,$17,2 # $16 = 1_0001 << 2 = 100_0100 = 44
srl $16,$18,2 # $16 = 1_0010 >> 2 = 0100 = 4
sra $16,$19,2 # 应该用负数验证,以后再说 $16 = 4
特别注意最后移位指令,注意注释内容。
指令编码的coe文件:
memory_initialization_radix = 16;
memory_initialization_vector =
00000000,
00430820,
00811021,
00412022,
00832823,
00e83024,
00c83825,
00c83826,
00e64027,
016c502a,
018b502b,
00ad6004,
00ad6006,
00af7007,
00118080,
00128082,
00138083;
最终测试
经过了每个模块的RTL优化和最终的仿真测试,结果没有问题!我们又为之前的数据通路增加了新的3个指令!Fighting!