P7-design-document

P7-Design-Document 0.顶层设计概述 ​ P7要求为实现MIPS微系统,需要为P6实现的流水线CPU添加异常中断功能,并封装为CPU模块、实现系统桥Bridge、计时器Timer1,Timer2等模块,最终形成MIPS微系统,如下图所示。 绿色虚线表示已经实现 紫色虚线表示新增部分 红色虚线为改变后的DM接口 P7需要实现的任务如下列表 任务 解释 计时器 课程组提供代码 系统桥 为CPU提供统一的访问外设的接口,自行实现 协处理器CP0 设置CPU的异常处理功能,反馈CPU的异常信息,自行实现 内部检测异常与流水 CPU检测内部指令执行错误 外部中断响应 CPU需要具有响应外部中断信号的能力 异常处理指令 异常处理程序中,会有一些特殊的指令需要实现 单周期CPU的封装 让CPU从外部看上去是单周期CPU 异常处理程序 利用MARS编写简单的异常处理程序进行测试 施工步骤: 更改流水线各级使之可以产生异常 添加CP0处理异常 添加Bridge与两个外设 异常与中断 异常:内部异常 如F级取指异常、D级计算溢出等 中断:来自外部设备,Timer0,Timer1,Interrupt 来自外部设备的中断比内部异常优先级更高 一.功能部件设计 0.新增指令的实现思路 P7中增加四条指令 mtc0 mfc0 eret syscall mtc0 :写入CP0中寄存器(12/14) 对于mtc0和mfc0指令 : 读取的CP0寄存器地址均为rd域,由于本实现中采用了集中式译码,故增加数据通路,将原指令的rd域流水下去,作为CP0寄存器地址, CP0_addr 指令格式 : 010000 || 00100 || rt || rd || 00000000000 mtc0 rt,rd MCU : CP0_WE_D T_rs_use = T_rt_use = 3(这里rt的真实使用时间是3,但是并不会对暂停/转发造成影响,Tuse >= Tnew成立,可以通过转发解决) T_new = 0 写入时应当注意 :Cause寄存器(13)不允许写入,EPC允许写入,SR寄存器部分字段允许写入,其他不允许写入的字段要保持为0 ...

December 3, 2023 · 11 min · sudo

P6课上测试

P6课上测试 ​ P6课上测试内容与P5课上测试内容大概只有第一题计算类型上的区别,变为考察乘除槽的指令 一.增添指令一般步骤 读懂RTL语言,注意opcode与funct位置,是什么类型的指令,会不会与已经添加过的指令发生冲突(例如P5上机第一题中计算指令与slt指令opcode重复QAQ) 使用课上提供的.class文件在MARS中进行模拟 计算指令是否可以进行归类,cal_R,cal_I;条件跳转指令和条件存储指令一般直接使用check信号对新信号进行特判。 在MCU中从上到下观察是否将新指令加入有效列表 明确新指令的T_use,T_new 二.题型分析 1.涉及到乘除槽的计算类指令 ​ P6的计算指令会涉及到乘除模块,以madd,maddu,msub,msubu等指令为例。 madd : 将两个有符号数相乘,计算结果与之前的HI,LO寄存器中的值相加,而不是覆盖 mddu:将两个无符号数相乘,计算结果与之前的HI,LO寄存器中的值相加,而不是覆盖 msub:将两个有符号数相乘,之前HI,LO中的值减去当前结果 msubu:将两个无符号数相乘,之前HI,LO中的值减去当前结果 1.verilog中的符号性问题 ​ $signed()关键字的功能为数据如何进行补位,根据递归确定外层+向内传播的表达式符号确定规则,例如在P1中遇到的三目表达式中有无符号数导致整个表达式变为无符号的情况。 算数(符号)右移表达式中移位立即数不必要声明为有符号 :$signed(A) »> B 为避免$singed()出现的问题,可以使用位扩展进行代替 1 {{16{imm[15]}},imm}//16位imm符号扩展为32位 2.计算指令中的符号乘除 mult 使用位拼接运算 1 {tmpHI,tmpLO} <= $signed(A) * $signed(B) madd 错误写法1 1 {tmpHI,tmpLO} <= {tmpHI,tmpLO} + $signed(A)*$signed(B) 位拼接运算{tmpHI,tmpLO}默认被当作无符号数,向内传播导致$signed(A)*$signed(B)变为无符号的 修改为: 1 {tmpHI,tmpLO} <= $signed({tmpHI,tmpLO}) + $signed(A)*$signed(B) 错误写法2 1 {tmpHI,tmpLO} <= {tmpHI.tmpLO} + $signed($signed(A)*$signed(B)) 使用$signed()屏蔽了外界符号性的传入,同时屏蔽了位宽信息的传入,由于$signed(A),$signed(B)为32位,则$signed($signed(A)*$signed(B))为32位 ...

