FPGA初始——自制CPU(下)(文末含源码)

终于来到了终篇,可能会有些许绕,但大家稳住!篇幅可能会略有些长。
作者:Trustintruth
来源:   https://zhuanlan.zhihu.com/p/98545078

废话不多说,干货开始:

总线

本设计使用的总线为使用时钟信号同步数据传输的同步总线。主控为4通道,总线从属为8通道。总线读数据时序图如下

在I阶段为请求使用总线的请求信号(req\_)来获取总线使用权。

在II阶段,总线仲裁器对总线发来的总线使用权请求进行调停,并发出总线使用许可信号(grnt\_),对于总线来说,一旦接收到总线使用许可信号后,即可开始总线访问。

在III阶段,总线访问开始。总线主控输出地址(addr)信号,并输出地址选通(as\_)信号。片选信号(CS\_)由地址解码器根据地址信号生成。,读取访问(rw)输出为读,rw和as\_保持1个时钟周期,地址位保持到总线访问结束。

在IV阶段,总线从属输出就绪(rdy\_)信号与读取的数据(rd\_data)

在V阶段,总线使用完毕,释放地址信号和使用权信号。

写的操作同理,这里就不再细细赘述了,就放个时序图大家自己体会吧

总线的实现:总线是由总线仲裁器,主线主控多路复用器,地址解码器以及总线从属多路复用器组成的,模块设计图如下

其中总线仲裁器对总线使用全进行调停。总线仲裁器接收总线主控发来的总线使用请求,并将使用权赋给合适的总线主控。我们制作的总线仲裁器发来的,他的四个状态分别是“0”“1”“2”“3”号总线主控持有总线使用权。针对总线使用权请求的调停,使用轮询(round robin)机制。设置优先级顺序“0号总线主控”>“1号总线主控”>“2号总线主控”>“3号主控”,优先级由0到3递减,如下图所示:

流水线设计

流水线设计是一种提高CPU的处理性能的技术,所谓流水线处理,是将处理操作分为多个阶段,然后像流水线作业一样执行。

CPU中的各种硬件资源,只在处理的相应时间段使用,其他大多数时间处于空闲状态。为了高校使用这些资源,在流水线处理的情况下,读取某指令之后,在该指令解码的同时读取下一条指令。通过各个阶段的动作重叠,可以使硬件资源更有效的利用。次这几采用最经典的5级流水线结构。如图所示:

IF阶段:将PC的值发送至内存,读取指令。

ID阶段:将读取的指令解码并决定将要进行的操作,从寄存器堆读取数据

EX阶段:使用运算器执行阶段,可以执行算术运算和逻辑运算。

MEM阶段:进行内存访问

WB阶段:将结果写回寄存器

他的操作重叠使用如下图:

但是在流水处理中,由于各个阶段的依赖关系、硬件资源的竞争等原因。会出现无法执行的情况。造成流水线故障称之为冒险。冒险分为三类:由于硬件资源的竞争操作无法执行的叫做结构冒险;由于数据还没准备好所引起的冒险叫做数据冒险;因无法确定下一条指令而引发的冒险叫做控制冒险。

中断是指让CPU暂停正在执行的操作,执行其他操作的功能。中断经常用在通知来自I/O的事件、处理程序执行中的异步事件等情况。

异常是指CPU的执行产生了预期之外的结果。例如,遇到无法解码的指令、运算结果溢出以及操作违反权限等情况。中断和异常最大的区别在于发生的原因。异常发生时的流水线动作,流水线化的CPU在异常发生时的处理稍微有些复杂。异常发生后,导致异常发生的指令以及其后的指令暂停执行,,并跳转到异常处理程序。根据不同的异常种类,发生异常的流水线级也不同。

CPU顶层设计

CPU有以下的部分组成:IF阶段、ID阶段、EX阶段、MEM阶段、CPU中的存储器通用寄存器、控制CPU的CPU控制单元,以及CPU可以直接访问的专用存储器SPM。WB阶段的写回的通用寄存器或CPU控制单元中实现,不需要单独的模块。设计的顶层框图如下:

