终于来到了终篇,可能会有些许绕,但大家稳住!篇幅可能会略有些长。
作者: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 Verilog源码
FPGA-自己动手写CPU-CPU Verilog源码-电路城论坛 - 电子工程师学习交流园地
(出处: 电路城论坛首页 - 电子工程师学习交流园地)
最近实在太忙了,所以拖更了很久,手头事情稍微少了一些。希望大家多多支持
推荐阅读
关注此系列,请关注专栏FPGA的逻辑