Giter Club home page Giter Club logo

ercesimips's Introduction

ercesiMIPS Processor Porject

Author: Meng Zhang ([email protected])

Author: Jianfeng An ([email protected])

Author: Danghui Wang ([email protected])

Date: 2017 April 9

Diagrams: ercesiMIPS wiki. If you have any questions, share it with us in our community chat room.

This repo has been put together to demonstrate a number of simple MIPS Processors written in Chisel.

Install Chisel (according to usb-bar chisel3)

  1. Install sbt Run the following from the terminal to install sbt in (Ubuntu-like) Linux.
echo "deb https://dl.bintray.com/sbt/debian /" | sudo tee -a /etc/apt/sources.list.d/sbt.list
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823
sudo apt-get update
sudo apt-get install sbt
  1. Install updated Verilator. Install dependencies:
sudo apt-get install git make autoconf g++ flex bison

Clone the Verilator repository:

git clone http://git.veripool.org/git/verilator

In the Verilator repository directory, check out a known good version (You can skip the step if the latest version is statable.):

cd verilator
git pull
git checkout verilator_3_886

In Verilator directory, build and install:

cd verilator
unset VERILATOR_ROOT
autoconf
./configure
make
sudo make install

Clone lab repository

Run the following from the terminal to clone lab resources.

cd ~/
git clone https://github.com/zavs/ercesiMIPS.git

Write your CPU and test it

A template ALU block has been presented in src/main/scala/SingleCycle/ALU.scala with the tester in src/test/scala/SingleCycle/ALUtests.scala. We borrowed the Launcher method from ucb-bar/chisel-tutorial. A new argument for shell runner needs to be name in Launcher.scala as the arguments of Map function:

object Launcher {
  val tests = Map(
        "ALU" -> { (backendName: String) =>
              Driver(() => new ALU(), backendName) {
                (c) => new ALUTests(c)
              }
            },
        "ALU11" -> { (backendName: String) =>
              Driver(() => new ALU11(), backendName) {
                (c) => new ALU11Tests(c)
              }
            }
        }
    )
}

In which, "ALU" is the argument for the shell runner, and ALU() is denoted the instance class of DUT in ALUTests(c). Just add new argument for you own block class.

To test the ALU module with ALUtests, just input the following command in ercesiMIPS root directory.

./run-single.sh ALU

If you still want to monitor its verification via waveform, just run this:

./run-single.bak.v.sh ALU

And the output .vcd file can be monitored in gtkwave program which could be install in Ubuntu-like Linux:

sudo apt-get install gtkwave

To test multicycle CPU with TopTests, just input the following command in ercesiMIPS root directory.

./run-multi.sh Top

If you still want to monitor multicycle's waveform, just run this:

./run-multi.bak.v.sh Top

ercesimips's People

Contributors

anjianfeng avatar leyuanniao avatar zavs avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

ercesimips's Issues

How to Debug Chisel Code

1. 如何调试

1.1 第一步:运行./run-single.sh Top使用C模型检查代码错误

该命令会将Chisel编译为C文件,然后gcc会将C文件编译为可执行文件并仿真。
一般要从第一个错误开始调试,因为后面的错误可能是前面的错误引起的。
所以要控制台输出中找到第一个错误位置,仔细找到是哪个文件出现的错误,看错误在第几行。
注意有的时候所报错误的.scala文件名并不是我们工程中有的文件名,这些文件是Chisel提供的,不用管这些文件错误,我们找到第一个是自己工程中文件名的错误。
找到错误所在文件的第几行具体位置,是解决错误的前提!
常见错误是拼写错误,Chisel是区分大小写的,所以信号名经常写错。
另一种常见错误是语法错误,请大家对照Tutorial手册中给出的正确语法。
在消除所有语法错误之后,你会得到下面的错误信息:
[info] [0.001] SEED 1494290977687
[info] [0.121] EXPECT AT 113 io_test_dm_out got 0 expected 1 FAIL
[info] [0.122] EXPECT AT 114 io_test_dm_out got 0 expected 2004 FAIL
[info] [0.123] EXPECT AT 115 io_test_dm_out got 0 expected 2004 FAIL
test Top Success: 100 tests passed in 121 cycles taking 0.153492 seconds
[info] [0.124] RAN 116 CYCLES FAILED FIRST AT CYCLE 113
================================================================================
Errors: 1: in the following commands
ercesiMIPS Top: test error occurred
================================================================================

