DIY编程器网

 找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 2266|回复: 0
打印 上一主题 下一主题

[待整理] 基于FPGA的M2M异构虚拟化系统

[复制链接]
跳转到指定楼层
楼主
发表于 2014-10-12 16:17:47 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
摘要         
        更具体地,虚拟化的层次可分为如图 2所示的指令集架构(ISA)、硬件抽象层(HAL)、操作系统、用户级库、应用程序的五个层次。图 2中还在各个层次下方列出了与其对应的知名产品。
       

        图 2 虚拟化技术的五个层次及其相应产品

         

        1.2.2.ISA层的虚拟化技术

        ISA层的虚拟化是虚拟化技术中的一个重要内容,它的主要目的是为上层应用提供一个或多个不同于物理层实际处理器ISA的ISA抽象层,从而使得多个处理器架构的应用程序能够在同一个处理器上运行,如图 3所示。
       

        图 3  ISA层的虚拟化

         

        ISA层虚拟化所产生的异构性使处理器本身的性能得到提高[1]。同时,x86架构凭借其强大的软硬件支持,仍将在未来的处理器竞争中占有统治地位[2] 。本项目将重点实现不同ISA(x86和MIPS)之间的虚拟化技术,使该RISC处理器与x86架构达到二进制兼容。
         
        1.2.3.动态翻译技术
        在ISA层的虚拟化中,二进制翻译是最重要的实现技术。二进制翻译是指把一中体系结构的机器码翻译成为另一种体系结构的机器码。通过二进制翻译,体系结构将成为一个软件层,它使得复杂的遗留体系结构能够用简单的硬件来实现,并且使得新颖的体系结构能够兼容原来的软件;此外,二进制翻译能够促进软件的优化[3]。
         
        二进制翻译有两种主要的实现方法,一种是静态翻译,另一种动态翻译。
         
        静态翻译把整个二进制可执行文件翻译成目标体系结构的指令集,它不需要先运行代码,所有的翻译都在编译时完成。所以静态翻译很难做到正确性,因为不是所有的代码都能在编译时被翻译器发现。比如,可执行文件的某些部分只能通过间接跳转才能到达,而间接跳转的目标值只有在运行时才知道。
         
        动态翻译把一小段源体系结构的代码翻译成目标体系结构的代码并把这一小段生成的代码缓存起来。只有当代码被运行时才开始执行翻译。跳转指令会被指定到已经翻译和缓存起来的代码段上。
         
        2.项目目标及开发平台选择
        2.1.项目目标及内容
        本项目提出一种新颖的M2M(Multiple ISAs applications to Multiple heter?-og?eneous core,M2M)概念,在虚拟化管理层上增加二进制翻译层,从而引入多个不同体系结构的应用程序,并在硬件层引入处理器的异构性。如图 4所示的项目总体层次包括多核异构层、虚拟化管理层、二进制翻译层以及软件应用层。
       

        图 4 项目总体层次

         

        本项目目标是基于Xilinx公司的XUP Virtex-5和Digilent的Nexys3 FPGA开发板实现多个RISC SoC平台建立硬件多核异构层;选择MIPS和x86 ISA构架设计吃豆子、俄罗斯方块等典型应用程序构成多样化应用场景,实现M2M原型系统。
         
        具体任务如下:
           
  •                 多核异构层

           
  •                 实现MIPS基本指令集,支持精确中断,含一级Cache的32位RISC CPU的RTL描述及其验证;       
  •                 完成VGA(或DVI)、PS/2、SRAM、UART等外设的控制器模块描述及各个模块的验证;       
  •                 引入Wishbone总线,集成SoC平台的各个模块;       
  •                 基于不同FPGA的RISC SoC平台(分别在XUP Vritex-5和Digilent Nexys3上基于自主软核QS-I搭建了RISC SoC平台),构建多核异构层
         
           
  •                 虚拟化管理层

           
  •                 对多核异构层的资源管理;       
  •                 对应用层多样化程序的调度分配
         
           
  •                 二进制翻译层

           
  •                 以软硬件协同方式完成RISC处理器在ISA层对x86的虚拟化,使得RISC处理器能够与多架构(如x86)二进制兼容;       
  •                 动态翻译的软件架构设计
         
           
  •                 软件应用层

           
  •                 系统软件编写。系统软件包括外设的驱动程序及系统控制程序;       
  •                 运行环境及库编写。设计实现系统库(如图形库、I/O库);       
  •                 应用软件编写。应用软件包括多个SoC完整应用系统(x86和MIPS),如简单的游戏程序。
         
        2.2.开发平台选择
        本项目主要选用Xilinx公司的XUP Virtex-5开发平台及Digilent公司的Nexys3开发平台。
         
        2.2.1.XUP Virtex-5 FPGA开发平台简介
        XUP Virtex5是集成了OpenSparcT1开源微处理器的一个强大系统。其丰富的特色以及开发平台的通用性,使得该板卡非常适合在教学及研究的领域搭建各种系统。如数字设计、嵌入式系统、通讯系统、网络、视频及图像处理等各个领域。XUP Virtex-5实体图见图 5,其资源模块图如图 6所示。XUP Virtex-5含有丰富的片上资源以及完整的工业标准接口。
       

        图 5 XUP Virtex-5 FPGA开发平台

         

       

        图 6 XUP Virtex-5平台资源模块

         

        本项目基于XUP Virtex-5 FPGA开发平台构建了基于自主软核QS-I的RISC SoC平台。
         
        2.2.2.Digilent Nexys3 FPGA开发平台简介
        Digilent公司的Nexys3平台是一个基于Xilinx Sparten-6 FPGA的完整,易用的数字电路开发平台。Sparten-6 FPGA为高性能逻辑设计做了充分优化,其片内的block RAM,时钟管理模块,DSP等资源为高级逻辑设计提供了必要硬件基础。Nexys3实体图见图 7,其平台资源模块如图 8。
       

        图 7 Digilent Nexys3 FPGA开发平台

       

        图 8 Digilent Nexys3平台资源模块

         

        本项目基于Digilent Nexys3 FPGA开发平台构建了基于自主软核QS-I的RISC SoC平台。
         
        3.总体设计方案说明
        3.1.1.系统总体框架及关键技术分析
         
        本项目的总体框架如图 9,实现时采用V5-MIPS core和N3-MIPS core构成异构原型。
       

        图 9 项目总体框架图

         

        项目的关键技术包括:
           
  •                 多核异构层

           
  •                 32位RISC处理器的结构竞争、控制竞争、数据竞争三大竞争的解决以及扩展CPU指令集,支持GNU的mips_elf_gcc编译器等开源工具链       
  •                 流水线中的精确中断及异常的实现       
  •                 CPU的Cache设计:

           
  •                 Cache的映射策略       
  •                 Cache的写策略

           
  •                 CPU和各个外设模块对Wishbone总线的集成:

           
  •                 CPU的对外接口设计       
  •                 各个外设符合Wishbone协议的接口设计       
  •                 各个模块与总线的集成

           
  •                 基于不同FPGA的RISC SoC平台(分别在XUP Vritex-5和Digilent Nexys3上基于自主软核QS-I搭建了RISC SoC平台),构建多核异构层。基于单个核的可扩展SoC框架核心如图 10

        图 10 RISC SoC

         
           
  •                 虚拟化管理层

           
  •                 对多核异构层的资源管理策略;       
  •                 对应用层程序的调度分配策略
         
           
  •                 二进制翻译层

           
  •                 以基本块为翻译单位的动态翻译实现       
  •                 寄存器的映射       
  •                 Big endian(MIPS)与little endian(x86)       
  •                 X86标志位处理       
  •                 x86中断及系统调用的模拟       
  •                 转移分发器       
  •                 翻译块管理
         
           
  •                 软件应用层

           
  •                 SoC系统软件模块       
  •                 各个外设模块的驱动程序
         
        3.1.2.RISC CPU(QS-I)结构
        RISC CPU(QS-I)的总体框架如图 11所示。QS-I中的整数流水线采用Fetch à Decode à Execute à Memory à Writeback的五级流水线结构。QS-I中采用哈佛结构的指令Cache和数据Cache。此外,为了加速ISA层虚拟化的动态翻译实现,QS-I中含有专门的硬件模块以加速动态翻译。
       

        图 11 RISC CPU(QS-I)总体框架图

         

         

        3.1.3.动态翻译策略与方案

       

        图 12 动态翻译及执行架构图

         

        二进制动态翻译及执行架构如图 12所示,主要有翻译及执行两个过程。翻译过程将源体系的程序进行翻译,生成新体系的程序。执行过程负责生成块在新体系下的运行。以8086转MIPS为例,源体系为8086,新体系为MIPS。图中SMEM代表的是源内存,保存的是8086可执行程序,TMEM代表的是目标内存,保存的是8086程序块翻译后得到的相应MIPS程序块。翻译过程是在MIPS环境中,翻译块的执行在虚拟环境中。两种环境的转换需要经过上下文切换。切换时,先保存当前状态,包括它自己的一套寄存器组,再载入新的状态。
         
        跳转缓存为硬件模块,加速生成块的执行。跳转缓存保存的是<SPC, TPC>对,SPC为某指令在SMEM中的地址,TPC则是在TMEM中相应的地址。在执行生成块遇到转移跳转指令时,根据SPC在跳转缓存中找对应的TPC,以继续执行生成块。
         
        3.1.4.Wishbone总线及基本外设
        Wishbone 总线最先是由Silicon公司提出,现在己被移交给OpenCores组织维护,它通过在IP核之间建立一个通用接口完成互联。可以用于在软核、固核以及硬核之间进行互联。
         
        Wishbone规范具有如下特点:
           
  •                 简单、紧凑,需要很少的逻辑门       
  •                 完整的普通数据据传输总线协议,包括单个读写传输周期和事件周期       
  •                 支持大端数据和小端数据,接口自动完成两者之间的转换       
  •                 支持存储器映射、FIFO存储器、交叉互联       
  •                 采用握手协议,允许速率控制,可以达到每个时钟周期进行一次数据传输       
  •                 支持普通周期结束、重试结束、错误结束等总线周期形式,支持用户自定义的标志       
  •                 采用MASTER/SLAVE体系结构,支持多点进程(Multi-MASTER)       
  •                 支持各种各样的IP核互联,包括USB、双向总线、复用器互联等
         
        相对于其他的IP核接口规范来说,Wishbone接口规范具有简单、开放、高效、利于实现等特点而且完全免费,并没有专利保护。基于上述优点,因此采用Wishbone总线进行接口设计。
         
        QS-I 硬件系统在团队自主研发的MIPS体系结构5级流水CPU的基础上,采用Wishbone开源总线,将主存、显示器、键盘、RS232等外部设备与CPU进行互联。CPU作为Wishbone开源总线MASTER/SLAVE体系结构中的MASTER设备,向总线发起访问外设的请求;Wishbone总线接收来自CPU的访问请求和访问命令,向外设发起访问请求;外设相应请求后,数据通过Wishbone总线传给CPU。采用Wishbone总线后,系统能够高效的访问各种外设,而且具有较好的可扩展性,比如在Wishbone开源总线MASTER/SLAVE体系结构中,MASTER设备和SLAVE设备都是可已同时联接多个的,总线会解决相关的访问冲突。
         
        下文将介绍Wishbone开源总线的相关信号的定义及作用。
       

        图 13  Wishbone总线规范中使用的主要信号

         

        所有的Wishbone接口信号都是高电平有效,设计成高电平有效的主要原因是由于低电平有效信号的书写问题,不同的设计者表达低电平有效信号的方式不同,拿最常见的低电平有效的复位信号来说,其表示方法就有_RST_I、N_RST_I、#RST_I和/RST_I,而高电平有效的信号其表达方式通常只有一种。所有的Wishbone接口信号都以_I或者_O结束。_I表示输入,_O表示输出。()表示该信号为总线信号,总线的宽度可以为1,也可以为大于1的任何值。
         
        在图7中,主设备具有遵守Wishbone规范的主设备接口,从设备具有遵守Wishbone规范的从设备接口,INTERCON模块将主设备和从设备的对应数据、地址和控制线连接起来,SYSCON模块提供时钟和复位信号,这两个信号被送入主设备和从设备。图7给出了Wishbone接口的常见信号,这些信号有些是必须的,有些是可选的。这些信号的基本功能描述如下:
         
        CLK_O/CLK_I:时钟信号。时钟信号由SYSCON模块产生,并送入各个主设备和从设备。SYSCON通常内部存在一个锁相环,将来源于芯片外的晶体振荡器或者时钟输入信号整形、分频或者倍频为芯片内所需要的时钟信号。所有Wishbone信号都同步到时钟信号上,包括复位信号。
         
        RST_O/RST_I:同步复位信号,高电平有效。复位信号由SYSCON模块产生,并送入各主设备及从设备。
        DAT_O()/DAT_I():主设备和从设备的之间的数据信号,数据可以由主设备传送给从设备,也可以由从设备传送给主设备。一对主设备和从设备之间最多存在两条数据总线,一条用于主设备向从设备传输数据,另外一条用于从设备向主设备传输数据。Wishbone规定数据总线的最大宽度为64位,这一规定实际上是考虑到目前商用处理器的最大位数为64,实际上数据总线的宽度可以是任意值。就笔者看来,在64位处理器以后,处理器将向多核方向发展,总线将向高速串行方向发展。
         
        ADR_O(n…m)/ADR_I(n…m):地址信号,主设备输出地址到从设备。n取决于IP核的地址宽度,m取决于数据总线DAT_O()/DAT_I()的宽度和粒度。数据总线的粒度指的是数据总线能够一次传送的最小比特数,很多处理器如ARM的数据总线的粒度为1个字节,但是也有一些处理器如CEVA TeakLite DSP的数据总线粒度为2个字节。一个位宽为32比特、粒度为1个字节的数据总线的地址信号应定义为ADR_O(n…2)/ADR_I(n…2)。在传送数据时,具体哪些字节有效通过SEL_O()/SEL_I()信号(见下文)控制。
         
        TGD_O/TGD_I()、TGA_O()/TGA_I():TGD_O/TGD_I()为数据标签,具体讲是附加于在数据总线DAT_O()/DAT_I()的标签,该标签可以用于传送关于数据总线的额外信息如奇偶校验信息、时间戳信息等。TGA_O/TGA_I()为地址标签,具体讲是附加于在地址总线ADR_O()/ADR_I()的标签,该标签可以用于传送关于地址总线的额外信息如地址总线奇偶校验信息、存储器保护信息等。Wishbone只规定了TGD_O/TGD_I和TGA_O()/TGA_I()的接口时序,用户可以定义TGD_O/TGD_I的具体含义。
         
        TGC_O/TGC_I():TGC_O/TGC_I()为总线周期标签,该标签可以用于传送关于当前总线周期所进行操作的描述如操作类型(包括单次操作、块操作、RMW操作)、中断应答类型、缓存操作类型等。类似的,Wishbone只规定了TGC_O/TGC_I()的接口时序,用户可以定义TGD_O/TGD_I的具体含义。
         
        ACK_O/ACK_I、ERR_O/ERR_I、RTY_O/RTY_I:主从设备间的操作结束方式信号。ACK表示成功,ERR表示错误,RTY表示重试(Retry)。操作总是在某一总线周期内完成的,因此操作结束方式也称为总线周期结束方式。成功是操作的正常结束方式,错误表示操作失败,造成失败的原因可能是地址或者数据校验错误,写操作或者读操作不支持等。重试表示从设备当前忙,不能及时处理该操作,该操作可以稍后重新发起。接收到操作失败或者重试后,主设备如何响应取决于主设备的设计者。
         
        SEL_O()/SEL_I():有效数据总线选择信号,标识当前操作中数据总线上哪些比特是有效的,以总线粒度为单位。SEL_O()/SEL_I()的宽度为数据总线宽度除以数据总线粒度。比如一个具有32位宽、粒度为1个字节的数据总线的选择信号应定义为SEL_O(3:0)/ SEL_I(3:0),SEL(4’b1001)代表当前操作中数据总线的最高和最低字节有效。
         
        CYC_O/CYC_I、LOCK_O/LOCK_I、GNT_O()/GNT_I:总线周期信号CYC_O/CYC_I有效代表一个主设备请求总线使用权或者正在占有总线,但是不一定正在进行总线操作(是否正在进行总线操作取决于选通信号STB_O/STB_I是否有效)。只有该信号有效,Wishbone主设备和从设备接口的其它信号才有意义。CYC_O/CYC_I信号在一次总线操作过程中必须持续有效,比如一次块读操作可能需要多个时钟周期,CYC_O/CYC_I信号必须保持持续有效。实际上,该信号的实际作用等同于其他总线标准中的仲裁申请信号。当存在多个主设备时,它们可能希望同时进行总线操作,主设备通过仲裁申请信号向仲裁器申请总线占有权,仲裁器通过一定的仲裁优先级逻辑向其中一个选定的主设备发送总线允许信号GNT_O()/GNT_I,表示该主设备可以占用总线。GNT_O()是仲裁器输出的允许信号,一般有多个;而对于一个主设备,其允许信号输入GNT_I却只有一个。一次总线操作可能需要多个时钟周期,比如一次块操作。在操作过程中,仲裁器可能会提前将总线占用权收回并分配给其他主设备从而打断当前主设备的操作,LOCK_O/LOCK_I有效代表本次总线操作是不可打断的。仲裁器收到LOCK_I信号,就不会提前收回总线使用权。图 13中只有一个主设备和一个从设备,因此没画出仲裁器模块,该模块可以视为是INTERCON的一部分,见本章最后给出的例子。
         
        STB_O/STB_I:选通信号。选通有效代表主设备发起一次总线操作。只有选通信号有效(此时CYC_O/CYC_I也必须为高),ADR_O/ADR_I()、DAT_O()/DAT_I()、SEL_O()/SEL_I()才有意义。在Wishbone总线规范中,CYC_O/CYC_I是最高层的控制信号,只有该信号有效,STB_O/STB_I信号才有意义。一个信号有意义是指该信号的当前值是需要主设备或者从设备解释的,0为无效,1为有效,而一个信号没有意义是指该信号的当前值主设备和从设备不必关心,是0还是1都无效。
         
        WE_O/WE_I:写使能信号,代表当前周期中进行的操作是写操作还是读操作。1代表写,0代表读。
         
        3.1.5.系统库及应用程序设计
        为了能够更好地发挥系统功能,扩大系统的应用范围,充分利用系统的各种硬件资源以及外围设备,包括键盘、串口、显示器,项目团队开发出了供应用程序调用的系统库。根据外设,可以将系统库函数分为四部分:系统宏定义和端口常量、键盘接口函数、显示器接口函数和串口接口函数。
       

        图 14  系统库及应用程序总体结构

         

        如图 14,在团队自主研发的QS-I 硬件系统的基础上,团队开发了函数系统库和多个应用程序。其中系统函数库包括图形库、VGA显示器接口、RS232串口接口、PS/2键盘接口。应用程序根据所最终运行时的机器码可以分为MIPS体系结构应用程序和x86体系结构应用程序。其中,MIPS体系结构应用程序包括使用MIPS汇编语言编写的俄罗斯方块游戏和使用C语言编写的吃豆子游戏;x86体系结构应用程序包括使用8086汇编语言编写的推箱子游戏。
         
        该项目的硬件系统是自主研发的,因而需要开发基于该系统的软硬件接口函数库,并且需要编写应用程序测试系统的各种功能。
         
        该项目的硬件系统支持MIPS体系结构的指令集。基于MIPS指令集,团队开发了将MIPS汇编语言翻译成机器码的汇编程序,并分别使用MIPS汇编语言和C语言开发了系统函数库。使用MIPS汇编语言开发应用程序,用来验证五级流水线CPU的功能正确性,检测出数据相关和控制相关出现的问题,验证硬件系统对MIPS指令集的支持度,验证CPU与各种外部设备互联的正确性以及系统的总体性能。
         
        基于MIPS汇编语言开发了俄罗斯方块游戏。俄罗斯方块的基本规则是移动、旋转和摆放游戏自动输出的各种方块,使之排列成完整的一行或多行并且消除得分。本设计的俄罗斯方块有5种基本形状,7种颜色;可通过键盘控制左移(A)、右移(D)、旋转(W)、加速下降(S)、暂停(Space)、退出(Q)、选择级别(L);游戏设置两种级别,普通下降速度和较快下降速度两级;游戏界面显示帮助、玩家所得分数、下一个即将下落的形状等信息;当方块到达顶端时,游戏失败;玩家通过不断的刷新最高分获得愉悦。
         
        为了提高软件开发的效率,使软件开发人员方便的使用硬件系统及其外设资源,团队使用Gcc编译器和MASM汇编器的组合,项目团队开发了基于C语言的应用程序,验证硬件系统对Gcc编译器和MASM汇编器生成的机器指令的支持。团队还开发了基于C语言的系统函数库,包括图形库、VGA显示器接口、键盘接口和串口接口等。此外,团队还使用JAVA语言,开发了MIPS指令CPU模拟器,使得在软件的开发过程中不需要每次都传输到硬件开发板上就可以进行调试,大大提高程序开发的效率。
         
        基于C语言开发了吃豆子游戏。吃豆子的基本规则是控制packman的移动,使之吃完所有的豆子,并且躲开怪物的追捕。本程序设计的怪物有2个;豆子有两种,一种普通的小豆子,还有一种大豆子,吃了大豆子能让怪物慢下来几秒钟;可通过键盘控制左移(A)、右移(D)、旋转(W)、加速下降(S)、暂停(Space)、退出(Q);游戏界面显示帮助、玩家所得分数等信息;当玩家被怪物抓到时,游戏失败;玩家将所有的豆子都吃光时,游戏正常结束。
         
        该硬件系统能够执行不同指令集体系结构的机器码。为了验证系统执行不同指令集机器码的正确性,调试出硬件系统中存在的问题,团队开发了基于x86汇编语言的应用程序。团队还开发了基于x86语言的系统函数库,包括图形库、VGA显示器接口、键盘接口和串口接口等。
         
        基于x86汇编语言开发了推箱子游戏。推箱子的基本规则是控制机器人的移动,使之把所有的箱子放到指定的位置。箱子只有1种;可通过键盘控制左移(A)、右移(D)、旋转(W)、加速下降(S)、暂停(Space)、退出(Q);游戏界面显示帮助、玩家所用的时间等信息;玩家将所有的箱子都放到适当的位置时,游戏正常结束。
         
         
         
        4.详细设计方案
        4.1.RISC CPU详细设计方案
        4.1.1.RISC CPU(QS-I)整体模块
       

        图 15 QS-I CPU整体模块图

         

        如图 15所示,QS-I的整体模块中主要包括两部分,第一部分是五级流水线和Cache,另一个模块是动态二进制翻译加速模块(DBT Accelerator)。CPU的对外接口符合Wishbone总线标准。
        在下一节中,我们将会介绍QS-I CPU中各个单元模块功能以及其时序。
         
        4.1.2.RISC CPU(QS-I)各单元模块功能与时序
        4.1.2.1五级流水线模块详细设计方案
           
  •                 功能描述
        本模块的功能是完成指令的五级流水化执行,并支持中断及异常。一条指令的执行经过是Fetch(取指) à Decode(译码) à Execute(执行) à Memory(访存) à Writeback(写回)五个阶段。其中Fetch阶段从指令Cache中由PC(Praogram Counter)取得下一条指令;Decode阶段完成指令的大部分译码工作,并产生相应的控制信号;Execute阶段主要完成指令的算术运算以及少量选择电路;Memory阶段完成访存任务,数据将从数据Cache中读取或者写入;Writeback阶段完成写回指令的修改Register File(寄存器堆)的写操作。此外,本模块完成了流水线中的精确中断处理。
         
           
  •                 子模块列表
                                                                        Module
                       
                                                                        Description
                       
                                                                        pc_reg
                       
                                                                        Module of PC register
                       
                                                                        if_stage
                       
                                                                        Instruction Fetch stage
                       
                                                                        if_id_reg
                       
                                                                        Registers between IF and ID stages
                       
                                                                        id_stage
                       
                                                                        Instruction Decode stage
                       
                                                                        id_exe_stage
                       
                                                                        Registers between ID and EXE stages
                       
                                                                        exe_stage
                       
                                                                        Execute stage
                       
                                                                        exe_mem_reg
                       
                                                                        Registers between EXE and MEM stages
                       
                                                                        mem_stage
                       
                                                                        Memory stage
                       
                                                                        mem_wb_reg
                       
                                                                        Registers between MEM and WB stages
                       
                                                                        wb_stage
                       
                                                                        Writeback stage
                       
                                                                        except
                       
                                                                        Module of exception handling
                       
         
           
  •                 详细设计

           
  •                 pc_reg
        本模块完成对PC寄存器的更新任务。若流水线中出现stall或者Cache发生miss等情况则延迟对PC寄存器的更新。本模块的时序如下图所示。
       

        图 16 pc_reg时序图

         

           
  •                 if_stage
        本模块完成对指令Cache的取指。模块的对外接口符合Wishbone总线标准。CPU的对外接口包括IBus(指令总线)和DBus(数据总线),为了将CPU成功集成入Wishbone总线中,CPU的对外接口部分的逻辑设计必须符合Wishbone标准。此外,CPU对外部设备(如RAM,ROM,UART)的访问速度以及CPU有无Cache(高速缓存)是未知的,因此这部分的逻辑设计必须带有通用性。
        本模块的主要时序如下图。
       

        图 17 if_stage时序图

         

           
  •                 if_id_reg
        本模块完成IF和ID两个阶段之间的信号流水。本模块的时序图如下。
       

        图 18 if_id_reg时序图

         

           
  •                 id_stage
        本模块完成指令的基本译码,并产生相应的控制信号。RISC处理器的结构竞争、控制竞争、数据竞争三大竞争的解决:
         
        采用旁路(bypass)技术解决数据竞争,双跳(double bump)解决结构竞争,延时槽技术解决控制竞争。
         
        本模块的基本时序图如下。
       

        图 19 id_stage时序图

         

           
  •                 id_exe_stage
        本模块完成IF和ID两个阶段之间的信号流水。本模块的时序图如下。
       

        图 20  id_exe_reg时序图

         

           
  •                 exe_stage
        本模块完成指令的算术逻辑运算。其中,运算包括add, sub, and, or, xor, nor, sll, srl, sra, lui等算术或逻辑运算。
        为了x86架构动态翻译的加速需要,在ALU(算术运算单元)中增加了x86 flag标志寄存器,并将该寄存器作为一个系统寄存器,用户程序可通过mfc0,mtc0指令来修改flag标志寄存器。
         
        本模块的时序图如下。
       

        图 21  exe_stage时序图

           
  •                 exe_mem_reg
        本模块完成EXE和MEM两个阶段之间的信号流水。本模块的时序图如下。
       

        图 22  exe_mem_reg时序图

           
  •                 mem_stage
        本模块完成对数据Cache的读写。模块的对外接口符合Wishbone总线标准。本模块的主要时序如下图。
       

        图 23  mem_stage时序图

           
  •                 mem_wb_reg
        本模块完成MEM和WB两个阶段之间的信号流水。本模块的时序图如下。
       

        图 24 mem_wb_reg时序图

         

           
  •                 wb_stage
        本模块完成写回指令的寄存器堆修改操作。本模块的时序图如下。
       

        图 25 wb_stage时序图

         

           
  •                 except
        本模块完成流水线中的中断及异常处理。为了完成精确中断,即产生异常的指令前已经在流水线中的指令完成执行,而在异常指令后的指令不允许完成执行(不修改CPU状态),才能响应异常。因此,在实现精确中断时,需要对流水线中的指令进行跟踪,所有的异常或中断信号将延迟到流水线的特定阶段(Writeback)进行响应,并且对不同类型的异常信号,中断程序的返回地址不同。本模块的主要时序图如下。
       

        图 26 except时序图

         
        4.1.2.2Cache模块详细设计方案

           
  •                 功能描述
        本模块实现指令Cache和数据Cache。其中,指令Cache和数据Cache的映射策略都采用直接映射方式。指令Cache只读,数据Cache的写策略为写通过(主存和Cache里的数据时钟保持一致)。
           
  •                 子模块列表
                               
                                           
  •                                                                                 
                       
                               
                                           
  •                                                                                 
                       
                               
                                           
  •                                                                                 
                       
                                                                        Instruction Cache top
                       
                               
                                           
  •                                                                                 
                       
                                                                        Data Cache top
                       

           
  •                 详细设计

           
  •                 ic_top
        本模块的时序图如下。
       

        图 27 ic_top时序图

         

        4.1.2.3动态翻译硬件模块详细设计方案
           
  •                 功能描述
        为了提高动态翻译效率,我们在CPU中增加了硬件加速模块。动态翻译硬件加速包括以下部分:
           
  •                 在QS-I CPU的ALU模块中增加x86 flag寄存器(MIPS架构中没有flag标志寄存器),软件可通过mtc0,mfc0两条指令来访问flag寄存器。这样x86的算术逻辑或比较指令(如add, sub, cmp等),以及条件跳转指令(如ja, jb, jg等)有效地得到了硬件支持,使得软件的翻译效率大大提高。       
  •                 在QS-I CPU外增加了JLUT(Jump address Lookup Table),即跳转地址查表。通过CAM(Content Address Memory)的硬件支持,跳转指令的翻译效率将比完全基于软件的翻译方式提高一个数量级。在QS-I中将新增4条用户指令campi, ramri, camwi, camwi用于软件对JLUT的访问。
         
           
  •                 子模块列表
                               
                                           
  •                                                                                 
                       
                               
                                           
  •                                                                                 
                       
                               
                                           
  •                                                                                 
                       
                               
                                           
  •                                                                                 
                       
                                                                         
                       
                                                                        JLUT top
                       
                                                                         
                       
                               
                                           
  •                                                                                 
                       
                                                                        SPC is stored in CAMs, and it will take less than two clock cycles to get address of the CAMs content specified.
                       
                                                                         
                       
                               
                                           
  •                                                                                 
                       
                                                                        TPC is stored in ubiquitous RAMs.
                       

           
  •                 详细设计
        如下方的JLUT详细设计图所示,JLUT模块与QS-I CPU之间通过campi, camwi, ramwi, ramwi四条指令进行交互。
        campi用于CAM的查表,camwi用于CAM的写操作,ramwi用于RAM的写操作,RAMRI用于RAM的读操作。
        4条指令的格式如下。
                               
                                        Instruction

                       
                               
                                        Format

                       
                               
                                        Usage

                       
                               
                                        campi

                       
                               
                                        opcode, rs, 5’h0, rd, 5’h0, func

                       
                               
                                        campi rd, rs

                       
                               
                                        camwi

                       
                               
                                        opcode, rs, rt, 5’h0, 5’h0, func

                       
                               
                                        camwi rt, rs

                       
                               
                                        ramwi

                       
                               
                                        opcode, rs, rt, 5’h0, 5’h0, func

                       
                               
                                        ramwi rt, rs

                       
                               
                                        ramri

                       
                               
                                        opcode, rs, 5’h0, rd, 5’h0, func

                       
                               
                                        ramri rd, rs

                       
       

        图 28  JLUT详细设计图

         

        4.2.动态翻译详细设计方案
        二进制翻译技术能够把一种体系结构的二进制程序翻译成另一种体系结构的二进制程序,以在新的体系结构下运行。二进制翻译主要有三类:解释执行、静态翻译及动态翻译。
         
        在系统总体框架图中,二进制翻译层可运行不同的翻译程序,以在不同的体系之间进行转换,如x86到MIPS、ARM到MIPS、x86到ARM等。本部分挑选了8086到MIPS的动态翻译作为实现原型。
         
        4.2.1.二进制翻译介绍
        二进制翻译可以分为三大类:解释执行、静态翻译和动态翻译。
         
        解释执行的流程是:取指、解析、执行。它对源机器代码进行实时解释并执行,然后继续下一条指令。系统不对已解释的指令进行保存或缓存。在这个框架下,不能对代码进行优化。这种翻译技术能取得高度兼容性,但执行效率很低。
         
        静态翻译是先将源可执行文件转换成目标机器可执行文件,然后运行在目标机器上。这是离线翻译,因此有充足的时间对代码进行优化,以提高代码的执行效率。但静态翻译很难做到正确性,如代码的自修改问题,执行过程中有些跳转值只能在运行时才能获知等问题。
         
        解释执行是实时翻译,静态翻译是离线翻译,动态翻译就像是两者的折中。它不像解释执行那样对每条指令进行翻译并马上执行,也不像静态翻译那样将指令完全翻译好之后才执行。它每次对一个基本块进行翻译并执行,然后取另一个块。一个基本块一般包含多条算术类型指令,最后是一条控制流(Control Flow)类型指令。已翻译的块可进行缓存或保存。动态翻译只对将要执行的代码进行翻译,且能很好地解决代码自修改问题。
         
        4.2.2.二进制翻译策略选择
        本项目采取的是软硬协同动态翻译策略,将源二进制代码进行翻译,当遇到控制流类型指令,如跳转指令,系统调用等,翻译过程挂起,将已翻译的指令序列作为一个基本块,然后运行基本块。当基本块执行完以后,会跳到下一处执行。若下一处已翻译过,则继续执行,否则暂停执行以进行翻译,如此过程循环。完整的流程如下图所示。
       

        图 29 x86程序翻译执行流程

         

        基本块执行时有硬件模块辅助,如图 12所示。硬件模块管理跳转缓存,缓存的基本项为<SPC, TPC>对。程序执行到跳转指令时,程序向跳转缓存发送SPC,得到相应的TPC,再跳至TPC继续执行生成块。简单的示例如图 30所示。源程序从块A开始执行,到末尾时,需要跳转到块C。翻译后执行,执行完块A’后将要跳转,此时的跳转地址是SMEM中地址,即SPC,要转换成相应的TPC,该TPC就由跳转缓存中寻找。
       

        图 30 SMEM与TMEM的映射

         

        4.2.3.8086程序的载入
        首先,由系统向服务器发送命令,命令格式为x86 *.com,它包含在自定义传输协议中,类型码为86,要求.com文件仅使用一个段,大小限制为64KB。服务器找到所指定的文件,并将其传送给系统,系统将其存放在内存中。至此,完成8086可执行程序的载入。
         
        4.2.4.标志寄存器处理
        8086中有个标志寄存器FLAGS,而MIPS中没有与之相对应的标志寄存器,解决办法有二,软件模拟实现或硬件提供支持。
         
        软件模拟指的是,当一条8086指令执行后,会影响哪些标志位,然后用软件方法将其模拟出来,使两者的结果一致。如执行add ax, bx对溢出位的影响。模拟时,将ax移到MIPS的$t0寄存器的低16位,将bx移到MIPS的$t1寄存器的低16位,然后对$t0和$t1做加法,结果放到$t0,相对应的指令为add $t0, $t0, $t1。结果是否溢出则要查看$t0的第16位。最后,还要将溢出位存放至标志寄存器的对应位。这中间还要涉及移位运算、位运算等,所需代价很大,但有个好处是无需对硬件平台做改动,使硬件平台更为纯粹。
         
        若采用提供硬件支持,那么硬件平台需稍做修改,增加一个类FLAGS寄存器。仍以上面的add ax, bx为例。将ax、bx分别放到$t0、$t1的高16位,然后进行相加,是否溢出的结果会自动保存到新添加的类FLAGS寄存器里,因而软件层面无需再做处理。此种做法,增加了硬件工作,但大大简化了软件的操作。8086的FLAGS有多个标志位,若要完全实现,那么对本身的硬件平台改动会比较大,因此,我们只选择了其中几个进行实现,如Z、O、C、S等。
         
        4.2.5.寄存器映射
        MIPS有32个通用寄存器,从0号到31号,每个寄存器为32位。8086的通用寄存器有8个:AX、CX、DX、BX、SP、BP、SI和DI。这8个通用寄存器都是16位,AX、CX、DX和BX还可以分成两个8位寄存器,高8位和低8位,如AX可分为AH和AL。此外,段寄存器有CS、DS、ES和SS,都是16位。还有IP寄存器和FLAGS寄存器,也都是16位。
         
        因为MIPS的寄存器数量比8086的寄存器多,可以采用直接映射,一个8086寄存器对应一个MIPS寄存器,而不需要对寄存器进行置换,简化了寄存器的管理。
         
        Table 1 寄存器映射
                               
                                        8086

                       
                               
                                        MIPS

                       
                               
                                        AX

                       
                               
                                        s0(R16)

                       
                               
                                        CX

                       
                               
                                        s1(R17)

                       
                               
                                        DX

                       
                               
                                        s2(R18)

                       
                               
                                        BX

                       
                               
                                        s3(R19)

                       
                               
                                        SP

                       
                               
                                        s4(R20)

                       
                               
                                        BP

                       
                               
                                        s5(R21)

                       
                               
                                        SI

                       
                               
                                        s6(R22)

                       
                               
                                        DI

                       
                               
                                        s7(R23)

                       
                               
                                        CS

                       
                               
                                        t4(R12)

                       
                               
                                        DS

                       
                               
                                        t5(R13)

                       
                               
                                        ES

                       
                               
                                        t6(R14)

                       
                               
                                        SS

                       
                               
                                        t7(R15)

                       
                               
                                        IP

                       
                               
                                        t8(R24)

                       
