diff --git a/npc/Makefile b/npc/Makefile deleted file mode 100644 index 34e6e03..0000000 --- a/npc/Makefile +++ /dev/null @@ -1,102 +0,0 @@ -NVBOARD_HOME ?= $(abspath ../nvboard) -PREFIX ?= build -CHISEL_VDIR := $(PREFIX)/chisel - -CPPSRCS := $(addprefix $(PWD)/,$(wildcard csrc/*.cpp)) - -VERILATOR_FLAGS := --cc --exe -LDFLAGS += $(shell sdl2-config --libs) -lSDL2_image - -CHISEL_TOP_PACKAGE := learning -CHISEL_TOP_MODULE := Main -CHISEL_TARGET := verilog - -SUBDIRS := obj nvobj -SUBDIRS := $(addprefix $(PREFIX),$(SUBDIRS)) - -SUBMAKE := $(OBJDIR)/V$(CHISEL_TOP_MODULE).mk - -# Pretty printing -MAKEFLAGS += --no-print-directory -GREEN := \e[32m -NC := \e[0m -define colorize - printf '$(GREEN)'$(1)'$(NC) $(2)\n' -endef - -all: sim-bin nvboard-bin - -$(SUBDIRS):%: %/V$(CHISEL_TOP_MODULE).mk - verilator $(VERILATOR_FLAGS) $(addprefix -CFLAGS , $(CXXFLAGS)) $(addprefix -LDFLAGS , $(LDFLAGS)) --Mdir $(abspath $(OBJDIR)) $(CHISEL_VSRC) $(CPPSRCS) - -$(OBJDIR)/V$(CHISEL_TOP_MODULE): $(SUBMAKE) - @$(call colorize,"SUBMAKE",$^) - $(MAKE) -s -C $(dir $@) -f $< $(notdir $@) - -$(SUBMAKE): $(CPPSRCS) $(OBJDIR) chisel-src - @$(call colorize,"VERILATOR",$@) - verilator $(VERILATOR_FLAGS) $(addprefix -CFLAGS , $(CXXFLAGS)) $(addprefix -LDFLAGS , $(LDFLAGS)) --Mdir $(abspath $(OBJDIR)) $(CHISEL_VSRC) $(CPPSRCS) - -$(OBJDIR): - mkdir -p $@ - -$(CHISEL_VDIR)/filelist.f: $(wildcard src/main/scala/*.scala) - @$(call colorize,"CIRCT",$^) - sbt --error "runMain circt.stage.ChiselMain --module $(CHISEL_TOP_PACKAGE).$(CHISEL_TOP_MODULE) --split-verilog --target $(CHISEL_TARGET) -td $(CHISEL_VDIR)" - -compile_commands.json: clean - $(MAKE) $(CHISEL_VDIR)/filelist.f - $(RM) compile_commands.json - bear --append -- $(MAKE) nvboard-bin - bear --append -- $(MAKE) sim-bin - -.PHONY: clean nvboard sim nvboard-bin sim-bin git_trace_sim git_trace_nvboard - -SRC_AUTO_BIND := $(abspath $(PREFIX)/auto_bind.cpp) -NXDC_FILES := $(abspath constr/top.nxdc) -$(SRC_AUTO_BIND): $(NXDC_FILES) - NVBOARD_HOME=$(NVBOARD_HOME) python3 $(NVBOARD_HOME)/scripts/auto_pin_bind.py $< $@ - -nvboard-bin: OBJDIR := $(PREFIX)/nvobj -nvboard-bin: SUBMAKE := $(OBJDIR)/V$(CHISEL_TOP_MODULE).mk -# TODO: fix this awkward way to find nvboard.a -nvboard-bin: CPPSRCS := $(addprefix $(PWD)/,$(wildcard csrc_nvboard/*.cpp)) $(SRC_AUTO_BIND) $(NVBOARD_HOME)/build/nvboard.a -nvboard-bin: CXXFLAGS += -I$(NVBOARD_HOME)/include $(shell sdl2-config --cflags) -nvboard-bin: $(CPPSRCS) $(SUBMAKE) $(SRC_AUTO_BIND) $(OBJDIR)/V$(CHISEL_TOP_MODULE) - @echo $(SUBMAKE) $(OBJDIR) - -sim-bin: VERILATOR_FLAGS += --trace -sim-bin: $(CPPSRCS) $(OBJDIR)/V$(CHISEL_TOP_MODULE) - -chisel-src: $(CHISEL_VDIR)/filelist.f - $(eval CHISEL_VSRC := $(wildcard $(CHISEL_VDIR)/*.sv)) - @echo "GENERATED: $(CHISEL_VSRC)" - -ifneq (,$(wildcard ../Makefile)) -include ../Makefile -else -define git_commit # not in ICS subfolder, no tracing -endef -endif - -git_trace_sim: - $(call git_commit, "sim RTL") - -git_trace_nvboard: - $(call git_commit, "nvboard") - -nvboard: OBJDIR := $(PREFIX)/nvobj -nvboard: nvboard-bin git_trace_nvboard - @echo "Running NVBoard ..." - @echo "================================" - @NVBOARD_HOME=$(NVBOARD_HOME) $(OBJDIR)/V$(CHISEL_TOP_MODULE) - -sim: sim-bin git_trace_sim - @echo "Running verilator sim ..." - @echo "================================" - @$(OBJDIR)/V$(CHISEL_TOP_MODULE) - -clean: - $(RM) -r $(PREFIX) - -$(V).SILENT: diff --git a/npc/core/src/main/scala/Reg.scala b/npc/core/src/main/scala/Main.scala similarity index 70% rename from npc/core/src/main/scala/Reg.scala rename to npc/core/src/main/scala/Main.scala index dd5dc4f..a27983e 100644 --- a/npc/core/src/main/scala/Reg.scala +++ b/npc/core/src/main/scala/Main.scala @@ -25,6 +25,36 @@ class RegisterFile(readPorts: Int) extends Module { } } +class ALUGenerator(width: Int) extends Module { + require(width >= 0) + val io = IO(new Bundle { + val a = Input(UInt(width.W)) + val b = Input(UInt(width.W)) + val op = Input(UInt(4.W)) + val out = Output(UInt(width.W)) + }) + + val adder_b = fill(width)(io.op(0)) ^ io.b // take (-b) if sub + val add = io.a + adder_b + val and = io.a & io.b + val not = ~io.a + val or = io.a | io.b + val xor = io.a ^ io.b + val slt = io.a < io.b + val eq = io.a === io.b + + io.out := MuxLookup(0.U, io.op, Seq( + 0.U -> add, + 1.U -> add, // add with b reversed + 2.U -> not, + 3.U -> and, + 4.U -> or, + 5.U -> xor, + 6.U -> slt, + 7.U -> eq, + )) +} + class MuxGenerator(width: Int, nInput: Int) extends Module { require(width >= 0) require(nInput >= 1) diff --git a/npc/core/src/test/scala/Main.scala b/npc/core/src/test/scala/Main.scala new file mode 100644 index 0000000..9c92149 --- /dev/null +++ b/npc/core/src/test/scala/Main.scala @@ -0,0 +1,125 @@ +import chisel3._ +import chiseltest._ +import org.scalatest.freespec.AnyFreeSpec + +class RegisterFileSpec extends AnyFreeSpec with ChiselScalatestTester { + "RegisterFile should work" - { + "with 2 read ports" in { + test(new RegisterFile(2)) { c => + def readExpect(addr: Int, value: Int, port: Int = 0): Unit = { + c.io.readAddr(port).poke(addr.U) + c.io.readData(port).expect(value.U) + } + def write(addr: Int, value: Int): Unit = { + c.io.writeEnable.poke(true.B) + c.io.writeData.poke(value.U) + c.io.writeAddr.poke(addr.U) + c.clock.step(1) + c.io.writeEnable.poke(false.B) + } + // everything should be 0 on init + for (i <- 0 until 32) { + readExpect(i, 0, port = 0) + readExpect(i, 0, port = 1) + } + + // write 5 * addr + 3 + for (i <- 0 until 32) { + write(i, 5 * i + 3) + } + + // check that the writes worked + for (i <- 0 until 32) { + readExpect(i, if (i == 0) 0 else 5 * i + 3, port = i % 2) + } + } + } + } +} + +class ALUGeneratorSpec extends AnyFreeSpec with ChiselScalatestTester { + "With 32 width, " - { + "add should work" in { + test(new ALUGenerator(32)) { c => + c.io.op.poke(0.U) + c.io.a.poke(5.U) + c.io.b.poke(3.U) + c.io.out.expect(8.U) + } + } + "sub should work" in { + test(new ALUGenerator(32)) { c => + c.io.op.poke(1.U) + c.io.a.poke(5.U) + c.io.b.poke(3.U) + c.io.out.expect(2.U) + } + } + "not should work" in { + test(new ALUGenerator(32)) { c => + c.io.op.poke(2.U) + c.io.a.poke(5.U) + c.io.b.poke(3.U) + c.io.out.expect((-6).U) + } + } + "and should work" in { + test(new ALUGenerator(32)) { c => + c.io.op.poke(3.U) + c.io.a.poke(5.U) + c.io.b.poke(3.U) + c.io.out.expect(1.U) + } + } + "or should work" in { + test(new ALUGenerator(32)) { c => + c.io.op.poke(4.U) + c.io.a.poke(5.U) + c.io.b.poke(3.U) + c.io.out.expect(7.U) + } + } + "xor should work" in { + test(new ALUGenerator(32)) { c => + c.io.op.poke(5.U) + c.io.a.poke(5.U) + c.io.b.poke(3.U) + c.io.out.expect(6.U) + } + } + "compare should work" in { + test(new ALUGenerator(32)) { c => + c.io.op.poke(6) + + c.io.a.poke(62.U) + c.io.b.poke(3.U) + c.io.out.expect(0.U) + + c.io.a.poke(2.U) + c.io.b.poke(103.U) + c.io.out.expect(1.U) + + c.io.a.poke(16.U) + c.io.b.poke(16.U) + c.io.out.expect(1.U) + } + } + "equal should work" in { + test(new ALUGenerator(32)) { c => + c.io.op.poke(7) + + c.io.a.poke(62.U) + c.io.b.poke(3.U) + c.io.out.expect(0.U) + + c.io.a.poke(2.U) + c.io.b.poke(103.U) + c.io.out.expect(0.U) + + c.io.a.poke(16.U) + c.io.b.poke(16.U) + c.io.out.expect(1.U) + } + } + } +} diff --git a/npc/core/src/test/scala/Reg.scala b/npc/core/src/test/scala/Reg.scala deleted file mode 100644 index e287f48..0000000 --- a/npc/core/src/test/scala/Reg.scala +++ /dev/null @@ -1,71 +0,0 @@ -import chisel3._ -import chiseltest._ -import org.scalatest.freespec.AnyFreeSpec - -class RegisterFileSpec extends AnyFreeSpec with ChiselScalatestTester { - "RegisterFile should work" - { - "with 2 read ports" in { - test(new RegisterFile(2)) { c => - def readExpect(addr: Int, value: Int, port: Int = 0): Unit = { - c.io.readAddr(port).poke(addr.U) - c.io.readData(port).expect(value.U) - } - def write(addr: Int, value: Int): Unit = { - c.io.writeEnable.poke(true.B) - c.io.writeData.poke(value.U) - c.io.writeAddr.poke(addr.U) - c.clock.step(1) - c.io.writeEnable.poke(false.B) - } - // everything should be 0 on init - for (i <- 0 until 32) { - readExpect(i, 0, port = 0) - readExpect(i, 0, port = 1) - } - - // write 5 * addr + 3 - for (i <- 0 until 32) { - write(i, 5 * i + 3) - } - - // check that the writes worked - for (i <- 0 until 32) { - readExpect(i, if (i == 0) 0 else 5 * i + 3, port = i % 2) - } - } - } - } -} - -class MuxGeneratorSpec extends AnyFreeSpec with ChiselScalatestTester { - "MuxGenerator should work" - { - "when there are 2 inputs" in { - test(new MuxGenerator(8, 2)) { c => - c.io.in(0).poke(0.U) - c.io.in(1).poke(1.U) - c.io.sel.poke(0.U) - c.io.out.expect(0.U) - c.io.sel.poke(1.U) - c.io.out.expect(1.U) - } - } - "when there are 1024 inputs" in { - test(new MuxGenerator(32, 1024)) { c => - for (i <- 0 until 1024) { - c.io.in(i).poke(i.U) - } - for (i <- 0 until 1024) { - c.io.sel.poke(i.U) - c.io.out.expect(i.U) - } - } - } - } - "MuxGenerator should raise exception" - { - "when nInput is not 2^n" in { - assertThrows[IllegalArgumentException] { - test(new MuxGenerator(8, 3)) { c => } - } - } - } -}