这是因为我们在执行完"inst.s"中所有MIPS指令之后,会用expect检查DMM中存储的数据是否正确。
当然如果你一次把单周期MIPS的功能写对了,上面的错误就不会出现,否则你就开始第二步。

1.2 第二步:运行./run-bak.v.sh Top使用Verilog模型调试功能错误

这个命令会将Chisel文件转换为Verilog文件,并仿真产生一个Top.vcd文件。
Top.vcd在子目录test_run_dir中,test_run_dir中会有一个SingleCycle.LauncherXXX的子目录,如果有多个SingleCycle.Launcher目录则选择日期最近的一个查看。
使用gtkWave打开Top.vcd,将想观察的信号拖动到右面窗口,你就可以像使用Modelsim调试一样通过波形分析哪个信号的值出现了错误。


2. 如何避免Chisel3语法变化导致的警告?

2.1 声明寄存器

原语法:val pc = Reg(init = 0.U(30.W))
现语法:val pc = RegInit(0.U(30.W))(复位为0) 或val pc = Reg(UInt(30.W))(不复位)
使用原语法会报一个警告:

DatPath.scala:40: method apply in object Reg is deprecated: Use Reg(t), RegNext(next, [init]) or RegInit([t], init) instead
[warn] val pc = Reg(init = 0.U(30.W))
[warn] ^
[warn] one warning found

2.2 端口方向翻转

原语法:val dat = new DatToCtlIo().flip()
现语法:val dat = Flipped(new DatToCtlIo)
使用原语法会报一个警告:

[info] [0.002] Elaborating design...
[warn] CtlPath.scala:44: Flipped(Data) should be used over Data.flip in class SingleCycle.CtlPath
[info] [0.195] Done elaborating.
[info] [0.000] Elaborating design...
[warn] CtlPath.scala:44: Flipped(Data) should be used over Data.flip in class SingleCycle.CtlPath
[info] [0.016] Done elaborating.

3. 常见问题

3.1 scala文件中语句的描述顺序有何要求?

回顾一下Verilog中的always语句,一个module内部的always全部是并行执行的,这是硬件的根部特征,所以在Verilog中所有always语句的前后顺序是没有关系的。
Chisel也是类似的**,所以描述时不用考虑前后关系,Chisel会按照每行语句的信号赋值关系,正确产生符合数据流的电路。

3.2 设计文件与测试文件的区别?

一般工程中,设计文件放在main目录下,其语法必须使用Chisel语法格式撰写;
而测试文件放在test目录下,其语法使用Scala语法撰写。
所以两者的根本区别在于语法格式,因为设计文件是要生成电路的,所以必须用Chisel描述;而测试文件相当testbench,不用生成电路只是用来产生测试激励,所以用scala描述即可。Scala可以提供丰富的语句,让我们进行读写文件等复杂操作。
大家不用专门学习scala,因为我们的测试文件基本不用修改。

3.3 sbt启动下载时间过长怎么办?

由于下载网站在国外,大约下载200MB左右的内容,如果时间过长,可以换一个时间段尝试下载。
另外一种方法是从其他同学那里拷贝相关的文件,复制到自己的机器上。
主要是拷贝用户主目录下的两个隐藏文件夹.sbt和.ivy2。建议先压缩,拷贝后再解压缩。

3.4 明明修改了文件,但是还是显示以前的错误

这很可能是sbt运行目录错误,你修改了其他目录的代码。你可以用pwd命令查看当前目录是否正确,也在编辑器中确认编辑的文件目录是否正确。

3.5 "inst.s"文件放在哪个目录下?

应放在工程根目录,而不是src目录里面。
顺便说一下,"inst.s"可以用MARS汇编器导出,这样调试时应对比MARS单步执行结果看每条指令执行结果是否正确。