December 2, 2023 · 4 min · sudo

P6-design-document

P6-Design-Document 一.数据通路图示 ​ P6在P5的基础上新增乘除槽单元、字节使能单元、数据扩展单元,在顶层模块中增加与testbench的接口,将IM与DM外移到testbench中。 二.功能模块定义 1. MCU 1.端口定义列表 名称 方向 位宽 描述 D_opcode I 6 操作码 D_funct I 6 功能码 SelA3_D O 2 选择写入寄存器地址 RegWrite_D O 1 寄存器堆写入地址 EXTOp_D O 1 立即数扩展信号 SelEMout_D O 1 选择E、M级转发数据 SelWout_D O 2 选择W级转发数据/写入GRF数据 SeLALUB_D O 1 选择ALU_B端口输入数据 SelALUS_D O 1 选择ALU_S端口移位数据 check_D O 1 判定是否为新信号(课上扩展使用) mf_D O 1 mfhi/mflo指令,选择E级输出数据 start_D O 1 乘除类导致延迟的指令信号,只有md类需要 CMPOp_D O 3 B类跳转指令操作码 NPCOp_D O 3 跳转地址选择 ALUOp_D O 4 ALU计算操作 MDUOp_D O 4 乘除类计算操作 DMOp_D O 4 存取指令操作 T_rs_use_D O 2 位于D级用到rs寄存器中值的周期数 T_rt_use_D O 2 位于D级用到rt寄存器中值的周期数 T_new_D O 2 位于D级产生新信号的周期数 注:由于BE模块产生字节使能信号,将原内存写入使能信号MemWrite省去 ...

November 28, 2023 · 11 min · sudo

P5课上测试

