支持指令流水的计算机系统设计与实现【100010365】
支持指令流水的计算机系统设计与实现
一、实验概要
在本次实验中,我们解析了 CPU 的基本组成,运行原理以及协同工作机制,设计并实现了基于五段流水的 CPU,CPU 的主频为 25MHz,在该主频下可以正确的运行,并且实现了 CPU 对于 ram,uart 等硬件的操作与控制,完成了 CPU 的基本功能。同时我们充分利用实验平台上的资源,对该 CPU 的功能进行了拓展,实现了 cpu 对于 flash,软硬件中断,VGA 显示以及 PS2 键盘的支持,CPU 具体支持的功能如下
THCO-MIPS 指令集中分配给本组的 25 条基本指令与 5 条扩展指令,这 5 条扩展指令为 SLLV、SRLV、BTNEZ、SW_RS 以及 SLT
通过数据前推以及插入气泡解决数据冲突和控制冲突
任意次数的中断
FLASH 开机引导
ps2 键盘的输入
VGA 终端的显示,与 ps2 键盘结合实现一个简单的文本编辑器
图 1:简单的文本编辑器
二、总体设计
2.1 流水线设计
流水线设计概述
在本次实验中,我们完成的 CPU 为支持指令流水的 CPU,采用了经典的五级流水设计,这五个阶段分别为: if(取指)、id(译码)、ex(执行)、mem(访存)以及 wb(写回)每个阶段所需要的控制信号和数据均由上一阶段产生并储存在两个阶段之间的四个段寄存器当中,这样就实现了指令控制信号的逐步流水。
if(取指)阶段设计
在该阶段,根据输入到 PC 寄存器控制器的控制信号在输入的数据中选择 PC 寄存器的值,并将该值以及该值对应的指令寄存器中的指令传给 if/id 寄存器组,以供下一阶段选择,
输入的可供选择的数据有 PC 的当前值、PC+1 的值,分支选择器传回的值等
id(译码)阶段设计
在这个阶段,对 if/id 寄存器输出的指令进行译码,取出需要用到的寄存器编号以及立即数的值进行取值和立即数扩展,并且通过 controller 模块根据指令产生所有的控制信号,并将这些信息传给 id/ex 寄存器,以供下一阶段使用。同时 J 类型和 B 类型指令会在该阶段通过分支选择器进行判断并传回是否 PC 的控制信号以及供选择的 PC 寄存器的值,这样解决了控制冲突。
冒险检测单元同样位于该阶段,通过检测读寄存器的编号以及下面阶段传回的写寄存器的编号,以及读写内存的地址是否有冲突。若有,则通过数据前推解决数据冲突。
ex 阶段设计
根据 id 阶段产生的控制信号,以及寄存器的值、扩展后的立即数等信息,ALU 模块进行对应的逻辑运算,将结果以及后面阶段的控制信号传递到 ex/mem 寄存器
mem 阶段设计
在这个阶段根据传入的控制信号,ex 阶段 ALU 的运算结果,对 ram 进行读写操作 为了解决结构冲突,我们使用 ram1 作为数据寄存器,ram2 作为指令寄存器。在这一阶段我们将同时把地址信息传给两个ram的适配器,并且根据该地址信息选择ram的输出。
若遇到在 mem 阶段访问 ram2 的情况,我们通过暂停流水线解决该结构冲突。
wb 阶段设计
在这个阶段,根据前面传入的控制信号和结果,写入寄存器的编号,将结果写入寄存器堆,到此流水线结束
2.2 数据通路设计
数据通路的总体设计如下图所示
总体设计
图 2:数据通路图
我们以课本上的数据通路为基础设计了该数据通路,主要的区别有
- 取消了数据旁路单元,改为通过数据前推解决数据冲突(绿色线表示)
- 增加了一个用以解决结构冲突的单元(蓝色线表示),主要功能为选择两个ram1 输出的数据和读取的地址
- 将用以解决控制冲突的分支选择器独立出来作为一个单独的模块(橙色线表示)
- 扩大了冒险检测单元的功能(紫色线表示)
三、基本功能实现
3.1 概述与类型对照表
基本功能指的是 25 条基本指令以及 5 条扩展指令的实现,指令流水结构的实现,以及控制冲突,数据冲突,结构冲突的解决。这一部分我们将主要介绍我们通过 VHDL 语言实现的该指令流水 CPU 的各个主要模块,以及这些模块间的关系。每个模块都会给出其具体功能,输入输出的信号表以及其在 CPU 结构中的位置等相关信息 为了提高代码的可读性、减少重复代码的工作量以及降低出错的几率,我们自定义了多种自定义变量,如下表所示:
类型名 | 内容 | 说明 |
---|---|---|
bt | ez、nez、d、none | 分支类型(零分支、非零分支,直接、非分支) |
as | imme、reg、none | ALU 第二个变量来源(立即数、寄存器、无) |
mtr | alu、mem、none | 写回数据来源(寄存器、内存、无) |
alutype | add 等 | ALU 运算操作码 |
id_control | branch(std_logic) | 是否为分支指令 0-不是 1-是 |
id_control | branchtype(bt) | 分支类型 |
id_control | jump(std_logic) | 是否为 jr 指令 0-不是 1-是 |
ex_control | alusrc(as) | ALU 第二个变量来源 |
ex_control | aluop(alutype) | ALU 运算操作码 |
mem_control | memread(std_logic) | 是否需要读内存 0-不需要 1-需要 |
mem_control | memwrite(std_logic) | 是否需要写内存 0-不需要 1-需要 |
ram_control | en(std_logic) | ram 使能信号 |
ram_control | oe(std_logic) | ram 读使能信号 |
ram_control | we(std_logic) | ram 写使能信号 |
表 1:自定义变量类型表
3.2 顶层模块 mymips
顶层模块的主要功能为接受时钟信号和 reset 信号以及与串口和内存交互 顶层模块的接口如下
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
tsre | in | std_logic | 串口数据发送完毕标志 |
tbre | in | std_logic | 串口发送数据标志 |
data_ready | in | std_logic | 数据准备信号 |
rdn | out | std_logic | 串口读使能信号 |
wrn | out | std_logic | 串口写使能信号 |
ram1_control | out | ram_control | ram1 控制信号 |
ram2_control | out | ram_control | ram2 控制信号 |
ram1_data_io | inout | std_logic_vector | ram1 数据 |
ram2_data_io | inout | std_logic_vector | ram2 数据 |
ram1_addr_o | out | std_logic_vector | ram1 写地址 |
ram2_addr_o | out | std_logic_vector | ram2 写地址 |
表 2:顶层模块接口表
3.3 if/id, id/ex, ex/mem, mem/wb 寄存器组
这四个寄存器组均位于五级流水线的两个阶段之间,用以储存上一阶段的结果和向下一阶段输出控制信号和数据
If/id 寄存器将 if 和 id 两个阶段隔离开,由时钟上升沿驱动,在每个上升沿到来时,将输入信号锁存并根据上个时钟周期的输入信号改变输出信号,这里的信号主要指的是 pc 寄存器的值和指令的值,同时在这里处理关于暂停流水线的信号。
If/id 寄存器的接口如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
if_pc | in | std_logic_vector | if 输入 pc 寄存器值 |
if_inst | in | std_logic_vector | if 输入指令值 |
stall | in | std_logic_vector | 暂停流水线控制信号 |
id_pc | out | std_logic_ | 向 id 阶段输出 pc 寄存器值 |
id_inst | out | std_logic | 向 id 阶段输出 |
表 3:if/id 寄存器接口表
id/ex 寄存器将 id 和 ex 两个阶段隔开,将 id 阶段取得或生成的数据和控制信号在时钟上升沿信号的驱动下输出到下一阶段,使得流水继续传递
id/ex 寄存器的接口如下
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
id_excontrol | in | ex_control | ;控制器输出信号 |
id_memcontrol | in | mem_control | ;控制器输出信号 |
id_wbcontrol | in | wb_control | ;控制器输出信号 |
stall | in | std_logic_vector | 暂停流水线控制信号 |
id_reg1 | in | std_logic_vector | rx 寄存器所存内容 |
id_reg2 | in | std_logic_vector | ry 寄存器所存内容 |
id_imme | in | std_logic_vector | 扩展后的 16 为立即数的值 |
id_regwrite | in | std_logic_vector | 写回寄存器的编号 |
ex_excontrol | out | ex_control | ;向 ex 阶段输出 16 位控制信号 |
ex_memcontrol | out | mem_control | ;向 ex 阶段输出 16 位控制信号 |
ex_wbcontrol | out | wb_control | ;向 ex 阶段输出 16 位控制信号 |
ex_regwrite | out | std_logic_vector | 向 ex 阶段输出写回寄存器编号 |
表 4: id/ex 寄存器接口表
ex/mem 寄存器承接 ex 与 mem 阶段,由时钟上升沿驱动,在时钟上升沿来到时将所有输入数据锁存并输出需要输出的数据,使得流水可以继续传递
ex/mem 寄存器的接口如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
ex_memcontrol | in | mem_control | 上一阶段输入的控制信号 |
ex_wbcontrol | in | wb_control | 上一阶段输入的控制信号 |
ex_aluans | in | std_logic_vector | alu 的运算结果 |
ex_aluinputb | in | std_logic_vector | alu 的第二个参数,可能在访存时使用 |
ex_reg_write | in | std_logic_vector | 写回寄存器的编号 |
mem_memcontrol | out | mem_control | 向下一阶段输出控制信号 |
mem_wbcontrol | out | wb_control | 向下一阶段输出控制信号 |
mem_regwrite | out | std_logic_vector | 向 mem 阶段输出写回寄存器编号 |
mem_aluans | out | std_logic_vector | 向 mem 输出 alu 运算结果 |
mem_aluinputB | out | std_logic_vector | 向 mem 输出 alu 的第二个输入 |
表 5:ex/mem 寄存器接口表
mem/wb 寄存器承接 wb 与 mem 阶段,由时钟上升沿驱动,在时钟上升沿来到时将所有输入数据锁存并输出需要输出的数据,使得流水可以继续传递
mem/wb 寄存器的接口如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
ex_wbcontrol | in | wb_control | 上一阶段输入的控制信号 |
aluans | in | std_logic_vector | alu 的运算结果 |
ram_data | in | std_logic_vector | mem 阶段从内存中取出的结果 |
mem_reg_write | in | std_logic_vector | 写回寄存器的编号 |
wb_wbcontrol | out | wb_control | 向下一阶段输出控制信号 |
wb_regwrite | out | std_logic_vector | 向 wb 阶段输出写回寄存器编号 |
regwritedata | out | std_logic_vector | 向wb阶段输出写回寄存器的内容 |
表 6:mem/wb 寄存器接口表
3.4 暂停流水线模块
暂停流水线模块(ctrl 模块)负责处理各种各样暂停流水线的情况,将部分发来的需要暂停流水线的请求进行处理,生成 4 位暂停流水线信号码,发给执行暂停流水线的 if/id 寄存器等模块,这些模块根据 4 位编码选择暂停方式 ctrl 模块的接口如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
stallreq_form_id | in | std_logic | id 段译码后发来的暂停请求 |
stallreq_form_conflict | in | std_logic | 因结构冲突发送的暂停流水线请求 |
stallreq_form_branch | in | std_logic | 因 branch 类发送的暂停流水线请求 |
stallreq_form_jump | in | std_logic | 因 jump 类发送的暂停流水线请求 |
stall | out | std_logic_vector | 四位的暂停流水线操作码 |
表 7:ctrl 模块接口表
3.5 pc 寄存器与控制器
pc 寄存器与控制器(pc_reg 模块)接受所有可能的下一个 PC 值,并且通过一个控制器进行选择,用以执行跳转或是暂停流水线操作。
pc_reg 模块的接口表如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
stall | in | std_logic_vector | 四位的暂停流水线操作码 |
branchaddr | in | std_logic_vector | branch 类指令返回的地址 |
jumpaddr | in | std_logic_vector | jump 类指令返回的地址 |
pcbranchsrc | out | std_logic | 是否执行 branch 类指令 |
pcjumpsrc | out | std_logic | 是否执行 jump 类指令 |
表 8:pc_reg 模块接口表
3.6 控制信号产生器
控制信号产生器(controller 模块),是一个组合逻辑模块,读入指令并根据指令输出所有与其相关的控制信号,并传入到 id/ex 阶段的寄存器中
controller 模块的控制信号表如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
id_inst | in | std_logic_vector | 上一阶段取出的指令 |
control_id | out | id_control | 控制器输出 id 段控制信号 |
control_ex | out | ex_control | 控制器输出 ex 段控制信号 |
control_mem | out | mem_control | 控制器输出 mem 段控制信号 |
control_wb | out | wb_control | 控制器输出 wb 段控制信号 |
表 9:controller 模块接口表 具体的控制信号表见附录 1
3.7 译码器
译码器(interpreter 模块),是一个组合逻辑模块,读入整条指令和 PC 值,并根据指令内容输出与寄存器和立即数的内容,并连接到寄存器堆,具体来讲是两个可能的读寄存器号和一个可能的写寄存器号,以及一个扩展为 16 位的立即数,直接输出到 id/ex 寄存器
interpreter 模块的信号表如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
id_pc | in | std_logic_vector | 上一阶段取得的 pc 值 |
id_inst | in | std_logic_vector | 上一阶段取出的指令 |
read_reg1 | out | std_logic_vector | 可能的第一个读寄存器的编号 |
read_reg2 | out | std_logic_vector | 可能的第二个读寄存器的编号 |
imme | out | std_logic_vector | 可能的扩展后 16 位立即数的值 |
write_reg | out | std_logic_vector | 可能的写寄存器的编号 |
表 10:interpreter 模块接口表具体到每条指令的译码内容见附录 2
寄存器编号为 4 位的二进制串,若不需要该寄存器编号输出,则寄存器编号规定为“1111”,若不需要立即数输出,则立即数口输出 16 位 0
3.8 分支选择器
分支选择器(branch_ctrl 模块)是用来处理分支指令的组合逻辑模块,读入当前的 PC 值,以及经过译码器和控制信号生成模块处理过的分支指令的参数的控制信号,进行处理并判断是否进行分支,最后将结果输入到 PC 寄存器的控制器
branch_ctrl 模块的信号表如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
int_pc | in | std_logic_vector | 上一阶段取得的 pc 值 |
int_imme | in | std_logic_vector | 译码器输出的立即数 |
int_inputA | in | std_logic_vector | 寄存器堆取出的比较对象值 |
ctrl_branch | in | std_logic | 控制信号生成模块输出,是否为分支指令 |
ctrl_branch_type | in | std_logic | 分支指令的种类 |
stallreq_load | in | std_logic | 若处于暂停流水线状态,暂不分支 |
ctrl_pc | out | std_logic_vector | 执行分支指令后预期的 pc 值 |
branch_confirm | out | std_logic | 是否进行分支 |
表 11: branch_ctrl 模块接口表
3.9 冒险检测单元
冒险检测单元(load_related 模块)是用来处理数据前推可能无法解决的问题,即 load 类型指令所使用的写寄存器,与下一条指令的读寄存器出现冲突这一问题,为一个组合逻辑模块,在遇到上述问题时输出暂停流水线信号到 3.4 中提到的暂停流水线模块
load_related 模块的信号表如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
ex_memread | in | std_logic | 上一条指令是否为 load 类型(读内存) |
ex_regwrite | in | std_logic_vector | 上一条指令可能写的寄存器号 |
id_reg1 | in | std_logic_vector | id 段可能读的寄存器 1 号 |
id_reg2 | in | std_logic | id 段可能读的寄存器 2 号 |
stallreq_load | out | std_logic | 暂停流水线信号 |
表 12:load_related 模块接口表
3.10 寄存器堆
寄存器堆(regfile 模块)存放所有 13 个寄存器,编号为“0000”至“1100”,其中“0000”至“0111”对应 R0 至 R7 这 8 个通用寄存器,其余 5 个寄存器如下表
编号 | “1000” | “1001” | “1010” | “1011” | “1100” |
---|---|---|---|---|---|
寄存器名 | SP | PC | RA | T | IH |
表 13:特殊寄存器编号表
寄存器堆读入译码器输出的读寄存器编号,若其合法,则输出对应寄存器的值。读寄存器的工作由组合逻辑完成同时为了解决数据冲突,数据前推的执行也在寄存器堆模块中完成。数据前推输入前两条指令所写的寄存器编号,与该阶段指令的读寄存器编号比较,若相同则跳过读寄存器阶段,通过数据前推直接输出数据,这样就替代了数据旁路模块的功能。
寄存器写回为时序逻辑模块,由时钟的上升沿驱动。值得注意的是,由于数据前推功能的存在,若 wb 阶段写寄存器与读寄存器冲突会直接进行数据前推,因此没有采用将一个时钟周期平分前一半写后一半读的设计
regfile 模块的信号表如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
writeaddr | in | std_logic_vector | ;写端口寄存器号/数据/写使能 |
writedata | in | std_logic_vector | ;写端口寄存器号/数据/写使能 |
we | in | std_logic | ;写端口寄存器号/数据/写使能 |
readaddr1 | in | std_logic_vector | ;读端口 1 寄存器号/使能/数据 |
re1 | in | std_logic | ;读端口 1 寄存器号/使能/数据 |
readdata1 | out | std_logic_vector | ;读端口 1 寄存器号/使能/数据 |
readaddr2 | in | std_logic_vector | ;读端口 2 寄存器号/使能/数据 |
re2 | in | std_logic | ;读端口 2 寄存器号/使能/数据 |
readdata2 | out | std_logic_vector | ;读端口 2 寄存器号/使能/数据 |
ex_regwrite_i | in | std_logic_vector | ;ex 阶段数据前推 |
ex_writedata_i | in | std_logic_vector | ;ex 阶段数据前推 |
ex_regwrite_enable_i | in | std_logic | ;ex 阶段数据前推 |
mem_regwrite_i | in | std_logic_vector | ;mem 阶段数据前推 |
mem_writedata_i | in | std_logic_vector | ;mem 阶段数据前推 |
mem_regwrite_enable_i | in | std_logic | ;mem 阶段数据前推 |
表 14:regfile 模块接口表
3.11 算术逻辑单元
算术逻辑单元(ALU 模块)处理所有的算术逻辑运算,为组合逻辑模块,读取上一阶段输入的两个寄存器读端口的输出以及扩展后的 16 位立即数,并根据输入的控制信号,包括选择第二个操作数的 alusrc 信号和选择操作码的 aluop 信号,进行运算,最后输出结果
操作码和对应的逻辑运算内容如下表所示 (一操作数和二操作数表示直接等于第一个或第二个操作数)
操作码 | addu | subu | andu | oru | xoru | sllu |
---|---|---|---|---|---|---|
逻辑运算 | 加 | 减 | 与 | 或 | 异或 | 逻辑左移 |
操作码 | saru | srlu | sltu | equal1 | equal2 | equal0 |
逻辑运算 | 算术右移 | 逻辑右移 | 大小比较 | 一操作数 | 二操作数 | 判断相等 |
表 15:操作码和逻辑运算种类对照表
ALU 模块的信号如下表:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
inputA | in | std_logic_vector | 可能的寄存器堆读端口 1 输出 |
inputB | in | std_logic_vector | 可能的寄存器堆读端口 2 输出 |
inputC | in | std_logic_vector | 可能的扩展后的 16 位立即数 |
excontrol | in | ex_control | 控制信号 |
output | out | std_logic_vector | 运算结果 |
表 16:ALU 模块接口表
3.12 结构冲突选择器
结构冲突选择器(mem 模块)负责解决在 mem 阶段读取 ram2 时出现的结构冲突,为组合逻辑模块,具体方法是将读取请求同时发给两个 ram 的适配器,然后适配器根据地址是否在该 ram 范围内输出结果至该模块,该模块在两个结果中选择正确的一个作为读取内存的输出,若产生了冲突,则输出暂停流水线信号
mem 模块的信号如下表:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
memcontrol_i | in | mem_control | 控制信号 |
aluans_i | in | std_logic_vector | alu 的结果,即读内存的地址 |
ram1data_i | in | std_logic_vector | ram1 读取的结果 |
ram2data_i | in | ex_control | ram2 读取的结果 |
stallreq_ram_conflict | out | std_logic | 暂停流水线信号 |
aluans_o | out | std_logic_vector | 读内存地址输出到内存适配器 |
ramdata_o | out | std_logic_vector | 解决结构冲突后的读内存输出 |
表 17:mem 模块接口表
3.13 ram1 与 ram2 适配器
ram1 适配器(ram1_adapter 模块)与 ram2 适配器(ram2_adapter 模块)为时序逻辑模块,负责处理 cpu 与 ram,cpu 与串口,ram 与串口的交互,同时 ram2 适配器也在计算机启动时直接读入 flash 中的数据,这一点将会在扩展功能的介绍中详细说明
ram1_adapter 的信号如下表
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
memcontrol_i | in | mem_control | 四位的暂停流水线操作码 |
aluans_i | in | std_logic_vector | branch 类指令返回的地址 |
aluinputB_i | in | std_logic_vector | jump 类指令返回的地址 |
tsre | in | std_logic | 数据发送完毕标志 |
tbre | in | std_logic | 发送数据标志 |
data_ready | in | std_logic | 数据准备信号 |
ram1_adapter_data_o | out | std_logic_vector | 适配器数据输出 |
mem_addr_o | out | std_logic_vector | 内存地址输出 |
mem_data_o | out | std_logic_vector | 内存数据输出 |
ram1_data_io | inout | std_logic_vector | ram1 总线数据 |
rdn | out | std_logic | 读使能 |
wrn | out | std_logic | 写使能 |
ram2_control_o | out | ram_control | ram2 控制信号 |
ram1_control_o | out | ram_control | ram1 控制信号 |
表 18:ram1_adapter 接口表
ram2_adapter 的信号如下表:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
pc | in | std_logic_vector | pc 值 |
has_ram_conflict | in | std_logic | 接口冲突判断 |
booting | in | mem_control | ;flash 功能用信号 |
flash_addr_i | in | std_logic_vector | ;flash 功能用信号 |
flash_data_i | in | std_logic_vector | ;flash 功能用信号 |
mem_addr_i | in | std_logic_vector | 内存地址输出 |
mem_data_i | in | std_logic_vector | 内存数据输出 |
mem_ram2_control_i | in | std_logic_vector | ram2 控制信号 |
ram2_adapter_data_o | out | std_logic_vector | 适配器数据输出 |
ram2_addr_o | out | std_logic_vector | 内存地址输出 |
ram2_data_o | out | std_logic_vector | 内存数据输出 |
ram2_data_io | inout | std_logic_vector | ram2 总线数据 |
ram2_control_o | out | ram_control | ram2 控制信号 |
表 19:ram2_adapter 接口表
四、扩展功能实现
4.1 flash 引导
flash 引导的作用是在每次计算机启动时将监控程序直接从 flash 加载到 ram2 中,因为 flash 断电后数据不会丢失,这样可以省略每次开机时都需要重复写入监控程序的过程 具体的实现方法为将监控程序写入 flash 中,利用 flash 断电数据不丢失的特性保存监控程序的数据,在启动后启用一个计数器,随着时钟周期+1,然后将该周期读取的 flash 中的数据写入 ram2 中,直到读取完监控数据的五百余条指令后完成 flash 引导的过程,我们把这个过程叫做 booting 过程。在 booting 过程中,流水线会被暂停,过程结束后流水线启动,这时 CPU 开始运转。
flash 引导(flash_io 模块)的信号如下表:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
booting | out | std_logic | booting 过程控制信号 |
data_out | out | std_logic_vector | flash 输出数据 |
addr_out | out | std_logic_vector | flash 输出数据对应 ram2 地址 |
flash_byte | out | std_logic | flash 操作模式 |
flash_vpen | out | std_logic | flash 写保护 |
flash_ce | out | std_logic | flash 使能信号 |
flash_oe | out | std_logic | flash 读使能 |
flash_we | out | std_logic | flash 写使能 |
flash_rp | out | std_logic | flash 工作控制 |
flash_addr | out | std_logic_vector | flash23 位地址线 |
flash_data | inout | std_logic_vector | flash 输入或输出数据 |
表 20:flash_io 模块接口表
4.2 软/硬件中断
我们设计的中断分为指令中断和硬件中断两种类型,处理过程基本一致,但是触发方法不同,软件中断通过实现 INT 指令实现,硬件中断通过按下 PS2 键盘上的 enter 按键触发, ps2 键盘的实现将在 4.3 节中详细说明。
软硬件中断的处理过程在 if/id 寄存器中进行,收到中断信号后将该寄存器输出置 0, cpu 空转一个周期,PC 置为跳转地址,进入一状态机,该状态机具体如下表所示:
当前状态 | 下一状态 | 输出 | 对应指令 |
---|---|---|---|
0001 | 0010 | 1110111001000000 | MFPC r6 |
0010 | 0011 | 0110001111111111 | ADDSP FF |
0011 | 0100 | 1101011000000000 | SW_SP r6 0 |
0100 | 0101 | 0110111001001111 | LI r6 imme |
0101 | 0110 | 0110001111111111 | ADDSP FF |
0110 | 0111 | 1101011000000000 | SW_SP r6 0 |
0111 | 1000 | 0110111000000101 | LI r6 5 |
1000 | 1001 | 0000100000000000 | NOP |
1001 | 1010 | 1110111000000000 | JR r6 |
1010 | 0000 | 0000100000000000 | NOP |
表 21:中断处理状态机
该状态机即为中断处理状态机,其执行的命令如上表所示
4.3 ps2 键盘控制器
ps2 键盘控制器的设计主要由 3 个模块组成,一个顶层模块 my_keyboard 与 CPU 交互,一个 key_data 模块,用 50MHz 驱动,接受扫描码,并将处理过的扫描码输出到 convert 模块,在这里截获 enter 键的按下和弹起事件,输出到顶层模块中再输出到 CPU,作为硬件中断的触发信号
扫描码的读取使用了栈这一数据结构的思想,在模拟 D 触发器处理 ps2_clk 的毛刺后,对于每个 ps2_clk 的上升沿,将 ps2_data 的数据压入一个大小为 12 栈中,栈低初始为 0,栈满后视为成功读取一个扫描码,这时会将同步信号置为 1,激活 convert 模块,并将处理后的扫描码中的 8 位输入 convert 模块
同时为了实现第一章中提到的文本编辑器,我们在 CPU 中添加了一个将扫描码转化为 ASCII 码的模块(ascii 模块),该模块为一个组合逻辑模块,在检测到同步信号为 1 时,将读入到的扫描码中的 8 位转化为按键对应的字母的十六进制 ASCII 码,然后通过输出给 VGA 模块,通过 VGA 控制器处理后显示在屏幕上,这一点会在 4.4 节中详细说明 my_keyboard 模块的信号表如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
ps2clk | in | std_logic | ps2 键盘时钟信号 |
ps2data | in | std_logic | ps2 键盘数据信号 |
request | out | std_logic | 同步信号 |
data | out | std_logic_vector | 扫描码中的八位数据信号 |
con_clk | out | std_logic | enter 按键按下事件信号 |
表 22:my_keyboard 模块接口表
key_data 模块的信号表如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
ps2clk | in | std_logic | ps2 键盘时钟信号 |
ps2data | in | std_logic | ps2 键盘数据信号 |
request | out | std_logic | 同步信号 |
data | out | std_logic_vector | 扫描码中的八位数据信号 |
表 23:key_data 模块接口表
convert 模块的信号表如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
request | in | std_logic | 同步信号 |
data | in | std_logic_vector | 扫描码中的八位数据信号 |
con_clk | out | std_logic | enter 按键按下事件信号 |
表 24:convert 模块接口表
ascii 模块接口表
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 50MHz 时钟信号 |
request | in | std_logic | 同步信号 |
data | in | std_logic_vector | 扫描码中的八位数据信号 |
asc_clk | out | std_logic | 合法按键按下事件信号 |
ascii | out | std_logic_vector | 扫描码转换的八位 ASCII 码 |
表 25:ascii 模块接口表
4.4 VGA 显示控制器
VGA 显示控制器主要由四个模块组成,顶层模块(vga_top 模块)与 ps2 键盘以及 CPU 进行交互,并且通过 VGA 接口与显示器相连,在 25M 时钟的驱动下,输出 RGB 值、行同步信号、场同步信号等信息。
信号同步模块(VGA模块)主要负责同步显示器,考虑到使用显示器分辨率为640×480,帧频率为 60Hz,因此使用 25MHz 的时钟作为电子束扫描显示区和消隐区的扫描频率。在每行结束时对行同步信号进行同步,全部扫描完成时对场同步信号进行同步,并即时向顶层模块返回当前扫描位置的坐标
屏幕显示模块(char_mem 模块)将整个屏幕分成了 80*30 的小方块,每个方块用于存储每个位置上显示的字符的 ASCII 码值,并接受信号同步模块返回的当前扫描位置坐标,然后根据该坐标将该位置字符的 ASCII 码值传入字符模型模块。
字符模型模块(font_rom 模块)根据 16 进制 ASCII 码的顺序储存了尺寸为 8*16 的 128 个可打印字模。从屏幕系那是模块获得 ASCII 值后将该位置对应的 RGB 值返回给顶层模块,
VGA 显示控制器的整个工作流程到此结束
vga_top 模块的信号表如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk_out | in | std_logic | 25MHz 时钟信号 |
char_we | in | std_logic | 字符写使能 |
char_value | in | std_logic_vector | 扫描码中的八位数据信号 |
r | out | ;std_logic_vector | ;rgb 值 |
g | out | ;std_logic_vector | ;rgb 值 |
b | out | ;std_logic_vector | ;rgb 值 |
hs | out | std_logic | 行同步信号 |
表 26:vga_top 模块接口表
vga 模块的信号表如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
rst | in | std_logic | reset 信号 |
clk | in | std_logic | 25MHz 时钟信号 |
h_counter | out | std_logic_vector | 行计数信号 |
v_counter | out | std_logic_vector | 场计数信号 |
hs | out | std_logic | 行同步信号 |
vs | out | std_logic | 场同步信号 |
表 27:vga 模块接口表
char_mem 模块的信号表如下:
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
clk | in | std_logic | 25MHz 时钟信号 |
char_read_address | in | std_logic | mem 读地址 |
char_write_address | in | std_logic_vector | mem 写地址 |
char_write_value | in | std_logic_vector | mem 写数据 |
char_read_value | out | std_logic_vector | mem 读数据 |
char_we | in | std_logic | mem 写使能信号 |
表 28:char_mem 模块接口表
font_rom 模块的信号表如下
信号名称 | 类型 | 信号类型 | 说明 |
---|---|---|---|
addr | in | std_logic_vector | 取字符11位信息(ASCII码+4位行数) |
clk | in | std_logic | 25MHz 时钟信号 |
char_we | in | std_logic | 按行输出字符信息 |
表 29:font_rom 模块接口表
五、实验结果
5.1 基本功能的实验结果展示
图 3:成功显示 OK·
这代表监控程序中的 25 条基本指令成功实现
图:4:扩展指令 SLLV 的测试样例
图 5:扩展指令 BTNEZ 的测试样例
图 6:扩展指令 SLT 的测试样例
图 7:扩展指令 SLT 的测试样例
图 9:扩展指令 RW_RS 的测试样例
通过这 5 个测试样例,可以说明 5 条扩展指令已经全部实现并可以正常工作,这说明我们的计算机可以很好的完成所要求的基本功能
5.2 ps2 键盘与中断结合实现软硬件中断
实验结果
软件中断由实现 INT 指令实现,如下右图所示,硬件中断通过截获 ps2 键盘按下并松开 enter 键触发,效果如下左图所示:
图 10:通过 enter 键触发的硬件中断
图 11:通过 int 指令触发的软件中断
处理过中断后执行相同指令所花费的时间会有毫秒级别的延长,这代表中断正确进行
5.3 ps2 键盘与 VGA 显示控制器结合实现文本编辑器
文本编辑器的主要界面如第一章图 1 所示,其中下图所示部分以及位于屏幕右下角的部分为固定在屏幕上的界面,其余空白部分可以用作文本编辑,我们一共实现了 26 个英文字母以及一些常用符号并 0~9 这 10 个数字的正确显示,并可以通过 space 键输出空格,通过 enter 键输出换行
图 12:欢迎界面及右下角的 copyright 栏
我们对 PS2 键盘处理的一点不足是没有办法处理多按键同时按下的情况,遇到这种情况时候输出的字符会显示为*,下图所示的原单词RETURN在输入E时同时按到了其它按键,这时候输出了字符*,变为了 R*TURN
图 13:在通过 PS2 键盘输入时的错误
六、总结与感想
经过了一星期的设计与两星期的实现,我们小组按时成功完成了“奋斗三星期,造台计算机”的任务。当我们最后实现的“ps2键盘与VGA控制器联合文本编辑器”成功实现的时候,所有小组成员都感到如释重负。作为我们这三星期成果的这台计算机,虽然有着很多的不足,例如主频只有 25MHz,而有些小组的主频可以做到 45MHz 以上,也有着很多我们开始想实现但最终没有能够实现的功能,例如通过硬件 Term 实现用 PS2 键盘输入指令,在显示器上显示结果这一终极目标,但是总体来讲还是令人满意的,也令全部的小组成员在看到我们的实验成果时有一种成就感涌上心头。回顾这三星期的历程,毫无疑问我们是有许多收获的 首先,经过三星期,我们对于 VHDL 语言以及背后的硬件设计理论有了前所未有的了解,开始时 VHDL 作为一门语言,与我们所熟悉的 C++,JAVA,PYTHON 等高级程序设计语言的差异让我们无所适从,在完成代码之后需要几倍的时间去让它通过编译,以致最终正确运行。在大实验之前的实验 2 中,将串口实验与内存读写实验组合的失败尝试也让我们一度感到非常沮丧与无所适从。其中最主要的差异就在于其执行时的时序问题,以及芯片的硬件特性所带来的局限性。但在这三星期后,我们对于这种差异性有了一定的适应,这一点在代码的开发阶段有着充分的体现。在实现阶段的开始,我们的几乎每一个模块在测试时最初都没有办法正常运行,正常运行后也无法得到预想当中的结果,但在开发的最后阶段,每个模块开发完成后第一个版本基本上都可以完成预定的的功能,只需要对其功能上缺少的部分作出一定调整便可以投入使用,这也大大加快了我们的开发流程。
第一周的设计流程对我们来说也是宝贵的经验,在设计阶段的最初我们体会到了自己对课堂上知识掌握的程度不够,首个版本数据通路的设计,即我们在第二次小课上展示的数据通路 1.0,其过程非常的艰难,而且最终基本上与课本上的设计一致,并没有加入多少我们自己的理解,但是 3 天后的数据通路 4.0 版本,不仅根据我们自己的理解加入了大量的调整,以至于删除了一个重要的模块,而且最后证明这一设计考虑的非常充分,与最后实际实现的版本只有部分地方做出了微调。这次实验的经历让我们对课程内容有了更好的理解。 与这一学期之前的软件工程大作业一道,这次以小组为单位的实验过程,让我们对于团队协作有了更充分的理解,小组成员之间的分工泾渭分明,其工作量和工作方向也考虑到了个人的水平以及擅长的方向,同时我们也利用了 git 工具进行版本控制,解决了 VHDL 在合并时相较于其它的高级程序语言更难进行的问题。包括最终的版本在内一共有 5 次比较大的版本变化,也有了利用版本回退功能避开通过调试无法解决的问题的经历。
在这三星期的过程中我们将大部分的时间都投入到了这一实验当中,虽然已经大三了,但是再度让我们找回了刚进入大学时的冲劲与对事物的新鲜感,对之后的学习生活肯定会有很大的帮助。在此我们要感谢刘卫东老师在课堂上对理论知识的讲解,和在实验小课上对我们的数据通路和模块设计的指导,感谢周围的在我们遇到困难时帮助我们的同学,以及通过努力最终完成了这一实验的小组成员们,正是因为有这么多人的帮助与努力,才为我们带来了这一段令人难忘的经历,才让我们能够成功应对这一次令人难忘的挑战。
附录 1 控制信号表
表 30:控制信号表
注:表中显示为 x 的部分在具体实现中均规定为 none 由于 INT 指令的实现不遵从五级流水过程,因而其控制信号不在表中
附录 2 译码表
指令名称 | read1 | read2 | imme | write |
---|---|---|---|---|
ADDIU | rx | im_8 | rx | |
ADDIU3 | rx | im_4 | ry | |
ADDSP | sp | im_8 | sp | |
BTEQZ | t | im_8 | ||
BTNEZ | t | im_8 | ||
MTSP | rx | sp | ||
SW_RS | sp | ra | im_8 | |
ADDU | rx | ry | rz | |
SUBU | rx | ry | rz | |
AND | rx | ry | rx | |
CMP | rx | ry | t | |
JR | rx | |||
MFPC | pc | rx | ||
OR | rx | ry | rx | |
SLLV | rx | ry | ry | |
SLT | rx | ry | t | |
SRLV | rx | ry | ry | |
B | im_11 | |||
BEQZ | rx | im_8 | ||
BNEZ | rx | im_8 | ||
LI | im_8 | rx | ||
LW | rx | im_8 | ry | |
LW_SP | sp | im_8 | rx | |
MFIH | ih | rx | ||
mtih | rx | ih | ||
NOP | ||||
SLL | ry | im_3 | rx | |
SRA | ry | im_8 | rx | |
SW | rx | ry | im_8 | |
SW_SP | sp | rx | im_8 |
表 31:指令译码表
注:表中 imme 列”im_”后的数字代表该条指令指令中的立即数位数 表中空白部分在实际实现中若处于 imme 列,则输出 16 位 0,否则输出 4 位 1 由于 INT 指令实现不遵照五级流水过程,因而其译码结果不在表中
♻️ 资源
大小: 6.90MB
➡️ 资源下载:https://download.csdn.net/download/s1t16/87368175
支持指令流水的计算机系统设计与实现【100010365】相关推荐
- 【Computer Organization笔记16】大实验任务详细说明:支持指令流水的计算机系统设计与实现
本次笔记内容: P31 计算机组成原理(31) P32 计算机组成原理(32) 我的计组笔记汇总:计算机组原理成笔记 视频地址:计算机组成原理 清华大学刘卫东 全58讲 国家精品课程 1080P 更完 ...
- 计算机组成流水系统可以,计算机组成原理实验十三建立指令流水系统实验
计算机组成原理实验十三建立指令流水系统实验 评阅计算机组成原理实验报告十三姓名 学号 时间 四7-9 地点 行健楼 606机房 一 建立指令流水系统实验 1. 实验内容及要求(1)实验内容:1. 分析 ...
- 【计算机系统设计】实践笔记(2)数据通路构建:第一类R型指令分析(2)
待办事项 时钟频率高,取指周期长,远大于执行周期,如何处理? 不可综合逻辑的处理 接上一篇 [计算机系统设计]实践笔记(2)数据通路构建:第一类R型指令分析(1) 8.2 ALU运算器 `timesc ...
- 【计算机系统设计】实践笔记(2)数据通路构建:第一类R型指令分析(1)
0 回顾 上一次实践笔记(0)我们实现了一个最简单的,能够每个上升沿+4的PC. 我们最需要关注的就是器件功能的独立性,避免内外功能混杂,同时一定要注意脑中有电路(RTL级描述的抽象电路而不是实际的门 ...
- 计算机组成原理之指令流水
指令流水 概念 原理 影响指令流水效率加倍的因素 影响指令流水线性能的因素 流水线性能 流水线的多发技术 流水线结构 概念 指令流水是指为提高处理器执行指令的效率,把一条指令的操作分成多个细小的步骤, ...
- CPU的结构和功能——指令流水及中断系统
CPU结构 (一)CPU的功能 想了解CPU的结构,首先要知道CPU的功能.然后讨论什么样的结构能够为CPU提供相应的功能.CPU由运算器和控制器组成. 1.控制器的功能 取指令,把指令从内存单元中取 ...
- 《逻辑与计算机设计基础(原书第5版)》——1.2 计算机系统设计的抽象层次...
1.2 计算机系统设计的抽象层次 正如莫格里奇(Moggridge是IDEO公司的共同创办人,他设计了世界上第一台笔记本电脑-译者注)所说的那样,设计就是一个理解问题的所有相关限制,并找到能平衡这些限 ...
- 超标量体系结构_CPU体系结构以及指令流水原理
计算机CPU的体系结构 什么是cpu指令流水 指令流水线(英语:Instruction pipeline)是为了让计算机和其它数字电子设备能够加速指令的通过速度(单位时间内被运行的指令数量)而设计的技 ...
- 设计计算机系统,紫外可见分光光度计的计算机系统设计
40多年来,紫外可见分光光度计等光学类分析仪器(主要是各类光谱.色谱仪器)的研制者和使用者,发现很多分析仪器的适用性受到软件的限制.实践表明,现代光学类分析仪器的计算机系统设计非常重要.仪器的设计者应 ...
最新文章
- Go的日志模块glog调研笔记
- 第39次《中国互联网络发展状况统计报告》发布
- mysql group by怎么用
- PMcaff-干货| 内容营销可不仅仅是点击量而已
- 你为什么不爱发朋友圈了?
- java初学者笔记总结day6
- appium+python自动化项目实战(二):项目工程结构
- 判断一颗二叉树是否是二叉排序树
- DataGrip使用入门
- webuploader
- Ubuntu18.04 上 phpvirtualbox 折腾记
- 【读书笔记-数据挖掘概念与技术】数据仓库与联机分析处理(OLAP)
- 常用的字符编码:ASCII、Unicode、UTF-8
- vue前端怎么导出图片_Vue将页面导出为图片或者PDF
- 魔兽世界官方小说android,魔兽世界官方小说(套装共6册)
- 学计算机惠普和联想笔记本哪个好,笔记本做得好,未必只有惠普和联想
- 视频加封面制作步骤,全部视频添加不同图片封面图
- Android Studio问题解决:Gradle sync failed: Sync failed: reason unknown
- RANSAC随机抽样一致算法
- VS2010安装离线MSDN
热门文章
- 官网学习Vue(一)Vue基础篇
- XP的分布式系统系列教程之Erasure-Code(实践与分析篇)
- 钉钉开发者工具真机调试时候一直加载中
- 光源色温不能决定光源唯一的原因
- 利用API函数修改PB自带toolbar的字体
- workbench设置单元坐标系_ANSYS入门教程,ANSYS中的坐标系及其操作
- Redis有序集合命令ZRANGEBYLEX详解与应用
- 对以下学员随机排序,生成一个新数组:var arr = [“鹿晗“,“王俊凯“,“蔡徐坤“,“彭于晏“,“周杰伦“,“刘德华“,“赵本山“]
- Neutrino追问AMA第15期|Celer 创始人董沫博士:链下扩容更容易做高互动低延迟的应用
- 前端开发处理企业微信浏览器时,开启企业微信浏览器调试模式的方法。