3.6 自己安装的Linux注意什么

用java -version检查一下java版本,我们的某些文件需要1.8的支持,如果是1.7版本,请用以下命令更新:
sudo apt-get install openjdk-8-jdk
另外可以把/etc/apt/sources.list文件修改为以下内容,以便加快更新速度:

deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial main restricted
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-updates main restricted
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial universe
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-updates universe
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial multiverse
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-updates multiverse
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-security main restricted
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-security universe
deb http://mirrors.tuna.tsinghua.edu.cn/ubuntu/ xenial-security multiverse

3.7 运行./run-single.sh Top报错“Permission denied”怎么办?

该脚本可能失去了执行权限,可以运行chmod +x ./run-single.sh修复。
也有可能是项目有些文件所属用户发生了变化,可以运行sudo chown -R cs11007 ./ercesiMIPS修复。

Why I have an error saying that '=' expected but ';' found

I have never use ';' in my code but the error says that ';' is found when '=' is expected
here is my code:
`//**************************************************************************
//--------------------------------------------------------------------------
// ercesiMIPS Single Cycle Processor Control path
//
// Junpeng
// version 0.1
//--------------------------------------------------------------------------
//**************************************************************************

package SingleCycle

import chisel3._
import chisel3.util._

class CtltoDatIo extends Bundle()
{
val nPC_MUX_sel = Output(Bool())
val RegWr = Output(Bool())
val RegDst = Output(Bool())
val funct = Output(UInr(5.W))
val Branch = Output(Bool())
val Jump = Output(Bool())
val ExtOp = Output(Bool())
val ALUctr = Output(UInt(2.W))
val ALUsrc = Output(Bool())
val MemtoReg = Output(Bool())
val Rd = Output(UInt(5.W))
val Rt = Output(UInt(5.W))
val Rs = Output(UInt(5.W))
val Imm16 = Output(UInt(16.W))
val Imm26 = Output(UInt(26.W))
}

class CPathIo extends Bundle()
{
val Inst = Input(UInt(32.W))
val boot = Input(Bool())
val MemWr = Output(Bool())
val MemRead = Output(Bool())
val valid = Output(Bool())
val ctl = new CtltoDatIo()
val dat = Flipped(new DatToCtlIo)
}

class CtlPath extends Module()
{
val io = IO(new CPathIo ())
// Add your code here. You can init all control signals first.
// Then decode these signals according to current instruction.
io.MemWr := 0.U
io.valid := 1.U
io.ctl.RegWr := false.B
val Instop := io.Inst(31,26)
io.ctl.Rs := io.Inst(25,21)
io.ctl.Rt := io.Inst(20,16)
io.ctl.Rd := io.Inst(15,11)
io.ctl.shamt := io.Inst(10,6)
val funct := io.Inst(5,0)
val io.ctl.Imm16 := io.Inst(15,0)
val io.ctl.Imm26 := io.Inst(25,0)

//decoder
when(Instop === B000000.U)//R-type
{
	io.ctl.ALUctr := B10.U
	io.ctl.Branch := false
	io.MemWr      := false
	io.MemRead    := false
	io.ctl.RegWr  := true
	io.ctl.MemtoReg := false
	io.ctl.ALUsrc := false
	io.ctl.RegDst := true
	io.ctl.funct  := funct
}.elsewhen(Instop === B100011.U)//lw
{
	io.ctl.ALUctr := B00.U
	io.ctl.Branch := false
	io.MemWr      := false
	io.MemRead    := true
	io.ctl.RegWr  := true
	io.ctl.MemtoReg := true
	io.ctl.ALUsrc := true
	io.ctl.RegDst := false
	//io.ctl.funct  := funct
}.elsewhen(Instop === 101011.B)//sw
{
	io.ctl.ALUctr := B00.U
	io.ctl.Branch := false
	io.MemWr      := true
	io.MemRead    := false
	io.ctl.RegWr  := false
	//io.ctl.MemtoReg := true
	io.ctl.ALUsrc := true
	//io.ctl.RegDst := false
	//io.ctl.funct  := funct
}
.elsewhen(Instop === B000010.U)//j
{
	io.ctl.Jump := true
	io.ctl.ALUctr := B11.U
}

}`
and the line 54,59,60,63 have this kind of error
I am mad at this error, somebody help!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