P5课上测试 一.增添指令一般步骤 明确指令RTL,可以使用课上提供的.class文件进行模拟 明确非转发数据通路:可以在单周期中思考新指令的行为,构思出数据通路 考虑转发: 考虑GRF的五位写入地址是否正确,一般在第二步已经调整完毕,但是像lwer,lhso等条件存储类指令只有在M级中从DM中取出数据后才能明确写入地址,需要在M级将写入地址再次修改 明确新指令的T_rs_use,T_rt_use,T_new,用不到的寄存器设置为T_rs/rt_use = 3 二.课上测试题型分析 1.计算指令——在E级就可以完成所有操作 ​ 新增的计算指令一般只需要增加ALU的功能,但是计算行为会稍稍复杂一些,Tnew和Tuse与calc_R或calc_I型保持一致即可 例如 循环左移 1 2 if(B[4:0] == 5'b0) out = A; else out = A << B[4:0] | A >> (6'd32 - B[4:0]) 2.条件跳转类——在D级就可以完成所有操作 条件跳转 + 无条件链接 条件跳转 + 条件链接 条件跳转 + 条件(无条件)链接 + 不跳转时清空延迟槽 1.条件跳转 增加CMP中的判断功能 2.无条件链接 D级将RegWrite置为1,跟随流水并更改A3(31),最后在W级选择PC8作为写入数据 3.条件链接 在D级根据CMP模块的输出结果判断RegWrite是否有效 设置一个check信号用以确定当前指令是否为新指令,进行流水 1 2 3 4 5 6 7 wire D_RegWrite_new = check_D ? (D_CMP_out ? 1'b1 : 1'b0) : RegWrite_D; //判断是否为新信号 如果是就已经隐含着regwrite=1,只需对CMP结果进行判断 //如果不是就保留原信号 //uut E_reg top_e_reg( .RegWrite_D(D_RegWrite_new), ); 4.不跳转时清空延迟槽 根据CMP输出结果判断是否要清空D级流水寄存器(这就是hyggge学长P5教程中D级clr信号接口的原因QAQ) 注意:当前为stall状态则不能清空延迟槽,此时传入CMP模块的不是新值,判断无效 1 2 3 4 5 wire D_reg_clr = check_D & ~D_CMP_out & ~stall //新信号 & 不跳转 & 不暂停 D_reg top_d_reg( .D_clr(D_reg_clr) //加入到同步复位列表中 ); 3.条件存储类——在M级就可以完成所有操作 在原来基础上修改stall控制信号,如果D级的指令要读寄存器,而且后面的新指令可能要写这个寄存器,那么就stall 修改写入A3地址并传入W_reg和HCU 1. condition1 condition成立 :将DM中值写入A condition不成立 :将DM中值写入B 1 2 3 4 5 6 7 8 9 10 11 12 13 14 //第一种题型(eg:condition满足向rt号写,否则写31号) assign stall_rs_E = (D_A1 != 5'd0) & (check_E ? (D_A1 == E_A3 | D_A1 == 5'd31) : D_A1 == E_A3) & (RFWrite_E) & (Tuse_rs < Tnew_E); assign stall_rs_M = (D_A1 != 5'd0) & (check_M ? (D_A1 == M_A3 | D_A1 == 5'd31) : D_A1 == M_A3) & (RFWrite_M) & (Tuse_rs < Tnew_M); assign stall_rt_E = (D_A2 != 5'd0) & (check_E ? (D_A2 == E_A3 | D_A2 == 5'd31) : D_A2 == E_A3) & (RFWrite_E) & (Tuse_rt < Tnew_E); assign stall_rt_M = (D_A2 != 5'd0) & (check_M ? (D_A2 == M_A3 | D_A2 == 5'd31) : D_A2 == M_A3) & (RFWrite_M) & (Tuse_rt < Tnew_M); //更改写入寄存器地址(eg:condition满足向rt号写,否则写31号) wire M_A3_new = check_M ? (condition ? `rt : 5'd31) : M_A3; //uut W_reg top_w_reg( .M_A3(M_A3_new), ); HCU top_hcu( .M_A3(M_A3_new), ); 2.condition2 conditon成立 :向31号写 condition不成立 :不写(写入0号寄存器) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 //按照第一种题型以写成 (check_M ? (D_A2 == 5'd31 | D_A2 == 5'd0): D_A2 == M_A3),因为前面有条件 D_A2 != 5'd0,所以可以简化 assign stall_rs_E = (D_A1 != 5'd0) & (check_E ? D_A2 == 5'd31 : D_A2 == M_A3) & (RFWrite_E) & (Tuse_rs < Tnew_E); assign stall_rs_M = (D_A1 != 5'd0) & (check_M ? D_A2 == 5'd31 : D_A2 == M_A3) & (RFWrite_M) & (Tuse_rs < Tnew_M); assign stall_rt_E = (D_A2 != 5'd0) & (check_E ? D_A2 == 5'd31 : D_A2 == M_A3) & (RFWrite_E) & (Tuse_rt < Tnew_E); assign stall_rt_M = (D_A2 != 5'd0) & (check_M ? D_A2 == 5'd31 : D_A2 == M_A3) & (RFWrite_M) & (Tuse_rt < Tnew_M); //更改写入寄存器地址(eg:condition满足向31号写,否则不写) wire M_A3_new = check_M ? (condition ? 5'd31 : 5'd0) : M_A3; //uut W_reg top_w_reg( .M_A3(M_A3_new), ); HCU top_hcu( .M_A3(M_A3_new), ); 3.condition3 condition满足时写入DM中值的低五位 这种情况下可能写入所有的寄存器,直接暂停 1 2 3 4 5 6 7 8 9 10 11 12 13 14 //第三种题型 (eg:condition满足时写入位置为DM的读取值的低五位) assign stall_rs_E = (D_A1 != 5'd0) & (check_E ? 1'b1 : D_A2 == M_A3) & (RFWrite_E) & (Tuse_rs < Tnew_E); assign stall_rs_M = (D_A1 != 5'd0) & (check_M ? 1'b1 : D_A2 == M_A3) & (RFWrite_M) & (Tuse_rs < Tnew_M); assign stall_rt_E = (D_A2 != 5'd0) & (check_E ? 1'b1 : D_A2 == M_A3) & (RFWrite_E) & (Tuse_rt < Tnew_E); assign stall_rt_M = (D_A2 != 5'd0) & (check_M ? 1'b1 : D_A2 == M_A3) & (RFWrite_M) & (Tuse_rt < Tnew_M); //更改写入寄存器地址(eg:写入位置为DM的读取值的低五位) wire M_A3_new = check_M ? DM_out[4:0] : M_A3; //uut W_reg top_w_reg( .M_A3(M_A3_new), ); HCU top_hcu( .M_A3(M_A3_new), );

