【单周期CPU】LoongArch | LA32R | 二选一控制器MUX | 数据通路

news2024/12/23 19:18:45

前言:本章内容主要是演示在vivado下利用Verilog语言进行单周期简易CPU的设计。一步一步自己实现模型机的设计。本章先介绍单周期简易CPU中数据通路的设计。

💻环境:一台内存4GB以上,装有64位Windows操作系统和Vivado 2017.4以上版本软件的PC机。

💎本章所采用的指令为LoongArch之LA32R版

目录

Ⅰ前置知识 

         0x00 二选一控制器

0x01 数据通路

Ⅱ Verilog实现 

0x00 二选一控制器

0x01 构建数据通路

Ⅲ 结果分析

0x00 思路一结果分析

0x01 思路二结果分析


Ⅰ前置知识 

0x00 二选一控制器

数据选择器是一种多路输入单路输出的组合逻辑电路,MUX可以实现在两个输入信号中选择一个作为输出信号的功能。它通常用于选择数据通路中的输入信号或控制信号。在数字电路设计中,MUX是一个非常基本的元件,也是其他复杂电路的基础。

 “2 选 1”数据选择器电路的输入/输出信号设计说明如图:

信号名称信号用途说明
输入信号a通道1数据输入,位宽为32位
b通道2数据输入,位宽为32位
s通道选择,位宽是1位
输出信号y输出所选通道的数值,位宽为32位

0x01 数据通路

把前两篇博客中所设计的基本部件:ALU、寄存器堆和存储器进行连接,搭建数据通路。

该模块的电路参考结构图如下:

图中模块和信号说明如下:

  • Registers模块:寄存器堆模块,信号及读写功能说明参见实验2。
  • ALU模块:算术逻辑运算单元模块,信号及读写功能说明参见实验1。
  • MUX模块:2选1模块。
  • DataRAM模块:RAM存储器,信号及读写功能说明参见实验2。
  • Ext模块:立即数扩展模块,信号及读写功能说明参见实验1。
  • Instr信号:32位输入信号,其值位指令的32位机器码。
  • clk信号:时钟信号,输入。
  • srcReg信号:MUX数据输入选择信号。根据不同指令,选择指令提供的rk或rd值作为寄存器堆Rb的输入。
  • ALUBsrc信号:MUX数据输入选择信号。根据不同指令,选择寄存器或立即数作为ALU的源操作数b。
  • MemToReg信号:MUX数据输入选择信号。根据不同指令,选择将ALU的运算结果或是DataRAM取出的值作为要存入寄存器堆的值。

Ⅱ Verilog实现 

0x00 二选一控制器

设计代码:

module mux(a,b,s,y);
input[31:0]a,b; input s; output[31:0]y; assign y = (!s)?a:b;
endmodule

由于该模块比较简单,且本篇文章重点在于构建数据通路,故不再给出仿真代码和仿真波形,感兴趣的读者可自行验证。

0x01 构建数据通路

利用Verilog HDL设计顶层电路模型,把前面实验设计的ALU、寄存器堆和存储器进行连接,搭建支持下表所示6条LA32R指令功能的数据通路。

指令

功能

说明

add.w rd,rj,rk

GR[rd]⟵GR[rj]+GR[rk]

加法

slt rd,rj,rk

if (GR[rj]<GR[rk]) GR[rd]⟵1 else GR[rd]⟵0

带符号数的大小比较

sltu rd,rj,rk

if (GR[rj]<GR[rk]) GR[rd]⟵1 else GR[rd]⟵0

无符号数的大小比较

lu12i.w rd,si20

GR[rd] ⟵si20 || 12’b0

GR[rd]的高20位为si20,低12位为0

st.w rd,rj,si12

Addr⟵GR[rj] + Signextend(si12) , M[Addr]⟵GR[rd]

把GR[rd]的值存入内存Addr单元,

ld.w rd,rj,si12

Addr⟵GR[rj] + Signextend(si12) , GR[rd] ⟵M[Addr]

从内存Addr单元取数,存入R[rd]

所用到的六条指令的龙芯架构32位精简版指令参考如下图:

本文提供两种思路:

1.在书写仿真文件时,将信号量给出。

2.在书写设计文件时,将信号量封装在设计文件中。

❓思考:

读者可以先思考这两种思路有什么各存在什么优缺点

🚩注:

数据通路所用到的模块(ALU,寄存器等)在本专栏的前两篇文章中均以给出,故此处不再赘述,但是构建数据通路时要注意将这几个模块写入设计文件中。

下列设计文件中只给出顶层设计代码。

需要的模块:

【单周期CPU】LoongArch | 32位寄存器DR | 32位的程序计数器PC | 通用寄存器堆Registers | 32位RAM存储器_流继承的博客-CSDN博客

【单周期CPU】LoongArch | 立即数扩展模块Ext | 32位算术逻辑运算单元(ALU)_流继承的博客-CSDN博客

首先根据指令,选择合适的控制信号: 

思路一:

设计代码:

module cpu (
input [31:0]Instr,
input clk, srcReg, ALUBSrc,MemWrEn, MemToReg,RegWr,
input [1:0]ExtOp, 
input [2:0]AluCtrl,
output [31:0]Result,
output zero) ;

wire [4:0]rb;
wire [31:0]aluA, aluB, regBusB, imm, addrDram, datainDram, dataoutDram, aluResult, MemToRegMux1;

MUX muxRb(.mux_out(rb),.mux_in1(Instr[14:10]),.mux_in2(Instr[4:0]),.sel(srcReg));
Registers registers(.busW(Result),.clk(clk),.RegWr(RegWr),
    .Ra(Instr[9:5]),.Rb(rb),.Rw(Instr[4:0]),
    .busA(aluA),.busB(regBusB));
li_ji_shu ext(.DataIn(Instr),.ExtOp(ExtOp),.DataOut(imm));
MUX muxAluB(.mux_out(aluB),.mux_in1(regBusB),.mux_in2(imm),.sel(ALUBSrc));
ALU alu(.a(aluA),.b(aluB),.op(AluCtrl),.AddResult(aluResult),.Zero(zero));
RAM dataRam(.clk(clk),.MemWrEn(MemWrEn),.addr(aluResult),.data_in(regBusB),.data_out(MemToRegMux1));
MUX muxResult(.mux_out(Result),.mux_in1(aluResult),.mux_in2(MemToRegMux1),.sel(MemToReg));
endmodule

仿真代码:

module sim_cpu();
    reg [31:0]Instr;
    reg clk, srcReg, ALUBSrc,MemWrEn, MemToReg,RegWr;
    reg [1:0]ExtOp;
    reg [2:0]AluCtrl;
    wire [31:0]Result;
    wire zero ;
    Cpuuu1(Instr,clk, srcReg, ALUBSrc,MemWrEn, MemToReg,RegWr,ExtOp,AluCtrl,Result,zero);
    initial clk = 0;
    always begin
        #20 clk = ~clk;
    end
    
    initial begin
Instr = 32'b000101_0_0000_0000_0000_0000_0001_00001; 

    srcReg=0; ExtOp=2'b10;ALUBSrc=1; AluCtrl=3'b111;MemWrEn=0;MemToReg=0; RegWr=1 ; #40; 
//    $stop;

Instr = 32'b000101_0_0000_0000_0000_0000_0010_00010;
 
    srcReg=0; ExtOp=2'b10;ALUBSrc=1; AluCtrl=3'b111;MemWrEn=0;MemToReg=0; RegWr=1 ; #40;
//    $stop;

    Instr = 32'b000101_0_1111_1111_1111_1111_1111_00011; 
    srcReg=0; ExtOp=2'b10;ALUBSrc=1; AluCtrl=3'b111;MemWrEn=0;MemToReg=0; RegWr=1 ; #40;
//    $stop;

    Instr = 32'b000101_0_1111_1111_1111_1111_1110_00100;
    srcReg=0; ExtOp=2'b10;ALUBSrc=1; AluCtrl=3'b111;MemWrEn=0;MemToReg=0; RegWr=1 ; #40;
    $stop;

    Instr = 32'b000000_0000_01_00000_00010_00001_00101;
    srcReg=0; ExtOp=2'b00;ALUBSrc=0; AluCtrl=3'b000;MemWrEn=0;MemToReg=0; RegWr=1 ; #40;
    $stop;
    end
endmodule

思路二: 

设计代码:

module cpu(Zero,busA,data_out,Instr,clk);
	input clk;
	input [31:0] Instr;
	output Zero;
	output [31:0] busA,data_out;

	
	wire [31:0] imm1,imm2,imm3,imm4,imm5,imm6,imm7,imm8;
	reg [1:0] ExtOp;
	reg SrcReg,RegWr,ALUBSrc,MemWrEn,MemToReg;
	reg [2:0] ALUCtrl;