BEQ get wrong adrress because we may ignore the priority between "<<" and "+"

For inst.s, just the first one inst our teacher gave us.
The PC of beq is 0x20,and the ins is 0x12320001,so we will get a imm32 = 0x01 after decode and extend;
Next if beq is ok, we will get a new PC = PC + 4  + imm32<<2. So there is a trap in the priority between "<< " and "+":    the priority of "+" is higher than "<<".
If we write our code like this,we will get a wrong adress 0x94.( Now the PC is 0x20,io.pc_imm_32 is 0x01)
pc_b 		:= pc + 4  + io.pc_imm32 << 2
This is because the priority of "+" is higher than "<<",so we first get pc+4+io.pc_imm32, which is 0x20+0x04+0x01 = 0x25 ( for the demical is 37 ) ;then we let 0x25<<2,so we get a  wrong address 0x94(for the demical is 37*4=148).

But that's not we want. we just want imm32<<2 first, so we can just do , add a bracket like this:
pc_b := pc + 4 + (io.pc_imm32 << 2)

Why RegFile can't be written?

I just submit the issue of Yang.
I wrote a tester for regfile. And I know RegWr should be set to 1 to enable reg file writing. Here is the tester.

class RegFileTests(c: RegFile) extends PeekPokeTester(c) {
	var tbusa = 0
	var tbusb = 0
    
	def asUInt(InInt: Int) = (BigInt(InInt >>> 1) << 1) + (InInt & 1)

	for (i <- 0 until 32){
		val tbusw  = i + 1000
		val tra    = i
        val trb    = 0
        val trw    = i
        val tregwr = 1

		poke(c.io.BusW, tbusw)
		poke(c.io.RA, tra)
		poke(c.io.RB, trb)
		poke(c.io.RW, trw)
	    poke(c.io.RegWr, tregwr)

		step(1)

	}
	for(i <- 0 until 32){
		val tbusw  = i + 1000
		val tra    = i
        val trb    = 0
        val trw    = i
        val tregwr = 0
        
        poke(c.io.RA, tra)
		poke(c.io.RB, trb)
        expect(c.io.BusA, i + 1000)
        expect(c.io.BusB, 1000)

        if( i== 30 ){
	    	poke(c.io.RegWr, 1)
	    	println(peek(c.io.RegWr)toString)

        	poke(c.io.BusW, 666)
        	printf("i=30\n")
        	println(peek(c.io.BusW)toString)

			poke(c.io.RW, 15)
			println(peek(c.io.RW)toString)

        }
        else if( i == 31 ){
        	printf("i=31\n")

        	poke(c.io.RB, 15)
        	println(peek(c.io.BusB)toString)

        	expect(c.io.BusB, 666)
        }
        printf("tra: %d tbusw: %d\n",tra,tbusw)
	}
}

The output of this tester raises a error:

Circuit state created
SEED 1494282266689
tra: 0 tbusw: 1000
tra: 1 tbusw: 1001
tra: 2 tbusw: 1002
tra: 3 tbusw: 1003
tra: 4 tbusw: 1004
tra: 5 tbusw: 1005
tra: 6 tbusw: 1006
tra: 7 tbusw: 1007
tra: 8 tbusw: 1008
tra: 9 tbusw: 1009
tra: 10 tbusw: 1010
tra: 11 tbusw: 1011
tra: 12 tbusw: 1012
tra: 13 tbusw: 1013
tra: 14 tbusw: 1014
tra: 15 tbusw: 1015
tra: 16 tbusw: 1016
tra: 17 tbusw: 1017
tra: 18 tbusw: 1018
tra: 19 tbusw: 1019
tra: 20 tbusw: 1020
tra: 21 tbusw: 1021
tra: 22 tbusw: 1022
tra: 23 tbusw: 1023
tra: 24 tbusw: 1024
tra: 25 tbusw: 1025
tra: 26 tbusw: 1026
tra: 27 tbusw: 1027
tra: 28 tbusw: 1028
tra: 29 tbusw: 1029
i=30
666
15
1
tra: 30 tbusw: 1030
i=31
1015
EXPECT AT 32   io_BusB got 1015 expected 666 FAIL
tra: 31 tbusw: 1031
test RegFile Success: 64 tests passed in 37 cycles taking 0.285850 seconds
RAN 32 CYCLES FAILED FIRST AT CYCLE 32
================================================================================
Errors: 1: in the following commands
ercesiMIPS RegFile: test error occurred
================================================================================

