From 18113f6d0854c03abd46eb6a65cc70b1c90e31d3 Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Mon, 3 Feb 2020 01:27:14 -0500 Subject: [PATCH] Begin implementing CPU with fetching and decoding This starts an nMigen hardware implementation of the Jade Rose processor. This is a large-scale, mostly untested implementation, which is structured around a multi-cycle decode with an 8-bit memory bus. In the first cycle, the instruction is fetched, in the second cycle, it's either executed or the second byte of the instruction is fetched. Most of the implementation so far is just the decode switch block. --- .gitignore | 2 + hardware/core.py | 159 +++++++++++++++++++++++++++++++++++++++++++++++ hardware/main.py | 6 ++ 3 files changed, 167 insertions(+) create mode 100644 .gitignore create mode 100644 hardware/core.py create mode 100644 hardware/main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d646835 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.pyc +__pycache__/ diff --git a/hardware/core.py b/hardware/core.py new file mode 100644 index 0000000..7a2fdc1 --- /dev/null +++ b/hardware/core.py @@ -0,0 +1,159 @@ +from nmigen import * +from enum import Enum + + +class CpuState(Enum): + READ = 0 + EXEC = 1 + EXEC2 = 2 + HALT = 3 + + +class Core(Elaboratable): + def __init__(self): + self.regs = Array(Signal(8, reset=0) for _ in range(4)) + self.it = Signal(8, reset=0) + self.data1 = Signal(8, reset=255) + self.data2 = Signal(8, reset=254) + self.code = Signal(8, reset=1) + self.link = Signal(8, reset=0) + + self.pc = Signal(16, reset=0) + self.state = Signal(CpuState) + self.inst = Signal(8) + + self.addr = Signal(16) + self.r_en = Signal() + self.w_en = Signal() + self.d_in = Signal(8) + self.d_out = Signal(8) + self.debug = Signal(8) + self.debug_en = Signal() + + def elaborate(self, platform): + m = Module() + with m.If(self.state == CpuState.HALT): + pass # We're halted :) + with m.Elif(self.state == CpuState.READ): + m.d.sync += self.state.eq(CpuState.EXEC) + m.d.sync += self.pc.eq(self.pc + 1) + m.d.comb += [ + self.addr.eq(self.pc), + self.r_en.eq(True), + ] + with m.Elif(self.state == CpuState.EXEC): + m.d.sync += self.state.eq(CpuState.READ) + m.d.sync += self.inst.eq(self.d_in) + m.d.sync += self.pc.eq(self.pc + 2) + m.d.comb += [ + self.addr.eq(self.pc + 1), + self.r_en.eq(True), + ] + self.execute(m, self.d_in) + with m.Elif(self.state == CpuState.EXEC2): + m.d.sync += self.state.eq(CpuState.READ) + self.execute2(m, self.inst, self.d_in) + return m + + def execute(self, m, inst): + m.d.sync += self.state.eq(CpuState.READ) + with m.Switch(inst): + with m.Case("00000000"): # HALT + m.d.sync += self.state.eq(CpuState.HALT) + with m.Case("00000001"): # NOPE + pass + with m.Case("00000010"): # PRNT + m.d.comb += self.debug.eq(self.it) + m.d.comb += self.debug_en.eq(True) + with m.Case("00000011"): # WAIT + pass # @TODO: Implement WAIT + with m.Case("0000010-"): # reserved + pass + with m.Case("00000110"): # CABA + pass # @TODO: Implement CABA + with m.Case("00000111"): # COFA + pass # @TODO: Implement COFA + with m.Case("00001---"): # ALU1 + self.inst_alu1(m, inst) + with m.Case("000100--"): # GET? + pass + with m.Case("000101--"): # UNSPECIFIED + m.d.sync += self.state.eq(CpuState.HALT) + with m.Case("000110--"): # SET? + pass + with m.Case("000111--"): # UNSPECIFIED + m.d.sync += self.state.eq(CpuState.HALT) + with m.Case("000111--"): # UNSPECIFIED + pass + with m.Case("00100---"): # GETR + pass + with m.Case("00101---"): # SETR + pass + with m.Case("00110---"): # SWAP + pass + with m.Case("00111---"): # ISLT + pass + with m.Case("01------"): # ALUR + pass + with m.Case("10------"): # LD/ST [12][UR} + pass + with m.Case("110-----"): # RESERVED + m.d.sync += self.state.eq(CpuState.HALT) + with m.Case("11100---"): # LD2D + pass + with m.Case("11101---"): # ST2D + pass + with m.Case("1111----"): # WITH-IMM + m.d.sync += self.state.eq(CpuState.EXEC2) + m.d.sync += self.pc.eq(self.pc) + pass + + def execute2(self, m, base, imm): + with m.Switch(Cat(imm, base)): + with m.Case("111100----------"): # ALUI + pass + with m.Case("11110100--------"): # BEZI + pass + with m.Case("11110101--------"): # JOFI + pass + with m.Case("11110110--------"): # CABI + pass + with m.Case("11110111--------"): # COFI + pass + with m.Case("111110----------"): # Reserved + m.d.sync += self.state.eq(CpuState.HALT) + with m.Case("1111110---------"): # Reserved + m.d.sync += self.state.eq(CpuState.HALT) + with m.Case("11111110--------"): # GETI + m.d.sync += self.it.eq(imm) + with m.Case("11111111--------"): # EXT1 (reserved) + m.d.sync += self.state.eq(CpuState.HALT) + + def inst_alu1(self, m, inst): + op = inst[0:3] + with m.Switch(op): + with m.Case("000"): + m.d.sync += self.it.eq(0) + with m.Case("001"): + m.d.sync += self.it.eq(self.it << 1) + with m.Case("010"): + m.d.sync += self.it.eq(self.it >> 1) + with m.Case("011"): + m.d.sync += self.it.eq(self.it // 2) + with m.Case("100"): + m.d.sync += self.it.eq(self.it + 1) + with m.Case("101"): + m.d.sync += self.it.eq(self.it - 1) + with m.Case("110"): + m.d.sync += self.it.eq(~self.it) + with m.Case("111"): + m.d.sync += self.it.eq(-self.it) + + def inputs(self): + return [self.d_in] + + def outputs(self): + return [self.addr, self.d_out, self.r_en, self.w_en, self.debug] + + def ports(self): + return self.inputs() + self.outputs() diff --git a/hardware/main.py b/hardware/main.py new file mode 100644 index 0000000..67f2d4d --- /dev/null +++ b/hardware/main.py @@ -0,0 +1,6 @@ +from nmigen.cli import main +from core import Core + +if __name__ == '__main__': + core = Core() + main(core, ports=core.ports()) -- 2.43.2