简介
这篇博文主要介绍的是基于mips的 singlecycle CPU设计及实现的第二个单元ALU算术逻辑单元。
详解
衔接上篇的寄存器的讨论,这次将对ALU算术逻辑单元进行编写。mips支持的指令有很多指令形式(instruction format),如
R-type:Op, rs, rt, rd 单纯只用到了寄存器的值,
I-type:OP rt, IMM(rs) 或 OP rs, rt, IMM 用到了立即值,(这里的立即值是16位)
J-type:Op, address 用到了地址。(这里的地址是26位)。
为什么要进行分类呢,因为根据不同的类型,ALU 算术逻辑单元的数据总线的值可能会有不同。
例如:
R-type ADD:bus1 就是rt, bus2 就是rd
I-type LD: bus1 就是rs, bus2就是 IMM
I-type BEQ: bus1 就是rs, bus2就是 IMM(减法判断结果是不是0)
j-type JAL: 不需要ALU,PC单元能够单独解决。
算术逻辑单元只能实现一些比较简单的算术,但是mips却支持很多的指令。因为这些复杂的指令可以通过简单的逻辑算术和别的模块解决。
但是为什么不能直接根据opcode直接进行运算,像 opcode == add 把rt + rd的值放入rs。直接把多个模块需要合作解决的和在一起。 因为通过简单的逻辑指令和别的模块的合作,能灵活的实现复杂的指令,最重要的是能省下不少晶体管。
那为什么算术逻辑单元的输出还有负数符号,溢出符号。但是在总的singlecycle CPU设计上没有这些输出信号,因为mips不关心溢出和负数符号,但是零符号是需要用来检测分支的。
ALU 算术逻辑单元
如图所示上图展示了算术逻辑单元的一些输入及输出。
输入:
PortA(31:0) 32位的bus1输入
PortB(31:0) 32位的bus2输入
ALUOP(3:0) 算术逻辑单元指令选择
输出:
Negative 负符号
Overflow 溢出符号
Zero 零符号
Output Port(31:0) 算术逻辑单元输出
这次因为接口比较少,所以偷了懒没有写接口文件,但是写了FPGA文件。
主代码alu.sv
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
| `include "cpu_types_pkg.vh" module alu ( input logic [31:0] portA, input logic [31:0] portB, input logic [3:0] aluOP, output logic negFlag, output logic oveFlag, output logic zerFlag, output logic [31:0] outputPort ); import cpu_types_pkg::*; always_comb begin negFlag = 0; oveFlag = 0; zerFlag = 0; outputPort = '0; if (aluOP == ALU_SLL) outputPort = portB << portA; else if (aluOP == ALU_SRL)) outputPort = portB >> portA; else if (aluOP == ALU_ADD) begin) outputPort = portA + portB; if (outputPort[31] != (portA[31] ^ portB[31])) oveFlag = 1; end else if (aluOP == ALU_SUB) begin outputPort = portA - portB; if (outputPort[31] != (portA[31] ^ portB[31])) oveFlag = 1; end else if (aluOP == ALU_AND) outputPort = portA & portB; else if (aluOP == ALU_OR) outputPort = portA | portB; else if (aluOP == ALU_XOR) outputPort = portA ^ portB; else if (aluOP == ALU_NOR) outputPort = ~(portA | portB); else if (aluOP == ALU_SLTU) outputPort = portA < portB; else if (aluOP == ALU_SLT) begin if (portA[31] == 1 && portB[31] == 0) outputPort = 1; else if (portA[31] == 0 && portB[31] == 1) outputPort = 0; else outputPort = portA < portB; end if (outputPort == 0) zerFlag = 1; if (outputPort[31] == 1) negFlag = 1; end endmodule
|
这次测试代码直接用最简单粗暴的方式测试了,但是覆盖率还是100%。(根据questasim的report判断的)
测试代码alu_tb.sv
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
| `include "cpu_types_pkg.vh"
`timescale 1 ns / 1 ns module alu_tb();
parameter PERIOD = 10;
logic CLK = 0, nRST;
logic [31:0] portA; logic [31:0] portB; logic [3:0] aluOP; logic negFlag; logic oveFlag; logic zerFlag; logic [3:0] i; logic [31:0] outputPort; always #(PERIOD/2) CLK++;
import cpu_types_pkg::*; alu DUT(portA,portB,aluOP,negFlag,oveFlag,zerFlag,outputPort); initial begin portA = '0; portB = '0; aluOP = '0;
for (i = 0; i < 14; i = i + 1) begin @(posedge CLK) portA = '1; portB = '1; aluOP = i; @(posedge CLK) portA = 0; portB = 0; aluOP = i; @(posedge CLK) portA = 1; portB = 0; aluOP = i;
@(posedge CLK) portA = 0; portB = 1; aluOP = i;
@(posedge CLK) portA = '0; portB = '0; aluOP = i;
end @(posedge CLK) portA = '0; portB = '0; aluOP = '1; @(posedge CLK) portA = '0; portB = '0; aluOP = 0; @(posedge CLK)
$stop; end endmodule
|
FPGA代码alu_fpga.sv
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
|
`include "cpu_types_pkg.vh"
module alu_fpga ( input logic CLOCK_50, input logic [3:0] KEY, input logic [17:0] SW, output logic [17:0] LEDR, output logic [6:0] HEX0, output logic [6:0] HEX1, output logic [6:0] HEX2, output logic [6:0] HEX3 ); import cpu_types_pkg::*; logic negFlag; logic oveFlag; logic zerFlag; logic [31:0] output1; reg [31:0] register; reg [31:0] nregister; logic [31:0] portA; assign portA = {16*{SW[16]},SW[15:0]}; always_ff @(posedge CLOCK_50) begin if (SW[17]) register <= {16*{SW[16]},SW[15:0]}; else register <= register; end alu DUT(.portA(portA),.portB(register),.aluOP(~KEY),.negFlag(negFlag),.oveFlag(oveFlag),.zerFlag(zerFlag),.outputPort(output1));
always_comb begin unique casez (output1[3:0]) 'h0: HEX0 = 7'b1000000; 'h1: HEX0 = 7'b1111001; 'h2: HEX0 = 7'b0100100; 'h3: HEX0 = 7'b0110000; 'h4: HEX0 = 7'b0011001; 'h5: HEX0 = 7'b0010010; 'h6: HEX0 = 7'b0000010; 'h7: HEX0 = 7'b1111000; 'h8: HEX0 = 7'b0000000; 'h9: HEX0 = 7'b0010000; 'ha: HEX0 = 7'b0001000; 'hb: HEX0 = 7'b0000011; 'hc: HEX0 = 7'b0100111; 'hd: HEX0 = 7'b0100001; 'he: HEX0 = 7'b0000110; 'hf: HEX0 = 7'b0001110; default: HEX0 = 7'b1000000; endcase
unique casez (output1[7:4]) 'h0: HEX1 = 7'b1000000; 'h1: HEX1 = 7'b1111001; 'h2: HEX1 = 7'b0100100; 'h3: HEX1 = 7'b0110000; 'h4: HEX1 = 7'b0011001; 'h5: HEX1 = 7'b0010010; 'h6: HEX1 = 7'b0000010; 'h7: HEX1 = 7'b1111000; 'h8: HEX1 = 7'b0000000; 'h9: HEX1 = 7'b0010000; 'ha: HEX1 = 7'b0001000; 'hb: HEX1 = 7'b0000011; 'hc: HEX1 = 7'b0100111; 'hd: HEX1 = 7'b0100001; 'he: HEX1 = 7'b0000110; 'hf: HEX1 = 7'b0001110; default: HEX1 = 7'b1000000; endcase
unique casez (output1[11:8]) 'h0: HEX2 = 7'b1000000; 'h1: HEX2 = 7'b1111001; 'h2: HEX2 = 7'b0100100; 'h3: HEX2 = 7'b0110000; 'h4: HEX2 = 7'b0011001; 'h5: HEX2 = 7'b0010010; 'h6: HEX2 = 7'b0000010; 'h7: HEX2 = 7'b1111000; 'h8: HEX2 = 7'b0000000; 'h9: HEX2 = 7'b0010000; 'ha: HEX2 = 7'b0001000; 'hb: HEX2 = 7'b0000011; 'hc: HEX2 = 7'b0100111; 'hd: HEX2 = 7'b0100001; 'he: HEX2 = 7'b0000110; 'hf: HEX2 = 7'b0001110; default: HEX2 = 7'b1000000; endcase
unique case(output1[15:12]) 'h0: HEX3 = 7'b1000000; 'h1: HEX3 = 7'b1111001; 'h2: HEX3 = 7'b0100100; 'h3: HEX3 = 7'b0110000; 'h4: HEX3 = 7'b0011001; 'h5: HEX3 = 7'b0010010; 'h6: HEX3 = 7'b0000010; 'h7: HEX3 = 7'b1111000; 'h8: HEX3 = 7'b0000000; 'h9: HEX3 = 7'b0010000; 'ha: HEX3 = 7'b0001000; 'hb: HEX3 = 7'b0000011; 'hc: HEX3 = 7'b0100111; 'hd: HEX3 = 7'b0100001; 'he: HEX3 = 7'b0000110; 'hf: HEX3 = 7'b0001110; default: HEX3 = 7'b1000000; endcase end
assign LEDR[17:14] = portA[3:0];
assign LEDR[13:10] = register[3:0]; assign LEDR[0] = negFlag; assign LEDR[1] = oveFlag; assign LEDR[2] = zerFlag;
endmodule
|
借此alu逻辑算术单元也算完成了。