<div style="clear:both;">                  
        4.2.6.上下文切换
        从翻译流程可以看出,整个过程有两个运行环境。翻译过程是在MIPS环境中,执行则是在MIPS的8086虚拟环境中。但硬件只有MIPS的一套寄存器,8086的寄存器是映射到MIPS的寄存器。在切换运行环境时,要先保存当前寄存器组,再载入新的寄存器组。方式可以有两种。一种是将寄存器组保存至堆栈,另一种是将寄存器组保存至内存。这里将其保存至内存,因为这更利于调试,可将寄存器值调出来查看。
         
        上下文切换实现:
        /**
         * context switch
         * @param type - 0:从x86切换到mips,other:从mips切换到x86
         */
        void _contextSwitch(int type) {
            if (type == _MIPS_TYPE) {
                _saveRegisters(_X86_TYPE);
                _loadRegisters(_MIPS_TYPE);
            } else {
                _saveRegisters(_MIPS_TYPE);
                _loadRegisters(_X86_TYPE);
            }
        }
         
         
        上下文切换的流程及实现如下。
       

        图 34 寄存器组切换

         

        4.2.7.字节顺序与边界对齐问题
        字节序(Byte Order)一般有两种:大端序(big-endian)和小端序(little-endian)。大端序是将最高有效字节(MSB,Most Significant Byte)存放至低地址,小端序则是将最低有效字节(LSB,Least Significant Byte)存放至低地址。MIPS采用的是大端序,而8086使用的是小端序。因此,在二进制翻译中,必须要处理这种差异。
         
        8086在访问内存时,如果是字节操作,那么翻译时,可以使用对应的MIPS字节操作指令,如lb,sb等。字节的操作不会有字节序问题,处理多字节时会有字节序问题。8086访问多字节时,不能使用相应的MIPS指令,而应拆分读取,最后再拼凑。过程如图 35所示。
         
       

        图 35 字节序问题解决

         

        4.2.8.堆栈处理
        MIPS有一个堆栈寄存器$sp,8086则是使用一个堆栈段寄存器SS与一个堆栈指针寄存器SP,实际地址为段基址加偏移地址。本设计MIPS和8086各自有自己的堆栈空间。8086堆栈操作对象有寄存器、立即数、内存等。这里以寄存器压栈为例,出栈情况类似。压栈时,首先要计算出绝对地址,然后将寄存器保存。计算地址方法:(SS << 4) + IP,是一个20位地址,寻址1M。push AX时,是对一个16位寄存器压栈,由上面的字节序分析可知,要拆分成两个字节压栈。这里为了简便,直接对32位MIPS寄存器进行压栈,因为8086寄存器只占用MIPS寄存器的高16位。这里,绝对地址是放在辅助寄存器里的,因此要对该辅助寄存器进行保存,以防止要压栈的寄存器就是该辅助寄存器而造成压栈失败。我们是将该辅助寄存器保存在MIPS的堆栈中。
         
        4.2.9.操作数分析
        8086的操作数类型比较多样,有寄存器、立即数、内存等。每个类型的位宽还可以变化,有8位、16位之分。MIPS比较规则,寄存器为32位,立即数为16位。两者之间的转换是翻译的重要方面。
         
        寄存器如果是16位,则可以直接映射到对应MIPS寄存器的高16位。如果是8位寄存器,不管是高8位还是低8位,一般都要移出至辅助寄存器的高8位。移到高8位的原因是为了运算时能产生正确的标志位。运算完后再将运算结果搬回8086寄存器。
         
        立即数有3种情况:8位,16位及由8位符号扩展来的16位立即数。8位及8位符号扩展可直接以字节访问内存得到,16位立即数则要注意字节序问题,上面已分析过。
         
        内存方面涉及到内存地址及所指向的内存内容。内存地址的拼接会在下文详细分析,8086有多种段寄存器与偏移的组合方式。取内存内容时也会遇到字节序的问题。
        操作数类型示意图如下。
       

        图 36 操作数类型

         

        4.2.10.二进制翻译及代码生成
        这部分主要解析8086指令,将其拆分,并生成相应的MIPS代码。在具体解析指令之前,先研究下8086二进制机器码的特点,抽出公共特征。以下几张表均来自于[6]。
         
        Table 2 oo修饰位说明
                               
                                        oo

                       
                               
                                        功能

                       
                               
                                        00

                       
                                                                        如果mmm=110,那么位移量在操作码后面,否则没有使用位移量
                       
                               
                                        01

                       
                                                                        操作码后面是8位有符号的位移量
                       
                               
                                        10

                       
                                                                        操作码后面是16位有符号的位移量
                       
                               
                                        11

                       
                                                                        mmm指定一个寄存器而不是一种寻址方式
                       
         
        Table 3 16位寄存器/存储器(mmm)字段描述
                               
                                        mmm

                       
                               
                                        16位寄存器

                       
                               
                                        000

                       
                               
                                        DS:[BX+SI]

                       
                               
                                        001

                       
                               
                                        DS:[BX+DI]

                       
                               
                                        010

                       
                               
                                        SS:[BP+SI]

                       
                               
                                        011

                       
                               
                                        SS:[BP+DI]

                       
                               
                                        100

                       
                               
                                        DS:[SI]

                       
                               
                                        101

                       
                               
                                        DS:[DI]

                       
                               
                                        110

                       
                               
                                        SS:[BP]

                       
                               
                                        111

                       
                               
                                        DS:[BX]

                       
         
        Table 4寄存器字段(rrr)的分配
                               
                                        rrr

                       
                               
                                        w=0

                       
                               
                                        w=1

                       
                               
                                        000

                       
                               
                                        AL

                       
                               
                                        AX

                       
                               
                                        001

                       
                               
                                        CL

                       
                               
                                        CX

                       
                               
                                        010

                       
                               
                                        DL

                       
                               
                                        DX

                       
                               
                                        011

                       
                               
                                        BL

                       
                               
                                        BX

                       
                               
                                        100

                       
                               
                                        AH

                       
                               
                                        SP

                       
                               
                                        101

                       
                               
                                        CH

                       
                               
                                        BP

                       
                               
                                        110

                       
                               
                                        DH

                       
                               
                                        SI

                       
                               
                                        111

                       
                               
                                        BH

                       
                               
                                        DI

                       
         
        Table 5 对段寄存器的寄存器字段(rrr)的分配
                               
                                        rrr

                       
                               
                                        段寄存器

                       
                               
                                        000

                       
                               
                                        ES

                       
                               
                                        001

                       
                               
                                        CS

                       
                               
                                        010

                       
                               
                                        SS

                       
                               
                                        011

                       
                               
                                        DS

                       
         
        对oo字段的解析如下。当oo为00的时,mmm索引存储器有点变化。当mmm为110b时,并不是按照表3来索引SS:[BP],而是DS+16位偏移量,其余情况则是按照表3,偏移量为0。当oo为01时,偏移量为8位有符号数。当oo为10时,偏移量为16位有符号数。根据oo和mmm来计算内存地址的过程如下。
       

        图 37 计算内存地址

         

        rrr字段的实现。当w为1时,rrr对应的是16位寄存器,可直接映射到MIPS寄存器的索引值。当w为0时,rrr对应的是8位寄存器,需要对其重新标号,将AL作为0开始标号。代码实现中,寄存器对应的宏如下,其中包含了段寄存器。
        #define _X86_AL 0
        #define _X86_CL 1
        #define _X86_DL 2
        #define _X86_BL 3
        #define _X86_AH 4
        #define _X86_CH 5
        #define _X86_DH 6
        #define _X86_BH 7
        #define _X86_AX _MIPS_S0 // 0       16
        #define _X86_CX _MIPS_S1 // 1       17
        #define _X86_DX _MIPS_S2 // 2       18
        #define _X86_BX _MIPS_S3 // 3       19
        #define _X86_SP _MIPS_S4 // 4       20
        #define _X86_BP _MIPS_S5 // 5       21
        #define _X86_SI _MIPS_S6 // 6       22
        #define _X86_DI _MIPS_S7 // 7       23
        #define _X86_CS _MIPS_T4 // 8       12
        #define _X86_DS _MIPS_T5 // 9       13
        #define _X86_ES _MIPS_T6 // 10      14
        #define _X86_SS _MIPS_T7 // 11      15
        #define _X86_IP _MIPS_T8 // 12      24
        8086二进制机器码的主要字段已解析,还有个别字段。
        Table 6 其他字段
                               
                                        字段

                       
                               
                                        说明

                       
                               
                                        d

                       
                               
                                        运算方向

                       
                               
                                        w

                       
                               
                                        是否为字

                       
                               
                                        s

                       
                               
                                        是否符号扩展

                       
                               
                                        disp

                       
                               
                                        位移量

                       
                               
                                        data

                       
                               
                                        数据,如立即数

                       
         
        翻译时将二进制机器码拆分成各个字段,然后根据其语义生成对应的MIPS指令。一条8086指令一般要翻译成多条MIPS指令,其中一条为核心指令,其余为辅助指令。如加法指令中,将内容移出至辅助寄存器都是辅助指令,最终的加法是核心指令。一般,核心指令对应MIPS的一条指令,因此,可将其抽象出来,实现函数如下。
        /**
         * x86运算与mips对应关系
         */
        int _keyGenerate(int type, int rt, int rd, int **target) {
            int instruction;
         
            switch (type) {
            case _X86_ADD0:
                instruction = _gen_add(rd, rt, rd);
                break;
            case _X86_AND0:
                instruction = _gen_and(rd, rt, rd);
                break;
            case _X86_OR0:
                instruction = _gen_or(rd, rt, rd);
                break;
            case _X86_SUB0:
                instruction = _gen_sub(rd, rt, rd);
                break;
            case _X86_XOR0:
                instruction = _gen_xor(rd, rt, rd);
                break;
            case _X86_MOV3:
                instruction = _gen_add(rt, _MIPS_ZERO, rd);
                break;
            case _X86_CMP0:
                instruction = _gen_sub(rd, rt, _MIPS_ZERO);
                break;
            default:
                break;
            }
         
            *(*target)++ = instruction;
         
            return instruction;
        }
         
            因为8086混合8位和16位寄存器,所以经常需要将8位寄存器移出至临时寄存器,运算完以后再从临时寄存器移入8086寄存器。这些操作比较频繁,可以将其封装成函数。
        指令的操作码决定了进行何种运算,指令还具有各种操作数。每条指令一般都有多种操作数类型。常见的8086指令类型如下。
       

        图 40 常见8086指令类型

         

        以加法指令add为例。
        add reg, reg类型。根据reg位宽不一样,可分成五类:16位的一种,低8位跟高8位两两组合形成四种。
         
            其余类型都是在reg,reg类型的基础上。遇到reg,mem类型,先计算出内存地址,再取出内存内容将其载入到辅助寄存器,接下来就是reg,reg类型的操作。mem,reg与reg,mem类似,只是运算方向相反,最后要将结果从寄存器写回内存。遇到立即数类型,则是将立即数载入寄存器再做运算。
         
        4.2.11.代码执行及转移分发器
        当一个基本块翻译完以后,经上下文切换后,由MIPS环境转入8086环境,开始执行已翻译的代码块。当执行至代码块末尾时,遇到跳转指令,然后转入转移分发器进行处理。转移分发器计算跳转目标地址(8086),以此地址在跳转缓存中寻找对应的MIPS地址。如果找到了,说明跳转目标是已翻译过的,那么继续跳过去执行。如果没找到,说明目标块还没有翻译,那么先执行翻译过程,再执行翻译块。
         
        4.3.系统库及应用程序详细设计方案
        4.3.1.系统可用资源及其分配
        系统可用内存空间有1MB的空间。内存地址空间的分配如下图:
       

        图 46 内存分配(1M)

         

        系统通过wishbone开源总线,与virtex-5硬件开发板自带的键盘、鼠标和串口等外设互联。键盘的硬件接口是PS/2标准,virtex-5支持16位色液晶显示器,串口用来与其他电脑主机连接,进行通信和数据传输。
         
                               
                                        端口类型

                       
                               
                                        端口地址

                       
                               
                                        rs232数据端口

                       
                               
                                        20000000H

                       
                               
                                        rs232忙位检测

                       
                               
                                        20000004H

                       
                               
                                        vga显示基址

                       
                               
                                        50000000H

                       
                               
                                        led

                       
                               
                                        70000000H

                       
                               
                                        键盘

                       
                               
                                        90000000H

                       
        表 1 系统常用端口及其地址

         
           
  •                 系统库函数设计与实现
        系统最初只支持MIPS汇编语言。用MIPS汇编语言开发出俄罗斯方块游戏。这个应用程序,不仅帮助找出了硬件系统存在的一些bug,如流水线CPU中存在的数据相关和控制相关问题,指令集的功能问题,而且还验证了硬件系统与RS232串口之间的数据传输功能的正确性,以及键盘接口和显示器接口的正确性和易用性。
         
           
  •                 系统宏定义和端口常量
        系统的内存和外设的地址空间是连续分配的。为了提高开发应用程序的过程中的设备无关性和应用程序的通用性,我们把各个端口定义为常量,供系统库和应用程序参考使用。
         
                                                                        unsigned int* text_base     = (int *)0x000C0000;
                                                                        unsigned int* graphics_base  = (int *)0x50000000;   //VGA 端口
                                                                        unsigned int* ps2_base      = (int *)0x90000000;   //ps2键盘 端口
                                                                        unsigned int* rs232_base    = (int *)0xFFFFE000;
                                                                        unsigned int* rs232_busy    = (int *)0xFFFFF000;
                                                                        unsigned int* font_base     = (int *)0x000C12C0;  //字库 端口
                       
