精华内容
下载资源
问答
  • 全手工制作CPU

    2019-09-15 01:34:01
    Magic-1是一个名叫Bill Buzbee的家伙手工打造的CPU,也是基于这个CPU制造的计算机的名字。“制作Magic-1的念头是在一次午餐中冒...Buzbee是一位编译器作者,但他为自己不熟悉CPU的实际工作而感到郁闷,因此想到动...
    796dd46210bc20dee6113a58.jpg
    Magic-1是一个名叫Bill Buzbee的家伙手工打造的CPU,也是基于这个CPU制造的计算机的名字。“制作Magic-1的念头是在一次午餐中冒出来的”,Buzbee说。Buzbee是一位编译器作者,但他为自己不熟悉CPU的实际工作而感到郁闷,因此想到动手来亲自作一个CPU。Buzbee在大学时候没有学过任何电子类的课程,而且对于晶体管、电阻、电容等也只是一知半解。他的朋友Ken想到有一本老杂志曾经介绍过如何使用TTL集成电路来制作极其简单的CPU,并推荐他去看一看。一周后,Buzbee找到了这些文章,并全部读了一遍。然后在下一次午餐上,Buzbee对Ken说起决定自己打造一个CPU,Ken说:为什么不呢?于是,Bill Buzbee的Magic-1项目拉开了序幕——
    2001年12月6日 Buzbee开始写项目日志,并为自己制定了一张艰难但还算乐观的日程表
    2001年12月18日 完成错误处理和中断机制的基本设计
    2001年12月29日 完成微代码的第一轮设计
    2002年1月6日 完成了Magic-1模拟器,可以对调用/返回指令序列进行跟踪
    2002年1月8日 模拟了Fibonacci函数,并丰富了软件接口约定
    2002年1月13日 Magic-1汇编器(qas)成形
    2002年1月18日 决定是选择大尾数法还是小尾数法表示整数的字节顺序,最终选择了大尾数法
    2002年2月28日 在模拟器中完成了页面错误机制
    2002年3月9日 决定使用普通电线来进行连线
    2002年6月3日 对指令集架构进行了意义重大的改变
    2002年6月4日 用C语言编写了Fabonacci程序并编译为Magic汇编程序
    2002年6月22日 改用分立的数据和代码地址空间
    2002年6月25日 设计用于多进程的上下文环境切换
    2002年7月12日 完成对微代码的重写工作
    2002年8月13日 放弃对rotate指令的支持
    2002年9月11日 上了Gil Smith的一堂电子电路课程
    2002年9月22日 从eBay上买了板材和外壳
    2002年的其他日子 向Ken Sumrall请教如何使用寄存器,从他那里学到了Ohm规则
    2003年3月30日 从Jhon Doran的D16/M中得到了灵感,完成了ALU/寄存器板的架构
    2003年4月13日 完成控制板的架构
    2003年4月14日 思考前面板的架构
    2003年5月3日 第一轮架构设计完毕
    2003年5月6日 将构建环境从Linux一直到Windows
    2003年5月16日 构思新的Magic-1模拟器
    2003年5月27日 在新的模拟器上成功运行了Fibonacci程序
    2003年6月3日 完成了新的汇编器功能
    2003年6月21日 Magic架构验证和测试用例达到了100%覆盖率
    2003年6月23日 Alistair Roe通过email提出了Magic-1外壳的构想
    2003年8月3日 将LCC(C编译器)一直到了Magic上
    2003年8月10日 在模拟器上成功地实现了Fibonacci程序的C语言版本 (这一天是Buzbee的生日)
    2003年8月27日 从David Conroy那里学到了信号完整性
    2003年9月18日 决定使用普通的带皮电线进行连接(奇怪,前面已经决定一次了)
    2004年1月3日 完成了全部的设计工作,开始制作
    2004年1月18日 Magic-1有了第一次心跳
    2004年1月26日 前面板完成
    2004年2月9日 内存板完成
    2004年2月20日 EPROM子板完成
    2004年2月26日 设备板完成
    2004年3月7日 微代码序列成功运转
    2004年3月9日 执行了第一条指令
    2004年3月19日 控制板完成
    2004年4月8日 尝试发布前期的工作
    2004年4月12日 ALU/寄存器板完成;同日被告知,发布尝试失败
    2004年4月13日 Magic-1成功运行了Fibonacci程序!
    2004年4月25日 Dave Conroy的测试会话发现了不真实的内存碎片
    2004年5月3日 Magic-1能“说话”了
    2004年5月8日 运行“Sieve of Erasthones”基准
    2004年5月16日 完成了IDE接口,Alistair Roe完成了外壳设计
    2004年7月23日 发布基本架构
    2004年9月12日 用户模式程序可以工作
    2004年9月15日 运行“Dhrystone”基准
    2004年9月22日 使用copy-on-write实现了fork()
    2004年10月23日 Magic-1的Dhrystone得分达到了384(0.25MIPS)
    2004年10月31日 运行Colossal Cave Adventure
    2005年4月9日 Alistair Roe设计的外壳到货
    2005年5月13日 Magic-1的硬件设计完毕

    学过数字逻辑和数字电路的朋友一定知道,通过使用TTL门电路,的确是能够实现一个CPU的;笔者在大学时也曾在软件上使用74系列芯片模拟过功能非常简单的CPU。然而,Buzbee朋友搞得这个家伙却是一个功能完全的CPU,我不知该如何形容它的功能,我本想说“麻雀虽小,五脏俱全”,但是……

    这款“家酿”CPU可以支持完整的硬件地址转换、内存影射IO和DMA,并且支持多进程,主频“高达”3MHz;该CPU采用8位地址总线,每个进程拥有128K地址空间,其中包括32个2K的数据页和32个2K的代码页,这些地址影射到22位的物理地址空间中,如果算上外部设备的地址空间就是23位物理地址空间。

    怎么样?够强的吧?这还不算什么,这个Buzbee还用这个CPU组装了一台微型计算机,名字就叫Magic-1,这台计算机包括两个串口和一块20M的1.3吋硬盘和另一块30M硬盘。最让人“乍舌”的事,这位朋友还将这台计算机做为一个Web服务器,同时支持Telnet会话(虽然只支持一个会话)。哦,还有,为了让这个系统能够跑起来,Buzbee还为它准备了一个C编译器!

    9f438b135ab1a7d3f6039e2d.jpg

    镂空的顶板,可以看到内部的照明蓝光,非常绚丽
    92a04afbfa7a6e67024f562d.jpg
    ALU/寄存器板的元件面
    5a2e810aa34f793db0351d2e.jpg
    ALU/寄存器板的连线面
    5dccf7243df8c9308644f92f.jpg
    控制板的元件面
    85719d8267c009bc6d81192f.jpg
    看看这个CPU的大小
    3cd403084a6dcfd263d98628.jpg
    看看背面连线的局部图,疯狂吧。。。
    02075fdf21280e1048540328.jpg

    转载于:https://my.oschina.net/yunbaobao/blog/35929

    展开全文
  • 如何手工制作CPU

    千次阅读 2008-05-22 10:22:00
    Buzbee是一位编译器作者,但他为自己不熟悉CPU的实际工作而感到郁闷,因此想到动手来亲自作一个CPU。Buzbee在大学时候没有学过任何电子类的课程,而且对于晶体管、电阻、电容等也只是一知半解。他的朋友Ken想到有一...

    Magic-1是一个名叫Bill Buzbee的家伙手工打造的CPU,也是基于这个CPU制造的计算机的名字。“制作Magic-1的念头是在一次午餐中冒出来的”,Buzbee说。Buzbee是一位编译器作者,但他为自己不熟悉CPU的实际工作而感到郁闷,因此想到动手来亲自作一个CPU。Buzbee在大学时候没有学过任何电子类的课程,而且对于晶体管、电阻、电容等也只是一知半解。他的朋友Ken想到有一本老杂志曾经介绍过如何使用TTL集成电路来制作极其简单的CPU,并推荐他去看一看。一周后,Buzbee找到了这些文章,并全部读了一遍。然后在下一次午餐上,Buzbee对Ken说起决定自己打造一个CPU,Ken说:为什么不呢?于是,Bill Buzbee的Magic-1项目拉开了序幕——
    2001年12月6日 Buzbee开始写项目日志,并为自己制定了一张艰难但还算乐观的日程表
    2001年12月18日 完成错误处理和中断机制的基本设计
    2001年12月29日 完成微代码的第一轮设计
    2002年1月6日 完成了Magic-1模拟器,可以对调用/返回指令序列进行跟踪
    2002年1月8日 模拟了Fibonacci函数,并丰富了软件接口约定
    2002年1月13日 Magic-1汇编器(qas)成形
    2002年1月18日 决定是选择大尾数法还是小尾数法表示整数的字节顺序,最终选择了大尾数法
    2002年2月28日 在模拟器中完成了页面错误机制
    2002年3月9日 决定使用普通电线来进行连线
    2002年6月3日 对指令集架构进行了意义重大的改变
    2002年6月4日 用C语言编写了Fabonacci程序并编译为Magic汇编程序
    2002年6月22日 改用分立的数据和代码地址空间
    2002年6月25日 设计用于多进程的上下文环境切换
    2002年7月12日 完成对微代码的重写工作
    2002年8月13日 放弃对rotate指令的支持
    2002年9月11日 上了Gil Smith的一堂电子电路课程
    2002年9月22日 从eBay上买了板材和外壳
    2002年的其他日子 向Ken Sumrall请教如何使用寄存器,从他那里学到了Ohm规则
    2003年3月30日 从Jhon Doran的D16/M中得到了灵感,完成了ALU/寄存器板的架构
    2003年4月13日 完成控制板的架构
    2003年4月14日 思考前面板的架构
    2003年5月3日 第一轮架构设计完毕
    2003年5月6日 将构建环境从Linux一直到Windows
    2003年5月16日 构思新的Magic-1模拟器
    2003年5月27日 在新的模拟器上成功运行了Fibonacci程序
    2003年6月3日 完成了新的汇编器功能
    2003年6月21日 Magic架构验证和测试用例达到了100%覆盖率
    2003年6月23日 Alistair Roe通过email提出了Magic-1外壳的构想
    2003年8月3日 将LCC(C编译器)一直到了Magic上
    2003年8月10日 在模拟器上成功地实现了Fibonacci程序的C语言版本 (这一天是Buzbee的生日)
    2003年8月27日 从David Conroy那里学到了信号完整性
    2003年9月18日 决定使用普通的带皮电线进行连接(奇怪,前面已经决定一次了)
    2004年1月3日 完成了全部的设计工作,开始制作
    2004年1月18日 Magic-1有了第一次心跳
    2004年1月26日 前面板完成
    2004年2月9日 内存板完成
    2004年2月20日 EPROM子板完成
    2004年2月26日 设备板完成
    2004年3月7日 微代码序列成功运转
    2004年3月9日 执行了第一条指令
    2004年3月19日 控制板完成
    2004年4月8日 尝试发布前期的工作
    2004年4月12日 ALU/寄存器板完成;同日被告知,发布尝试失败
    2004年4月13日 Magic-1成功运行了Fibonacci程序!
    2004年4月25日 Dave Conroy的测试会话发现了不真实的内存碎片
    2004年5月3日 Magic-1能“说话”了
    2004年5月8日 运行“Sieve of Erasthones”基准
    2004年5月16日 完成了IDE接口,Alistair Roe完成了外壳设计
    2004年7月23日 发布基本架构
    2004年9月12日 用户模式程序可以工作
    2004年9月15日 运行“Dhrystone”基准
    2004年9月22日 使用copy-on-write实现了fork()
    2004年10月23日 Magic-1的Dhrystone得分达到了384(0.25MIPS)
    2004年10月31日 运行Colossal Cave Adventure
    2005年4月9日 Alistair Roe设计的外壳到货
    2005年5月13日 Magic-1的硬件设计完毕
    学过数字逻辑和数字电路的朋友一定知道,通过使用TTL门电路,的确是能够实现一个CPU的;笔者在大学时也曾在软件上使用74系列芯片模拟过功能非常简单的CPU。然而,Buzbee朋友搞得这个家伙却是一个功能完全的CPU,我不知该如何形容它的功能,我本想说“麻雀虽小,五脏俱全”,但是……

    这款“家酿”CPU可以支持完整的硬件地址转换、内存影射IO和DMA,并且支持多进程,主频“高达”3MHz;该CPU采用8位地址总线,每个进程拥有128K地址空间,其中包括32个2K的数据页和32个2K的代码页,这些地址影射到22位的物理地址空间中,如果算上外部设备的地址空间就是23位物理地址空间。

    怎么样?够强的吧?这还不算什么,这个Buzbee还用这个CPU组装了一台微型计算机,名字就叫Magic-1,这台计算机包括两个串口和一块20M的1.3吋硬盘和另一块30M硬盘。最让人“乍舌”的事,这位朋友还将这台计算机做为一个Web服务器,同时支持Telnet会话(虽然只支持一个会话)。哦,还有,为了让这个系统能够跑起来,Buzbee还为它准备了一个C编译器!

     
    796dd46210bc20dee6113a58.jpg (7.3 KB)
     
     

    2008-1-5 19:58
    42543636.jpg (59.83 KB)
     
    2008-1-5 19:58
    1111111111111111111.jpg (44.08 KB)
      镂空的顶板,可以看到内部的照明蓝光,非常绚丽
    2008-1-5 19:58
    22222222222222222222222.jpg (89.19 KB)
      ALU/寄存器板的元件面
    2008-1-5 19:58
    333333333333333333.jpg (88.1 KB)
      ALU/寄存器板的连线面
    2008-1-5 19:58
    666666666666666666.jpg (64.87 KB)
      控制板的元件面
    2008-1-5 19:58
    7777777777777777777777.jpg (31.95 KB)
      好大的家伙
    2008-1-5 19:58
    88888888888888888.jpg (79.16 KB)
      看看背面连线的局部图,疯狂吧。。。
    展开全文
  • 制作多周期CPU(分析)

    千次阅读 2017-05-31 22:02:42
    上次做完单周期CPU后开始思考多周期的相关设计,最近总算做出来一个马马虎虎的。先来说说思路。要求 原理1.多周期多周期CPU指的是将整个CPU的执行过程分成几个阶段,每个阶段用一个时钟周期去完成,然后开始...

    上次做完单周期CPU后开始思考多周期的相关设计,最近总算做出来一个马马虎虎的。先来说说思路。

    要求

    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述

    原理

    1.多周期

    多周期CPU指的是将整个CPU的执行过程分成几个阶段,每个阶段用一个时钟周期去完成,然后开始执行下一条指令,一般将一条指令的执行分为以下几个阶段:

    (1)取指令(IF):根据程序计数器pc中的指令地址,从指令寄存器中取出对应指令,同时pc自动递增产生相邻的下一条指令的地址(pc+4),但是遇到跳转指令和分支指令,如beq,j,jal,jr则需要更进一步的处理

    (2)指令译码(ID):对从指令寄存器中取出的指令进行解析,将这条指令的操作码、相关寄存器、操作数、地址等等分离,传入到相应的组件中去

    (3)指令执行(EXE):根据指令译码得到的操作控制信号,具体执行指令动作,这里主要是ALU的运算操作

    (4)存储器访问(MEM):对于需要访问存储的操作,比如sw,lw,完成从存储器中读取数据并传出,或者是向存储器进行写操作

    (5)结果写回(WB):指令执行的结果或者是存储器访问得到的结果写回相应的寄存器中

    对于不同的指令,有些阶段是不需要的,比如跳转类型指令,只有IF,ID两个阶段,sw指令没有WB阶段。因此,不同的指令会在不同数量的时钟周期内完成,因此叫多周期。

    这里写图片描述

    2.要实现的指令格式

    MIPS指令的三种格式:
    这里写图片描述
    其中,
    op:为操作码;
    rs:只读。为第1个源操作数寄存器,寄存器地址(编号)是00000~11111,00~1F;
    rt:可读可写。为第2个源操作数寄存器,或目的操作数寄存器,寄存器地址(同上);
    rd:只写。为目的操作数寄存器,寄存器地址(同上);
    sa:为位移量(shift amt),移位指令用于指定移多少位;
    funct:为功能码,在寄存器类型指令中(R类型)用来指定指令的功能与操作码配合使用;
    immediate:为16位立即数,用作无符号的逻辑操作数、有符号的算术操作数、数据加载(Laod)/数据保存(Store)指令的数据地址字节偏移量和分支指令中相对程序计数器(PC)的有符号偏移量;
    address:为地址。

    3.将不同阶段状态化

    根据不同指令的操作流程,可以将在某阶段具有相同特性的几个指令分到一个状态内,比如对于EXE阶段,对add来说,它的下一个阶段是WB,而对于beq来说,则是IF,这样就不能把它们分到一个状态内,根据不同指令完成所需要的具体阶段,可以绘制出下图

    这里写图片描述

    4.状态转换的实现

    用三位二进制来表示不同的状态,则可以通过D触发器来实现状态的转换。根据状态转移图可知,下一个状态不仅和当前状态有关,还和操作码有关,因此D触发器的输入应该考虑到当前的状态以及操作码。当一个时钟周期过后,状态发生转移。

    这里写图片描述

    根据我的理解,多周期就是说在不同的阶段只做当前阶段的事情,比如IF阶段就只用完成从PC中传出指令地址然后到指令寄存器中获取指令,同时完成PC+4,而对于其他组件是做无用功的,或者说是不干活的,要实现这一点就要用到控制单元中发出的不同控制信号,因此状态转移的实现应该放到控制单元中。

    5.数据传递

    根据数据通路图
    这里写图片描述

    图中的IR指令寄存器使得指令代码保持稳定,IRWre使得IR在适当的时间获取正确的指令,pc写使能控制信号PCWre确保了pc的适时修改,这样能确保多周期CPU的稳定工作,而ADR、BDR、ALUoutDR和DBDR四个寄存器不需要写使能信号,其作用是将数据通路切分,比如ADR和BDR会将寄存器组传来的数据堵塞,当EXE阶段到来时获取寄存器组传来的值再传到数据选择其中然后供ALU使用,这样人为制造了小的延迟,保证正确的数据在正确的时机传入到正确的组件当中。
    这里写图片描述
    这里写图片描述

    关于ALUOp,几乎每个指令都有EXE阶段(除了跳转指令),而EXE阶段的核心就是ALU,对于不同的操作,ALU执行着不同的运算
    这里写图片描述

    6.关于上升沿,下降沿

    为了起到延迟的作用,本次实验中的PC,Control Unit,IR,ADR,BDR,ALUoutDR,DBDR的时钟边沿触发需要深入考虑,在实验中我发现,对于同一个时钟边沿,控制单元此时状态转换产生的控制信号无法立刻被其他组件拿来使用,必须等到下一次相同的时钟边沿到来才能使用。也就是说,无法立刻使用新值。比如对于PC和控制单元,若是PC在始终上升沿发生状态转换,如果PC的写操作也是在上升沿触发,如果到达IF阶段,控制单元此时发出的写使能信号无法在瞬间被PC捕获并且完成写操作,而要等到下一个周期才能完成,然而此时已经是ID阶段了,因此会违背多周期的性质,可能会造成许多错误,因此需要认真考虑。

    分析

    1.CPU架构建立

    根据数据通路图,一共有21个组件,根据其相应的联系,我分成了以下几个组:

    1.1.PC组:

    用于获取当前要执行的指令地址,并产生下一条指令的地址,包括:

    1.1.1.PC:
    获取当前要执行的指令地址,如果是置零操作,则将指令地址重置为1,受到时钟的控制,在时钟下降沿获取指令或者完成置零操作
    输入:CLK,RST,PCWre(写使能信号),NextPC
    输出:CurPC
    当时钟下降沿到来时,若PCWre为1,PC获取NextPC作为当前要执行的指令的地址。

    这里写图片描述

    1.1.2.ALU_For_Next(PC4获取加法器):
    获取当前指令临近的下一条指令的地址

    这里写图片描述

    1.1.3.ALU_For_Beq(beq指令相关跳转加法器):
    当beq操作需要跳转时,从位扩展器获取beq要跳转的跳转条数,与PC4做加法获取目标地址
    输入:PC4,Aim(跳转条数)
    输出:BeqPC
    由于一条指令是4个字节,因此PC4应与Aim * 4相加

    这里写图片描述

    1.1.4.Extend_For_Address(地址扩展器):
    对于跳转指令,由于一条指令32位,操作码占了6位,因此需要对指令中获取的相关地址进行扩展,具体原理是:由于MIPS32的指令代码长度占4个字节,所以指令地址二进制数最低2位均为0,将指令地址放进指令代码中时,可省掉。这样,除了最高6位操作码外,还有26位可用于存放地址,事实上,可存放28位地址,剩下最高4位由pc+4最高4位拼接上。
    输入:PC4,Address(26位)
    输出:JumpPC

    这里写图片描述

    1.1.5.Mux_FourToOne_PC(四选一指令地址选择器):
    对于不同的操作,下一条指令的地址可能会不同,比如add、sub类型的指令,下一条指令地址就是PC4;而对于beq类型指令,可能是BeqPC;对于jr指令,则是来自于31号寄存器的值;而j,jal则应该是JumpPC,而具体选择哪一个,则是控制单元的事情。
    输入:PC4,BeqPC,JrPC,JumpPC,PCSrc(来自Control Unit)
    输出:NextPC

    这里写图片描述

    1.2.控制组:

    1.2.1.Control Unit(控制单元):
    作为CPU的大脑,Control Unit负责发送相关控制信号给不同的组件。在我的理解中,不同的阶段,其实只有处在该阶段的相关组件才在进行正常的工作,其他组件则做的是无用功或者停止工作的。比如WB阶段,工作的组件就是寄存器组,此时PC不能获取下一条指令地址,ALU也不能做别的运算来影响这个阶段。如何保证只有相关组件正常工作,而其他组件不影响整个流程,这也用到了控制信号。所以控制信号应该不仅由操作码来决定,同时也应该由当前阶段来决定。
    首先解决如何实现状态转移,应该有两个数据,一个是CurState另外一个是NextState。这里用到了D触发器,自然想到了通过卡洛图来实现,根据不同类型的操作码,状态转移应该是这样实现的:

    这里写图片描述

    注意:000(IF)->001(ID)在程序运行的开始控制单元是没有获取任何操作码的,因此在初始阶段应该执行这个操作

    这里写图片描述

    至于后面当控制单元需要再由000获取001时该怎么办,此时并没有传入当前的指令,我在这里的选择是直接无视这个问题,因为对于任意一条指令,000->001是无条件的,因此即使传入到控制单元的操作码还是上一条的也能正确完成NextState的获取。
    完成了状态的转换的实现,就要考虑控制信号的发出了,对于不同状态,不同指令,发出的有用的控制信号的数量,具体的值是不同的,根据数据通路图,我绘制了这样的真值表

    这里写图片描述

    带*号表示在改变状态时必须把这些值设为非写状态,对于PCWre,如果一直处于1,则每个阶段它都会获取下一条指令的地址,错误;对于IRWre,虽然只要PC不会改变读取的指令也不会改变,但是这样做能够确保IR只在ID阶段工作,可以做到严格的阶段分工明确,而RD,WR和RegWre,则是出于数据保护的思考,确保只有在需要的时候才进行读取或写入,这样确保了寄存器组和存储器中的数据始终正确和安全。
    至于为什么把WrRegDSrc和RegDst、PCSrc放在ID阶段,因为j类型指令只到ID阶段,由此可以推出,对寄存器组的读取也是在ID阶段。由此可以推出不同阶段正常工作的寄存器组件:

    IF:
    PC,ALU_For_Next,ALU_For_Beq,Extend_For_Address,InstructionMemory

    ID:
    IR,Mux_ThreeToOne_WriteReg,Sign_Zero_Extend,RegisterFile,Mux_FourToOne_PC,Mux_TwoToOne_For_WriteData

    EXE:
    ADR,BDR,Mux_TwoToOne_For_InputA,Mux_TwoToOne_For_InputB,ALU,Mux_TwoToOne_Data

    MEM:
    ALUoutDR,DataMemory

    WB:
    DBDR,RegisterFile

    关于何时状态转移,我是以PC为参照考虑的,PC获取地址是在下降沿获取的,如果控制单元也是在下降沿改变状态的,则它传输的PCWre不能瞬间被PC获取并进行取地址操作,如果是在上升沿改变状态,则接下来的一个下降沿PC就可以顺利获取指令地址。

    这里写图片描述

    而NextState的获取,以及针对不同状态不同信号的处理,放在了一个always@( * )中来时时监控。信号的输出用了case语句

    这里写图片描述

    接下来的其他组件就要根据已经定好的PC和Control Unit的触发边沿进行确认。

    1.3.指令组:

    完成取出指令以及对指令的解析:

    1.3.1.InstructionMemory(指令存储器):
    初始将指令载入,通过PC传来的地址获取相应的指令
    输入:CurPC,InstMemRW
    输出:instcode
    指令寄存器我规定存储单元度位8位,用大端规则存储,即指令高位存到寄存器低
    地址,低位存到高地址,这是$readmemb方法默认实现的,取操作码就可以通过
    instcode = { InstMemory[CurPC], InstMemory[CurPC + 1], InstMemory[CurPc + 2], InstMemory[CurPC + 3] };

    这里写图片描述

    1.3.2.IR(指令寄存器):
    将从指令存储器中获取的指令载入,对其进行解析,将相关的op,rs,rt,rd,sa等分离出来。
    这里同样用到了时钟信号,因为指令从IF阶段就已经获取,只是被IR“拒之门外”,因此当控制单元更改状态为ID,将IRWre置为1时,若IR是上升沿触发,此时相关数据都已经存在,可以一瞬间完成指令的载入,而下降沿触发也同样可以完成,为了达到延迟效果,这里我选了下降沿触发。
    输入:CLK,instcode,IRWre
    输出:op,rs,rt,rd,sa,immediate,address

    这里写图片描述

    1.4.寄存器访问组:

    包括对寄存器组相应输入的选择以及寄存器组本身:

    1.4.1.Mux_ThreeToOne_WriteReg(三选一写寄存器选择器):
    对于写寄存器操作,目标对象可以是rd,rt,同时,对于jal指令,在跳转之前需要将PC4的值写入31号寄存器,因此写寄存器有三个选择
    输入:rt,rd,ReDst
    输出:WriteReg

    这里写图片描述

    1.4.2.Mux_TwoToOne_Data(二选一数据选择器):
    这个组件同样适用于ALU的输入端B的选择,以及对存储器的输出和ALU结果的输出的选择。当指令为jal时,需要将PC4写入31号寄存器。
    输入:WrRegDSrc,PC4,DB
    输出:WriteData

    这里写图片描述

    1.4.3.RegisterFile(寄存器组):
    获取相应寄存器的值,或者对相应寄存器进行写操作
    输入:RegWre,CLK,Reg1,Reg2,WriteReg,WriteData
    输出:DataOut1,DataOut2
    由于0号寄存器不可写,固定是0,因此我实际上只有31个寄存器,写操作是在时钟下降沿进行的,因为对于WB阶段,状态转换是在上升沿的,但是DBDR要在下降沿才能将数据传入,这时候如果采用下降沿写寄存器的话来不及,因此我选择上升沿写,在WB阶段结束的最后那一刻完成状态的转换,同时完成写寄存器的操作

    这里写图片描述

    1.5.位扩展组:

    1.5.1.Sign_Zero_Extend(位扩展器):
    指令中的立即数是16位的,要进入ALU的输入端则必须变为32位,逻辑操作(与、或)使用的是无符号扩展;加、减等操作使用的是有符号扩展
    输入:Imm_Number,ExtSel
    输出:Result

    这里写图片描述

    1.6.计算组:

    核心组件是ALU,包括数据的获取,以及ALU的运算

    1.6.1.DR:
    包括ADR,BDR,ALUoutDR,DBDR,作用就是起到延迟效果,保证CPU工作的稳定性,我设置的是时钟下降沿时载入数据并输出,原因是由于EXE阶段只有ADR和BDR起延迟效果,状态转移是在上升沿,所以只能是下降沿载入数据,ALUoutDR在MEM阶段才会被使用,也是MEM阶段唯一起延迟效果的,因此下降沿没问题。同理,由于数据存储器是随时读,因此DBDR下降沿也没有问题。

    这里写图片描述

    1.6.2.Mux_TwoToOne_For_InputA(二选一ALU输入端A数据选择器):
    通常ALU的A输入端口是寄存器组1号输出端口输出的rs寄存器的值,但是当操作为sll时,应该是指令中的sa段,但是sa只有5位,所以这个选择器的功能有两个:1.将sa通过无符号扩展变为32位;2.根据控制信号ALUSrcA输入到ALU的值应该是寄存器组入的值还是sa的值
    输入:ALUSrcA,DataIn,sa
    输出:DataOut

    这里写图片描述

    1.6.3.Mux_TwoToOne_For_InputB(二选一ALU输入端B数据选择器):
    同1.4.2原理相同

    1.6.4.ALU(运算单元):
    大多数操作都可能用到ALU,包add,sll,sub等,ALU的是对数据进行基本的算术操作,根据实验内容部分ALU运算表可以定相关方法,zero输出端是运算结果是否为0的,要用处是在beq中,两个值是否相等就是将其相减并结果是否与0相等,ALU输出的值可能是一个具体的数据(如add),也可能是个地址(如sw、lw)
    输入:Reg1,Reg2,ALUOp
    输出:result,zero

    这里写图片描述

    1.7.存储器组:

    1.7.1.DataMemory(数据存储器):
    lw操作和sw操作关到了数据的存储,我就制作了一个数据存储器用于存放数据。为了保该模CPU的稳定性,我设计数据存储器的读操作是随时的,存储单元度为8位,同样用大端规则存储。为了保证数据的安全性,对于写操作,我用了时钟。同样,由于状态转换是上升沿,因此写操作在下降沿触发最合适,但是ALUoutDR在下降沿才能传入正确的数据,因此只能用上升沿触发

    这里写图片描述

    1.7.2.MuxTwoToOne_For_DBDR(二选一DBDR数据选择器):
    选择DBDR的输入数据是来自ALU还是数据存储器,与1.4.2原理相同

    1.7.3.DBDR:
    与1.6.1原理相同

    MCPU(多周期CPU):

    作为顶层模块,包含上述所有组件
    输入:CLK,RST
    输出:CurPC,instcode
    由上文可以绘制结构图:

    这里写图片描述

    根据数据通路图即结构图将相关数据正确地传入传出即可。

    仿真测试:

    在InstructionMemory模块中取了$readmemb方法,所以通过文件写入,指令真值表为

    这里写图片描述

    在instruction.txt中写:

    这里写图片描述

    创建仿真组件MCPU_sim:

    这里写图片描述

    实验心得

    本次实验使我再次认识到了数据通路图的重要性,尽管多周期CPU很复杂,有相比于单周期更多的组件,更多的逻辑,但是只要仔细分析数据通路图就会发现,整个多周期CPU其实是由许多独立的组件各司其职,然后有Control Unit来统领全局的。因此我用了自上而下的设计思想,把一个多周期CPU层层拆分,根据各个组件完成的功能把它们分到多个组中,然后逐步完善底层设计。
    多周期与单周期不同,单周期是在一个周期内,所以组件都在工作,因此控制单元很容易分配工作,但是多周期每个周期只有一部分组件在工作,而其他组件,有些可以让它做无用功,有些则必须使其停止工作,比如ALU就可以让它做无用功,但是数据存储器就不行,如果此时数据存储器做的是写操作,可能会更改数据存储器中的相关数据,这样子就无法做到数据保护,因此,为了数据安全性,在非MEM阶段,数据存储器必须停止工作,即RD,WR都设为高阻态。
    另外,本实验基础组件与单周期CPU相似,但是关于时钟周期上却要下很大的功夫,如PC该何时读取下一条地址,IR何时载入指令,各个DR何时将数据获取,寄存器组何时实现写操作以及数据存储器何时完成读、写操作。实验一开始,由于思路不清晰以及对数据通路图的认识不够深入,经常出现如寄存器组来不及写入数据等问题。后来我是通过递进式思考,即先将PC的触发边沿确定,然后确定控制单元,接下来以此作为参照确定IR的触发边沿,以此类推,最后较为合理的推出了各个组件触发边沿之间的关系。
    本次实验使我在理解了单周期CPU的基础上对多周期CPU的工作机制有了更深入的了解。同时也认识到控制单元在CPU中举重若轻的地位。可以说,控制单元是CPU的灵魂、大脑,控制单元的好坏直接影响到了CPU的工作效率。对于控制单元该如何调配各个组件,在设计的时候应该静下心仔细思考。期间我绘制了状态关系图和相关真值表,发现这样做以后自己的思路更加清晰了,这为我以后的实验提供了一个重要的启示:不要光是想,将思路、过程写下来会使得自己更清楚自己应该做什么。
    同时我也认识到了在一个CPU中,只要有任何一个组件的任何一个步骤发生错误,都可能导致CPU整个运行发生不可想象的错误,虽然设计多周期CPU比较复杂,但是我们应该时时刻刻保持细心,绝不能容忍一丝错误。
    与单周期CPU实验相比,这次实验我有了一些改进,同时也有一些不足点
    改进:1.严格地将不同功能的组件独立出来,使得各个组件分工明确,各司其职
    2.考虑到了数据的保护,保证了CPU执行时期的安全性
    不足点:1.时钟触发边沿的选择还是有点漏洞,比如寄存器组写的触发沿是时钟上升沿,其实严格来讲,此时已经是下一个阶段,不符合多周期的本质
    2.对于控制单元的编写存在代码冗杂,我会接下来思考更好的定义方式

    展开全文
  • 自己动手写cpu》读书笔记

    千次阅读 2016-05-04 13:28:41
    本文来自《自己动手写cpu》一书的总结。原来自己看过原作者的《步步惊芯--软核处理器分析》以及其他关于or1200的书。本次粗略浏览了该书,就某些感兴趣的部分详细分析,并总结成此文。

    本文来自《自己动手写cpu》一书的总结。原来自己看过原作者的《步步惊芯--软核处理器分析》以及其他关于or1200的书。本次粗略浏览了该书,就某些感兴趣的部分详细分析,并总结成此文。

    关于5级流水的架构,可以自己去参考《计算机接口》一书。本文重点不在此。


    1、如何从rom里面取地址

    简化版的最基本的sopc的框图如下:

    module openmips(
    	input	wire			clk,
    	input wire			rst,	 
    	input wire[`RegBus]           rom_data_i,
    	output wire[`RegBus]           rom_addr_o,
    	output wire                    rom_ce_o	
    );
    always @ (posedge clk) begin
    		if (ce == `ChipDisable) begin
    			pc <= 32'h00000000;
    		end else begin
    	 		pc <= pc + 4'h4;
    		end
    	end
    	
    	always @ (posedge clk) begin
    		if (rst == `RstEnable) begin
    			ce <= `ChipDisable;
    		end else begin
    			ce <= `ChipEnable;
    		end
    	end
    	input wire[`InstAddrBus]      if_pc,
    	input wire[`InstBus]          if_inst,
    	output reg[`InstAddrBus]      id_pc,
    	output reg[`InstBus]          id_inst  
    	
    	always @ (posedge clk) begin
    		if (rst == `RstEnable) begin
    			id_pc <= `ZeroWord;
    			id_inst <= `ZeroWord;
    	  end else begin
    		  id_pc <= if_pc;
    		  id_inst <= if_inst;
    		end
    	end

    可以看到以上3个代码段,第一段是openmips中的,表示怎么连接含有指令的rom,第二段是pc_reg.v中的,pc是自加的,然后一直是可以对rom取址的,第3段是if_id.v中的,可以看到取址后传递给了译码模块。这就是最基本的程序跑起来的开始。

    后面作者自己收东西写指令码实现了与寄存器s0的操作,按照$readmemh的要求写成 inst_rom.data。inst_rom.v中读入了该文件。可以参考$readmemh的语法。




    2、协处理器的概念:









          比如说这里的定时器中断时间就应该很有用。


    3、异常相关指令的实现








    这里的定时器中断设置,是自己去写的汇编代码,然后验证指令正确。实际上我们一般是用c语言去写程序,编译器生成汇编代码和机器码。所以这些异常指令实际上应该是编译器去生产的,异常程序入口地址也不是我们操心的。但是这里我们只能自己写了。


    4、wishbone总线:








    说明:根据上面那个图,可以看到哈佛结构的指令rom和数据ram,都是例化了wishbone接口,均采用点对点的方式,连接到外部的有同样wishbone接口的rom和ram中。由于只是采用了这种接口协议,针对具体的情况还得在中间再加上一层接口模块,即上图所示的状态机,一部分是控制状态转化的时序电路,另一部分是给处理器接口信号赋值的组合电路。

    module openmips(
    	input	wire		      clk,
    	input wire		      rst,	
            input wire[5:0]               int_i,
      
      //指令wishbone总线
    	input wire[`RegBus]           iwishbone_data_i,
    	input wire                    iwishbone_ack_i,
    	output wire[`RegBus]           iwishbone_addr_o,
    	output wire[`RegBus]           iwishbone_data_o,
    	output wire                    iwishbone_we_o,
    	output wire[3:0]               iwishbone_sel_o,
    	output wire                    iwishbone_stb_o,
    	output wire                    iwishbone_cyc_o, 
    	
      //数据wishbone总线
    	input wire[`RegBus]           dwishbone_data_i,
    	input wire                    dwishbone_ack_i,
    	output wire[`RegBus]           dwishbone_addr_o,
    	output wire[`RegBus]           dwishbone_data_o,
    	output wire                    dwishbone_we_o,
    	output wire[3:0]               dwishbone_sel_o,
    	output wire                    dwishbone_stb_o,
    	output wire                    dwishbone_cyc_o,
    	
    	output wire                    timer_int_o	
    );
    wishbone_bus_if dwishbone_bus_if(
    		.clk(clk),
    		.rst(rst),
    	
    		//来自控制模块ctrl
    		.stall_i(stall),
    		.flush_i(flush),
    
    	
    		//CPU侧读写操作信息
    		.cpu_ce_i(ram_ce_o),
    		.cpu_data_i(ram_data_o),
    		.cpu_addr_i(ram_addr_o),
    		.cpu_we_i(ram_we_o),
    		.cpu_sel_i(ram_sel_o),
    		.cpu_data_o(ram_data_i),
    	
    		//Wishbone总线侧接口
    		.wishbone_data_i(dwishbone_data_i),
    		.wishbone_ack_i(dwishbone_ack_i),
    		.wishbone_addr_o(dwishbone_addr_o),
    		.wishbone_data_o(dwishbone_data_o),
    		.wishbone_we_o(dwishbone_we_o),
    		.wishbone_sel_o(dwishbone_sel_o),
    		.wishbone_stb_o(dwishbone_stb_o),
    		.wishbone_cyc_o(dwishbone_cyc_o),
    
    		.stallreq(stallreq_from_mem)	       	
    );
    
    	wishbone_bus_if iwishbone_bus_if(
    		.clk(clk),
    		.rst(rst),
    	
    		//来自控制模块ctrl
    		.stall_i(stall),
    		.flush_i(flush),
    	
    		//CPU侧读写操作信息
    		.cpu_ce_i(rom_ce),
    		.cpu_data_i(32'h00000000),
    		.cpu_addr_i(pc),
    		.cpu_we_i(1'b0),
    		.cpu_sel_i(4'b1111),
    		.cpu_data_o(inst_i),
    	
    		//Wishbone总线侧接口
    		.wishbone_data_i(iwishbone_data_i),
    		.wishbone_ack_i(iwishbone_ack_i),
    		.wishbone_addr_o(iwishbone_addr_o),
    		.wishbone_data_o(iwishbone_data_o),
    		.wishbone_we_o(iwishbone_we_o),
    		.wishbone_sel_o(iwishbone_sel_o),
    		.wishbone_stb_o(iwishbone_stb_o),
    		.wishbone_cyc_o(iwishbone_cyc_o),
    
    		.stallreq(stallreq_from_if)	       	
    );

    该章节源代码并没有给出有wishbone接口的rom和ram的源代码,但是从第一段的代码中可以看到实际上是要接有这么一种接口的rom和ram的。第二段代码可以看到分别实现了指令和数据的wb接口控制模块,直接接到外面的ram和rom。


    5、小型的SOPC






    module openmips_min_sopc(
    	input	wire			clk,
    	input wire			rst,	
    	
            //新增uart接口
    	input wire                   uart_in,
    	output wire                   uart_out,
    	
    	//16位GPIO输入接口
    	input wire[15:0]             gpio_i,
    </pre><pre code_snippet_id="1671506" snippet_file_name="blog_20160504_9_3260604" name="code" class="plain">        //32位GPIO输出接口
    	output wire[31:0]            gpio_o,
    	//flash接口
    	input wire[7:0]             flash_data_i,
    	output wire[21:0]           flash_addr_o,
    	output wire                 flash_we_o,
    	output wire                 flash_rst_o,
    	output wire                 flash_oe_o,
    	output wire                 flash_ce_o,  
    <span style="font-family: Arial, Helvetica, sans-serif;">                 //sdrsm接口</span>
    
    	output wire sdr_clk_o,
      output wire sdr_cs_n_o,
      output wire sdr_cke_o,
     output wire sdr_ras_n_o,
      output wire sdr_cas_n_o,
      output wire sdr_we_n_o,
      output wire[1:0] sdr_dqm_o,
      output wire[1:0] sdr_ba_o,
      output wire[12:0] sdr_addr_o,
       inout wire[15:0] sdr_dq_io	
    );

     openmips openmips0(
    		.clk(clk),
    		.rst(rst),
    		// 指令wb总线接口连到wb总线互联矩阵的主设备接口1
    		.iwishbone_data_i(m1_data_o),
    		.iwishbone_ack_i(m1_ack_o),
    		.iwishbone_addr_o(m1_addr_i),
    		.iwishbone_data_o(m1_data_i),
    		.iwishbone_we_o(m1_we_i),
    		.iwishbone_sel_o(m1_sel_i),
    		.iwishbone_stb_o(m1_stb_i),
    		.iwishbone_cyc_o(m1_cyc_i), 
      
    		.int_i(int),
    		// 数据wb总线接口连到wb总线互联矩阵的主设备接口0
    		.dwishbone_data_i(m0_data_o),
    		.dwishbone_ack_i(m0_ack_o),
    		.dwishbone_addr_o(m0_addr_i),
    		.dwishbone_data_o(m0_data_i),
    		.dwishbone_we_o(m0_we_i),
    		.dwishbone_sel_o(m0_sel_i),
    		.dwishbone_stb_o(m0_stb_i),
    		.dwishbone_cyc_o(m0_cyc_i),
    	
    		.timer_int_o(timer_int)	
    	
    );
    	// GPIO连到wb总线互联矩阵的从设备接口2
    	gpio_top gpio_top0(
        .wb_clk_i(clk),
    		.wb_rst_i(rst), 
    		.wb_cyc_i(s2_cyc_o),
    		.wb_adr_i(s2_addr_o[7:0]),
    		.wb_dat_i(s2_data_o),
    		.wb_sel_i(s2_sel_o),
    		.wb_we_i(s2_we_o),
    		.wb_stb_i(s2_stb_o),
    	  .wb_dat_o(s2_data_i),
    		.wb_ack_o(s2_ack_i),
    		.wb_err_o(),
    		.wb_inta_o(gpio_int),
    		.ext_pad_i(gpio_i_temp),
    		.ext_pad_o(gpio_o),
    		.ext_padoe_o()
      );
    	// fiash控制器连到wb总线互联矩阵的从设备接口3
    	flash_top flash_top0(
        .wb_clk_i(clk),
        .wb_rst_i(rst),
        .wb_adr_i(s3_addr_o),
        .wb_dat_o(s3_data_i),
        .wb_dat_i(s3_data_o),
        .wb_sel_i(s3_sel_o),
        .wb_we_i(s3_we_o),
        .wb_stb_i(s3_stb_o), 
        .wb_cyc_i(s3_cyc_o), 
        .wb_ack_o(s3_ack_i),
    	
    	//与小型sopc外部接口相连,对外是flash芯片
        .flash_adr_o(flash_addr_o),
        .flash_dat_i(flash_data_i),
        .flash_rst(flash_rst_o),
        .flash_oe(flash_oe_o),
        .flash_ce(flash_ce_o),
        .flash_we(flash_we_o)
      );
    	// uart控制器连到wb总线互联矩阵的从设备接口1
    	uart_top	uart_top0(
    	   .wb_clk_i(clk), 
    	   .wb_rst_i(rst),
    	   .wb_adr_i(s1_addr_o[4:0]),
    	   .wb_dat_i(s1_data_o),
    	   .wb_dat_o(s1_data_i), 
    	   .wb_we_i(s1_we_o), 
    	   .wb_stb_i(s1_stb_o), 
    	   .wb_cyc_i(s1_cyc_o),
    	   .wb_ack_o(s1_ack_i),
    	   .wb_sel_i(s1_sel_o),
    	   
    	   .int_o(uart_int),
    	   //连接uart接口
    	   .stx_pad_o(uart_out),
    	   .srx_pad_i(uart_in),
    	   .cts_pad_i(1'b0), 
    	   .dsr_pad_i(1'b0), 
    	   .ri_pad_i(1'b0), 
    	   .dcd_pad_i(1'b0),
    	   .rts_pad_o(),  
    	   .dtr_pad_o()
    	);
    	// sdram控制器连到wb总线互联矩阵的从设备接口0
      sdrc_top sdrc_top0(
         .cfg_sdr_width(2'b01),
         .cfg_colbits(2'b00),
         
         .wb_rst_i(rst),
         .wb_clk_i(clk),
                        
         .wb_stb_i(s0_stb_o),
         .wb_ack_o(s0_ack_i),
         .wb_addr_i({s0_addr_o[25:2],2'b00}),
         .wb_we_i(s0_we_o),
         .wb_dat_i(s0_data_o),
         .wb_sel_i(s0_sel_o),
         .wb_dat_o(s0_data_i),
         .wb_cyc_i(s0_cyc_o),
         .wb_cti_i(3'b000),
    		
    		//连接sdram
         .sdram_clk(clk),
         .sdram_resetn(~rst),
         .sdr_cs_n(sdr_cs_n_o),
         .sdr_cke(sdr_cke_o),
         .sdr_ras_n(sdr_ras_n_o),
         .sdr_cas_n(sdr_cas_n_o),
         .sdr_we_n(sdr_we_n_o),
         .sdr_dqm(sdr_dqm_o),
         .sdr_ba(sdr_ba_o),
         .sdr_addr(sdr_addr_o),
         .sdr_dq(sdr_dq_io),
                        
    		//Parameters
         .sdr_init_done(sdram_init_done),
         .cfg_req_depth(2'b11),
         .cfg_sdr_en(1'b1),
         .cfg_sdr_mode_reg(13'b0000000110001),
         .cfg_sdr_tras_d(4'b1000),
         .cfg_sdr_trp_d(4'b0010),
         .cfg_sdr_trcd_d(4'b0010),
         .cfg_sdr_cas(3'b100),
         .cfg_sdr_trcar_d(4'b1010),
         .cfg_sdr_twr_d(4'b0010),
         .cfg_sdr_rfsh(12'b011010011000),
    	   .cfg_sdr_rfmax(3'b100)
      );
    
    	wb_conmax_top wb_conmax_top0(
         	.clk_i(clk),
         	.rst_i(rst),
    
    	    // Master 0 Interface,数据接口
    	    .m0_data_i(m0_data_i),
    	    .m0_data_o(m0_data_o),
    	    .m0_addr_i(m0_addr_i),
    	    .m0_sel_i(m0_sel_i),
    	    .m0_we_i(m0_we_i), 
    	    .m0_cyc_i(m0_cyc_i), 
    	    .m0_stb_i(m0_stb_i),
    	    .m0_ack_o(m0_ack_o), 
    
    	    // Master 1 Interface,指令接口
    	    .m1_data_i(m1_data_i),
    	    .m1_data_o(m1_data_o),
    	    .m1_addr_i(m1_addr_i),
    	    .m1_sel_i(m1_sel_i),
    	    .m1_we_i(m1_we_i), 
    	    .m1_cyc_i(m1_cyc_i), 
    	    .m1_stb_i(m1_stb_i),
    	    .m1_ack_o(m1_ack_o), 
    
    	    // Slave 0 Interface,sdram控制器
    	    .s0_data_i(s0_data_i),
    	    .s0_data_o(s0_data_o),
    	    .s0_addr_o(s0_addr_o),
    	    .s0_sel_o(s0_sel_o),
    	    .s0_we_o(s0_we_o), 
    	    .s0_cyc_o(s0_cyc_o), 
    	    .s0_stb_o(s0_stb_o),
    	    .s0_ack_i(s0_ack_i), 
    	    .s0_err_i(1'b0), 
    	    .s0_rty_i(1'b0),
    
    	    // Slave 1 Interface,uart控制器
    	    .s1_data_i(s1_data_i),
    	    .s1_data_o(s1_data_o),
    	    .s1_addr_o(s1_addr_o),
    	    .s1_sel_o(s1_sel_o),
    	    .s1_we_o(s1_we_o), 
    	    .s1_cyc_o(s1_cyc_o), 
    	    .s1_stb_o(s1_stb_o),
    	    .s1_ack_i(s1_ack_i), 
    	    .s1_err_i(1'b0), 
    	    .s1_rty_i(1'b0),
    
    	    // Slave 2 Interface,gpio接口
    	    .s2_data_i(s2_data_i),
    	    .s2_data_o(s2_data_o),
    	    .s2_addr_o(s2_addr_o),
    	    .s2_sel_o(s2_sel_o),
    	    .s2_we_o(s2_we_o), 
    	    .s2_cyc_o(s2_cyc_o), 
    	    .s2_stb_o(s2_stb_o),
    	    .s2_ack_i(s2_ack_i), 
    	    .s2_err_i(1'b0), 
    	    .s2_rty_i(1'b0),
    
    	    // Slave 3 Interface,flash控制器
    	    .s3_data_i(s3_data_i),
    	    .s3_data_o(s3_data_o),
    	    .s3_addr_o(s3_addr_o),
    	    .s3_sel_o(s3_sel_o),
    	    .s3_we_o(s3_we_o), 
    	    .s3_cyc_o(s3_cyc_o), 
    	    .s3_stb_o(s3_stb_o),
    	    .s3_ack_i(s3_ack_i), 
    	    .s3_err_i(1'b0), 
    	    .s3_rty_i(1'b0),
    	);
    
    endmodule


    从上面可以看到是怎么集成进去的。全书写了几个外设模块的原理,但是一个关键的wb_conmax的实现原理,其实现了总线的上述诸多功能,包括仲裁等。这里只是直接拿来用了,理清这些关系很重要。

    为什么flash的地址是0x30000000开始,因为我们是把他接在从设备3了。wb_conmax有规定,根据从设备来设定地址区段。

    这些外置的接口ip都是通过mcu操纵其ip内部的寄存器来实现的。比如gpio:



    所以要知道外部io口上的数据,只需内核去读这个地址的RGPIO_IN的这个寄存器就可以了。

    所以我们实际上理解的编译器,实际上只是将我们的c语言代码编译成最核心的流水线内核的操作指令,涉及到外设的部分仅仅是生成一些操作寄存器的指令,即编译器只是理解外设与寄存器一致,只需生成访存和写回的指令即可。


    6、测试与验证

    在上面实现后怎么验证,作者用de2的开发板:


    由于与mips指令兼容,所以可以使用mips的编译器,生成了inst_rom.o的可重定位elf文件,通过ld文件生成可执行文件,但是有elf文件头,与我们期望的格式还是有很大差别,可以利用mips-sde-elf-objcopy得到.bin的纯二进制格式,这正是我们需要的。(如果是用modelsim仿真,还需将其生成modelsim中存储器初始化文件的格式生成.data文件)

    de2中将利用控制面板程序擦写进flash。

    注意在编译前要修改ram.ld文件,将其中的起始地址从0x00000000修改为0x30000000,因为前2个测试陈旭是在flash中运行的,而flash的起始地址是0x30000000.

    例子3模拟了os的启动过程:




    后面是应用程序:







    怎么写入flash呢?





    展开全文
  • 如何制作一个简单的16位CPU

    千次阅读 2012-11-16 09:58:56
    我果然标题党了吗?...如何制作一个简单的16位CPU,首先我们要明确CPU是做什么的,想必各位都比我清楚,百度的资料也很全。。。。。 http://baike.baidu.com/view/2089.htm 如果想要制作一个
  • 第一、 制作自己的数据集 分为train和val两个文件夹,train下面有每个类别的一个单独的文件夹,文件夹里面放着这个类别的图片。 第二、 制作train.txt和val.txt(标签从0开始) 第三、 根据convert_...
  • 在下个学期,本人有一门计算机组成原理的课程设计,需要用Verilog设计一个可用的CPU。为了这门课设,我选择在这个暑假先进行研究一下。 在经过多方查找之后,我在学校的图书馆中找到了《CPU自制入门》这本书。这本书...
  • 自己动手写CPU(基于FPGA与Verilog)

    千次阅读 2018-04-03 20:29:00
    大三上学期开展了数字系统设计的课程,下学期便要求自己写一个单周期CPU和一个多周期CPU,既然要学,就记录一下学习的过程。  CPU——中央处理器,顾名思义,是计算机中最重要的一部分,功能就是周而复始地执行...
  • 简单CPU设计

    万次阅读 多人点赞 2018-01-03 13:30:50
    设计CPU的主体思路如下: 要设计一个简单的CPU,我们需要先设计一个简易版的指令系统,然后根据该指令系统搭建对应的数据通路,在数据通路的基础上实现控制逻辑,下一步是加上时钟信号,划分流水线,紧接着解决流水...
  • 所以我决定攻克这一世界(我个人的世界)难题,自己做一个CPU来理解CPU的工作原理。 “ 造一个什么样的CPU好呢?Intel i7那样的CPU好不好?” “好是好!可我现在也只敢在心里想一想,毕竟我幼儿园都没毕业,说...
  • Linux系统查看CPU使用率、内存使用率、磁盘使用率

    万次阅读 多人点赞 2019-04-16 15:17:00
    一、查看CPU使用率 1. top 命令 top命令可以看到总体的系统运行状态和cpu的使用率 。 %us:表示用户空间程序的cpu使用率(没有通过nice调度) %sy:表示系统空间的cpu使用率,主要是内核程序。 %ni:表示用户空间...
  • 所以我决定攻克这一世界(我个人的世界)难题,自己做一个CPU来理解CPU的工作原理。  “ 造一个什么样的CPU好呢?Intel i7那样的CPU好不好?”  “好是好!可我现在也只敢在心里想一想,毕竟我幼儿园都没毕业,...
  • 自己动手设计一个FPGA上可运行的CPU及汇编器前言简易CPU的基本功能和架构简介 前言 其实自己写一个CPU的想法早在2008年还在上大学的时候就有了,因为后来工作的原因,当然更多的是因为自己懒散的原因,一直没有投入...
  • cpu是什么

    千次阅读 2019-06-30 14:53:25
    把GPU当作CPU用又有新突破  利用GPU代替CPU工作,有这种事吗?没错。在2003年,一个来自斯坦福大学的小组发布他们的BrookGPU,这是一款针对图形芯片设计的类似于C语言的编译器和执行系统,该小组指出,通过该系统,...
  • OpenMIPS处理器在设计的时候就计划与MIPS32...本节将说明如何安装使用GNU开发工具链以及如何制作Makefile文件,从而以更加方便、快捷、自动的方式对测试程序进行编译,并得到指令存储器ROM的初始化文件inst_rom.data。
  • 电脑cpu怎么看 怎么看cpu好坏 (全文)

    千次阅读 2016-12-30 22:19:11
    前些天为大家介绍了如何看电脑配置,其中也简单的提到了,怎么看电脑cpu,以及判断cpu的性能等。今天我们将围绕如何看电脑cpu以及如何看cpu好坏等新手朋友常问的问题为大家做个深度的介绍。 以下分条为大家做个...
  • 自己动手画一个CPU——Logisim,上

    千次阅读 2020-07-20 19:01:10
    实验一:八位可控加减法器 电路设计图: 代码测试结果 实验二:原码一位乘法器设计 电路设计图: 代码测试结果 实验三:MIPS运算器设计 电路设计图 代码测试结果: 实验四:汉字字库存储芯片扩展实验 ......
  • cpu运作原理

    千次阅读 2020-04-02 12:34:26
    我们每个程序员或许都有一个梦,那就是成为大牛,我们或许都沉浸在各种框架中,以为框架就是一切,以为应用层才是最重要的,你错了。在当今计算机行业中,会应用是基本素质,如果你懂...CPU 的全称是Central Proces...
  • CPU设计初探

    千次阅读 2010-12-17 03:19:00
    习惯了“实践-理论-实践”的学习模式,对硬件知之甚少却学完了《计算机组成原理》,我努力尝试让自己找到这门课的感觉。没有FPGA的试验器材,也没有多少真实芯片在身边,于是我拾起《数字电路》,准备在虚拟环境下,...
  • 科学Sciences导读:图解CPU生产全过程——以intel CORE i7为例,展望CPU架构。本文简介英特尔Intel x86架构、生产制造CPU的原料和准备、CPU生产制造过程,并展望CPU的x86架构和RISC架构。其中,重新整理《图解intel ...
  • 如何自己制作一个RISC指令集的CPU

    千次阅读 2019-02-26 11:28:54
    如何制作一个简单的16位CPU,首先我们要明确CPU是做什么的,想必各位都比我清楚,百度的资料也很全。。。。。如果想要制作一个CPU,首先得明白下计算机的组成结构(或者计...
  • CPU执行程序的简单理解

    千次阅读 2019-06-07 22:00:26
    本文引用了《CPU执行程序的原理(简化过程)》、《CPU的运行到函数调用做个了解》部分图片和内容,这里向作者致谢。 这里不去讲过于专业的专业知识,毕竟,CPU上的任意一个知识点,想讲明白,都不容易,我们从使用...
  • CPU GPU设计工作原理(转载)

    千次阅读 2017-08-13 01:07:01
    ...这个不用讨论,很简单的道理你看看显卡芯片的更新速度和CPU的更新速度就可见一斑了。还是简单说说他们的设计原理吧。 CPU: 但是,现在我要问一句:“什么是CPU?”我相信大多数人并不知道
  • 写一写CPU中涉及到RAM的部件,如寄存器堆、数据存储器等。  大家应该在大一刚接触到计算机的时候就知道ROM、RAM了吧。但也记不得那些繁杂的名称,只知道ROM是只读存储器,RAM是可读写存储器。其实知道这些也就够了...
  • 制作自己的数据集之前,我们先下载VOC2007数据集。 百度云地址: http://pan.baidu.com/s/1gfdSFRX 解压,然后,将该数据集放在py-faster-rcnn-master\data目录下。(后面你将用你的训练数据集替换VOC2007数据集...
  • 芯片和CPU有什么不同? 芯片是“集成电路”的俗称。集成电路有模拟集成电路和数字集成电路,如果一片集成电路(芯片)中既有模拟电路又有数字电路,则称其为数模混合集成电路。  CPU是中央处理器,包含运算器和...
  • CPU维护及故障处理

    千次阅读 2019-08-16 08:16:37
    第三章 CPU维护及故障处理 保证夏季CPU风扇运转良好的方法  转眼又到了酷暑季节,该给CPU风扇加油了,让它“吃饱好干活”。某文采用电器包装用的塑料泡沫来做CPU油封,理由是“材料易找,而且方便易行”。笔者对此...
  • cpu工作原理简析

    千次阅读 2014-06-02 00:27:29
    在了解CPU工作原理之前,我们先简单谈谈CPU是如何生产出来的。CPU是在特别纯净的硅材料上制造的。一个CPU芯片包含上百万个精巧的晶体管。人们在一块指甲盖大小的硅片上,用化学的方法蚀刻或光刻出晶体管。因此,从这...
  • Linux系统进程CPU使用率限制脚本

    千次阅读 2018-11-16 16:46:56
    一、背景 近日在客户系统运维中发现,有系统...根据此编写脚本,配合定时任务放置在服务器上,达到限制程序CPU情况,可根据自己系统CPU核心数进行参数配置,会记录CPU超过阀值的日志,可供后期进行查看分析。 二、...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 59,370
精华内容 23,748
热门标签
关键字:

如何制作自己的cpu