Exception: sbt.TrapExitSecurityException thrown from the UncaughtExceptionHandler in thread "run-main-0"
java.lang.RuntimeException: Nonzero exit code: 1
	at scala.sys.package$.error(package.scala:27)
[trace] Stack trace suppressed: run last test:runMain for the full output.
[error] (test:runMain) Nonzero exit code: 1
[error] Total time: 18 s, completed May 9, 2017 6:24:29 AM

Anyone tells me why the error happens?

Different result between run-single and run-bak

很多同学都遇到了这个问题,run-bak始终正确,但是run-single时对时错。
我分析了两个同学的代码,发现时对时错的代码描述在逻辑判断上过于复杂,主要集中在对dmm和imm的操作部分,例如某同学top.scala的原始部分代码如下:

when (io.boot && io.test_im_wr){
	imm(io.test_im_addr >> 2) := io.test_im_in
} 
.elsewhen (io.boot && io.test_dm_wr){
	dmm(io.test_dm_addr >> 2) := io.test_dm_in
} 
.elsewhen (io.boot && io.test_im_rd){
	io.test_im_out := imm(io.test_im_addr >> 2)
} 
.elsewhen (io.boot && io.test_dm_rd){
	io.test_dm_out := dmm(io.test_dm_addr >> 2)
} 
.elsewhen (!io.boot){
    cpath.io.Inst := imm(dpath.io.imem_addr >> 2)
	dpath.io.dmem_datOut := dmm(dpath.io.dmem_addr >> 2)
	when (cpath.io.MemWr === "b1".U) {
		dmm(dpath.io.dmem_addr >> 2) := dpath.io.dmem_datIn
	}

}

这个代码从逻辑上来说确实没有问题,但是条件判断嵌套过于复杂,在执行上述判断嵌套却无法始终保持正确,所以我做了下面的修改:

cpath.io.Inst := imm(dpath.io.imem_addr >> 2)
dpath.io.dmem_datOut := dmm(dpath.io.dmem_addr >> 2)
io.test_dm_out := dmm(io.test_dm_addr >> 2)

when (io.boot && io.test_im_wr){
	imm(io.test_im_addr >> 2) := io.test_im_in
} 
.elsewhen (io.boot && io.test_dm_wr){
	dmm(io.test_dm_addr >> 2) := io.test_dm_in
} 
.elsewhen (io.boot && io.test_im_rd){
	io.test_im_out := imm(io.test_im_addr >> 2)
} 
.elsewhen (io.boot && io.test_dm_rd){
	//io.test_dm_out := dmm(io.test_dm_addr >> 2)
} 
.elsewhen (!io.boot){
	//cpath.io.Inst := imm(dpath.io.imem_addr >> 2)
	//dpath.io.dmem_datOut := dmm(dpath.io.dmem_addr >> 2)
	when (cpath.io.MemWr === "b1".U) {
		dmm(dpath.io.dmem_addr >> 2) := dpath.io.dmem_datIn
	}

}

这个修改将一些不必要的条件去除了(被注释掉的三行),重新运行run-single始终可以通过。

暂时的结论:
不要做过于复杂的判断嵌套,否则有些分支可能不能按预期进入。
至于为何run-bak始终正确,可能是两者仿真机制不同。
我们目前只能从代码描述上进行预防。
请有问题的同学进行尝试,可以将修改结果进行反馈。

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.