always @ (*) begin
		if(Instr[29:27]==3'b000)
			case(Instr[17:15])
				3'b000:{SrcReg,RegWr,ALUBSrc,ALUCtrl,MemWrEn,MemToReg,ExtOp} = 10'b0100000000;
				3'b100:{SrcReg,RegWr,ALUBSrc,ALUCtrl,MemWrEn,MemToReg,ExtOp} = 10'b0101010000;
				3'b101:{SrcReg,RegWr,ALUBSrc,ALUCtrl,MemWrEn,MemToReg,ExtOp} = 10'b0101100000;
			endcase
		if(Instr[29:27]==3'b010)
			{SrcReg,RegWr,ALUBSrc,ALUCtrl,MemWrEn,MemToReg,ExtOp} = 10'b0111110010;
		if(Instr[29:27]==3'b101) begin
			if(Instr[24:22]==3'b110)
				{SrcReg,RegWr,ALUBSrc,ALUCtrl,MemWrEn,MemToReg,ExtOp} = 10'b1010001000;
			if(Instr[24:22]==3'b010)
				{SrcReg,RegWr,ALUBSrc,ALUCtrl,MemWrEn,MemToReg,ExtOp} = 10'b0110000100;
		end
	end

	assign busA = imm3;
	assign data_out = imm7;

	Ext M1(.Dataout(imm1),.DataIn(Instr),.Extop(ExtOp));
	MUX M2(.mux_out(imm2),.sel(SrcReg),.mux_in1(Instr[14:10]),.mux_in2(Instr[4:0]));
	Registers M3(.busA(imm3),.busB(imm4),.clk(clk),.RegWr(RegWr),.busW(imm8),.Rw(Instr[4:0]),.Ra(Instr[9:5]),.Rb(imm2));
	MUX M4(.mux_out(imm5),.sel(ALUBSrc),.mux_in1(imm4),.mux_in2(imm1));
	ALU M5(.Zero(Zero),.AddResult(imm6),.a(imm3),.b(imm5),.op(ALUCtrl));
	RAM M6(.data_out(imm7),.clk(clk),.MemWrEn(MemWrEn),.addr(imm6),.data_in(imm4));
	MUX M7(.mux_out(imm8),.sel(MemToReg),.mux_in1(imm6),.mux_in2(imm7));
endmodule

仿真代码:

module sim_cpu();
	reg clk;
	reg [31:0] Instr;
	wire Zero;
	wire [31:0] busA,data_out;
	cpu uu1(Zero,busA,data_out,Instr,clk);
	initial clk = 0;
 always begin
  Instr = 32'b0001010_00000000000000000001_00001;#100;//lu12i.w 存4'h1000入1号寄存器
   $stop;
  Instr = 32'b0001010_00000000000000000010_00010;#100;//lu12i.w 存4'h2000入2号寄存器
   $stop;
  Instr = 32'b00000000000100000_00001_00010_00011;#100;//add.w 1+2寄存器->3寄存器
   $stop;
  Instr = 32'b0001010_10000000000000000001_00100;#100;//lu12i.w 存4'h-1000入4号寄存器
   $stop;
  Instr = 32'b00000000000100100_00001_00100_00101;#100;//slt 带符号比较结果存入寄存器5
   $stop;
  Instr = 32'b00000000000100101_00001_00100_00110;#100;//sltu 无符号数比较,结果存入寄存器6
   $stop;
  Instr = 32'b0010100110_000000000001_00001_00010;#100;//st.w 2寄存器存入1寄存器
   $stop;
  Instr = 32'b0010100010_000000000001_00001_00111;#100;//ld.w
   $stop;
 end
 always #50 clk = ~clk;
endmodule  

Ⅲ 结果分析

0x00 思路一结果分析

 仿真结果:

阅读仿真波形,可得到如下的仿真结果数据。

 数据通路电路仿真测试输入信号初值仿真测试结果

序号

输入

输出

Instr

SrcReg

ExtOp

ALUBSrc

AluCtrl

MemWrEn

MemToReg

RegWr

Result

Zero

1

32’h14000021

0

2’b10

1

3’b111

0

0

1

32’h00001000

0

2

32’h14000042

0

2’b10

1

3’b111

0

0

1

32’h00002000

0

3

32’h15ffffe3

0

2’b10

1

3’b111

0

0

1

32’hfffff000

0

4

32’h15ffffc4

0

2’b10

1

3’b111

0

0

32’hffffe000

0

0x01 思路二结果分析

 结果展示:

根据寄存器中是否存入了指令里给出的值判断是否成功

阅读实验结果,可得到如下的数据。

数据通路电路仿真测试输入信号初值测试结果

序号

输入

结果

Instr

1

0001010_00000000000000000001_00001

把值存入寄存器1,寄存器1内的值为00001000

2

0001010_00000000000000000010_00010

把值存入寄存器2,寄存器2内的值为00002000

3

00000000000100000_00001_00010_00011

寄存器1和2的结果相加,存入寄存器3,寄存器3内的值为00003000

4

0001010_10000000000000000001_00100

把值存入寄存器4,寄存器4内的值为80001000

5

00000000000100100_00001_00100_00101

带符号比较结果存入寄存器5,值为00000001

6

00000000000100101_00001_00100_00110

无符号数比较,结果存入寄存器6,值为00000000

7

0010100110_000000000001_00001_00010

将值存入内存

8

0010100010_000000000001_00001_00111

将值从内存取出

 分析:

首先把值存入寄存器1,并检查寄存器1内的值是否为00001000;

再把值存入寄存器2,检查寄存器2内的值是否为00002000;

把寄存器1和2的结果相加,存入寄存器3,检查寄存器3内的值是否为00003000。

把值存入寄存器4,寄存器4内的值为80001000,对寄存器4和寄存器1的值分别进行无符号比较和有符号比较;

带符号比较结果存入寄存器5,值为00000001。

无符号数比较,结果存入寄存器6,值为00000000。

最后再实现将值存入内存和将值从内存取出。均成功实现。

🚩注:

使用ld.w rd,rj,si12指令的时,若ram中定义的单元数太小,则无法在寄存器中存入数据

根据本篇所书写的指令,可以改为:reg [31:0] mem [0:10000]

得到结果: 

📢PS:读者可自行在网上查阅 LoongArch之LA32R版 的相关资料,以便于对LA32R版的指令有进一步的了解。

END 


📝因为作者的能力有限,所以文章可能会存在一些错误和不准确之处,恳请大家指出!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/744510.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为OD机试真题 Java 实现【矩阵中非1的元素个数】【2023 B卷 200分】,附详细解题思路

目录 一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明先将[0,0]位置的值变为1。第一次同化&#xff1a;第二次同化&#xff1a; 大家好&#xff0c;我是哪吒。 一、题目描述 存在一个m*n的二维数组&#xff0c;其成员取…

小程序项目时间选择器用法

项目需求是要实现这种形式, 但是相信大家都试了各种插件,都不太合适,uView框架也不能满足自己的需要; 推荐使用:uview-ui-plus 基本上小程序遇到的单选多选 日期 省市区 都可以完美的实现,可以通过插件市场安装使用 但是要实现ui给的原型图 还需要做一下调整 弹性布局给两个选…

Linux查找关键字出现的位置

在Linux中&#xff0c;您可以使用以下命令来查找文件中关键字出现的位置&#xff1a; grep -rnw /path/to/search -e keyword其中&#xff1a; - -r 递归地搜索指定路径下的所有子目录。 - -n 显示匹配行的行号。 - -w 完整匹配单词&#xff0c;而不是部分匹配。 - /path/to/s…

pandas 重复数据处理详解

概要 重复值处理主要涉及两个部分&#xff0c;一个是找出重复值&#xff0c;第二个是删除重复值&#xff0c;也就是根据自己设定的条件进行删除操作。本次来介绍关于重复数据处理的几个常用方法。 定位重复值 对于重复值&#xff0c;我们首先需要查看这些重复值是什么样的形式…

LFU算法的详细介绍与实现

LRU 算法的淘汰策略是 Least Recently Used&#xff0c;也就是每次淘汰那些最久没被使用的数据&#xff1b;而 LFU 算法的淘汰策略是 Least Frequently Used&#xff0c;也就是每次淘汰那些使用次数最少的数据。 LRU 算法的核心数据结构是使用哈希链表 LinkedHashMap&#xff…

spring全家桶(一):如何创建springboot项目

一.如何创建springboot项目 1.通过官网网站创建项目&#xff1a;https://start.spring.io/ 2.eclipse通过插件Spring Tool Suite(sts)创建项目 3.idea默认已经有spring插件 二.程序入口 SpringBootApplication public class HelloApplication {public static void main(Strin…

Linux--获取当前进程的父进程PID(即PPID)

方法一&#xff1a;编程法 #include <sys/types.h>pid_t ppidgetppid(); 方法二&#xff1a;指令法 ps axj | head -1 && ps axj | grep 当前进程PID 注&#xff1a;你会发现&#xff0c;每次查看当前进程PID时&#xff0c;PID都不相同&#xff0c;但是它的P…

设计模式——原型模式

原型模式比较简单&#xff0c;本质就是将一个设置好一部分公共属性的对象进行克隆&#xff0c;产生出大量的对象&#xff0c;再对每个对象进行相应的个性化处理需要注意的是&#xff1a;对象克隆时&#xff0c;如果其成员变量中存在引用类型&#xff08;数组、引用对象等&#…

《人工智能.一种现代方法》原版精读思维导图-第二章

目录 书籍 相关 2. Intelligent Agents 2.1 Agents and Environments 2.2 Good Behavior: The Concept of Rationality 2.3 The Nature of Environments 2.4 The Structure of Agents summary 书籍 人工智能.一种现代方法 Artificial Intelligence. The Modern Appro…

基于LLM大模型开发Web App生成器

随着越来越多的代码生成模型公开可用&#xff0c;现在可以以我们以前无法想象的方式进行文本到网络甚至文本到应用程序。 本教程介绍了一种通过流式传输和渲染内容来生成 AI Web 内容的直接方法。 推荐&#xff1a;用 NSDT设计器 快速搭建可编程3D场景。 1、在 Node 应用程序中…

13 个最佳免费 PDF 编辑器清单

您正在寻找一款真正免费的 PDF 编辑器&#xff0c;不仅可以编辑和添加文本&#xff0c;还可以更改图像、添加您自己的图形、签署您的名字、填写表格等等&#xff1f;您来对地方了&#xff1a;我研究了这些类型的应用程序&#xff0c;以得出您正在寻找的内容的列表。 其中一些是…

element 表格套输入框

实现效果&#xff1a; 编辑&#xff1a; 查看&#xff1a;点击平台补贴展示弹窗 <el-table:data"tableData"border:header-cell-style"{background:#D7D7D7,color:#000}"style"width: 100%"row-dblclick"dbclick":cell-class-name…

c++中的时间处理(3)与sleep相关的时间函数

1、Sleep()函数 头文件&#xff1a; Windows下为&#xff1a;windows.h Linux下为&#xff1a;unistd.h 注意&#xff1a; &#xff08;1&#xff09;Sleep是区分大小写的&#xff0c;有的编译器是大写&#xff0c;有的是小写。 &#xff08;2&#xff09;Sleep括号里的时间&…

ELK中grok插件、mutate插件、multiline插件、date插件的相关配置

目录 grok 正则捕获插件 自定义表达式调用 mutate 数据修改插件 示例&#xff1a; ●将字段old_field重命名为new_field ●添加字段 ●将字段删除 ●将filedName1字段数据类型转换成string类型&#xff0c;filedName2字段数据类型转换成float类型 ●将filedName字段中…

Nginx调优和探活配置

Nginx基本参数优化 1 . worker_processes 1; # 指定 Nginx 要开启的进程数&#xff0c;结尾的数字就是进程的个数&#xff0c;可以为 auto。 这个参数调整的是 Nginx 服务的 worker 进程数&#xff0c;Nginx 有 Master 进程和 worker 进程之分&#xff0c;Master 为管理进程、真…

Web常见请求参数接收的总结

首先本文所展示的参数接收的总结&#xff0c;都是基于Spring Boot框架而言的&#xff0c;不是一般传统方式使用request对象来完成参数的接收 简单参数的接收 对于简单参数的接收&#xff0c;在Spring Boot框架中&#xff0c;在Controller类中设置对应的处理方式时&#xff0c;…

SpringMVC 中的数据验证如何使用 @Valid 注解

SpringMVC 中的数据验证如何使用 Valid 注解 在 Web 开发中&#xff0c;数据验证是一个非常重要的环节。它可以确保数据的合法性和正确性&#xff0c;保护系统不受到恶意攻击或用户误操作的影响。在 SpringMVC 中&#xff0c;我们可以使用 Valid 注解来实现数据验证。 Valid 注…

排序算法第二辑——选择排序

一&#xff0c;选择排序 选择排序算是简单排序中的渣渣&#xff0c;这种算法基本上是没有什么用处的。但是作为一个初学者&#xff0c;我又必须要会写这种算法。这种算法的实现实现思想和它的名字一样&#xff0c;就是在一个范围内选择最大或者最小的数据然后再交换数据实现排序…

山西电力市场日前价格预测【2023-07-13】

日前价格预测 预测明日&#xff08;2023-07-13&#xff09;山西电力市场全天平均日前电价为342.42元/MWh。其中&#xff0c;最高日前电价为403.93元/MWh&#xff0c;预计出现在00: 15。最低日前电价为282.08元/MWh&#xff0c;预计出现在24: 00。 价差方向预测 1&#xff1a;实…

为什么大部分游戏公司仍在坚持使用SVN?

游戏开发是一个复杂的过程&#xff0c;涉及多个开发人员的协作和大量的代码、艺术资源以及其他项目文件。版本控制系统在游戏开发中起着至关重要的作用。它提供了对项目代码和文件的管理、跟踪和协作能力&#xff0c;对于保持项目的稳定性、团队协作的顺畅性以及追踪项目历史和…