流水线的细节如图:

根据我们(上)篇讲的指令架构,CPU这样处理。首先总线接口用来对总线进行控制。由于我们增设了SPM,所以总线接口要根据访问的地址选择总线和SPM的访问。一位CPU与SPM直接相连,所以CPU对SPM进行读写只需要一个周期。访问总线时需要遵循总线协议进行访问控制。状态迁移就像前面说的,这里补充一个图帮助理解。

接下来设计IF阶段

IF阶段的操作有取指令,并确定下一条指令的位置(下一条PC寄存器的内容),这一阶段主要是与存储接触,所以IF阶段主要有流水线寄存器与总线接口组成。PC和指令寄存器的时序如下:

由于SPM也按照时钟上升沿同步读取动作,所以从SPM读取指令时还要延时一个周期。这样指令与PC寄存器的对应内容会错开两个时钟周期。

为减少空闲时间,我们可以采用两项时钟,CPU只有在SPM读取时使用180度相位的时钟,而在180度相位的上升沿读取数据,在相位为0度时钟上升沿锁存,实际上就是要求SPM数据读取速度为之前的二倍。

IF阶段的流水线寄存器(if\_reg)核心代码为:

always @(posedge clk or `RESET_EDGE reset) begin
        if (reset == `RESET_ENABLE) begin 
            if_pc    <= #1 `RESET_VECTOR;
            if_insn <= #1 `ISA_NOP;
            if_en    <= #1 `DISABLE;
        end else begin
            if (stall == `DISABLE) begin 
                if (flush == `ENABLE) begin    
                            if_pc    <= #1 new_pc;
                    if_insn <= #1 `ISA_NOP;
                    if_en    <= #1 `DISABLE;
                end else if (br_taken == `ENABLE) begin 
                                    if_pc    <= #1 br_addr;
                    if_insn <= #1 insn;
                    if_en    <= #1 `ENABLE;
                end else begin                            // ���̃A�h���X
                    if_pc    <= #1 if_pc + 1'd1;
                    if_insn <= #1 insn;
                    if_en    <= #1 `ENABLE;
                end
            end
        end
    end

endmodule

首先异步复位,设置PC(if\_pc)为复位向量,指令寄存器(if\_insn)设置为NOP,流水线有效标志位(if\_en)设置为无效。接下来,流水线寄存器在延时信号(stall)无效时才可以更新,首先对流水线寄存器进行刷新,刷新信号(flush)有效时,PC(if\_pc)设置为新的地址。接下来如是分支(br\_taken)信号有效,PC被设置为分支目的地址(br\_addr),指令寄存器设置为读取的指令(insn)、流水线数据有效位设置为有效。最后为PC的步进,即为更新PC为下一条地址,指令寄存器设置为读取的指令(insn)、流水线数据有效位设置为有效。

IF顶层设计时,只进行指令的读取,总线的读写设置为读,写入数据设置为0,持续将地址有效信号(as\_)设置为有效(ENABLE\_\_)

ID阶段对指令进行解码并生成必要的信号。数据的直通。Load冒险的检测、分支的判定都在这一阶段进行。ID阶段由指令解码器和流水线寄存器构成。

指令解码器从输入的指令码中分解出各个指令字段、生成地址、数据和控制等信号。数据直通与load冒险检测、分支判定也在指令寄存器中进行。核心代码如下:

wire [`IsaOpBus]    op        = if_insn[`IsaOpLoc];    
    wire [`RegAddrBus]    ra_addr = if_insn[`IsaRaAddrLoc]; 
    wire [`RegAddrBus]    rb_addr = if_insn[`IsaRbAddrLoc]; 
    wire [`RegAddrBus]    rc_addr = if_insn[`IsaRcAddrLoc]; 
    wire [`IsaImmBus]    imm        = if_insn[`IsaImmLoc];    
    /********** ���l **********/
    // �����g��
    wire [`WordDataBus] imm_s = {{`ISA_EXT_W{imm[`ISA_IMM_MSB]}}, imm};
    // �[���g��
    wire [`WordDataBus] imm_u = {{`ISA_EXT_W{1'b0}}, imm};
    /********** ���W�X�^�̓ǂݏo���A�h���X **********/
    assign gpr_rd_addr_0 = ra_addr; // �ėp���W�X�^�ǂݏo���A�h���X 0
    assign gpr_rd_addr_1 = rb_addr; // �ėp���W�X�^�ǂݏo���A�h���X 1
    assign creg_rd_addr     = ra_addr; // ���䃌�W�X�^�ǂݏo���A�h���X
    /********** �ėp���W�X�^�̓ǂݏo���f�[�^ **********/
    reg            [`WordDataBus]    ra_data;                          // �����Ȃ�Ra
    wire signed [`WordDataBus]    s_ra_data = $signed(ra_data);      // �����t��Ra
    reg            [`WordDataBus]    rb_data;                          // �����Ȃ�Rb
    wire signed [`WordDataBus]    s_rb_data = $signed(rb_data);      // �����t��Rb
    assign mem_wr_data = rb_data; // �������������݃f�[�^
    /********** �A�h���X **********/
    wire [`WordAddrBus] ret_addr  = if_pc + 1'b1;                     // �߂��Ԓn
    wire [`WordAddrBus] br_target = if_pc + imm_s[`WORD_ADDR_MSB:0]; // ������
    wire [`WordAddrBus] jr_target = ra_data[`WordAddrLoc];           // �W�����v��

    /********** �t�H���[�f�B���O **********/
    always @(*) begin
        /* Ra���W�X�^ */
        if ((id_en == `ENABLE) && (id_gpr_we_ == `ENABLE_) && 
            (id_dst_addr == ra_addr)) begin
            ra_data = ex_fwd_data;     // EX�X�e�[�W�����̃t�H���[�f�B���O
        end else if ((ex_en == `ENABLE) && (ex_gpr_we_ == `ENABLE_) && 
                     (ex_dst_addr == ra_addr)) begin
            ra_data = mem_fwd_data;     // MEM�X�e�[�W�����̃t�H���[�f�B���O
        end else begin
            ra_data = gpr_rd_data_0; // ���W�X�^�t�@�C�������̓ǂݏo��
        end
        /* Rb���W�X�^ */
        if ((id_en == `ENABLE) && (id_gpr_we_ == `ENABLE_) && 
            (id_dst_addr == rb_addr)) begin
            rb_data = ex_fwd_data;     // EX�X�e�[�W�����̃t�H���[�f�B���O
        end else if ((ex_en == `ENABLE) && (ex_gpr_we_ == `ENABLE_) && 
                     (ex_dst_addr == rb_addr)) begin
            rb_data = mem_fwd_data;     // MEM�X�e�[�W�����̃t�H���[�f�B���O
        end else begin
            rb_data = gpr_rd_data_1; // ���W�X�^�t�@�C�������̓ǂݏo��
        end
    end

    /********** ���[�h�n�U�[�h�̌��o **********/
    always @(*) begin
        if ((id_en == `ENABLE) && (id_mem_op == `MEM_OP_LDW) &&
            ((id_dst_addr == ra_addr) || (id_dst_addr == rb_addr))) begin
            ld_hazard = `ENABLE;  // ���[�h�n�U�[�h
        end else begin
            ld_hazard = `DISABLE; // �n�U�[�h�Ȃ�
        end
    end

    /********** ���߂̃f�R�[�h **********/
    always @(*) begin
        /* �f�t�H���g�l */
        alu_op     = `ALU_OP_NOP;
        alu_in_0 = ra_data;
        alu_in_1 = rb_data;
        br_taken = `DISABLE;
        br_flag     = `DISABLE;
        br_addr     = {`WORD_ADDR_W{1'b0}};
        mem_op     = `MEM_OP_NOP;
        ctrl_op     = `CTRL_OP_NOP;
        dst_addr = rb_addr;
        gpr_we_     = `DISABLE_;
        exp_code = `ISA_EXP_NO_EXP;
        /* �I�y�R�[�h�̔��� */
        if (if_en == `ENABLE) begin
            case (op)
                /* �_�����Z���� */
                `ISA_OP_ANDR  : begin // ���W�X�^���m�̘_����
                    alu_op     = `ALU_OP_AND;
                    dst_addr = rc_addr;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_ANDI  : begin // ���W�X�^�Ƒ��l�̘_����
                    alu_op     = `ALU_OP_AND;
                    alu_in_1 = imm_u;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_ORR      : begin // ���W�X�^���m�̘_���a
                    alu_op     = `ALU_OP_OR;
                    dst_addr = rc_addr;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_ORI      : begin // ���W�X�^�Ƒ��l�̘_���a
                    alu_op     = `ALU_OP_OR;
                    alu_in_1 = imm_u;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_XORR  : begin // ���W�X�^���m�̔r���I�_���a
                    alu_op     = `ALU_OP_XOR;
                    dst_addr = rc_addr;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_XORI  : begin // ���W�X�^�Ƒ��l�̔r���I�_���a
                    alu_op     = `ALU_OP_XOR;
                    alu_in_1 = imm_u;
                    gpr_we_     = `ENABLE_;
                end
                /* �Z�p���Z���� */
                `ISA_OP_ADDSR : begin // ���W�X�^���m�̕����t����Z
                    alu_op     = `ALU_OP_ADDS;
                    dst_addr = rc_addr;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_ADDSI : begin // ���W�X�^�Ƒ��l�̕����t����Z
                    alu_op     = `ALU_OP_ADDS;
                    alu_in_1 = imm_s;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_ADDUR : begin // ���W�X�^���m�̕����Ȃ���Z
                    alu_op     = `ALU_OP_ADDU;
                    dst_addr = rc_addr;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_ADDUI : begin // ���W�X�^�Ƒ��l�̕����Ȃ���Z
                    alu_op     = `ALU_OP_ADDU;
                    alu_in_1 = imm_s;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_SUBSR : begin // ���W�X�^���m�̕����t�����Z
                    alu_op     = `ALU_OP_SUBS;
                    dst_addr = rc_addr;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_SUBUR : begin // ���W�X�^���m�̕����Ȃ����Z
                    alu_op     = `ALU_OP_SUBU;
                    dst_addr = rc_addr;
                    gpr_we_     = `ENABLE_;
                end
                /* �V�t�g���� */
                `ISA_OP_SHRLR : begin // ���W�X�^���m�̘_���E�V�t�g
                    alu_op     = `ALU_OP_SHRL;
                    dst_addr = rc_addr;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_SHRLI : begin // ���W�X�^�Ƒ��l�̘_���E�V�t�g
                    alu_op     = `ALU_OP_SHRL;
                    alu_in_1 = imm_u;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_SHLLR : begin // ���W�X�^���m�̘_�����V�t�g
                    alu_op     = `ALU_OP_SHLL;
                    dst_addr = rc_addr;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_SHLLI : begin // ���W�X�^�Ƒ��l�̘_�����V�t�g
                    alu_op     = `ALU_OP_SHLL;
                    alu_in_1 = imm_u;
                    gpr_we_     = `ENABLE_;
                end
                /* ��� � */
                `ISA_OP_BE      : begin // ���W�X�^���m�̕����t�����r�iRa == Rb�j
                    br_addr     = br_target;
                    br_taken = (ra_data == rb_data) ? `ENABLE : `DISABLE;
                    br_flag     = `ENABLE;
                end
                `ISA_OP_BNE      : begin // ���W�X�^���m�̕����t�����r�iRa != Rb�j
                    br_addr     = br_target;
                    br_taken = (ra_data != rb_data) ? `ENABLE : `DISABLE;
                    br_flag     = `ENABLE;
                end
                `ISA_OP_BSGT  : begin // ���W�X�^���m�̕����t�����r�iRa < Rb�j
                    br_addr     = br_target;
                    br_taken = (s_ra_data < s_rb_data) ? `ENABLE : `DISABLE;
                    br_flag     = `ENABLE;
                end
                `ISA_OP_BUGT  : begin // ���W�X�^���m�̕����Ȃ����r�iRa < Rb�j
                    br_addr     = br_target;
                    br_taken = (ra_data < rb_data) ? `ENABLE : `DISABLE;
                    br_flag     = `ENABLE;
                end
                `ISA_OP_JMP      : begin // �������
                    br_addr     = jr_target;
                    br_taken = `ENABLE;
                    br_flag     = `ENABLE;
                end
                `ISA_OP_CALL  : begin // �R�[��
                    alu_in_0 = {ret_addr, {`BYTE_OFFSET_W{1'b0}}};
                    br_addr     = jr_target;
                    br_taken = `ENABLE;
                    br_flag     = `ENABLE;
                    dst_addr = `REG_ADDR_W'd31;
                    gpr_we_     = `ENABLE_;
                end
                /* �������A�N�Z�X���� */
                `ISA_OP_LDW      : begin // ���[�h�ǂݏo��
                    alu_op     = `ALU_OP_ADDU;
                    alu_in_1 = imm_s;
                    mem_op     = `MEM_OP_LDW;
                    gpr_we_     = `ENABLE_;
                end
                `ISA_OP_STW      : begin // ���[�h��������
                    alu_op     = `ALU_OP_ADDU;
                    alu_in_1 = imm_s;
                    mem_op     = `MEM_OP_STW;
                end
                /* �V�X�e���R�[������ */
                `ISA_OP_TRAP  : begin // �g���b�v
                    exp_code = `ISA_EXP_TRAP;
                end
                /* ������� */
                `ISA_OP_RDCR  : begin // ���䃌�W�X�^�̓ǂݏo��
                    if (exe_mode == `CPU_KERNEL_MODE) begin
                        alu_in_0 = creg_rd_data;
                        gpr_we_     = `ENABLE_;
                    end else begin
                        exp_code = `ISA_EXP_PRV_VIO;
                    end
                end
                `ISA_OP_WRCR  : begin // ���䃌�W�X�^�ւ̏�������
                    if (exe_mode == `CPU_KERNEL_MODE) begin
                        ctrl_op     = `CTRL_OP_WRCR;
                    end else begin
                        exp_code = `ISA_EXP_PRV_VIO;
                    end
                end
                `ISA_OP_EXRT  : begin // ���O�����̕��A
                    if (exe_mode == `CPU_KERNEL_MODE) begin
                        ctrl_op     = `CTRL_OP_EXRT;
                    end else begin
                        exp_code = `ISA_EXP_PRV_VIO;
                    end
                end
                /* ���̑��̖��� */
                default          : begin // �����`����
                    exp_code = `ISA_EXP_UNDEF_INSN;
                end
            endcase
        end
    end

endmodule

首先分解出各个字段,然后对立即数按前面说过的方法进行扩充,接下来对寄存器读取地址赋值,通过读取指令的Ra和Rb字段控制寄存器读取读取地址。

load指令会发生冒险,其发生的条件为:ID/EX流水线寄存器存放的之前的指令为load指令,通用寄存器的写入地址与当前指令的读取地址相等。ID/EX流水线寄存器有效,内存操作为load指令。篇幅受限只放一张图吧

EX阶段主要进行运算和中断检测。EX阶段由算数逻辑运算单元和流水线寄存器构成。ALU在前面我们已经讲过,他在EX阶段发挥重要作用。其顶层如下图

MEM阶段主要为内存的访问,在执行LDW和STW等指令时,内存访问操作是在MEM阶段进行的,主要为内存访问控制模块,流水线寄存器以及总线接口构成。

最后就是CPU控制模块,进行对保存CPU状态的控制寄存器进行管理,并对流水线进行控制。她共有控制寄存器如下:

其对应代码为:

always @(*) begin
        /* �f�t�H���g�l */
        new_pc = `WORD_ADDR_W'h0;
        flush  = `DISABLE;
        /* �p�C�v���C���t���b�V�� */
        if (mem_en == `ENABLE) begin // �p�C�v���C���̃f�[�^���L��
            if (mem_exp_code != `ISA_EXP_NO_EXP) begin         // ���O����
                new_pc = exp_vector;
                flush  = `ENABLE;
            end else if (mem_ctrl_op == `CTRL_OP_EXRT) begin // EXRT����
                new_pc = epc;
                flush  = `ENABLE;
            end else if (mem_ctrl_op == `CTRL_OP_WRCR) begin // WRCR����
                new_pc = mem_pc;
                flush  = `ENABLE;
            end
        end
    end

    /********** ���荞�݂̌��o **********/
    always @(*) begin
        if ((int_en == `ENABLE) && ((|((~mask) & irq)) == `ENABLE)) begin
            int_detect = `ENABLE;
        end else begin
            int_detect = `DISABLE;
        end
    end

    /********** �ǂݏo���A�N�Z�X **********/
    always @(*) begin
        case (creg_rd_addr)
           `CREG_ADDR_STATUS     : begin // 0��:�X�e�[�^�X
               creg_rd_data = {{`WORD_DATA_W-2{1'b0}}, int_en, exe_mode};
           end
           `CREG_ADDR_PRE_STATUS : begin // 1��:���O�����O�̃X�e�[�^�X
               creg_rd_data = {{`WORD_DATA_W-2{1'b0}}, 
                               pre_int_en, pre_exe_mode};
           end
           `CREG_ADDR_PC         : begin // 2��:�v���O�����J�E���^
               creg_rd_data = {id_pc, `BYTE_OFFSET_W'h0};
           end
           `CREG_ADDR_EPC         : begin // 3��:���O�v���O�����J�E���^
               creg_rd_data = {epc, `BYTE_OFFSET_W'h0};
           end
           `CREG_ADDR_EXP_VECTOR : begin // 4��:���O�x�N�^
               creg_rd_data = {exp_vector, `BYTE_OFFSET_W'h0};
           end
           `CREG_ADDR_CAUSE         : begin // 5��:���O����
               creg_rd_data = {{`WORD_DATA_W-1-`ISA_EXP_W{1'b0}}, 
                               dly_flag, exp_code};
           end
           `CREG_ADDR_INT_MASK     : begin // 6��:���荞�݃}�X�N
               creg_rd_data = {{`WORD_DATA_W-`CPU_IRQ_CH{1'b0}}, mask};
           end
           `CREG_ADDR_IRQ         : begin // 6��:���荞�݌���
               creg_rd_data = {{`WORD_DATA_W-`CPU_IRQ_CH{1'b0}}, irq};
           end
           `CREG_ADDR_ROM_SIZE     : begin // 7��:ROM�̃T�C�Y
               creg_rd_data = $unsigned(`ROM_SIZE);
           end
           `CREG_ADDR_SPM_SIZE     : begin // 8��:SPM�̃T�C�Y
               creg_rd_data = $unsigned(`SPM_SIZE);
           end
           `CREG_ADDR_CPU_INFO     : begin // 9��:CPU�̏���
               creg_rd_data = {`RELEASE_YEAR, `RELEASE_MONTH, 
                               `RELEASE_VERSION, `RELEASE_REVISION};
           end
           default                 : begin // �f�t�H���g�l
               creg_rd_data = `WORD_DATA_W'h0;
           end
        endcase
    end

    /********** CPU�̐��� **********/
    always @(posedge clk or `RESET_EDGE reset) begin
        if (reset == `RESET_ENABLE) begin
            /* � ����Z�b�g */
            exe_mode     <= #1 `CPU_KERNEL_MODE;
            int_en         <= #1 `DISABLE;
            pre_exe_mode <= #1 `CPU_KERNEL_MODE;
            pre_int_en     <= #1 `DISABLE;
            exp_code     <= #1 `ISA_EXP_NO_EXP;
            mask         <= #1 {`CPU_IRQ_CH{`ENABLE}};
            dly_flag     <= #1 `DISABLE;
            epc             <= #1 `WORD_ADDR_W'h0;
            exp_vector     <= #1 `WORD_ADDR_W'h0;
            pre_pc         <= #1 `WORD_ADDR_W'h0;
            br_flag         <= #1 `DISABLE;
        end else begin
            /* CPU�̏��Ԃ��X�V */
            if ((mem_en == `ENABLE) && (stall == `DISABLE)) begin
                /* PC�ƕ����t���O�̕ � */
                pre_pc         <= #1 mem_pc;
                br_flag         <= #1 mem_br_flag;
                /* CPU�̃X�e�[�^�X���� */
                if (mem_exp_code != `ISA_EXP_NO_EXP) begin         // ���O����
                    exe_mode     <= #1 `CPU_KERNEL_MODE;
                    int_en         <= #1 `DISABLE;
                    pre_exe_mode <= #1 exe_mode;
                    pre_int_en     <= #1 int_en;
                    exp_code     <= #1 mem_exp_code;
                    dly_flag     <= #1 br_flag;
                    epc             <= #1 pre_pc;
                end else if (mem_ctrl_op == `CTRL_OP_EXRT) begin // EXRT����
                    exe_mode     <= #1 pre_exe_mode;
                    int_en         <= #1 pre_int_en;
                end else if (mem_ctrl_op == `CTRL_OP_WRCR) begin // WRCR����
                   /* ���䃌�W�X�^�ւ̏������� */
                    case (mem_dst_addr)
                        `CREG_ADDR_STATUS      : begin // �X�e�[�^�X
                            exe_mode     <= #1 mem_out[`CregExeModeLoc];
                            int_en         <= #1 mem_out[`CregIntEnableLoc];
                        end
                        `CREG_ADDR_PRE_STATUS : begin // ���O�����O�̃X�e�[�^�X
                            pre_exe_mode <= #1 mem_out[`CregExeModeLoc];
                            pre_int_en     <= #1 mem_out[`CregIntEnableLoc];
                        end
                        `CREG_ADDR_EPC          : begin // ���O�v���O�����J�E���^
                            epc             <= #1 mem_out[`WordAddrLoc];
                        end
                        `CREG_ADDR_EXP_VECTOR : begin // ���O�x�N�^
                            exp_vector     <= #1 mem_out[`WordAddrLoc];
                        end
                        `CREG_ADDR_CAUSE      : begin // ���O����
                            dly_flag     <= #1 mem_out[`CregDlyFlagLoc];
                            exp_code     <= #1 mem_out[`CregExpCodeLoc];
                        end
                        `CREG_ADDR_INT_MASK      : begin // ���荞�݃}�X�N
                            mask         <= #1 mem_out[`CPU_IRQ_CH-1:0];
                        end
                    endcase
                end
            end
        end
    end

endmodule

摘自《自己动手写CPU》

摘自《自己动手写CPU》

摘自《自己动手写CPU》

CPU至此设计完成了,整体代码我放在了论坛里,需要的可以在下面链接直接下载。
自己动手写CPU -CPU Verilog源码
FPGA-自己动手写CPU-CPU Verilog源码-电路城论坛 – 电子工程师学习交流园地

(出处: 电路城论坛首页 – 电子工程师学习交流园地)

最近实在太忙了,所以拖更了很久,手头事情稍微少了一些。希望大家多多支持

推荐阅读

  • FPGA初始——自制CPU(上)
  • 基于FPGA的并行扰码解扰器设计
  • FPGA 系统中的处理器核们(二):软核,可杀鸡亦可屠龙?

关注此系列,请关注专栏FPGA的逻辑

发表评论

邮箱地址不会被公开。 必填项已用*标注