基于FPGA的简化RISC_CPU设计

基于FPGA的简化RISC_CPU设计

一、 RISC各个子模块的设计

CPU即中央处理器,是计算机内部负责信息处理及内存资源管理的核心部件。其主要的功能有取指令、分析指令、执行指令。RISC即精简指令集计算机(Reduced Instruction Set Computer)是目前CPU主流架构之一。下面就详细的介绍一个简化的RISC_CPU的实现过程。
RISC_CPUZ主要由11个基本部件组成,分别为:时钟发生器、指令寄存器、累加器、ALU算术逻辑运算单元、数据控制器、状态控制器、程序计数器、地址多路器、地址译码器、程序存储器(ROM)、数据存储器(RAM)组成。其中:

1. 时钟模块

为整个CPU系统提供统一的时钟资源。其中fetch为clk1的八分频,用于整个CPU系统工作状态的控制。指令周期为8个CLK1时钟组成,其中当fetch为高电平时CPU系统取指令,分析指令;当fetch为低电平时为执行指令操作。时钟模块采用状态机设计。
具体的指令步骤如下:
第0个时钟:指令寄存器寄存由ROM送来的高八位指令代码;
第1个时钟:指令寄存器寄存由ROM送来的低八位指令代码;
第2个时钟:空操作;
第3个时钟:PC指向下一条指令;
第4个时钟:根据16位指令数据,进行相关的运算;
第5个时钟:根据16位指令数据,继续运算;
第6个时钟:空操作;
第7个时钟:准备下一条指令的读取;
在这里插入图片描述
代码如下:
// -----------------------------------------------------------------------------
// Copyright © 2014-2021 All rights reserved
// -----------------------------------------------------------------------------
// Author : jishengli jari (jsl) jishengli_jari@163.com
// File : clk_gen.v
// Create : 2021-07-01 13:29:33
// Revise : 2021-07-05 13:46:37
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
`timescale 1ns / 1ps
module clk_gen(
input wire clk,
input wire rst,
output wire clk1,
output wire clk2,
output wire clk4,
output wire fetch, // 时钟clk的8分频信号。
output wire alu_clk
);
parameter S1 = 4’b0001;
parameter S2 = 4’b0010;
parameter S3 = 4’b0011;
parameter S4 = 4’b0100;
parameter S5 = 4’b0101;
parameter S6 = 4’b0110;
parameter S7 = 4’b0111;
parameter S8 = 4’b1000;

reg [3:0] status;
reg clk2_r;
reg clk4_r;
reg fetch_r;
reg alu_clk_r;

assign clk1 = ~clk;
assign clk2 = clk2_r;
assign clk4 = clk4_r;
assign fetch = fetch_r;
assign alu_clk = alu_clk_r;

always @(posedge clk or negedge rst) begin
if (!rst) begin
status <= S1;
end
else begin
case (status)
S1:begin status <= S2; end
S2:begin status <= S3; end
S3:begin status <= S4; end
S4:begin status <= S5; end
S5:begin status <= S6; end
S6:begin status <= S7; end
S7:begin status <= S8; end
S8:begin status <= S1; end
default:begin status <= S1; end
endcase
end
end

always @(negedge clk or negedge rst) begin
if (!rst) begin
// reset
clk2_r <= 'd0;
end
else begin
clk2_r <= ~clk2_r;
end
end
always @(negedge clk or negedge rst) begin
if (!rst) begin
// reset
clk4_r <= 'd1;
end
else if (status == S2 || status == S4 || status == S6 || status == S8)begin
clk4_r <= ~clk4_r;
end
end
always @(negedge clk or negedge rst) begin
if (!rst) begin
// reset
fetch_r <= 'd0;
end
else if (status == S4 || status == S8)begin
fetch_r <= ~fetch_r;
end
end
always @(negedge clk or negedge rst) begin
if (!rst) begin
// reset
alu_clk_r <= 'd0;
end
else if (status == S1)begin
alu_clk_r <= ~alu_clk_r;
end
else begin
alu_clk_r <= 'd0;
end
end
endmodule

2. 指令寄存器模块

存储由ROM送来的低八位和高八位操作指令,其中组合的16位操作指令中高三位为操作符,低13位为相应的地址数据。

图2 指令寄存器模块原理图
代码如下:
`timescale 1ns / 1ps
module register(
input wire [7:0] data,
input wire ena,
input wire clk1,
input wire rst,
output wire [15:0] opc_iraddr
);
reg [15:0] opc_iraddr_r;
reg status;
assign opc_iraddr = opc_iraddr_r;
always @(posedge clk1 or negedge rst) begin
if (!rst) begin
// reset
status <= 'd0;
end
else if (ena == 'd1) begin
status <= ~status;
end
end
always @(posedge clk1 or negedge rst) begin
if (!rst) begin
// reset
opc_iraddr_r <= 'd0;
end
else if (ena == 'd1 && status == 'd0) begin
opc_iraddr_r[15:8] <= data;
end
else if (ena == 'd1 && status == 'd1) begin
opc_iraddr_r[7:0] <= data;
end
end
endmodule

3. 累加器模块

用于存放当前的结果,它也是双目运算中的一个数据来源。

图3 累加器模块原理图

代码如下:
`timescale 1ns / 1ps
module accum(
input wire clk1,
input wire rst,
input wire ena,
input wire [7:0] data,
output wire [7:0] accum
);
reg [7:0] accum_r;
assign accum = accum_r;
always @(posedge clk1 or negedge rst) begin
if (!rst) begin
// reset
accum_r <= 'd0;
end
else if (ena == 'd1) begin
accum_r <= data;
end
end
endmodule

4. 算术运算器模块

根据相应的操作码完成操作运算。

图4 算术运算模块原理图

代码如下:
`timescale 1ns / 1ps
module alu(
input wire alu_clk,
input wire [2:0] opcode,
input wire [7:0] data,
input wire [7:0] accum,
output wire zero,
output wire [7:0] alu_out
);
reg [7:0] alu_out_r;
assign alu_out = alu_out_r;

parameter HLT = 3’b000, //停机操作
SKZ = 3’b001, //若为0跳过下一条语句。该操作先判断当前alu中的结果是否为0,若是0则跳过下一条语句,否则继续执行
ADD = 3’b010, //加运算
ANDD = 3’b011, //与运算
XORR = 3’b100, //异或运算
LDA = 3’b101, //读数据 指令
STO = 3’b110, // 写数据
JMP = 3’b111; //无条件跳转语句
assign zero = !accum; //“!”表示逻辑取反,“~”表示按位取反

always @(posedge alu_clk) begin
case(opcode)
HLT,SKZ,STO,JMP:begin alu_out_r <= accum; end
ADD: begin alu_out_r <= accum + data; end
ANDD: begin alu_out_r <= data & accum; end
XORR: begin alu_out_r <= data ^ accum; end
LDA: begin alu_out_r <= data; end
default:begin alu_out_r <=8’bxxxx_xxxx; end
endcase

end
endmodule

5. 数据控制模块

用于控制控制数据往RAM中进行写数据操作。

图5 数据控制模块原理图

代码如下:
`timescale 1ns / 1ps
module datactl(
input wire [7:0] in,
input wire data_ena,
output wire [7:0] data
);
assign data = (data_ena)? in : 8’bzzzz_zzzz;

endmodule

6. 地址多路器模块

用于判断当前的地址是RAM地址(数据寄存器地址)还是ROM地址(指令寄存器地址)并输出。

图6 地址多路器模块原理图

代码如下:
`timescale 1ns / 1ps
module adr(
input wire [12:0] ir_addr,
input wire [12:0] pc_addr,
input wire fetch,
output wire [12:0] addr
);
assign addr = fetch ? pc_addr : ir_addr;//pc_addr 程序计数地址;ir_addr 数据/端口地址
endmodule

7. 程序计数器模块

用于提供指令地址,以便读取指令。相当于一个地址指针,当前指令执行完毕后便自动指向下一条指令的地址。

图7 程序计数器模块原理图

代码如下:
`timescale 1ns / 1ps
module counter(
input wire clock,
input wire rst,
input wire [12:0] ir_addr,
input wire load, // 如果正在执行跳转语句,这时cpu状态控制器将会输出load指令。 每条指令执行完需要两个时钟。pc_addr已被增2 指向下一条指令
output wire [12:0] pc_addr
);
reg [12:0] pc_addr_r;
assign pc_addr = pc_addr_r;
always @(posedge clock or negedge rst) begin
if (!rst) begin
// reset
pc_addr_r <= 'd0;
end
else if (load == 'd1) begin
pc_addr_r <= ir_addr;
end
else begin
pc_addr_r <= pc_addr_r + 1’b1;
end
end
endmodule

8. 状态控制器模块

维持整个cpu系统正常的工作。

图8-1 状态控制器时钟模块原理图

图8-2 状态控制器模块原理图

代码1如下:
`timescale 1ns / 1ps
module machinectl(
input wire rst,
input wire fetch,
output wire ena
);
reg ena_r;
assign ena = ena_r;
always @(posedge fetch or negedge rst) begin
if (!rst) begin
// reset
ena_r <= 'd0;
end
else begin
ena_r <= 'd1;
end
end

endmodule
代码2如下:
`timescale 1ns / 1ps

module machine(
input wire clk1,
input wire zero,
input wire ena,
input wire [2:0] opcode,
output wire inc_pc,
output wire load_acc,
output wire load_pc,
output wire rd,
output wire wr,
output wire load_ir,
output wire datactl_ena,
output wire halt
);

reg inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r;
assign inc_pc = inc_pc_r;
assign load_acc = load_acc_r;
assign load_pc = load_pc_r;
assign rd = rd_r;
assign wr = wr_r;
assign load_ir = load_ir_r;
assign datactl_ena = datactl_ena_r;
assign halt = halt_r;

reg [2:0] status;

parameter HLT = 3’b000,
SKZ = 3’b001,
ADD = 3’b010,
ANDD = 3’b011,
XORR = 3’b100,
LDA = 3’b101,
STO = 3’b110,
JMP = 3’b111;

always @(posedge clk1) begin
if (!ena) begin
// reset
status <= 3’d0;
{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8’d0; // 八位指令
end
else begin
ctl_cycle;
end
end

task ctl_cycle;begin

	case(status)
		3'b000 : 
			begin
				{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0001_0100;
				status <= 3'b001; 
			end
		3'b001 : 
			begin
				{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b1001_0100; 
				status <= 3'b010; 
			end
		3'b010 :
			begin
				{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0000_0000; 
				status <= 3'b011; 
			end
		3'b011 :  //分析指令从这里开始
			begin
				if (opcode == HLT)  //暂停指令
				begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b1000_0001;  
				end
				else begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b1000_0001;
				end
				status <= 3'b100;
			end
		3'b100 :   //fetch oprand
			begin
				if(opcode == JMP)
				begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0010_0000; 
				end
				else if (opcode == ADD || opcode == ANDD || opcode == XORR || opcode == LDA) begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0001_0000;
				end
				else if (opcode == STO) begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0000_0010;
				end
				else begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0000_0000;
				end
				status <= 3'b101;
			end
		3'b101 :   // operation
			begin
				if(opcode == ADD || opcode == ANDD || opcode == XORR || opcode == LDA)
				begin //过了一个时钟后与累加器的内容进行运算
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0101_0000;  
				end
				else if (opcode == SKZ && zero == 1) begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b1000_0000;
				end
				else if (opcode == JMP) begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b1010_0000;
				end
				else if (opcode == STO) begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0000_1010;
				end
				else begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0000_0000;
				end
				status <= 3'b100;
			end				
		3'b110 : //idle
			begin
				if(opcode == STO)
				begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0000_0010; 
					 
				end
				else if (opcode == ADD || opcode == ANDD || opcode == XORR || opcode == LDA) begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0001_0000;
				end
				else begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0000_0000;
				end
				status <= 3'b111;
			end				
		3'b111 : 
			begin
				if(opcode == SKZ && zero == 1)
				begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b1000_0000; 
					status <= 3'b100; 
				end
				else begin
					{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0000_0000;
				end
				status <= 3'b000;
			end				
		default: 
			begin
				{inc_pc_r,load_acc_r,load_pc_r,rd_r,wr_r,load_ir_r,datactl_ena_r,halt_r} <= 8'b0000_0000; 
				status <= 3'b000; 
			end
	endcase
end

endtask

endmodule

9. 地址译码器模块

根据地址多路器送来的地址进行判断选通RAM或者ROM,以供取操作数据或者取指令。

图9 地址译码器模块原理图

代码如下:
`timescale 1ns / 1ps
module addr_decode(
input wire [12:0] addr,
output wire ram_sel,
output wire rom_sel
);

reg ram_sel_r;
reg rom_sel_r;

assign ram_sel = ram_sel_r;
assign rom_sel = rom_sel_r;

always @(addr) begin
casex(addr)
13’b1_1xxx_xxxx_xxxx: begin {rom_sel_r,ram_sel_r} <= 2’b01; end
13’b0_xxxx_xxxx_xxxx: begin {rom_sel_r,ram_sel_r} <= 2’b10; end
13’b1_0xxx_xxxx_xxxx: begin {rom_sel_r,ram_sel_r} <= 2’b10; end
default : begin {rom_sel_r,ram_sel_r} <= 2’bzz; end
endcase
end
endmodule

10. RAM模块

为整个CPU系统提供数据的存储。用于操作数据的读写操作。

图10 RAM模块原理图

代码如下:
`timescale 1ns / 1ps
//装载数据,可读可写
module ram(
inout wire [7:0] data,
input wire [9:0] addr,
input wire ena,
input wire read,
input wire write

);

reg [7:0] ram_r [10’h3ff:0]; // 1024byte//

assign data = (read && ena)? ram_r[addr]:8’hzz;

always @(write) begin
ram_r[addr] <= data;
end
endmodule

11. ROM模块

为整个CPU系统提供指令数据的存储。

图11 ROM模块原理图

代码如下:
`timescale 1ns / 1ps
//装载程序可读不可写
module rom(
input wire [12:0] addr,
input wire read,
input wire ena,
output wire [7:0] data
);
reg [7:0] rom_r [13’h1fff:0]; // 8kb内存
assign data = (read && ena)?rom_r[addr] : 8’bzzzz_zzzz;
endmodule

二、 RISC的top模块及各个模块的连接说明

1. top模块

对以上11个模块进行封装和内部连线。

图12-1 top模块总原理图

图12-2 top模块详细原理图

代码如下:
`timescale 1ns / 1ps
module top_risc(
input wire clk,
input wire reset,
output wire halt,
output wire rd,
output wire wr,
output wire [12:0] addr,
output wire [7:0] data
);
wire clk1;
wire clk2;
wire clk4;
wire fetch;
wire alu_clk;
wire load_ir;
wire load_pc;
wire load_acc;
wire [2:0] opcode;
wire [12:0] ir_addr;
wire [7:0] alu_out;
wire [7:0] accum;
wire [7:0] zero;
wire datactl_ena;
wire [12:0] pc_addr;
wire ram_sel;
wire rom_sel;
wire inc_pc;
wire ena;
clk_gen clk_gen_inst (
.clk (clk),
.rst (reset),
.clk1 (clk1),
.clk2 (clk2),
.clk4 (clk4),
.fetch (fetch),
.alu_clk (alu_clk)
);

register register_inst (.data(data), .ena(load_ir), .clk1(clk1), .rst(reset), .opc_iraddr({opcode,ir_addr}));

accum accum_inst (.clk1(clk1), .rst(reset), .ena(load_acc), .data(alu_out), .accum(accum));

alu alu_inst (
		.alu_clk (alu_clk),
		.opcode  (opcode),
		.data    (data),
		.accum   (accum),
		.zero    (zero),
		.alu_out (alu_out)
	);

datactl datactl_inst (.in(alu_out), .data_ena(datactl_ena), .data(data));

adr adr_inst (.ir_addr(ir_addr), .pc_addr(pc_addr), .fetch(fetch), .addr(addr));

addr_decode addr_decode_inst (.addr(addr), .ram_sel(ram_sel), .rom_sel(rom_sel));

ram ram_inst (.data(alu_out), .addr(addr[9:0]), .ena(ram_sel), .read(rd), .write(wr));

rom rom_inst (.addr(addr), .read(rd), .ena(rom_sel), .data(data));

counter counter_inst (.clock(inc_pc), .rst(reset), .ir_addr(ir_addr), .load(load_pc), .pc_addr(pc_addr));

machinectl machinectl_inst (.rst(reset), .fetch(fetch), .ena(ena));

machine machine_inst (
		.clk1        (clk1),
		.zero        (zero),
		.ena         (ena),
		.opcode      (opcode),
		.inc_pc      (inc_pc),
		.load_acc    (load_acc),
		.load_pc     (load_pc),
		.rd          (rd),
		.wr          (wr),
		.load_ir     (load_ir),
		.datactl_ena (datactl_ena),
		.halt        (halt)
	);

endmodule

三、 RISC的仿真(Simulation)代码设计

1. 仿真模块

用于对整个系统进行仿真。
代码如下:
// -----------------------------------------------------------------------------
// Copyright © 2014-2021 All rights reserved
// -----------------------------------------------------------------------------
// Author : jishengli jari (jsl) jishengli_jari@163.com
// File : top_risc_tb.v
// Create : 2021-07-07 12:51:44
// Revise : 2021-07-12 11:13:46
// Editor : sublime text3, tab size (4)
// -----------------------------------------------------------------------------
timescale 1ns / 1ps //include “top_risc.v”
`define period 100
module top_risc_tb ();

// clock
reg clk;
// synchronous reset
reg reset;
// (*NOTE*) replace reset, clock, others
wire          halt;
wire          rd;
wire          wr;
wire    [12:0]addr;
wire    [7:0] data;
reg    [(3*8):0] mnemonic;
integer   test;
reg    [12:0] PC_ADDR;
reg    [12:0] IR_ADDR;
top_risc top_risc_inst
	(
		.clk   (clk),
		.reset (reset),
		.halt  (halt),
		.rd    (rd),
		.wr    (wr),
		.addr  (addr),
		.data  (data)
	);

initial begin
	// do something
	$timeformat(-9,1,"ns",12);
	display_debug_message;
	sys_reset;
	test1;
	$stop;
	test2;
	$stop;
	test3;
	$stop;

end

task display_debug_message;
begin
$display("\n**************************");
$display("* THE FOLLOWING DEBUG TASK ARE AVAILABLE:");
$display("
“test1;“to load the 1st diagnostic program”);
$display(”“test2;“to load the 2nd diagnostic program”);
$display(”
“test3;“to load the Fibonacci program”);
$display(”**************************\n");
end
endtask

task test1;
begin
test = 0;
disable MONITOR;
$readmemb(“test1.pro”,top_risc_inst.rom_inst.rom_r);
$display(“rom loaded successfully”);
$readmemb(“test1.dat”,top_risc_inst.ram_inst.ram_r);
$display(“ram loaded successfully”);
#1 test =1;
#14800;
sys_reset;
end
endtask

task test2;
begin
test = 0;
disable MONITOR;
$readmemb(“test2.pro”,top_risc_inst.rom_inst.rom_r);
$display(“rom loaded successfully”);
$readmemb(“test2.dat”,top_risc_inst.ram_inst.ram_r);
$display(“ram loaded successfully”);
#1 test =2;
#11600;
sys_reset;
end
endtask

task test3;
begin
test = 0;
disable MONITOR;
$readmemb(“test3.pro”,top_risc_inst.rom_inst.rom_r);
$display(“rom loaded successfully”);
$readmemb(“test3.dat”,top_risc_inst.ram_inst.ram_r);
$display(“ram loaded successfully”);
#1 test =3;
#94000;
sys_reset;
end
endtask

task sys_reset;
begin
reset <= 'd1;
#(period*0.7) reset <= 'd0; #(1.5*period) reset <= 'd1;
end
endtask

always @(test)
begin:MONITOR
case(test)
1:begin // display result when running test 1
$display("\n*** RUNNING CPU test1 – The Basic CPU diagnostic Program ");
$display("\n TIME PC INSTR ADDR DATA");
$display("-------------------------------------");
while(test == 1)
@(top_risc_inst.adr_inst.pc_addr)
if((top_risc_inst.adr_inst.pc_addr%2 == 1) && (top_risc_inst.adr_inst.fetch == 1))
begin
#60 PC_ADDR <= top_risc_inst.adr_inst.pc_addr -1;
IR_ADDR <= top_risc_inst.adr_inst.ir_addr;
#340 s t r o b e ( " strobe("%t %h %s %h %h", strobe("time,PC_ADDR,mnemonic,IR_ADDR,data);
end
end
2:begin
$display("\n
RUNNING CPU test1 – The Advanced CPU diagnostic Program ");
$display("\n TIME PC INSTR ADDR DATA");
$display("-------------------------------------");
while(test == 2)
@(top_risc_inst.adr_inst.pc_addr)
if((top_risc_inst.adr_inst.pc_addr%2 == 1) && (top_risc_inst.adr_inst.fetch == 1))
begin
#60 PC_ADDR <= top_risc_inst.adr_inst.pc_addr -1;
IR_ADDR <= top_risc_inst.adr_inst.ir_addr;
#340 s t r o b e ( " strobe("%t %h %s %h %h", strobe("time,PC_ADDR,mnemonic,IR_ADDR,data);
end
end
3:begin
$display("\n
RUNNING CPU test3 – An Executable Program ***");
$display("\n This program should calculate the Fibonacci");
$display("\n Time Fibonacci Number");
$display("-------------------------------------");
while(test == 3)
begin
wait(top_risc_inst.alu_inst.opcode == 3’h1)
s t r o b e ( " strobe("%t %d", strobe("time,top_risc_inst.ram_inst.ram_r[10’h2]);
wait(top_risc_inst.alu_inst.opcode !=3’h1);
end
end
default:begin

	end
	endcase

end

always @(posedge halt) begin
#500
$display("\n ************************");
$display("
A HALT INSTRUCTION WAS PROCESSED !!! **");
$display("****************************\n");
end
always #(`period/2) clk = ~clk;
always @(top_risc_inst.alu_inst.opcode)
case(top_risc_inst.alu_inst.opcode)
3’b000 :mnemonic = “HLT”;
3’h1 :mnemonic = “SKZ”;
3’h2 :mnemonic = “ADD”;
3’h3 :mnemonic = “AND”;
3’h4 :mnemonic = “XOR”;
3’h5 :mnemonic = “LDA”;
3’h6 :mnemonic = “STO”;
3’h7 :mnemonic = “JMP”;
default:mnemonic = “???”;
endcase
endmodule

2. 仿真过程中所需要的指令和数据文件

代码如下:
test1.dat:
@00 //address statement at ram
00000000 //1800 data_1: //constant 00(hex)
11111111 //1801 data_2: //constant ff(hex)
10101010 //1802 temp: //varirable - starts with aa(hex)
test1.pro:
/* ***************************************************************
test1 程序是用于验证risc_cpu逻辑功能的机器代码,汇编语言编译
本汇编程序用于测试risc_cpu的基本指令集,如果risc_cpu的各条指令执行正确,
它应在地址为2E(hex)处,执行HLT时停止运行。如果该程序在任何其他地址暂停
运行,则必有一条指令运行出错。
@后面时十六进制数表示存储器的地址,以下二进制数为机器码。
机器码 地址 汇编助记符号 注释

****************************************************************/
@00 //address statement
111_00000 //00 begin: jmp tst_jmp
0011_1100
000_00000 //02 HLT: JMP did not work at all
0000_0000
000_00000 //04
0000_0000
101_11000 //06 JMP_OK: LDA DATA_1
0000_0000
001_00000 //08 SKZ
0000_0000
000_00000 //0a hlt //skz or lda did not work
0000_0000
101_11000 //0c lda data_2
0000_0001
001_00000 //0e skz
0000_0000
111_00000 //10 jmp skz_ok
0001_0100
000_00000 //12 hlt //skz or lda did not work
0000_0000
110_11000 //14 skz_ok: sto temp //store non - zero value in temp
0000_0010
101_11000 //16 lda data_1
0000_0000
110_11000 //18 sto temp //store zero value in temp
0000_0010
101_11000 //1a lda temp
0000_0010
001_00000 //1c skz //check to see if sto worked
0000_0000
000_00000 //le hlt //sto did not work
0000_0000
100_11000 //20 xor data_2
0000_0001
001_00000 //22 skz //check to see if xor worked
0000_0000
111_00000 //24 jmp xor_ok
0010_1000
000_00000 //26 hlt //xor did not work at all
0000_0000
100_11000 //28 xor_ok: xor data_2
0000_0001
001_00000 //2a skz
0000_0000
000_00000 //2c hlt //xor did not switch all bits
0000_0000
000_00000 //2e end: hlt //congratulations-test1 passed!
0000_0000
111_00000 //30 jmp begin //run test again
0000_0000
@3c
111_00000 //3c tst_jmp: jmp jmp_ok
0000_0110
000_00000 //3e hlt //jmp is borken
test2.dat:
@00 //address statement at ram
00000001 //1800 data_1: //constant 01(hex)
10101010 //1801 data_2: //constant aa(hex)
11111111 //1802 data_3: //constant ff(hex)
00000000 //1803 temp:
test2.pro:
/
***************************************************************
test2 程序是用于验证risc_cpu逻辑功能的机器代码,汇编语言编译
本汇编程序用于测试risc_cpu的高级指令集,如果risc_cpu的各条指令执行正确,
它应在地址为20(hex)处,执行HLT时停止运行。如果该程序在任何其他地址暂停
运行,则必有一条指令运行出错。
@后面时十六进制数表示存储器的地址,以下二进制数为机器码。
机器码 地址 汇编助记符号 注释

****************************************************************/
@00 //address statement
101_11000 //00 begin: lda data_2
0000_0001
011_11000 //02 and data_3
0000_0010
100_11000 //04 xor data_2
0000_0001
001_00000 //06 skz
0000_0000
000_00000 //08 hlt //and does not work
0000_0000
010_11000 //0a add data_1
0000_0000
001_00000 //0c skz
0000_0000
111_00000 //0e jmp add_ok
0001_0010
000_00000 //10 hlt // add does not work
0000_0000
100_11000 //12 add_ok: xor data_3
0000_0010
010_11000 //14 add data_1 //ff plus 1 makes -1
0000_0000
110_11000 //16 sto temp
0000_0011
101_11000 //18 lda data_1
0000_0000
010_11000 //1a add temp // -1 plus 1 should make zero
0000_0011
001_00000 //1c skz
0000_0000
000_00000 //le hlt //add did not work
0000_0000
000_00000 //20 end: hlt // congratutions – test2 passed!
0000_0000
111_00000 //22 jmp begin //run test again
0000_0000
test3.dat:
@00 //address statement at ram
00000001 //1800 fan1: //data storage for 1st fib. no.
00000000 //1801 fan2: //data storage for 2nd fib. no.
00000000 //1802 temp: //temproray data storage
10010000 //1803 limit: //max value to calculate 144(dec)
test3.pro:
/
***************************************************************
test3 程序是一个计算0-144的fibonacci序列的程序,汇编语言编译
@后面时十六进制数表示存储器的地址,以下二进制数为机器码。
机器码 地址 汇编助记符号 注释

*****************************************************************/
@00 //address statement
101_11000 //00 loop: lda fan2 //load value in fan2 into accum
0000_0001
110_11000 //02 sto temp //store accumulator in temp
0000_0010
010_11000 //04 add fan1 //add value in fan1 to accumulator
0000_0000
110_11000 //06 sto fan2 //store result in fan2
0000_0001
101_11000 //08 vlda temp //load temp into the accumulator
0000_0010
110_11000 //0a sto fan1 //store accumulator in fan1
0000_0000
100_11000 //0c xor limit //compare accumulator to limit
0000_0011
001_00000 //0e skz //if accum =0 , skip to done
0000_0000
111_00000 //10 jmp loop //jump to address of loop
0000_0000
000_00000 //12 done: hlt //end of program
0000_0000

四、 特别说明

本例程适合FPGA新手去锻炼codeing 能力,最后的仿真波形如下图所示,具体细节可以自己去做实验仿真有问题可以留言探讨大家一起进步。本例程参考夏宇文老师的Verilog数字系统设计一书。
在这里插入图片描述

  • 6
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
EDA(电子设计自动化)技术是数字电路设计中必不可少的一环,通常包含了设计部分和验证部分,主要是为了提高设计效率和设计成功率。RISC(精简指令集计算机)CPU是一种非常流行的微处理器架构,在当前的大多数芯片中都有应用。因此,基于EDA技术的RISC CPU设计实验,对于电子工程专业的学生来说,是一项非常重要的任务。 这个实验需要的硬件和软件工具包括:适当的电平转换器(将FPGA的输出转换成电平),串行EEPROM模块,Xilinx ISE设计套件等。在这个设计实验中,学生们需要完成以下几个关键阶段:选择CPU架构和指令集,设计ALU(算术逻辑单元),设置计算机指令集,实现总线和控制逻辑等。 首先,选择一个适合该实验的CPU架构是非常重要的。学生们可以选择一些主流的RISC CPU架构作为参照,比如上古版本的ARM(如ARM7),RISC-V等。其次,根据所选的架构设计ALU的具体功能。ALU主要用于执行各种算术和逻辑操作,包括加、减、与、或、异或等等。此外,还需要根据指令集和CPU格式来设计ALU的输出信号和控制逻辑。 在设计ALU之后,需要设置计算机的指令集,这个指令集是CPU能够理解的命令集。在这个过程中,学生们需要确定操作码和操作数格式,以及每个指令的操作类型(如运算、移位、跳转等)。在确定指令集之后,需要设计总线,总线的作用是连接并控制所有芯片之间的数据传输。 在CPU设计中,控制逻辑也是非常重要的。学生们需要设计各种控制器,包括时钟控制器、计数器、状态控制器等。这些控制器都有不同的作用,例如时钟控制器可以在指定的时钟周期内触发指定的操作,状态控制器可以决定系统状态以触发相应的操作。控制逻辑的设计对于CPU的性能有着至关重要的作用。 综上所述,基于EDA技术的RISC CPU设计实验是不可或缺的一项实践任务。通过完成这个设计,可以让学生们更好地理解数字电路和计算机组成原理,并提高他们处理复杂设计问题的能力。对于未来要从事硬件设计工作的学生来说,这是一项非常有价值的实验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值