- 该文章主要记录ALU-DMA系统设计中ALU的设计点。
1. ALU_TOP架构
- 主要包含四个模块
- ALU_RF:主要由一个深度为16,宽度为32bits的双端口RAM组成。主要用于存放ALU中操作数。
- ALU_EXEC:主要根据输入的inst进行运算,执行乘法、加法,减法,与,或,异或,异或非等运算。
- ALU_FIFO:主要用于缓存ALU_SLAVE输出的指令以及ALU_EXEC输出的运算结果。
- ALU_SLAVE:根据ALU_TOP的输入s_din和s_addr以及一些控制信号s_sel等,产生其它模块的控制信号,以及ALU_TOP层的数据输出和中断。
2. ALU_SLAVE
- s_addr设置不同的值,s_din输入值所代表含义如下。
s_addr | value | s_din |
---|---|---|
[7:0] | 8'h00 | OPERATION_START |
[7:0] | 8'h01 | INTERRUPT |
[7:0] | 8'h02 | INTERRUPT_ENABLE |
[7:0] | 8'h03 | INSTRUCTION |
[7:0] | 8'h04 | ALU_READ |
[7:0] | 8'h05 | ALU_STATUS |
[7:0] | 8'h1x | ALU_RF |
- 根据s_addr和s_sel,s_wr信号进行RF模块的读写、FIFO中reslut的读操作和inst的写操作。
- 根据s_addr和s_in的值产生一些状态控制信号如:op_start,opdone_clear,以用于ALU_EXEC中;还有中断信号s_interrupt。
3. ALU_RF
- 该模块有3个读地址和1个写地址;
- 写
- 写相关信号由ALU_SLAVE提供,主要根据s_addr判断为读写寄存器模式,根据s_wr判断是写/读。对于写,SLAVE提供写地址、写数据以及写使能信号。
- RF内部根据waddr进行译码&写使能信号,选通寄存器,进行写入。
- 读
- 读地址根据来源分为两种,一种为ALU_SLAVE发出的,读出数据直接输出ALU_TOP;另一种为ALU_EXEC发出的,读出数据作为ALU运算的操作数。
- 前者,根据SLAVE的读写使能信号we以及raddr进行读取数据。至于为什么要设计直接读出寄存器值的功能,我认为是为了在系统工作异常时可以检测RF模块是否出错。
- 后者,根据EXEC模块给的rd_en和rADDR1、rADDR2来取数据。
4. ALU_EXEC
- 首先介绍3中的rd_en信号和rADDR1、rADDR2。
- rd_en是由EXEC中next_state判断的,当下一状态是MUL/EXEC时,需要准备好运算的操作数,所以将RF的读使能信号拉高。
- 两个操作数的地址是根据inst进行译码得到的,不仅可以得到操作数地址,还可以得到位移量shamt,以及执行那个运算的操作码opcode。主要映射关系如下代码所示:
assign opcode = inst[13:10]; assign opAaddr = inst[9:6]; assign opBaddr = inst[5:2]; assign shamt = inst[1:0];
- ALU_EXEC中主要进行读取指令,运算,输出结果等操作,通过状态机来实现。
- 首先判断FIFO的rd_err_result、wr_err_inst信号是否拉高,两者分别为SLAVE读FIFO的result和向FIFO中写inst信号,如果SLAVE与FIFO的交互发生问题,那么需要将状态拉到FAULT。
- IDLE:起始状态,什么都不做;从该状态跳出进入INST_POP1需要使用ALU_SLAVE提供的op_start信号。
- INST_POP1:该状态主要做的任务是处理从FIFO中读出指令。
- (1)首先如果FIFO反馈的rd_err_inst信号拉高,即读指令发生问题,那么状态会跳到FAULT中,不继续操作。
- (2)如果FIFO反馈的rd_ack_inst信号拉高,即从FIFO中读出新的指令,那么根据opcode分为三种情况:NOP:没有操作、MUL:乘法操作、EXEC:基础运算操作。
- (3)如果rd_ack_inst信号如果为0,说明没有读到,便仍在INST_POP1阶段。
- NOP: 没有操作停留一周期,直接跳到INST_POP2状态,再次读取指令。
- EXEC:在此前一阶段,已经拿到了操作数A,B;在这阶段花一个周期计算,并跳转到RESULT_PUSH1状态。
- MUL:直到mul_done信号被拉高,表示乘法运算结束后,跳到RESULT_PUSH1状态。否则没计算完成,保持MUL状态。
- RESULT_PUSH1:判断向FIFO中写result是否出错,如果wr_err_result拉高,状态跳到FAULT。如果没有出错,则会跳到RESULT_PUSH2阶段。
- RESULT_PUSH2:下一状态为INST_POP2.需要连续两个RESULT_PUSH阶段,我认为是因为EXEC和MUL阶段输出结果为64bit,需要分两个周期(每周期能传输32bit数据,所以划分了两个连续状态)传输一个完整的数据。
- INST_POP2:
- 这一步再次判断rd_err_inst是否被拉高,如果被拉高,说明FIFO中所有指令都被读取完毕,那么执行结束,进入EXEC_DONE状态。区分于INST_POP1中的(1),那次的rd_err_inst判断是最开始读指令时,防止FIFO设计错误,或ALU_SLAVE写入inst错误,导致FIFO中为空;若最开始检测没有问题,那么rd_err_inst的再次拉高就证明时FIFO中的指令都被读完,而不是设计错误.
- 如果rd_err_inst未被拉高,则再次进入INST_POP1的步骤(2)(3)。
- EXEC_DONE:如果opdone_clear信号被拉高,则状态机回到最开始的状态IDLE。如果未被拉高,则一直保持执行完毕的状态。
- FAULT:错误阶段,停留在该阶段,还没有写跳出FAULT的处理,会导致死循环。
- ALU_calculation
- 首先需要对输入的两个操作数opA和opB做符号扩展,得到opA_s和opB_s,由32bits扩展为64bits。之后用opA_s和opB_s做EXEC中的操作。并根据op_code将计算后的值赋值给64bits的temp变量。
- 对result的赋值,首先在RESULT_PUSH1阶段将temp的高32bit符号位给出,这个阶段会首先判断FIFO是否已经满了,如果满了还要往里面写,就会跳转到FAIL状态。如果FIFO没满,还可以继续写.高32bit和低32bit分两个周期给出。
- ALU_multiplier
- 具体的运算实现,后续文章介绍。
5. ALU_FIFO
- 模块包括指令INST_FIFO和运算结果RESULT_FIFO。
- INST_FIFO
- 对于inst的读取,需要拿到ALU_EXEC的读使能信号rd_en_inst.
- 在ALU_EXEC模块中,只有state为INST_POP1或INST_POP2时,rd_en_inst = 1.
- 对于inst的写入,需要拿到ALU_SLAVE输出的写使能信号wr_en_inst.主要由s_addr决定是否为指令写模式。
- 对于inst的读取,需要拿到ALU_EXEC的读使能信号rd_en_inst.
- RESULT_FIFO
- 输入来自于ALU_EXEC的计算结果result输出;FIFO输出作为ALU_SLAVE的输入,内部mux选通输出到s_dout.
- 对于FIFO中result的读取,需要拿到ALU_SLAVE的读使能信号rd_en_result.在s_addr[7:0]= 4时,拉高读使能信号。
- 对于FIFO中result的写入,需要拿到ALU_EXEC的写使能信号wr_en_result,在state为RESULT_PUSH1和RESULT_PUSH2时,拉高写使能信号。
- FIFO具体设计以及对满,空的处理后续文章介绍。