数字IC设计实战:逐步升级,Tiny—RISC CPU 设计·小型模块设计

本次解析模块内容:        

                div

                ctrl

                rib

                regs(简单,看原作者解释即可)

                csr_regs(和regs长得同父同母)

参考文献:从零开始写RISC-V处理器 | liangkangnan的博客 (gitee.io)

参考代码:tinyriscv: 一个从零开始写的极简、非常易懂的RISC-V处理器核。 (gitee.com)

目录

div

ctrl

reg

csr_regs

rib


div

`include "defines.v"

// 除法模块
// 试商法实现32位整数除法
// 每次除法运算至少需要33个时钟周期才能完成

//负数的补码取反+1 得出正数原码
//正数的原码取反+1 得出负数补码
module div(

    input wire clk,
    input wire rst,

    // from ex
    input wire[`RegBus] dividend_i,      // 被除数
    input wire[`RegBus] divisor_i,       // 除数
    input wire start_i,                  // 开始信号,运算期间这个信号需要一直保持有效
    input wire[2:0] op_i,                // 具体是哪一条指令
    input wire[`RegAddrBus] reg_waddr_i, // 运算结束后需要写的寄存器

    // to ex
    output reg[`RegBus] result_o,        // 除法结果,高32位是余数,低32位是商
    output reg ready_o,                  // 运算结束信号
    output reg busy_o,                  // 正在运算信号
    output reg[`RegAddrBus] reg_waddr_o  // 运算结束后需要写的寄存器

    );

    // 状态定义
    localparam STATE_IDLE  = 4'b0001;
    localparam STATE_START = 4'b0010;
    localparam STATE_CALC  = 4'b0100;
    localparam STATE_END   = 4'b1000;

    reg[`RegBus] dividend_r;//buffer
    reg[`RegBus] divisor_r;
    reg[2:0] op_r;
    reg[3:0] state;
    reg[31:0] count;
    reg[`RegBus] div_result;
    reg[`RegBus] div_remain;
    reg[`RegBus] minuend;
    reg invert_result;

    wire op_div = (op_r == `INST_DIV);
    wire op_divu = (op_r == `INST_DIVU);
    wire op_rem = (op_r == `INST_REM);
    wire op_remu = (op_r == `INST_REMU);

    wire[31:0] dividend_invert = (-dividend_r);//被除数的相反数,这里是直接用一个负号把求补码然后取反+1的操作给忽略了,只能说RTL语言现在太简化了
    wire[31:0] divisor_invert = (-divisor_r);//除数的相反数
    wire minuend_ge_divisor = minuend >= divisor_r;//这是一个标志信号,表示验证的被除部分大于除数,这样就可以在当前除数位赋值为1
    wire[31:0] minuend_sub_res = minuend - divisor_r;//这是判断完被除部分大于除数之后,留下的余数,这时候需要继续向下1bit 1bit读取
    wire[31:0] div_result_tmp = minuend_ge_divisor? ({div_result[30:0], 1'b1}): ({div_result[30:0], 1'b0});//这里其实是一个左移位寄存器的机制,通过不断左移,慢慢拼接出完整的结果
    wire[31:0] minuend_tmp = minuend_ge_divisor? minuend_sub_res[30:0]: minuend[30:0];//这里是对操作的被除部分进行进位以及保留

    // 状态机实现
    always @ (posedge clk) begin
        if (rst == `RstEnable) begin//初始化
            state <= STATE_IDLE;
            ready_o <= `DivResultNotReady;
            result_o <= `ZeroWord;
            div_result <= `ZeroWord;
            div_remain <= `ZeroWord;
            op_r <= 3'h0;
            reg_waddr_o <= `ZeroWord;
            dividend_r <= `ZeroWord;
            divisor_r <= `ZeroWord;
            minuend <= `ZeroWord;
            invert_result <= 1'b0;
            busy_o <= `False;
            count <= `ZeroWord;
        end else begin
            case (state)
                STATE_IDLE: begin
                    if (start_i == `DivStart) begin
                        op_r <= op_i;//引入opcode
                        dividend_r <= dividend_i;//引入被除数
                        divisor_r <= divisor_i;//引入除数
                        reg_waddr_o <= reg_waddr_i;//引入要把结果写入的寄存器地址
                        state <= STATE_START;
                        busy_o <= `True;//开启忙状态
                    end else begin
                        op_r <= 3'h0;
                        reg_waddr_o <= `ZeroWord;
                        dividend_r <= `ZeroWord;
                        divisor_r <= `ZeroWord;
                        ready_o <= `DivResultNotReady;
                        result_o <= `ZeroWord;
                        busy_o <= `False;
                    end
                end

                STATE_START: begin
                    if (start_i == `DivStart) begin
                        // 除数为0
                        if (divisor_r == `ZeroWord) begin
                            if (op_div | op_divu) begin
                                result_o <= 32'hffffffff;//这里表示运算不合法
                            end else begin
                                result_o <= dividend_r;//其余情况应该是与求余数,因为运算不合法,所以没有触发除法操作,余数就是被除数本身
                            end
                            ready_o <= `DivResultReady;
                            state <= STATE_IDLE;
                            busy_o <= `False;
                        // 除数不为0
                        end else begin
                            busy_o <= `True;//开始运算,继续忙
                            count <= 32'h40000000;//这个暂且不清楚⭐
                            state <= STATE_CALC;
                            div_result <= `ZeroWord;
                            div_remain <= `ZeroWord;

                            // DIV和REM这两条指令是有符号数运算指令
                            if (op_div | op_rem) begin
                                // 被除数求相反数
                                if (dividend_r[31] == 1'b1) begin
                                    dividend_r <= dividend_invert;//将被除数正数化
                                    minuend <= dividend_invert[31];//这个表示将被除数的头一位放入 被除部分 这里其实就是进行循环读取的时候了,一个一个读入,并不断判断是否大于除数,一旦出现大于的情况,马上在原地留个1,算出相减结果并替代,然后继续读
                                end else begin
                                    minuend <= dividend_r[31];//这里同样是将除数正数化,因为负数很难拿来计算,算法和正数不一致,一般都是先全部化为正数
                                end
                                // 除数求相反数
                                if (divisor_r[31] == 1'b1) begin
                                    divisor_r <= divisor_invert;
                                end
                            end else begin
                                minuend <= dividend_r[31];
                            end

                            // 运算结束后是否要对结果取补码
                            if ((op_div &&
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值