前言
继续Processor内模块分析,上下文请见完成目录:
结构图还是要先摆好的:
Processor
ALU/Reg_Y/Reg_Z
运算器,负责将Reg_Y的值data1和总线上送过来的值data2根据IR传过来的指令进行运算并输出,这个简化版处理器支持四种运算:
ADD:data1 + data2
SUB:data1 - data2
AND:data1 & data2
NOT:~data1
那么,直接看代码好啦:
1. module alu_unit #( 2. `include "C:/Users/gaoji/Desktop/RISC_SPM/src/para_def.v" 3. )( 4. output reg [WORD_WD -1:0] alu_out, 5. output zero_flag, 6. input [WORD_WD -1:0] alu_in1, alu_in2, 7. input [OPCODE_WD -1:0] opcode 8. ); 9. 10. always @(*)begin 11. case(opcode) 12. NOP: alu_out = 'h0; 13. ADD: alu_out = alu_in2 + alu_in1; 14. SUB: alu_out = alu_in2 - alu_in1; 15. AND: alu_out = alu_in2 & alu_in1; 16. NOT: alu_out = ~alu_in2; 17. default:alu_out = 'hx; 18. endcase 19. end 20. 21. assign zero_flag = ~(|alu_out); 22. 23. endmodule//: alu_unit
代码还是很简单的,就是根据opcode来进行选择处理类型,当拍输出结果,opcode就是从IR中来的;
zero_flag是一个辅助标志,用来表征当前输出结果是否为全0,传入controller。
Reg_Y用来临时存放数据,例如做R1和R2的加法,那么需要先读取R1的值写入Reg_Y,再读取R2的值,最后由ALU进行处理,其代码如之前所示的reg_unit。
Reg_Z用来存放zero_flag,以供Controller随时debug,其实现就是一个简单的D-flop,不赘述代码。
Mux_2/Bus_2
同Mux_1和Bus_1一样是数据总线,不过更像是内部数据总线。Mux_2可以接受ALU/mem/Mux_1的数据,并将之写到Bus_2总线上,Bus_2总线几乎连接着Processor内的所有模块。
Mux_2的代码如下:
1. module mux_3sel1 #( 2. `include "C:/Users/gaoji/Desktop/RISC_SPM/src/para_def.v" 3. )( 4. output reg [WORD_WD -1:0] mux_out, 5. input [WORD_WD -1:0] mux_in0, mux_in1, mux_in2, 6. input [SEL2_WD -1:0] sel 7. ); 8. 9. always @(*)begin 10. case(sel) 11. 'h0: mux_out = mux_in0; 12. 'h1: mux_out = mux_in1; 13. 'h2: mux_out = mux_in2; 14. default: mux_out = {WORD_WD{1'bx}}; 15. endcase 16. end 17. 18. endmodule//: mux_3sel1
Bus_2总线驱动着内部的模块,当需要把Mem的值写入R0时,Controller只需要load_R0,同时Mux_2上选择mem数据到Bus_2,就可以完成了。
Add_R
Add_R模块专门给Mem送地址线的,不管读出的地址还是写入的地址都有他来送,可见我们目前只能实现1R1W的读写访问,其实现方式同样是reg_unit。
好了,Processor的内部实现已经叙述完了,继续结合结构图,来看下我们需要Controller如何控制使数据能够顺畅的字Processor内流转。
Controller
控制信号
Load_PC:Controller置高Load_PC时,Bus_2总线上的数据会写入PC中,一般要执行指令跳转时使用;
Inc_PC:命令PC自加1;
Load_IR:将Bus_2总线上的数据写入IR,一般是用来从mem加载指令到IR;
Load_R0/1/2/3:将Bus_2总线上的数据写入R0/1/2/3;
sel_Bus_1_Mux:控制Mux_1输出,0/1/2/3/4分别对应R0/1/2/3/PC;
Load_Reg_Y:将Bus_2总线上的数据写入Reg_Y;
Load_Reg_Z:将ALU输出写入Reg_Z;
sel_Bus_2_Mux:控制Mux_2输出,0/1/2分别对应ALU/Mux_1/mem;
write:Controller直接作用于mem,写有效;
Controller输出的这些控制总线,总体而言就完成四个功能:
1.决定何时把总线上的数据转载到寄存器上;
2.决定把哪一路数据打到总线上;
3.决定何时将数据写入mem;
4.控制三态总线。
输入instruction
那么Controller如何决定何时控制哪些信号呢?那么就需要解析当前正在执行的指令,这个指令时由IR送到Controller中的;
在我们看完指令集后,再来具体分析这个过程。
Memory
存储通路就是最简单的mem模块,直接看下代码就好了,当拍写入当拍读出的mem:
1. `ifndef MEM_UNIT 2. `define MEM_UNIT 3. 4. module mem_unit #( 5. `include "C:/Users/gaoji/Desktop/RISC_SPM/src/para_def.v" 6. )( 7. output [WORD_WD -1:0] data_out, 8. input [WORD_WD -1:0] data_in, 9. input [ADDR_WD -1:0] addr_in, 10. input write_en, 11. input clk, rst_n 12. ); 13. 14. reg [WORD_WD -1:0] mem[MEM_SIZE -1:0]; 15. always @(posedge clk)begin 16. if(write_en)begin 17. if(DISPLAY_EN == 1) $display("%0t, %m, addr-%0d- update to 'b%8b('h%2h-'d%0d)", $realtime, addr_in, data_in, data_in, data_in); 18. mem[addr_in] <= data_in; 19. end 20. end 21. assign data_out = mem[addr_in]; 22. 23. endmodule//: mem_unit 24. 25. `endif