November 27, 2023 · 3 min · sudo

P5-pipeline-CPU-Design-Document

P5-pipeline-CPU-Design-Document 零.数据通路描述 ​ MIPS架构的五级流水线CPU数据通路有以下五个阶段: 取指阶段F(Fetch):从指令存储器中读取指令 译码阶段D(Decode):从寄存器文件中读取源操作数并对指令译码得到控制信号 执行阶段E(Execute):使用ALU执行计算 存储阶段M(Memory):读或写数据存储 写回阶段(Writeback):将结果写回到寄存器文件 ​ 通过在五个阶段之间加入寄存器来保存前面周期产生的数据(最后一个阶段的寄存器即为GRF),形成五级流水线CPU,数据通路如下图表示: 一.数据命名规范(checkstyle) 在控制信号连线名末尾加上“_阶段字母”,如NPCOp_D,用于区分所属的阶段 在流水线寄存器输出信号前加上“阶段字母_",如:D_A1,用以区分所属阶段 功能部件输出信号前加上功能部件名称 常规MUX命名采用"MUX-部件名-端口名”的命名方式,选择信号命名为"Sel+部件名+端口名" 转发MUX命名采用"HMUX-部件名-端口名"的命名方式,选择信号命名为"Fwd+部件名+端口名" 二.部件设计 1.MCU(main control unit) 1.端口定义列表 ​ 主控制单元模块负责对指令进行译码以及产生控制信号,本CPU的设计采用集中式译码,在P5中需要完成的指令及其需要的控制信号列表如下,其中有些控制信号对应多路选择器的选择。 名称 方向 位宽 描述 D_opcode I 6 D级输出opcode D_funct I 6 D级输出funct SelA3_D I 2 选择当前指令要写入的寄存器,写入E级流水线寄存器 RegWrite_D O 1 GRF写入使能 MemWrite_D O 1 DM写入使能 EXTOp_D O 1 对立即数进行符号扩展还是0扩展 SelEMout_D O 1 选择E,M级转发数据信号 SelWout_D O 2 选择W级转发数据信号 SelALUB_D O 1 ALU的B端口rt数据与32位立即数的选择 CMPOp_D O 3 D级B类指令比较选择 NPCOp_D O 3 写入NPC选择 ALUOp_D O 3 ALU操作信号 DMOp_D O 3 DM操作信号 T_rs_use_D O 2 指令处在D级时还有多少个周期需要使用rs寄存器的值 T_rt_use_D O 2 指令处在D级时还有多少个周期需要使用rt寄存器的值 T_new_D O 2 指令处在D级时还有多少个周期可以产生写入寄存器的新值 2.分指令梳理控制信号 指令 取高电平的控制信号和ALUOp add SelA3_D = 2’b01, RegWrite, ALUOp = 3’b000, NPCOp = 3’b000; sub SelA3_D = 2’b01, RegWrite, ALUOp = 3’b001, NPCOp = 3’b000; ori RegWrite,ALUSrc, SelALUB, ALUOp=3’b010, NPCOp = 3’b000; lui RegWrite,ALUSrc, SelALUB, ALUOp=3’b011, NPCOp = 3’b000; lw RegWrite, SelWout = 2’b01, SelALUB, EXTOp, ALUOp=3’b000, NPCOp = 3’b000; sw MemWrite, SelALUB, EXTOp, ALUOp=3’b000, NPCOp = 3’b000; beq CMPOp = 3’b000,EXTOp, NPCOp = 3’b001, CMPOp = 3’b000; jal RegWrite, SelA3_D = 2’b10, NPCOp = 3’b010; jr NPCOp = 3’b11; nop 相当于sll $0,$0,0,不需要进行特殊处理 3.译码方式的改动 ​ 在P4中,我才用使用reg类型变量记录指令识别的形式,在always块中每次都为代表所有信号的寄存器赋初值0,这样会导致pc一直自增下去,程序不断向后跑(虽然在上机时无影响),在蒋老师的测评机上会出现多输出的情况。在P5中,使用wire类型进行指令的识别,当识别的指令为x时,所有指令对应的wire都是不定值x,导致MCU中输出的控制信号为x,使得pc停下来,可以正常在蒋老师的测评机上跑结果。 ...