<div style="clear:both;">                  
           
  •                 主要模块:
           
  •                 start函数
        参数:无
        功能:游戏的主体框架,作用类似main函数
        说明:内部调用Tetris、tetris_clr、wait_key、start_enter等函数,构成整个游戏的主体框架。
           
  •                 Tetris函数
        参数:无
        功能:游戏的逻辑控制主体
        说明:调用各种初始化函数、俄罗斯方块的生成函数和运动函数、玩家键盘控制函数、各种信息显示函数,实现游戏的所有逻辑。
           
  •                 ini_boarder函数
        参数:无
        功能:画俄罗斯方块运行的左右边界
        说明:初始化游戏时使用
           
  •                 set_bottom函数
        参数:无
        功能:画俄罗斯方块运行的下边界
        说明:初始化游戏时使用
           
  •                 info函数类
        函数类:info_help、info_next、info_fail
        参数:无
        功能:显示各种信息
        说明:在初始化游戏界面时调用info_help显示帮助信息;调用info_next在屏幕左上角显示下一个即将下落的方块的形状和颜色,当游戏失败时调用info_fail提醒玩家。
           
  •                 check_key函数
        参数:输出参数$v0和$v1
        功能:检查是否有键按下
        说明:键盘无键按下时$v0=0;否则$v0=1,$v1=键值
           
  •                 chk_dlt函数
        参数:输出参数$v0
        功能:检查并删除满行
        说明:检测每一行是否已摆满方块,如果摆满则将其删除。返回值$v0为删除的满行数。
           
  •                 detect_collision函数
        参数:输出参数$v0
        功能:检查是否有方块冲突
        说明:如果有冲突则返回$v0=2
           
  •                 null_loop函数
        参数:输入参数$a0
        功能:空循环,用于延时
        说明:$a0=0,表示长延时;$a0=1表示短延时
           
  •                 get_next_seq函数
        参数:输入参数$a3、输出参数$v0
        功能:根据当前方块的形状,得到下一个方块的形状
        说明:输入$a3当前形状的序列号,返回$v0下一个方块的形状的序列号
           
  •                 draw_pic函数类
        函数类:pre_draw_pic类、un_pre_draw_pic类、draw_pic类
        参数:输入参数$a0和$a2
        功能:根据方块现在的位置和接下来的位置来画方块
        说明:输入参数$a0是当前位置,$a2是方块将要移动的位置
        4.3.2.2.基于C语言的吃豆子游戏设计

           
  •                 开发环境:自己编写的硬件系统模拟器SimulatorForV5.jar,自己编写的内存填充工具MemoryFilling.jar,自己编写的硬件内存分配文件memory.bin,Gcc编译环境,严格的简单的C语言程序源文件user.c,安装jre或jdk并配置好环境变量的windows XP及以上的操作系统。       
  •                 程序流程:
       

        图 48吃豆子程序流程图

         

           
  •                 主要模块:
           
  •                 start_game函数
        原型:void start_game();
        功能:游戏程序的逻辑主函数
        说明:控制packman的运动,两个怪物的简单智能移动,检查游戏的终止条件,判断键盘输入并做相应的响应
         
           
  •                 init_game函数
        原型:void init_game();
        功能:初始化游戏的全局变量
        说明:每次游戏重新开始时,需要调用该函数进行全局变量的初始化
           
  •                 check_catch函数
        原型:int check_catch()
        功能:检测packman是否被怪物抓到
        说明:如果被抓到返回1,否则返回0
         
           
  •                 check_collision函数
        原型:int check_collision()
        功能:检测两个怪物是否有冲突
        说明:如果有则返回1,否则返回0
         
           
  •                 clear_screen函数
        原型:void clear_screen(int color)
        功能:用指定颜色清屏
        说明:color为指定的颜色值,可以用宏定义的颜色值或者相应的16位数值
         
           
  •                 draw函数类
        原型:void draw_map();void draw_info();void draw_ball(int x, int y);void draw_pea(int x, int y)
        功能:在指定坐标位置画形状
        说明:(x,y)为形状的左上角定点的坐标
         
           
  •                 display函数类
        原型:int display_win();int display_lose();void display_bye()
        功能:全屏显示提示信息
        说明:分别提示玩家游戏成功、游戏失败、感谢信息
           
  •                 get_direction函数类
        原型:int get_direction_1st();int get_direction_2nd()
        功能:随机得到怪物的下一次移动的方向
        说明:根据两个怪物位置的全局变量,简单的智能算法,计算两个怪物下一次移动的方向
           
  •                 move函数类
        原型:void move_monster_1st();void move_monster_2nd ()
        功能:移动两个怪物
        说明:根据两个怪物的全局变量,计算后分别移动
           
  •                 null_loop函数
        原型:void null_loop(int length)
        功能:空循环,用于延时
        说明:length参数为0时是长延时,为1时是短延时
           
  •                 save_lattice函数
        原型:void save_lattice(int x, int y, int * array)
        功能:把指定位置方格的像素值保存在数组中
        说明:(x,y)为指定方格的坐标,array为全局数组变量
           
  •                 resume_lattice函数
        原型:void resume_lattice(int x, int y, int * array)
        功能:把方格像素存储到指定的坐标
        说明:array为保存像素值的全局变量,(x,y)为要存放颜色的方格的坐标
         
        4.3.2.3.基于x86汇编语言的推箱子游戏设计

           
  •                 开发环境: x86模拟器emu8086.exe,DOS模拟器DosBox.exe,x86汇编器NASM,windows XP及以上的操作系统。       
  •                 程序流程
       

        图 49 推箱子程序流程图

         
           
  •                 主要模块:
           
  •                 init_game函数
        参数:无
        功能:游戏初始化
        说明:初始化游戏的界面和各个变量,每次重新进入游戏时调用该函数
           
  •                 move_man函数
        参数:输入参数ax,bx
        功能:在指定的坐标处画man
        说明:ax为横坐标,bx为纵坐标
           
  •                 move_box函数
        参数:输入参数ax,bx
        功能:在指定的坐标处画box
        说明:ax为横坐标,bx为纵坐标
           
  •                 check_win函数
        参数:输出参数dx
        功能:检查箱子是否都放到指定位置
        说明:如果箱子已经摆放好返回dx=1,否则返回dx=0
           
  •                 check_collision函数
        参数:输出参数dx
        功能:检查man和箱子是否能够移动
        说明:如果有冲突返回dx=1,否则返回dx=0
           
  •                 check_key函数
        参数:输出参数ax,bx
        功能:检查是否有键按下
        说明:如果有键按下,ax=1,bx=键值;否则ax=0,bx无意义
           
  •                 get_key函数
        参数:输出参数dx
        功能:等待键盘输入
        说明:键盘被按下后,返回键值dx=键值
           
  •                 rectangle函数
        参数:输入参数arg0,arg1,arg2,arg3,arg4
        功能:画带颜色填充的长方形
        说明:参数通过堆栈传递,arg0=left,arg1=top,arg2=right,arg3=bottom,arg4=color,颜色采用R5G5B6的16位色
           
  •                 xy2array函数
        参数:输入参数ax,bx,输出参数dx
        功能:将游戏中的x和y坐标对应到一位数组中
        说明:ax为横坐标,bx为纵坐标,返回dx
           
  •                 draw_box函数
        参数:输入参数ax,bx,cx
        功能:在指定坐标处画box并用指定颜色填充
        说明:ax为横坐标,bx为纵坐标,cx指定颜色
         
        5.参考文献

           
  •                 Matthew Chapman, Daniel J. Magenheimer, and Parthasarathy Ranganathan. MagiXen: Combining binary translation and virtualization. Technical Report HPL-2007- 77, Hewlett-Packard Laboratories, 2007.       
  •                 D.Wentzlaff and A. Agarwal. Constructing virtual architectures on a tiled processor. In Proc. 2006 International Symposium on Code Generation and Optimization, 2006.       
  •                 E. R. Altman, K. Ebcioglu, M. Gschwind, and S. Sathaye. Advances and future challenges in binary translation and optimization. IEEE Proceedings, 89(11):1710&ndash;1722, Nov. 2001.       
  •                 H.-S. Kim and J. E. Smith, “Hardware support for control transfers in code caches,” in MICRO 36: Proceedings of the 36th annual IEEE/ACM International Symposium on Microarchitecture. Washington, DC, USA: IEEE Computer Society, 2003, p. 253.       
  •                 M. Bolado, H. Posadas, J. Castillo, P. Huerta, P. S?anchez, C. S?anchez, H. Fouren, and F. Blasco, “Platform based on open-source cores for industrial applications,” in Proc. DATE, 2004, p. 21014.       
  •                 Barry B. Brey, “INTEL Microprocessors 8086/8088, 80186/80188, 80286, 80386, 80486, Pentium, Pentium ProProcessor, Pentium II, III, 4” , Seventh Edition, Pearson Prentice Hall, 2005.
         
        6.附录

        本节将列出项目的关键代码,包括Verilog HDL硬件部分代码以及汇编或C软件部分代码。
        6.1动态翻译跳转地址查表jlut_top.v
        `timescale 1ns / 1ps
        //////////////////////////////////////////////////////////////////////////////////
        // Company:
        // Engineer:
        //
        // Create Date:    11:53:30 03/05/2012
        // Design Name:
        // Module Name:    jlut_top
        // Project Name:
        // Target Devices:
        // Tool versions:
        // Description:
        //
        // Dependencies:
        //
        // Revision:
        // Revision 0.01 - File Created
        // Additional Comments:
        //
        //////////////////////////////////////////////////////////////////////////////////
        module jlut_top(
           clk, rst,
            index_rd, ram_rd,
            cam_wr, ram_wr,
            dina, dinb, dout
            );
          
            //
            // parameters
            //
          
            parameter cam_awidth = 6;
            parameter cam_dwidth = 32;
            parameter ram_awidth = 6;
            parameter ram_dwidth = 32; // {valid, tpc}
          
            //
            // inputs & outputs
            //
          
            // clk & rst
            input clk;
            input rst;
          
            input index_rd;
            input ram_rd;
            input cam_wr;
            input ram_wr;
            input  [31:0] dina;
            input  [31:0] dinb;
            output [31:0] dout;
            reg    [31:0] dout;
          
            //
            // variable declarations
            //
          
            wire        cam_busy;
            wire       cam_match;
            wire [cam_awidth-1:0] cam_wr_addr;
            wire [cam_awidth-1:0] cam_match_addr;
            wire [cam_dwidth-1:0] cam_din;
            reg     cam_we_r;
            wire     cam_we_rising;
          
            wire [ram_awidth-1:0] ram_addr;
            wire [ram_dwidth-1:0] ram_din;
            wire [ram_dwidth-1:0] ram_dout;
            reg     ram_we_r;
            wire     ram_we_rising;
          
            //
            // module body
            //
          
            assign cam_din = dina;
            assign cam_wr_addr = dinb;
          
            assign ram_din = dina;
            assign ram_addr = ram_rd? dina : dinb;
          
            assign cam_we_rising = ~cam_we_r & cam_wr;
            assign ram_we_rising = ~ram_we_r & ram_wr;
          
            always @(posedge clk or posedge rst)
                if(rst) begin
                    cam_we_r <= #1 1'h0;
                    ram_we_r <= #1 1'h0;
                end else begin
                    cam_we_r <= #1 cam_wr;
                    ram_we_r <= #1 ram_wr;
                end
          
               
            always @(*)
                if(index_rd)
                    if(cam_match) // hit
                        dout = cam_match_addr;
                    else // miss
                        dout = {1'h1, 31'h0};
                else if(ram_rd)
                    dout = ram_dout[31:0];
                else
                    dout = 32'h0;
          
            spc_cam spc_cam(
                .clk(clk),
                .din(cam_din),
                .we(cam_we_rising),
                .wr_addr(cam_wr_addr),
                .busy(cam_busy),
                .match(cam_match),
                .match_addr(cam_match_addr)
                );
          
            tpc_ram tpc_ram(
                .a(ram_addr),
                .d(ram_din),
                .clk(clk),
                .we(ram_we_rising),
                .spo(ram_dout)
                );
         
        endmodule
        /*******************************************************************************
        *     (c) Copyright 1995 - 2010 Xilinx, Inc. All rights reserved.              *
        *                                                                              *
        *     This file contains confidential and proprietary information              *
        *     of Xilinx, Inc. and is protected under U.S. and                          *
        *     international copyright and other intellectual property                  *
        *     laws.                                                                    *
        *                                                                              *
        *     DISCLAIMER                                                               *
        *     This disclaimer is not a license and does not grant any                  *
        *     rights to the materials distributed herewith. Except as                  *
        *     otherwise provided in a valid license issued to you by                   *
        *     Xilinx, and to the maximum extent permitted by applicable                *
        *     law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND                  *
        *     WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES              *
        *     AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING                *
        *     BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-                   *
        *     INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and                 *
        *     (2) Xilinx shall not be liable (whether in contract or tort,             *
        *     including negligence, or under any other theory of                       *
        *     liability) for any loss or damage of any kind or nature                  *
        *     related to, arising under or in connection with these                    *
        *     materials, including for any direct, or any indirect,                    *
        *     special, incidental, or consequential loss or damage                     *
        *     (including loss of data, profits, goodwill, or any type of               *
        *     loss or damage suffered as a result of any action brought                *
        *     by a third party) even if such damage or loss was                        *
        *     reasonably foreseeable or Xilinx had been advised of the                 *
        *     possibility of the same.                                                 *
        *                                                                              *
        *     CRITICAL APPLICATIONS                                                    *
        *     Xilinx products are not designed or intended to be fail-                 *
        *     safe, or for use in any application requiring fail-safe                  *
        *     performance, such as life-support or safety devices or                   *
        *     systems, Class III medical devices, nuclear facilities,                  *
        *     applications related to the deployment of airbags, or any                *
        *     other applications that could lead to death, personal                    *
        *     injury, or severe property or environmental damage                       *
        *     (individually and collectively, "Critical                                *
        *     Applications"). Customer assumes the sole risk and                       *
        *     liability of any use of Xilinx products in Critical                      *
        *     Applications, subject only to applicable laws and                        *
        *     regulations governing limitations on product liability.                  *
        *                                                                              *
        *     THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS                 *
        *     PART OF THIS FILE AT ALL TIMES.                                          *
        *******************************************************************************/
        // The synthesis directives "translate_off/translate_on" specified below are
        // supported by Xilinx, Mentor Graphics and Synplicity synthesis
        // tools. Ensure they are correct for your synthesis tool(s).
         
        // You must compile the wrapper file spc_cam.v when simulating
        // the core, spc_cam. When compiling the wrapper file, be sure to
        // reference the XilinxCoreLib Verilog simulation library. For detailed
        // instructions, please refer to the "CORE Generator Help".
         
        `timescale 1ns/1ps
         
        module spc_cam(
            clk,
            din,
            we,
            wr_addr,
            busy,
            match,
            match_addr);
         
         
        input clk;
        input [31 : 0] din;
        input we;
        input [5 : 0] wr_addr;
        output busy;
        output match;
        output [5 : 0] match_addr;
         
        // synthesis translate_off
         
              CAM_V6_1 #(
                .c_addr_type(0),
                .c_cmp_data_mask_width(32),
                .c_cmp_din_width(32),
                .c_data_mask_width(32),
                .c_depth(64),
                .c_din_width(32),
                .c_has_cmp_data_mask(0),
                .c_has_cmp_din(0),
                .c_has_data_mask(0),
                .c_has_en(0),
                .c_has_multiple_match(0),
                .c_has_read_warning(0),
                .c_has_single_match(0),
                .c_has_we(1),
                .c_has_wr_addr(1),
                .c_match_addr_width(6),
                .c_match_resolution_type(0),
                .c_mem_init(0),
                .c_mem_init_file("no_coe_file_loaded"),
                .c_mem_type(1),
                .c_read_cycles(1),
                .c_reg_outputs(0),
                .c_ternary_mode(0),
                .c_width(32),
                .c_wr_addr_width(6))
            inst (
                .CLK(clk),
                .DIN(din),
                .WE(we),
                .WR_ADDR(wr_addr),
                .BUSY(busy),
                .MATCH(match),
                .MATCH_ADDR(match_addr),
                .CMP_DATA_MASK(),
                .CMP_DIN(),
                .DATA_MASK(),
                .EN(),
                .MULTIPLE_MATCH(),
                .READ_WARNING(),
                .SINGLE_MATCH());
         
         
        // synthesis translate_on
         
        // XST black box declaration
        // box_type "black_box"
        // synthesis attribute box_type of spc_cam is "black_box"
         
        endmodule
        /*******************************************************************************
        *     (c) Copyright 1995 - 2010 Xilinx, Inc. All rights reserved.              *
        *                                                                              *
        *     This file contains confidential and proprietary information              *
        *     of Xilinx, Inc. and is protected under U.S. and                          *
        *     international copyright and other intellectual property                  *
        *     laws.                                                                    *
        *                                                                              *
        *     DISCLAIMER                                                               *
        *     This disclaimer is not a license and does not grant any                  *
        *     rights to the materials distributed herewith. Except as                  *
        *     otherwise provided in a valid license issued to you by                   *
        *     Xilinx, and to the maximum extent permitted by applicable                *
        *     law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND                  *
        *     WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES              *
        *     AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING                *
        *     BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-                   *
        *     INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and                 *
        *     (2) Xilinx shall not be liable (whether in contract or tort,             *
        *     including negligence, or under any other theory of                       *
        *     liability) for any loss or damage of any kind or nature                  *
        *     related to, arising under or in connection with these                    *
        *     materials, including for any direct, or any indirect,                    *
        *     special, incidental, or consequential loss or damage                     *
        *     (including loss of data, profits, goodwill, or any type of               *
        *     loss or damage suffered as a result of any action brought                *
        *     by a third party) even if such damage or loss was                        *
        *     reasonably foreseeable or Xilinx had been advised of the                 *
        *     possibility of the same.                                                 *
        *                                                                              *
        *     CRITICAL APPLICATIONS                                                    *
        *     Xilinx products are not designed or intended to be fail-                 *
        *     safe, or for use in any application requiring fail-safe                  *
        *     performance, such as life-support or safety devices or                   *
        *     systems, Class III medical devices, nuclear facilities,                  *
        *     applications related to the deployment of airbags, or any                *
        *     other applications that could lead to death, personal                    *
        *     injury, or severe property or environmental damage                       *
        *     (individually and collectively, "Critical                                *
        *     Applications"). Customer assumes the sole risk and                       *
        *     liability of any use of Xilinx products in Critical                      *
        *     Applications, subject only to applicable laws and                        *
        *     regulations governing limitations on product liability.                  *
        *                                                                              *
        *     THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS                 *
        *     PART OF THIS FILE AT ALL TIMES.                                          *
        *******************************************************************************/
        // The synthesis directives "translate_off/translate_on" specified below are
        // supported by Xilinx, Mentor Graphics and Synplicity synthesis
        // tools. Ensure they are correct for your synthesis tool(s).
         
        // You must compile the wrapper file tpc_ram.v when simulating
        // the core, tpc_ram. When compiling the wrapper file, be sure to
        // reference the XilinxCoreLib Verilog simulation library. For detailed
        // instructions, please refer to the "CORE Generator Help".
         
        `timescale 1ns/1ps
         
        module tpc_ram(
            a,
            d,
            clk,
            we,
            spo);
         
         
        input [5 : 0] a;
        input [31 : 0] d;
        input clk;
        input we;
        output [31 : 0] spo;
         
        // synthesis translate_off
         
              DIST_MEM_GEN_V5_1 #(
                .C_ADDR_WIDTH(6),
                .C_DEFAULT_DATA("0"),
                .C_DEPTH(64),
                .C_FAMILY("virtex5"),
                .C_HAS_CLK(1),
                .C_HAS_D(1),
                .C_HAS_DPO(0),
                .C_HAS_DPRA(0),
                .C_HAS_I_CE(0),
                .C_HAS_QDPO(0),
                .C_HAS_QDPO_CE(0),
                .C_HAS_QDPO_CLK(0),
                .C_HAS_QDPO_RST(0),
                .C_HAS_QDPO_SRST(0),
                .C_HAS_QSPO(0),
                .C_HAS_QSPO_CE(0),
                .C_HAS_QSPO_RST(0),
                .C_HAS_QSPO_SRST(0),
                .C_HAS_SPO(1),
                .C_HAS_SPRA(0),
                .C_HAS_WE(1),
                .C_MEM_INIT_FILE("no_coe_file_loaded"),
                .C_MEM_TYPE(1),
                .C_PARSER_TYPE(1),
                .C_PIPELINE_STAGES(0),
                .C_QCE_JOINED(0),
                .C_QUALIFY_WE(0),
                .C_READ_MIF(0),
                .C_REG_A_D_INPUTS(0),
                .C_REG_DPRA_INPUT(0),
                .C_SYNC_ENABLE(1),
                .C_WIDTH(32))
            inst (
                .A(a),
                .D(d),
                .CLK(clk),
                .WE(we),
                .SPO(spo),
                .DPRA(),
                .SPRA(),
                .I_CE(),
                .QSPO_CE(),
                .QDPO_CE(),
                .QDPO_CLK(),
                .QSPO_RST(),
                .QDPO_RST(),
                .QSPO_SRST(),
                .QDPO_SRST(),
                .DPO(),
                .QSPO(),
                .QDPO());
         
         
        // synthesis translate_on
         
        // XST black box declaration
        // box_type "black_box"
        // synthesis attribute box_type of tpc_ram is "black_box"
         
        endmodule
         
        6.2QS-I CPU top模块qs1_pipelined_cpu.v

                `timescale 1ns / 1ps
        //////////////////////////////////////////////////////////////////////////////////
        // Company: ZJU
        // Engineer: Yao Yuan
        //
        // Create Date:    20:32:21 03/11/2011
        // Design Name:
        // Module Name:    top
        // Project Name:
        // Target Devices:
        // Tool versions:
        // Description:
        //
        // Dependencies:
        //
        // Revision:
        // Revision 0.01 - File Created
        // Additional Comments:
        //
        //////////////////////////////////////////////////////////////////////////////////
        `include "QS1_defines.v"
         
        module pipelined_cpu(
            // clk & rst
            clk, rst,
          
            // ibus signals
            ibus_data_o, ibus_data_i, ibus_addr_o,
            ibus_sel_o, ibus_we_o, ibus_cyc_o, ibus_stb_o,
            ibus_ack_i, ibus_err_i, ibus_rty_i,
            ibus_tag_i, ibus_tag_o,
          
            // dbus signals
            dbus_data_o, dbus_data_i, dbus_addr_o,
            dbus_sel_o, dbus_we_o, dbus_cyc_o, dbus_stb_o,
            dbus_ack_i, dbus_err_i, dbus_rty_i,
            dbus_tag_i, dbus_tag_o,
          
            // JLUT port signals
            campi, ramri, camwi, ramwi, jlut_douta, jlut_doutb, jlut_din,
            int_pending
            );
          
            //
            // parameters
            //
          
            parameter       dw  = 32;      // Data bus Width
            parameter       aw  = 32;      // Address bus Width
            parameter       sw = 4;
          
            //
            // inputs & outputs
            //
          
            // clk & rst
            input       clk;
            input       rst;
               
            // ibus master
            output [dw-1:0] ibus_data_o;
            input   [dw-1:0] ibus_data_i;
            output [aw-1:0] ibus_addr_o;
            output [sw-1:0] ibus_sel_o;
            output          ibus_we_o;
            output          ibus_cyc_o;
            output          ibus_stb_o;
            input             ibus_ack_i;
            input           ibus_err_i;
            input           ibus_rty_i;
            input  [   3:0] ibus_tag_i;
            output [   3:0] ibus_tag_o;
         
            // dbus master
            output [dw-1:0] dbus_data_o;
            input   [dw-1:0] dbus_data_i;
            output [aw-1:0] dbus_addr_o;
            output [sw-1:0] dbus_sel_o;
            output          dbus_we_o;
            output          dbus_cyc_o;
            output          dbus_stb_o;
            input             dbus_ack_i;
            input           dbus_err_i;
            input           dbus_rty_i;
            input  [   3:0] dbus_tag_i;
            output [   3:0] dbus_tag_o;
          
            // Jump Lookup Table (TLUT) port signals
            output          campi;
            output          ramri;
           output         camwi;
           output          ramwi;
            output [  31:0] jlut_douta;
            output [  31:0] jlut_doutb;
            input  [  31:0] jlut_din;
          
            // External interrupt signals
            input  [  19:0] int_pending;
         
            //
            // variable declarations
            //
          
            wire wpcir;
            wire iwait;
            wire dwait;
            wire cpu_wait_n;
            wire dwreg;
            wire ewreg;
            wire mwreg;
            wire wwreg;
            wire dwmem;
            wire ewmem;
            wire mwmem;
            wire dm2reg;
            wire em2reg;
            wire mm2reg;
            wire wm2reg;
            wire daluimm;
            wire ealuimm;
            wire dshift;
            wire eshift;
            wire djal;
            wire ejal;
            wire [1:0] pcsource;
            wire [3:0] daluc;
            wire [3:0] ealuc;
            wire [4:0] drn;
            wire [4:0] ern0;
            wire [4:0] ern;
            wire [4:0] mrn;
            wire [4:0] wrn;
            wire [31:0] pc;
            wire [31:0] bpc;
            wire [31:0] jpc;
            wire [31:0] npc;
            wire [31:0] pc4;
            wire [31:0] ins;
            wire [31:0] inst;
            wire [31:0] dpc4;
            wire [31:0] da;
            wire [31:0] db;
            wire [31:0] dimm;
            wire [31:0] ea;
            wire [31:0] eb;
            wire [31:0] eimm;
            wire [31:0] ealu;
            wire [31:0] walu;
            wire [31:0] malu;
            wire [31:0] epc4;
            wire [31:0] mb;
            wire [31:0] mmo;
            wire [31:0] wmo;
            wire [31:0] wdi;
         
            wire id_isbr;
            wire sig_int;
            wire sig_syscall;
            wire [`QS1_MEMOP_WIDTH-1:0] dmemop;
            wire [`QS1_MEMOP_WIDTH-1:0] ememop;
            wire [`QS1_MEMOP_WIDTH-1:0] mmemop;
            wire except_start;
            wire [ 4:0] exc_code;
            wire [31:0] to_epc;
            wire dmfc0;
            wire emfc0;
            wire [31:0] dcop0_o;
            wire [31:0] ecop0_o;
            wire abort_id;
            wire abort_mem;
            wire abort_wb;
        `ifdef BT_ENABLE
            wire [ 3:0] x86_flag;
        `endif
            wire emf_jlut;
            wire [31:0] ejlut_din;
          
            //
            // module body
            //
          
            assign cpu_wait_n = !(iwait | dwait);
          
            // JLUT port assignments
            assign jlut_douta = da;
            assign jlut_doutb = db;
          
            pc_reg       pc_reg(
                // clk & rst
                .clk(clk),
                .rst(rst),
                   
                // internal signals
                .wpcir(wpcir),
                .cpu_wait_n(cpu_wait_n),
                .npc(npc),
                .pc(pc)
                );
         
            if_stage     if_stage(
                // clk & rst
                .clk(clk),
                .rst(rst),
               
                // ibus signals
                .ibus_data_i(ibus_data_i),
                .ibus_addr_o(ibus_addr_o),
                .ibus_sel_o(ibus_sel_o),
                .ibus_we_o(ibus_we_o),
                .ibus_cyc_o(ibus_cyc_o),
                .ibus_stb_o(ibus_stb_o),
                .ibus_ack_i(ibus_ack_i),
                .ibus_err_i(ibus_err_i),
                .ibus_rty_i(ibus_rty_i),
                .ibus_tag_o(ibus_tag_o),
               
                // internal signals
                .wpcir(wpcir),
                .cpu_wait_n(cpu_wait_n),
                .iwait(iwait),
                .pcsource(pcsource),
                .pc(pc),
                .bpc(bpc),
                .da(da),
                .jpc(jpc),
                .npc(npc),
                .pc4(pc4),
                .ins(ins),
                .except_start(except_start)
                );
         
            if_id_reg    if_id_reg(
                // clk & rst
                .clk(clk),
                .rst(rst),
               
                .pc4(pc4),
                .ins(ins),
                .wpcir(wpcir),
                .dpc4(dpc4),
                .inst(inst),
                .cpu_wait_n(cpu_wait_n)
                );
         
            id_stage     id_stage(
                // clk & rst
                .clk(clk),
                .rst(rst),
               
                // internal signals
                .mwreg(mwreg),
                .ewreg(ewreg),
                .wwreg(wwreg),
                .em2reg(em2reg),
                .mm2reg(mm2reg),
                .mrn(mrn),
                .ern(ern),
                .wrn(wrn),
                .inst(inst),
                .wdi(wdi),
                .ealu(ealu),
                .malu(malu),
                .mmo(mmo),
                .dpc4(dpc4),
                .wpcir(wpcir),
                .dwreg(dwreg),
                .dm2reg(dm2reg),
                .dwmem(dwmem),
                .daluimm(daluimm),
                .dshift(dshift),
                .djal(djal),
                .djalr(djalr),
                .pcsource(pcsource),
                .daluc(daluc),
                .drn(drn),
                .bpc(bpc),
                .jpc(jpc),
                .da(da),
                .db(db),
                .dimm(dimm),
                .mfc0(dmfc0),
                .isbr(id_isbr),
                .sig_int(sig_int),
                .sig_syscall(sig_syscall),
                .dmemop(dmemop),
                .except_start(except_start),
                .to_epc(to_epc),
                .exc_code(exc_code),
                .cop0_o(dcop0_o),
                .abort_id(abort_id),
                .abort_wb(abort_wb),
        `ifdef BT_ENABLE
                .x86_flag(x86_flag),
        `endif
                .campi(campi),
                .ramri(ramri),
                .camwi(camwi),
                .ramwi(ramwi),
                .jlut_din(jlut_din),
                // external interrupt signals
                .int_pending(int_pending)
                );
               
            id_exe_reg   id_exe_reg(
                // clk & rst
                .clk(clk),
                .rst(rst),
               
                .dwreg(dwreg),
                .dm2reg(dm2reg),
                .dwmem(dwmem),
                .daluimm(daluimm),
                .dshift(dshift),
                .djal(djal),
                .djalr(djalr),
                .cpu_wait_n(cpu_wait_n),
                .daluc(daluc),
                .drn(drn),
                .da(da),
                .db(db),
                .dimm(dimm),
                .dpc4(dpc4),
                .dcop0_o(dcop0_o),
                .dmfc0(dmfc0),
                .dmemop(dmemop),
                .dmf_jlut(campi | ramri),
                .djlut_din(jlut_din),
               
                .ewreg(ewreg),
                .em2reg(em2reg),
                .ewmem(ewmem),
                .ealuimm(ealuimm),
                .eshift(eshift),
                .ejal(ejal),
                .ejalr(ejalr),
                .ealuc(ealuc),
                .ern0(ern0),
                .ea(ea),
                .eb(eb),
                .eimm(eimm),
                .epc4(epc4),
                .ecop0_o(ecop0_o),
                .emfc0(emfc0),
                .ememop(ememop),
                .emf_jlut(emf_jlut),
                .ejlut_din(ejlut_din)
                );
         
            exe_stage    exe_stage(
                // clk & rst
                .clk(clk),
                .rst(rst),
               
                .ejal(ejal),
                .ejalr(ejalr),
                .ealuimm(ealuimm),
                .eshift(eshift),
                .ealuc(ealuc),
                .ern0(ern0),
                .epc4(epc4),
                .ea(ea),
                .eimm(eimm),
                .eb(eb),
        `ifdef BT_ENABLE
                .x86_flag(x86_flag),
        `endif
                .ern(ern),
                .ealu(ealu),
                .ecop0_o(ecop0_o),
                .emfc0(emfc0),
                .ejlut_din(ejlut_din),
                .emf_jlut(emf_jlut)
                );
         
            exe_mem_reg  exe_mem_reg(
                // clk & rst
                .clk(clk),
                .rst(rst),
               
                .ewreg(ewreg),
                .em2reg(em2reg),
                .ewmem(ewmem),
                .cpu_wait_n(cpu_wait_n),
                .ern(ern),
                .ealu(ealu),
                .eb(eb),
                .ememop(ememop),
               
                .mwreg(mwreg),
                .mm2reg(mm2reg),
                .mwmem(mwmem),
                .mrn(mrn),
                .malu(malu),
                .mb(mb),
                .mmemop(mmemop)
                );
         
            mem_stage    mem_stage(
                // clk & rst
                .clk(clk),
                .rst(rst),
               
                // dbus signals
                .dbus_data_o(dbus_data_o),
                .dbus_data_i(dbus_data_i),
                .dbus_addr_o(dbus_addr_o),
                .dbus_sel_o(dbus_sel_o),
                .dbus_we_o(dbus_we_o),
                .dbus_cyc_o(dbus_cyc_o),
                .dbus_stb_o(dbus_stb_o),
                .dbus_ack_i(dbus_ack_i),
                .dbus_err_i(dbus_err_i),
                .dbus_rty_i(dbus_rty_i),
                .dbus_tag_i(dbus_tag_i),
                .dbus_tag_o(dbus_tag_o),
               
                // internal signals
                .mwmem(mwmem),
                .mm2reg(mm2reg),
                .dwait(dwait),
                .malu(malu),  // memory address to bus
                .mb(mb),      // memory write data to bus
                .mmo(mmo),
                .wpcir(wpcir),
                .cpu_wait_n(cpu_wait_n),
                .abort_mem(abort_mem),
                .memop(mmemop)
                );
         
            mem_wb_reg  mem_wb_reg(
                // clk & rst
                .clk(clk),
                .rst(rst),
               
                // internal signals
                .mwreg(mwreg),
                .mm2reg(mm2reg),
                .cpu_wait_n(cpu_wait_n),
<p>                   
        6.4软件部分翻译模块bt_decode.c

        /** not finished
         * 解析总入口
         * @param x86Source - x86源程序地址
         * @param mipsTarget - 生成mips程序地址
         * @return
         */
        int _decodeMain(char **x86Source, int **mipsTarget) {
            int i;
         
            int opcode;
            int direction;
            int isWord;
         
            int opType;
         
            int oo;
            int rrr;
            int mmm;
            int disp;
            int data;
            int isData;
            int imm;
            int isImm;
            int isMem;
            char c;
            int x86Reg1;
            int x86Reg2;
            int shiftAmout;
            int type = 0;
            int flag = 1; // 结束标志,int 4ch
         
            int rt;
            int rd;
            int addr;
            int addrH;
            int addrL;
         
         
            _push(_MIPS_RA, mipsTarget);
         
            while (1) {
                c = *(*x86Source)++;
                if (c == 0xcd) { // int
                    flag = 0;
                    c = *(*x86Source)++;
                    *(*mipsTarget)++ = _gen_ori(_MIPS_ZERO, _MIPS_A0, c);
                    addr = (int) _intrEntr;
                    printf("intr: %d\n", addr);
                    addrH = (addr >> 16) & 0xffff;
                    addrL = (addr >> 0) & 0xffff;
                    *(*mipsTarget)++ = _gen_lui(_MIPS_V0, addrH);
                    *(*mipsTarget)++ = _gen_ori(_MIPS_V0, _MIPS_V0, addrL);
                    *(*mipsTarget)++ = _gen_jalr(_MIPS_V0, _MIPS_RA);
                    *(*mipsTarget)++ = MIPS_NOP;
         
                    if (c == 0x22) {
                        _pop(_MIPS_RA, mipsTarget);
                        *(*mipsTarget)++ = _gen_jr(_MIPS_RA);
                        *(*mipsTarget)++ = MIPS_NOP;
                        break;
                    } else {
                        continue;
                    }
                }
         
                opcode = (c >> 2) & 0x3f;
                direction = (c >> 1) & 0x1;
                isWord = (c >> 0) & 0x1;
         
                opType = opcode;
                disp = 0;
         
                if (((c >> 6) & 0x3) == 0x0) {
                    if ((c & 0x7) == 0x6) { // 00sss110, push seg
                        rrr = (c >> 3) & 0x7;
                        x86Reg1 = _getX86RegSeg(rrr);
                        _push(x86Reg1, mipsTarget);
                    } else if ((c & 0x7) == 0x7) { // 00sss111, pop seg
                        rrr = (c >> 3) & 0x7;
                        x86Reg1 = _getX86RegSeg(rrr);
                        _pop(x86Reg1, mipsTarget);
                    }
                } else if (((c >> 3) & 0x1f) == _X86_PUSH0_0) { // push reg
                    rrr = c & 0x7;
                    x86Reg1 = _getX86Reg(rrr, 1);
                    _push(x86Reg1, mipsTarget);
                } else if (((c >> 3) & 0x1f) == _X86_POP0_0) {
                    rrr = c & 0x7;
                    x86Reg1 = _getX86Reg(rrr, 1);
                    _pop(x86Reg1, mipsTarget);
                } else if (((c >> 1) & 0x7f) == 0x63) {
                    c = *(*x86Source)++;
                    oo = (c >> 6) & 0x3;
                    rrr = (c >> 3) & 0x7;
                    mmm = (c >> 0) & 0x7;
         
                    if (rrr == 0x0) { // mov mem, imm
                        opType = _X86_MOV3;
                        _push(_MIPS_T1, mipsTarget);
                        _push(_MIPS_T2, mipsTarget);
                        _getMemAddr(oo, mmm, x86Source, mipsTarget); // 内存地址在t0
                        *(*mipsTarget)++ = _gen_add(_MIPS_T0, _MIPS_ZERO, _MIPS_T2);
         
                        if (isWord) {
                            *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T1, 0);
                            *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T0, 1);
                            *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 8);
                            *(*mipsTarget)++ = _gen_or(_MIPS_T0, _MIPS_T1, _MIPS_T0);
                            *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 16);
         
                            x86Reg2 = _MIPS_T0;
                        } else {
                            *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T0, 0);
                            *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 24);
         
                            x86Reg2 = _MIPS_T0 - DEL_AH;
                        }
         
                        rd = x86Reg2;
                        _regImm(opType, 0, isWord, rd, x86Source, mipsTarget); // 结果在rd
         
                        if (!isWord) {
                            rd += DEL_AH;
                            printf("\n##rd: %d\n", rd);
                            *(*mipsTarget)++ = _gen_srl(rd, rd, 24);
                            *(*mipsTarget)++ = _gen_sb(_MIPS_T2, rd, 0);
                        } else {
                            *(*mipsTarget)++ = _gen_srl(rd, _MIPS_T1, 16);
                            *(*mipsTarget)++ = _gen_sb(_MIPS_T2, _MIPS_T1, 0);
                            *(*mipsTarget)++ = _gen_srl(_MIPS_T1, _MIPS_T1, 8);
                            *(*mipsTarget)++ = _gen_sb(_MIPS_T2, _MIPS_T1, 1);
                        }
         
                        _pop(_MIPS_T2, mipsTarget);
                        _pop(_MIPS_T1, mipsTarget);
                    }
                } else if (((c >> 4) & 0xf) == 0x0b) { // mov reg, imm
                    clear();
                    printf("mov reg, imm");
                    opType = _X86_MOV3;
                    isWord = (c >> 3) & 0x1;
                    rrr = c & 0x7;
                    rd = _getX86Reg(rrr, isWord);
                    _regImm(opType, 0, isWord, rd, x86Source, mipsTarget); // 结果在rd
                }
         
                // 移位指令,opcode有七位
                opcode = (c >> 1) & 0x7f;
                imm = 0;
                isImm = 0;
                isMem = 0;
                isData = 0;
                if ((opcode == 0x68) || (opcode == 0x69) || (opcode == 0x60)) {
                    _push(_MIPS_T1, mipsTarget);
                    _push(_MIPS_T2, mipsTarget);
                    switch (opcode) {
                    case 0x68:
                        imm = 1;
                        isImm = 1;
                    case 0x69:
                        break;
                    case 0x60:
                        isData = 1;
                        break;
                    default:
                        break;
                    }
         
                    c = *(*x86Source)++;
                    oo = (c >> 6) & 0x3;
                    rrr = (c >> 3) & 0x7;
                    mmm = (c >> 0) & 0x7;
         
                    switch (oo) {
                    case 0:
                    case 1:
                    case 2:
                        isMem = 1;
                        _getMemAddr(oo, mmm, x86Source, mipsTarget); // 内存地址在t0
                        *(*mipsTarget)++ = _gen_add(_MIPS_T0, _MIPS_ZERO, _MIPS_T2); // save t0 to t2
         
                        if (isWord) {
                            *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T1, 0);
                            *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T0, 1);
                            *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 8);
                            *(*mipsTarget)++ = _gen_or(_MIPS_T0, _MIPS_T1, _MIPS_T0);
                            *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 16); // t0: content
         
                            x86Reg2 = _MIPS_T0;
                        } else {
                            *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T0, 0);
                            *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 24);
         
                            x86Reg2 = _MIPS_T0 - DEL_AH;
                        }
         
                        rd = x86Reg2;
         
                        break;
                    case 3: // reg, imm
                        rd = _getX86Reg(mmm, isWord);
                        break;
                    }
         
                    if (isData) {
                        imm = _getX86Data(0, 0, x86Source);
                    }
         
                    printf("*** t2 *** %d\n", (int)*mipsTarget);
         
                    _shiftEntrance(rrr, isWord, rd, imm, isImm, mipsTarget);
         
                    if (isMem) { // rd, write to mem
                        if (!isWord) {
                            rd += DEL_AH;
                            printf("\n##rd: %d %d\n", rd, (int)*mipsTarget);
                            *(*mipsTarget)++ = _gen_srl(rd, rd, 24);
                            *(*mipsTarget)++ = _gen_sb(_MIPS_T2, rd, 0);
                        } else {
                            *(*mipsTarget)++ = _gen_srl(rd, _MIPS_T1, 16);
                            *(*mipsTarget)++ = _gen_sb(_MIPS_T2, _MIPS_T1, 0);
                            *(*mipsTarget)++ = _gen_srl(_MIPS_T1, _MIPS_T1, 8);
                            *(*mipsTarget)++ = _gen_sb(_MIPS_T2, _MIPS_T1, 1);
                        }
                    }
         
                    _pop(_MIPS_T2, mipsTarget);
                    _pop(_MIPS_T1, mipsTarget);
                    continue;
                }
         
         
                opcode = (c >> 2) & 0x3f; // 6位opcode
                switch (opcode) {
                case _X86_ADD0: // 常规寄存器
                case _X86_AND0:
                case _X86_OR0:
                case _X86_SUB0:
                case _X86_XOR0:
                case _X86_MOV3:
                case _X86_CMP0:
                    c = *(*x86Source)++;
                    oo = (c >> 6) & 0x3;
                    rrr = (c >> 3) & 0x7;
                    mmm = (c >> 0) & 0x7;
         
                    x86Reg1 = _getX86Reg(rrr, isWord);
         
                    switch (oo) {
                    case 0:
                    case 1:
                    case 2:
                        _push(_MIPS_T1, mipsTarget);
                        _push(_MIPS_T2, mipsTarget);
                        _getMemAddr(oo, mmm, x86Source, mipsTarget); // 内存地址在t0
                        *(*mipsTarget)++ = _gen_add(_MIPS_T0, _MIPS_ZERO, _MIPS_T2);
         
                        if (isWord) {
                            *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T1, 0);
                            *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T0, 1);
                            *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 8);
                            *(*mipsTarget)++ = _gen_or(_MIPS_T0, _MIPS_T1, _MIPS_T0);
                            *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 16);
         
                            x86Reg2 = _MIPS_T0;
                        } else {
                            *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T0, 0);
                            *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 24);
         
                            x86Reg2 = _MIPS_T0 - DEL_AH;
                        }
         
                        rd = direction ? x86Reg1 : x86Reg2;
                        rt = direction ? x86Reg2 : x86Reg1;
         
                        _regToReg(opType, rt, rd, mipsTarget);
         
                        if (direction == 0) { // rd, write to mem
                            if (!isWord) {
                                rd += DEL_AH;
                                printf("\n##rd: %d\n", rd);
                                *(*mipsTarget)++ = _gen_srl(rd, rd, 24);
                                *(*mipsTarget)++ = _gen_sb(_MIPS_T2, rd, 0);
                            } else {
                                *(*mipsTarget)++ = _gen_srl(rd, _MIPS_T1, 16);
                                *(*mipsTarget)++ = _gen_sb(_MIPS_T2, _MIPS_T1, 0);
                                *(*mipsTarget)++ = _gen_srl(_MIPS_T1, _MIPS_T1, 8);
                                *(*mipsTarget)++ = _gen_sb(_MIPS_T2, _MIPS_T1, 1);
                            }
                        }
         
                        _pop(_MIPS_T2, mipsTarget);
                        _pop(_MIPS_T1, mipsTarget);
                        break;
                    case 3: // reg, reg
                        x86Reg2 = _getX86Reg(mmm, isWord);
                        rd = direction ? x86Reg1 : x86Reg2;
                        rt = direction ? x86Reg2 : x86Reg1;
                        _regToReg(opType, rt, rd, mipsTarget);
                        break;
                    }
                    break;
                case _X86_ADC2_ADD2_AND2_CMP2_SUB2_XOR2_OR2: // imm
                    c = *(*x86Source)++;
                    oo = (c >> 6) & 0x3;
                    rrr = (c >> 3) & 0x7;
                    mmm = (c >> 0) & 0x7;
         
                    opType = rrr;
         
                    switch (oo) {
                    case 0:
                    case 1:
                    case 2:
                        _push(_MIPS_T1, mipsTarget);
                        _push(_MIPS_T2, mipsTarget);
                        _getMemAddr(oo, mmm, x86Source, mipsTarget); // 内存地址在t0
                        *(*mipsTarget)++ = _gen_add(_MIPS_T0, _MIPS_ZERO, _MIPS_T2);
         
                        if (isWord) {
                            *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T1, 0);
                            *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T0, 1);
                            *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 8);
                            *(*mipsTarget)++ = _gen_or(_MIPS_T0, _MIPS_T1, _MIPS_T0);
                            *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 16);
         
                            x86Reg2 = _MIPS_T0;
                        } else {
                            *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T0, 0);
                            *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 24);
         
                            x86Reg2 = _MIPS_T0 - DEL_AH;
                        }
         
                        rd = x86Reg2;
                        _regImm(opType, direction, isWord, rd, x86Source, mipsTarget); // 结果在rd
         
                        if (!isWord) {
                            rd += DEL_AH;
                            printf("\n##rd: %d\n", rd);
                            *(*mipsTarget)++ = _gen_srl(rd, rd, 24);
                            *(*mipsTarget)++ = _gen_sb(_MIPS_T2, rd, 0);
                        } else {
                            *(*mipsTarget)++ = _gen_srl(rd, _MIPS_T1, 16);
                            *(*mipsTarget)++ = _gen_sb(_MIPS_T2, _MIPS_T1, 0);
                            *(*mipsTarget)++ = _gen_srl(_MIPS_T1, _MIPS_T1, 8);
                            *(*mipsTarget)++ = _gen_sb(_MIPS_T2, _MIPS_T1, 1);
                        }
         
                        _pop(_MIPS_T2, mipsTarget);
                        _pop(_MIPS_T1, mipsTarget);
                        break;
                    case 3:
                        rd = _getX86Reg(mmm, isWord);
                        _regImm(opType, direction, isWord, rd, x86Source, mipsTarget); // direction used as sign
                        break;
                    }
                    break;
                case _X86_ADD1: // ACC
                case _X86_AND1:
                case _X86_OR1:
                case _X86_SUB1:
                case _X86_XOR1:
                case _X86_CMP1:
                    if (direction == 0) {
                        rd = _getX86Reg(0, isWord);
                        opType = _convertAccType(opType);
                        _regImm(opType, direction, isWord, rd, x86Source, mipsTarget); // direction used as sign
                    }
                    break;
                case _X86_MOV0: // mov mem, acc
                    oo = 0;
                    mmm = 6;
                    opType = _X86_MOV3;
                    x86Reg1 = _getX86Reg(0, isWord);
         
                    _push(_MIPS_T1, mipsTarget);
                    _push(_MIPS_T2, mipsTarget);
                    _getMemAddr(oo, mmm, x86Source, mipsTarget); // 内存地址在t0
                    *(*mipsTarget)++ = _gen_add(_MIPS_T0, _MIPS_ZERO, _MIPS_T2);
         
                    if (isWord) {
                        *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T1, 0);
                        *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T0, 1);
                        *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 8);
                        *(*mipsTarget)++ = _gen_or(_MIPS_T0, _MIPS_T1, _MIPS_T0);
                        *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 16);
         
                        x86Reg2 = _MIPS_T0;
                    } else {
                        *(*mipsTarget)++ = _gen_lbu(_MIPS_T2, _MIPS_T0, 0);
                        *(*mipsTarget)++ = _gen_sll(_MIPS_T0, _MIPS_T0, 24);
         
                        x86Reg2 = _MIPS_T0 - DEL_AH;
                    }
         
                    rt = direction ? x86Reg1 : x86Reg2;
                    rd = direction ? x86Reg2 : x86Reg1;
                    clear();
                    printf("mov mem, acc %d %d %d %d\n", direction, rt, rd, (int)*mipsTarget);
         
                    _regToReg(opType, rt, rd, mipsTarget);
                    printf("************************\n");
         
                    if (direction == 1) { // rd, write to mem
                        if (!isWord) {
                            rd += DEL_AH;
                            printf("\n##rd: %d\n", rd);
                            *(*mipsTarget)++ = _gen_srl(rd, rd, 24);
                            *(*mipsTarget)++ = _gen_sb(_MIPS_T2, rd, 0);
                        } else {
                            *(*mipsTarget)++ = _gen_srl(rd, _MIPS_T1, 16);
                            *(*mipsTarget)++ = _gen_sb(_MIPS_T2, _MIPS_T1, 0);
                            *(*mipsTarget)++ = _gen_srl(_MIPS_T1, _MIPS_T1, 8);
                            *(*mipsTarget)++ = _gen_sb(_MIPS_T2, _MIPS_T1, 1);
                        }
                    }
         
                    _pop(_MIPS_T2, mipsTarget);
                    _pop(_MIPS_T1, mipsTarget);
                    break;
                default:
                    break;
                }
            }
         
         
            return 0;
        }
         
        /**
         * 分析操作数
         * @return 0: reg, reg; 1: mem, reg; 2: reg, mem; 3: mem, imm; 4: mem, imm; 5: acc, imm; 6: acc, mem; 7: mem, acc;
         */
        int _analyseOperands(char **source, int direction, int isWord, int *rt, int *rd, int *mem, int *imm) {
            int oo;
            int rrr;
            int mmm;
            int disp;
            int word = *source;
            int b;
            int x86Reg1;
            int x86Reg2;
         
            int shiftAmout;
         
            int type = 0;
         
        //    b = _getByte(word, *index++);
        //    _adaptIndex(source, index);
         
            oo = (b >> 6) & 0x3;
            rrr = (b >> 3) & 0x7;
            mmm = (b >> 0) & 0x7;
         
            x86Reg1 = _getX86Reg(rrr, isWord);
         
            switch (oo) {
            case 0:
                if (mmm == 0x6) {
         
                }
                break;
            case 1:
                break;
            case 2:
                break;
            case 3: // reg, reg
                x86Reg2 = _getX86Reg(mmm, isWord);
                *rd = direction ? x86Reg1 : x86Reg2;
                *rt = direction ? x86Reg2 : x86Reg1;
                type = 0;
                break;
            }
         
            return type;
        }
         
        int _getByte(int word, int index) {
            return (word >> (24 - 8 * index)) & 0xff;
        }
         
        int _getOpcode(int word, int index) {
            int opcode = word >> (24 - 8 * index + 2);
            return opcode & 0x3f;
        }
         
        int _getX86Reg(int index, int isWord) {
            int reg;
         
            index &= 0x7;
         
            switch (index) {
            case 0:
                reg = isWord ? _X86_AX : _X86_AL;
                break;
            case 1:
                reg = isWord ? _X86_CX : _X86_CL;
                break;
            case 2:
                reg = isWord ? _X86_DX : _X86_DL;
                break;
            case 3:
                reg = isWord ? _X86_BX : _X86_BL;
                break;
            case 4:
                reg = isWord ? _X86_SP : _X86_AH;
                break;
            case 5:
                reg = isWord ? _X86_BP : _X86_CH;
                break;
            case 6:
                reg = isWord ? _X86_SI : _X86_DH;
                break;
            case 7:
                reg = isWord ? _X86_DI : _X86_BH;
                break;
            }
         
            return reg;
        }
         
        /**
         * 段寄存器索引
         */
        int _getX86RegSeg(int index) {
            int reg;
            index &= 0x7;
            switch (index) {
            case 0:
                reg = _X86_ES;
                break;
            case 1:
                reg = _X86_CS;
                break;
            case 2:
                reg = _X86_SS;
                break;
            case 3:
                reg = _X86_DS;
                break;
            default:
                break;
            }
         
            return reg;
        }
         
        void _adaptIndex(int *source, int *index) {
            if (*index == 4) {
                source++;
                *index = 0;
            }
        }
         
        /**
         * 寄存器到寄存器指令类型
         * rt, rd不能为t1和t2
         */
        void _regToReg(int type, int rt, int rd, int **target) {
            int delAl = _X86_AX - _X86_AL;
            int delAh = _X86_AX - _X86_AH;
            int rtNew;
            int rdNew;
         
            printf("#rt #rd %d %d\n", rt, rd);
            if ((rt > _X86_BH) || (rd > _X86_BH)) { // ok
                printf("##0##\n");
                _keyGenerate(type, rt, rd, target);
            } else {
                _push(_MIPS_T1, target);
                _push(_MIPS_T2, target);
         
                if ((rt < _X86_AH) && (rt >= _X86_AL)) {
                    if ((rd < _X86_AH) && (rd >= _X86_AL) ) { // rt = al, rd = al ok
         
                        printf("##1##\n");
                        rtNew = rt + delAl;
                        rdNew = rd + delAl;
                        _assSet(rdNew, _MIPS_T1, _L2H, target);
                        _assSet(rtNew, _MIPS_T2, _L2H, target);
                        _keyGenerate(type, _MIPS_T2, _MIPS_T1, target);
                        _assMove(_MIPS_T1, rdNew, _H2L, target);
         
                    } else { // rt = al, rd = ah ok
                        printf("##2##\n");
                        rtNew = rt + delAl;
                        rdNew = rd + delAh;
                        _assSet(rdNew, _MIPS_T1, _H2H, target);
                        _assSet(rtNew, _MIPS_T2, _L2H, target);
                        _keyGenerate(type, _MIPS_T2, _MIPS_T1, target);
                        _assMove(_MIPS_T1, rdNew, _H2H, target);
                    }
                } else {
                    if ((rd < _X86_AH) && (rd >= _X86_AL)) { // rt = ah, rd = al
                        printf("##3##\n");
                        rtNew = rt + delAh;
                        rdNew = rd + delAl;
                        _assSet(rdNew, _MIPS_T1, _L2H, target);
                        _assSet(rtNew, _MIPS_T2, _H2H, target);
                        _keyGenerate(type, _MIPS_T2, _MIPS_T1, target);
                        _assMove(_MIPS_T1, rdNew, _H2L, target);
                    } else { // rt = ah, rd = ah ok
                        printf("##4##\n");
                        rtNew = rt + delAh;
                        rdNew = rd + delAh;
                        _assSet(rdNew, _MIPS_T1, _H2H, target);
                        _assSet(rtNew, _MIPS_T2, _H2H, target);
                        _keyGenerate(type, _MIPS_T2, _MIPS_T1, target);
                        _assMove(_MIPS_T1, rdNew, _H2H, target);
                    }
                }
         
                _pop(_MIPS_T2, target);
                _pop(_MIPS_T1, target);
            }
        }
         
        int _getX86Data(int isWord, int isSigned, char **source) {
            int data;
            char c;
         
            c = *(*source)++;
            data = c;
            if (isWord && !isSigned) {
                c = *(*source)++;
                data |= c << 8;
            } else if (isSigned) {
                data <<= 24;
                data >>= 24;
                data &= 0xffff;
            }
         
            return data;
        }
         
        /**
         * 寄存器与立即数之间
         */
        void _regImm(int type, int isSigned, int isWord, int rd, char **source, int **target) {
            int delAl = _X86_AX - _X86_AL;
            int delAh = _X86_AX - _X86_AH;
         
            int data;
            char c;
         
            type = _convertItype(type);
         
        //    c = *(*source)++;
        //    data = c;
        //    if (isWord && !isSigned) {
        //        c = *(*source)++;
        //        data |= c << 8;
        //    } else if (isSigned) {
        //        data <<= 24;
        //        data >>= 24;
        //        data &= 0xffff;
        //    }
         
            data = _getX86Data(isWord, isSigned, source);
         
            _push(_MIPS_T1, target);
         
            if (isWord) { // add cx, ?
                *(*target)++ = _gen_lui(_MIPS_T1, data);
                _keyGenerate(type, _MIPS_T1, rd, target);
            } else {
                printf("#############%d %d\n", rd, data);
                if ((rd < _X86_AH) && (rd >=_X86_AL)) { // al
                    printf("# # rd is al # #\n");
                    _push(_MIPS_T2, target);
         
                    rd += delAl;
                    printf("##target: %d\n", (int)*target);
                    _assSet(rd, _MIPS_T1, _L2H, target);
                    *(*target)++ = _gen_lui(_MIPS_T2, data);
                    *(*target)++ = _gen_sll(_MIPS_T2, _MIPS_T2, 8);
                    _keyGenerate(type, _MIPS_T2, _MIPS_T1, target);
                    _assMove(_MIPS_T1, rd, _H2L, target);
         
                    _pop(_MIPS_T2, target);
                } else {
                    rd += delAh;
                    *(*target)++ = _gen_lui(_MIPS_T1, data);
                    *(*target)++ = _gen_sll(_MIPS_T1, _MIPS_T1, 8);
                    _keyGenerate(type, _MIPS_T1, rd, target);
                }
            }
         
            _pop(_MIPS_T1, target);
        }
         
        /**
         * 计算内存目标地址,放在$t0
         */
        void _getMemAddr(int oo, int mmm, char **source, int **target) {
            // 内存地址计算后放到t0, 低20位
            int segmentReg = _X86_DS;
            int indexReg1 = -1; // null
            int indexReg2 = -1; // null
            int hasIndex = 1;
            int disp = 0;
         
            switch (oo) {
            case 0:
                if (mmm == 0x6) {
                    mmm = 0x8;
                    disp = *(*source)++;
                    disp |= (*(*source)++) << 8;
                }
                break;
            case 1:
                disp = *(*source)++;
                break;
            case 2:
                disp = *(*source)++;
                disp |= (*(*source)++) << 8;
                break;
            }
         
            switch (mmm) {
            case 0: // ds: [bx + si]
                indexReg1 = _X86_BX;
                indexReg2 = _X86_SI;
                break;
            case 1: // ds: [bx + di]
                indexReg1 = _X86_BX;
                indexReg2 = _X86_DI;
                break;
            case 2: // ss: [bp + si]
                segmentReg = _X86_SS;
                indexReg1 = _X86_BP;
                indexReg2 = _X86_SI;
                break;
            case 3: // ss: [bp + di]
                segmentReg = _X86_SS;
                indexReg1 = _X86_BP;
                indexReg2 = _X86_DI;
                break;
            case 4: // ds: [si]
                indexReg1 = _X86_SI;
                break;
            case 5: // ds: [di]
                indexReg1 = _X86_DI;
                break;
            case 6: // ss: [bp]
                segmentReg = _X86_SS;
                indexReg1 = _X86_BP;
                break;
            case 7: // ds: [bx]
                indexReg1 = _X86_BX;
                break;
            case 8: // ds: [0]
                break;
            default:
                break;
            }
         
            _push(_MIPS_T1, target);
         
            *(*target)++ = _gen_srl(segmentReg, _MIPS_T0, 12);
         
            if (indexReg1 != -1) {
                *(*target)++ = _gen_add(indexReg1, _MIPS_ZERO, _MIPS_T1);
                if (indexReg2 != -1) {
                    *(*target)++ = _gen_add(_MIPS_T1, indexReg2, _MIPS_T1);
                }
                *(*target)++ = _gen_srl(_MIPS_T1, _MIPS_T1, 16);
<p>               &nbsp
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|文字版|手机版|DIY编程器网 ( 桂ICP备14005565号-1 )

GMT+8, 2025-8-3 05:28 , 耗时 0.293413 秒, 22 个查询请求 , Gzip 开启.

各位嘉宾言论仅代表个人观点,非属DIY编程器网立场。

桂公网安备 45031202000115号

DIY编程器群(超员):41210778 DIY编程器

DIY编程器群1(满员):3044634 DIY编程器1

diy编程器群2:551025008 diy编程器群2

QQ:28000622;Email:libyoufer@sina.com

本站由桂林市临桂区技兴电子商务经营部独家赞助。旨在技术交流,请自觉遵守国家法律法规,一旦发现将做封号删号处理。

快速回复 返回顶部 返回列表