From 785578db9c0cbba65a52a21cde8687209097954a Mon Sep 17 00:00:00 2001 From: David Lenfesty Date: Sun, 23 May 2021 22:16:14 -0600 Subject: [PATCH] Started RV32I core, theoretically done integer immediates --- .gitignore | 4 ++ core.py | 105 +++++++++++++++++++++++++++++++++++++++++ instruction_decoder.py | 42 +++++++++++++++++ opcodes.py | 40 ++++++++++++++++ regfile.py | 31 ++++++++++++ 5 files changed, 222 insertions(+) create mode 100644 .gitignore create mode 100644 core.py create mode 100644 instruction_decoder.py create mode 100644 opcodes.py create mode 100644 regfile.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fb55e3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +__pycache__ +*.v +*.vcd +*.gtkw diff --git a/core.py b/core.py new file mode 100644 index 0000000..1376b7a --- /dev/null +++ b/core.py @@ -0,0 +1,105 @@ +from nmigen import * +from enum import Enum + + +from opcodes import Opcodes +from instruction_decoder import InstructionDecoder +from regfile import RegisterFile + +# TODO replace this with a proper wishbone bus +MemoryBus = Record([ + ("rw", 1), + ("addr", 32), + ("data", 32), + ("i_valid", 1), + ("o_ready", 1), +]) + +class IntImmediate(Enum): + ADDI = 0b000 + SLTI = 0b010 + SLTIU = 0b011 + XORI = 0b100 + ORI = 0b110 + ANDI = 0b111 + +class RV32ICore(Elaboratable): + """Basic RV32-I core.""" + def __init__(self): + + self.mem = MemoryBus + self.decoder = InstructionDecoder() + + def elaborate(self, platform): + m = Module() + m.submodules.decoder = self.decoder + m.submodules.regfile = RegisterFile() + + regfile = m.submodules.regfile + + pc = Signal(unsigned(32)) + instr = Signal(unsigned(32)) # Internal reg to hold instrunction data + r = Array([Signal(32) for _ in range(32)]) + + decoder_ports = self.decoder.ports() + funct = decoder_ports[1] + funct = decoder_ports[2] + imm = decoder_ports[3] + immu = decoder_ports[4] + src = decoder_ports[5] + dest = decoder_ports[6] + + m.d.comb += self.decoder.instr.eq(instr) + + m.d.sync += regfile.wen.eq(0) + + with m.FSM(): + with m.State("READ_PC"): + # Issue memory read to PC + m.d.sync += self.mem.rw.eq(0) + m.d.sync += self.mem.addr.eq(pc) + m.d.sync += self.mem.i_valid.eq(1) + m.next = "LOAD_PC" + + with m.State("LOAD_PC"): + m.d.sync += self.mem.i_valid.eq(0) + + with m.If(self.mem.o_ready): + m.d.sync += instr.eq(self.mem.data) + m.next = "DECODE" + + with m.State("DECODE"): + with m.Switch(self.decoder.opcode): + with m.Case(Opcodes.OP_IMM): + m.next = "READ_PC" + + m.d.comb += regfile.raddr1.eq(imm) + m.d.comb += regfile.waddr.eq(imm) + m.d.sync += regfile.wen.eq(1) + + with m.Switch(funct): + with m.Case(IntImmediate.ADDI): + m.d.sync += regfile.wdata.eq(regfile.raddr1 + imm) + + with m.Case(IntImmediate.SLTI): + m.d.sync += regfile.wdata.eq(regfile.raddr1 + imm) + + with m.Case(IntImmediate.SLTIU): + m.d.sync += regfile.wdata.eq(regfile.raddr1 + immu) + + with m.Case(IntImmediate.ANDI): + m.d.sync += regfile.wdata.eq(regfile.raddr1 & immu) + + with m.Case(IntImmediate.ORI): + m.d.sync += regfile.wdata.eq(regfile.raddr1 | immu) + + with m.Case(IntImmediate.XORI): + m.d.sync += regfile.wdata.eq(regfile.raddr1 ^ immu) + + return m + +if __name__ == "__main__": + from nmigen.cli import main + top = RV32ICore() + + main(top) diff --git a/instruction_decoder.py b/instruction_decoder.py new file mode 100644 index 0000000..cf5f2ff --- /dev/null +++ b/instruction_decoder.py @@ -0,0 +1,42 @@ +from nmigen import * +from opcodes import Opcodes + +class InstructionDecoder(Elaboratable): + def __init__(self): + #### Input + self.instr = Signal(unsigned(32)) + + #### Output + self.opcode = Signal(Opcodes) + self.funct = Signal(unsigned(3)) + self.imm = Signal(32) + self.immu = Signal(unsigned(32)) + # Register selection + self.src = Signal(unsigned(5)) + self.dest = Signal(unsigned(5)) + self.base = Signal(unsigned(5)) + + def ports(self) -> tuple: + return (self.instr, self.opcode, self.funct, self.imm, self.immu, self.src, self.dest, self.base) + + def elaborate(self, platform): + m = Module() + + m.d.comb += self.opcode.eq(self.instr[0:6]) + + # TODO do actual sign extension + + with m.Switch(self.opcode): + with m.Case(Opcodes.OP_IMM): + m.d.comb += self.imm[0:10].eq(self.instr[20:30]) + for i in range(11, 32): + m.d.comb += self.imm[i].eq(self.instr[31]) + + m.d.comb += self.immu[0:11].eq(self.instr[20:31]) + m.d.comb += self.immu[12:31].eq(0) + + m.d.comb += self.funct.eq(self.instr[12:14]) + m.d.comb += self.src.eq(self.instr[15:19]) + m.d.comb += self.dest.eq(self.instr[7:11]) + + return m diff --git a/opcodes.py b/opcodes.py new file mode 100644 index 0000000..c074295 --- /dev/null +++ b/opcodes.py @@ -0,0 +1,40 @@ +from enum import Enum + + +class Opcodes(Enum): + """List of all opcodes for RV32I instruction set""" + LOAD = 0b00000 + LOAD_FP = 0b00001 + CUSTOM_0 = 0b00010 + MISC_MEM = 0b00011 + OP_IMM = 0b00100 + AUIPC = 0b00101 + OP_IMM_32 = 0b00110 + # 48b? + + STORE = 0b01000 + STORE_FP = 0b01001 + CUSTOM_1 = 0b01010 + AMO = 0b01011 + OP = 0b01100 + LUI = 0b01101 + OP_32 = 0b01110 + # 64b? + + MADD = 0b10000 + MSUB = 0b10001 + NMSUB = 0b10010 + NMADD = 0b10011 + OP_FP = 0b10100 + # reserved + CUSTOM_2 = 0b10110 + # 48b? + + BRANCH = 0b11000 + JALR = 0b11001 + # reserved + JAL = 0b11011 + SYSTEM = 0b11100 + # reserved + CUSTOM_3 = 0b11110 + # >80b? diff --git a/regfile.py b/regfile.py new file mode 100644 index 0000000..4d00fb1 --- /dev/null +++ b/regfile.py @@ -0,0 +1,31 @@ +from nmigen import * + +class RegisterFile(Elaboratable): + def __init__(self): + self.regs = Array([Signal(32) for _ in range(32)]) + + self.wen = Signal() + self.waddr = Signal(unsigned(5)) + self.raddr1 = Signal(unsigned(5)) + self.raddr2 = Signal(unsigned(5)) + self.wdata = Signal(unsigned(32)) + self.rdata1 = Signal(unsigned(32)) + self.rdata2 = Signal(unsigned(32)) + + def elaborate(self, platform): + m = Module() + + with m.If(self.wen): + m.d.sync += self.regs[self.waddr].eq(self.wdata) + + with m.If(self.raddr1 == 0): + m.d.comb += self.rdata1.eq(0) + with m.Else(): + m.d.comb += self.rdata1.eq(self.regs[self.raddr1]) + + with m.If(self.raddr2 == 0): + m.d.comb += self.rdata2.eq(0) + with m.Else(): + m.d.comb += self.rdata2.eq(self.regs[self.raddr2]) + + return m