November 12, 2023 · 24 min · sudo

支持40条指令P4_Verilog_CPU

P4-Verilog-CPU-Design-Document 零.数据通路描述(P4设计文档中最好加上P3图) ​ 本次设计文档基于P3-logisim-CPU的设计文档完成,P4的主要任务即为将logisim电路映射到verilog代码(看图写话?),对上次P3中实现的每个模块单独建立module.v,利用verilog语言对其行为进行建模,注意本次CPU设计中,不同于P3的异步复位,P4在各模块中使用同步复位!!!,verilog语言表示同步复位与异步复位如下代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 //同步复位表示方法 always@(posedge clk) begin if(reset) begin PC <= 32'h0000_3000; end else begin PC <= npc; end end //异步复位表示方法 将复位信号加入敏感信号列表 always@(posedge clk or posedge reset) begin if(reset) begin PC <= 32'h0000_3000; end else begin PC <= npc; end end 一.模块设计 1.IFU取指令单元 ​ IFU中存储单元有PC寄存器和存储指令的ROM。IFU中存储指令要求的ROM大小为16KiB,即4096*32bit。在verilog语言中使用寄存器数组实现对ROM的模拟,即4096个32位的寄存器。使用系统命令$readmemh("code.txt,ROM")将code.txt中的16进制代码读入名为ROM的数组中,由于PC同步复位到32’h0000_3000,而ROM中对应存储第一条指令的地址为0,即PC寄存器中存储的地址与真实ROM地址存在0x0000_3000差量,故设计reg [31:0] tmp存储实际的地址,即pc-0x0000_3000,进而读出指令的地址为tmp[13:2]。 ...

November 10, 2023 · 11 min · sudo

支持40条指令P3_logisim_CPU

CPU Design Document 1. CPU Design 1.总体设计 ​ 通过对于《计算机组成与设计》一书的阅读,对于P3的CPU设计我有如下思考,设计MIPS架构的CPU的本质在于从指令存储器中读出32位MIPS指令,并对MIPS指令进行解析进而完成指令中要求的操作。在本次实验中,要求实现的指令有add,sub,ori,lw,sw,beq,lui,nop其中,R型指令有add,sub,I型指令有ori,lw,sw,beq,lui,特殊的有nop指令,32位全为0。 RIJ类型指令图: 此次实验中要求实现的基本指令: R型 Operation Op-Code rs rt rd shamt(移位) func add 000000 00000 100000 sub 000000 00000 100010 I型 Operation Op-Code rs rt 16-bit-immediate-value ori 001101 lw 100011 sw 101011 beq 000100 lui 001111 nop ​ 32’b0 ​ 将我们要实现的CPU分为几个模块:IFU(取指令单元),GRF(寄存器堆),ALU(算术逻辑单元),DM(数据存储器),EXT(扩展单元),Controller(控制器)。 ...

November 6, 2023 · 6 min · sudo

translate C recursive function into MIPS

Translate C recursive function into MIPS ​ 如何规范合理地把C语言中的递归函数翻译成MIPS assembly?这个问题令我头疼了一天,翻了网上很多教程总是感觉说的很浅,或者是重复着用MIPS编写计算阶乘的例子,找不到合适的教程对于一个迷茫的新手来说是一件非常绝望的事,经过我求助身边的大佬们,大佬们的一些分享,让我逐渐明白编写中的一些要点,并总结出一些编写规则。 一.C中的递归函数 ​ 对于编写MIPS程序,我们一般是先写出对应的C代码,再一句句翻译成MIPS语言。递归函数也是函数,从函数类型上看,应该有无返回值和有返回值这两种粗浅的大类,其中无返回值是一种值得注意的类型。 1.无返回值类型 ​ 在无返回值的递归函数中,我们往往只能看见递归层次中的return,但是实际上无返回值类型的返回语句可以省略,即程序运行结束时的return;可以省略,在翻译时,题目可能就会使用省略这一种写法,需要注意这个“隐藏”的return并在MIPS中自行编写。例如如i下C程序 1 2 3 4 5 6 7 8 9 10 #include<stdio.h> void print() { printf("hello world!"); (return;) } int main() { print(); } 2.有返回值类型 ​ 有返回值类型常见的为int等,有返回值类型的函数必须“显式“地说明出返回值,如果有分支,则在每个分支中都需要进行返回值的说明,这一类函数可以明显地看出哪里需要return对于编写比较友好,例如计算阶乘的示例 ...

October 22, 2023 · 5 min · sudo

P2课下&&MIPS常用宏定义

P2课下&&常用MIPS宏定义 ​ MIPS是一门非常灵活的语言,在编写过程中可以非常直观地感受到对内存的操作(虽然我编写MIPS都是照着C语言一句一句翻译),但也正因为是这样,使得MIPS编码看起来有一点繁琐,我们可以使用自己定义的宏macro进行代码风格的改善,将可以复用的代码抽离出来,类似于C语言中的函数。 1. MIPS——macro ​ MIPS中宏定义的方法是 //无参数宏定义 .macro name //statement .end_macro //有参数宏定义 .macro name(%parameter1,%parameter2,...) //statement .end_macro 1. end .macro end li $v0,10 syscall .end_macro 2. readinteger .macro readinteger(%des) li $v0,5 syscall move %des,$v0 .end_macro 3. printinteger .macro printinterger(%src) li $v0,1 move $a0,%src syscall .end_macro 4. printstr .macro printstr(%src) li $v0,4 la $v0,%src syscall .end_macro 5. 函数调用push .macro push(%src) sw %src,0($sp) subi ge$sp,$sp,4 .end_macro 6. 函数调用pop .macro pop(%des) addi $sp,$sp,4 lw %des,0($sp) .end_macro 7.一维数组计算地址偏移量 .macro getVectorAddress(%des,%col) sll %des,%col,2 .end_macro 8.二维数组计算地址偏移量 .macro getMatrixAddress(%des,%i,%j) multu %i,col(矩阵的列数) mflo %des add %des,%des,%j sll %des,%des,2 .end_macro 注意:一定是乘上矩阵的列数!QAQ之前乘了矩阵的行数还是看内存发现的bug ...

October 20, 2023 · 1 min · sudo

P1上机日志

P1上机——verilog完成部件设计以及状态机 ​ 永远在周一晚上上机之前才是我学计组效率最高的时候 ​ ——地球人 一.三段式状态机的设计 ​ 在上机之前我心血来潮想要把自己编写状态机的代码风格从一直以来的一段式更新为三段式,在看了网上好多有的没的真真假假对对错错的写法之后,我决定转头求助蒋老师和曾老师,果然得到了靠谱的答案,并成功应用到晚上的上机中QAQ。 ​ 分段思路:就如同状态机的设计图,有三个模块:状态转移逻辑(组合逻辑),状态存储(时序逻辑),输出逻辑(组合逻辑),在verilog中分段实现即可,逻辑更加清晰。具体来说可以写成always-always-assign这种形式,当然最后一段也可以写成always. ​ 下面给出示例代码: ​ Moore型 输出逻辑中对状态进行判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 module moore_example( input in; input clk; input rst_n; output check; ); localparam s0 = 4'b0001; localparam s1 = 4'b0010; localparam s2 = 4'b0100; localparam s3 = 4'b1000; reg [3:0] state; reg [3:0] n_state; //状态存储逻辑 always@(posedge clk or negedge rst_b)begin if(!rst_n)begin state <= s0; end else begin state <= n_state; end end //状态转移逻辑 一般来说两层case嵌套逻辑更加清楚 always@(*)begin case(state)begin s0: begin case(in)begin 1'b1:n_state = s1; 1'b0:n_state = s0; end end //省略其他部分 default: n_state = s0; end end //输出逻辑 assign check = (state == s3)?1'b1:1'b0; ​ Mealy型 输出逻辑中对当前状态和输入进行判断 ...

October 16, 2023 · 3 min · sudo